Healthcheck в Docker Compose

Практическая настройка healthcheck в Docker Compose: проверка состояния контейнера, интервалы, таймауты, retries и диагностика unhealthy.

Запущенный контейнер не всегда означает, что приложение внутри него действительно работает. Процесс может оставаться активным, но сервис уже не отвечает на запросы или не может подключиться к зависимостям.

Механизм healthcheck позволяет Docker регулярно выполнять проверочную команду и присваивать контейнеру состояние healthy или unhealthy.

В этой инструкции рассматривается только настройка и диагностика healthcheck в Docker Compose.

Проверено на: Ubuntu Server 24.04 LTS и Docker Compose Plugin
Уровень сложности: начальный
Время выполнения: около 15 минут
Требуемый доступ: пользователь с доступом к Docker

Что будет рассмотрено

После выполнения инструкции можно будет:

  • добавить healthcheck в compose.yaml;
  • проверять HTTP-сервис;
  • настроить интервалы и таймауты;
  • просматривать статус контейнера;
  • изучать историю проверок;
  • диагностировать состояние unhealthy;
  • отключать встроенный healthcheck образа.

Создание тестового проекта

Создайте каталог:

mkdir -p ~/docker-healthcheck-example
cd ~/docker-healthcheck-example

Базовый пример с Nginx

Создайте файл compose.yaml:

cat >compose.yaml <<'EOF'
services:
  web:
    image: nginx:alpine
    container_name: healthcheck-web
    ports:
      - "8080:80"
    healthcheck:
      test:
        - CMD
        - wget
        - --spider
        - -q
        - http://127.0.0.1/
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
EOF

Проверьте конфигурацию:

docker compose config

Запустите контейнер:

docker compose up -d

Проверка статуса

docker compose ps

Сразу после запуска состояние может быть:

health: starting

После успешной проверки:

healthy

Проверьте через Docker:

docker ps   --format 'table {{.Names}}	{{.Status}}'

Параметры healthcheck

test

Команда проверки:

test:
  - CMD
  - wget
  - --spider
  - -q
  - http://127.0.0.1/

Она запускается внутри контейнера.

В образе должна присутствовать используемая команда. В данном примере это wget.

interval

Интервал между проверками:

interval: 30s

Docker будет запускать проверку каждые 30 секунд.

timeout

Максимальное время выполнения одной проверки:

timeout: 5s

Если команда не завершилась за 5 секунд, проверка считается неуспешной.

retries

Количество последовательных ошибок:

retries: 3

После трёх неуспешных проверок контейнер получит статус:

unhealthy

start_period

Льготный период после запуска:

start_period: 10s

Ошибки в течение этого периода не учитываются как окончательный отказ.

Параметр полезен для приложений, которым требуется время на инициализацию.

Проверка HTTP через CMD-SHELL

Тот же healthcheck можно записать в строковой форме:

healthcheck:
  test:
    - CMD-SHELL
    - wget --spider -q http://127.0.0.1/ || exit 1
  interval: 30s
  timeout: 5s
  retries: 3
  start_period: 10s

CMD-SHELL запускает команду через shell и позволяет использовать:

  • ||;
  • &&;
  • перенаправления;
  • переменные;
  • несколько команд.

Для простой команды безопаснее использовать CMD.

Проверка с curl

Если в образе установлен curl:

healthcheck:
  test:
    - CMD
    - curl
    - --fail
    - --silent
    - http://127.0.0.1/health

Параметр --fail заставляет curl возвращать ошибку при HTTP-кодах 400 и 500.

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

docker compose exec web which curl

Если команда отсутствует, такой healthcheck работать не будет.

Проверка TCP-порта

Если приложение не имеет HTTP endpoint, можно проверить открытый порт.

Пример с nc:

healthcheck:
  test:
    - CMD-SHELL
    - nc -z 127.0.0.1 5432
  interval: 30s
  timeout: 5s
  retries: 3

Перед использованием убедитесь, что nc установлен в образе.

Проверка процесса

Пример:

healthcheck:
  test:
    - CMD-SHELL
    - pgrep nginx >/dev/null || exit 1

Такой вариант проверяет наличие процесса, но не подтверждает работоспособность сервиса.

Для веб-приложений лучше проверять реальный HTTP endpoint.

Просмотр результата healthcheck

Проверьте текущее состояние:

docker inspect   --format '{{.State.Health.Status}}'   healthcheck-web

Ожидаемый результат:

healthy

История проверок

Подробный вывод:

docker inspect healthcheck-web |
jq '.[0].State.Health'

Краткая история:

docker inspect   --format '{{json .State.Health.Log}}'   healthcheck-web |
jq

В журнале отображаются:

  • время начала;
  • время завершения;
  • код возврата;
  • вывод команды.

Код возврата

Для healthcheck важен код завершения команды:

0 — проверка успешна
1 — проверка неуспешна

Команду можно проверить вручную:

docker compose exec web   wget --spider -q http://127.0.0.1/

Проверьте код:

echo $?

Успешный результат:

0

Создание состояния unhealthy

Остановите Nginx внутри контейнера:

docker compose exec web nginx -s stop

Подождите несколько интервалов и проверьте:

