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

26 lines
No EOL
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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