Проверки здоровья подов в Kubernetes: liveness, readiness и startup probes с примерами | AdminWiki

Проверки здоровья подов в Kubernetes: liveness, readiness и startup probes с примерами

03 октября 2026 12 мин. чтения
Содержание статьи

Зачем нужны проверки здоровья подов в Kubernetes

Kubernetes запускает контейнер и дальше не знает, что происходит внутри процесса. Приложение может уйти в deadlock, зациклиться на ретраях к недоступной базе или перестать отвечать на запросы, а под останется в статусе Running: для kubelet процесс жив. Трафик через Service продолжит идти на этот под, и пользователи получат таймауты или 502.

Пробы здоровья закрывают этот разрыв. Проба это периодическая проверка, которую выполняет kubelet на той же ноде, где работает под. Результат проверки задаёт одно из трёх действий: перезапустить контейнер, исключить под из балансировки трафика или ничего не делать и подождать. Механизм проверки выбирается из трёх вариантов: HTTP-запрос (httpGet), установка TCP-соединения (tcpSocket) или запуск команды внутри контейнера (exec).

ПробаЧто проверяетДействие при провалеТипичный сценарий
livenessProbeПроцесс жив и отвечаетkubelet перезапускает контейнерDeadlock, зависание, утечка памяти
readinessProbeГотовность принимать трафикПод исключается из Endpoints сервисаПрогрев кэша, соединения с БД, миграции
startupProbeИнициализация завершенаПерезапуск контейнера; liveness и readiness отключены до успехаJVM, .NET, тяжёлые миграции

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

Цена ошибки в конфигурации высокая. Один и тот же failureThreshold, выставленный слишком низко, превращает просадку ответа на пару секунд в шторм перезапусков. Проверка внешней зависимости внутри liveness превращает падение базы в каскадный отказ всего деплоя.

Liveness probe: перезапуск зависшего контейнера

livenessProbe отвечает на вопрос, нужно ли перезапускать контейнер. kubelet считает проверку провалившейся после failureThreshold неудач подряд и убивает контейнер. Дальше работает restartPolicy пода (по умолчанию Always): контейнер стартует заново, а в событиях появляется запись Back-off restarting failed container с растущей задержкой между попытками.

Ставьте liveness для сценариев, где процесс жив, а приложение мертво: взаимная блокировка потоков, бесконечный цикл, зависшая очередь задач. Если сервис сам завершает процесс при фатальной ошибке, restartPolicy справится без liveness, а лишняя проверка добавит риск ложных перезапусков.

Провал liveness стоит дорого: контейнер убивают, незавершённые запросы теряются, приложение прогревает кэш заново. Поэтому liveness проверяет только сам процесс и не обращается к БД, кэшу или внешним API.

Пример liveness probe через httpGet

Основной вариант для веб-приложений и API. Эндпоинт отвечает быстро и не требует аутентификации.

apiVersion: v1
kind: Pod
metadata:
  name: web-api
spec:
  containers:
  - name: app
    image: registry.example.com/web-api:1.4.2
    ports:
    - name: http
      containerPort: 8080
    livenessProbe:
      httpGet:
        path: /healthz
        port: http
      initialDelaySeconds: 15
      periodSeconds: 10
      timeoutSeconds: 2
      failureThreshold: 3
      successThreshold: 1

Успехом считается ответ с кодом в диапазоне 200-399, остальные коды kubelet трактует как провал. Порт указывается числом или именем из ports контейнера, в примере это имя http. По умолчанию httpGet отправляет заголовок Host с IP пода; если приложение маршрутизирует по домену, добавьте httpHeaders с нужным значением Host.

initialDelaySeconds 15 даёт приложению время поднять слушателя, periodSeconds 10 задаёт интервал, failureThreshold 3 означает перезапуск примерно после 30 секунд непрерывных сбоев. Эндпоинт /healthz держите максимально простым: запрос к БД внутри него превращает падение базы в перезапуск всего деплоя. Готовые комбинации проверок для разных типов нагрузки собраны в руководстве по настройке Readiness и Liveness Probes в Kubernetes.

Пример liveness probe через exec

Подходит для приложений без HTTP-сервера: воркеров очередей, демонов, legacy-сервисов. Команда выполняется внутри контейнера, успех это код возврата 0.

    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 2
      failureThreshold: 3
      successThreshold: 1

