Пробы: 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 важна и при удалении пода. Правильная последовательность:
- Под помечается на удаление и убирается из endpoints.
- Приложение получает
SIGTERM. - Оно перестаёт принимать новые запросы, дорабатывает текущие.
- Процесс завершается.
Шаги 1 и 2 происходят параллельно, а не последовательно. Поэтому под может получить SIGTERM раньше, чем прокси на всех нодах узнают об исключении из endpoints. Отсюда распространённый приём — небольшая пауза в preStop:
lifecycle:
preStop:
exec:
command: ["sleep", "5"]
Пять секунд дают правилам обновиться до того, как приложение начнёт закрываться.