--- work_item: ORCH-062 stage: analysis author_agent: analyst status: ready-for-review created_at: 2026-06-09 model_used: claude-opus-4-8 --- # 01 — BRD (бизнес-требования): ORCH-062 — INFRA: авто-prune docker build cache на mva154 Work Item: **ORCH-062** · Repo: **orchestrator** · Стадия: analysis ## 1. Бизнес-контекст и проблема **Установленный факт (инцидент 07.06.2026).** Хост-диск mva154 тихо дорос до 100% и положил **весь конвейер всех проектов** (один прод-инстанс `orchestrator` на общей БД/очереди обслуживает и `enduro-trails`, и `orchestrator`). Доминирующий «пожиратель» в этом инциденте — **docker build cache**: частые пересборки образа (`docker compose up -d --build` при прод-деплое, пересборки staging-образа `--profile staging` и `check_staging_image_fresh` ORCH-058) накапливают слои build cache, который дорос до **≈11 ГБ**. Заполнение диска положило **CI + Gitea** и остановило приём вебхуков/обработку очереди. **Что уже сделано (ORCH-063, не дублировать).** Введён фоновый daemon `src/disk_watchdog.py`, который **только сигнализирует** (Telegram-алерт при заполнении ≥85%). В ADR/INFRA ORCH-063 явно зафиксировано: *«watchdog только сигнализирует — он не трогает диск/контейнер … Авто-очистка — вне объёма ORCH-063 (отдельная задача)»*. **ORCH-062 — и есть эта отдельная задача:** автоматическое освобождение места за счёт build cache, чтобы инцидент 07.06 не повторялся и не требовал ручного вмешательства оператора. **Приоритет:** P1 (риск повторной полной остановки конвейера всех проектов). ## 2. Объём (scope) ### В объёме - Автоматическое периодическое освобождение **docker build cache** на хосте mva154, чтобы он не мог бесконтрольно дорасти до заполнения диска. - Удержание «тёплого» недавнего кэша (политика хранения по возрасту, ориентир из запроса — `until=24h`), чтобы не убивать скорость штатных пересборок. - Наблюдаемость результата авто-prune для оператора (когда последний раз отработал, сколько освобождено / текущий объём build cache). - Обратимость: kill-switch и конфигурируемость периода/порога/политики хранения. - Документирование операционной процедуры в `docs/operations/INFRA.md` (и инфра-требований в `07-infra-requirements.md` — заполняет архитектор). ### Вне объёма - **Очистка прочих «пожирателей» диска** (старые worktree-каталоги `/home/slin/repos/_wt/*` завершённых задач, логи, dangling-образы `docker image prune`) — это **ручная** операция оператора по ORCH-063; авто-уборка этих категорий — отдельные задачи, здесь НЕ делается. - **Изменение поведения disk-watchdog** (`src/disk_watchdog.py`, пороги/алерты ORCH-063) — не трогаем; ORCH-062 ортогонален и комплементарен (watchdog сигналит, pruner убирает). - **Любое управление конвейером / стадиями / Quality Gates.** Авто-prune — операционная фоновая задача, НЕ элемент `STAGE_TRANSITIONS` / `QG_CHECKS` (ровно как watchdog/reconciler/job_reaper). - **Перезапуск/рестарт прод-контейнера** `orchestrator` ради уборки — категорически вне объёма (self-hosting групповой риск). - Выбор между конкретными механизмами реализации (heartbeat-демон в приложении vs host `daemon.json builder.gc` vs host-cron) — это **архитектурное решение** (06-adr), не предмет BRD. ## 3. Заинтересованные стороны - **Owner / оператор (slin, homenet542@gmail.com)** — заказчик, принимает результат, владеет хостом mva154 и его host-prerequisites. - **Все прод-проекты** (`enduro-trails`, `orchestrator`) — косвенно затронуты: общий инстанс, общий диск; падение диска = простой всех. - **Self-hosting контур** — изменение касается инструмента, который работает в проде и обслуживает другие проекты; безопасность изменения критична. ## 4. Бизнес-требования (BR) - **BR-1 (авто-освобождение)** — docker build cache очищается **автоматически, периодически, без ручного вмешательства** оператора, так что он не может бесконтрольно заполнить диск (устранение корня инцидента 07.06). - **BR-2 (удержание тёплого кэша)** — очистка удаляет преимущественно **старый** build cache (политика по возрасту, ориентир `until=24h`); свежий кэш недавних сборок сохраняется, чтобы штатные пересборки не теряли скорость без необходимости. - **BR-3 (self-hosting безопасность)** — операция уборки **никогда не нарушает работу запущенных контейнеров и не удаляет образы/слои, используемые работающими прод-контейнерами**, и **никогда не рестартит/не роняет прод**. Затрагивается **только build cache** (`docker builder prune`), не образы запущенных сервисов. - **BR-4 (наблюдаемость)** — оператор может увидеть состояние авто-prune: включён ли, когда последний раз отработал, объём/освобождено (через тот же канал наблюдаемости, что у фоновых демонов — блок в `GET /queue`, и/или Telegram при значимом освобождении). - **BR-5 (обратимость)** — поведение управляется **kill-switch**: выключение возвращает систему к поведению «как сейчас» 1:1 (никакой авто-уборки), как у `ORCH_DISK_MONITOR_ENABLED` / `ORCH_RECONCILE_ENABLED`. - **BR-6 (конфигурируемость)** — период, порог запуска и политика хранения (возраст/объём удержания) задаются конфигом (env), с безопасными дефолтами; невалидные значения деградируют на дефолт (как валидаторы ORCH-063). ## 5. Нефункциональные требования (NFR) - **NFR-1 (never-raise)** — фоновая уборка не должна ронять процесс/конвейер ни на одном уровне: ошибка docker-команды / недоступность docker.sock / таймаут логируются и проглатываются (как per-tick/per-send never-raise в `disk_watchdog.py`). - **NFR-2 (изоляция от Quality Gate)** — `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / схема БД **не изменяются**; авто-prune — операционный демон/процедура, не гейт. - **NFR-3 (нулевая регрессия при выключении)** — при выключенном kill-switch поведение байт-в-байт как до задачи; никакого фонового потока/процедуры не стартует. - **NFR-4 (низкий оверхед)** — частота уборки — порядка часов; сама команда `docker builder prune` дешева и не должна влиять на латентность конвейера; уборка не должна конкурировать за ресурсы с активными сборками сверх необходимого. - **NFR-5 (best-effort состояние)** — учёт «когда убирали в последний раз» может быть in-memory / best-effort (как анти-спам watchdog'а): сброс при рестарте безопасен (приведёт максимум к одной лишней безопасной уборке), без новой миграции БД. - **NFR-6 (документируемость)** — операционная процедура, env-переменные и поведение при сбое зафиксированы в `docs/operations/INFRA.md` и `.env.example` в том же PR (golden source = код+доки). ## 6. Допущения и ограничения - **A-1.** У контейнера `orchestrator` есть доступ к `/var/run/docker.sock` (через `group_add: ["999"]`, gid docker — НЕ удалять, ORCH-040), что технически позволяет приложению вызывать `docker builder prune`. Это **не предрешает** выбор реализации (демон в приложении vs host-уровень). - **A-2.** `docker builder prune` по контракту docker затрагивает **только build cache**, не останавливает контейнеры и не удаляет образы запущенных сервисов — это основа безопасности BR-3. - **A-3.** Доминирующий «пожиратель» в инциденте — именно build cache (≈11 ГБ); прочие категории (worktree/логи/dangling-образы) адресуются отдельно (см. Вне объёма). - **A-4.** Хост — mva154 (`network_mode: host`), uid рантайма 1000:1000; любые host-prerequisites (например, права на docker.sock, настройка `daemon.json` если выбран этот путь) — процедура Owner, в git не коммитятся (по аналогии с P-1…P-4 в INFRA.md). - **Ограничение C-1.** Нельзя рестартить docker daemon в рабочее время без окна тишины, если выбранный архитектором путь (`daemon.json builder.gc`) требует перезапуска демона — это решает и планирует архитектор/Owner (вне объёма кода). ## 7. Критерии успеха - Build cache на mva154 удерживается в безопасных пределах **автоматически**: после внедрения повторение сценария 07.06 (build cache → 11 ГБ → диск 100%) предотвращается без ручных действий. - Свежие сборки не теряют скорость без необходимости (тёплый кэш ≤ политики хранения сохраняется). - Запущенные прод-контейнеры и обслуживание `enduro-trails` не затронуты; прод не рестартился. - Оператор видит состояние авто-prune и может его выключить одним флагом. - Детальные PASS/FAIL — в `03-acceptance-criteria.md`. ## 8. Риски Краткий перечень (детальная проработка — `10-tech-risks.md`, заполняет архитектор): - **R-1.** Слишком агрессивная политика (`-a` без возрастного фильтра / малый `until`) убивает тёплый кэш → каждая сборка «холодная» и медленная. Митигирует BR-2 (удержание по возрасту). - **R-2.** Гонка уборки с активной сборкой staging/прод-образа (`check_staging_image_fresh`, build-once retag) → теоретически удаление кэша во время сборки. `docker builder prune` штатно не трогает кэш, занятый активной сборкой, но политику/таймиг проверить (адресует архитектор). - **R-3.** Реализация через host-`daemon.json` требует рестарта docker daemon → риск для self-hosting; реализация через демон в приложении требует доступа к docker.sock и устойчивости к его недоступности. - **R-4.** Ошибочное расширение скоупа на `docker image prune` / `system prune` → удаление образов запущенных контейнеров. Жёстко исключено BR-3 (только build cache).