7.7 KiB
work_item, stage, author_agent, status, created_at, model_used
| work_item | stage | author_agent | status | created_at | model_used |
|---|---|---|---|---|---|
| ORCH-100 | architecture | architect | proposed | 2026-06-10 | claude-opus-4-8 |
adr-0033: Sidecar-watchdog F1b — мозг мониторинга в отдельном контейнере
- Статус: proposed
- Дата: 2026-06-10
- Задача: ORCH-100 (FND/F1b)
- Детальный ADR:
docs/work-items/ORCH-100/06-adr/ADR-001-sidecar-watchdog.md - Парный ADR:
adr-0030(F1a/metrics— источник сырья)
Контекст
Домен 0 «Фундамент» эпика автономного саморазвития, рамка наблюдаемости заказчика: наблюдатель
отделён от наблюдаемого. F1a (adr-0030) отдаёт read-only GET /metrics — только сырьё. F1b —
мозг: читает сырьё, дополняет внешними сигналами (хост/контейнеры/зависимости), решает по порогам,
алертит. Частичные стражи (disk_watchdog/reaper/reconciler) живут ВНУТРИ процесса орка — орк
завис/упал ⇒ они мертвы, платформа слепа в критический момент. Рамки: C-1 (отдельный контейнер, код в
watchdog/), C-2 (без внешнего плеча — принятый риск), C-3 (тонкий стек, НЕ Grafana/Prometheus; хост
впритык). Критический инвариант: орк лёг ⇒ /metrics недоступен = сам сигнал тревоги.
Решение
Новая папка watchdog/ — тонкий Python-3.12-stdlib демон (без сторонних зависимостей), отдельный
образ watchdog/Dockerfile + сервис orchestrator-watchdog в docker-compose.yml (network_mode: host, read-only docker.sock, mem_limit: 128m, restart: unless-stopped). Тик: (1) GET /metrics;
(2) хост (диск/inode/память/CPU, stdlib); (3) статусы контейнеров через read-only docker.sock
(GET-only — без docker SDK); (4) пинг Plane/Gitea/Anthropic. Сигналы проходят через обобщённую
чистую decide(signal_active, prev, now, cooldown) -> alert|realert|recovery|none (генерализация
disk_watchdog.decide_action; per-signal in-memory AlertState). Алерт — в собственный Telegram-
канал sidecar (свои WATCHDOG_TG_*; НЕ импорт src/notifications.py). Особый сигнал — /metrics
не отвечает → orch_down. Всё never-raise (per-source/per-tick/per-send), под kill-switch
WATCHDOG_ENABLED, строго read-only к наблюдаемому. src/**/STAGE_TRANSITIONS/QG_CHECKS/
check_*/схема БД орка — не тронуты (F1b вне процесса орка и вне конвейера QG).
- Стек — Python stdlib (
urllib,socket+http.clientдля docker.sock,shutil.disk_usage,/proc/meminfo); pytest на чистые функции. Отвергнуты Go /dockerSDK / Prometheus (C-3). - Реестр сигналов —
orch_down(K подряд неудачных опросов),host_mem/host_disk_crit,agent_hung(Δcpu_ticks/clk_tck/Δgenerated_at< floor при растущемruntime_s; нужно 2 опроса — sidecar stateful-арбитр),stage_stuck(age_in_stage_s),job_failed(edge),queue_depth,container_down(per name),dep_down(per name). Пороги/интервалы/URL — из env. - Владелец диск-алерта (BR-10) — штатные 85% остаются за внутренним
disk_watchdog(ORCH-063, канал орка) ⇒ нулевой дубль по построению; sidecar покрывает провал «орк+disk_watchdog мертвы» черезorch_down, плюс opt-in (default off) независимый критический потолокhost_disk_crit(97%) — другое событие/канал, не повтор 85%. - Толерантность контракта — неизвестные ключи
/metricsигнорируются, отсутствие опционального не ошибка, ростschema_version→ warning (зеркало аддитивной политики adr-0030). - Kill-switch
WATCHDOG_ENABLED=false→ демон инертен (idle-loop, не exit) ⇒ нулевой эффект.
Альтернативы
- Go /
dockerSDK /requests— отклонено: вес/вторая цепочка против C-3 и консистентности сdisk_watchdog. - Prometheus/Grafana/TSDB — отклонено: прямой запрет C-3.
- Sidecar — единственный владелец диска — отклонено: потеря покрытия, когда сам sidecar/Docker
недоступен; выбрана связка primary
disk_watchdog+ opt-in ceiling. - Push из орка в sidecar — отклонено: зависший орк не пушит; pull падает = сам сигнал
orch_down. - bridge +
host.docker.internal— отклонено: на Linux ненадёжно;network_mode: hostпроще. - Своя БД/файл порогов — отклонено: C-3; in-memory best-effort достаточно (как
disk_watchdog).
Последствия
- Внешний мозг мониторинга переживает падение орка;
orch_downделает наблюдателя громче в инцидент. - Строго read-only + независимый канал + never-raise ⇒ self-hosting-безопасно (enduro не затронут); падение sidecar не влияет на конвейер.
- Аддитивно/обратимо:
src/**/гейты/схема байт-в-байт; kill-switch → нулевая регрессия; дубль диска исключён структурно. - Плата: новый контейнер на впритык-хосте (
mem_limit: 128m+ замер RSS на staging обязательны); C-2 (падёт хост → молчит и sidecar); новая поверхность совместимости/metrics↔F1b (толерантный парсинг + единый репо контракта); CPU-liveness Linux-специфичен. - Топология меняется (новый контейнер) →
07-infra-requirements.md; схема БД не меняется → 08 = N/A. Новый компонент + контейнер + канал →arch:major-change; прод-выкат через staging-гейт (8501), деплой sidecar НЕ рестартит прод-контейнер. - Откат: не запускать сервис /
WATCHDOG_ENABLED=false(мгновенный) или удалениеwatchdog/+ сервиса + env — без следов в БД/схеме.
Связи
adr-0030 (F1a /metrics — парный источник сырья; контракт cpu_ticks/clk_tck/generated_at/
schema_version), adr-0024 (disk_watchdog — образец решающей функции/never-raise + владелец
диск-алерта), adr-0025 (build-cache-pruner — паттерн «вторая половина»), adr-0017 (serial_gate —
leaf snapshot()/never-raise), adr-0011 (job-reaper — pid/liveness-семантика). Прямой источник —
F1a (GET /metrics); F1b — его потребитель.