Обновление и миграции¶
Как выкатить новую версию платформы: штатная процедура, кто и когда применяет миграции схем, как сократить простой, как откатиться. Статья для инженера, который выполняет выкладку на промышленную установку.
Что такое релиз¶
Релиз платформы — коммит суперпроекта. Он закрепляет ревизии всех
компонентов указателями сабмодулей, а заодно compose.yml, .env.example,
deploy/ и пакеты каталога. Обновить установку — значит перевести клон
суперпроекта на новый коммит, подтянуть сабмодули на закреплённые ревизии,
пересобрать образы и пересоздать изменившиеся контейнеры.
sequenceDiagram
participant Op as Инженер
participant Git as Клон суперпроекта
participant D as Docker
participant Svc as Сервисы
Op->>Op: бэкап БД и secrets/
Op->>Git: git pull --ff-only
Op->>Git: git submodule update --init --recursive
Op->>D: docker compose build (сервисы продолжают работать)
Op->>D: docker compose up -d
D->>Svc: пересоздание изменившихся контейнеров
Svc->>Svc: alembic upgrade head при старте
Op->>Svc: make smoke, /health/ready
Штатная выкладка¶
cd /opt/taimen/src
PROFILES="--profile core --profile platform --profile edge" # профили вашей установки
# 0. Бэкап (обязательно перед релизом с миграциями)
# см. «Резервное копирование»
# 1. Код
git fetch && git log --oneline HEAD..origin/main # что приезжает
git pull --ff-only
git submodule update --init --recursive
git submodule status # ни одной строки с «+» или «-»
# 2. Сборка заранее: работающие контейнеры не трогаются
docker compose $PROFILES build
# 3. Переключение: пересоздаются только контейнеры с новым образом или конфигурацией
docker compose $PROFILES up -d
# 4. Проверка
make smoke
curl -fsS http://127.0.0.1:18000/health/ready # {"status":"ready","revision":"..."}
docker compose --profile "*" ps
Сначала build, потом up
make up выполняет up -d --build, то есть собирает образы прямо в
момент переключения. На слабой машине сборка занимает минуты, и всё это
время часть сервисов может быть пересоздана, а часть — ещё нет. Раздельные
build и up -d сокращают окно переключения до секунд: пересоздание
контейнера API с готовым образом занимает порядка 5–10 секунд, а claim
живого исполнителя (по умолчанию CP_CLAIM_TTL_SECONDS=300) это
переживает.
Команды — из корня клона
docker compose читает .env из каталога проекта. Если запускаете
compose из другого каталога или с -f, передавайте
--env-file /opt/taimen/src/.env, иначе интерполяция упадёт на
required variable CP_POSTGRES_PASSWORD is missing a value.
Один образ Control Plane на три процесса¶
control-plane-api, control-plane-worker и context-adapter запускаются
из одного образа ${IMAGE_PREFIX}/control-plane:${IMAGE_TAG}. Секция
build: есть только у control-plane-api; у worker и адаптера её нет.
Следствия:
docker compose build control-plane-workerничего не собирает. Собирайтеcontrol-plane-apiили весь профильcore.- После сборки пересоздайте все три контейнера.
docker compose up -dбез имён сервисов сделает это сам (у всех трёх сменился образ). Если перечисляете сервисы явно — перечисляйте все три:
- Имя сервиса адаптера —
context-adapter, без префиксаcontrol-plane-. Ошибка в имени роняет всю командуupсno such service, и не поднимается ни один из перечисленных сервисов.
Миграции схем¶
Отдельного шага миграции нет: каждый сервис с собственной БД приводит схему к head при старте.
| Сервис | Когда применяются миграции | Механизм |
|---|---|---|
iam-service |
При старте контейнера | alembic upgrade head && uvicorn … |
control-plane-api |
При старте контейнера | alembic upgrade head && uvicorn … |
control-plane-worker, context-adapter |
Не применяют | Стартуют после того, как control-plane-api стал healthy |
memory-service |
При старте, идемпотентно | Сервис создаёт недостающие таблицы и индексы; миграции аддитивные |
platform-api |
До старта | Одноразовый контейнер platform-migrations (service_completed_successfully) |
policy-service, entitlement-service |
При старте контейнера | alembic upgrade head && uvicorn … |
GET /health/ready Control Plane сравнивает ревизию БД с head образа и
отвечает 503 с reason: migrations_pending, пока они расходятся — поэтому
healthcheck не пропустит worker и адаптер к старой схеме:
{"status": "unavailable", "reason": "migrations_pending",
"dbRevision": "<старая ревизия>", "headRevision": "<новая ревизия>"}
Проверить ревизии вручную:
docker compose exec control-plane-db psql -U control_plane -d control_plane \
-c 'SELECT version_num FROM alembic_version'
docker compose exec iam-db psql -U iam -d iam -c 'SELECT version_num FROM alembic_version'
docker compose run --rm --no-deps control-plane-api alembic heads
Индексы строятся не CONCURRENTLY
Миграции Control Plane создают индексы обычным CREATE INDEX, который
блокирует запись в таблицу на время построения. На большой базе релиз с
новыми индексами выкатывайте в окно обслуживания.
Релиз с миграциями Control Plane¶
Для релиза, который меняет схему журнала или курсоров, консервативный порядок такой (адаптер доставки в память — singleton, его лучше остановить до смены схемы):
docker compose $PROFILES build
docker compose stop context-adapter
docker compose up -d control-plane-api # применит миграции
curl -fsS http://127.0.0.1:18000/health/ready # ждать 200
docker compose up -d control-plane-worker context-adapter
После обновления¶
| Что проверить | Когда нужно |
|---|---|
Повторный прогон deploy/bootstrap.py |
Если релиз менял AUDIENCES, потолки service accounts, права агентов по умолчанию или пакеты каталога. Скрипт идемпотентен: приводит allowedScopes audiences к реестру (PATCH), при изменившемся потолке перевыпускает service account ядра и отзывает прежний |
| Перезапуск ядра после bootstrap | Если bootstrap перевыпустил secrets/control-plane-iam.env: docker compose up -d control-plane-api control-plane-worker context-adapter |
| План каталога | python3 tools/cp_packages.py plan --install deploy/packages.yaml --server https://platform.example.com показывает расхождения каталога до применения (токен — CP_TOKEN) |
Пересборка platform-web |
При смене TAIMEN_PUBLIC_URL или CP_TIMEZONE: значения NEXT_PUBLIC_* зашиваются в бандл на этапе сборки (build-args) |
| Realm Keycloak | Правки шаблона platform-realm.json на существующий realm не попадают: --import-realm импортирует только при первом старте. Изменения вносятся через Admin API |
| Runner-хост | Обновить отдельно, см. ниже |
| Рабочие места операторов | Переустановить пакет control-plane (MCP-плагин, CLI) и перезапустить сессию: новые инструменты cp_* появляются только в новой сессии |
Обновление runner-хоста¶
Исполнитель не обновляется вместе с хостом платформы.
cd <клон суперпроекта на runner-хосте>
git pull --ff-only && git submodule update --init --recursive
docker compose -f deploy/runner/docker/compose.yml up -d --build
Образ собирается из дерева суперпроекта; при старте entrypoint обновляет
bare-зеркала (git fetch), отдельной установки пакета нет.
Всё — от пользователя runner, иначе в каталогах появятся файлы
root, и следующая установка упадёт с Permission denied:
sudo -u runner git -C <runner-root>/src/control-plane pull --ff-only
sudo -u runner env HOME=/home/runner \
UV_TOOL_DIR=<runner-root>/tools UV_TOOL_BIN_DIR=<runner-root>/bin \
/home/runner/.local/bin/uv tool install --reinstall <runner-root>/src/control-plane
sudo -u runner git -C <runner-root>/<repo>.git fetch origin '+refs/heads/*:refs/heads/*'
sudo systemctl restart <юниты исполнителей>
uv tool install от root ставит пакет в /root/.local/share/uv/tools —
мимо сервисов, и они молча остаются на старом коде. Обновлять нужно оба
места: src/ (из чего собран демон) и bare-зеркало (из чего делаются
рабочие копии задач).
Остановка исполнителя безопасна в любой момент: при следующем старте демон
находит свой осиротевший run и закрывает его с
failure_reason=restart_recovery, задача возвращается в очередь, рабочая
копия сохраняется.
Откат релиза¶
Быстрый откат без миграций¶
Если новый релиз не менял схему (ревизии Alembic до и после совпадают), откат — это обратный переход по коммиту:
git checkout <предыдущий коммит суперпроекта>
git submodule update --init --recursive
docker compose $PROFILES build
docker compose $PROFILES up -d
Держите предыдущие образы
По умолчанию все образы помечаются тегом local и новая сборка
перезаписывает старую. Если перед сборкой выставлять в .env
IMAGE_TAG=<короткий хэш коммита>, образ предыдущего релиза остаётся
на хосте, и откат сводится к возврату прежнего IMAGE_TAG и
docker compose up -d — без пересборки.
Откат с миграциями¶
Alembic откатывает схему только кодом, который знает новую ревизию, поэтому порядок строгий:
- Определите ревизию, на которую откатываетесь (head предыдущего релиза):
по журналу выкладок (см. ниже) или командой
alembic heads, выполненной образом предыдущего релиза. -
Новым образом выполните downgrade:
-
Переключите код и образы на предыдущий релиз (как в быстром откате).
- Поднимите сервисы и проверьте
/health/ready.
Если downgrade невозможен
Не каждая миграция обратима без потерь. Если downgrade не проходит, восстановите БД из бэкапа, снятого перед релизом (см. Резервное копирование), и поднимите предыдущий релиз поверх восстановленной базы. После восстановления из дампа Control Plane доставит в память события повторно — это безопасно: память дедуплицирует их по идентификатору события.
Журнал выкладок¶
Записывайте для каждой выкладки: коммит суперпроекта, IMAGE_TAG, ревизии
Alembic control-plane и iam-service до и после, время переключения,
результат make smoke. Эти данные нужны для отката и для разбора инцидентов.