Canary deployment — стратегия развертывания приложений, которая позволяет постепенно внедрять в production новую версию приложения.
Этот подход даёт возможность тестировать новые версии на небольшой части трафика, минимизируя риски и обеспечивая плавный переход.
С помощью Canary deployment возможно переключение трафика на новую версию по мере уверенности в её стабильности, с возможностью быстрого отката на старую версию при возникновении проблем.
В Deckhouse Kubernetes Platform Canary deployment может быть реализован средствами ingress-nginx
или istio
(рекомендуемый способ).
Примеры настроек Canary deployment средствами Ingress NGINX
Для реализации Canary deployment средствами Ingress NGINX используются аннотации и правила, которые определяют направление части трафика на новую версию приложения.
Создание Deployment и Service для стабильной версии
Пример манифеста для стабильной версии:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v1
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: v1
template:
metadata:
labels:
app: my-app
version: v1
spec:
containers:
- name: app
image: app:v1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: app-service
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 80
Создание Deployment и Service для Canary-версии
Пример манифеста для Canary-версии:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v2
spec:
replicas: 1
selector:
matchLabels:
app: my-app
version: v2
template:
metadata:
labels:
app: my-app
version: v2
spec:
containers:
- name: app
image: app:v2
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: app-canary-service
spec:
selector:
app: my-app
version: v2
ports:
- port: 80
targetPort: 80
Настройка Ingress для Canary deployment
Для реализации Canary deployment с использованием Ingress NGINX используются специальные аннотации:
nginx.ingress.kubernetes.io/canary
— включает Canary-режим для Ingress.nginx.ingress.kubernetes.io/canary-weight
— указывает процент трафика, который будет направлен на Canary-версию.
Пример манифеста для Ingress (10% трафика будет направлено на Canary-версию (app-canary-service
), 90% трафика — на стабильную версию (app-service
)):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10" # 10% трафика на Canary.
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-canary-service
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress-main
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80
Постепенное увеличение трафика на Canary-версию
Вы можете постепенно увеличивать процент трафика на Canary-версию, изменяя значение аннотации nginx.ingress.ernetes.io/canary-weight
. Например, чтобы направить 50% трафика на Canary-версию, обновите аннотацию следующим образом:
nginx.ingress.kubernetes.io/canary-weight: "50"
Откат или завершение Canary deployment
Если Canary-версия работает стабильно, вы можете полностью переключить трафик на новую версию, удалив Canary-аннотации и обновив основной Ingress.
Если возникли проблемы, вы можете уменьшить процент трафика на Canary-версию или полностью отключить ее, установив nginx.ingress.kubernetes.io/canary-weight: "0"
.
Дополнительные аннотации для Canary deployment
nginx.ingress.kubernetes.io/canary-by-header
— направляет трафик на Canary-версию на основе значения HTTP-заголовка.nginx.ingress.kubernetes.io/canary-by-cookie
— направляет трафик на Canary-версию на основе значения cookie.
Пример использования заголовка:
nginx.ingress.kubernetes.io/canary-by-header: "canary"
nginx.ingress.kubernetes.io/canary-by-header-value: "true"
В этом случае трафик будет направлен на Canary-версию, если запрос содержит заголовок canary: true
.
Примеры настроек Canary deployment средствами Istio
Istio отвечает лишь за гибкую маршрутизацию запросов, которая опирается на спецзаголовки запросов (например, cookie) или просто на случайность. За настройку этой маршрутизации и переключение между канареечными версиями отвечает CI/CD-система.
Подразумевается, что в одном пространстве имён размещены два Deployment с разными версиями приложения. У подов разных версий разные лейблы (version: v1
и version: v2
).
Требуется настроить два кастомных ресурса:
- DestinationRule с описанием, как идентифицировать разные версии вашего приложения (subset’ы);
- VirtualService с описанием, как распределять трафик между разными версиями приложения.
Пример:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: productpage-canary
spec:
host: productpage
# Subset'ы доступны только при обращении к хосту через VirtualService из пода под управлением Istio.
# Эти subset'ы должны быть указаны в маршрутах.
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
Распределение по наличию cookie
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: productpage-canary
spec:
hosts:
- productpage
http:
- match:
- headers:
cookie:
regex: "^(.*;?)?(canary=yes)(;.*)?"
route:
- destination:
host: productpage
subset: v2 # Ссылка на subset из DestinationRule.
- route:
- destination:
host: productpage
subset: v1
Распределение по вероятности
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: productpage-canary
spec:
hosts:
- productpage
http:
- route:
- destination:
host: productpage
subset: v1 # Ссылка на subset из DestinationRule.
weight: 90 # Процент трафика, который получат поды с лейблом `version: v1`.
- route:
- destination:
host: productpage
subset: v2
weight: 10