140 lines
12 KiB
Markdown
140 lines
12 KiB
Markdown
---
|
||
work_item: ORCH-062
|
||
stage: analysis
|
||
author_agent: analyst
|
||
status: ready-for-review
|
||
created_at: 2026-06-09
|
||
model_used: claude-opus-4-8
|
||
---
|
||
|
||
# 02 — ТЗ (TRZ): ORCH-062 — INFRA: авто-prune docker build cache на mva154
|
||
|
||
Work Item: **ORCH-062** · Repo: **orchestrator** · Стадия: analysis
|
||
|
||
> ТЗ описывает **требуемое поведение и точки изменения**, выведенные из BRD и фактического кода.
|
||
> **Выбор механизма реализации — за архитектором (`06-adr`).** Запрещено комментировать ТЗ задним
|
||
> числом: если требование не годится — вернуть в Анализ.
|
||
|
||
## 1. Сводка изменения
|
||
|
||
Ввести **автоматическое периодическое освобождение docker build cache** на хосте mva154, чтобы
|
||
build cache не мог дорасти до заполнения диска (корень инцидента 07.06.2026, ≈11 ГБ → диск 100% →
|
||
падение CI+Gitea+конвейера всех проектов). Это комплемент к disk-watchdog (ORCH-063, «только
|
||
сигнал»): watchdog предупреждает, **pruner убирает**. Требование — безопасно для self-hosting
|
||
(только build cache, без рестарта прода, never-raise), обратимо (kill-switch), наблюдаемо (`GET
|
||
/queue`) и конфигурируемо.
|
||
|
||
**Развилка реализации (решает архитектор, фиксируется в `06-adr` + `07-infra-requirements.md`):**
|
||
- **Вариант A — heartbeat-демон в приложении:** новый leaf-модуль, фоновый
|
||
`threading.Thread(daemon=True)`, моделируемый **1:1 на `src/disk_watchdog.py`**
|
||
(`start()/stop()/status()`, `threading.Event`, per-tick never-raise, kill-switch, блок в `GET
|
||
/queue`), который периодически вызывает `docker builder prune` через docker.sock.
|
||
- **Вариант B — host-уровень `daemon.json builder.gc.defaultKeepStorage`:** конфигурация
|
||
garbage-collection BuildKit на хосте (инфра-процедура Owner, без кода приложения).
|
||
- **Вариант C — host-cron** `docker builder prune -af --filter until=24h` (инфра-процедура Owner).
|
||
|
||
ТЗ ниже формулирует требования **инвариантно к выбору**; колонка «применимость» в §2 помечает, что
|
||
именно затрагивается при code-пути (Вариант A). Если архитектор выбирает чистый инфра-путь (B/C),
|
||
изменения `src/**` не требуются, а предметом становятся `07-infra-requirements.md` + INFRA.md +
|
||
host-процедура (см. §7, §5 теста).
|
||
|
||
## 2. Задействованные модули / пути
|
||
|
||
| Путь | Действие | Применимость |
|
||
|------|----------|--------------|
|
||
| `src/build_cache_pruner.py` (новый leaf) | создать: фоновый демон-pruner по образцу `src/disk_watchdog.py` | Вариант A |
|
||
| `src/config.py` | добавить флаги kill-switch/период/политика хранения (блок рядом с `disk_monitor_*`, строки ~392–442) + валидаторы | Вариант A (часть флагов — и для B/C как декларация) |
|
||
| `src/main.py` | в `lifespan` — `start()`/`stop()` нового демона рядом с `disk_watchdog.start()/stop()` (строки ~113–120); в `GET /queue` — блок наблюдаемости рядом с `"disk_monitor": disk_watchdog.status()` (строка ~186) | Вариант A |
|
||
| `.env.example` | задокументировать новые env-переменные (канон) | A / B / C (декларация) |
|
||
| `docs/operations/INFRA.md` | секция «авто-prune build cache» + переменные в карте env; уточнить, что освобождение build cache теперь автоматизировано (ORCH-063 говорил «ручная операция») | A / B / C (обязательно) |
|
||
| `docs/work-items/ORCH-062/06-adr/ADR-001-*.md` | решение по выбору механизма + параметрам (архитектор) | A / B / C |
|
||
| `docs/work-items/ORCH-062/07-infra-requirements.md` | host-prerequisites/процедура (docker.sock / daemon.json / cron) (архитектор) | A / B / C |
|
||
| `tests/test_build_cache_pruner.py` (новый) | unit/integration по `04-test-plan.yaml` | Вариант A |
|
||
| `CHANGELOG.md` | запись в `## [Unreleased]` | A / B / C |
|
||
|
||
> Модуль-pruner должен быть **leaf** (как `disk_watchdog.py`, `serial_gate.py`, `task_deps.py`):
|
||
> без обратных зависимостей на `stage_engine`/`stages`/`qg`, чтобы не задевать конвейер.
|
||
|
||
## 3. Функциональные требования
|
||
|
||
### FR-1 — периодическая авто-уборка build cache (BR-1)
|
||
Build cache очищается автоматически по расписанию/периодически без участия оператора. Для code-пути
|
||
(A): фоновый поток с периодом `prune_interval_s` (порядка часов) вызывает уборку каждый тик. Для
|
||
инфра-пути (B/C): garbage-collection BuildKit / cron обеспечивают эквивалентную периодичность.
|
||
Привязка: BR-1.
|
||
|
||
### FR-2 — политика удержания тёплого кэша (BR-2)
|
||
Уборка по умолчанию удаляет **старый** build cache, удерживая свежий. Ориентир из бизнес-запроса —
|
||
возрастной фильтр `--filter until=24h` (для пути A: команда вида `docker builder prune -f --filter
|
||
until=<retention>`), либо порог объёма `builder.gc.defaultKeepStorage` (для пути B). Параметры
|
||
удержания конфигурируемы (см. §ниже). Флаг `-a/--all` применять **только** в сочетании с возрастным
|
||
фильтром/политикой удержания, не как «снести весь кэш». Привязка: BR-2.
|
||
|
||
### FR-3 — self-hosting-безопасность операции (BR-3, NFR-2)
|
||
- Уборка затрагивает **исключительно build cache** — команда строго `docker builder prune`
|
||
(BuildKit GC). **Запрещены** `docker image prune`, `docker system prune`, любое удаление образов
|
||
запущенных сервисов и любая остановка/рестарт контейнеров.
|
||
- Операция **никогда не рестартит и не роняет прод-контейнер** `orchestrator` (групповой риск
|
||
self-hosting).
|
||
- Для пути A: вызов docker — неблокирующий конвейер, с таймаутом; недоступность docker.sock →
|
||
пропуск тика (never-raise).
|
||
- Привязка: BR-3, NFR-1, NFR-2.
|
||
|
||
### FR-4 — наблюдаемость (BR-4)
|
||
Состояние авто-prune доступно оператору. Для пути A — блок в `GET /queue` (как `disk_monitor`):
|
||
`enabled`, `interval_s`, `retention`, `last_run_ts`, и (best-effort) результат последней уборки
|
||
(освобождено байт / текущий объём build cache, если доступно из `docker builder prune`/`du`).
|
||
Опционально — Telegram-сообщение при значимом освобождении (как recovery-сообщение watchdog'а).
|
||
Для пути B/C — наблюдаемость через хост (`docker system df`), описанная в INFRA.md. Привязка: BR-4.
|
||
|
||
### FR-5 — kill-switch + конфигурируемость (BR-5, BR-6, NFR-3)
|
||
- `*_enabled` (kill-switch, дефолт безопасный): выключено → демон не стартует (путь A) / процедура
|
||
неактивна; поведение 1:1 как до задачи (NFR-3).
|
||
- Конфигурируемые: период (`*_interval_s`), политика удержания (возраст `until` и/или объём
|
||
`keep_storage`), опц. порог запуска. Невалидные значения → лог-warning + дефолт (как валидаторы
|
||
`disk_monitor_interval_s`/`disk_monitor_threshold_pct` в `config.py`).
|
||
- Область раската — безопасная: операция привязана к хосту mva154; не вводит per-repo гейтов.
|
||
- Привязка: BR-5, BR-6.
|
||
|
||
### FR-6 — never-raise на всех уровнях (NFR-1)
|
||
Любая ошибка (subprocess-сбой, ненулевой rc, таймаут, недоступность docker.sock, parsing-ошибка
|
||
вывода) логируется и проглатывается; фоновый цикл/процедура продолжает жить и не влияет на
|
||
конвейер. Для пути A — `try/except` per-tick и per-команда, как `_run`/`tick`/`_send` в
|
||
`disk_watchdog.py`. Привязка: NFR-1, NFR-5.
|
||
|
||
## 4. Изменения API
|
||
|
||
**Внешних HTTP-эндпоинтов оркестратора (`src/main.py`) НЕ добавлять и не менять контрактно.**
|
||
Допустимо (путь A): `GET /queue` дополнить **read-only** блоком `build_cache_pruner`/аналогичным
|
||
ключом (наблюдаемость, не источник истины) — по образцу блока `disk_monitor`. Внутренний контракт
|
||
нового модуля (путь A) — `start()` / `stop(timeout)` / `status() -> dict`, 1:1 как `DiskWatchdog`.
|
||
|
||
## 5. Изменения схемы БД
|
||
|
||
**Нет.** Схема БД (`src/db.py`) не трогается. Учёт «времени последней уборки» — in-memory /
|
||
best-effort (NFR-5), новой миграции не требуется (как анти-спам-состояние disk-watchdog).
|
||
|
||
## 6. Требования к новым/изменённым QG checks
|
||
|
||
**Нет.** `QG_CHECKS` / `check_*` / `_parse_*` / `STAGE_TRANSITIONS` / `src/stage_engine.py` **не
|
||
изменяются**. Авто-prune — операционный фоновый демон/процедура (категория `reconciler` /
|
||
`job_reaper` / `disk_watchdog`), **не** элемент реестра Quality Gate.
|
||
|
||
## 7. Совместимость / регресс · артефакты pipeline
|
||
|
||
- **Обратная совместимость / обратимость:** kill-switch (FR-5) выключает фичу в 1:1-исходное
|
||
состояние; никаких изменений поведения для `enduro-trails` и для конвейера (демон ортогонален).
|
||
- **Область раската:** только хост mva154 / self-hosting инстанс; фича не вводит per-repo гейтов и
|
||
не меняет рёбер конвейера.
|
||
- **Артефакты pipeline, которые должны быть созданы/обновлены:**
|
||
- `06-adr/ADR-001-*.md` — выбор механизма (A/B/C) + параметры удержания/периода (архитектор).
|
||
- `07-infra-requirements.md` — host-процедура: доступ к docker.sock (A) / правка `daemon.json` +
|
||
окно рестарта docker daemon (B) / cron-юнит (C) (архитектор).
|
||
- `10-tech-risks.md` — детализация R-1…R-4 из BRD (архитектор).
|
||
- `docs/operations/INFRA.md` — секция авто-prune + карта env; снять формулировку ORCH-063
|
||
«освобождение места — ручная операция» в части build cache.
|
||
- `.env.example` — новые переменные.
|
||
- `CHANGELOG.md` — `## [Unreleased]`.
|
||
- `12-review.md`, `13-test-report.md`, `14-deploy-log.md`, `15-staging-log.md` — по ходу конвейера.
|
||
- `tests/` — реализовать тесты из `04-test-plan.yaml` (путь A).
|