Rolling update и probes: как выкатка идёт без простоя
Любимый сценарный вопрос: «Опишите по шагам, что делает кластер, когда вы поменяли тег образа».
Вопрос: «Вы выкатили новую версию, и во время деплоя часть запросов упала с 502. Реплик было три, стратегия — RollingUpdate. Что пошло не так?»
Что на самом деле проверяет интервьюер
Это вопрос уровня middle+. Он проверяет, понимает ли кандидат, что «Pod запущен» и «Pod готов принимать трафик» — разные вещи, и знает ли он, чем управляется скорость и безопасность выкатки. Ошибки при деплое почти всегда объясняются двумя причинами: отсутствием readiness probe и отсутствием корректного завершения работы.
Развёрнутый ответ: rolling update по шагам
Вы поменяли image: api:1.4.2 на api:1.5.0 и применили манифест. Дальше:
- Deployment видит, что шаблон пода изменился, и создаёт новый ReplicaSet с нулём реплик. Старый остаётся с тремя.
- Согласно
maxSurgeновый ReplicaSet поднимает первые поды. ПриmaxSurge: 1в кластере временно будет 4 пода. - Новый под проходит планирование, скачивание образа и запуск. Трафик он не получает, пока не пройдёт readiness probe.
- Как только под стал Ready, его адрес попадает в EndpointSlice, и Service начинает слать на него запросы.
- Deployment гасит один старый под, соблюдая
maxUnavailable— сколько реплик разрешено потерять относительно желаемого числа. - Цикл повторяется, пока весь трафик не переедет на новый ReplicaSet. Старый остаётся с нулём реплик — он нужен для мгновенного отката.
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # сколько подов сверх replicas можно создать
maxUnavailable: 0 # 0 = не терять мощность вообще (нужен запас на ноде)
minReadySeconds: 10 # под считается стабильным, продержавшись Ready 10 секунд
progressDeadlineSeconds: 600
kubectl rollout status deployment/api # следить за выкаткой
kubectl rollout history deployment/api # история ревизий
kubectl rollout undo deployment/api # откат на предыдущий ReplicaSet
kubectl rollout undo deployment/api --to-revision=4
Три probe и зачем нужна каждая
| Probe | Вопрос, на который отвечает | Что происходит при провале |
| readiness | Готов ли под принимать трафик? | под убирают из Endpoints, но не перезапускают |
| liveness | Жив ли процесс или завис? | контейнер перезапускается |
| startup | Закончился ли долгий старт? | остальные probe не выполняются, пока не пройдёт |
containers:
- name: api
image: registry.local/api:1.5.0
readinessProbe:
httpGet: { path: /ready, port: 8080 }
initialDelaySeconds: 2
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
periodSeconds: 10
failureThreshold: 3
startupProbe:
httpGet: { path: /healthz, port: 8080 }
periodSeconds: 5
failureThreshold: 30 # даём приложению до 150 секунд на старт
lifecycle:
preStop:
exec:
command: ["sleep", "5"]
Ключевое различие, которое и просят объяснить: readiness — про трафик, liveness — про перезапуск. Отсюда практическое правило: readiness должен проверять зависимости (доступна ли база, прогрет ли кэш), а liveness — только сам процесс. Если повесить проверку базы на liveness, кратковременная недоступность СУБД устроит лавину перезапусков всех подов сразу и превратит мелкий инцидент в полный отказ сервиса.
Возвращаемся к 502 при деплое
Две классические причины, и хороший ответ называет обе.
1. Нет readiness probe
Без неё под считается готовым сразу после старта контейнера. Приложение ещё поднимает пул соединений и читает конфиг, а Service уже шлёт на него запросы — клиенты получают ошибки.
2. Нет корректного завершения старых подов
Удаление пода запускает два процесса параллельно: kubelet шлёт контейнеру SIGTERM, а контроллер эндпоинтов убирает адрес пода из EndpointSlice. Обновление правил на всех нодах занимает некоторое время, поэтому какие-то запросы всё ещё летят в под, который уже начал выключаться. Лечится связкой из трёх вещей:
preStopс паузой в несколько секунд — под остаётся живым, пока правила разъезжаются по кластеру;- graceful shutdown в приложении: перестать принимать новые соединения, дообработать текущие, закрыть пул;
- достаточный
terminationGracePeriodSeconds(по умолчанию 30) — по его истечении прилетит SIGKILL.
Третья причина, о которой стоит упомянуть, — maxUnavailable больше нуля при малом числе реплик: с тремя репликами вы на время выкатки остаётесь с двумя, и если запаса по мощности нет, оставшиеся не выдерживают нагрузку. Для защиты от подобного на уровне кластера существует PodDisruptionBudget: он не даёт добровольным выселениям (drain ноды, апгрейд) опустить число готовых подов ниже порога.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: api
Типичные ошибки кандидатов
- Не различают readiness и liveness — самая частая ошибка во всём блоке Kubernetes.
- Проверяют доступность базы в liveness probe и получают каскадные перезапуски при любом мигании СУБД.
- Ставят
initialDelaySeconds: 60вместо startupProbe для медленно стартующих приложений — деплой становится неоправданно долгим. - Забывают про preStop и graceful shutdown, а потом объясняют 502 «сетевыми проблемами».
- Думают, что
kubectl rollout undoоткатывает базу данных. Он откатывает только манифест — миграции нужно проектировать обратно совместимыми.
Как ответить кратко (20–30 секунд)
«При смене образа Deployment создаёт новый ReplicaSet и переливает в него реплики, соблюдая maxSurge и maxUnavailable; старый ReplicaSet сохраняется для отката. Трафик на новый под идёт только после успешной readiness probe. 502 при деплое — это почти всегда либо отсутствие readiness probe, из-за чего под получает запросы раньше готовности, либо отсутствие graceful shutdown: под убирают из Endpoints и одновременно шлют ему SIGTERM, поэтому нужны preStop с небольшой паузой и корректное завершение соединений. И помню разницу: readiness управляет трафиком, liveness перезапускает контейнер, поэтому проверку базы в liveness ставить нельзя.»