Вопрос 22 · Раздел 14

Как организовать Rolling Update в Kubernetes?

Представьте, что вы меняете шины на автомобиле. Rolling Update — это как менять шины по одной, пока машина медленно едет. Машина никогда не останавливается полностью. Альтернати...

Версии по языкам: English Russian Ukrainian

🟢 Junior Level

Простое определение

Rolling Update (Плавное обновление) — это стратегия деплоя в Kubernetes, которая заменяет старые Pod’ы на новые постепенно, без простоя приложения. Вместо того чтобы убить все старые Pod’ы и создать новые сразу, Kubernetes делает это по одному (или небольшими группами), ожидая готовности каждого нового Pod’а перед удалением старого.

Rolling update – стратегия обновления по умолчанию в Deployment. K8s постепенно заменяет старые Pod’ы на новые, без простоя приложения.

Аналогия

Представьте, что вы меняете шины на автомобиле. Rolling Update — это как менять шины по одной, пока машина медленно едет. Машина никогда не останавливается полностью. Альтернатива (Recreate) — снять все 4 шины сразу, и машина стоит, пока не наденете новые.

Пример YAML

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # максимум 1 дополнительный Pod
      maxUnavailable: 0  # ноль Pod'ов недоступны
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: app
          image: my-app:2.0  # новый образ
          readinessProbe:
            httpGet:
              path: /health/ready
              port: 8080
            periodSeconds: 5

Пример kubectl

# Обновить образ
kubectl set image deployment/my-app app=my-app:2.0

# Отслеживать прогресс обновления
kubectl rollout status deployment/my-app

# Посмотреть историю ревизий
kubectl rollout history deployment/my-app

# Откатиться к предыдущей версии
kubectl rollout undo deployment/my-app

# Откатиться к конкретной ревизии
kubectl rollout undo deployment/my-app --to-revision=3

# Приостановить обновление
kubectl rollout pause deployment/my-app

# Возобновить обновление
kubectl rollout resume deployment/my-app

Когда использовать

  • Обновление версии приложения без downtime
  • Canary deployment (постепенное переключение трафика)
  • Когда приложение поддерживает обратную совместимость API
  • Для stateless сервисов (API, веб-приложения)

🟡 Middle Level

Как это работает

Когда вы меняете образ контейнера в Deployment, Kubernetes:

  1. Создаёт новый ReplicaSet с новым образом (старый ReplicaSet сохраняется)
  2. Начинает создавать Pod’ы в новом ReplicaSet, руководствуясь maxSurge
  3. Ждёт, пока каждый новый Pod пройдёт Readiness Probe
  4. После успеха удаляет Pod из старого ReplicaSet, руководствуясь maxUnavailable
  5. Повторяет, пока все Pod’ы не будут в новом ReplicaSet
  6. Старый ReplicaSet сохраняется (с replicas=0) для rollback

Параметры maxSurge и maxUnavailable:

  • maxSurge: 25% — при 4 репликах может создать 1 дополнительный Pod (4 + 1 = 5)
  • maxUnavailable: 25% — при 4 репликах минимум 3 должны быть доступны (4 - 1 = 3)

Можно задавать в процентах или абсолютных числах: maxSurge: 1, maxUnavailable: 0.

// maxUnavailable=1 -- максимум 1 Pod может быть недоступен во время обновления.
// maxSurge=1 -- максимум 1 дополнительный Pod может быть создан сверх desired.
// При replicas=3: 3 → 4(new) → 2(old)+2(new) → 1(old)+3(new) → 3(new)

Практические сценарии

Сценарий 1: Zero Downtime для 1 реплики

replicas: 1
strategy:
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

Kubernetes создаст новый Pod (итого 2), дождётся Readiness, затем удалит старый.

Сценарий 2: Быстрый деплой для 100 реплик

replicas: 100
strategy:
  rollingUpdate:
    maxSurge: 25%     # +25 Pod'ов сразу
    maxUnavailable: 25%  # -25 Pod'ов сразу

Все 100 Pod’ов обновятся за 4 волны (25 за раз). Быстро, но с временным снижением доступности.

Сценарий 3: Pause/Resume для ручной проверки

