Как организовать rolling update в Kubernetes
Rolling Update — это замена колёс на движущемся многоосном грузовике или поезде:
🟢 Junior Level
30-секундный ответ
Rolling Update (Плавное волновое обновление) — это стандартная стратегия обновления приложений в объекте Deployment, при которой старые поды заменяются новыми постепенно, небольшими группами, без остановки сервиса и без простоя (Zero Downtime).
- Вместо одновременного уничтожения всех работающих инстансов (стратегия
Recreate), Kubernetes запускает один или несколько новых подов. - Дожидается, пока новые поды успешно пройдут пробу готовности (
readinessProbe). - Только после подтверждения готовности новой версии Kubernetes начинает плавно гасить старые поды.
- В любой момент времени пользователи получают непрерывный доступ к приложению.
Наглядная аналогия из жизни
Rolling Update — это замена колёс на движущемся многоосном грузовике или поезде:
- У грузовика 8 колёс.
- Механики приподнимают одну ось, меняют старое колесо на новое, проверяют затяжку болтов (
readinessProbe) и опускают его на дорогу. - Затем переходят к следующему колесу.
- Грузовик ни на секунду не останавливается и продолжает везти груз. Альтернатива
Recreate— заглушить двигатель, снять все 8 колёс сразу, оставив грузовик на кирпичах (полный простой бизнеса).
Базовый манифест Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Максимум +1 под сверх нормы (не более 5 подов всего)
maxUnavailable: 0 # Ни один под не должен быть недоступен (всегда минимум 4)
selector:
matchLabels:
app: payment
template:
metadata:
labels:
app: payment
spec:
containers:
- name: payment-app
image: payment-service:2.1.0 # Конкретный тег версии (НЕ :latest!)
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
🟡 Middle Level
Математика maxSurge и maxUnavailable
Поведение волнового обновления управляется двумя ключевыми параметрами в процентах или абсолютных числах:
Базовое число реплик: replicas = 4
maxSurge: 1 -> В пике обновления в кластере может быть создано: 4 + 1 = 5 подов
maxUnavailable: 0 -> В любой момент времени клиентам должны отвечать минимум: 4 - 0 = 4 пода
Фазы обновления по шагам:
- Шаг 1: Создаётся 1 новый под версии
v2. Всего в кластере 5 подов (4v1+ 1v2). - Шаг 2: Новый под
v2проходитreadinessProbe. Трафик начинает поступать на него. - Шаг 3: Удаляется 1 старый под
v1. В кластере остаётся 4 пода (3v1+ 1v2). - Шаг 4: Процесс циклически повторяется до полной замены всех 4 подов на версию
v2.
Управление процессом через kubectl rollout
Kubernetes предоставляет мощный набор команд для контроля деплоя:
# 1. Запустить обновление новой версии образа
kubectl set image deployment/payment-service payment-app=payment-service:2.2.0
# 2. Отслеживать прогресс обновления в реальном времени
kubectl rollout status deployment/payment-service
# 3. Приостановить деплой (например, для ручной проверки на канареечном поде)
kubectl rollout pause deployment/payment-service
# 4. Возобновить деплой дальше
kubectl rollout resume deployment/payment-service
# 5. Посмотреть историю ревизий
kubectl rollout history deployment/payment-service
# 6. МГНОВЕННЫЙ ОТКАТ к предыдущей стабильной ревизии при аварии
kubectl rollout undo deployment/payment-service
Внутренний механизм: Роль ReplicaSet
Объект Deployment сам по себе не управляет подами напрямую. Он управляет объектами ReplicaSet:
- При изменении секции
spec.templateконтроллер Deployment создаёт новый ReplicaSet (например,payment-78d4f8). - Постепенно увеличивает счетчик
replicasв новом ReplicaSet и уменьшает его в старом ReplicaSet (payment-59c6b8). - Старый ReplicaSet остаётся в кластере со значением
replicas: 0. Это позволяет выполнить командуrollout undoза полсекунды без повторной генерации конфигураций!
🔴 Senior Level
Проблема сосуществования версий и паттерн «Expand / Contract»
Во время Rolling Update в кластере одновременно работают поды старой версии (v1) и новой версии (v2):
- Запрос одного пользователя может попасть на
v1, а следующий — наv2. - Если разработчик в версии
v2удалит столбец в базе данных или переименует поле в JSON, работающие подыv1немедленно начнут падать с ошибками.
Паттерн эволюции БД «Expand and Contract» (Параллельный запуск):
Фаза 1 (Expand): Выполняем миграцию Liquibase/Flyway: ДОБАВЛЯЕМ новую колонку как NULLABLE.
Старая версия v1 продолжает работать, игнорируя эту колонку.
Фаза 2 (Rollout): Раскатываем Rolling Update версии v2. v2 пишет данные и в старую, и в новую колонку.
Фаза 3 (Contract):После 100% успеха раскатки отдельным скриптом удаляем старую ненужную колонку.
Конфликт с PodDisruptionBudget (PDB) и ResourceQuota
Две наиболее частые причины «зависания» Rolling Update в продакшене:
1. Зависание из-за ResourceQuota (Нехватка квот)
Если в Namespace установлена жесткая квота, например ровно на 10 подов, а у вас работает replicas: 10:
- При деплое с
maxSurge: 2Kubernetes попытается создать 11-й и 12-й под. - API-сервер заблокирует их создание с ошибкой
exceeded quota. - Новые поды зависнут в статусе
Pending, и обновление заблокируется на 10 минут (progressDeadlineSeconds). - Решение: при дефиците квот использовать конфигурацию
maxSurge: 0,maxUnavailable: 1(сначала освободить место, затем создавать).
2. Зависание из-за конфликта с PodDisruptionBudget
Если объявлен PDB с требованием minAvailable: 100%, а в Deployment задано maxUnavailable: 25%:
- Контроллер деплоя не сможет удалить ни один старый под, так как это нарушит контракт PDB!
Идеальная формула 100% Zero-Downtime для Java/Spring Boot
Для гарантированного отсутствия ошибок 502 Bad Gateway во время деплоя необходимо одновременное выполнение 4 условий:
maxUnavailable: 0— не выключать старые поды до поднятия новых.- Надежная
readinessProbe— новый Spring Boot под начинает получать трафик только после полной загрузки контекста. preStop: sleep 10— задержка передSIGTERMдля компенсации сетевого лага обновленияiptablesагентамиkube-proxy.- Spring Boot Graceful Shutdown — параметр
server.shutdown: gracefulвapplication.yaml, чтобы Tomcat корректно завершил активные транзакции перед гибелью.
4 Tricky Questions
1. Почему использование тега образа :latest (например, image: my-app:latest) полностью ломает механизм автоматического запуска Rolling Update в Kubernetes?
Ответ:
Контроллер Deployment отслеживает изменения в манифесте по хэшу спецификации spec.template (поле pod-template-hash):
- Если вы собрали новый Docker-образ, запушили его в реестр под тем же именем
my-app:latestи выполнилиkubectl apply -f deployment.yaml, то с точки зрения API-сервера манифест не изменился ни на один символ. - Контроллер видит прежнюю строку
image: my-app:latest, не регистрирует факта обновления и вообще не запускает процедуру Rolling Update. - Все поды продолжат работать на старом коде. Для корректной работы всегда необходимо использовать семантические теги версий (
:1.4.2) или SHA256-хэш дайджеста образа (@sha256:...).
2. Что произойдёт с объектами ConfigMap и Secret, если выполнить команду отката ревизии kubectl rollout undo?
Ответ: Они НЕ откатятся! Это критическая ловушка для DevOps-инженеров:
- Механизм ревизий
Deploymentи его дочернихReplicaSetсохраняет историю исключительно спецификации самого пода (образы, переменные, порты, селекторы). - Внешние объекты
ConfigMapиSecretверсионируются независимо. - Если вы обновили конфиг в
ConfigMapи раскатали новую версию, а затем выполнилиrollout undo, под откатится на старый Docker-образ, но будет монтировать НОВЫЙ, уже изменённыйConfigMap! Это может привести к несовместимости и падению старого бинарника. - Решение: паттерн Immutable ConfigMaps с добавлением хэша в имя конфига (
app-config-v123) или использование инструментов GitOps (Helm / ArgoCD).
3. Для чего нужен параметр spec.progressDeadlineSeconds, и что происходит при его превышении во время сбоя деплоя?
Ответ:
progressDeadlineSeconds(по умолчанию 600 секунд / 10 минут) задаёт максимальное время, в течение которого процесс обновления должен демонстрировать прогресс (создание новых подов, прохождение readiness probe).- Если новые поды зависают в ошибке (например,
CrashLoopBackOffилиImagePullBackOff) и прогресса нет в течение этого периода:- Контроллер выставляет условию
ProgressingстатусFalseс причинойProgressDeadlineExceeded. - Автоматического отката (Rollback) Kubernetes НЕ выполняет! Процесс обновления просто замирает в текущей промежуточной точке (часть трафика на старых подах, часть зависших подов), сигнализируя мониторингу или CI/CD пайплайну о необходимости аварийного вмешательства.
- Контроллер выставляет условию
4. В чём состоит разница между стратегиями обновления RollingUpdate в Deployment и в StatefulSet?
Ответ:
- В
Deployment: поды эфемерны, не имеют стабильного сетевого имени и постоянного диска. Они могут создаваться и удаляться пачками в произвольном порядке с превышением лимита (maxSurge). - В
StatefulSet:- Не поддерживается концепция
maxSurge(нельзя превышать желаемое число реплик, так как каждый под привязан к собственному выделенномуPersistentVolume). - Обновление происходит строго по одному в строго обратном порядковом номере: сначала под
app-2, затемapp-1, затемapp-0. - Новый под
app-Nсоздаётся с сохранением прежнего диска и сетевого DNS-имени только ПОСЛЕ того, как старый под с этим номером был полностью остановлен и отмонтирован.
- Не поддерживается концепция
🎯 Шпаргалка для интервью
Главное о Rolling Update
- Суть: плавное волновое замещение старых подов новыми без остановки сервиса.
maxSurge: сколько подов можно создать сверх желаемого числа (запас мощности).maxUnavailable: сколько подов допустимо вывести из строя во время процесса.- Формула Zero Downtime:
maxUnavailable: 0+readinessProbe+preStop: sleep 10+ Graceful Shutdown в Spring Boot. - Роль ReplicaSet: Deployment создаёт новый RS, наращивает его и сохраняет старый с 0 реплик для мгновенного отката (
rollout undo). - Эволюция БД: паттерн Expand/Contract обязателен, так как v1 и v2 сосуществуют во время деплоя.
- Антипаттерн :latest: при использовании
:latestконтроллер не видит изменений манифеста и не запускает деплой.
Сравнение стратегий деплоя в Kubernetes
| Стратегия | Downtime | Дополнительные ресурсы | Скорость отката | Особенности |
| :— | :—: | :—: | :—: | :— |
| RollingUpdate | 🟢 Zero | В пределах maxSurge | Быстрый (rollout undo) | Две версии работают одновременно |
| Recreate | 🔴 Есть | 0 (сначала удаляет всё) | Долгий (пересоздание) | Для баз данных и несовместимых схем |
| Blue-Green | 🟢 Zero | +100% (удвоение кластера) | Мгновенный (свитч Ingress) | Изоляция версий, дорого по ресурсам |
| Canary | 🟢 Zero | + процент на канарейку | Мгновенный | Проверка на 5–10% реальных пользователей |
Красные флаги на собеседовании (Чего говорить нельзя)
- ❌ «Чтобы обновить приложение, мы используем образ с тегом :latest» — Kubernetes не обнаружит изменений и обновление не запустится.
- ❌ «При выполнении rollout undo база данных и секреты автоматически возвращаются к старой версии» — ConfigMaps, Secrets и БД живут отдельно и не откатываются.
- ❌ «Rolling Update гарантирует, что две версии приложения никогда не будут работать одновременно» — они гарантированно работают параллельно на этапе переключения.