
Healthcheck в Docker: как проверять готовность сервиса
Настраиваем HEALTHCHECK для HTTP-сервиса, разбираем starting, healthy и unhealthy, проверяем историю проб и избегаем ложных срабатываний.
Содержание
Статус running означает только, что основной процесс контейнера не завершился. Он не подтверждает, что приложение закончило подготовку данных, открыло сетевой порт и способно обработать запрос. Healthcheck — короткая команда, которую Docker периодически запускает внутри контейнера. По её результату появляются состояния starting, healthy или unhealthy.
Что должна показывать проверка
Проверяйте быстрый локальный endpoint или встроенную диагностическую команду приложения. Установите реалистичные interval, timeout, start_period и retries, затем посмотрите .State.Health через docker inspect. Не ожидайте автоматического перезапуска от одного статуса unhealthy: Docker Engine фиксирует результат, а реакцию должен задавать внешний мониторинг или платформа запуска.
Что именно должен подтверждать healthcheck
Полезная проба отвечает на вопрос: «Этот экземпляр сейчас способен выполнить минимальную основную операцию?» Для HTTP-сервиса это обычно отдельный /health, который быстро возвращает успешный код после завершения инициализации.
Неудачные варианты:
- проверять только наличие процесса, который и так является PID 1;
- загружать тяжёлую страницу с несколькими запросами к базе;
- обращаться к публичному домену через DNS, CDN и reverse proxy;
- всегда возвращать
200, даже когда приложение ещё не готово; - писать в health endpoint подробности конфигурации и секреты.
Docker предоставляет один health status, поэтому в нём приходится аккуратно совместить признаки жизни и готовности. Если отказ внешней системы не делает локальный процесс неисправным, не включайте эту зависимость в каждую пробу. Иначе краткий сетевой сбой отметит здоровые экземпляры как неработающие.
Добавьте HEALTHCHECK в образ
Пример для образа, в котором уже установлен wget и приложение слушает порт 3000:
HEALTHCHECK --interval=30s --timeout=3s --start-period=20s --retries=3 \
CMD wget -q -O /dev/null http://127.0.0.1:3000/health || exit 1
Параметры означают:
interval— пауза между обычными проверками;timeout— максимальная длительность одной попытки;start-period— время на инициализацию, в которое неудачи не увеличивают обычный счётчик отказов;retries— число последовательных неудач доunhealthy.
Команда должна завершаться кодом 0 при успехе и 1 при ошибке. Код 2 зарезервирован. Не добавляйте curl или wget в минимальный production-образ только по привычке: приложение может иметь собственную команду вроде /app/server healthcheck, которая не требует shell и лишних пакетов.
Проверьте поведение на тестовом контейнере
Для опыта достаточно отдельного контейнера Nginx, который не публикует порт наружу. Команда задаёт проверку главной страницы каждые пять секунд и создаёт контейнер health-demo:
docker run -d --name health-demo \
--health-cmd='wget -q -O /dev/null http://127.0.0.1/ || exit 1' \
--health-interval=5s \
--health-timeout=2s \
--health-retries=3 \
nginx:alpine
Через несколько секунд проверьте состояние тремя командами. Первая показывает краткий статус, вторая — текущий результат и число подряд неудачных попыток, третья — историю запусков проверки:
docker ps --filter name=health-demo
docker inspect health-demo \
--format 'status={{.State.Health.Status}} failures={{.State.Health.FailingStreak}}'
docker inspect health-demo --format '{{json .State.Health.Log}}'
История содержит время, exit code и короткий вывод команды. Сохраняйте диагностический текст компактным и не печатайте в него токены, строки подключения и переменные окружения.
События смены состояния можно наблюдать отдельно:
docker events \
--filter container=health-demo \
--filter event=health_status
Остановите просмотр через Ctrl+C; контейнер продолжит работать.
Переопределите проверку в Compose
Compose может задать healthcheck, даже если его нет в образе, или переопределить параметры:
services:
web:
image: example/web:tested
healthcheck:
test: ["CMD", "/app/server", "healthcheck"]
interval: 30s
timeout: 3s
start_period: 20s
retries: 3
В каталоге Compose-проекта проверьте итоговую конфигурацию до запуска. Затем примените её и убедитесь, что таблица сервисов содержит ожидаемое состояние healthcheck:
docker compose config
docker compose up -d
docker compose ps
Если другой сервис использует depends_on с condition: service_healthy, Compose дождётся успешной проверки при старте. Это не отменяет повторные попытки подключения внутри клиента: зависимость может стать недоступной уже после запуска.
Почему unhealthy не равен restart
Healthcheck меняет метаданные работающего контейнера, но не завершает PID 1. Обычные политики always, unless-stopped и on-failure связаны с остановкой процесса, поэтому сами по себе не перезапускают unhealthy.
Не добавляйте скрипт, который без разбора убивает контейнер при первой ошибке. Сначала определите:
- сколько неудач считать подтверждённым отказом;
- не вызван ли результат перегрузкой самого healthcheck;
- безопасен ли автоматический restart для текущей операции;
- кто ограничивает частоту перезапусков;
- куда отправляется уведомление и где остаётся диагностика.
Иногда правильная реакция — вывести экземпляр из балансировки и сохранить его для исследования, а не сразу стереть состояние перезапуском.
Настройте интервалы по реальному времени запуска
Слишком частая проба создаёт лишнюю нагрузку и шум. Слишком большой timeout удерживает зависшие процессы проверки, а короткий даёт ложные ошибки при допустимой задержке. Измерьте обычный и худший запуск на холодном кеше, после миграции и при ограниченных ресурсах.
Проверка должна быть детерминированной, укладываться в небольшую долю interval и не менять данные. Для базы предпочтительнее лёгкий запрос чтения или встроенная команда готовности, а не создание и удаление рабочих записей.
Как откатить неудачную проверку
Если новый healthcheck даёт ложные отказы, сначала сохраните docker inspect и журнал приложения. Затем верните предыдущий образ или Compose-конфигурацию в compose.yaml и пересоздайте только сервис web без перезапуска его зависимостей:
docker compose up -d --no-deps web
docker compose ps web
Временное healthcheck: { disable: true } допустимо для диагностики, но не должно незаметно остаться постоянным решением. Исправьте endpoint или интервалы и повторите тест отказа.
Базовые команды диагностики разобраны в материале о run, logs и inspect. Связь healthcheck с порядком запуска показана в руководстве по Compose, а влияние ограниченных ресурсов рассматривается в статье о CPU и памяти.
Источники
Самопроверка
Проверьте, что материал усвоен
Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.
Разбираем коротко
Частые вопросы
Перезапускает ли Docker контейнер со статусом unhealthy?
Сам healthcheck только меняет состояние здоровья контейнера и создаёт событие. Обычная restart policy реагирует на завершение основного процесса, а не на unhealthy. Реакцию нужно проектировать отдельно.
Должен ли healthcheck проверять базу данных и все внешние API?
Обычно нет. Проверка должна отражать способность данного сервиса выполнять свою основную работу, но не превращать краткий сбой зависимости в каскад ложных отказов.
Почему контейнер долго остаётся в состоянии starting?
Пока не завершилась успешная проба или не накопились учитываемые неудачи, Docker показывает starting. Проверьте start_period, interval, timeout и фактическое время инициализации приложения.