# Деплой первых 2 Pod'ов и пауза
kubectl set image deployment/my-app app=my-app:2.0
kubectl rollout pause deployment/my-app
# Проверить логи, метрики, тесты
kubectl rollout resume deployment/my-app

Таблица распространённых ошибок

Ошибка Последствие Решение
Отсутствие Readiness Probe Kubernetes убивает старые Pod’ы, новые ещё не готовы → downtime Всегда добавлять readinessProbe
Использование тега :latest Kubernetes не видит изменений в образе, Rolling Update не запускается Всегда использовать конкретные теги (:1.0, :2.0) или SHA256 digest
maxUnavailable: 0 при большом числе реплик Обновление занимает очень много времени (по одному Pod’у) Использовать maxUnavailable: 10-25% для баланса speed/availability
maxSurge слишком большой Временное превышение ресурсных квот, OOMKill на нодах Ограничить maxSurge доступными ресурсами нод
Нет Graceful Shutdown Активные запросы обрываются при удалении Pod’а Обрабатывать SIGTERM, использовать preStop hook
Обратная несовместимость API Новые Pod’ы не могут работать со старыми данными/схемой БД Использовать backward-compatible миграции или Blue-Green
Стратегия Recreate вместо RollingUpdate Полный downtime на время обновления Проверить strategy.type: RollingUpdate

Сравнительная таблица стратегий деплоя

Характеристика Rolling Update Recreate Blue-Green Canary
Downtime Zero Да Zero Zero
Ресурсы +maxSurge 0 x2 (полный второй набор) +canary%
Скорость Средняя Быстрая Быстрая Медленная
Rollback Быстрый (rollout undo) Быстрый Мгновенный (переключение трафика) Мгновенный
Сложность Низкая (по умолчанию) Низкая Высокая (2 Deployment’а + Ingress) Высокая (Ingress weight)
Риск Средний (две версии сосуществуют) Высокий Низкий Низкий
Когда использовать Stateless API, микросервисы Dev, тесты, stateful с миграцией Критичные сервисы, compliance Production с мониторингом

Когда НЕ использовать

  • Stateful-приложения с миграцией БД — Rolling Update запускает старые и новые Pod’ы одновременно. Если схема БД несовместима, используйте Recreate + миграцию, или Operator

Rolling update НЕ подходит для stateful-приложений (БД) – новые Pod’ы не имеют данных. Для stateful используйте StatefulSet с ordered updates.

  • Приложения без обратной совместимости — новая версия ломает API для старых клиентов. Используйте Blue-Green
  • Очень большие Deployment’ы с ограниченными ресурсамиmaxSurge может потребовать больше CPU/RAM, чем есть на нодах
  • Тестовые окружения — используйте Recreate для скорости

🔴 Senior Level

Глубинная механика: Deployment Controller, ReplicaSet, и Reconciliation

Deployment Controller Architecture: Deployment Controller (в kube-controller-manager) работает по standard reconciliation loop:

  1. Watch: Подписывается на Deployment, ReplicaSet, Pod события через informer’ы
  2. Sync: При изменении Deployment (новый образ), контроллер:
    • Создаёт новый ReplicaSet с новым PodTemplateSpec
    • Вычисляет desired replica count для нового и старого RS
    • Масштабирует новый RS up, старый RS down
  3. Scaling Logic:
    desiredReplicas = spec.replicas
    maxSurge = ceil(desiredReplicas * maxSurge%)
    maxUnavailable = floor(desiredReplicas * maxUnavailable%)
       
    Для каждой волны:
      newRS_replicas = min(desiredReplicas + maxSurge - current_total, desiredReplicas)
      oldRS_replicas = max(current_total - newRS_replicas - maxUnavailable, 0)
    
  4. Pod Creation/Deletion: Новый ReplicaSet создаёт Pod’ы через свой own reconciliation loop. Pod’ы проходят scheduling → kubelet → container creation → Readiness Probe
  5. RS Scaling: Когда новые Pod’ы Ready, Deployment Controller уменьшает replicas старого RS

Revision History: Каждый ReplicaSet сохраняет аннотацию deployment.kubernetes.io/revision. Kubernetes хранит последние N ревизий (по умолчанию 10, настраивается через revisionHistoryLimit). Это позволяет rollout undo — просто масштабировать старый RS обратно.