Логика шаблона: приложение само поддерживает файл-маркер и удаляет его при зависании рабочего потока. Команда должна быть короткой и завершаться быстро. Каждый запуск exec-пробы тратит CPU и память контейнера, поэтому тяжёлые скрипты с сетевыми вызовами внутрь проверки не ставим.

Пример liveness probe через tcpSocket

tcpSocket устанавливает TCP-соединение с портом. Проверка подтверждает, что сокет открыт, но о логике приложения не говорит: процесс может принимать соединения и не обрабатывать запросы.

    livenessProbe:
      tcpSocket:
        port: 3306
      initialDelaySeconds: 15
      periodSeconds: 20
      timeoutSeconds: 3
      failureThreshold: 3

Для баз данных и брокеров это рабочий вариант: порт 3306 у MySQL или 5672 у RabbitMQ отвечает, даже когда сервис перегружен. Если у приложения есть HTTP-эндпоинт, выбирайте httpGet, он проверяет больше.

Readiness probe: управление трафиком без перезапуска

readinessProbe определяет, попадёт ли под в список бэкендов сервиса. При провале kubelet помечает под как NotReady и убирает его из Endpoints: новые запросы на этот под не идут, контейнер продолжает работать, состояние и открытые соединения сохраняются. Как только проверка снова проходит, под возвращается в балансировку.

Сценарии для readiness: прогрев кэша после старта, установка соединений с БД и брокером, ожидание миграций, временная перегрузка пула потоков. Это ещё и механизм плавного rolling update: Deployment поднимает новый под, ждёт Ready и только затем гасит старый. Если readiness не проходит, обновление зависает и упирается в progressDeadlineSeconds (по умолчанию 600 секунд). Связку readiness с репликами и стратегиями обновления разбираем в статье про отказоустойчивость Kubernetes: probes, реплики и стратегии обновления.

Пример readiness probe через httpGet

В отличие от liveness, здесь проверка зависимостей уместна: соединение с БД, доступность Redis, применение миграций.

    readinessProbe:
      httpGet:
        path: /ready
        port: http
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 2
      failureThreshold: 3
      successThreshold: 1

timeoutSeconds 2 при periodSeconds 5 оставляет запас на медленный ответ базы. Если эндпоинт опрашивает внешние сервисы, кэшируйте результат на 1-2 секунды, иначе каждая проверка порождает лишний запрос к зависимости.

Пример readiness probe через exec

    readinessProbe:
      exec:
        command:
        - test
        - -f
        - /tmp/ready
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 3

Шаблон test -f /tmp/ready подходит воркерам и приложениям, которые сами знают момент готовности: процесс создаёт файл после инициализации и удаляет его при потере соединения с брокером.

Не делайте readiness строже, чем требует трафик. Если при сбое БД все поды уходят из балансировки, сервис отдаёт 503 при живых контейнерах: добавьте failureThreshold 3 и разделите проверки на критичные и желательные.

Startup probe: защита медленно стартующих приложений

startupProbe запускается первой. Пока она не пройдёт успешно, kubelet не выполняет liveness и readiness, а контейнер не получает трафик. Если проверка не уложилась в отведённый бюджет, контейнер перезапускается. После первого успеха startup probe передаёт управление liveness probe, которая дальше отвечает за обнаружение зависаний.

Кандидаты на startup probe: Java-сервисы на Spring Boot, приложения .NET, сервисы, которые на старте прогоняют миграции схемы или строят индекс. Без неё приходится завышать initialDelaySeconds у liveness, и тогда зависание в рантайме обнаруживается с той же задержкой. Со startup probe liveness настраивается агрессивно: она включается после готовности приложения.

Пример startup probe через httpGet

    startupProbe:
      httpGet:
        path: /healthz
        port: http
      initialDelaySeconds: 10
      periodSeconds: 10
      timeoutSeconds: 2
      failureThreshold: 30

Бюджет на старт считается как initialDelaySeconds + periodSeconds × failureThreshold: 10 + 10 × 30 = 310 секунд. Значение покрывает инициализацию JVM с прогревом и короткие миграции. После успеха проверка /healthz переходит к liveness probe, и та реагирует на зависания с интервалом 10 секунд.

Пример startup probe через exec

    startupProbe:
      exec:
        command:
        - test
        - -f
        - /tmp/started
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 60

Бюджет здесь 5 + 5 × 60 = 305 секунд. Приложение создаёт /tmp/started после завершения инициализации. Для воркеров без HTTP это основной способ отличить долгий старт от зависания.

