Healthcheck в Docker Compose: как отличить запущенный контейнер от реально работающего сервиса

Контейнер со статусом Up в выводе docker ps означает лишь одно: процесс внутри запущен. Приложение может зависнуть на инициализации, потерять соединение с базой данных или отвечать ошибками на каждый запрос — контейнер при этом остаётся «живым». Healthcheck решает эту проблему: Docker периодически выполняет команду проверки внутри контейнера и по её коду возврата определяет, действительно ли сервис работает.

Синтаксис healthcheck в Compose-файле​


Секция healthcheck объявляется внутри определения сервиса. Минимальный рабочий пример:

YAML:
services:
  web:
    image: nginx:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Поле test задаёт команду проверки. Два формата записи:

  • Exec-форма (массив): ["CMD", "curl", "-f", "http://localhost"] — команда выполняется напрямую без оболочки.
  • Shell-форма: ["CMD-SHELL", "curl -f http://localhost || exit 1"] — команда передаётся в /bin/sh -c. Удобна, когда нужны пайпы, перенаправления или логические операторы.

Если test установлен в ["NONE"], healthcheck отключается — это полезно, когда базовый образ содержит проверку, которая не подходит для вашего сценария.

Параметры таймингов и их влияние на поведение​


ПараметрЗначение по умолчаниюЧто контролирует
interval30sПауза между последовательными проверками
timeout30sМаксимальное время ожидания ответа от команды проверки
retries3Сколько подряд неудачных проверок нужно для перехода в статус unhealthy
start_period0sОкно после старта контейнера, в течение которого неудачные проверки не засчитываются
start_interval5sИнтервал проверок внутри start_period (доступен в новых версиях Compose)

start_period критичен для сервисов с долгой инициализацией — например, PostgreSQL, который восстанавливает данные из WAL, или Java-приложения с тяжёлым спринг-контекстом. Без него контейнер может получить статус unhealthy ещё до того, как приложение успело подняться.

start_interval позволяет опрашивать сервис чаще в начальной фазе, не увеличивая нагрузку в штатном режиме. Например, start_interval: 5s при interval: 30s даёт быстрый детект готовности сразу после старта. Параметр поддерживается в актуальных версиях Compose; в более ранних он игнорируется.

Статусы контейнера и их смысл​


После запуска контейнер проходит через несколько состояний здоровья:

  • starting — контейнер запущен, но start_period ещё не истёк. Неудачные проверки не влияют на статус.
  • healthy — последняя проверка (или серия проверок) завершилась с кодом 0.
  • unhealthy — количество подряд неудачных проверок достигло retries.
  • none — healthcheck не определён или отключён через NONE.

Посмотреть текущий статус и вывод последней проверки:

Bash:
docker inspect --format='{{.State.Health.Status}}' <container>
docker inspect --format='{{json .State.Health.Log}}' <container> | jq .

В Health.Log хранится массив последних проверок с таймстампом, кодом возврата и выводом stdout/stderr — это первое место, куда стоит смотреть при диагностике.

Интеграция с depends_on: запуск по готовности, а не по старту​


Короткая форма depends_on гарантирует только порядок создания контейнеров. Compose запустит зависимый сервис сразу после старта зависимости, не дожидаясь её готовности. Длинная форма с condition: service_healthy меняет поведение: зависимый сервис не стартует, пока зависимость не перейдёт в healthy.

YAML:
services:
  db:
    image: postgres:18
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
      interval: 5s
      timeout: 5s
      retries: 5
      start_period: 10s

  web:
    build: .
    depends_on:
      db:
        condition: service_healthy

Доступные значения condition:

  • service_started — эквивалент короткой формы, ждёт только запуска процесса.
  • service_healthy — ждёт перехода зависимости в статус healthy. Требует, чтобы у зависимости был определён healthcheck.
  • service_completed_successfully — ждёт успешного завершения контейнера (код 0). Подходит для init-джобов и миграций.

Если у зависимости нет healthcheck, а указан condition: service_healthy, Compose выдаст ошибку конфигурации.

Начиная с Docker Compose 2.20.0 доступен параметр required: false внутри depends_on. При таком значении Compose не блокирует запуск зависимого сервиса, если зависимость не стала здоровой, а лишь выдаёт предупреждение. Это полезно для опциональных сервисов, без которых приложение может работать в деградированном режиме.

Практические примеры проверок для популярных сервисов​


PostgreSQL​


YAML:
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
  interval: 10s
  timeout: 5s
  retries: 5

pg_isready проверяет, принимает ли сервер соединения, не требуя аутентификации. Двойной $$ экранирует переменную от подстановки на уровне Compose — значение подставится из переменных окружения контейнера.

Redis​


YAML:
healthcheck:
  test: ["CMD", "redis-cli", "ping"]
  interval: 10s
  timeout: 3s
  retries: 3

redis-cli ping возвращает PONG с кодом 0, если сервер отвечает. Если настроен пароль, добавьте -a <password> или используйте переменную окружения.

Nginx / HTTP-сервисы​


YAML:
healthcheck:
  test: ["CMD-SHELL", "curl -sf http://localhost/health || exit 1"]
  interval: 15s
  timeout: 5s
  retries: 3

