Питання 22 · Розділ 14

Як організувати Rolling Update в Kubernetes?

Уявіть, що ви міняєте шини на автомобілі. Rolling Update — це як міняти шини по одній, поки машина повільно їде. Машина ніколи не зупиняється повністю. Альтернатива (Recreate) —...

Мовні версії: 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 Нуль Так Нуль Нуль
Ресурси +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 низький Високий = швидше оновлення, але більше ресурсів і вищий ризик співіснування двох версій. Низький = повільніше, але безпечніше
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