← К программе курса

Модуль 11 / 19

Probes, startup, graceful shutdown и readiness

KUBERNETES

Содержание
01

1. Цели и результат модуля

Вы научитесь проектировать разные health signals для старта, маршрутизации и самовосстановления, а также проверять завершение Pod без обрыва трафика.

  • startupProbe защищает медленный старт
  • readiness управляет участием в Service
  • liveness инициирует restart контейнера
  • preStop и SIGTERM дают время на drain
  • Events связывают probe failure с действием kubelet
02

2. Теория: три разных вопроса

Startup отвечает: приложение уже завершило запуск? Readiness: можно ли сейчас отправлять ему новый трафик? Liveness: способен ли процесс восстановиться без рестарта? Один endpoint для всех трёх вопросов часто превращает временную зависимость в restart loop.

Probe оценивает контейнер с точки зрения kubelet на его Node. Она не доказывает работоспособность всей внешней цепки DNS, ingress или удалённой зависимости.

03

3. Теория: exec, HTTP, TCP и gRPC

Exec проверяет exit code команды внутри контейнера. HTTP success обычно 200–399; TCP доказывает лишь accept соединения; gRPC использует стандартный health protocol. Выбирайте самый дешёвый сигнал, который отвечает именно на вопрос probe.

timeoutSeconds, periodSeconds, successThreshold и failureThreshold формируют detection window. Слишком короткие значения создают ложные отказы и дополнительную нагрузку.

04

4. StartupProbe и медленный запуск

Пока startupProbe не успешна, liveness и readiness не выполняются. Максимальное окно запуска примерно failureThreshold × periodSeconds с учётом timeout. После первого успеха startupProbe больше не запускается для этого container instance.

05

5. Readiness и EndpointSlice

Readiness failure не перезапускает контейнер. Pod получает Ready=False, а EndpointSlice отмечает endpoint неготовым, чтобы Service перестал направлять новый трафик. Процесс может продолжать фоновые задачи и восстановить readiness.

Ready endpoint и момент прекращения внешнего трафика не абсолютно синхронны: controllers, proxy configuration и существующие connections имеют задержки.

06

6. Liveness и цена рестарта

После серии liveness failures kubelet завершает и перезапускает контейнер согласно restartPolicy. Liveness должна обнаруживать состояние, которое действительно исправляется restart, а не временную недоступность базы.

preStop выполняется и при probe-driven termination. Поэтому Last State может быть Completed/0, хотя trigger был liveness failure; доказательство находится в Events Unhealthy и росте restartCount.

07

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
08

8. Лабораторная работа: startupProbe

Приложение намеренно ждёт восемь секунд. Наблюдайте Running 0/1, затем Ready 1/1 без liveness restart: startupProbe дала корректное окно запуска.

terminal
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 -w
terminal
kubectl -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 yaml
09

9. Лабораторная работа: readiness и endpoints

Удаление readiness-файла оставляет restartCount прежним, но Ready становится false и endpoint исключается из трафика. Возврат файла восстанавливает Pod.

terminal
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=30s
10

10. Лабораторная работа: liveness restart

Удалите liveness-файл. Event Unhealthy является причиной действия kubelet; после preStop контейнер может иметь Last State Completed с exit 0, но restartCount увеличивается.

terminal
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

11. Диагностика: graceful shutdown и сломанная probe

Удалите Pod без ожидания и сравните terminating endpoint с новым Pod. Пятисекундный preStop укладывается в grace period 15 секунд.

terminal
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.

terminal
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=90s
12

12. Контроль, критерии освоения и cleanup

  • Какой вопрос задаёт каждая probe?
  • Почему TCP не доказывает корректный HTTP?
  • Когда отключается startupProbe?
  • Readiness failure рестартует контейнер?
  • Как readiness связана с EndpointSlice?
  • Когда liveness вредна?
  • Почему Last State Completed не исключает liveness trigger?
  • Что делает preStop?
  • Что происходит после grace period?
  • Почему rollout может зависнуть при Running Pods?
terminal
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.