9.0 KiB
work_item, stage, author_agent, status, created_at, model_used
| work_item | stage | author_agent | status | created_at | model_used |
|---|---|---|---|---|---|
| ORCH-123 | architecture | architect | proposed | 2026-06-16 | claude-opus-4-8 |
adr-0049: Граница исполнения docker — все docker-операции host-side, не изнутри app-контейнера
Сквозной (cross-cutting) ADR. Кодифицирует инвариант «docker-операции оркестратора исполняются host-side через доверенный ssh-канал, никогда изнутри прод-контейнера», охватывающий компоненты ORCH-036/058/115/123/101, и амендит execution-strategy-решение adr-0048 (D3/D5). Поводом стала задача ORCH-123 (баг: staging-runner отклонился от инварианта). Локальная детализация (D1–D9) —
docs/work-items/ORCH-123/06-adr/ADR-001-host-side-staging-execution-and-env-classification.md.
Статус
Proposed
Контекст
Прод-контейнер orchestrator (8500) не содержит docker CLI (Dockerfile:11:
openssh-client git curl ca-certificates + pinned gitleaks; python:3.12-slim docker не несёт).
/var/run/docker.sock смонтирован rw + group_add 999 (ORCH-040 «МИНА 1»), но клиента, который
бы им воспользовался, нет — сознательно: добавление CLI/SDK активировало бы root-эквивалентный путь
исполнения для всего, что бежит в контейнере (вкл. LLM-агентов). Поэтому в оркестраторе сложился
инвариант исполнения, ранее не выделенный в отдельный ADR:
- ORCH-036 (
self_deploy.build_deploy_command, adr-0007) — прод-деплой исполняется host-side черезssh + setsid bash <hook> --deployна127.0.0.1. - ORCH-058 (
image_freshness, adr-0008) — ребилд staging-образа (ssh … bash <hook> --build-staging) и инспекция revision (image_revision(ssh_target=…)) — host-side; модуль прямо документирует: «docker lives on the HOST (the container ships only openssh-client git)». - ORCH-101 (adr-0036) — host-параметры
канала (
deploy_ssh_*,deploy_host_repo_path,repos_dir/host_repos_dir) расхардкожены.
ORCH-115 (adr-0048), заменяя LLM-деплойера
детерминированным staging_runner, отклонился от инварианта: зашил docker exec изнутри
прод-контейнера через proc_group → Popen → FileNotFoundError: docker → постоянный
environment-дефект, ложно маршрутизированный как транзиентная инфра → DEFER → fail-closed FAILED →
откат deploy-staging → development (винит код задачи за дефект окружения раннера). Инцидент
ORCH-116/ORCH-123.
Решение
Кодифицировать инвариант (нормативно): docker-операции оркестратора (docker/docker compose/
docker exec/docker inspect/docker tag) исполняются host-side через доверенный ssh-канал
(deploy_ssh_host=127.0.0.1, ключ смонтирован, openssh-client в образе) — никогда изнутри
прод-контейнера, который docker CLI не несёт. /var/run/docker.sock не используется изнутри
контейнера; docker CLI/SDK в образ не добавляется (любое исключение — отдельный явный
security-review: socket-из-контейнера = root-эквивалент на хосте, обслуживающем все проекты).
ORCH-123 приводит staging_runner в соответствие (амендит adr-0048 D3/D5):
- D3 (амендмент adr-0048):
staging_runner.build_staging_commandтеперь обёртываетdocker exec orchestrator-staging python3 staging_check.py …вssh <user>@<host> '<…>'(зеркалоimage_freshness.image_revision(ssh_target=…)). Внутренняя команда сюиты и exit-код-контракт — те же; меняется лишь инициатор/канал. - D5 (амендмент adr-0048 двухуровневого исхода): введён третий класс исхода
permanent-env(зеркалоmerge_gate.classify_retest_failure, ORCH-110); корневой инвариант — «сюита не исполнилась» (environment ИЛИ транзиентная инфра) НИКОГДА не оканчивается код-фейл-откатом и не жжёт developer-retry; откат — только для реально исполнившейся сюиты сexit≠0. Терминал исчерпания DEFER изменён с fail-closed-FAILED+advance на infra-HOLD + alert (как ORCH-110 D3).
Кросс-каттинговые инварианты (сохранены байт-в-байт, как adr-0048):
STAGE_TRANSITIONS/ реестр и именаQG_CHECKS/check_staging_status/_parse_staging_status/ machine-verdict-ключи (staging_status:/deploy_status:/…) / схема БД — не тронуты (замена стратегии исполнения продюсера, не гейта/стадии).- Единственный транспорт LLM-консультации (
launcher._spawn/S0, adr-0047) — соблюдён (раннер LLM не зовёт). - Сквозной бюджет времени ORCH-065/109/110 (
reaper_max_running_s> Σ(работ на ребре) + grace) — не растёт (host-side ssh заменяет in-container call, окно ≤staging_runner_timeout_s). - Граница transition-lease ORCH-114 — берётся внутри
advance_stage; раннер не трогает.
Скоуп — self-hosting only (staging_runner_repos="" → is_self_hosting_repo); под флагами
staging_runner_enabled (→ LLM-путь) и новым staging_runner_exec_host_side (дефолт True →
фикс; False → прежний in-container call). never-raise во всех публичных функциях.
Последствия
- + Инвариант «docker host-side» выделен и задокументирован → будущие компоненты не повторят отклонение ORCH-115; reviewer ловит in-container docker как регресс инварианта.
- + staging-сюита реально исполняется в проде; инфра/environment ≠ код-фейл на staging-ребре (закрыт RCA-класс ORCH-110 на этом ребре полностью); анти-over-tolerance цел.
- + Без расширения привилегий (нет docker CLI/SDK в контейнере, сокет не используется); согласовано с ORCH-036/058.
- − Remote tree-kill ограничен локальным ssh-клиентом (как
image_freshness.rebuild_staging_image); backstop — bounded таймаут внутриstaging_check.py. - − Permanent-env/исчерпавшая-DEFER задача держится на
deploy-staging(блокирует serial-gate репо до починки оператором) — принятый tradeoff (зеркало ORCH-110), self-hosting only. - Откат:
ORCH_STAGING_RUNNER_ENABLED=false(→ LLM) илиORCH_STAGING_RUNNER_EXEC_HOST_SIDE=false(→ in-container call).
Ссылки
- Локальный ADR:
docs/work-items/ORCH-123/06-adr/ADR-001-host-side-staging-execution-and-env-classification.md - Амендит: adr-0048 (D3/D5 ORCH-115)
- Опирается на: adr-0007 (ORCH-036 self-deploy ssh), adr-0008 (ORCH-058 image-freshness host-side docker), adr-0042 (ORCH-110 proc_group + classify + infra-tolerance), adr-0036 (ORCH-101 host-параметризация)
- Сверено по коду:
src/staging_runner.py,src/self_deploy.py:220,src/image_freshness.py:185/246,scripts/orchestrator-deploy-hook.sh:166/197,Dockerfile:11,docker-compose.yml