Новинка Виртуальный VDS сервер в Нидерландах от 390р
Выберите продукт

Docker HEALTHCHECK правильно: шаблоны проверок и связка с restart‑политиками

HEALTHCHECK в Docker — недооценённый инструмент стабильности. От корректной проверки зависит запуск зависимостей, обновления без даунтайма и сигналы мониторингу. В статье — практика: параметры, шаблоны HTTP/TCP/CLI, частые ошибки и связка с restart policy, Compose и деплоем на VDS.
Docker HEALTHCHECK правильно: шаблоны проверок и связка с restart‑политиками

HEALTHCHECK в Docker — это встроенный механизм, который периодически запускает команду внутри контейнера и оценивает его состояние: healthy, starting или unhealthy. При правильно заданной проверке вы получаете предсказуемый старт зависимостей, аккуратные rolling‑обновления, а мониторинг получает корректные сигналы о деградации. Но по умолчанию restart‑политики Docker не привязаны к состоянию health. В этой статье разберём, как писать проверку так, чтобы она была быстрой и надёжной, какие бывают шаблоны для HTTP/TCP/CLI‑проверок, и как интегрировать результат с restart policy и процессами деплоя на VDS.

Как работает HEALTHCHECK в Docker

Механика проста: вы объявляете команду проверки и параметры периодичности в Dockerfile или docker-compose. Демон запускает её по расписанию. Если команда возвращает код выхода 0 — контейнер считается healthy. Любой ненулевой код трактуется как unhealthy. Пока контейнер не прошёл ни одной успешной проверки, статус будет starting (на него влияет start-period).

Ключевые параметры:

  • --interval — как часто запускать проверку (например, 30s).
  • --timeout — сколько ждать завершения проверки (например, 3s).
  • --retries — сколько подряд неуспешных запусков нужно, чтобы пометить как unhealthy.
  • --start-period — льготный период «прогрева», в который фейлы не учитываются.

Посмотреть статус можно командами:

docker ps

docker inspect --format '{{json .State.Health}}' <container>

docker inspect <container> | grep -A5 Health

Главные принципы грамотного healthcheck

  • Проверка должна быть быстрой (миллисекунды — единицы секунд), иначе она сама станет проблемой.
  • Проверяйте «готовность» сервиса, а не «живость» процесса. Открытый порт не значит, что приложение готово обслуживать запросы.
  • Изолируйте зависимости проверки. В идеале команда опирается только на то, что уже есть в вашем образе.
  • Локальность прежде всего: используйте 127.0.0.1 или localhost, избегайте DNS внешних доменов и внешних сетей (если нужна надёжность сети, поможет настройка Docker firewall: iptables и nftables).
  • Подбирайте параметры под реальное поведение приложения: учтите прогрев JIT/кэшей/миграций БД.
  • Логируйте кратко. Вывод проверки попадает в метаданные контейнера и логи; длинные сообщения будут мешать.

Цель healthcheck — повысить стабильность, а не «наказать» контейнер. Избыточно строгая проверка срежет доступность не хуже реального падения.

Примеры HEALTHCHECK: HTTP, TCP и CLI‑проверки

Шаблоны проверок: HTTP, TCP, CLI

HTTP‑проверка сервиса

Базовый случай для веб‑приложений: запрос к локальному эндпоинту, который быстро отвечает 200 OK. Не используйте тяжёлые маршруты, рендер и работу с БД, если можно иметь облегчённую точку /health или /ready.

# Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=20s --retries=3 CMD curl -fsS http://127.0.0.1:8080/health || exit 1

Если в образе нет curl, допустим wget -q --spider, либо перенос проверки в скрипт:

# Dockerfile
COPY health.sh /usr/local/bin/health.sh
RUN chmod +x /usr/local/bin/health.sh
HEALTHCHECK --interval=30s --timeout=3s --start-period=20s --retries=3 CMD /usr/local/bin/health.sh
# /usr/local/bin/health.sh
#!/bin/sh
URL="${HEALTHCHECK_URL:-http://127.0.0.1:8080/health}"
exec wget -q --spider "$URL"

TCP‑проверка порта

Когда у приложения нет HTTP‑эндпоинта (SMTP, Redis, кастомные TCP‑сервисы), можно проверять установку TCP‑соединения.

# BusyBox/Alpine: netcat-openbsd или netcat
HEALTHCHECK --interval=20s --timeout=2s --retries=5 CMD nc -z -w 2 127.0.0.1 6379

Альтернатива без утилит — bash c /dev/tcp:

HEALTHCHECK --interval=20s --timeout=2s --retries=5 CMD bash -c 'echo >/dev/tcp/127.0.0.1/6379'

CLI‑проверка «готовности» СУБД

Для PostgreSQL и MySQL есть быстрые встроенные команды готовности:

# PostgreSQL
HEALTHCHECK --interval=10s --timeout=2s --retries=5 CMD pg_isready -U "$POSTGRES_USER" -h 127.0.0.1 -p 5432

# MySQL/MariaDB
HEALTHCHECK --interval=10s --timeout=2s --retries=5 CMD mysqladmin ping -h 127.0.0.1 -u "$MYSQL_USER" --silent

