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