Перейти к содержанию

Хранилище объектов (MinIO)

Как в установке Taimen устроено S3-совместимое хранилище: зачем оно ядру и панели платформы, какие бакеты и учётные записи заводятся, как подключить внешний S3 вместо MinIO контура, что бэкапить и как разбирать типичные сбои. Статья для инженера эксплуатации.

Кто пользуется хранилищем

Потребитель Бакет (переменная, умолчание) Учётная запись Что лежит
Control Plane (API и worker) CP_S3_BUCKET, artifacts отдельный пользователь CP_S3_ACCESS_KEY_ID с политикой только на свой бакет Содержимое артефактов задач (см. Артефакты)
platform-api (профиль platform) S3_BUCKET, platform root-учётка MinIO S3_ACCESS_KEY_ID Файлы документов панели (см. Документы и файлы)

Ядро хранит в PostgreSQL только записи: ссылку на объект, размер, media type и SHA-256. Байты — только в хранилище. Объект содержимого адресуется контрольной суммой внутри tenant'а: tenants/<tenant-id>/sha256/<hex>. Одинаковые файлы одного tenant'а хранятся одним объектом, файлы разных tenant'ов не пересекаются.

MinIO в compose

Сервис Профили Назначение
minio core, platform S3-сервер, данные в томе platform_minio, лимит памяти MINIO_MEM_LIMIT (256m), healthcheck mc ready local
minio-bootstrap core, platform одноразовый: бакеты, политика и пользователь ядра; завершается после старта

MinIO входит в профиль core: хранилище содержимого артефактов — часть ядра, а не панели. Имя тома — ${COMPOSE_PROJECT_NAME}_platform_minio, переопределяется VOLUME_PLATFORM_MINIO.

Наружу MinIO не смотрит. Портов на хост не публикуется, маршрута в Caddy (профиль edge) нет. Клиенты не получают адресов объектов: байты артефактов входят через PUT /api/v1/artifact-contents и выходят через GET /api/v1/artifacts/{id}/content, где ядро проверяет права и пишет событие выдачи.

Что делает minio-bootstrap

Сервис выполняет набор команд mc от root-учётки MinIO:

  1. создаёт бакеты S3_BUCKET и CP_S3_BUCKET (mc mb --ignore-existing);
  2. создаёт политику cp-artifacts (см. ниже);
  3. заводит пользователя CP_S3_ACCESS_KEY_ID с секретом CP_S3_SECRET_ACCESS_KEY;
  4. привязывает к нему политику, если она ещё не привязана.

Все шаги идемпотентны: сервис можно перезапускать при каждом up. control-plane-api ждёт его успешного завершения (service_completed_successfully); зависимость помечена необязательной, чтобы установка с внешним S3, где сервиса нет, тоже поднималась.

Политика пользователя ядра

Политика cp-artifacts для бакета по умолчанию artifacts:

{"Version": "2012-10-17", "Statement": [
  {"Effect": "Allow", "Action": ["s3:GetBucketLocation", "s3:ListBucket"],
   "Resource": ["arn:aws:s3:::artifacts"]},
  {"Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
   "Resource": ["arn:aws:s3:::artifacts/*"]}
]}
  • Объекты своего бакета — чтение, запись и удаление: загрузка содержимого, выдача, удаление администратором и очистка worker'ом.
  • s3:ListBucket на бакет нужен, потому что при старте API проверяет бакет запросом HeadBucket, а без этого права хранилище отвечает 403 и ядро считает хранилище недоступным.
  • Создавать бакеты ядру не нужно и нельзя: бакет заводит minio-bootstrap. Бакета документов панели и других бакетов пользователь ядра не видит, root-ключи ядру не выдаются.

Ключи

Все четыре ключа генерирует make secrets (tools/fill_secrets.py), если в .env они пусты:

Переменная Кто использует
S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY root-учётка minio, minio-bootstrap, platform-api
CP_S3_ACCESS_KEY_ID, CP_S3_SECRET_ACCESS_KEY пользователь ядра: minio-bootstrap заводит его, процессы Control Plane ходят под ним
CP_S3_BUCKET бакет содержимого артефактов, по умолчанию artifacts

CP_S3_ACCESS_KEY_ID и CP_S3_SECRET_ACCESS_KEY в compose.yml обязательны (${VAR:?…}): без них не поднимется ни MinIO-bootstrap, ни процессы ядра. Остальные настройки ядра (CP_S3_ENDPOINT_URL, CP_S3_REGION, таймауты, лимиты) — в Конфигурации Control Plane.