Если вы храните пароль в окружении, используйте заранее подготовленный .my.cnf или переменные окружения клиента БД, чтобы не светить секреты в аргументах.

Шаблон для приложений с зависимостями

Иногда приложение «готово» только если оно успешно подсоединяется к БД/кэшу. Дешёвый вариант — проверка своей же внутренней readiness‑логики через облегчённый HTTP‑эндпоинт. Если такого нет — небольшая оболочка‑скрипт:

# /usr/local/bin/app-health.sh
#!/bin/sh
# 1) порт
nc -z -w 1 127.0.0.1 8080 || exit 1
# 2) быстрая проверка зависимостей
pg_isready -U "$POSTGRES_USER" -h "$DB_HOST" -p 5432 || exit 1
# 3) опционально: проверка очереди/кэша
redis-cli -h "$REDIS_HOST" PING | grep -q PONG || exit 1
exit 0

Минимизируйте число внешних вызовов и избегайте DNS, чтобы сама проверка не становилась точкой отказа.

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

  • HTTP‑приложение: interval=15–30s, timeout=2–3s, retries=3, start-period=20–60s.
  • БД и брокеры (быстрая готовность): interval=10–20s, timeout=1–2s, retries=5, start-period по старту демона.
  • Тяжёлый прогрев (миграции/кеши): увеличьте start-period до реального worst‑case; не завышайте timeout, лучше повышайте retries.

Не пытайтесь «лечить» медленную проверку большим timeout. Лучше сделайте проверку проще и быстрее.

HEALTHCHECK и restart policy: что работает, а что нет

Важно понимать: в Docker здоровье контейнера не вызывает его автоматический перезапуск. Restart policy (no, on-failure, always, unless-stopped) реагирует только на выход основного процесса. Поэтому «контейнер unhealthy» и «контейнер перезапускается» — это разные события по умолчанию.

Рабочие связки

  • Порядок запуска в Compose через depends_on и service_healthy. Вы можете заставить сервис B стартовать только после того, как сервис A стал healthy. Это решает проблему гонок по старту зависимостей.
  • Грейс‑переключение при обновлениях. В Docker Swarm и некоторых стратегиях деплоя обновления ждут статуса healthy, прежде чем выключать старый экземпляр. Корректный healthcheck — ключ к нулевому даунтайму.
  • Авто‑рестарт при деградации через внешний «сторож». Если вам нужен рестарт при unhealthy, добавьте процесс, который подписывается на события Docker и перезапускает контейнеры при смене статуса. Это может быть отдельный контейнер‑watcher или системный юнит на хосте.

Пример: depends_on с ожиданием service_healthy

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -h 127.0.0.1 -p 5432"]
      interval: 10s
      timeout: 2s
      retries: 5
      start_period: 20s

  app:
    build: .
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

Такой подход исключает преждевременный старт app, когда db ещё в starting или unhealthy.

FastFox VDS
Облачный VDS-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды

Пример «сторожа», который перезапускает unhealthy

Минимальный скрипт на хосте, слушающий события Docker. Он безопаснее, чем попытки «самоубийства» внутри контейнера.

# /usr/local/bin/docker-autoheal.sh
#!/bin/sh
# Требуется доступ к сокету Docker: /var/run/docker.sock

docker events --format '{{json .}}' | while read -r line; do
  echo "$line" | grep -q '"status":"health_status"' || continue
  id=$(echo "$line" | sed -n 's/.*"id":"\([a-f0-9]\{12\}\)".*/\1/p')
  status=$(echo "$line" | sed -n 's/.*health_status: \([a-z]*\).*/\1/p')
  [ "$status" = "unhealthy" ] || continue
  name=$(docker inspect --format '{{.Name}}' "$id" | sed 's#^/##')
  echo "autoheal: restarting $name ($id) due to unhealthy"
  docker restart "$id" >/dev/null 2>&1
done

Запускайте его как systemd‑сервис с Restart=always или как отдельный контейнер с доступом к сокету. Политика restart целевого приложения должна быть настроена так, чтобы рестарт допускался (unless-stopped или always).

Почему не стоит «убивать» процесс из healthcheck

Иногда советуют в проверке делать kill 1 при ошибке, чтобы сработала restart policy. Это превращает healthcheck в механизм рестартов, ломает диагностику и может попасть в цикл рестартов при флапе. Лучше отделяйте сигнализацию о здоровье и решение об автоматическом перезапуске (через сторож/оркестратор).

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

Nginx

Если есть stub_status или лёгкий локальный upstream, проверяйте его. Без него — хотя бы HEAD на корень.

HEALTHCHECK --interval=30s --timeout=2s --retries=3 CMD wget -q --spider http://127.0.0.1:80/

PHP‑FPM

Идеально — включить ping.path в php-fpm.conf и дергать /ping через локальный Nginx. Без Nginx проверяйте, что мастер‑процесс жив и порт слушается.

HEALTHCHECK --interval=20s --timeout=2s --retries=5 CMD sh -c 'pidof php-fpm >/dev/null && nc -z 127.0.0.1 9000'

