Пробы liveness и readiness
Поднял в кластере два одинаковых пода (под - это запущенный экземпляр приложения) и сломал каждому свою проверку. Тот, у кого падает проба ГОТОВНОСТИ: статус Running, готовность 0 из 1, перезапусков 0 - контейнер живёт, но за сервисом его адрес пропал, список адресов пуст. Тот, у кого падает проба ЖИВОСТИ: статус CrashLoopBackOff, перезапусков 2 - его перезапускают снова и снова.
Стержень: готовность решает, пускать ли трафик; живость решает, перезапускать ли контейнер. Перепутать их - значит перезапускать здоровое приложение или слать запросы в неготовое.
// Формулировки: «чем readiness отличается от liveness?», «зачем startup-проба?», «что проверять в пробе?»
Две пробы, два разных действия
Проба - это регулярный запрос к контейнеру: запрос по сети, команда внутри или проверка открытого порта. Отличаются пробы не тем, ЧТО спрашивают, а тем, что делает кластер с ответом.
Проба готовности управляет трафиком. Не прошла - под убирается из списка адресов сервиса, и запросы к нему не идут; сам контейнер при этом не трогают. Мой замер это подтверждает буквально: статус Running, ноль перезапусков и пустой список адресов за сервисом. Прошла обратно - под возвращается в раздачу сам.
// Проба живости управляет перезапуском. Не прошла заданное число раз подряд - контейнер убивают и запускают заново. В моём замере под с падающей живостью набрал два перезапуска и ушёл в состояние ожидания между попытками. Это лечение от зависаний, из которых приложение само не выходит: вечная блокировка, утечка соединений, замерший цикл.
- проба готовности
- решает, пускать ли на под трафик; контейнер не трогает
- проба живости
- решает, перезапускать ли контейнер
Проба запуска и медленный старт
Классическая авария: приложение поднимается 40 секунд (греет кэш, читает справочники), а проба живости начинает стучать через 10 и после трёх неудач убивает контейнер. Приложение не успевает стартовать никогда, а в логах видно бесконечный цикл перезапусков.
Лечится это отдельной пробой ЗАПУСКА: пока она не прошла, остальные пробы не работают вовсе. Ей дают щедрый предел - например, тридцать попыток по 10 секунд, то есть пять минут на старт, - а живости оставляют жёсткие короткие настройки. Так медленный старт и зависание в работе перестают конфликтовать.
// Старый способ - задержка первой проверки. Он хуже: задержку приходится ставить по худшему случаю, и всё это время зависший контейнер не будет замечен. Проба запуска эту дилемму снимает: она проверяет по-настоящему, а не ждёт по часам.
- проба запуска
- работает только на старте; пока не прошла, остальные пробы молчат
- порог неудач
- сколько неудачных проверок подряд считать поломкой
Что именно проверять
Проба живости должна проверять только сам процесс: отвечает ли он вообще. Простой адрес, который возвращает успех без обращения к чему-либо снаружи. Как только в живость попадает проверка базы, происходит следующее: база моргнула на минуту - и кластер ПЕРЕЗАПУСТИЛ все поды сервиса разом. Приложение было здорово, а теперь ещё и холодное.
Проба готовности, наоборот, может смотреть на зависимости: если без базы сервис не способен обслуживать запросы, честно вывести его из раздачи - правильное поведение. Но и тут есть край: если все реплики одновременно уйдут из раздачи, сервис исчезнет целиком. Иногда полезнее отдавать частичный ответ, чем полностью выпасть.
// Практические настройки, которые определяют скорость реакции: период между проверками, число неудач подряд до срабатывания и предел ожидания ответа. Ошибка в пределе ожидания даёт тихую беду: если проба ждёт ответа дольше, чем период между проверками, проверки наслаиваются, и под получает лишнюю нагрузку от собственного мониторинга.
- живость без зависимостей
- проверяет только сам процесс; падение базы не должно перезапускать поды
- период и предел ожидания
- как часто спрашиваем и сколько ждём ответа; вместе задают скорость реакции
Как отвечать: «Чем readiness отличается от liveness и что бывает, если их перепутать?»
Разные действия по итогу проверки. Готовность решает, пускать ли на под трафик: не прошла - адрес убирают из сервиса, а контейнер не трогают. Живость решает, перезапускать ли контейнер. Я проверял оба случая на стенде: под с падающей готовностью остался в состоянии Running с нулём перезапусков, но список адресов за сервисом стал пустым; под с падающей живостью ушёл в цикл перезапусков. Путают их двумя способами. Первый: в живость кладут проверку базы - и когда база моргает, кластер перезапускает все поды сервиса разом, хотя приложение здорово. Второй: живость ставят слишком агрессивно для медленно стартующего приложения, и оно не успевает подняться никогда; лечится отдельной пробой запуска, которая даёт щедрое время на старт и отключает остальные пробы, пока не пройдёт.
Ответ различает механизмы по действию, подтверждает измерением и называет обе типовые ошибки. Про пробу запуска вспоминают только те, кто ловил бесконечный цикл перезапусков.
На чём валятся
- −Кладут проверку базы в пробу живости: падение базы перезапускает все поды разом.
- −Ставят живость медленно стартующему приложению без пробы запуска.
- −Считают, что неготовый под перезапустится сам. Он останется работать, просто без трафика.
- −Задают предел ожидания больше периода проверок и получают наслоение проверок.
- −Обещают обновление без простоя, не настроив пробу готовности.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 7, остальные разбираются в тренажёре.
- В liveness-пробу добавили проверку доступности базы. Чем это опасно?A)Проба станет медленной и начнёт нагружать базу лишними запросамиB)Kubelet перестанет доверять пробе и отключит её после серии отказовC)Моргнула база — и кластер разом перезапустит все поды сервисаD)Проба будет считаться успешной, пока база отвечает хоть чем-то
показать ответ и разбор
+C)Моргнула база — и кластер разом перезапустит все поды сервиса// разбор: Liveness должна отвечать на вопрос о самом процессе. Если в неё зашита внешняя зависимость, то её недоступность превращается в массовый рестарт: поды падают одновременно, теряют прогретые кэши и пулы, а после старта снова бьются в ту же базу — авария усиливается вместо восстановления. Зависимости проверяют в readiness: под просто уйдёт из балансировки и вернётся сам.
- При выкатке часть запросов ловит 502, хотя приложение корректно обрабатывает SIGTERM. Почему?A)Не задан terminationGracePeriodSeconds, и под убивают мгновенноB)Эндпоинты обновляются медленнее, чем гаснет подC)Readiness-проба продолжает отвечать успехом до самого концаD)Балансировщик кэширует адреса подов и не следит за эндпоинтами
показать ответ и разбор
+B)Эндпоинты обновляются медленнее, чем гаснет под// разбор: Удаление пода запускает две независимые цепочки: kubelet шлёт SIGTERM, а контроллер эндпоинтов убирает под из сервиса, после чего kube-proxy на каждой ноде переписывает правила. Вторая цепочка отстаёт, и приложение, честно закрывшее слушающий сокет, получает соединения от ещё не обновившихся нод. Лечится хуком preStop с паузой в несколько секунд: под держит трафик, пока правила разъезжаются.
- Тяжёлое приложение стартует минуту. liveness-проба убивает его раньше, чем оно поднялось, — CrashLoop. Что добавить?A)startupProbe: пока идёт старт, liveness не считаетсяB)Просто убрать liveness-пробу совсемC)Увеличить readinessProbe, она прикроет стартD)Поднять число реплик, пока часть стартует
показать ответ и разбор
+A)startupProbe: пока идёт старт, liveness не считается// разбор: startupProbe создан ровно для медленного старта: пока он не прошёл, liveness и readiness не проверяются, и приложение не убивают на прогреве. После успешного старта включается обычная liveness. Альтернатива-костыль — большой initialDelaySeconds у liveness, но startupProbe гибче для приложений с непредсказуемым временем старта.
- Поды периодически перезапускаются без явной причины: liveness иногда не отвечает под нагрузкой. Что подкрутить?A)Перенести проверку в readiness — там перезапусков нетB)Отключить liveness на время пиковC)Слишком строгие пороги пробыD)Добавить ещё одну liveness-пробу для надёжности
показать ответ и разбор
+C)Слишком строгие пороги пробы// разбор: Дефолты пробы часто слишком жёсткие: один медленный ответ под нагрузкой — и failureThreshold превышен, под перезапущен. Подкручивают timeoutSeconds (проба не должна падать по таймауту при обычной нагрузке), periodSeconds и failureThreshold (сколько подряд промахов терпим), initialDelay. Цель — чтобы liveness ловила реальные зависания, а не редкие всплески задержки.
- Под нагрузкой поды по очереди выпадают из сервиса и возвращаются: readiness то проходит, то нет. Как разорвать цикл?A)Убрать readiness-пробу, чтобы поды перестали выпадать из сервисаB)Ужесточить порог readiness, чтобы она срабатывала порежеC)Перевести readiness в liveness, и пусть под перезапускаетсяD)Readiness конкурирует с трафиком и флапает; сделать её лёгкой
показать ответ и разбор
+D)Readiness конкурирует с трафиком и флапает; сделать её лёгкой// разбор: Спираль смерти: под нагрузкой readiness-проба, конкурирующая с трафиком за тот же пул/поток или делающая тяжёлую проверку, начинает таймаутить; под выкидывают из Service, его доля нагрузки переезжает на оставшиеся поды, чьи пробы тоже начинают падать — здоровые поды выпадают ровно тогда, когда нужны. Лечится: readiness должна быть дешёвой и независимой от нагрузки (лёгкий эндпоинт, достаточный timeout, не падать на кратковременном насыщении), а масштабировать — по реальным метрикам.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.