Files
pet-project-server/docs/adr/ADR-2026-07-25-local-image-build.md
T
av bb141807c9
Linting / YAML Lint (push) Canceled after 0s
Linting / Ansible Lint (push) Canceled after 0s
adr: записана локальная сборка образов вместо Yandex Container Registry
Постфактум по сегодняшней работе: почему ушли от cr.yandex к роли app_image
(упрощение деплоя без раннера + сокращение секретов) и чем за это платим.
2026-07-25 16:31:38 +03:00

6.1 KiB
Raw Blame History

Локальная сборка образов вместо Yandex Container Registry

  • Дата: 2026-07-25

Контекст

Три приложения собственной разработки — homepage, transcriber, remembos — деплоились через Yandex Container Registry. Схема у всех одинаковая: образ собирается локально, пушится в cr.yandex/... отдельным плейбуком (playbook-*-registry.yml), плейбук деплоя делает docker_login с yc_oauth_token, а compose тянет образ по registry-пути с тегом, зашитым в git.

Схема тянула за собой три проблемы.

Секрет несопоставимого радиуса. Ради одного docker login в vault лежал OAuth-токен всего аккаунта Yandex Cloud — реестр, виртуалки, биллинг. 24 июля он вместе с ещё 52 переменными ушёл открытым текстом в транскрипт: цена одной такой утечки — весь аккаунт, а не один сервис.

Разнесённый деплой. Сборка жила в task deploy репозитория приложения, который сам звал ansible-playbook. У transcriber этот таск дополнительно правил vars/transcriber.images.yml в чужом репозитории через yq и делал там git commit с новым тегом. У remembos registry-плейбука не было вовсе — образ пушился руками, а в compose стоял тег v0.2.0, вбитый в git. У homepage тег был плавающий :latest, то есть ночной docker image prune мог подтянуть другое содержимое без возможности отката.

Автоматизировать сборку пока нечем. Стабильного онлайн-раннера для workflow gitea у нас нет (отдельная задача — раннер on-demand), так что уехать в CI сборки не могут. Плюс после переезда сервера в Timeweb реестр остался единственной ниткой, тянущейся в Yandex Cloud.

Решение

Приложения переведены на общую роль app_image (канон — ~/projects/private/ansible-roles, в репозитории лежит завендоренная копия в roles/, синхронизация — inv roles-pull/roles-push). Роль:

  1. генерит случайный BUILD_ID — он же тег образа;
  2. запускает в директории приложения его собственную команду сборки (контракт — task image, BUILD_ID приходит через окружение), приложение обязано собрать полный образ и затегать его <app_image_name>:$BUILD_ID;
  3. печатает размер архива, везёт его на сервер через docker savecopydocker load;
  4. отдаёт факт app_image_tag, которым compose-шаблон подставляет образ (pull_policy: never — в реестре образа нет).

Два мотива за этим. Первый — упростить деплой, пока нет стабильного онлайн-раннера: теперь inv pl -- <app> собирает, доставляет и запускает приложение одной командой, отдельного ритуала в репозитории приложения не осталось. Второй — сократить набор секретов: реестр и OAuth-токен выпали из схемы, yc_oauth_token, yc_container_registry и yc_container_registry_repository удалены из vault (коммит 6a0d786).

Registry-плейбуки и vars/transcriber.images.yml удалены; у remembos заодно снесены gitea-workflow с goreleaser — они существовали только ради пуша образа в cr.yandex.

Последствия

  • + Из деплоя ушли внешний реестр и токен на весь аккаунт облака; в vault на три переменные меньше.
  • + Деплой любого из трёх приложений — одна команда inv pl -- <app>; «правка тега в чужом репозитории + коммит» как шаг деплоя исчезла.
  • + Тег каждого деплоя уникален, плавающего :latest больше нет. Откат — повторный деплой нужного коммита приложения.
  • - Дедупликации по слоям нет: каждый деплой везёт полный архив. Замеры docker save: homepage — 61 МБ, transcriber — 73 МБ, remembos — 21 МБ. Для ручного нечастого деплоя размен принят осознанно.
  • - Для деплоя нужен ноутбук с docker и тулчейнами: с чужой машины задеплоить нельзя, а playbook-all-applications.yml теперь собирает три образа за прогон.
  • - На сервере копятся образы от каждого прогона; чистит их ночной docker image prune -af (см. playbook-system.yml).
  • Контракт для новых приложений: чтобы деплоиться этой ролью, приложение должно иметь task image, тегающий образ из $BUILD_ID.
  • Осталось: buckland/playbook-trackers.yml всё ещё ходит в cr.yandex со своей копией токена в собственном vault — пока он жив, отзывать токен в облаке рано.