Дайте запас 30-50% к ожидаемому времени старта, но не больше. failureThreshold 300 при periodSeconds 10 даёт почти час ожидания, за который сломанная миграция или опечатка в конфиге останутся незамеченными.

Как выбрать параметры проб: тайминги и пороги

Пять параметров управляют поведением любой пробы. Значения по умолчанию подходят для простых случаев: periodSeconds 10 и timeoutSeconds 1 оставляют мало времени на ответ под нагрузкой.

ПараметрПо умолчаниюСмыслПрактическое значение
initialDelaySeconds0Пауза перед первой проверкой0 при наличии startup probe; 5-15 для liveness без неё
periodSeconds10Интервал между проверками10 для liveness, 5 для readiness
timeoutSeconds1Сколько ждать ответ2-3 для HTTP, всегда меньше periodSeconds
successThreshold1Успехов подряд для перехода в Ready1 для liveness и startup, 1-2 для readiness
failureThreshold3Неудач подряд до действия3 для liveness и readiness, расчёт по бюджету для startup

Бюджет старта: initialDelaySeconds + periodSeconds × failureThreshold. Приложению, которое поднимается две минуты, подойдут periodSeconds 10, failureThreshold 18 и initialDelaySeconds 10, итого 190 секунд. Для liveness с periodSeconds 10 и failureThreshold 3 контейнер перезапускается после 30 секунд непрерывных сбоев.

timeoutSeconds держите меньше periodSeconds. С timeoutSeconds 15 при periodSeconds 10 проверки накладываются друг на друга, и kubelet не успевает обрабатывать результаты. successThreshold больше 1 у liveness и startup API отклонит: для этих проб допустимо только значение 1.

Особенности для веб-приложений, баз данных и очередей

  • Веб-приложение и API. httpGet на /healthz для liveness и /ready для readiness, отдельный лёгкий обработчик без авторизации на том же порту, что и рабочий трафик.
  • База данных. tcpSocket на порт 3306 или 5432 для liveness, либо exec с admin-командой ping; readiness проверяет, что инстанс вышел из режима восстановления и принимает запросы.
  • Очередь и воркеры. HTTP-эндпоинта нет, поэтому liveness это exec-скрипт с проверкой heartbeat-файла, а подключение к брокеру уходит в readiness.
  • Медленный старт (JVM, .NET, тяжёлые миграции). startup probe с запасом по бюджету, liveness без initialDelaySeconds и периодом 10 секунд.

Типичные ошибки конфигурации проб и как их избежать

Инциденты с пробами связаны не столько с механизмом проверки, сколько с параметрами и выбором того, что проверяется. Разберём шесть конфигураций, которые ломают продакшен.

Ошибка: liveness probe зависит от базы данных

    livenessProbe:
      httpGet:
        path: /healthz?db=1
        port: 8080
      periodSeconds: 5
      failureThreshold: 2

База недоступна 30 секунд, эндпоинт отдаёт 500, и kubelet перезапускает все поды деплоя одновременно. После рестарта приложение снова открывает соединения к перегруженной базе и добивает её. Исправление: liveness проверяет только процесс по /healthz, а соединение с БД уходит в readiness probe с кэшированием результата.

Ошибка: initialDelaySeconds меньше реального времени старта

    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 30
      periodSeconds: 10
      failureThreshold: 3

Java-приложение стартует 90 секунд, контейнер убивают примерно на 60-й секунде, до того как поднимется HTTP-сервер. Исправление: startup probe с periodSeconds 10 и failureThreshold 12, а liveness включается после успешного старта.

Ошибка: слишком строгая readiness probe

Проверка с failureThreshold 1 и timeoutSeconds 1, которая опрашивает три зависимости, выводит под из балансировки при первом замедлении базы. Все реплики уходят из Endpoints, и сервис отвечает 503 при живых контейнерах. Исправление: timeoutSeconds 3, failureThreshold 3, кэш результата на несколько секунд.

Ошибка: exec-проба, которая может зависнуть

    livenessProbe:
      exec:
        command:
        - sh
        - -c
        - curl -s localhost:8080/healthz | grep ok
      timeoutSeconds: 1

Такой конфиг ломается по двум причинам: в образе может не быть curl или grep, а пайплайн способен ждать сетевого ответа дольше timeoutSeconds. Проба падает постоянно, контейнер уходит в перезапуск. Исправление: httpGet для HTTP-сервиса или прямой вызов одного бинарника в exec-форме без shell.

