# BRD: Healthcheck enduro-trails-app падает: в контейнере нет curl **Work Item:** ET-015 **Тип:** Bugfix / Infrastructure **Приоритет:** Низкий (приложение работает) / Важно для мониторинга **Дата:** 2026-06-05 **Запросил:** Слава ## 1. Контекст Контейнер `enduro-trails-app-1` (запускается из репозитория `enduro-trails`) на тестовой среде `mva154` (https://openclaw.mva154.duckdns.org/enduro/) показывает в Docker статус `unhealthy` уже ~31 час с `FailingStreak=3762`, при том что само приложение работает: - `curl снаружи :5556 → HTTP 200` (~7 мс отклик); - в логах живой трафик `200 OK`; - `RestartCount=0` (контейнер не перезапускался). ## 2. Корень проблемы В `docker-compose.yml` healthcheck настроен как: ```yaml healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5556/api/health"] ``` Базовый образ — `python:3.12-slim` (см. `Dockerfile`). В `slim`-варианте **нет** утилиты `curl`. Каждый цикл healthcheck завершается: ``` exec: "curl": executable file not found in $PATH exit code = -1 ``` Docker трактует это как «проверка провалена» и через `retries=3` помечает контейнер `unhealthy`. На самом деле приложение здорово. Дополнительный факт: эндпоинт `/api/health` **существует** в коде (`src/api/main.py:1224`, отдаёт `{"status": "ok", ...}`), так что двойной поломки (несуществующий путь) нет — проблема исключительно в отсутствии `curl`. ## 3. Бизнес-проблема 1. **Ложные алерты в мониторинге.** Любая система оповещений, опирающаяся на `docker inspect ... .State.Health.Status`, будет постоянно кричать об инциденте, который не существует. 2. **Эрозия доверия к мониторингу.** Если `unhealthy` всегда ложный, его игнорируют — и пропустят настоящий инцидент, когда он случится. 3. **Невозможность построения SLO/SLA.** Метрика «доступность контейнера» деградирована и непригодна для отчётности. ## 4. Цель Healthcheck контейнера `app` должен **честно** отражать состояние приложения: `healthy`, когда HTTP-эндпоинт `/api/health` на `:5556` отвечает `200 OK`; `unhealthy`, когда не отвечает. ## 5. Стейкхолдеры | Роль | Имя / Группа | Интерес | |------|--------------|---------| | Заказчик | Слава | Корректный мониторинг тестовой и будущей prod-среды | | Исполнитель | claude-bot | Реализация фикса | | Эксплуатация | mva154 host owner | Минимальный размер образа, никаких лишних пакетов | ## 6. Ограничения и нефункциональные требования - **Размер образа** не должен заметно расти. Добавление `curl` через `apt-get install` тянет ~10 МБ зависимостей + слой APT-кэша → нежелательно. Предпочтительно использовать то, что уже есть в образе (Python). - **Время выполнения healthcheck** не должно превышать `timeout: 5s` (текущее значение в compose). Реальное время отклика `/api/health` ~7 мс, запас огромный. - **Совместимость** с Docker Engine ≥ 20.10 (на mva154 стоит свежий). - **Никаких изменений** в логике приложения — эндпоинт `/api/health` уже существует и его поведение менять не нужно. ## 7. Out of scope - Доработка содержимого `/api/health` (например, добавление проверки OSRM, тайлов, диска) — отдельный work item, если понадобится. - Healthcheck для сервиса `gps-collector` (batch profile) — у него нет открытого порта и `restart: "no"`, healthcheck неуместен. - Healthcheck-настройки на стороне Gitea Actions / CI. ## 8. Сценарий «как должно стать» 1. Образ собирается без добавления `curl`. 2. `docker compose up -d app` поднимает контейнер. 3. Через ≤ `interval * retries` (= 30s × 3 = 90s, с учётом `start_period` если задан) `docker inspect ... .State.Health.Status` возвращает `healthy`. 4. Если приложение «зависает» (порт не отвечает) — healthcheck честно фиксирует `unhealthy` за то же окно. ## 9. Связи - Затрагивает: `Dockerfile`, `docker-compose.yml`. - Не затрагивает: `src/api/`, `src/web/`, БД, тайлы. - Соседние ADR: глобальных архитектурных решений не требует — локальное инженерное решение, оформляется в `06-adr/` work-item.