opencode-config/docs/handoff/pr-132-fix-docker-opencode-limits.md
Sergey e72d435b82
fix(docker): increase opencode limits and add tini reaper + healthcheck (#132)
* 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>
2026-07-29 21:19:01 +03:00

3.3 KiB
Raw Blame History


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 зомби-процессов (<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 не заберёт — но это всё равно убирает основную массу.