сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Kubernetes

Пробы liveness и readiness

Пробы: готовность и живость

Поднял в кластере два одинаковых пода (под - это запущенный экземпляр приложения) и сломал каждому свою проверку. Тот, у кого падает проба ГОТОВНОСТИ: статус Running, готовность 0 из 1, перезапусков 0 - контейнер живёт, но за сервисом его адрес пропал, список адресов пуст. Тот, у кого падает проба ЖИВОСТИ: статус CrashLoopBackOff, перезапусков 2 - его перезапускают снова и снова.

Стержень: готовность решает, пускать ли трафик; живость решает, перезапускать ли контейнер. Перепутать их - значит перезапускать здоровое приложение или слать запросы в неготовое.

// Формулировки: «чем readiness отличается от liveness?», «зачем startup-проба?», «что проверять в пробе?»

Две пробы, два разных действия

Проба - это регулярный запрос к контейнеру: запрос по сети, команда внутри или проверка открытого порта. Отличаются пробы не тем, ЧТО спрашивают, а тем, что делает кластер с ответом.

Проба готовности управляет трафиком. Не прошла - под убирается из списка адресов сервиса, и запросы к нему не идут; сам контейнер при этом не трогают. Мой замер это подтверждает буквально: статус Running, ноль перезапусков и пустой список адресов за сервисом. Прошла обратно - под возвращается в раздачу сам.

// Проба живости управляет перезапуском. Не прошла заданное число раз подряд - контейнер убивают и запускают заново. В моём замере под с падающей живостью набрал два перезапуска и ушёл в состояние ожидания между попытками. Это лечение от зависаний, из которых приложение само не выходит: вечная блокировка, утечка соединений, замерший цикл.

проба готовности
решает, пускать ли на под трафик; контейнер не трогает
проба живости
решает, перезапускать ли контейнер

Проба запуска и медленный старт

Классическая авария: приложение поднимается 40 секунд (греет кэш, читает справочники), а проба живости начинает стучать через 10 и после трёх неудач убивает контейнер. Приложение не успевает стартовать никогда, а в логах видно бесконечный цикл перезапусков.

Лечится это отдельной пробой ЗАПУСКА: пока она не прошла, остальные пробы не работают вовсе. Ей дают щедрый предел - например, тридцать попыток по 10 секунд, то есть пять минут на старт, - а живости оставляют жёсткие короткие настройки. Так медленный старт и зависание в работе перестают конфликтовать.

// Старый способ - задержка первой проверки. Он хуже: задержку приходится ставить по худшему случаю, и всё это время зависший контейнер не будет замечен. Проба запуска эту дилемму снимает: она проверяет по-настоящему, а не ждёт по часам.

проба запуска
работает только на старте; пока не прошла, остальные пробы молчат
порог неудач
сколько неудачных проверок подряд считать поломкой

Что именно проверять

Проба живости должна проверять только сам процесс: отвечает ли он вообще. Простой адрес, который возвращает успех без обращения к чему-либо снаружи. Как только в живость попадает проверка базы, происходит следующее: база моргнула на минуту - и кластер ПЕРЕЗАПУСТИЛ все поды сервиса разом. Приложение было здорово, а теперь ещё и холодное.

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

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

живость без зависимостей
проверяет только сам процесс; падение базы не должно перезапускать поды
период и предел ожидания
как часто спрашиваем и сколько ждём ответа; вместе задают скорость реакции

Как отвечать: «Чем readiness отличается от liveness и что бывает, если их перепутать?»

Разные действия по итогу проверки. Готовность решает, пускать ли на под трафик: не прошла - адрес убирают из сервиса, а контейнер не трогают. Живость решает, перезапускать ли контейнер. Я проверял оба случая на стенде: под с падающей готовностью остался в состоянии Running с нулём перезапусков, но список адресов за сервисом стал пустым; под с падающей живостью ушёл в цикл перезапусков. Путают их двумя способами. Первый: в живость кладут проверку базы - и когда база моргает, кластер перезапускает все поды сервиса разом, хотя приложение здорово. Второй: живость ставят слишком агрессивно для медленно стартующего приложения, и оно не успевает подняться никогда; лечится отдельной пробой запуска, которая даёт щедрое время на старт и отключает остальные пробы, пока не пройдёт.

Ответ различает механизмы по действию, подтверждает измерением и называет обе типовые ошибки. Про пробу запуска вспоминают только те, кто ловил бесконечный цикл перезапусков.

