Модуль 11 / 19
Probes, startup, graceful shutdown и readiness
Содержание
1. Цели и результат модуля
Вы научитесь проектировать разные health signals для старта, маршрутизации и самовосстановления, а также проверять завершение Pod без обрыва трафика.
- startupProbe защищает медленный старт
- readiness управляет участием в Service
- liveness инициирует restart контейнера
- preStop и SIGTERM дают время на drain
- Events связывают probe failure с действием kubelet
2. Теория: три разных вопроса
Startup отвечает: приложение уже завершило запуск? Readiness: можно ли сейчас отправлять ему новый трафик? Liveness: способен ли процесс восстановиться без рестарта? Один endpoint для всех трёх вопросов часто превращает временную зависимость в restart loop.
Probe оценивает контейнер с точки зрения kubelet на его Node. Она не доказывает работоспособность всей внешней цепки DNS, ingress или удалённой зависимости.
3. Теория: exec, HTTP, TCP и gRPC
Exec проверяет exit code команды внутри контейнера. HTTP success обычно 200–399; TCP доказывает лишь accept соединения; gRPC использует стандартный health protocol. Выбирайте самый дешёвый сигнал, который отвечает именно на вопрос probe.
timeoutSeconds, periodSeconds, successThreshold и failureThreshold формируют detection window. Слишком короткие значения создают ложные отказы и дополнительную нагрузку.
4. StartupProbe и медленный запуск
Пока startupProbe не успешна, liveness и readiness не выполняются. Максимальное окно запуска примерно failureThreshold × periodSeconds с учётом timeout. После первого успеха startupProbe больше не запускается для этого container instance.
5. Readiness и EndpointSlice
Readiness failure не перезапускает контейнер. Pod получает Ready=False, а EndpointSlice отмечает endpoint неготовым, чтобы Service перестал направлять новый трафик. Процесс может продолжать фоновые задачи и восстановить readiness.
Ready endpoint и момент прекращения внешнего трафика не абсолютно синхронны: controllers, proxy configuration и существующие connections имеют задержки.
6. Liveness и цена рестарта
После серии liveness failures kubelet завершает и перезапускает контейнер согласно restartPolicy. Liveness должна обнаруживать состояние, которое действительно исправляется restart, а не временную недоступность базы.
preStop выполняется и при probe-driven termination. Поэтому Last State может быть Completed/0, хотя trigger был liveness failure; доказательство находится в Events Unhealthy и росте restartCount.
7. Graceful shutdown и методика
При удалении Pod API задаёт deletionTimestamp, endpoint становится terminating/not ready, kubelet запускает preStop, посылает TERM и ждёт terminationGracePeriodSeconds, затем применяет KILL. Приложение должно перестать принимать новый трафик и завершить in-flight requests.
- Разделяйте start, ready и live signals
- Учитывайте worst-case startup time
- Не включайте все внешние зависимости в liveness
- Сверяйте probe port/path с реальным listener
- Сопоставляйте preStop и grace period
- Читайте Events до перезапуска Pod
- Проверяйте EndpointSlice при readiness failure
8. Лабораторная работа: startupProbe
Приложение намеренно ждёт восемь секунд. Наблюдайте Running 0/1, затем Ready 1/1 без liveness restart: startupProbe дала корректное окно запуска.
mkdir -p /tmp/k8s-m11
cat > /tmp/k8s-m11/app.yaml <<'EOF'
apiVersion: v1
kind: Namespace
metadata: {name: k8s-m11}
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: web, namespace: k8s-m11}
spec:
replicas: 1
selector: {matchLabels: {app: probe-web}}
template:
metadata: {labels: {app: probe-web}}
spec:
terminationGracePeriodSeconds: 15
containers:
- name: nginx
image: nginx:1.29-alpine
command: ["sh", "-c"]
args: ["rm -f /tmp/live /tmp/ready; sleep 8; touch /tmp/live /tmp/ready; exec nginx -g 'daemon off;'"]
ports: [{name: http, containerPort: 80}]
startupProbe:
exec: {command: ["sh", "-c", "test -f /tmp/live"]}
periodSeconds: 1
failureThreshold: 15
readinessProbe:
exec: {command: ["sh", "-c", "test -f /tmp/ready"]}
periodSeconds: 1
failureThreshold: 2
livenessProbe:
exec: {command: ["sh", "-c", "test -f /tmp/live"]}
periodSeconds: 1
failureThreshold: 3
lifecycle:
preStop:
exec: {command: ["sh", "-c", "rm -f /tmp/ready; sleep 5"]}
---
apiVersion: v1
kind: Service
metadata: {name: web, namespace: k8s-m11}
spec:
selector: {app: probe-web}
ports: [{name: http, port: 80, targetPort: http}]
EOF
kubectl apply -f /tmp/k8s-m11/app.yaml || true
kubectl apply --dry-run=server -f /tmp/k8s-m11/app.yaml
kubectl apply -f /tmp/k8s-m11/app.yaml
kubectl -n k8s-m11 get pod -wkubectl -n k8s-m11 get deployment,pod,service,endpointslice
pod=$(kubectl -n k8s-m11 get pod -l app=probe-web -o jsonpath='{.items[0].metadata.name}')
kubectl -n k8s-m11 get pod "$pod" -o jsonpath='started={.status.containerStatuses[0].started}{" ready="}{.status.containerStatuses[0].ready}{" restarts="}{.status.containerStatuses[0].restartCount}{"\n"}'
kubectl -n k8s-m11 describe pod "$pod"
kubectl -n k8s-m11 get endpointslice -l kubernetes.io/service-name=web -o yaml9. Лабораторная работа: readiness и endpoints
Удаление readiness-файла оставляет restartCount прежним, но Ready становится false и endpoint исключается из трафика. Возврат файла восстанавливает Pod.
pod=$(kubectl -n k8s-m11 get pod -l app=probe-web -o jsonpath='{.items[0].metadata.name}')
kubectl -n k8s-m11 exec "$pod" -- rm /tmp/ready
sleep 3
kubectl -n k8s-m11 get pod "$pod" -o custom-columns=NAME:.metadata.name,READY:.status.containerStatuses[0].ready,RESTARTS:.status.containerStatuses[0].restartCount
kubectl -n k8s-m11 get endpointslice -l kubernetes.io/service-name=web -o jsonpath='{range .items[0].endpoints[*]}address={.addresses[0]} ready={.conditions.ready}{"\n"}{end}'
kubectl -n k8s-m11 exec "$pod" -- touch /tmp/ready
kubectl -n k8s-m11 wait --for=condition=Ready "pod/$pod" --timeout=30s10. Лабораторная работа: liveness restart
Удалите liveness-файл. Event Unhealthy является причиной действия kubelet; после preStop контейнер может иметь Last State Completed с exit 0, но restartCount увеличивается.
pod=$(kubectl -n k8s-m11 get pod -l app=probe-web -o jsonpath='{.items[0].metadata.name}')
before=$(kubectl -n k8s-m11 get pod "$pod" -o jsonpath='{.status.containerStatuses[0].restartCount}')
kubectl -n k8s-m11 exec "$pod" -- rm /tmp/live
kubectl -n k8s-m11 get events --field-selector involvedObject.name="$pod" --watch
# Остановите watch после события Unhealthy и перезапуска.
kubectl -n k8s-m11 wait --for=condition=Ready "pod/$pod" --timeout=60s
kubectl -n k8s-m11 get pod "$pod" -o jsonpath='restarts={.status.containerStatuses[0].restartCount}{" lastReason="}{.status.containerStatuses[0].lastState.terminated.reason}{" lastExit="}{.status.containerStatuses[0].lastState.terminated.exitCode}{"\n"}'
printf 'restarts before=%s\n' "$before"
kubectl -n k8s-m11 describe pod "$pod"11. Диагностика: graceful shutdown и сломанная probe
Удалите Pod без ожидания и сравните terminating endpoint с новым Pod. Пятисекундный preStop укладывается в grace period 15 секунд.
old_pod=$(kubectl -n k8s-m11 get pod -l app=probe-web -o jsonpath='{.items[0].metadata.name}')
date -Iseconds
kubectl -n k8s-m11 delete pod "$old_pod" --wait=false
sleep 2
kubectl -n k8s-m11 get pod -o wide
kubectl -n k8s-m11 get endpointslice -l kubernetes.io/service-name=web -o jsonpath='{range .items[0].endpoints[*]}address={.addresses[0]} ready={.conditions.ready} terminating={.conditions.terminating}{"\n"}{end}'
kubectl -n k8s-m11 wait --for=delete "pod/$old_pod" --timeout=30s
kubectl -n k8s-m11 rollout status deployment/web --timeout=60s
date -IsecondsЗатем readiness никогда не становится успешной. Новый ReplicaSet существует, но rollout зависает и Service не должен направлять к нему трафик. Восстановите предыдущий template через rollback.
kubectl -n k8s-m11 patch deployment web --type=json -p='[
{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/exec/command","value":["sh","-c","test -f /tmp/never-ready"]}
]'
kubectl -n k8s-m11 rollout status deployment/web --timeout=20s || true
kubectl -n k8s-m11 get deployment,pod,endpointslice
kubectl -n k8s-m11 describe deployment web
kubectl -n k8s-m11 describe pod -l app=probe-web
kubectl -n k8s-m11 rollout undo deployment/web
kubectl -n k8s-m11 rollout status deployment/web --timeout=90s12. Контроль, критерии освоения и cleanup
- Какой вопрос задаёт каждая probe?
- Почему TCP не доказывает корректный HTTP?
- Когда отключается startupProbe?
- Readiness failure рестартует контейнер?
- Как readiness связана с EndpointSlice?
- Когда liveness вредна?
- Почему Last State Completed не исключает liveness trigger?
- Что делает preStop?
- Что происходит после grace period?
- Почему rollout может зависнуть при Running Pods?
kubectl delete namespace k8s-m11 --wait=true
rm -rf /tmp/k8s-m11
kubectl get namespace k8s-m11 2>&1 || trueМодуль освоен, если вы независимо вызываете readiness failure и liveness restart, подтверждаете их разными evidence и наблюдаете graceful termination.