Модуль 11 / 18
Healthcheck, зависимости и restart policies
Содержание
1. Цели и модель готовности
После модуля вы сможете различать running, healthy и ready, проектировать probes, управлять startup dependencies и выбирать restart policy по типу отказа.
- running означает только существование PID 1
- healthcheck выполняется внутри container
- unhealthy сам по себе не перезапускает container
- depends_on задаёт порядок и условия старта
- restart policy реагирует на завершение процесса
2. Теория: HEALTHCHECK
Контейнер с healthcheck проходит starting → healthy или unhealthy. Exit 0 означает успех, 1 — отказ, 2 зарезервирован. Engine сохраняет краткий output последних проверок и создаёт health_status events.
interval задаёт период, timeout ограничивает probe, retries считает последовательные отказы. start_period не учитывает ранние failures, а start_interval задаёт их частоту; успех во время start_period завершает льготный режим.
3. Теория: хорошая проверка
Probe должна быстро, дёшево и детерминированно проверять способность экземпляра обслужить запрос. Проверка лишь существования процесса даёт мало информации; глубокий запрос к каждой внешней зависимости создаёт каскадные отказы.
Healthcheck не является Kubernetes readiness/liveness-разделением. При необходимости traffic readiness реализует proxy/orchestrator, а приложение должно иметь retry, backoff и graceful degradation.
4. Теория: depends_on
Короткий depends_on ждёт только запуска dependency. condition: service_healthy ждёт healthy; service_completed_successfully подходит для конечной migration/job. Compose создаёт и удаляет services в dependency order.
Это startup coordination, а не runtime supervision. Если dependency позже отказала, приложение не обязано остановиться. restart: true внутри long-form depends_on относится к явной Compose-операции над dependency, не к автоматическому Engine restart.
5. Теория: restart policies
no не перезапускает; on-failure[:N] реагирует на non-zero exit; always и unless-stopped также переживают daemon restart с различием после ручной остановки. Политика начинает мониторинг после успешного запуска примерно 10 секунд.
Unhealthy не завершает PID 1 и потому не активирует restart policy. Restart loop лечат причиной, лимитом attempts и backoff приложения, а не бесконечным перезапуском.
6. Теория: границы и failure modes
Startup order не гарантирует готовность навсегда, а healthy не гарантирует корректность всех бизнес-операций. Клиенты должны переживать reconnect и временную недоступность.
Не соединяйте Docker restart policy с конкурирующим host process manager. Не используйте probe, которая меняет данные, требует привилегий или выводит secret.
7. Методические указания
- Сначала определите проверяемое свойство
- Назначьте timeout меньше interval
- Оставьте запас retries для transient failure
- Используйте start_period только для реального bootstrap
- Храните output probe коротким и без secrets
- Проверяйте State, Health.Log и events вместе
- Не путайте restart counter с health failures
- Тестируйте recovery, а не только failure
8. Лабораторная работа
Создайте медленно готовящийся gate, успешную migration и зависимое приложение. --wait должен завершиться только после выполнения условий.
mkdir -p /tmp/docker-module-11
printf '%s\n' 'name: docker-m11' 'services:' ' gate:' ' image: alpine:3.21' ' command: ["sh", "-c", "rm -f /tmp/ready; sleep 4; touch /tmp/ready; sleep 300"]' ' healthcheck:' ' test: ["CMD", "test", "-f", "/tmp/ready"]' ' interval: 1s' ' timeout: 1s' ' retries: 5' ' start_period: 2s' ' start_interval: 500ms' ' restart: unless-stopped' ' migrate:' ' image: alpine:3.21' ' command: ["sh", "-c", "echo migration=complete"]' ' app:' ' image: alpine:3.21' ' command: ["sh", "-c", "date -u +app_started=%FT%TZ; sleep 300"]' ' depends_on:' ' gate:' ' condition: service_healthy' ' migrate:' ' condition: service_completed_successfully' > /tmp/docker-module-11/compose.yaml
cd /tmp/docker-module-11
docker compose config --quiet
docker compose up --detach --wait --wait-timeout 30
docker compose ps --all
docker compose logs --no-color gate migrate appcd /tmp/docker-module-11
docker inspect docker-m11-gate-1 --format 'state={{.State.Status}} health={{.State.Health.Status}} restarts={{.RestartCount}}'
docker inspect docker-m11-gate-1 --format '{{json .State.Health.Log}}' | jq '.[-3:] | map({Start,End,ExitCode,Output})'
docker events --since 10m --until 0s --filter container=docker-m11-gate-1 --filter event=health_statusСделайте gate unhealthy и докажите, что процесс не перезапущен, затем восстановите ready state.
cd /tmp/docker-module-11
docker compose exec --interactive=false gate rm /tmp/ready
sleep 6
docker inspect docker-m11-gate-1 --format 'state={{.State.Status}} health={{.State.Health.Status}} restarts={{.RestartCount}}'
docker compose exec --interactive=false gate touch /tmp/ready
sleep 2
docker inspect docker-m11-gate-1 --format 'state={{.State.Status}} health={{.State.Health.Status}} restarts={{.RestartCount}}'Отдельно проверьте ограниченный on-failure restart.
docker run --detach --name docker-m11-restart --restart on-failure:2 alpine:3.21 sh -c 'n=0; test -f /attempt && n=$(cat /attempt); n=$((n+1)); echo "$n" > /attempt; echo attempt=$n; test "$n" -ge 3 && sleep 300; exit 1'
sleep 6
docker inspect docker-m11-restart --format 'state={{.State.Status}} exit={{.State.ExitCode}} restarts={{.RestartCount}}'
docker logs docker-m11-restart
docker update --restart no docker-m11-restartКритерий: app стартует после healthy и migration; unhealthy имеет RestartCount 0; restart-demo достигает третьей попытки.
9. Диагностика starting, unhealthy и restart loop
Разделяйте failure probe, failure приложения и failure runtime.
cd /tmp/docker-module-11
docker compose ps --all
docker inspect docker-m11-gate-1 --format '{{json .State}}' | jq
docker inspect docker-m11-gate-1 --format '{{json .Config.Healthcheck}}' | jq
docker compose logs --no-color --timestamps --tail 50
docker events --since 10m --until 0s --filter type=container | tail -30- Config.Healthcheck — effective command и timers
- State.Health.Log — exit code, duration и output
- State.ExitCode/OOMKilled/Error — причина завершения PID 1
- RestartCount и events — фактические рестарты
- Compose logs — порядок зависимостей
- ручной запуск probe — только после сохранения evidence
10. Контроль и cleanup
- Почему running не означает ready?
- Как работает start_period?
- Перезапускает ли unhealthy контейнер?
- Чем service_started отличается от service_healthy?
- Когда нужен service_completed_successfully?
- Разница always и unless-stopped?
- Почему restart loop опасен?
- Что хранит Health.Log?
docker rm --force docker-m11-restart 2>/dev/null || true
cd /tmp/docker-module-11
docker compose down --remove-orphans
cd /
rm -f /tmp/docker-module-11/compose.yaml
rmdir /tmp/docker-module-11
docker ps -a --filter name=docker-m11Модуль освоен, если вы доказываете готовность по фактам и отличаете startup dependency от runtime recovery.