--- pr: 132 title: fix(docker): increase opencode limits and add tini reaper + healthcheck --- ## Что сделано - `docker-compose.yml` сервис `opencode`: `limits.memory` 6G → 8G, `limits.cpus` '3' → '4', `limits.pids` 1024 → 2048 - Добавлен `init: true` на уровне сервиса (tini как PID 1 — reaping осиротевших зомби) - Добавлен `healthcheck`: `CMD-SHELL` curl к `localhost:4096`, допускает 200|401 как healthy (serve требует basic auth → 401 без credentials), `interval: 30s`, `timeout: 10s`, `retries: 3`, `start_period: 30s`. Независание → Docker рестартует через существующий `restart: unless-stopped` - `restart: unless-stopped` НЕ дублирован (уже был на строке 25) - Сервис `dind` — БЕЗ ИЗМЕНЕНИЙ - `tests/test_docker_compose.py`: +8 raw-text assertions (memory/cpu/pids limits, init:true, healthcheck block + timing, restart-not-duplicated, dind-unchanged). Итого 13 passed (5 baseline + 8 новых) - ADR-059 создан ## Почему Живая диагностика cgroup v2 внутри зависшего контейнера: `memory.peak` 5.72 GB из 6 GB (95%), `oom_kill`=0 → ядро делало direct reclaim (синхронный scan/free страниц в event loop) → Node.js event loop блокировался → `opencode serve` зависал (504 + не отвечал по IP). CPU: `nr_throttled`=879, `throttled_usec`=104.7s при лимите 3 ядра. >120 зомби-процессов (``), opencode — PID 1, не вызывает `wait()`. Swap на хосте отключён (`Swap: 0B`). 8G даёт +2.3 GB над пиком 5.72 GB; 4 ядра убирают троттлинг (на хосте 6 ядер); tini reaping зомби; healthcheck → авто-рестарт при зависании. ## Pending — Развёртывание на проде и наблюдение: подтвердить что direct-reclaim stalls и троттлинг исчезли, RSS-рост не упирается в новый 8G лимит. Если упрётся — копать в сторону утечки MCP-серверов (5 дубликатных групп puppeteer+context7, ~30 процессов, ~500 MB) — это вне scope данного issue. — Swap на хосте linux-1 остаётся отключённым — ops-задача (вне репо). ## Watch out — Healthcheck использует `CMD-SHELL` (не `CMD`) — нужен pipe `| grep -qE '^(200|401)$'` для допуска 401. В exec-форме (`CMD`) pipe не работает. `$$` в compose экранирует `$` для shell. — `curl -sf` трактует 401 как не-2xx → ложный unhealthy. Поэтому grep на 200|401, а не `-sf`. — Docker недоступен на CI-хосте — `docker compose config` не запускается; YAML валидирован через PyYAML. — `init: true` reaping'ит только осиротевших зомби (родитель умер). Прямых детей живого opencode tini не заберёт — но это всё равно убирает основную массу.