docker compose ps

Статус должен измениться на:

unhealthy

Посмотрите историю:

docker inspect healthcheck-web |
jq '.[0].State.Health.Log'

Важная особенность

Статус unhealthy сам по себе не перезапускает контейнер.

Docker продолжит считать контейнер запущенным, пока основной процесс не завершился.

Healthcheck нужен для:

  • диагностики;
  • оркестрации;
  • зависимостей;
  • внешнего мониторинга;
  • автоматизации через дополнительные инструменты.

Перезапуск тестового контейнера

docker compose restart web

Проверьте:

docker compose ps

Сначала появится:

health: starting

затем:

healthy

Зависимость от healthy-сервиса

Пример приложения, которое должно запускаться после готовности базы данных:

services:
  db:
    image: postgres:alpine
    environment:
      POSTGRES_PASSWORD: CHANGE_ME
    healthcheck:
      test:
        - CMD-SHELL
        - pg_isready -U postgres
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 20s

  app:
    image: alpine
    command:
      - sh
      - -c
      - while true; do sleep 3600; done
    depends_on:
      db:
        condition: service_healthy

Сервис app будет ожидать успешного healthcheck базы данных.

Это не заменяет повторные подключения внутри самого приложения.

Healthcheck для PostgreSQL

Пример:

healthcheck:
  test:
    - CMD-SHELL
    - pg_isready -U "$${POSTGRES_USER}" -d "$${POSTGRES_DB}"
  interval: 10s
  timeout: 5s
  retries: 5
  start_period: 20s

Двойной знак доллара:

$$

передаёт переменную внутрь контейнера, не позволяя Compose подставить её заранее.

Healthcheck для MySQL

Пример:

healthcheck:
  test:
    - CMD-SHELL
    - mysqladmin ping -h 127.0.0.1 -u root -p"$${MYSQL_ROOT_PASSWORD}" --silent
  interval: 10s
  timeout: 5s
  retries: 5
  start_period: 30s

Не выводите пароль в журналы и не используйте чувствительные значения непосредственно в публичном Compose-файле.

Переопределение встроенного healthcheck

Некоторые образы уже содержат HEALTHCHECK.

Посмотрите конфигурацию образа:

docker image inspect nginx:alpine |
jq '.[0].Config.Healthcheck'

Собственный healthcheck в compose.yaml переопределит настройки образа.

Отключение healthcheck

Чтобы отключить встроенную проверку образа:

services:
  app:
    image: IMAGE_NAME
    healthcheck:
      disable: true

Проверьте:

docker compose config

Настройка частоты проверок

Слишком частые проверки:

  • создают лишнюю нагрузку;
  • увеличивают объём логов;
  • могут ошибочно определять кратковременные задержки как отказ.

Для обычного веб-сервиса начальные параметры могут быть такими:

interval: 30s
timeout: 5s
retries: 3
start_period: 20s

Для тяжёлого приложения период запуска следует увеличить.

Что должен проверять endpoint

Хороший endpoint /health должен быстро отвечать и отражать состояние сервиса.

Минимальная проверка:

  • приложение принимает запросы;
  • основной процесс не завис;
  • критические внутренние компоненты доступны.

Не следует включать в каждую проверку тяжёлые операции или обращения ко всем внешним сервисам.

Типичные проблемы

Контейнер постоянно unhealthy

Проверьте историю:

docker inspect CONTAINER_NAME |
jq '.[0].State.Health'

Выполните тест вручную:

docker compose exec SERVICE_NAME   HEALTHCHECK_COMMAND

Команда не найдена

Проверьте:

docker compose exec SERVICE_NAME   which COMMAND_NAME

Используйте инструмент, который уже есть в образе, либо добавьте его при сборке собственного образа.

Healthcheck работает с хоста, но не в контейнере

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

Адрес:

127.0.0.1

указывает на сам контейнер, а не на хост.

Сервис запускается долго

Увеличьте:

start_period: 60s

При необходимости также увеличьте retries.

После изменения healthcheck остались старые настройки

Пересоздайте контейнер:

docker compose up -d --force-recreate

Обычный restart не изменяет конфигурацию контейнера.

Быстрый набор команд

Проверить статус:

docker compose ps

Получить состояние:

docker inspect   --format '{{.State.Health.Status}}'   CONTAINER_NAME

Посмотреть историю:

docker inspect CONTAINER_NAME |
jq '.[0].State.Health.Log'

Пересоздать контейнер:

docker compose up -d --force-recreate

Удаление тестового проекта

cd ~/docker-healthcheck-example
docker compose down
cd ~
rm -rf ~/docker-healthcheck-example

Итог

После выполнения инструкции:

  • добавлен healthcheck в Docker Compose;
  • настроены интервалы, таймауты и количество попыток;
  • проверены состояния starting, healthy и unhealthy;
  • изучена история проверок;
  • рассмотрены HTTP-, TCP- и процессные проверки;
  • показана зависимость service_healthy;
  • разобраны типичные ошибки.

Healthcheck должен проверять реальную готовность сервиса, а не только наличие запущенного процесса.

← Предыдущая статья Переменные окружения в Docker Compose Следующая статья → Политики перезапуска контейнеров Docker