Files
orchestrator/docs/history/LESSONS_ORCH-036-selfdeploy.md

7.1 KiB
Raw Blame History

Lessons Learned — 2026-06-07 (утро): ORCH-36 self-deploy bootstrap — каскад неготовности инфры

Итог

ORCH-36 (исполняемый самодеплой стадии deploy) замкнулась в проде — конвейер впервые задеплоил сам себя по полному циклу Phase A→B→C (approve → детачед ssh-хук → finalizer). Но путь до Done вскрыл четыре слоя неготовности инфраструктуры, каждый из которых требовал ручного bootstrap-разрыва: задача про автодеплой не может задеплоить сама себя, пока её же механизм + инфра не в проде.

Эпик ORCH-54: 4/6 в проде (ORCH-40 права, ORCH-43 merge-gate, ORCH-36 самодеплой, ORCH-53 reconciler). Конвейер автономен: мержит → катит в прод → чинит застрявшее.


Каскад из 4 инфра-багов (все вскрылись только при РЕАЛЬНОМ деплое)

1. 🔴 uid 1000 без записи в /etc/passwd → ssh/whoami падают

Симптом: self-deploy initiate failed: ssh launch failed (rc=255): No user exists for uid 1000. Корень: регрессия ORCH-40 — compose запускает контейнер под 1000:1000, но базовый образ python:3.12-slim не имеет passwd-записи для 1000. SSH-клиент (и whoami, getpwuid()) отказываются стартовать без валидного юзера. Фикс: в Dockerfilegroupadd -g 1000 app && useradd -u 1000 -g 1000 -m -d /home/slin -s /bin/bash slin. Rebuild + recreate. Коммит 64e031a. Урок: при переводе контейнера на non-root uid (ORCH-40) ОБЯЗАТЕЛЬНО создавать passwd- запись в образе, иначе ssh/git/любой инструмент с getpwuid() ломается. Проверять docker exec <c> whoami после смены uid.

2. 🔴 env-префикс: DEPLOY_* vs ORCH_DEPLOY_* (pydantic не видит)

Симптом: ssh: Could not resolve hostname : No address associated with hostname — host пустой, хотя в compose DEPLOY_SSH_HOST=127.0.0.1 задан. Корень: Settings имеет env_prefix = "ORCH_" → читает ТОЛЬКО ORCH_DEPLOY_SSH_HOST. Старые DEPLOY_* (без префикса) предназначались легаси enduro-деплоеру (читает через os.environ напрямую) и pydantic их игнорирует → дефолт host="". Доп: DEPLOY_HOOK_SCRIPT указывал на enduro-deploy-hook.sh, не на orchestrator-хук. Фикс: в docker-compose.yml добавлены ORCH_DEPLOY_SSH_USER/HOST, ORCH_DEPLOY_HOOK_SCRIPT=scripts/orchestrator-deploy-hook.sh, ORCH_DEPLOY_HOST_REPO_PATH (легаси DEPLOY_* оставлены для enduro). Коммит 115519e. Урок: все настройки, читаемые через pydantic Settings, ДОЛЖНЫ иметь префикс ORCH_. Проверять резолв: docker exec <c> python3 -c 'from src.config import settings; print(settings.deploy_ssh_host)'.

3. 🔴 /var/log/orchestrator принадлежит root → хук падает на tee

Симптом: tee: /var/log/orchestrator/deploy-hook.log: Permission denied, хук exit 1. Корень: лог-директория root:root, а хук бежит под slin. Фикс: chown -R slin:slin /var/log/orchestrator на хосте. Урок: все пути, в которые пишет хост-хук (логи, sentinel, prev-image), должны быть writable юзером, под которым ssh-сессия. Заложить создание/chown в provisioning хоста.

4. 🔴🔴 BUILD-ONCE retag берёт УСТАРЕВШИЙ staging-образ → катит регресс (ВАЖНО)

Симптом: деплой «зелёный» (result=0, health ok), но прод откатился на код 2-дневной давности — пропал deploy-finalizer (Unknown agent: deploy-finalizer), задача не закрылась. Корень: хук делает BUILD-ONCE: retag orchestrator-orchestrator-staging → orchestrator-orchestrator (без rebuild, by design ORCH-36 BR-6). Дизайн предполагал «staging-образ = свежий, провалидированный». В РЕАЛЬНОСТИ orchestrator-orchestrator-staging никто не пересобрал из нового main → retag катил в прод СТАРЫЙ образ → бесконечная петля: каждый Phase B возвращал прод в прошлое, finalizer (новый код) исчезал, Phase C не мог закрыть задачу. Фикс (ручной разрыв): пересобрать orchestrator-orchestrator-staging из актуального main ПЕРЕД retag → тогда хук катит свежий код. После этого Phase C отработал: result=0 → SUCCESS → deploy → done. Урок / ТЕХДОЛГ: retag-стратегия BUILD-ONCE предполагает гарантию свежести staging- образа, которой НЕТ. Нужна отдельная задача: либо staging-деплой пересобирает образ из текущего main перед валидацией, либо deploy-хук проверяет, что staging-образ собран из HEAD main (по labels/sha), иначе fail-fast. Сейчас «зелёный» деплой может молча катить регресс. Это самый опасный из четырёх — он не падает, а тихо откатывает прод.


Сквозной урок: bootstrap самохостинга

Любая задача, меняющая deploy/merge-механику САМОГО оркестратора, упирается в парадокс: её механизм не работает, пока не в проде, а в прод его можно влить только старым механизмом. Каждый слой (код → права → env → образ) вскрывается ТОЛЬКО при первом реальном прогоне. Закладывать в план таких задач ручной bootstrap-чеклист и гонять реальный деплой в staging-петле до мержа, а не только бумажные гейты.

Прод после (main 115519e+, образ 2026-06-07 09:47)

  • self_deploy.py + reconciler.py в проде, finalizer зарегистрирован (grep=5)
  • uid 1000 = slin (passwd ok), ssh slin@127.0.0.1 работает, /var/log/orchestrator writable
  • ORCH-36 task 43 → done, Plane → Done