← До програми курсу

Модуль 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.