Startup, readiness, liveness и завершение API
У API «Площадки» есть задержка начальной готовности. Процесс уже слушает порт, но каталог первые три секунды возвращает 503. Пока Kubernetes не знает об этой особенности, он не может использовать её при выборе адресатов Service. Теперь свяжем состояние программы с startupProbe, readinessProbe и livenessProbe. Каждая отвечает на свой вопрос.
Полный API Deployment находится в 06-api-health.yaml и уже ссылается на конфигурацию предыдущего урока. Исходники сервера содержат соответствующие HTTP-маршруты и обработку SIGTERM. Материал описывает ожидаемое поведение; автор не запускал probes, не удалял Pod и не проверял завершение соединений.
Три вопроса о состоянии процесса
Startup probe отвечает, завершилось ли начальное включение приложения. Readiness probe определяет, следует ли направлять к экземпляру обычный трафик Service. Liveness probe помогает обнаружить состояние, из которого процесс не может восстановиться сам и требуется перезапуск контейнера. Хотя все три могут быть HTTP-запросами, последствия не одинаковы.
Для API startup и readiness используют признак ready: задержка закончилась и не началось завершение. Liveness /healthz пока отвечает 200, когда обработчик способен работать. Мы намеренно не проверяем там доступность всей внешней системы. Если временно недоступна база, массовый перезапуск всех API может только увеличить число подключений и ухудшить ситуацию.
Фрагмент полного 06-api-health.yaml:
startupProbe:
httpGet:
path: /startupz
port: http
periodSeconds: 2
failureThreshold: 15
readinessProbe:
httpGet:
path: /readyz
port: http
periodSeconds: 2
livenessProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 10
Startup получает около тридцати секунд для последовательных неудачных проверок. Это запас для учебной трёхсекундной задержки, а не универсальный норматив. Пока startup не завершён, liveness и readiness не выполняются обычным образом для этого контейнера. Если startup постоянно не проходит, контейнер будет перезапущен. Общие правила описаны в документации probes.
Время и число ошибок нужно выбирать по наблюдениям реального приложения. Слишком короткий порог превращает медленный запуск в цикл перезапусков. Слишком длинный оставляет неработающую копию незамеченной. Учебные числа показывают форму решения; они не получены из замера «Площадки» и не должны восприниматься как доказанная оптимальная настройка.
Readiness влияет на трафик
Представим rolling update двух копий API. Новая копия запускается, но ещё возвращает 503 на /readyz. Старые готовые экземпляры продолжают обслуживать каталог. После прохождения readiness новый Pod становится подходящим адресатом обычного трафика Service. Так появляется связь между жизненным циклом процесса и поведением HTTP-пользователя.
Неуспешная readiness сама по себе не требует перезапуска контейнера. Программа может временно не принимать запросы, а затем восстановиться. Поэтому использовать readiness для принудительного «лечения» любого зависания неверно. Нужно различать временную непригодность экземпляра и состояние, которое требует пересоздания процесса.
Readiness не доказывает корректность всех пользовательских операций. Наш endpoint подтверждает только завершение начальной задержки и отсутствие завершения. Если разработчик сломал сериализацию каталога, probe может остаться зелёной. Признак готовности должен быть достаточно содержательным для допуска трафика и достаточно дешёвым, чтобы сам не стал источником нагрузки.
Для web готовность не зависит от успешности API. Он способен отдать страницу и сообщить пользователю об ошибке каталога. Если связать readiness web со всей цепочкой backend, временный сбой API может убрать и интерфейс, через который пользователь узнавал состояние. Иногда такая связанная проверка нужна, но её последствия должны соответствовать продукту.
SIGTERM и запас на завершение
При завершении Pod приложению нужно перестать принимать новую работу и закончить уже начатую. В API это делает существующий обработчик:
process.on('SIGTERM', () => {
shuttingDown = true;
server.close(() => process.exit(0));
setTimeout(() => process.exit(1), 20000).unref();
});
Значение shuttingDown делает readiness отрицательной. server.close прекращает принимать новые соединения и ждёт завершения обслуживаемых запросов. Отдельный таймер ограничивает ожидание двадцатью секундами. Метод unref не удерживает процесс живым, если другой работы уже нет. Это защита от бесконечного ожидания, а не подтверждение, что каждое обращение завершится успешно.
В Deployment задано terminationGracePeriodSeconds: 30. Kubernetes оставляет окно для корректного завершения, после которого процесс может быть принудительно остановлен. Мы выбираем внутренний предел меньше этого окна, чтобы приложение успело завершиться самостоятельно. Реальный порядок прекращения маршрутизации и доставки запросов асинхронен; между компонентами возможна небольшая гонка.
Не считайте удаление Pod мгновенным прекращением всех запросов. Клиент может держать соединение, прокси — переиспользовать его, а обновление адресатов — распространяться отдельно. Для долгих операций требуются дополнительные соглашения: таймаут клиента, возможность повторить безопасный запрос, отмена работы и подходящая длительность завершения. Один YAML-параметр не решает все эти задачи.
Разбор двух ошибочных настроек
Первый ошибочный вариант — liveness на /api/lessons, который возвращает 503 в начальную задержку. Без startup-контроля процесс может перезапускаться раньше, чем успевает стать готовым. Каждый новый процесс снова начинает задержку. Внешне кажется, что сервер «никогда не запускается», хотя причина создана политикой проверки.
Второй вариант — readiness проверяет только открытый TCP-порт. Для нашего API это произойдёт раньше завершения инициализации. Service начнёт отправлять каталог, а пользователь получит 503. TCP probe бывает уместной для иных приложений, но в этом проекте есть предметный HTTP-признак. Проверка должна отвечать на нужный вопрос, а не просто быть самой короткой.
В будущей лаборатории читатель может посмотреть describe pod, условия готовности и Events, сопоставив их с BOOT_DELAY_MS. Для длинной задержки, превышающей окно startup, ожидаются неудачные проверки и перезапуск. Это инструкция самостоятельного наблюдения, не выполненный эксперимент. Изменённую конфигурацию нужно вернуть до перехода к следующим темам.
В Deployment также задано minReadySeconds: 5: новая копия должна некоторое время оставаться готовой для оценки доступности обновления. Это отдельный параметр rollout, а не замена readiness. Он помогает избежать слишком раннего продвижения версии, которая кратко стала готовой и снова упала. При дальнейшей настройке ресурсов увидим, почему готовность может зависеть и от выделенной памяти.
После этого урока Service выбирает экземпляры по более содержательному состоянию, а приложение знает о завершении. Далее добавим requests и limits, чтобы описать потребности экземпляров и понять причины ожидания размещения.