Введение
Состояние Running означает лишь то, что процесс контейнера существует. Оно не подтверждает, что приложение готово принимать трафик или способно самостоятельно восстановиться. Пробы Kubernetes превращают специфичные для приложения проверки состояния в действия контроллеров и Service.
Вы добавите все три типа проб, увидите, как проверка готовности удаляет Pod из конечных точек Service, исправите возникшую проблему и добавите обработчик корректного завершения работы.
Важно не смешивать назначения механизмов: проба запуска защищает приложение от преждевременных проверок во время медленной инициализации, проба готовности определяет, может ли Pod получать трафик Service, а проба работоспособности решает, когда kubelet следует перезапустить зависший контейнер. Использование одной пробы для всех задач часто приводит к ненужным перезапускам или преждевременной передаче трафика.
Развёртывание приложения с пробами
На этом шаге разверните NGINX с проверками запуска, готовности и работоспособности для пути / на именованном порту http.
Манифест получился длиннее, поскольку все три пробы находятся внутри спецификации контейнера. Перед применением прочитайте его в следующем порядке: Deployment и метки, контейнер и именованный порт, затем пробы запуска, готовности и работоспособности. В этом примере все три проверки используют одну и ту же HTTP-конечную точку, но последствия их сбоев различаются.
Некоторые однострочные отображения и списки используют компактный «потоковый стиль» YAML, например {app: healthy-web}. Фигурные скобки обозначают отображение, а квадратные — список; по смыслу они эквивалентны многострочным вариантам YAML. Используйте такой формат только тогда, когда всё значение остаётся легко читаемым.
Порог запуска допускает до 24 секунд (12 × 2), прежде чем Kubernetes сочтёт запуск неудачным. После успешного запуска проба готовности выполняется каждые три секунды, а проба работоспособности — каждые пять секунд.
cd /home/labex/project/application-health
cat <<'EOF' > healthy-web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: healthy-web
namespace: practice-health
spec:
replicas: 2
selector:
matchLabels: {app: healthy-web}
template:
metadata:
labels: {app: healthy-web}
spec:
containers:
- name: web
image: nginx:1.27-alpine
imagePullPolicy: IfNotPresent
ports:
- {name: http, containerPort: 80, protocol: TCP}
startupProbe:
httpGet: {path: /, port: http}
failureThreshold: 12
periodSeconds: 2
readinessProbe:
httpGet: {path: /, port: http}
periodSeconds: 3
livenessProbe:
httpGet: {path: /, port: http}
periodSeconds: 5
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
name: healthy-web
namespace: practice-health
spec:
selector: {app: healthy-web}
ports:
- {name: http, port: 80, targetPort: http, protocol: TCP}
EOF
Создайте пространство имён и примените манифест. Команда rollout status ожидает, пока обе реплики пройдут проверку готовности, а не просто запустят свои процессы:
kubectl create namespace practice-health
kubectl apply -f healthy-web.yaml
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s
Проба запуска защищает медленную инициализацию, проба готовности управляет допуском к трафику, а проба работоспособности позволяет перезапустить контейнер после неисправимого сбоя.
Если развёртывание завершилось успешно, вы уже знаете больше, чем просто то, что «контейнеры запущены»: оба Pod успешно ответили на запрос проверки готовности.
Последняя строка вывода о развёртывании — первое подтверждение работоспособности:
Waiting for deployment "healthy-web" rollout to finish: 0 of 2 updated replicas are available...
Waiting for deployment "healthy-web" rollout to finish: 1 of 2 updated replicas are available...
deployment "healthy-web" successfully rolled out
Промежуточные строки являются нормальными. Kubernetes сообщает о ходе операции по мере того, как каждый Pod проходит проверки запуска и готовности; только последняя строка означает, что обе реплики доступны.
Связь проверки готовности с конечными точками Service
На этом шаге изучите условие готовности и EndpointSlices.
Сначала выведите привычную таблицу Pod, затем ограничьте вывод условием готовности и числом перезапусков. Пользовательская таблица помогает отличить состояние «не готов» от состояния «перезапускается»:
kubectl get pods -n practice-health -l app=healthy-web
kubectl get pods -n practice-health -l app=healthy-web -o custom-columns='POD:.metadata.name,READY_CONDITION:.status.conditions[?(@.type=="Ready")].status,RESTARTS:.status.containerStatuses[0].restartCount'
kubectl get endpointslices -n practice-health -l kubernetes.io/service-name=healthy-web -o jsonpath='{range .items[].endpoints[*]}{.addresses[0]} ready={.conditions.ready}{"\n"}{end}'
У обоих адресов должно отображаться ready=true. Сбой проверки готовности оставляет контейнер запущенным, но удаляет его адрес из обычной маршрутизации Service.
EndpointSlices связывают селектор Service с фактическими адресами Pod. Сам Service продолжает существовать, даже если Pod не готов; именно состояние его конечной точки предотвращает передачу туда обычного трафика.
Типичный вывод для работоспособного состояния выглядит так:
POD READY_CONDITION RESTARTS
healthy-web-... True 0
healthy-web-... True 0
10.244.104.11 ready=true
10.244.104.12 ready=true
Имена Pod и IP-адреса будут отличаться. Важна взаимосвязь: двум Pod с Ready=True соответствуют две конечные точки с ready=true, при этом ни один контейнер не перезапускался.
Наблюдение за сбоем проверки готовности
На этом шаге намеренно измените только путь проверки готовности на отсутствующий URL.
Это контролируемый сбой. JSON Patch заменяет одно вложенное поле, поэтому пробы запуска и работоспособности остаются без изменений. NGINX возвращает HTTP 404 для /not-ready, что проверка готовности воспринимает как ошибку.
kubectl patch deployment healthy-web -n practice-health --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/not-ready"}]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=20s || true
kubectl get pods -n practice-health -l app=healthy-web
kubectl get endpointslices -n practice-health -l kubernetes.io/service-name=healthy-web -o jsonpath='{range .items[].endpoints[*]}{.addresses[0]} ready={.conditions.ready}{"\n"}{end}'
|| true помечает истечение времени ожидания развёртывания как ожидаемое, чтобы оболочка продолжила выполнение. Новые контейнеры запускаются, но проверка готовности завершается ошибкой, поэтому Deployment не может перейти в доступное состояние.
В ходе проверки развёртывания получилось состояние, похожее на следующее:
error: timed out waiting for the condition
NAME READY STATUS RESTARTS
healthy-web-old-... 1/1 Running 0
healthy-web-old-... 1/1 Running 0
healthy-web-new-... 0/1 Running 0
...
10.244.104.17 ready=false
Новый Pod находится в состоянии Running, но не готов, и при этом не перезапускался. Два старых готовых Pod остаются запущенными, поскольку при поэтапном обновлении Deployment сохраняет доступность: Kubernetes не станет заменять дополнительные здоровые реплики, пока новая версия не сможет пройти проверку готовности. Поэтому истечение времени ожидания развёртывания подтверждает как работу проверки готовности, так и защитный механизм контроллера. В выводе EndpointSlice новый адрес имеет ready=false, поэтому обычная маршрутизация Service по-прежнему использует только работоспособные конечные точки.
Восстановление готовности и проверка трафика
На этом шаге восстановите путь проверки готовности и дождитесь возвращения конечных точек Service.
Используйте обратную версию предыдущего исправления, чтобы в эксперименте изменялась только одна переменная. Теперь ожидание статуса развёртывания завершится успешно, поскольку заменённые Pod отвечают на запрос / кодом HTTP 200.
Команда kubectl run health-client создаёт временный автономный Pod для выполнения запроса. Параметр --image выбирает BusyBox, --image-pull-policy=IfNotPresent повторно использует кэшированный образ, а --restart=Never запрашивает именно Pod, а не контроллер. Параметр --rm -i оставляет команду подключённой и удаляет Pod после завершения; второй -- отделяет параметры kubectl от команды wget, выполняемой внутри контейнера.
В wget -qO- параметр -q скрывает сообщения о ходе выполнения, а -O- выводит тело ответа в стандартный вывод. Канал передаёт это тело команде head, которая отображает только первые строки. URL использует имя Service healthy-web, которое кластерная DNS разрешает внутри пространства имён.
kubectl patch deployment healthy-web -n practice-health --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/"}]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s
kubectl run health-client -n practice-health --image=busybox:1.36 --image-pull-policy=IfNotPresent --restart=Never --rm -i -- wget -qO- http://healthy-web | head
Временный клиент использует кластерную DNS. Ограничение отображаемого HTML делает вывод более удобным для чтения, а успешный ответ подтверждает согласованную работу проверки готовности и маршрутизации Service.
Ответ начинается с узнаваемого HTML NGINX:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
Получение HTML после успешного развёртывания подтверждает весь путь запроса: DNS разрешила имя Service, Service выбрал готовую конечную точку, а NGINX обработал запрос.
После этого удаление временного Pod позволит не оставлять диагностические клиенты в пространстве имён.
Добавление корректного завершения работы
На этом шаге добавьте обработчик preStop и период ожидания перед завершением. Обработчик ненадолго задерживает остановку, чтобы удаление конечной точки успело распространиться до завершения процесса.
Рассмотрите отдельно две операции JSON Patch. Первая добавляет 20-секундный срок на завершение Pod. Вторая добавляет обработчик ожидания на пять секунд для контейнера с индексом 0. Kubernetes запускает обработчик в начале завершения, пока оставшееся время ожидания продолжает отсчитываться.
kubectl patch deployment healthy-web -n practice-health --type=json -p='[
{"op":"add","path":"/spec/template/spec/terminationGracePeriodSeconds","value":20},
{"op":"add","path":"/spec/template/spec/containers/0/lifecycle","value":{"preStop":{"sleep":{"seconds":5}}}}
]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s
kubectl get deployment healthy-web -n practice-health -o jsonpath='grace={.spec.template.spec.terminationGracePeriodSeconds}s preStop={.spec.template.spec.containers[0].lifecycle.preStop.sleep.seconds}s{"\n"}'
JSON patch изменяет только два указанных поля, поэтому пробы и остальные параметры контейнера сохраняются. Корректное завершение дополняет проверку готовности: одна управляет допуском к трафику, а другое даёт Kubernetes время удалить Pod из трафика до полного завершения остановки.
Итоговый вывод JSONPath должен быть таким: grace=20s preStop=5s. Пятитсекундный обработчик укладывается в 20-секундный лимит, оставляя NGINX время на нормальное завершение.
Развёртывание может приостановиться на сообщениях вроде 1 old replicas are pending termination, поскольку новый обработчик preStop намеренно добавляет пять секунд к остановке. Итоговый вывод остаётся таким:
deployment "healthy-web" successfully rolled out
grace=20s preStop=5s
Итоги
Вы связали проверки запуска, готовности и работоспособности с поведением Kubernetes, увидели, как проверка готовности удаляет конечные точки без остановки контейнеров, восстановили передачу трафика и добавили корректное завершение работы. Эти механизмы делают состояние приложения не просто наблюдаемым, а пригодным для принятия решений.


