Docker unhealthy и restart always: почему контейнер не перезапускается

Контейнер помечен unhealthy, приложение не отвечает, а счётчик перезапусков остаётся нулевым. В Compose уже стоит restart: always, поэтому кажется, что Docker игнорирует настройку. На самом деле состояние проверки здоровья и состояние основного процесса — разные вещи.

Политика перезапуска обычного Docker-контейнера реагирует на завершение контейнера. Неудачный healthcheck сам по себе не завершает основной процесс. Поэтому контейнер может долго оставаться одновременно запущенным и нездоровым. Для устранения проблемы нужно понять, что именно перестало работать: приложение, зависимость или сама команда проверки.

Сначала увидеть оба состояния

Для Compose-проекта с сервисом app начните с чтения состояния. Следующие команды подходят для Bash и не перезапускают сервис:

docker compose ps --all
docker compose logs --tail=100 app
container_id=$(docker compose ps -q app)
docker inspect --format '{{.State.Status}}' "$container_id"
docker inspect --format '{{.State.Health.Status}}' "$container_id"
docker inspect --format '{{.RestartCount}}' "$container_id"
docker inspect --format '{{json .State.Health.Log}}' "$container_id"

Команды inspect предполагают, что сервис имеет один запущенный контейнер и у него настроен healthcheck. Если ps -q ничего не вернул, сначала разберите состояние из ps --all: диагностировать healthcheck у отсутствующего запущенного экземпляра не получится. Для нескольких реплик проверяйте каждый идентификатор отдельно.

Сочетание running, unhealthy и нулевого RestartCount означает: процесс продолжает работать, последние проверки здоровья не проходят, перезапуска не было. В журнале Health.Log ищите код возврата и вывод проверки. Часто причина видна прямо там: исполняемый файл не найден, HTTP вернул ошибку или соединение не установилось.

Проверка тоже может быть сломана

В образе может отсутствовать curl, хотя приложение исправно слушает порт. Или проверка обращается по неверному пути. Тогда автоматическое пересоздание контейнера будет снова получать тот же unhealthy. Сначала проверьте, что команда healthcheck существует в конечном образе, использует правильный адрес и завершается ожидаемым кодом.

Если HTTP-проверка возвращает ошибку из-за базы данных, посмотрите доступность базы и логи соединения приложения. Восстановление приложения может не изменить ситуацию. Разделяйте локальную работоспособность процесса и доступность внешней зависимости: от назначения проверки зависит, какое действие уместно.

Отдельный случай — долгий старт. Приложение загружает данные, а проверка начинает считать ошибки слишком рано. Параметры start_period, interval, timeout и retries должны соответствовать реальному запуску и допустимой задержке обнаружения сбоя. Не увеличивайте их вслепую: иначе настоящий зависший сервис будет дольше выглядеть как запускающийся.

Когда нужен перезапуск, а когда — другое действие

Если приложение зависло внутри работающего процесса, отметка unhealthy лишь сообщает об этом. Для автоматического восстановления нужен отдельно выбранный механизм: оркестратор с соответствующим правилом, внешний контроллер или корректное завершение приложения при невосстановимой ошибке. Выбор зависит от того, может ли повторный старт помочь и безопасен ли он для текущих операций.

На обычном Compose-хосте стоит заранее описать операционный сценарий: кто получает сигнал, какие логи сохраняет и как проверяет восстановление. Бесконечный перезапуск без диагностики способен спрятать первопричину и многократно повторить незавершённую работу.

Если основной процесс действительно завершился, тогда проверяют политику restart и обстоятельства остановки. У ручной остановки есть отдельная семантика; она не эквивалентна аварийному выходу. Также не переносите ожидания от Kubernetes или Swarm на одиночный Docker Engine: это разные механизмы управления жизненным циклом.

Как закрепить диагностику

Попробуйте вопрос про unhealthy при restart always, затем пройдите подборку по запуску, портам и данным Compose. Она помогает отличать успешное завершение init-команды, недоступный HTTP и проблему проверки здоровья.

Чтобы собрать и обслуживать весь проект, откройте курс Docker и Docker Compose: вы запускаете приложение с PostgreSQL, настраиваете healthcheck и находите причины сбоев по логам и состоянию контейнеров. Дальше в программе — обновление версии, откат и восстановление данных.

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

  • Docker и Docker Compose: контейнеризация приложений ↗

    Упакуйте приложение в Docker и запустите его с PostgreSQL через Compose. Образы, сети, тома, диагностика поломок, обновление версий и восстановление данных — с практикой и итоговым проектом.