Ключи — секреты

Храните .env вместе с остальной конфигурацией установки: без него восстановленный том MinIO не откроется ни root-учёткой, ни пользователем ядра. См. Секреты и ротация.

Внешний S3 вместо MinIO

Содержимое артефактов можно держать у любого S3-совместимого провайдера. Файл compose.s3.example.yml в корне суперпроекта переводит minio и minio-bootstrap только в профиль platform; подключайте его вторым файлом:

docker compose -f compose.yml -f compose.s3.example.yml --profile core --profile edge up -d

В .env:

CP_S3_ENDPOINT_URL=https://s3.example.com
CP_S3_REGION=<регион провайдера>
CP_S3_BUCKET=<бакет>
CP_S3_ACCESS_KEY_ID=<ключ пользователя>
CP_S3_SECRET_ACCESS_KEY=<секрет пользователя>
  • Бакет создайте заранее средствами провайдера.
  • Пользователю нужны те же права, что у политики выше: ListBucket на бакет и GetObject, PutObject, DeleteObject на его объекты.
  • MinIO контура при этом остаётся нужен только панели платформы (профиль platform, документы); ядру — нет.

Хранилище выключено совсем

Если CP_S3_ENDPOINT_URL пуст, Control Plane работает без хранилища: артефакты-ссылки и JSON-артефакты создаются как обычно, а маршруты содержимого отвечают 503 content_store_unavailable. В поставочном compose.yml адрес по умолчанию — http://minio:9000.

Резервное копирование

Том platform_minio содержит и документы панели, и содержимое артефактов ядра. Бэкапьте его вместе с базами:

  • control-plane-db ↔ MinIO. Записи артефактов — в базе Control Plane, байты — в MinIO. Снимайте их в одном окне. Если том MinIO старше базы, у части записей contentState = stored объекта не окажется, и выдача таких артефактов ответит 503 content_store_unavailable.
  • platform-db ↔ MinIO — так же для документов панели.

Скрипт ежедневного бэкапа и порядок восстановления — в Резервном копировании. Снимайте копию тома после дампа базы Control Plane: объекты неизменяемы и адресуются контрольной суммой, так что объекты записей из дампа к этому моменту уже лежат в томе. Исключение — содержимое, которое администратор удалил между дампом и копией тома.

Очистка и рост

  • Незавершённые загрузки. Загрузка, на которую за CP_ARTIFACT_UPLOAD_TTL_SECONDS (24 часа по умолчанию) не сослался ни один артефакт, удаляется worker'ом вместе с объектом, если тот больше никому не нужен. Если хранилище недоступно, строки остаются до следующего прохода.
  • Содержимое артефактов хранится бессрочно. Закрытие задачи его не удаляет. Удалить байты конкретного артефакта может администратор tenant'а: POST /api/v1/artifacts/{id}:purge-content (см. Удаление содержимого).
  • Потолок одного файла — CP_ARTIFACT_MAX_BYTES (100 МиБ по умолчанию); тип артефакта может сузить его своим maxBytes.

Рост тома учитывайте в Ресурсах и масштабировании.

Типичные проблемы

Симптом Причина Что делать
503 content_store_unavailable на PUT /artifact-contents при работающем MinIO CP_S3_ENDPOINT_URL пуст — хранилище в ядре выключено Задать адрес и пересоздать процессы Control Plane
503 content_store_unavailable, в журнале API предупреждение content store unavailable at start-up MinIO не поднят, неверные ключи или у пользователя нет ListBucket docker compose ps minio minio-bootstrap, логи minio-bootstrap; проверить ключи в .env и политику
503 content_store_unavailable только при чтении отдельных артефактов Объекта нет в хранилище при записи stored: том восстановлен из более старой копии, чем база Восстановить том MinIO из копии, согласованной с базой
docker compose up падает на set CP_S3_ACCESS_KEY_ID Ключей ядра нет в .env make secrets — допишет недостающие ключи
413 request_too_large на загрузке Файл больше CP_ARTIFACT_MAX_BYTES Уменьшить файл или поднять лимит установки
422 artifact_too_large при создании артефакта Файл больше maxBytes зарегистрированного типа артефакта Выпустить версию типа с большим maxBytes (не выше CP_ARTIFACT_MAX_BYTES)

См. также