Files
orchestrator/docs/architecture/adr/adr-0049-host-side-docker-execution-boundary.md
claude-bot 2a47744c9d
All checks were successful
CI / test (push) Successful in 1m8s
architect(ET): auto-commit from architect run_id=748
2026-06-16 08:07:55 +03:00

9.0 KiB
Raw Blame History

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 отклонился от инварианта). Локальная детализация (D1D9) — 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 → PopenFileNotFoundError: 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