Прогрев, readiness и liveness — три разные задачи¶
Большинство сервисов отвечает на все три вопроса одним обработчиком, обычно ping базы. Каждое смешение создаёт свой сбой. Liveness с проверкой БД перезапускает все pod при её кратком отказе. Readiness, не переходящая в false, направляет трафик в завершающийся pod. Смешанный с readiness прогрев даёт холодные кеши или убитый до завершения pod. Я провёл один сервис через прогрев, отказ зависимости и SIGTERM и наблюдал намеренно разные ответы двух проб и рабочего маршрута.
Числа получены в эксперименте статьи: обе пробы и маршрут опрашиваются каждые сто миллисекунд в течение жизненного цикла. Версии: servicewright 0.10.0, FastAPI 0.141.1, Python 3.13.
Жизнь pod в шести переходах¶
0.0 s livez=ConnectError readyz=ConnectError /cached=ConnectError
3.3 s livez=200 readyz=200 /cached=200
6.1 s livez=200 readyz=503 /cached=200
9.2 s livez=200 readyz=200 /cached=200
11.2 s livez=200 readyz=503 /cached=200
13.2 s livez=ConnectError readyz=ConnectError /cached=ConnectError
Три секунды прогрева, Redis недоступен с шестой до девятой, SIGTERM на одиннадцатой. Каждое различие столбцов — отдельное решение.
В этом сервисе сокет открывается после прогрева. Сбой обязательной зависимости убирает под из маршрутизации без перезапуска; завершение также выключает readiness до закрытия сокета.
Прогрев отличается от readiness и liveness¶
Первые 3,3 секунды не отвечало ничего: ни маршрут, ни пробы. Слушатель привязывается после прогрева, чтобы не принимать запросы до подготовки кешей, пулов и клиентов.
У этого порядка есть следствие: во время прогрева HTTP liveness не может ответить. Это даже не 503, а отказ соединения. Проба с failureThreshold: 3 и periodSeconds: 5 имеет короткий бюджет и может убить долго прогревающийся pod, затем его замену и следующую. Развёртывание не поднимается; виден CrashLoopBackOff без ошибки приложения, которое выполняло порученную инициализацию.
Решение — startupProbe: она откладывает остальные пробы, допускает длинный стартовый бюджет и после успеха передаёт контроль короткой liveness. Прогрев — фаза с естественным концом.
Ещё два следствия. Ошибка обязательного прогрева должна прервать запуск, а не разрешить обслуживание холодным сервисом. SIGTERM во время прогрева должен переходить к очистке: отменённому при обновлении pod не нужно сначала заканчивать минутное заполнение кеша.
Liveness относится к процессу, а не зависимостям¶
С 6,1 до 9,2 секунды Redis был недоступен. Readiness стала 503, liveness оставалась 200, маршрут без Redis продолжал отвечать:
Если включить Redis в liveness, все реплики одновременно провалят пробу и перезапустятся при уже неисправной общей зависимости. Перезапуск не чинит Redis, зато уничтожает прогретые кеши и текущие запросы. После восстановления на Redis разом обрушится холодная группа новых pod.
Правило: liveness отвечает, застрял ли сам процесс. Заблокированный event loop, переставший выполнять работу пул потоков, невосстановимое внутреннее состояние. Если перезапуск не исправит причину, ей не место в liveness. Пока цикл работает, проба почти скучно зелёная — это нормально.
Readiness может зависеть от всего, что необходимо для обслуживания, но не от всего, что у pod просто есть. Здесь маршрут работал при false, потому что Redis ему не нужен. Слишком широкая проба исключает pod даже для работоспособных маршрутов. Где различие важно, полезна готовность по сервисам; иначе единый флаг проще при явном компромиссе.
Если все реплики провалят readiness из-за общей зависимости, недоступным станет весь сервис. Кеш, который можно было обойти, или рекомендации с запасным ответом приведут к полному отказу: балансировщику некуда отправлять запрос. Жёсткая зависимость может определять readiness, необязательная требует другого решения. Это аргумент о Redis fail open в другом месте.
Readiness должна выключиться раньше приёма запросов¶
При SIGTERM важен порядок обновления:
SIGTERM at 11.1 s, process exited at 13.3 s with code 0
readyz stopped saying 200 at 11.2 s (0.1 s after SIGTERM)
the route stopped answering at 13.2 s (2.1 s after SIGTERM)
requests kept succeeding for 2.0 s after readiness went false
Readiness стала false в пределах десятой секунды, сервис отвечал ещё две. Этот промежуток и нужен при остановке: сведения о выводе pod из Service распространяются не мгновенно. Kube-proxy, ingress и клиенты узнают об изменениях с задержкой, а запросы продолжают приходить уже останавливаемому pod.
Немедленное закрытие слушателя создаёт ошибки соединения для продолжающего идти трафика, которые могут выглядеть как 502 и вызывать повторы на другой, тоже завершающийся pod. Отсюда всплески 502 при каждом развёртывании, часто без соответствующих ошибок приложения.
Порядок: readiness false, продолжить обслуживание на время распространения, затем закрыть слушатель и завершить текущие запросы. Задержку нужно измерить в своём кластере — от false до фактического прекращения трафика. Остальная арифметика — drain grace, terminationGracePeriodSeconds, максимальный запрос — в статье об остановке.
Три проверки в таблице¶
| Liveness | Readiness | Startup | |
|---|---|---|---|
| Вопрос | Застрял ли процесс? | Направлять ли сюда трафик сейчас? | Закончилась ли инициализация? |
| Зависимости | Нет | Нужные для обслуживания | Проверяет завершение выбранного старта |
| Во время прогрева | Ещё не отвечает | False | Единственная активная проба |
| При отказе обязательной зависимости | True | False | Уже завершена |
| После SIGTERM | True до выхода процесса | Сразу false | Уже завершена |
| Последствие неудачи | Перезапуск pod | Прекращение маршрутизации | Продолжить ждать, затем перезапустить |
Часто удивляет последняя ячейка readiness: отрицательная готовность — не обязательно ошибка. Это решение маршрутизации. Прогревающийся, завершающийся или ожидающий обязательную зависимость pod ведёт себя правильно. Тревога на каждый отказ пробы шумит при каждом обновлении. Следите за последствиями для группы: нет готовых реплик либо их меньше необходимого.
Инструменты¶
Жизненный цикл предоставляет servicewright: прогрев до bind, readiness true после привязки всех точек входа и false до drain, реестр здоровья с отдельной liveness и readiness, учитывающей флаг и зарегистрированные проверки, настраиваемая задержка перед закрытием слушателя. Пробы обслуживает выбранный адаптер, порядок общий для HTTP, gRPC и worker.
Три вопроса, три ответа, шесть переходов. Один ping БД на всё способен дать и цикл перезапусков, и всплеск 502.