Rolling update и probes: как выкатка идёт без простоя

Любимый сценарный вопрос: «Опишите по шагам, что делает кластер, когда вы поменяли тег образа».

Вопрос: «Вы выкатили новую версию, и во время деплоя часть запросов упала с 502. Реплик было три, стратегия — RollingUpdate. Что пошло не так?»

Что на самом деле проверяет интервьюер

Это вопрос уровня middle+. Он проверяет, понимает ли кандидат, что «Pod запущен» и «Pod готов принимать трафик» — разные вещи, и знает ли он, чем управляется скорость и безопасность выкатки. Ошибки при деплое почти всегда объясняются двумя причинами: отсутствием readiness probe и отсутствием корректного завершения работы.

Развёрнутый ответ: rolling update по шагам

Вы поменяли image: api:1.4.2 на api:1.5.0 и применили манифест. Дальше:

  1. Deployment видит, что шаблон пода изменился, и создаёт новый ReplicaSet с нулём реплик. Старый остаётся с тремя.
  2. Согласно maxSurge новый ReplicaSet поднимает первые поды. При maxSurge: 1 в кластере временно будет 4 пода.
  3. Новый под проходит планирование, скачивание образа и запуск. Трафик он не получает, пока не пройдёт readiness probe.
  4. Как только под стал Ready, его адрес попадает в EndpointSlice, и Service начинает слать на него запросы.
  5. Deployment гасит один старый под, соблюдая maxUnavailable — сколько реплик разрешено потерять относительно желаемого числа.
  6. Цикл повторяется, пока весь трафик не переедет на новый 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 ставить нельзя.»

Проверьте себя
1. Чем readiness probe отличается от liveness probe?
AReadiness выполняется один раз при старте, liveness — периодически
BReadiness управляет тем, идёт ли на под трафик, а liveness при провале перезапускает контейнер
CReadiness проверяет ноду, liveness — контейнер
DОтличий нет, это синонимы для разных версий Kubernetes
2. Почему опасно проверять доступность базы данных в liveness probe?
AПроверка создаёт лишнюю нагрузку на базу
BКратковременная недоступность базы вызовет одновременный перезапуск всех подов и превратит мелкий сбой в полный отказ сервиса
CLiveness probe не умеет делать сетевые запросы
DKubernetes запрещает внешние проверки в liveness
3. Что нужно, чтобы при остановке пода не терялись уже принятые запросы?
AУвеличить maxSurge до числа реплик
BИспользовать стратегию Recreate вместо RollingUpdate
CpreStop-хук с небольшой паузой, graceful shutdown в приложении и достаточный terminationGracePeriodSeconds
DОтключить readiness probe на время выкатки