Пробы: liveness, readiness, startup

Три пробы, и почему их путаница превращает частичный отказ в полный.

Три пробы, три вопроса

Проба Вопрос Что при провале
readiness можно слать трафик? под убирают из Service
liveness процесс жив? контейнер перезапускают
startup запуск завершён? остальные пробы ждут

Разница в последствиях: readiness временно исключает, liveness перезапускает.

Самая дорогая ошибка

Проверять внешнюю зависимость в liveness-пробе:

livenessProbe:
  httpGet:
    path: /health/db     # проверяет соединение с базой

База стала недоступна на минуту. Проба падает у всех реплик одновременно, Kubernetes перезапускает их все. База от этого не поднимется, зато приложение теперь гарантированно недоступно — и после старта поды снова не пройдут пробу, уйдя в CrashLoopBackOff.

Вы своими руками превратили деградацию в полный отказ.

Правило: liveness проверяет только сам процесс. Внешние зависимости — это readiness, а лучше вообще не проба, а корректная обработка ошибок в приложении.

Разумная конфигурация

startupProbe:
  httpGet: { path: /healthz, port: 8080 }
  failureThreshold: 30
  periodSeconds: 10          # до 5 минут на запуск

readinessProbe:
  httpGet: { path: /ready, port: 8080 }
  periodSeconds: 5

livenessProbe:
  httpGet: { path: /healthz, port: 8080 }
  periodSeconds: 10
  failureThreshold: 3

/healthz отвечает 200, если процесс отвечает. /ready дополнительно проверяет, что приложение прогрелось и может обслуживать запросы.

Зачем startup-проба

Без неё приходится выбирать между двумя плохими вариантами: либо liveness с большим initialDelaySeconds (и тогда зависший процесс долго не перезапускается), либо с маленьким — и тогда медленно стартующее приложение убивают, не дав ему запуститься.

Startup-проба снимает противоречие: пока она не прошла, остальные пробы не выполняются. После — начинают работать с короткими интервалами.

Связь с остановкой

Readiness важна и при удалении пода. Правильная последовательность:

  1. Под помечается на удаление и убирается из endpoints.
  2. Приложение получает SIGTERM.
  3. Оно перестаёт принимать новые запросы, дорабатывает текущие.
  4. Процесс завершается.

Шаги 1 и 2 происходят параллельно, а не последовательно. Поэтому под может получить SIGTERM раньше, чем прокси на всех нодах узнают об исключении из endpoints. Отсюда распространённый приём — небольшая пауза в preStop:

lifecycle:
  preStop:
    exec:
      command: ["sleep", "5"]

Пять секунд дают правилам обновиться до того, как приложение начнёт закрываться.