adr: записана локальная сборка образов вместо Yandex Container Registry
Linting / YAML Lint (push) Canceled after 0s
Linting / Ansible Lint (push) Canceled after 0s

Постфактум по сегодняшней работе: почему ушли от cr.yandex к роли app_image
(упрощение деплоя без раннера + сокращение секретов) и чем за это платим.
This commit is contained in:
av
2026-07-25 16:31:38 +03:00
parent 0b2a3bd19c
commit bb141807c9
2 changed files with 79 additions and 0 deletions
@@ -0,0 +1,78 @@
# Локальная сборка образов вместо 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 — пока он жив, отзывать токен в облаке рано.
+1
View File
@@ -63,6 +63,7 @@
| Дата | Запись | Статус | | Дата | Запись | Статус |
| ---------- | ---------------------------------------------------------------------------------------------- | ------ | | ---------- | ---------------------------------------------------------------------------------------------- | ------ |
| 2026-07-25 | [Локальная сборка образов вместо Yandex Container Registry](ADR-2026-07-25-local-image-build.md) | — |
| 2026-06-22 | [Разнесение restic-операций на фазы под Intelligent Tiering](ADR-2026-06-22-restic-intelligent-tiering-phases.md) | — | | 2026-06-22 | [Разнесение restic-операций на фазы под Intelligent Tiering](ADR-2026-06-22-restic-intelligent-tiering-phases.md) | — |
| 2026-05-23 | [Переезд сервера с Yandex Cloud на Timeweb VPS](ADR-2026-05-23-migrate-to-timeweb.md) | — | | 2026-05-23 | [Переезд сервера с Yandex Cloud на Timeweb VPS](ADR-2026-05-23-migrate-to-timeweb.md) | — |
| 2026-04-04 | [Apprise как шлюз уведомлений](ADR-2026-04-04-apprise-notifications.md) | — | | 2026-04-04 | [Apprise как шлюз уведомлений](ADR-2026-04-04-apprise-notifications.md) | — |