На чём валятся

  • Кладут проверку базы в пробу живости: падение базы перезапускает все поды разом.
  • Ставят живость медленно стартующему приложению без пробы запуска.
  • Считают, что неготовый под перезапустится сам. Он останется работать, просто без трафика.
  • Задают предел ожидания больше периода проверок и получают наслоение проверок.
  • Обещают обновление без простоя, не настроив пробу готовности.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 7, остальные разбираются в тренажёре.

  1. #dvo_k8s_probes1 / 5
    В liveness-пробу добавили проверку доступности базы. Чем это опасно?
    A)Проба станет медленной и начнёт нагружать базу лишними запросами
    B)Kubelet перестанет доверять пробе и отключит её после серии отказов
    C)Моргнула база — и кластер разом перезапустит все поды сервиса
    D)Проба будет считаться успешной, пока база отвечает хоть чем-то
    показать ответ и разбор
    +C)Моргнула база — и кластер разом перезапустит все поды сервиса

    // разбор: Liveness должна отвечать на вопрос о самом процессе. Если в неё зашита внешняя зависимость, то её недоступность превращается в массовый рестарт: поды падают одновременно, теряют прогретые кэши и пулы, а после старта снова бьются в ту же базу — авария усиливается вместо восстановления. Зависимости проверяют в readiness: под просто уйдёт из балансировки и вернётся сам.

  2. #dvo_k8s_probes2 / 5
    При выкатке часть запросов ловит 502, хотя приложение корректно обрабатывает SIGTERM. Почему?
    A)Не задан terminationGracePeriodSeconds, и под убивают мгновенно
    B)Эндпоинты обновляются медленнее, чем гаснет под
    C)Readiness-проба продолжает отвечать успехом до самого конца
    D)Балансировщик кэширует адреса подов и не следит за эндпоинтами
    показать ответ и разбор
    +B)Эндпоинты обновляются медленнее, чем гаснет под

    // разбор: Удаление пода запускает две независимые цепочки: kubelet шлёт SIGTERM, а контроллер эндпоинтов убирает под из сервиса, после чего kube-proxy на каждой ноде переписывает правила. Вторая цепочка отстаёт, и приложение, честно закрывшее слушающий сокет, получает соединения от ещё не обновившихся нод. Лечится хуком preStop с паузой в несколько секунд: под держит трафик, пока правила разъезжаются.

  3. #dvo_k8s_probes3 / 5
    Тяжёлое приложение стартует минуту. liveness-проба убивает его раньше, чем оно поднялось, — CrashLoop. Что добавить?
    A)startupProbe: пока идёт старт, liveness не считается
    B)Просто убрать liveness-пробу совсем
    C)Увеличить readinessProbe, она прикроет старт
    D)Поднять число реплик, пока часть стартует
    показать ответ и разбор
    +A)startupProbe: пока идёт старт, liveness не считается

    // разбор: startupProbe создан ровно для медленного старта: пока он не прошёл, liveness и readiness не проверяются, и приложение не убивают на прогреве. После успешного старта включается обычная liveness. Альтернатива-костыль — большой initialDelaySeconds у liveness, но startupProbe гибче для приложений с непредсказуемым временем старта.

  4. #dvo_k8s_probes4 / 5
    Поды периодически перезапускаются без явной причины: liveness иногда не отвечает под нагрузкой. Что подкрутить?
    A)Перенести проверку в readiness — там перезапусков нет
    B)Отключить liveness на время пиков
    C)Слишком строгие пороги пробы
    D)Добавить ещё одну liveness-пробу для надёжности
    показать ответ и разбор
    +C)Слишком строгие пороги пробы

    // разбор: Дефолты пробы часто слишком жёсткие: один медленный ответ под нагрузкой — и failureThreshold превышен, под перезапущен. Подкручивают timeoutSeconds (проба не должна падать по таймауту при обычной нагрузке), periodSeconds и failureThreshold (сколько подряд промахов терпим), initialDelay. Цель — чтобы liveness ловила реальные зависания, а не редкие всплески задержки.

  5. #dvo_k8s_probes5 / 5
    Под нагрузкой поды по очереди выпадают из сервиса и возвращаются: readiness то проходит, то нет. Как разорвать цикл?
    A)Убрать readiness-пробу, чтобы поды перестали выпадать из сервиса
    B)Ужесточить порог readiness, чтобы она срабатывала пореже
    C)Перевести readiness в liveness, и пусть под перезапускается
    D)Readiness конкурирует с трафиком и флапает; сделать её лёгкой
    показать ответ и разбор
    +D)Readiness конкурирует с трафиком и флапает; сделать её лёгкой

    // разбор: Спираль смерти: под нагрузкой readiness-проба, конкурирующая с трафиком за тот же пул/поток или делающая тяжёлую проверку, начинает таймаутить; под выкидывают из Service, его доля нагрузки переезжает на оставшиеся поды, чьи пробы тоже начинают падать — здоровые поды выпадают ровно тогда, когда нужны. Лечится: readiness должна быть дешёвой и независимой от нагрузки (лёгкий эндпоинт, достаточный timeout, не падать на кратковременном насыщении), а масштабировать — по реальным метрикам.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.