Readiness Probe Integration: Deployment Controller не убивает старые Pod’ы, пока новые не перейдут в Ready: True. Status Pod’а обновляется kubelet → API Server → Deployment Controller informer → reconciliation.

Progress Deadline: spec.progressDeadlineSeconds (по умолчанию 600 секунд). Если Rolling Update не завершается за это время (например, новые Pod’ы не проходят Readiness), Deployment переходит в ProgressDeadlineExceeded статус. Это не rollback — Deployment остаётся в промежуточном состоянии, требует ручного вмешательства.

Trade-offs

Аспект Trade-off
maxSurge высокий vs низкий Высокий = быстрее обновление, но больше ресурсов и выше risk of two-version coexistence. Низкий = медленнее, но безопаснее
maxUnavailable высокий vs низкий Высокий = быстрее, но временное снижение доступности. Низкий = zero downtime, но медленнее
Rolling Update vs Blue-Green Rolling = меньше ресурсов, но две версии сосуществуют. Blue-Green = мгновенное переключение, но x2 ресурсов
RevisionHistoryLimit высокий vs низкий Высокий = больше история для rollback, но больше etcd storage и старых RS. Низкий = экономия, но ограниченный rollback
ProgressDeadlineSeconds Длинный = больше времени на debug, но дольше обнаружение проблем. Короткий = быстрый failure detection, но ложные срабатывания

Edge Cases (6+)

Edge Case 1: Две версии кода сосуществуют Во время Rolling Update одновременно работают старая (v1) и новая (v2) версии. Если v2 пишет данные в формате, который v1 не понимает, возможны corruption или errors. Решение: backward-compatible API, feature flags, или Blue-Green deployment.

Edge Case 2: Database migration incompatibility Rolling Update запускает Pod’ы с новым кодом, которые требуют новую схему БД. Но старые Pod’ы (v1) ещё работают и не знают новую схему. Решение: миграции БД делать ДО Rolling Update, в backward-compatible manner (добавить колонку, не удалять старую).

Edge Case 3: Resource quota exceeded during maxSurge Deployment с 100 репликами, maxSurge: 25%. Во время обновления нужно 125 Pod’ов. ResourceQuota namespace’а ограничивает 110 Pod’ов. Новые Pod’ы застревают в Pending, Rolling Update зависает на 10 минут (progressDeadlineSeconds). Решение: увеличить квоту, или уменьшить maxSurge.

Edge Case 4: Pod Disruption Budget (PDB) конфликт PDB устанавливает minAvailable: 80%. Rolling Update с maxUnavailable: 25% хочет убить 25 Pod’ов из 100. PDB разрешает убить только 20. Deployment Controller будет ждать, пока PDB позволит. Если PDB и maxUnavailable несовместимы, Rolling Update застревает.

Edge Case 5: Node drain во время Rolling Update Администратор делает kubectl drain node-X. Pod’ы на этой ноде переезжают. Одновременно идёт Rolling Update. Deployment Controller масштабирует новый RS, но новые Pod’ы могут быть запланированы на draining ноду → eviction → re-schedule. Это замедляет обновление и создаёт unnecessary churn.

Edge Case 6: Image Pull Backoff Новый образ my-app:2.0 имеет typo в registry URL или отсутствует. Новые Pod’ы входят в ImagePullBackOff. Deployment Controller ждёт Readiness, но Pod’ы даже не запускаются. Через progressDeadlineSeconds (10 минут) Deployment переходит в ProgressDeadlineExceeded. Решение: pre-deployment image validation, использовать ImagePullPolicy: IfNotPresent.

Edge Case 7: HPA + Rolling Update race condition HPA видит рост CPU во время Rolling Update (новые Pod’ы ещё не Ready, старые обрабатывают больше трафика). HPA масштабирует Deployment до 120 реплик. Rolling Update теперь обновляет 120 Pod’ов вместо 100. Это может усугубить resource contention. Решение: behavior.stabilizationWindowSeconds в HPA.