Флаг -f заставляет curl вернуть ненулевой код при HTTP-статусах 400+. Если в образе нет curl, подойдёт wget -q --spider http://localhost/health.

Проверка через скрипт внутри образа​


Для сложной логики (проверка нескольких эндпоинтов, валидация ответа, проверка дискового пространства) лучше положить скрипт в образ:

YAML:
healthcheck:
  test: ["CMD", "/app/healthcheck.sh"]
  interval: 30s
  timeout: 10s
  retries: 3

Скрипт должен возвращать 0 при успехе и любой ненулевой код при проблеме. Это даёт полный контроль над диагностикой и позволяет логировать причину сбоя в stdout — вывод попадёт в Health.Log.

Типичные ошибки и как их избежать​


Проверка не учитывает время инициализации. Без start_period тяжёлый сервис получает unhealthy до завершения запуска, и зависимые контейнеры не стартуют. Решение: оцените реальное время старта и задайте start_period с запасом.

Команда проверки отсутствует в образе. curl есть не во всех образах — в alpine-вариантах его часто нет. Проверьте состав образа или используйте wget, который обычно присутствует в Alpine.

Проверка проходит, но сервис не работает. Например, curl получает 200 от nginx, но backend-приложение за прокси уже упало. Healthcheck должен проверять реальный путь запроса, а не только наличие процесса.

Слишком агрессивные тайминги. interval: 1s с timeout: 1s создаёт нагрузку на контейнер и может давать ложные срабатывания при кратковременных задержках. Для большинства сервисов interval: 10–30s достаточен.

Зависимость от depends_on без healthcheck. Если сервис-зависимость не имеет healthcheck, условие service_healthy невозможно. Compose не будет ждать готовности — только факта запуска.

Shell-форма в distroless-образах. Если образ собран без /bin/sh (например, distroless), CMD-SHELL не сработает. Используйте exec-форму CMD с прямым указанием бинарника.

Диагностика проблем с healthcheck​


Если контейнер не переходит в healthy:

  1. Проверьте статус и лог проверок:

Bash:
docker inspect --format='{{json .State.Health}}' <container> | jq .

  • Выполните команду проверки вручную внутри контейнера:

Bash:
docker exec <container> curl -sf http://localhost/health

  • Убедитесь, что необходимые утилиты присутствуют в образе:

Bash:
docker exec <container> which curl

  • Проверьте, не блокирует ли start_period переход: если контейнер в статусе starting, проверки ещё не засчитываются.
  • Если используется CMD-SHELL, убедитесь, что /bin/sh существует в образе. В distroless-образах его нет — используйте exec-форму CMD.
  • Проверьте права пользователя: если основной процесс запущен от непривилегированного пользователя, команда проверки выполняется с теми же правами и может не иметь доступа к нужным ресурсам.

Healthcheck и автоматический рестарт​


Сам по себе статус unhealthy не перезапускает контейнер. В standalone Docker и Docker Compose смена статуса здоровья — это сигнал, а не триггер для действия. Директива restart: unless-stopped или restart: always перезапускает контейнер только при его остановке, а не при переходе в unhealthy.

Для автоматического рестарта при unhealthy в Compose-среде обычно используют внешний watchdog-скрипт, который опрашивает статус контейнеров и выполняет docker compose restart при необходимости. В оркестраторах уровня Kubernetes liveness-пробы управляют жизненным циклом подов напрямую, и аналогичная логика встроена в платформу.

Ограничения и нюансы спецификации​


  • Healthcheck выполняется внутри контейнера. Он не может проверить сетевую доступность сервиса снаружи или корректность маппинга портов.
  • Команда проверки работает с тем же пользователем, что и основной процесс (если не переопределено через USER в Dockerfile). Если процесс запущен от непривилегированного пользователя, проверка может не иметь доступа к нужным ресурсам.
  • start_interval поддерживается в актуальных версиях Compose. В более ранних версиях параметр игнорируется без ошибки.
  • При использовании depends_on с condition: service_healthy и required: false (Compose 2.20.0+) Compose не блокирует запуск зависимого сервиса, если зависимость не стала здоровой, а лишь выдаёт предупреждение.
  • Healthcheck не заменяет внешний мониторинг. Он фиксирует состояние сервиса в момент проверки, но не отслеживает деградацию между проверками. Для продакшена комбинируйте его с метриками и алертами.

Выбор стратегии проверки под задачу​


ЗадачаЧто проверятьФормат
Сервер принимает соединенияpg_isready, redis-cli pingExec-форма
HTTP-эндпоинт отвечает корректноcurl -sf на health-эндпоинтShell-форма
Приложение обрабатывает запросыСкрипт с реальным запросом и валидацией ответаСкрипт в образе
Зависимость готова к работеdepends_on + condition: service_healthyCompose-конфигурация
Init-джоб завершилсяcondition: service_completed_successfullyCompose-конфигурация

Ключевой принцип: проверяйте то, что реально важно для потребителя сервиса. Если веб-приложение ходит в базу данных, healthcheck базы должен подтверждать готовность принимать запросы, а не просто наличие процесса.

Источники​


 
Назад
Верх Низ