Постфактум по сегодняшней работе: почему ушли от cr.yandex к роли app_image (упрощение деплоя без раннера + сокращение секретов) и чем за это платим.
79 lines
6.1 KiB
Markdown
79 lines
6.1 KiB
Markdown
# Локальная сборка образов вместо 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 — пока он жив, отзывать токен в облаке рано.
|