Edge Case 8: Sidecar контейнер не обновляется Deployment имеет 2 контейнера: app и sidecar. Обновлён только образ app. Kubernetes создаёт новый ReplicaSet с новым app образом и старым sidecar образом. Если sidecar требует обновления для совместимости с новым app, возможны errors. Решение: обновлять все контейнеры в одном Deployment update.

Performance Numbers

Метрика Значение
ReplicaSet creation latency 10-50ms (API Server)
Pod scheduling latency 100ms-2s (зависит от cluster size)
Container startup (Java/Spring Boot) 30-180 секунд
Readiness Probe success latency 5-30 секунд (после startup)
Rolling Update 100 реплик (maxSurge 25%) ~5-15 минут (зависит от startup time)
Rolling Update 100 реплик (maxSurge 100%) ~2-5 минут (быстрее, но больше ресурсов)
Rollout undo latency 30 секунд - 5 минут (зависит от startup time)
etcd storage per ReplicaSet ~5-10KB (аннотации + spec)
Max revisionHistoryLimit Практически 50+ (etcd storage limitation)

Security

  • Image signing и verification — Rolling Update загружает новые образы. Убедитесь, что образы подписаны (cosign, Notary) и verified через Admission Webhook
  • RBAC на rollout undo — Откат может загрузить старую, потенциально уязвимую версию. Ограничьте rollout undo через RBAC
  • ImagePullSecrets — Обновлённые образы должны быть доступны из private registry. Убедитесь, что ImagePullSecrets актуальны во всех namespace’ах
  • Secret rotation — Rolling Update — хорошее время для ротации Secrets. Новые Pod’ы получат обновлённые Secrets из ConfigMap/Secret volume mounts
  • NetworkPolicy — Во время Rolling Update две версии приложения общаются с одними и теми же сервисами. NetworkPolicy должна разрешать трафик для обеих версий

Production War Story

Ситуация: E-commerce платформа, 200 реплик API, Rolling Update с maxSurge: 25%, maxUnavailable: 0. Деплой v2.3 с новой схемой БД (добавлена колонка user_preferences).

Что произошло:

  1. Миграция БД выполнена: добавлена колонка user_preferences (NOT NULL без default)
  2. Rolling Update начался: первые 50 Pod’ов (v2.3) стартовали успешно
  3. Оставшиеся 150 Pod’ов (v2.2) начали падать — ORM не мог мапить новую схему (колонка NOT NULL, но старые entity не знают о ней)
  4. Liveness Probe перезапускала v2.2 Pod’ы бесконечно
  5. 50 Pod’ов v2.3 обрабатывали 100% трафика → latency выросла с 200ms до 5 секунд
  6. HPA масштабировал до 300 реплик, но новые Pod’ы тоже были v2.3 (образ уже обновлён в RS)
  7. Database connection pool exhausted → полный outage на 40 минут

Post-mortem и fix:

  1. Миграции БД всегда backward-compatible: добавить колонку с DEFAULT NULL, не NOT NULL. Старый код игнорирует новую колонку, новый код использует её
  2. Canary deployment вместо Rolling Update для БД-зависимых изменений: 10% трафика на v2.3, мониторинг ошибок, затем full rollout
  3. PDB для защиты: minAvailable: 70% — Rolling Update не может убить больше 30% Pod’ов, даже если хочет
  4. Alert на restart rate: rate(kube_pod_container_status_restarts_total[5m]) > 0.1 — сработал бы через 2 минуты
  5. Rollback playbook: kubectl rollout undo должен быть автоматизирован, не manual

Мониторинг после fix:

# Alert: Deployment rollout stuck
kube_deployment_status_condition{condition="Progressing", status="false"} == 1

# Alert: Restart rate
rate(kube_pod_container_status_restarts_total[5m]) > 0.1

# Alert: HPA scaling anomaly
kube_horizontalpodautoscaler_status_current_replicas - kube_horizontalpodautoscaler_spec_max_replicas > 0

# Alert: DB connection pool
db_connection_pool_active / db_connection_pool_max > 0.8

Monitoring (Prometheus/Grafana)

Ключевые метрики:

# Deployment rollout status
kube_deployment_status_observed_generation
kube_deployment_status_replicas_available
kube_deployment_status_replicas_updated