Node.js/Go‑сервисы

Добавьте эндпоинт /health, где приложение без БД‑логики отвечает 200 OK, и проверяйте его.

HEALTHCHECK --interval=20s --timeout=2s --retries=3 CMD curl -fsS http://127.0.0.1:3000/health || exit 1

Redis/Key‑Value

Быстрый PING безопаснее, чем TCP‑handshake, потому что проверяет обработку команд.

HEALTHCHECK --interval=10s --timeout=2s --retries=5 CMD sh -c 'echo PING | nc 127.0.0.1 6379 | grep -q PONG'

Compose: healthcheck и depends_on с условием service_healthy

Compose‑опции healthcheck

В docker-compose можно описывать проверку прямо в сервисе:

services:
  web:
    image: myorg/web:latest
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:8080/health || exit 1"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 20s
    restart: unless-stopped

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

# Dockerfile
HEALTHCHECK NONE

Наблюдаемость: как мониторить здоровье

  • docker ps показывает колонку STATUS со значением (healthy|unhealthy).
  • docker inspect хранит историю прогонов и последние логи проверки в .State.Health.
  • docker events даёт поток событий health_status — подключайте алерты и авто‑действия. Для хранения метрик и событий посмотрите материал «VictoriaMetrics на одном VDS» — подходит для небольших инсталляций: VictoriaMetrics на одном VDS.
  • Логи: не зашумляйте. Выводите только то, что поможет понять причину.

Анти‑паттерны и типичные ошибки

  • Слишком тяжёлая проверка. Для HTTP‑пинга не рендерьте страницы и не ходите в БД без нужды.
  • Сетевые зависимости наружу. HEALTHCHECK не должен зависеть от доступности внешних сервисов и DNS.
  • Неверные интервалы. Большой timeout + маленький interval может приводить к конкурирующим запускам или ненужной нагрузке.
  • Отсутствие start-period. Прогрев кэшей и JIT ломает первые проверки — закладывайте реальное время старта.
  • Скрытые секреты в аргументах. Командная строка попадает в метаданные. Используйте переменные окружения клиентов или конфиг‑файлы.
  • Игнорирование статуса «starting». В связках с depends_on учитывайте, что некоторое время сервис ещё не готов.
  • Попытка привязать restart policy напрямую к health. Docker этого не делает. Нужен внешний сторож или оркестратор.

Отладка healthcheck

  • Запускайте команду проверки вручную через docker exec, чтобы увидеть реальные ошибки.
  • Временно снизьте interval и retries для ускоренной диагностики.
  • Проверьте лимиты контейнера (CPU/IO). При сильной нагрузке проверка может не успевать за timeout.
  • Смотрите .State.Health.Log через docker inspect — там последние N запусков с выводом.

Связка с обновлениями и развёртыванием

Корректный healthcheck — фундамент для обновлений без простоя: новый экземпляр считается готовым только после успешных проверок, затем можно трафик переключать или останавливать старый. В Swarm обновлятор также учитывает здоровье задач при поэтапном развёртывании. В самостоятельных деплоях на одном хосте этот же эффект достигается за счёт depends_on: service_healthy и сторожа, который не допустит «зависших» unhealthy‑контейнеров. Про безопасность окружения не забывайте: для плотной изоляции полезен подход с gVisor/Firecracker — см. изоляция контейнеров с gVisor и Firecracker.

Чек‑лист

  • Есть лёгкий и быстрый эндпоинт или CLI‑проверка, не зависящая от внешних сетей.
  • Подобраны interval, timeout, retries, start-period под реальный прогрев и SLA.
  • В Compose зависимые сервисы ждут service_healthy.
  • Настроен мониторинг событий health_status.
  • Решено, нужен ли авто‑рестарт при unhealthy и чем он обеспечивается (сторож/оркестратор).
  • Отключены неподходящие унаследованные проверки (HEALTHCHECK NONE).

Итоги

HEALTHCHECK — это дешёвый и эффективный способ повысить стабильность контейнеров: он фиксирует «готовность к работе» вместо простого факта запущенного процесса. Грамотно подобранные параметры и лёгкие шаблоны проверок позволяют связать здоровье с порядком запуска сервисов и сценариями обновления. Для автоматических перезапусков при деградации добавляйте небольшой сторож, а сами проверки держите максимально быстрыми и локальными. Так вы получите предсказуемый старт, корректный мониторинг и устойчивое поведение restart policy в реальных инцидентах.

Поделиться статьей

Вам будет интересно

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

После одновременной остановки всех узлов Galera обычного запуска MariaDB недостаточно. Сначала найдите сервер с последней транзакц ...
CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

Разберём, почему CoreDNS перезапускается или отвечает SERVFAIL, как обнаружить замкнутую пересылку DNS-запросов, проверить доступн ...
Apache Kafka KRaft на VDS: отдельные controller и broker

Apache Kafka KRaft на VDS: отдельные controller и broker

Соберём новый Kafka-кластер без ZooKeeper на шести Linux VDS: три узла controller образуют отказоустойчивый metadata quorum, а три ...