* fix(docker): increase opencode memory and cpu limits * fix(docker): add tini init for zombie reaping * fix(docker): add healthcheck for hang auto-restart * test(docker): assert opencode limits, init and healthcheck * docs(handoff): add handoff and ADR for opencode limits fix * docs(handoff): set PR number * docs(project-map): update docker-compose and test descriptions for PR#132 --------- Co-authored-by: opencode-agent <agent@opencode.local>
3.3 KiB
pr: 132 title: fix(docker): increase opencode limits and add tini reaper + healthcheck
Что сделано
docker-compose.ymlсервисopencode:limits.memory6G → 8G,limits.cpus'3' → '4',limits.pids1024 → 2048- Добавлен
init: trueна уровне сервиса (tini как PID 1 — reaping осиротевших зомби) - Добавлен
healthcheck:CMD-SHELLcurl к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 зомби-процессов (<defunct>), 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 не заберёт — но это всё равно убирает основную массу.