Зачем нужны Health Checks в Kubernetes?
Health checks в K8s -- это совокупность liveness, readiness и startup probes. Каждый решает свою задачу: liveness = жив ли, readiness = готов ли, startup = стартовал ли.
🟢 Junior Level
Простое определение
Health Checks (Проверки здоровья) — это механизмы Kubernetes, которые позволяют оркестратору понять, в каком состоянии находится приложение: запущено ли оно, готово ли принимать трафик, и не застряло ли. На основе этих проверок Kubernetes автоматически принимает решения: перезапустить контейнер, убрать его из балансировщика или дать больше времени на запуск.
Health checks в K8s – это совокупность liveness, readiness и startup probes. Каждый решает свою задачу: liveness = жив ли, readiness = готов ли, startup = стартовал ли.
Аналогия
Представьте, что вы управляете сетью кофеен. Health Checks — это ежедневные отчёты каждой кофейни:
- Liveness: “Мы открыты, свет горит” (контейнер жив)
- Readiness: “Кассы работают, бариста на месте, можем принимать клиентов” (готов к трафику)
- Startup: “Мы ещё делаем ремонт, скоро откроемся” (дайте время на запуск)
Пример YAML (все три пробы)
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: app
image: my-app:1.0
startupProbe:
httpGet:
path: /health/startup
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /health/liveness
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/readiness
port: 8080
periodSeconds: 5
failureThreshold: 3
Пример kubectl
# Посмотреть статус Pod'а
kubectl get pods
# READY: 1/1 — все пробы прошли, 0/1 — readiness провалена
# Детальная информация о пробах
kubectl describe pod my-app
# Проверить события (перезапуски из-за liveness)
kubectl get events --sort-by='.lastTimestamp'
Когда использовать
- Всегда в production — без health checks Kubernetes не сможет автоматически восстанавливать приложение
- Для любых микросервисов, API, веб-приложений
- Для Java-приложений с долгим стартом (JVM, Spring Boot) — обязательно startupProbe
Когда НЕ использовать liveness probe
НЕ используйте liveness probe для stateful-приложений (базы данных) – перезапуск может усугубить проблему. Вместо этого используйте readiness probe + мониторинг.
🟡 Middle Level
Как это работает
Kubernetes kubelet выполняет три типа проверок независимо друг от друга:
-
Liveness Probe — kubelet опрашивает эндпоинт. При провале (failureThreshold раз подряд) kubelet убивает контейнер и контейнерная среда его перезапускает. Restart count увеличивается.
-
Readiness Probe — kubelet опрашивает эндпоинт. При провале Pod удаляется из Endpoints всех связанных Services. Контейнер продолжает работать.
-
Startup Probe — kubelet опрашивает до первого успеха. Пока startupProbe не прошла, liveness и readiness отключены. После первого успеха startupProbe больше не выполняется.
Каждая проба выполняется в отдельной goroutine kubelet’а с таймаутом. Если проба не ответила в течение timeoutSeconds, она считается проваленной.
Практические сценарии
Сценарий 1: Java-приложение с долгим стартом (3 минуты)
startupProbe:
httpGet:
path: /actuator/health
port: 8080
failureThreshold: 30 # 30 попыток
periodSeconds: 10 # каждые 10 секунд = 5 минут максимум
# Только после успеха startupProbe:
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10 # проверяем каждые 10 секунд
Без startupProbe пришлось бы ставить initialDelaySeconds: 180 для liveness, что означало бы 3 минуты без проверки зависаний после запуска.
Сценарий 2: Временная перегрузка Приложение получает слишком много запросов, очередь переполнена. Readiness Probe возвращает 503, Pod снимается с балансировки, обрабатывает накопленные задачи и возвращается в строй.
Сценарий 3: Deadlock detection Liveness Probe проверяет не только HTTP, но и внутреннее состояние. При обнаружении deadlock — проба проваливается, контейнер перезапускается.
Таблица распространённых ошибок
| Ошибка | Последствие | Решение |
|---|---|---|
| Все три пробы на одном эндпоинте | Невозможно различить “завис” и “не готов” | Разделить /health/liveness, /health/readiness, /health/startup |
| Liveness проверяет внешнюю БД | При падении БД все Pod’ы перезапускаются бесконечно | Liveness проверяет только внутреннее состояние, без внешних зависимостей |
| Слишком агрессивный failureThreshold | Лишние перезапуски при кратковременных проблемах | Ставить 3-5 для liveness, 15-30 для startup |
| Отсутствие health checks | Зависшие Pod’ы получают трафик, пользователи видят 502 | Всегда добавлять все три пробы в production |
| timeoutSeconds слишком маленький (1 сек) | Провалы при GC pause или slow requests | Ставить 3-10 секунд, учитывая worst-case latency приложения |
Использование :latest тега |
Kubernetes не видит изменений и не запускает Rolling Update | Всегда использовать конкретные теги или SHA256 digest |
Сравнительная таблица всех трёх проб
| Характеристика | Startup Probe | Liveness Probe | Readiness Probe |
|---|---|---|---|
| Вопрос | “Запустился ли я?” | “Жив ли я?” | “Готов ли к трафику?” |
| Действие при сбое | Повторная попытка (до failureThreshold) | Перезапуск контейнера | Удаление из Service Endpoints |
| Когда отключена | После первого успеха — навсегда | Работает всегда (если нет startupProbe) | Работает всегда (если нет startupProbe) |
| Взаимодействие | Блокирует liveness и readiness | Перезапускает | Снимает с балансировки |
| Типичный periodSeconds | 5-10 | 10-30 | 3-10 |
| Типичный failureThreshold | 15-30 | 3-5 | 3 |
| Проверяет зависимости? | Нет | Нет | Да |
Когда НЕ использовать
- Короткоживущие Jobs/CronJobs — если Pod запускается, выполняет задачу и завершается, health checks не нужны (или нужны только для отладки)
- Sidecar-контейнеры без сетевого интерфейса — если контейнер только пишет логи или собирает метрики, liveness может быть достаточно, readiness — нет
- Init-контейнеры — они запускаются до основных и завершаются, health checks к ним не применяются
🔴 Senior Level
Глубинная механика: kubelet probe manager
Health Checks управляются компонентом kubelet, а точнее — подсистемой prober в исходном коде kubelet’а (pkg/kubelet/prober/).
Архитектура:
- worker — goroutine на каждую пробу, выполняет HTTP/TCP/exec проверки
- manager — координирует workers, хранит результаты в
results.Manager - statusManager — передаёт результаты в PodStatus, который отправляется в API Server
kubelet выполняет пробы асинхронно. Каждый worker имеет свою очередь (workqueue) и timeout. При timeout проба считается проваленной, даже если нет ответа.
Startup Probe + Liveness/Readiness взаимодействие:
Pod создан → startupProbe активна, liveness/readiness отключены
→ kubelet выполняет startupProbe каждые periodSeconds
→ При success: startupProbe отключается навсегда, liveness/readiness активируются
→ При failure (failureThreshold подряд): контейнер убивается (только с Kubernetes 1.20+)
До K8s 1.20 failure в startupProbe не приводил к убийству контейнера — это было исправлено в PR #95190.
Trade-offs
| Аспект | Trade-off |
|---|---|
| Startup vs initialDelaySeconds | startupProbe гибче: даёт много времени на старт, но потом быстрые liveness. initialDelaySeconds — либо слишком долго, либо слишком агрессивно |
| Частота liveness | Частые проверки = быстрее обнаружение deadlocks, но выше CPU overhead и ложные срабатывания при GC pause |
| HTTP vs exec | HTTP быстрее и даёт status code, но exec может проверить глубже (файлы, процессы). exec медленнее и создаёт дополнительные процессы |
| Зависимости в Readiness | Проверка зависимостей = честный статус, но риск каскадного отказа. Без проверки = Pod получает трафик, но не может его обработать |
| Single vs Multi endpoint | Один эндпоинт проще, но менее информативен. Раздельные эндпоинты точнее, но требуют больше кода и обслуживания |
Edge Cases (6+)
Edge Case 1: GC Pause в JVM и Liveness Probe
JVM может остановить все потоки на 5-30 секунд при Full GC (особенно без ZGC/Shenandoah). Если timeoutSeconds: 3 и failureThreshold: 3, три проваленные пробы убьют контейнер. Решение: ZGC/G1GC с MaxGCPauseMillis, timeoutSeconds: 10, или отдельный native sidecar для health checks вне JVM.
Edge Case 2: Cascading Failure через Readiness Все микросервисы проверяют одну БД в readinessProbe. БД замедляется → все Pod’ы одновременно отключаются от трафика → полный outage. Решение: Readiness проверяет только внутреннее состояние, а доступность БД обрабатывается через Circuit Breaker на уровне бизнес-логики.
Edge Case 3: kubelet restart сбрасывает probe state При рестарте kubelet теряет счётчик consecutive failures для startupProbe. Pod, который был близок к failureThreshold, получает “чистый лист”. Это может привести к тому, что никогда не запускающееся приложение будет бесконечно пытаться стартовать.
Edge Case 4: Containerd/CRI-O timeout
Если kubelet не может выполнить exec пробу из-за проблем с CRI (containerd/CRI-O завис), проба считается проваленной. Это не проблема приложения, но контейнер всё равно будет перезапущен. Мониторьте kubelet_pod_worker_duration_seconds.
Edge Case 5: Readiness + HPA race condition
HPA проверяет CPU/RPS. Readiness отключает Pod от трафика. HPA видит падение RPS на Pod и создаёт новые реплики. Но новые тоже могут провалить Readiness. Результат: explosion Pod’ов без реальной пользы. Решение: behavior.stabilizationWindowSeconds в HPA + proper Readiness без внешних зависимостей.
Edge Case 6: Liveness Probe + Graceful Shutdown conflict Liveness Probe проваливается, kubelet убивает контейнер. Одновременно приложение получает SIGTERM и начинает graceful shutdown (завершает запросы 30 сек). Но kubelet уже запустил новый контейнер. Два экземпляра одного приложения работают параллельно, возможен conflict на ресурсы (файлы, порты, БД коннекты).
Edge Case 7: Multi-container Pod с разными пробами В Pod’е 2 контейнера: app и sidecar (envoy). Liveness у app проходит, у sidecar — нет. kubelet перезапускает sidecar, но app продолжает работать. Если sidecar критичен (например, service mesh proxy), app работает без прокси. Решение: использовать ShareProcessNamespace и проверять оба контейнера.
Performance Numbers
| Метрика | Значение |
|---|---|
| kubelet probe overhead (HTTP) | ~1-5ms CPU, ~10-50KB RAM на одну пробу |
| kubelet probe overhead (exec) | ~10-50ms CPU, создание нового процесса |
| Probe timeout влияние | При timeout probe считается failed, но goroutine ждёт timeout перед очисткой |
| API Server PodStatus update latency | 10-100ms |
| EndpointSlice propagation delay | 500ms-2s |
| kubelet restart probe state loss | Полная потеря счётчика consecutive failures |
| JVM Full GC pause (G1GC) | 100ms-2s, (ZGC) 1-10ms |
| Максимум проб на Pod | Не ограничен, но каждая проба — goroutine, ~2KB stack |
Security
- Health эндпоинты не должны быть публичными — они могут раскрыть внутреннюю структуру приложения
- exec пробы запускаются от имени контейнера — если контейнер работает как root, exec probe тоже root. Это не эскалация, но стоит учитывать
- Liveness Probe не должен иметь side-effects — злоумышленник с доступом к Pod может вызвать liveness эндпоинт и спровоцировать перезапуск (DoS)
- mTLS для health эндпоинтов — если sidecar proxy (Istio) перехватывает весь трафик, health эндпоинты должны быть исключены из mTLS или использовать отдельный порт
- NetworkPolicy — ограничьте доступ к health эндпоинтам только от kubelet (через
kube-systemnamespace)
Production War Story
Ситуация: Финтех-компания, кластер из 500 нод, 5000 Pod’ов. Все сервисы имели один health эндпоинт /health, который проверял БД, Redis, и Kafka. Во время инцидента Redis начал тормозить (99th percentile latency выросла с 5ms до 500ms). Liveness Probe начал таймаутить, так как проверка Redis занимала 10+ секунд при timeoutSeconds: 5.
Эффект домино:
- Liveness Probe провалился → kubelet убил 2000 Pod’ов за 2 минуты
- Новые Pod’ы стартовали, но startupProbe проверяла Redis → тоже таймаутили
- HPA увидел падение реплик → создал ещё 3000 Pod’ов
- Кластер достиг лимита Pod’ов на нодах → новые Pod’ы в Pending
- Полный outage на 45 минут
Post-mortem и fix:
- Разделены эндпоинты:
/health/live(без внешних зависимостей),/health/ready(с Circuit Breaker) - startupProbe убрана проверка Redis — только JVM startup
- Добавлен Redis Circuit Breaker с fallback на degraded mode
- HPA ограничен
maxReplicasиstabilizationWindowSeconds: 300 - Внедрён Chaos Engineering — регулярное тестирование сценариев отказа
Мониторинг после fix:
# Alert: Liveness перезапуски растут
rate(kube_pod_container_status_restarts_total[5m]) > 0.1
# Alert: Readiness failures
sum(kube_pod_status_ready{condition="false"}) by (namespace) > 10
# Alert: HPA scaling слишком агрессивный
kube_horizontalpodautoscaler_status_desired_replicas - kube_horizontalpodautoscaler_spec_min_replicas > 5
Monitoring (Prometheus/Grafana)
Ключевые метрики:
# Перезапуски контейнеров (liveness failures)
rate(kube_pod_container_status_restarts_total[5m])
# Pod'ы не в Ready состоянии
sum(kube_pod_status_ready{condition="false"}) by (namespace)
# kubelet probe latency (должна быть < periodSeconds)
histogram_quantile(0.99, kubelet_pod_worker_duration_seconds_bucket)
# Probe timeout events (через kubelet logs)
kubelet_pod_probe_failed_total
# HPA scaling events
kube_horizontalpodautoscaler_status_current_replicas
# EndpointSlice availability
sum(kube_endpoint_address_available) by (service)
Grafana Dashboard панели:
- Pod Restarts Rate (по namespace) — красная линия при > 0.1/sec
- Liveness vs Readiness failure rate — корреляция
- Probe latency p50/p99 — должна быть стабильной
- HPA current vs desired replicas
- Correlate с infrastructure metrics: node CPU, memory, disk I/O
Highload Best Practices
- Всегда разделяйте liveness и readiness эндпоинты — liveness = “процесс жив”, readiness = “могу обслуживать”
- Используйте startupProbe для JVM-приложений — это спасение для Spring Boot с 2-3 минутным стартом
- Liveness без внешних зависимостей — проверяйте только deadlock, thread starvation, критическую нехватку памяти
- Readiness с Circuit Breaker — если зависимость недоступна, возвращайте Ready в degraded mode, а не отключайте Pod
- timeoutSeconds >= p99 latency вашего эндпоинта — учитывайте worst-case с GC pause
- Не используйте
:latest— Kubernetes не обнаружит изменение и не запустит Rolling Update - Graceful Shutdown + preStop hook — дайте время на завершение запросов:
lifecycle: preStop: exec: command: ["sh", "-c", "sleep 10"] terminationGracePeriodSeconds: 60 - Monitor restart rate — более 1 перезапуска в минуту на Pod = проблема с liveness конфигурацией или приложением
- Используйте Prometheus Operator + kube-state-metrics — для автоматического сбора метрик о health checks
- Chaos Engineering — регулярно тестируйте сценарии: падение БД, network partition, node drain — и проверяйте, как health checks справляются
🎯 Шпаргалка для интервью
Обязательно знать:
- 3 типа проб: Liveness (перезапуск), Readiness (убрать из трафика), Startup (защита при старте)
- Startup Probe блокирует liveness и readiness до первого успеха — спасение для JVM
- Liveness БЕЗ внешних зависимостей, Readiness МОЖЕТ проверять зависимости (с Circuit Breaker)
- Death Spiral — агрессивные liveness настройки → массовый kill Pod’ов → outage
- Для Java: timeout > max GC pause, startupProbe для JIT warmup (failureThreshold: 30)
- Разделяйте эндпоинты:
/health/live,/health/ready,/health/startup - Без health checks K8s не сможет автоматически восстанавливать приложение
Частые уточняющие вопросы:
- «Зачем все 3 пробы?» — Startup даёт время на старт, Liveness ловит зависания, Readiness — готовность к трафику
- «Liveness для stateful-приложений?» — Нет, перезапуск может усугубить проблему; используйте readiness + мониторинг
- «Kubelet restart сбрасывает probe state?» — Да, теряет счётчик consecutive failures для startupProbe
- «Exec vs HTTP probe?» — HTTP быстрее, exec проверяет глубже; exec создаёт доп. процессы
Красные флаги (НЕ говорить):
- «Все пробы на одном эндпоинте» (невозможно различить «завис» и «не готов»)
- «Liveness проверяет БД, кэш, внешние API» (внешние проблемы → массовый kill)
- «Health checks не нужны в dev» (баги конфигурации всплывут только в prod)
- «Startup Probe = Liveness с большим delay» (нет, startup блокирует liveness/readiness)
Связанные темы:
- [[Что такое liveness probe]] — детально о liveness
- [[Что такое readiness probe]] — детально о readiness
- [[Как организовать rolling update в Kubernetes]] — health checks при деплое