DevOps

Почему HealthCheck в Docker‑контейнере не срабатывает?

В контейнере на базе scratch HealthCheck не проходит из‑за отсутствия /etc/hosts, httpcheck и localhost. Как проверить и исправить проблему для Swarm и реплик?

Почему HealthCheck в Docker‑контейнере не срабатывает, хотя приложение отвечает 200 на /health? В контейнере используется образ scratch, приложение слушает порт 80, а HealthCheck прописан так:

dockerfile
HEALTHCHECK --interval=1m --timeout=5s CMD ["httpcheck", "http://localhost/health"]

При запуске в Swarm контейнеры завершаются и пересоздаются, а при replicas > 1. Может ли отсутствие доступа к localhost в scratch‑образе быть причиной? Как проверить доступность /health внутри контейнера и исправить проблему?

Почему HealthCheck не срабатывает
В scratch‑образе почти всё отсутствует, что обычно помогает Docker‑у «знать», как проверить состояние приложения:

  1. Нет /etc/hosts – Docker добавляет его только в образах, которые имеют файловую систему. В чистом scratch он не создаётся, поэтому localhost не резолвится в адрес контейнера.
  2. Отсутствует инструмент для проверкиhttpcheck (или curl, wget) не входит в scratch. Если в инструкции HEALTHCHECK указать исполняемый файл, которого нет, Docker получает код выхода 127 и помечает контейнер «unhealthy».
  3. Порт 80 не объявлен – хотя приложение слушает 80, Docker не знает, что нужно пробросить этот порт, если в Dockerfile не прописан EXPOSE 80. В Swarm это не влияет на HealthCheck, но удобно для диагностики.

Из‑за этих факторов контейнер в Swarm быстро падает и создаётся заново, а при replicas > 1 Docker «потерял» одну из копий и пытается заменить её.


Как проверить доступность /health внутри контейнера

  1. Запустите контейнер с интерактивным шеллом (только если ваш образ содержит его).

    bash
    docker run -d --name test-health myimage
    docker exec -it test-health /bin/sh            # в scratch не работает
    

    Для scratch понадобится добавить временное ядро‑шлюзовое приложение:

    bash
    docker run --rm -it --entrypoint sh busybox
    

    На этом слое можно сразу проверить, что ваш сервис отвечает:

    bash
    wget -qO- http://127.0.0.1/health
    
  2. Используйте docker exec с уже установленным в контейнере инструментом.
    Если в образе есть curl (или вы его добавили в отдельном слое), то:

    bash
    docker exec test-health curl -f http://127.0.0.1/health
    

    Вывод 200 OK подтверждает, что эндпоинт работает.

  3. Проверка через Docker Inspect

    bash
    docker inspect --format '{{.State.Health.Status}}' test-health
    

    Если вы видите unhealthy, значит HealthCheck действительно не проходит.


Как исправить проблему

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

Опция A – использовать curl

Dockerfile
FROM scratch
# добавляем curl (статически слинкованную версию, ~75 KB)
COPY --from=ghcr.io/tarampampam/microcheck:1 /bin/curl /bin/curl
HEALTHCHECK --interval=1m --timeout=5s CMD ["curl", "-f", "http://127.0.0.1/health"]

Опция B – использовать httpcheck

Dockerfile
FROM scratch
COPY --from=ghcr.io/tarampampam/microcheck:1 /bin/httpcheck /bin/httpcheck
HEALTHCHECK --interval=1m --timeout=5s CMD ["httpcheck", "http://127.0.0.1/health"]

Важно – используйте exec‑форму (CMD [...]), а не shell‑форму (CMD ["curl", ...]), потому что в scratch нет /bin/sh.

2. Резолвим адрес через IP, а не через localhost

127.0.0.1 всегда указывает на сам контейнер, не зависит от DNS или /etc/hosts:

Dockerfile
HEALTHCHECK --interval=1m --timeout=5s CMD ["httpcheck", "http://127.0.0.1/health"]

3. Объявите порт (необязательно, но удобно)

Dockerfile
EXPOSE 80

4. Установите параметры восстановления

Если вы используете Swarm, добавьте --health-retries и --health-interval в сервис:

bash
docker service create \
  --name myservice \
  --replicas 3 \
  --health-interval 1m \
  --health-timeout 5s \
  --health-retries 3 \
  myimage

5. Проверьте результат

bash
docker ps
# → HEALTH: healthy
docker inspect --format '{{.State.Health.Status}}' <container>
# → healthy

Итог

  • Да, отсутствие localhost‑псевдонима в scratch может помешать работе HealthCheck.
  • Ключ – использовать IP‑адрес 127.0.0.1 и гарантировать наличие исполняемого проверочного утилита (curl, httpcheck, или собственный скрипт).
  • После этих изменений HealthCheck будет работать корректно, а Swarm больше не будет пересоздавать контейнеры из‑за «неустойчивости».
Авторы
Проверено модерацией
Модерация
Почему HealthCheck в Docker‑контейнере не срабатывает?