После выката приложение запускается около 90 секунд, но контейнер постоянно перезапускается. Какой механизм вызывает это поведение?
apiVersion: apps/v1
kind: Deployment
metadata:
name: report-api
spec:
template:
spec:
containers:
- name: api
image: example/report-api:4.2
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
Контейнер перезапускается из-за того, что liveness-проба начинает проверять приложение раньше его готовности. Если три последовательные HTTP-проверки завершаются ошибкой, kubelet считает контейнер неисправным, завершает его и запускает заново. Медленный, но ещё запускающийся процесс ошибочно воспринимается как зависший.
Для отделения длительного запуска от последующей проверки жизнеспособности обычно применяют startupProbe. Пока она не завершилась успешно, livenessProbe не выполняется.
В контейнерных системах сам факт существования процесса не доказывает, что приложение может обслуживать запросы: процесс может зависнуть, потерять рабочий поток или ещё загружать конфигурацию. Поэтому оркестраторы используют проверки состояния, чтобы автоматически обнаруживать неисправные экземпляры.
Однако проверка «жив ли процесс» и проверка «готов ли он принимать трафик» имеют разные цели. Разделение на liveness, readiness и позднее startup позволяет не применять одинаковую политику к быстрому сервису и к сервису с длительной инициализацией.
В примере первая проверка выполняется через 10 секунд после старта контейнера, затем примерно каждые 5 секунд. Если приложение не отвечает успешно, после трёх последовательных ошибок kubelet признаёт его неисправным.
В результате контейнер может быть завершён примерно через 20–25 секунд после начала проверок, хотя ему требуется 90 секунд для нормального запуска. Новый экземпляр проходит тот же цикл, возникает CrashLoopBackOff, а приложение не успевает перейти в рабочее состояние.
LivenessProbe отвечает на вопрос: «Нужно ли перезапустить контейнер?». Её не следует использовать как единственный способ описать длительную инициализацию. Ошибка liveness-проверки приводит к перезапуску контейнера, а не просто убирает его из балансировки.
ReadinessProbe отвечает на другой вопрос: «Можно ли направлять этому экземпляру трафик?». Неуспешная readiness-проверка обычно исключает Pod из соответствующих Service-эндпоинтов, но не перезапускает контейнер.
Для приложения с долгим стартом задают startupProbe:
Здесь startupProbe допускает примерно 120 секунд неуспешного запуска. Пока она не завершилась успешно, livenessProbe не активна. После успешного старта liveness начинает обнаруживать зависание, а readiness отдельно управляет допуском к трафику.
Параметры нельзя выбирать произвольно. Слишком маленькие таймауты создают ложные срабатывания при нагрузке, а слишком большие задерживают восстановление действительно зависшего контейнера. Эндпоинт liveness должен проверять минимальное состояние процесса и не зависеть без необходимости от внешней базы данных: временная недоступность базы не всегда означает, что контейнер нужно перезапускать.
Сервис формировал локальный индекс при запуске около двух минут. Сначала команда увеличила initialDelaySeconds до 150 секунд. Это убрало перезапуски, но увеличило время обнаружения зависания почти до двух с половиной минут и не отделило готовность к трафику от жизнеспособности процесса.
Другим вариантом было убрать livenessProbe. Это снизило риск ложных перезапусков, но зависший процесс мог оставаться в системе бесконечно, а оркестратор не получал сигнала для восстановления.
Выбранное решение — добавить startupProbe, оставить умеренные параметры liveness и использовать отдельную readiness-проверку после построения индекса. Такой вариант сохранил автоматическое восстановление зависших экземпляров и не направлял запросы в ещё не готовые контейнеры.
Что произойдёт, если startupProbe так и не станет успешной?
Если число последовательных неудач достигнет failureThreshold, kubelet применит обычное поведение неуспешной проверки и перезапустит контейнер. StartupProbe не отключает механизм восстановления, а только ограничивает период, в течение которого liveness и readiness не начинают действовать по своим правилам. Поэтому её бюджет времени должен превышать нормальное время запуска, но не быть бесконечным.
Может ли успешная livenessProbe означать, что экземпляру можно отдавать запросы?
Нет. Liveness подтверждает, что контейнер, вероятно, не застрял и его не требуется перезапускать. Приложение при этом может ещё не загрузить маршруты, не установить обязательное соединение или не завершить прогрев кэша. Для управления трафиком нужна readinessProbe, а её проверка должна отражать именно способность обслуживать запросы.
Почему проверка liveness, зависящая от базы данных, часто опасна?
При кратковременной проблеме с базой все экземпляры могут одновременно стать «неживыми» и начать перезапускаться. Это не устраняет неисправность, а создаёт шторм перезапусков и дополнительную нагрузку на базу. Обычно liveness проверяет внутреннюю работоспособность приложения, а зависимость от внешнего компонента учитывается в readiness или обрабатывается логикой самого сервиса.