# Rollout progress
kube_deployment_status_condition{condition="Progressing"}

# Pod restart rate
rate(kube_pod_container_status_restarts_total[5m]) by (deployment)

# Rolling Update duration
time() - kube_deployment_metadata_generation

# HPA scaling events
kube_horizontalpodautoscaler_status_current_replicas
kube_horizontalpodautoscaler_status_desired_replicas

# Pod readiness
kube_pod_status_ready{condition="true"} / kube_pod_status_ready

Grafana Dashboard панели:

  1. Deployment rollout progress: available vs updated replicas over time
  2. Rolling Update duration — от начала до завершения
  3. Pod restart rate — детекция crash loops
  4. HPA scaling — корреляция с Rolling Update
  5. Error rate (5xx) — корреляция с деплоями
  6. Latency p50/p99 — детекция degradation во время обновления
  7. DB connection pool usage — детекция exhaustion

Highload Best Practices

  1. Всегда используйте maxUnavailable: 0 для критичных сервисов — Zero Downtime обязателен
  2. maxSurge: 25-50% для баланса speed/resources — быстрее, чем по одному, но не перегружает ноды
  3. Readiness Probe — обязательна — без неё Kubernetes не знает, когда новые Pod’ы готовы
  4. Graceful Shutdown + preStop hook:
    lifecycle:
      preStop:
        exec:
          command: ["sh", "-c", "sleep 10"]
    terminationGracePeriodSeconds: 60
    
  5. Database миграции backward-compatible — add columns with DEFAULT, never drop/rename
  6. Canary через Ingress annotationscanary-weight: "10" для первых 10% трафика
  7. PDB для защиты от aggressive Rolling Update:
    apiVersion: policy/v1
    kind: PodDisruptionBudget
    metadata:
      name: my-app-pdb
    spec:
      minAvailable: 70%
      selector:
        matchLabels:
          app: my-app
    
  8. Monitor rollout duration — alert если Rolling Update > 15 минут
  9. Automated rollback — при error rate > 5% автоматически rollout undo
  10. ProgressDeadlineSeconds: 600 — 10 минут достаточно для Java-приложений с долгим стартом, но не слишком долго для обнаружения проблем
  11. Image digest вместо tagsmy-app@sha256:abc123 вместо my-app:2.3 для reproducibility
  12. HPA stabilizationWindowSeconds: 300 — предотвращает race condition между Rolling Update и HPA

🎯 Шпаргалка для интервью

Обязательно знать:

  • Rolling Update — постепенная замена Pod’ов без downtime (стратегия по умолчанию в Deployment)
  • maxSurge — сколько дополнительных Pod’ов создать, maxUnavailable — сколько удалить
  • При изменении образа создаётся новый ReplicaSet; старый сохраняется для rollback
  • Readiness Probe обязательна — K8s не удаляет старые Pod’ы пока новые не готовы
  • kubectl rollout undo — быстрый откат; rollout pause/resume — ручная проверка
  • Database миграции должны быть backward-compatible (add column с DEFAULT, не drop)
  • PDB может конфликтовать с maxUnavailable — Rolling Update застревает

Частые уточняющие вопросы:

  • «Почему :latest тег плох для Rolling Update?» — K8s не видит изменений в образе, обновление не запускается
  • «Две версии кода работают одновременно — это проблема?» — Да, если нет обратной совместимости API
  • «Rolling Update для stateful-приложений?» — Нет, используйте StatefulSet с ordered updates
  • «Rolling Update застрял — что делать?» — Проверьте: ImagePullBackOff, ResourceQuota, PDB, Readiness Probe

Красные флаги (НЕ говорить):

  • «Rolling Update = Recreate» (Recreate = downtime, Rolling = zero downtime)
  • «Использую :latest для удобства» (K8s не обнаружит изменение образа)
  • «Миграции БД после деплоя» (миграции ДО, backward-compatible)
  • «maxUnavailable: 0 для 100 реплик» (обновление займёт очень много времени)

Связанные темы:

  • [[Что такое ReplicaSet]] — механизм обновления
  • [[Что такое readiness probe]] — блокирует удаление старых Pod’ов
  • [[Что такое StatefulSet и когда его использовать]] — rolling update для stateful