Files
pet-project-server/docs/adr/ADR-2026-07-25-local-image-build.md
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

79 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Локальная сборка образов вместо 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 save``copy`
`docker 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 — пока он жив, отзывать токен в облаке рано.