Як організувати Rolling Update в Kubernetes?
Уявіть, що ви міняєте шини на автомобілі. Rolling Update — це як міняти шини по одній, поки машина повільно їде. Машина ніколи не зупиняється повністю. Альтернатива (Recreate) —...
🟢 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:
- Створює новий ReplicaSet з новим образом (старий ReplicaSet зберігається)
- Починає створювати Pod’и в новому ReplicaSet, керуючись
maxSurge - Чекає, поки кожен новий Pod пройде Readiness Probe
- Після успіху видаляє Pod зі старого ReplicaSet, керуючись
maxUnavailable - Повторює, поки усі Pod’и не будуть в новому ReplicaSet
- Старий 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:
- Watch: Підписується на Deployment, ReplicaSet, Pod події через informer’и
- Sync: При зміні Deployment (новий образ), контроллер:
- Створює новий ReplicaSet з новим PodTemplateSpec
- Обчислює desired replica count для нового та старого RS
- Масштабує новий RS up, старий RS down
- 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) - Pod Creation/Deletion: Новий ReplicaSet створює Pod’и через свій own reconciliation loop. Pod’и проходять scheduling → kubelet → container creation → Readiness Probe
- 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).
Що сталося:
- Міграція БД виконана: додана колонка
user_preferences(NOT NULL без default) - Rolling Update почався: перші 50 Pod’ів (v2.3) стартували успішно
- Решта 150 Pod’ів (v2.2) почали падати — ORM не міг мапити нову схему (колонка NOT NULL, але старі entity не знають про неї)
- Liveness Probe перезапускала v2.2 Pod’и нескінченно
- 50 Pod’ів v2.3 обробляли 100% трафіку → latency зросла з 200ms до 5 секунд
- HPA масштабував до 300 реплік, але нові Pod’и теж були v2.3 (образ вже оновлений в RS)
- Database connection pool exhausted → повний outage на 40 хвилин
Post-mortem та fix:
- Міграції БД завжди backward-compatible: додати колонку з
DEFAULT NULL, неNOT NULL. Старий код ігнорує нову колонку, новий код використовує її - Canary deployment замість Rolling Update для БД-залежних змін: 10% трафіку на v2.3, моніторинг помилок, потім full rollout
- PDB для захисту:
minAvailable: 70%— Rolling Update не може вбити більше 30% Pod’ів, навіть якщо хоче - Alert на restart rate:
rate(kube_pod_container_status_restarts_total[5m]) > 0.1— спрацював би через 2 хвилини - 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 панелі:
- Deployment rollout progress: available vs updated replicas over time
- Rolling Update duration — від початку до завершення
- Pod restart rate — детекція crash loops
- HPA scaling — кореляція з Rolling Update
- Error rate (5xx) — кореляція з деплоями
- Latency p50/p99 — детекція degradation під час оновлення
- DB connection pool usage — детекція exhaustion
Highload Best Practices
- Завжди використовуйте
maxUnavailable: 0для критичних сервісів — Zero Downtime обов’язковий maxSurge: 25-50%для балансу speed/resources — швидше, ніж по одному, але не перевантажує ноди- Readiness Probe — обов’язкова — без неї Kubernetes не знає, коли нові Pod’и готові
- Graceful Shutdown + preStop hook:
lifecycle: preStop: exec: command: ["sh", "-c", "sleep 10"] terminationGracePeriodSeconds: 60 - Database міграції backward-compatible — add columns with DEFAULT, never drop/rename
- Canary через Ingress annotations —
canary-weight: "10"для перших 10% трафіку - PDB для захисту від aggressive Rolling Update:
apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: my-app-pdb spec: minAvailable: 70% selector: matchLabels: app: my-app - Monitor rollout duration — alert якщо Rolling Update > 15 хвилин
- Automated rollback — при error rate > 5% автоматично
rollout undo - ProgressDeadlineSeconds: 600 — 10 хвилин достатньо для Java-додатків з довгим стартом, але не занадто довго для виявлення проблем
- Image digest замість tags —
my-app@sha256:abc123замістьmy-app:2.3для reproducibility - 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