21 KiB
INFRA.md — инфраструктура и эксплуатация оркестратора
RUNBOOK. Топология, контейнеры, порты, переменные окружения, границы. Секреты тут НЕ хранятся — только дескрипторы. Реальные значения — в
.envна хосте.
Топология
host: mva154 (slin@82.22.50.71), network_mode: host
┌──────────────────────────────────────────────────────────────────────┐
│ orchestrator (PROD) :8500 env_file .env │
│ БД: ./data/orchestrator.db (обслуживает ВСЕ прод-проекты) │
│ │
│ orchestrator-staging (STAGING) :8501 env_file .env.staging │
│ БД: ./data/staging/orchestrator.db (изолирована, только sandbox) │
│ profile: staging — НЕ стартует обычным `docker compose up` │
└──────────────────────────────────────────────────────────────────────┘
│ webhooks │ git
▼ ▼
Plane (ag_proj) Gitea (localhost:3000)
/repos/<project> ← общий каталог репозиториев (host: /home/slin/repos)
Контейнеры
| Контейнер | Роль | Порт | env_file | БД (хост) | Старт |
|---|---|---|---|---|---|
orchestrator |
прод | 8500 | .env |
./data/orchestrator.db |
docker compose up -d |
orchestrator-staging |
staging / песочница | 8501 | .env.staging |
./data/staging/orchestrator.db |
docker compose --profile staging up -d orchestrator-staging |
Оба: network_mode: host, init: true (tini как PID 1 — reaping зомби, B-2), restart: unless-stopped.
Рантайм-uid (ORCH-040)
Оба сервиса бегут под user: "1000:1000" (slin), не root. Артефакты конвейера
(git worktree /repos/_wt/..., коммиты в docs/work-items/...) создаются как
slin:slin, поэтому git pull / git reset на хосте под slin работают без ручного
chown. Доступ к docker.sock сохранён через group_add: ["999"] (gid docker, не
через root — НЕ удалять). При переносе на другой хост uid пересматривается. См.
ADR docs/work-items/ORCH-040/06-adr/ADR-001-run-agents-as-host-uid.md и глобальный
docs/architecture/adr/adr-0005-container-runs-as-host-uid.md.
Host-prerequisites (обязательная процедура Owner, в git не коммитятся):
- P-1 (блокер): uid 1000 читает claude creds —
chown -R 1000:1000 /home/slin/.claude; проверкаsudo -u '#1000' test -r /home/slin/.claude/.credentials.json. Без этого preflight (ORCH-044) заворачивает весь конвейер. - P-2: ssh-ключи в
/home/slin/.orchestrator-sshчитаемы uid 1000 (маунт ведёт в/home/slin/.ssh). - P-3:
id slin→1000:1000;/repos,/app/dataуже1000:1000. - P-4: прод-рестарт self — только в окно тишины (
GET /statusбез активных задач): общий инстанс с enduro-trails. - Разовый разгребающий
chown -R 1000:1000 /home/slin/repos/orchestratorдля старыхroot:rootфайлов из истории (вне объёма кода).
Тома (volumes)
./data→/app/data(БД; у staging —./data/staging)/home/slin/repos→/repos(рабочие репозитории проектов)/var/run/docker.sock(для docker-операций деплоя)- claude-code, node,
~/.claude*(CLI агентов, ro) ~/.orchestrator-ssh→/home/slin/.ssh(ro, деплой по ssh; target в HOME агента, согласован сHOME=/home/slinиз launcher — ORCH-040, ранее/root/.ssh)
Disk-watchdog: мониторинг заполнения диска mva154 (ORCH-063)
07.06.2026 диск хоста mva154 тихо дорос до 100% и положил весь конвейер всех проектов
(один прод-инстанс orchestrator на общей БД/очереди). Чтобы такой инцидент сигнализировался
заранее, работает фоновый daemon-поток src/disk_watchdog.py (каркас reconciler/job_reaper):
- Что мониторится: заполнение хост-разделов по смонтированным bind-путям (
/repos→ host/home/slin/repos,/app/data→ host./data) через stdlibshutil.disk_usage— НЕ overlay/контейнера (иначе замер ложно-низкий). Пути с одним физическим устройством (st_dev) дедуплицируются → один алерт, не два. - Порог и период: при заполнении ≥ 85% (
ORCH_DISK_MONITOR_THRESHOLD_PCT) шлётся Telegram-алерт оператору; замер — раз в 300с (ORCH_DISK_MONITOR_INTERVAL_S). Пока диск выше порога, повтор — не чаще раза в ~6ч (ORCH_DISK_MONITOR_REALERT_S, анти-спам). При возврате ниже порога — однократное recovery-сообщение. - Как отключить:
ORCH_DISK_MONITOR_ENABLED=false(демон не стартует;GET /queue→disk_monitor.enabled=false; поведение 1:1 как сейчас). Наблюдаемость — блокdisk_monitorвGET /queue(последний замер:used_pct/free_gb/alerting/last_alert_atпо каждому пути). - Что делать при алерте: watchdog только сигнализирует — он не трогает диск/контейнер и не
рестартит прод (self-hosting безопасность). Освобождение docker build cache автоматизировано
отдельным демоном (ORCH-062, см. ниже); прочие «пожиратели» — старые worktree-каталоги
/home/slin/repos/_wt/*завершённых задач, логи, dangling-образы (docker image prune) — по-прежнему ручная операция оператора (авто-уборка этих категорий — вне объёма ORCH-062/063).
Build-cache-pruner: авто-prune docker build cache на mva154 (ORCH-062)
Доминирующий «пожиратель» в инциденте 07.06.2026 — docker build cache (≈11 ГБ от частых
пересборок прод/staging-образов). Чтобы он не мог снова заполнить диск без оператора, работает
фоновый daemon-поток src/build_cache_pruner.py (каркас disk_watchdog) — «вторая половина»
watchdog'а: watchdog сигналит, pruner убирает.
- Что делает: каждые
ORCH_BUILD_CACHE_PRUNE_INTERVAL_S(дефолт 21600с = 6ч) выполняет строгоdocker builder prune -f --filter until=<until>(BuildKit GC; дефолтuntil=24h— удаляется build cache старше суток, тёплый свежий кэш сохраняется). Команда затрагивает только build cache — НЕ образы/контейнеры запущенных сервисов; рестарт docker daemon/прода НЕ выполняется (self-hosting безопасность). - Как исполняется: в контейнере нет
dockerCLI (образ несёт толькоopenssh-client git), поэтому уборка идёт на хосте через ssh тем же каналомORCH_DEPLOY_SSH_USER@_HOST, что деплой/image_freshness. ПустойORCH_DEPLOY_SSH_HOST→ тик no-op (фича активна только на self-host, где ssh настроен). - Как отключить:
ORCH_BUILD_CACHE_PRUNE_ENABLED=false(демон не стартует; поведение 1:1 как до ORCH-062). Наблюдаемость — блокbuild_cache_pruneвGET /queue(enabled/interval_s/until/last_run_ts/last_reclaimed/last_error); never-raise; in-memory учёт (без миграции). - Ручной fallback (если ssh-канал недоступен) — host-cron на mva154:
0 */6 * * * docker builder prune -f --filter until=24h(off-git, процедура Owner).
Переменные окружения (карта; значения — в .env)
| Переменная | Назначение |
|---|---|
ORCH_PLANE_API_URL / _TOKEN / _WORKSPACE_SLUG |
доступ к Plane API |
ORCH_PLANE_WEB_URL |
внешний (браузерный) web-URL Plane для кликабельных ссылок на issue в уведомлениях (ORCH-017); пусто → фолбэк на ORCH_PLANE_API_URL, loopback-фолбэк → ссылка опускается |
ORCH_PLANE_WEBHOOK_SECRET |
HMAC-проверка вебхуков Plane |
ORCH_GITEA_URL / _TOKEN / _WEBHOOK_SECRET |
доступ к Gitea + HMAC |
ORCH_CLAUDE_BIN |
путь к claude CLI |
ORCH_REPOS_DIR / ORCH_HOST_REPOS_DIR |
каталог репозиториев (в контейнере / на хосте) |
ORCH_DB_PATH |
путь к SQLite БД |
ORCH_PROJECTS_JSON |
реестр проектов (Plane id → repo + prefix); пусто → дефолт из src/projects.py |
ORCH_AGENT_MODEL_DEFAULT |
LLM-модель агентов по умолчанию (ORCH-41); дефолт claude-opus-4-8 |
ORCH_AGENT_MODEL_<AGENT> |
per-agent модель (ANALYST/ARCHITECT/DEVELOPER/REVIEWER/TESTER/DEPLOYER); пусто → default |
ORCH_AGENT_EFFORT_DEFAULT |
режим работы --effort по умолчанию (ORCH-41): low|medium|high|xhigh|max; дефолт high |
ORCH_AGENT_EFFORT_<AGENT> |
per-agent effort; дефолт: думающие → high, tester/deployer → medium |
ORCH_AGENT_FALLBACK_MODEL |
опц. фолбэк-модель при overloaded (--fallback-model); пусто → без флага |
ORCH_SELF_DEPLOY_ENABLED |
ORCH-036 kill-switch исполняемого самодеплоя (true); false → legacy-путь для всех |
ORCH_SELF_DEPLOY_REPOS |
CSV репозиториев с реальным самодеплоем; пусто → только self-hosting orchestrator |
ORCH_DEPLOY_REQUIRE_MANUAL_APPROVE |
требовать человеческий Plane «Approved» для прод-деплоя (true, безопасно) |
ORCH_DEPLOY_FINALIZE_DELAY_S / _MAX_ATTEMPTS |
задержка и бюджет defer'ов finalizer'а (Фаза C; 90 / 10) |
ORCH_DEPLOY_SSH_USER / _SSH_HOST |
куда запускается detached хост-деплой (Фаза B, ssh user@host) |
ORCH_DEPLOY_HOOK_SCRIPT / _HOST_REPO_PATH |
путь к хук-скрипту (отн. репо) и чекаут orchestrator на хосте |
ORCH_DEPLOY_PROD_SOURCE_IMAGE |
staging-образ для build-once retag на прод-тег (без rebuild) |
ORCH_DEPLOY_PROD_TARGET_SERVICE / _TARGET_PORT / _TARGET_IMAGE / _COMPOSE_PROFILE / _PREV_IMAGE_FILE |
прод-цель хука + снапшот для авто-rollback |
ORCH_IMAGE_FRESHNESS_ENABLED |
ORCH-058 единый kill-switch провенанса staging-образа (A+B как целое); дефолт true, false → legacy build-once без проверки свежести |
ORCH_IMAGE_FRESHNESS_REPOS |
CSV репозиториев с реальным гейтом свежести; пусто → только self-hosting orchestrator |
ORCH_RECONCILE_ENABLED |
kill-switch sweeper потерянных webhook (ORCH-053); дефолт true. При инциденте/раскатке — false глушит весь фоновый reconciler |
ORCH_RECONCILE_PLANE_ENABLED |
отдельный флаг F-2 (опрос Plane API); false гасит только plane-ветку, F-1 продолжает работать; дефолт true |
ORCH_RECONCILE_INTERVAL_S |
период фонового прохода reconciler, сек; дефолт 120 |
ORCH_RECONCILE_GRACE_DEFAULT_S |
порог «застряла» по tasks.updated_at, сек; дефолт 600 |
ORCH_RECONCILE_GRACE_OVERRIDES_JSON |
per-stage пороги, напр. {"development":300}; невалидный JSON → дефолт |
ORCH_RECONCILE_NOTIFY_UNBLOCK |
слать Telegram при разблокировке застрявшей задачи; дефолт true |
ORCH_DISK_MONITOR_ENABLED |
kill-switch disk-watchdog (ORCH-063); дефолт true. false → демон не стартует, поведение 1:1 как сейчас |
ORCH_DISK_MONITOR_INTERVAL_S |
период heartbeat-замера заполнения диска, сек; дефолт 300 |
ORCH_DISK_MONITOR_THRESHOLD_PCT |
порог заполнения для алерта, %; дефолт 85 (валидация 1..100, иначе → дефолт) |
ORCH_DISK_MONITOR_REALERT_S |
cooldown повторного алерта, пока выше порога, сек; дефолт 21600 (~6 ч) |
ORCH_DISK_MONITOR_PATHS |
CSV отслеживаемых хост-bind-путей; пусто → /repos,/app/data |
ORCH_BUILD_CACHE_PRUNE_ENABLED |
kill-switch build-cache-pruner (ORCH-062); дефолт true. false → демон не стартует, поведение 1:1 как до задачи |
ORCH_BUILD_CACHE_PRUNE_INTERVAL_S |
период тика авто-prune, сек; дефолт 21600 (~6 ч); валидация >0, иначе → дефолт |
ORCH_BUILD_CACHE_PRUNE_UNTIL |
возраст удержания тёплого кэша (docker builder prune --filter until=); дефолт 24h; валидация ^\d+[smhdw]?$, иначе → 24h |
ORCH_BUILD_CACHE_PRUNE_ALL |
добавить -a к prune (только в паре с until); дефолт false |
ORCH_BUILD_CACHE_PRUNE_TIMEOUT_S |
таймаут ssh-команды prune, сек; дефолт 120 |
ORCH_BUILD_CACHE_PRUNE_NOTIFY_MIN_GB |
Telegram при освобождении ≥ N ГБ; дефолт 0 (тихо) |
DEPLOY_SSH_USER / _HOST / DEPLOY_HOOK_SCRIPT |
параметры деплой-хука |
Секреты — только в .env / .env.staging на хосте, в гит НЕ коммитятся. Канон — .env.example, .env.staging.example.
Реестр проектов (src/projects.py, ORCH-6)
Связывает Plane project id → gitea repo + work-item prefix. Источник: ORCH_PROJECTS_JSON, fallback — встроенный дефолт. Прод видит: enduro-trails (ET), orchestrator (ORCH). Staging видит ТОЛЬКО orchestrator-sandbox (SANDBOX) — изоляция.
Модель и effort агентов (src/config.py + src/agents/launcher.py, ORCH-41)
Модель LLM и режим работы (--effort) каждого агента конфигурируемы — глобально per-agent (env) и per-project (через ORCH_PROJECTS_JSON).
Приоритет резолвинга (resolve_agent_model / resolve_agent_effort):
- per-project override —
agent_models/agent_effortsв записиORCH_PROJECTS_JSON; - per-agent env —
ORCH_AGENT_MODEL_<AGENT>/ORCH_AGENT_EFFORT_<AGENT>(если непусто); - глобальный дефолт —
ORCH_AGENT_MODEL_DEFAULT(claude-opus-4-8) /ORCH_AGENT_EFFORT_DEFAULT(high); - пусто → флаг не передаётся, действует дефолт CLI.
Значения effort: low < medium < high < xhigh < max — рычаг «качество vs стоимость/время». Дефолтная раскладка: думающие агенты (analyst/architect/developer/reviewer) → high, механические (tester/deployer) → medium. Невалидное значение → лог-warning, флаг опускается.
Per-project override в ORCH_PROJECTS_JSON (поля agent_models / agent_efforts опциональны, старые записи работают):
{"plane_project_id":"...","repo":"orchestrator","work_item_prefix":"ORCH",
"agent_models":{"developer":"claude-opus-4-8","reviewer":"claude-sonnet-4-6"},
"agent_efforts":{"developer":"xhigh","tester":"low"}}
⚠️ Бюджет (ORCH-38):
claude-opus-4-8дефолт в коде; реальное переключение прод-env делается отдельно после согласования.
⚠️ Self-hosting — оркестратор дорабатывает САМ СЕБЯ
Факт: прод-инстанс orchestrator (8500) — ОДИН на ВСЕ прод-проекты (enduro-trails + orchestrator), с ОБЩЕЙ БД ./data/orchestrator.db и общей очередью задач (ORCH-1).
Следствие — групповой риск: когда орк выполняет задачу из проекта ORCH (дорабатывает себя), он бежит в том же инстансе, что обслуживает enduro-trails.
- Рестарт / падение прод-контейнера орк-задачей → конвейер ВСЕХ проектов встаёт.
- Кривой self-деплой (ORCH-36, Вариант B) → лежат все проекты сразу.
- Общая очередь → орк-задача занимает concurrency-слоты других проектов.
Что изолировано (безопасно):
- Staging (8501) — отдельная БД (
./data/staging), отдельный реестр (ORCH_PROJECTS_JSON= только sandbox). Прод-проекты не видит. - Репозитории разделены, изоляция веток через git worktree (ORCH-2).
Страховки:
- Стадия
deploy-staging(порт 8501) — обязательный гейт перед прод-деплоем орка. Прод-деплой недостижим, пока staging-гейт не зелёный (см.STAGING.md, ORCH-35). Гейт условный: реален только для self-hosting (repo=orchestrator), для остальных проектов — no-op. - Свежесть staging-образа (ORCH-058): на ребре
deploy-staging → deploy(ПОСЛЕ merge-gate, ДО Phase A) QG-под-чекcheck_staging_image_freshпересобирает staging-образ из валидированного коммита и пересоздаёт 8501 (Strategy A), а хук перед build-once retag fail-closed сверяет OCI-лейблrevisionсEXPECTED_REVISION(Strategy B). Гарантирует: в прод промоутится РОВНО провалидированный артефакт (инцидент LESSONS_ORCH-036 п.4 — тихий промоут устаревшего образа). Сборки/recreate — ТОЛЬКО staging (8501); FAIL → откат наdevelopment. Условный: реален только для self-hosting.
Правила для агентов при задачах ORCH:
- НЕ перезапускать / не ронять прод-контейнер
orchestratorв рамках задачи. - Все проверки деплоя — на staging (8501), боевой 8500 не трогать.
- Деплой self — только через хук с health-check + авто-rollback (
DEPLOY_HOOK.md).
Эксплуатация (быстрые команды)
# статус
docker ps --filter name=orchestrator
curl -s http://localhost:8500/health
curl -s http://localhost:8500/status # активные задачи
curl -s http://localhost:8500/queue # очередь
# поднять staging-песочницу
docker compose --profile staging up -d orchestrator-staging
curl -s http://localhost:8501/health
# логи
docker logs --tail 100 orchestrator
RUNBOOK 2026-06-05. Обновлять при изменении топологии/портов/переменных. См. CONTRIBUTING.md §8.