Ошибка: неверный порт пробы

Проба обращается к 8080, а приложение слушает 8000. kubelet получает connection refused и перезапускает здоровый контейнер. Исправление: сверьте containerPort с портом из логов старта, а при использовании имени порта убедитесь, что оно объявлено в ports контейнера.

Ошибка: бесконечный бюджет на старт

startup probe с periodSeconds 10 и failureThreshold 300 ждёт 50 минут. За это время сломанная миграция или опечатка в конфиге остаются незамеченными, а деплой висит в ожидании. Исправление: запас 30-50% от ожидаемого времени старта, остальное ловят метрики и алерты.

Как проверить и отладить пробы в кластере

Диагностика начинается с событий пода: там указано, какая проба упала и почему.

kubectl describe pod web-api-7d9c8b6f5c-2xk4p
kubectl get events --field-selector involvedObject.name=web-api-7d9c8b6f5c-2xk4p
kubectl logs web-api-7d9c8b6f5c-2xk4p --previous
kubectl get endpoints web-api

В kubectl describe pod смотрите блок Events и колонку Restart Count. Типичные сообщения:

  • Liveness probe failed: HTTP probe failed with statuscode: 500 - эндпоинт отвечает, но проверка внутри него падает: ошибка зависимости или логики.
  • Readiness probe failed: dial tcp 10.244.1.7:8080: connect: connection refused - порт закрыт, приложение не слушает или указан неверный порт.
  • Liveness probe failed: context deadline exceeded - ответ не уложился в timeoutSeconds, проверьте таймаут и нагрузку.
  • Back-off restarting failed container - kubelet уже убил контейнер и увеличивает задержку перед следующим запуском.

Логи предыдущего контейнера (kubectl logs ... --previous) показывают, что происходило перед перезапуском: панику, OOM или обрыв соединения с БД.

Проверить httpGet вручную можно изнутри пода теми же параметрами, что в манифесте: kubectl exec -it pod -- curl -v localhost:8080/healthz. Для exec-проб выполните ту же команду в контейнере и сверьте код возврата. Для readiness полезно посмотреть, попал ли под в Endpoints сервиса: kubectl get endpoints web-api.

При отладке можно временно поднять failureThreshold или initialDelaySeconds, чтобы контейнер выжил и отдал логи. После правки верните рассчитанные значения. События проб стоит вывести в мониторинг: как собирать метрики и алерты по health-проверкам, разбираем в материале про health-check и раннюю диагностику отказов сервисов.

Чек-лист перед деплоем в продакшен

  1. liveness не обращается к внешним сервисам. Проверка касается только процесса и его способности отвечать; БД, кэш и брокер в liveness дают каскадные перезапуски.
  2. readiness проверяет готовность принимать трафик. Эндпоинт учитывает зависимости, но с разумным таймаутом и кэшированием результата.
  3. Для старта дольше 30 секунд есть startup probe. Иначе долгая инициализация лечится завышенным initialDelaySeconds, который скрывает зависания в рантайме.
  4. timeoutSeconds меньше periodSeconds. Проверки не накладываются друг на друга.
  5. Бюджет старта посчитан. initialDelaySeconds + periodSeconds × failureThreshold покрывает худшее время инициализации с запасом 30-50%.
  6. successThreshold для liveness и startup равен 1. Другие значения API не примет.
  7. Пробы не создают лишней нагрузки. Лёгкий обработчик, короткие команды, никаких тяжёлых SQL в каждой проверке.
  8. Конфигурация проверена в staging. Остановите зависимость и посмотрите, как ведут себя liveness, readiness и общее время восстановления.
  9. События проб попадают в мониторинг. Алерт на частые перезапуски и на поды в NotReady ловит проблему до жалоб пользователей.
  10. Есть связка с graceful shutdown. terminationGracePeriodSeconds и preStop hook дают приложению завершить запросы, пока readiness уже сняла трафик.

Начните с одного сервиса: вынесите проверку зависимостей из liveness в readiness, добавьте startup probe, если старт занимает больше 30 секунд, и прогоните конфигурацию по чек-листу. Шаблоны из этой статьи копируются в манифест как есть, меняются только порт и путь эндпоинта. Сценарии с репликами, PodDisruptionBudget и обновлением без простоя собраны в руководстве по отказоустойчивому кластеру Kubernetes.

Источники

Поделиться: