Files
orchestrator/docs/architecture/adr/adr-0025-build-cache-pruner.md

6.9 KiB
Raw Permalink Blame History

work_item, stage, author_agent, status, created_at, model_used
work_item stage author_agent status created_at model_used
ORCH-062 architecture architect proposed 2026-06-09 claude-opus-4-8

adr-0025: Build-cache-pruner — фоновый heartbeat-демон авто-уборки docker build cache на хосте

Сквозной (cross-cutting) ADR: вводит новый фоновый компонент оркестратора в ряду reconciler (adr-0007), job_reaper (adr-0011) и disk_watchdog (adr-0024). Детальное решение задачи — docs/work-items/ORCH-062/06-adr/ADR-001-build-cache-pruner.md.

Статус

Proposed (ORCH-062)

Контекст

07.06.2026 диск хоста mva154 тихо дорос до 100% и положил весь self-hosting-конвейер всех проектов (один прод-инстанс orchestrator на общей БД/очереди). Доминирующий «пожиратель» — docker build cache (≈11 ГБ от частых пересборок прод/staging-образов). disk_watchdog (adr-0024, ORCH-063) ввёл сигнал о заполнении (Telegram ≥85%) и явно отложил авто-очистку в отдельную задачу. ORCH-062 — эта задача: автоматическое освобождение build cache, чтобы инцидент не повторялся без оператора.

Сверено по коду: контейнер orchestrator не содержит docker CLI (Dockerfile:11 — только openssh-client git curl); host-docker-операции приложение уже делает через ssh на хост (image_freshness.image_revision, self_deploy Phase B), канал deploy_ssh_user@deploy_ssh_host настроен. У оркестратора три проверенных фоновых daemon-потока с единым каркасом.

Решение

Вводится четвёртый фоновый компонент build-cache-pruner (src/build_cache_pruner.py):

  • Калька каркаса disk_watchdog/reconciler/reaper: daemon-поток, чистый стоп через _stop.wait(interval), контракт start()/stop(timeout)/status(), старт/стоп в main.lifespan (старт последним — после disk_watchdog.start(); стоп первым в reverse), наблюдаемость — аддитивный блок build_cache_prune в GET /queue. Leaf-модуль (без обратных зависимостей на stage_engine/stages/qg).
  • Уборка — строго docker builder prune -f --filter until=<until> (BuildKit GC, дефолт until=24h): удаляется только старый build cache, тёплый ≤24ч сохраняется. -a — опционально и только в паре с возрастным фильтром. Запрещены docker image prune/system prune/удаление образов запущенных сервисов/остановка-рестарт контейнеров.
  • Исполнение на хосте через ssh (CLI в контейнере нет): ssh deploy_ssh_user@deploy_ssh_host "docker builder prune …", bounded таймаутом. Нет ssh-таргета → тик no-op → фича естественно скоупится на self-hosting-прод.
  • Конфиг/kill-switch (ORCH_BUILD_CACHE_PRUNE_*, дефолты безопасные): enabled (дефолт true), interval_s (6ч), until (24h), all (false), timeout_s, notify_min_gb. Валидаторы по образцу disk_monitor_* (невалид → лог + дефолт).
  • Сигнал + лечение как пара: disk_watchdog сигналит о росте диска, build-cache-pruner убирает доминирующего «пожирателя» — две половины одной операционной защиты.

Инварианты: STAGE_TRANSITIONS, реестр QG_CHECKS, check_*, src/stage_engine.py, схема БД — не меняются (pruner — эксплуатационный демон, не Quality Gate, как watchdog/reaper). Без миграции БД (учёт результата in-memory, best-effort). never-raise per-команда/per-tick. Уборка никогда не рестартит docker daemon/прод-контейнер (self-hosting безопасность; рестарт-путь — отвергнутый Вариант B). При выключенном kill-switch — поведение 1:1 как сейчас (нулевая регрессия для enduro-trails).

Альтернативы

  • host daemon.json builder.gc.defaultKeepStorage — отвергнуто: требует рестарта docker daemon (останавливает ВСЕ контейнеры хоста = групповой self-hosting риск); политика по объёму, не по возрасту; не наблюдаемо в GET /queue.
  • host-cron — отвергнуто как основное (оставлено ручным fallback): off-git невидимая инфра, без /queue-наблюдаемости, без config-kill-switch, не тестируется.
  • raw-HTTP по docker.sock / docker CLI в образе — отвергнуто: лишний код / раздувание образа против уже существующего ssh-канала.

Последствия

  • + Корень инцидента 07.06 устраняется автоматически; тёплый кэш сохранён; без новых зависимостей и без рестарта docker/прода (принцип «всё в Docker, минимум зависимостей»).
  • + Знакомый паттерн фонового демона → низкий риск, наблюдаемость, обратимость, тестируемость.
  • Зависимость от ssh на хост (как image_freshness/self_deploy); нет таргета → no-op (наблюдаемо), фича не работает, но ничего не ломает.
  • Откат: ORCH_BUILD_CACHE_PRUNE_ENABLED=false; миграций БД нет.

Ссылки

  • Задачный ADR: docs/work-items/ORCH-062/06-adr/ADR-001-build-cache-pruner.md
  • Инфра/риски: docs/work-items/ORCH-062/07-infra-requirements.md, docs/work-items/ORCH-062/10-tech-risks.md
  • Комплемент: adr-0024-disk-watchdog.md (ORCH-063 — сигнал)
  • Родственные компоненты: adr-0007-reconciler.md, adr-0011-job-reaper-lease-reclaim.md
  • Топология host / env-карта: docs/operations/INFRA.md