Files
enduro-trails/docs/work-items/ET-015/02-trz.md
claude-bot c2cf8280ca
All checks were successful
CI / lint (push) Successful in 4s
CI / test (push) Successful in 9s
CI / build (push) Successful in 3s
analyst(ET): auto-commit from analyst run_id=101
2026-06-05 15:11:28 +00:00

9.0 KiB
Raw Blame History

ТЗ: Healthcheck enduro-trails-app — заменить curl на python-проверку

Work Item: ET-015 Базовый документ: 01-brd.md Версия: 1.0 Дата: 2026-06-05

1. Постановка

Заменить в docker-compose.yml (сервис app) healthcheck-команду так, чтобы она:

  • использовала средства, уже доступные в образе python:3.12-slim (т.е. интерпретатор python3), без установки дополнительных пакетов;
  • обращалась к http://localhost:5556/api/health и трактовала HTTP-код 2xx как «healthy», любой иной отклик и любую ошибку соединения — как «unhealthy»;
  • укладывалась в timeout: 5s.

2. Текущее состояние

docker-compose.yml, строки 2226:

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:5556/api/health"]
  interval: 30s
  timeout: 5s
  retries: 3

Dockerfile: базовый образ python:3.12-slim. curl отсутствует. Установлен pip, доступен python3 (и алиас python).

src/api/main.py:1224:

@app.get("/api/health")
async def health():
    return {
        "status": "ok",
        "db_path": DATA_PATH,
        "db_exists": os.path.exists(DATA_PATH),
    }

Возвращает HTTP 200 + JSON. Менять не требуется.

3. Целевое состояние

3.1. Изменение в docker-compose.yml

Секция healthcheck сервиса app приводится к виду:

healthcheck:
  test:
    - "CMD"
    - "python"
    - "-c"
    - "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:5556/api/health', timeout=3).status == 200 else 1)"
  interval: 30s
  timeout: 5s
  retries: 3
  start_period: 20s

Пояснения:

  • CMD (а не CMD-SHELL) — никакого shell-парсинга, аргументы передаются как массив, экранирование не нужно.
  • python — алиас, имеющийся в python:3.12-slim (есть и python3, оба указывают на один интерпретатор).
  • urllib.request.urlopen(..., timeout=3) — стандартная библиотека, без зависимостей; внутренний timeout=3 короче внешнего timeout: 5s, остаётся запас на старт интерпретатора.
  • sys.exit(0 if ... == 200 else 1) — корректное преобразование статуса HTTP в exit code. Любой raise (URLError, HTTPError, timeout) пробросится наверх, процесс завершится ненулевым кодом → unhealthy.
  • start_period: 20s — добавляется, чтобы Docker не считал ранние ошибки запуска приложения «провалом» healthcheck в окне старта. Uvicorn поднимается за < 2 c, 20 с — комфортный запас.

3.2. Изменения в Dockerfile

Не требуются. Добавлять curl через apt-get нельзя — раздувает образ и противоречит выбранному подходу.

3.3. Изменения в src/api/main.py

Не требуются. Эндпоинт /api/health существует и отдаёт 200.

4. Альтернативы (рассмотрены и отклонены)

Вариант Плюсы Минусы Решение
apt-get install curl в Dockerfile Привычная команда +~10 МБ к образу, новый APT-слой, противоречит slim-философии Отклонено
wget --spider Однострочник wget тоже отсутствует в python:3.12-slim (проверено: пакет wget не входит в slim) Отклонено
HEALTHCHECK в Dockerfile Декларативно Дублирует compose, при изменении нужно пересобирать образ Отклонено, держим в compose
Отдельный health-скрипт scripts/healthcheck.py Чище YAML Лишний файл для одной строки, мутит образ Отклонено

Принятый вариант: inline python one-liner через urllib.request.

5. Реализационные требования

R-1. Изменение docker-compose.yml

  • В сервисе app секция healthcheck заменяется на конструкцию из п. 3.1.
  • Остальные параметры сервиса (ports, volumes, environment) не затрагиваются.

R-2. Идемпотентность пересборки

  • Изменения не требуют ребилда образа (docker compose build). Достаточно docker compose up -d app для пересоздания контейнера с новой healthcheck-командой.
  • Допускается ребилд при необходимости — это не должно ломать сборку.

R-3. Обратная совместимость

  • Никаких ENV-переменных, влияющих на путь healthcheck, не вводится. Адрес http://localhost:5556/api/health зашит в строку. (Локальный — localhost внутри контейнера; порт всегда 5556, как ENV PORT в Dockerfile.)

R-4. Документация

  • В docs/work-items/ET-015/06-adr/healthcheck-via-python.md зафиксировать решение «использовать python-one-liner вместо curl». Автор ADR — следующий этап (Architecture), не Анализ.
  • Обновить CHANGELOG.md в секции «Unreleased» строкой формата fix(infra): use python urllib for container healthcheck (ET-015).

R-5. Линт и форматирование

  • YAML-валидность docker-compose.yml проверяется make lint.
  • Длина строки python one-liner допустима в YAML (нет лимита 120 для строковых значений multi-line array).

6. Тестирование

См. 04-test-plan.yaml. Кратко:

  • integration-1: после docker compose up -d app контейнер должен выйти в healthy за ≤ 120 с.
  • integration-2: при остановке uvicorn (или искусственном блоке порта) — за ≤ 120 с переходит в unhealthy.
  • unit-1 (опционально): smoke-тест python-one-liner вне Docker через python -c "..." против поднятого локально make dev.

7. Деплой и откат

  • Деплой: make deploy-test (как обычно). При деплое compose пересоздаст контейнер enduro-trails-app-1.
  • Проверка: docker inspect enduro-trails-app-1 --format '{{.State.Health.Status}}'healthy в течение нескольких циклов (interval=30s × 3 = 90s плюс start_period=20s).
  • Откат: git revert коммита; docker compose up -d app. Старая (поломанная) healthcheck-команда вернётся, но сам сервис продолжит работать.

8. Риски

Риск Вероятность Митигация
Python one-liner крэшится на каком-то Docker-движке из-за квотинга низкая YAML-массив ["CMD", "python", "-c", "..."] — без shell, без экранирования
Длинная строка усложняет редактирование средняя Использовать YAML block-scalar (>- или `
Эндпоинт /api/health в будущем сделают «дорогим» и timeout=3s не хватит низкая Эндпоинт сейчас отдаёт ~7 мс; при изменении — пересмотр timeout
На prod-среде iptables/сеть отличаются и localhost внутри контейнера ведёт себя иначе очень низкая localhost в network namespace контейнера = loopback контейнера, не зависит от хоста

9. Definition of Ready (для следующего этапа)

  • BRD прочитан, ТЗ согласовано.
  • Доступ к тестовой среде mva154 для проверки docker inspect.
  • make deploy-test и docker compose доступны из ветки.