Zero-downtime деплой в Kubernetes: rolling updates, canary и blue-green на практике
Введение: что такое zero-downtime деплой и почему это критично
Каждый раз, когда вы выкатываете новую версию приложения, возникает риск: пользователи могут столкнуться с ошибками, недоступностью сервиса или непредсказуемым поведением. Zero-downtime деплой — это набор практик и технических подходов, которые позволяют обновлять production-окружение без прерывания обслуживания запросов.
В контексте Kubernetes zero-downtime деплой перестал быть экзотикой — это базовое требование для любого серьёзного продукта. Платформа предоставляет встроенные механизмы (rolling updates), а также позволяет реализовать более сложные стратегии: blue-green и canary. В этой статье мы разберём каждую из них с реальными манифестами и командами.
Целевая аудитория статьи — DevOps-инженеры, SRE и backend-разработчики, которые уже работают с Kubernetes и хотят выстроить надёжные пайплайны доставки.
Rolling Update в Kubernetes: настройка и подводные камни
Rolling update — стратегия деплоя по умолчанию в Kubernetes. Новые поды запускаются постепенно, а старые завершаются по мере готовности новых. Это позволяет поддерживать доступность сервиса на протяжении всего обновления.
Ключевые параметры: maxSurge и maxUnavailable
Поведение rolling update контролируется двумя параметрами в секции strategy Deployment-манифеста:
- maxSurge — максимальное количество подов сверх желаемого числа реплик, которые могут существовать одновременно. Можно задать как число или процент.
- maxUnavailable — максимальное количество подов, которые могут быть недоступны в процессе обновления.
Пример манифеста Deployment с настроенным rolling update:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
spec:
replicas: 4
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: my-app
version: v2
spec:
containers:
- name: my-app
image: my-registry/my-app:v2
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
Readiness и Liveness пробы — обязательный элемент
Без корректно настроенных проб rolling update теряет смысл. Readiness probe сообщает Kubernetes, что под готов принимать трафик. Пока проба не пройдена, под не включается в балансировку. Liveness probe определяет, жив ли процесс — если нет, под перезапускается.
Типичная ошибка: приложение стартует, но ещё не завершило инициализацию (прогрев кешей, миграции). Без readiness probe Kubernetes начнёт слать трафик на неготовый инстанс. Используйте также startupProbe для приложений с долгим стартом.
Команды kubectl для управления rolling update
# Обновить образ
kubectl set image deployment/my-app my-app=my-registry/my-app:v2 -n production
# Следить за процессом
kubectl rollout status deployment/my-app -n production
# Просмотр истории
kubectl rollout history deployment/my-app -n production
# Откат к предыдущей версии
kubectl rollout undo deployment/my-app -n production
Blue-Green деплой: переключение трафика через Service и Ingress
Blue-green деплой предполагает наличие двух идентичных окружений: blue (текущая production-версия) и green (новая версия). Трафик в любой момент направлен только на одно из них. После успешного тестирования green-окружения вы переключаете трафик одной командой.
Манифест blue-деплоя и green-деплоя
# Blue Deployment (текущий production)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-blue
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: my-app
slot: blue
template:
metadata:
labels:
app: my-app
slot: blue
version: v1
spec:
containers:
- name: my-app
image: my-registry/my-app:v1
ports:
- containerPort: 8080
---
# Green Deployment (новая версия)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-green
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: my-app
slot: green
template:
metadata:
labels:
app: my-app
slot: green
version: v2
spec:
containers:
- name: my-app
image: my-registry/my-app:v2
ports:
- containerPort: 8080
Service и переключение трафика
apiVersion: v1
kind: Service
metadata:
name: my-app-svc
namespace: production
spec:
selector:
app: my-app
slot: blue # Меняем на "green" для переключения
ports:
- protocol: TCP
port: 80
targetPort: 8080
Переключение трафика выполняется патчем сервиса:
kubectl patch service my-app-svc -n production \
-p '{"spec":{"selector":{"slot":"green"}}}'
При использовании Ingress с NGINX Ingress Controller или аналогами можно переключать трафик на уровне Ingress-ресурса, изменяя backend-сервис. Это даёт дополнительный контроль без изменения самого Service.
Преимущество blue-green: мгновенное переключение и такой же мгновенный откат. Недостаток: удвоенные ресурсы на период деплоя.
Canary деплой: постепенный роллаут с весовыми правилами
Canary-деплой позволяет направить небольшой процент трафика на новую версию приложения, не подвергая всех пользователей риску. Это идеальная стратегия для тестирования изменений на реальной нагрузке.
Canary через нативный Kubernetes (без service mesh)
Простой способ — запустить canary-deployment с малым числом реплик рядом со stable-deployment. Kubernetes балансирует трафик между подами пропорционально их количеству.
# Stable Deployment: 9 реплик = ~90% трафика
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-stable
namespace: production
spec:
replicas: 9
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
track: stable
spec:
containers:
- name: my-app
image: my-registry/my-app:v1
---
# Canary Deployment: 1 реплика = ~10% трафика
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-canary
namespace: production
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
track: canary
spec:
containers:
- name: my-app
image: my-registry/my-app:v2
Service при этом выбирает поды только по метке app: my-app, охватывая оба деплоя.
Canary через Ingress с весовыми аннотациями (NGINX)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-canary-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
rules:
- host: my-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-canary-svc
port:
number: 80
Аннотация canary-weight: "10" направляет 10% трафика на canary. Значение меняется постепенно — 10%, 25%, 50%, 100% — по мере уверенности в стабильности новой версии.
Canary с Argo Rollouts
Для продвинутого управления canary-деплоем рекомендуется использовать Argo Rollouts — расширение Kubernetes, которое добавляет тип ресурса Rollout со встроенной поддержкой canary, blue-green и анализа метрик.
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
namespace: production
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10
- pause: {duration: 5m}
- setWeight: 30
- pause: {duration: 10m}
- setWeight: 60
- pause: {duration: 10m}
- setWeight: 100
analysis:
templates:
- templateName: success-rate
startingStep: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-registry/my-app:v2
ports:
- containerPort: 8080
Интеграция стратегий деплоя в CI/CD пайплайн
Стратегии деплоя без простоев раскрывают свой потенциал только в связке с автоматизированным CI/CD пайплайном. Рассмотрим типичный флоу на примере GitLab CI:
stages:
- build
- test
- deploy-canary
- promote
- rollback
build:
stage: build
script:
- docker build -t my-registry/my-app:$CI_COMMIT_SHA .
- docker push my-registry/my-app:$CI_COMMIT_SHA
deploy-canary:
stage: deploy-canary
script:
- kubectl set image deployment/my-app-canary
my-app=my-registry/my-app:$CI_COMMIT_SHA -n production
- kubectl rollout status deployment/my-app-canary -n production
environment:
name: production/canary
promote:
stage: promote
when: manual
script:
- kubectl set image deployment/my-app-stable
my-app=my-registry/my-app:$CI_COMMIT_SHA -n production
- kubectl rollout status deployment/my-app-stable -n production
rollback:
stage: rollback
when: manual
script:
- kubectl rollout undo deployment/my-app-stable -n production
- kubectl rollout undo deployment/my-app-canary -n production
Ключевые принципы интеграции:
- Тег образа Docker привязывается к коммиту (
$CI_COMMIT_SHA) — это обеспечивает воспроизводимость. - Каждый шаг деплоя верифицируется через
kubectl rollout status. - Promote (полный роллаут) выполняется вручную после наблюдения за метриками canary.
- Rollback доступен как отдельный шаг пайплайна.
Откат (Rollback): автоматический и ручной
Даже идеальный деплой может пойти не так. Kubernetes хранит историю ревизий Deployment, что позволяет быстро откатиться.
Ручной откат
# Откат к предыдущей ревизии
kubectl rollout undo deployment/my-app -n production
# Откат к конкретной ревизии
kubectl rollout undo deployment/my-app --to-revision=3 -n production
# Просмотр ревизий с деталями
kubectl rollout history deployment/my-app -n production --revision=2
Автоматический откат через Argo Rollouts и анализ метрик
Argo Rollouts поддерживает AnalysisTemplate — описание условий успешности деплоя на основе метрик из Prometheus:
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
namespace: production
spec:
metrics:
- name: success-rate
interval: 1m
successCondition: result[0] >= 0.95
failureLimit: 3
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(rate(http_requests_total{status!~"5..",app="my-app"}[1m]))
/
sum(rate(http_requests_total{app="my-app"}[1m]))
Если success rate опускается ниже 95% три раза подряд, Argo Rollouts автоматически прерывает деплой и откатывается к предыдущей версии.
Также полезно настроить progressDeadlineSeconds в Deployment — если деплой не завершается за указанное время, Kubernetes помечает его как failed:
spec:
progressDeadlineSeconds: 300
Мониторинг деплоя: как понять, что обновление прошло успешно
Zero-downtime деплой невозможен без наблюдаемости. Что нужно мониторить:
- Error rate — процент ответов 5xx. Резкий рост сигнализирует о проблеме.
- Latency (p50, p95, p99) — деградация задержек может быть признаком регрессии.
- Pod restart count — частые рестарты говорят о крашах приложения.
- Readiness probe failures — поды не проходят проверку готовности.
- Kubernetes events —
kubectl get events -n production --sort-by=.lastTimestamp.
Полезные команды для мониторинга деплоя в реальном времени
# Статус деплоя
kubectl rollout status deployment/my-app -n production
# Состояние подов
kubectl get pods -n production -l app=my-app -w
# Описание пода при проблемах
kubectl describe pod <pod-name> -n production
# Логи в реальном времени
kubectl logs -f -l app=my-app -n production --tail=100
# События namespace
kubectl get events -n production --sort-by=.lastTimestamp
Интегрируйте Prometheus + Grafana с дашбордами деплоя. Хорошей практикой является автоматическая аннотация дашборда Grafana при каждом деплое — это позволяет визуально сопоставить изменение метрик с моментом обновления.
Сравнение стратегий деплоя
Каждая стратегия подходит для своих сценариев. Ниже — ключевые характеристики:
- Rolling Update: встроен в Kubernetes, минимальные накладные расходы по ресурсам, подходит для большинства сервисов. Недостаток — в процессе обновления одновременно работают две версии, что требует обратной совместимости API и схемы БД.
- Blue-Green: мгновенное переключение и откат, идеален для критичных систем. Требует удвоенных ресурсов на время деплоя. Сложнее при stateful-компонентах.
- Canary: наиболее безопасная стратегия — реальный трафик тестирует новую версию на малой доле пользователей. Требует качественного мониторинга и инструментов управления весами (Argo Rollouts, Istio, NGINX). Подходит для highload-систем с большим трафиком.
Рекомендации по выбору:
- Начните с Rolling Update — это разумный выбор по умолчанию для большинства приложений.
- Переходите к Blue-Green, если требуется гарантированный мгновенный откат и у вас есть запас ресурсов.
- Внедряйте Canary для highload-сервисов, где даже 1% ошибок критичен, и есть зрелая система мониторинга.
Заключение
Zero-downtime деплой в Kubernetes — это не просто техническая настройка, а инженерная культура. Rolling update подойдёт большинству команд как отправная точка. Blue-green даёт уверенность при критичных обновлениях. Canary — наиболее зрелая стратегия для production-систем с высокой нагрузкой.
Любая из этих стратегий работает надёжно только в комбинации с корректными readiness/liveness пробами, автоматизированным CI/CD пайплайном и полноценным мониторингом. Инвестируйте время в настройку этих компонентов — они окупятся спокойными ночами и уверенными релизами.
Лучший деплой — тот, который пользователи не замечают.
Технологии
Теги
Руслан Исмаилов
Senior Web / Backend разработчик. Senior web/backend разработчик с 9-летним опытом. Стек: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, микросервисы, CI/CD. Подробнее обо мне →