За замовчуванням Kubernetes управляє стратегією виливки нового коду за допомогою Deployment об'єкта. Він забезпечує доволі простий функціонал, котрий, тим не менш, задовільнить більшість потреб: rolling update чи recreate стратегії виливки; наявність історії, котра може забезпечити безпечний rollback на попередні версії та інші. Deployment забезпечує поступову заміну старіших подів на новіші, перевіривши проходження readiness-проби кожного перед включенням його в трафік, або ж, у випадку recreate стратегії, видаляє старі поди і водночас за раз замінює їх на нові.
Будь-який новий реліз йде із додатковими ризиками, що можуть вплинути на доступність сервісу. І тому простих стратегій, що з коробки надає Kubernetes, буває недостатньо. Скажімо, може бути бажання перемкнути на нову версію (тег імеджа) лише певний відсоток трафіку чи протестувати нову версію прямо перед перемиканням на неї. Тож такі прогресивні стратегії виливки потенційно зменшать ризики та допоможуть заздалегідь знайти критичні недоліки, не вплинувши на значну кількість запитів.
Увесь подальший код доступний за посиланням.
1. PROGRESSIVE DELIVERY
Як вже було сказано, progressive delivery - це набір технік доставки коду, що зменшують ймовірність простою роботи сервісу чи його некоректну роботу. Найпопулярнішими варіантами таких доставок є:
- Blue-Green. Виливається додаткова версія коду, після чого працює одночасно дві: blue (стара) та green (нова). Перемикання production трафіку на новий код зазвичай не миттєве, а лиш в тому разі, коли є впевненість у коректності його роботи, наприклад після прогону додаткових тестів. Дуже часто це робиться на рівні DNS, коли на нову версію прив'язують основне доменне ім'я сервісу. У разі проблем є швидка можливість повернення на попередню версію, адже старий сервіс про всяк випадок не вимикають одразу.
- Canary. На нову версію коду спрямовується лише якийсь відсоток трафіку. У разі відсутності помилок цей відсоток збільшується і доходить до 100. Є різні варіації цієї техніки: наприклад може виливатись якась частина сервісу на новій версії без включення його в трафік, для того щоб додатково прогнати на ній тести. Такий собі варіант Blue-Green іn Canary. Ця техніка також зменшує ризики виливки проблемного коду, адже є ймовірність, що клієнти на новій версії повідомлять, що щось пішло не так допоки цю версію отримають всі користувачі сервісу. Немає якихось чітких часових рамок того як швидко має бути досягнуто завершення виливки нової версії. Для деяких компаній це години, а для інших - дні чи навіть тижні.
- Feature Flags/Toggles. Способи активації додаткового (нового) функціоналу для групи користувачів у межах однієї версії коду, що імплементується додатковими змінними в межах самого коду чи окремих систем із якими він інтегрується. Все заради поступової виливки цих змін.
- A/B Тестування. У межах однієї версії коду клієнтам буде продемонстровано різний функціонал чи різний вигляд елементів сторінки. Якщо у разі такого "експерименту" над користувачами буде підтверджено бажаний результат - ці зміни можуть бути активовані для всієї бази користувачів.
У цій серії статей ми зосередимось на розгортанні коду, а саме на Canary та Blue-Green як техніках, котрі безпосередньо імплементуються на рівні інфраструктури, а не особливостях роботи кодової бази.
2. BASIC BLUE-GREEN EXAMPLE
Для імплементації Blue-Green чи Canary технік Deployment потребує додаткової конфігурації та додаткового управління релізним процесом. Виключно для цілей демонстрації розглянемо як би міг виглядати Blue-Green без допомоги додаткових операторів. У більшій чи меншій мірі ці ж техніки використовуються і в інших більш просунутих інструментах.
Отже, нехай у нас буде 2 деплойменти: теперішня версія blue та майбутня версія green.
$ cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-1-28
spec:
selector:
matchLabels:
name: nginx
version: "1-28"
replicas: 2
template:
metadata:
labels:
name: nginx
version: "1-28"
spec:
containers:
- name: nginx
image: nginx:1.28
ports:
- name: http
containerPort: 80
EOF
$ cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-1-29
spec:
selector:
matchLabels:
name: nginx
version: "1-29"
replicas: 2
template:
metadata:
labels:
name: nginx
version: "1-29"
spec:
containers:
- name: nginx
image: nginx:1.29
ports:
- name: http
containerPort: 80
EOF
Тут нічого особливого - 2 версії nginx, що працюють на порті 80. Тепер опишемо сервіс із типом LoadBalancer:
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold: "2"
service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "5"
service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout: "4"
labels:
name: nginx
spec:
ports:
- name: http
port: 80
targetPort: 80
selector:
name: nginx
version: "1-28"
type: LoadBalancer
EOF
Тип LoadBalancer має забезпечувати контролер, що працює в інсталяції Kubernetes. У моєму випадку це AWS Load Balancer Controller. Цей вибір не принциповий, проте опис сервісу цього ж типу із іншим контролером, особливо в секції анотацій, може бути інакшим.
Балансувальник для сервісу буде представлений NLB і на його фактичне створення може піти до 1.5 хвилини.
$ EXTERNAL_IP=$(kubectl get svc nginx -o jsonpath="{.status.loadBalancer.ingress[*].hostname}")
$ curl -s http://$EXTERNAL_IP/version | grep nginx
<hr><center>nginx/1.28.0</center>
Тепер відредагуємо секцію selector сервісу де виставимо новішу (green) версію:
$ kubectl edit svc nginx
...
selector:
name: nginx
version: "1-29"
...
І перевіримо чи відбулось перемикання:
$ while true; do curl -s http://$EXTERNAL_IP/version | grep nginx; sleep 1; date; done
<hr><center>nginx/1.28.0</center>
Wed Sep 18 15:41:07 EEST 2025
...
Wed Sep 18 15:41:19 EEST 2025
<hr><center>nginx/1.29.1</center>
Це також не миттєвий процес, на час його виконання впливають хелсчеки балансувальника, котрі описані в анотаціях. Щось подібне також присутнє і для Canary, проте не будемо довго на цьому зупинятись, адже це занадто мануальний процес і не вартий подальшого розвитку.
Links:
https://github.com/ianlewis/kubernetes-bluegreen-deployment-tutorial
3. ARGO ROLLOUTS
Argo Rollouts - це один із інструментів Argo Project. Вони всі доволі гарно інтегруються, хоча і не є залежними один від одного.
Argo Rollouts - це Kubernetes контролер та набір додаткових CRD, що імплементують вже згадані вище прогресивні техніки доставки коду, тестування та експерименти. Більш того він може інтегруватись із різноманітними ingress, mesh та gateway api контролерами для більш гранулярного управління трафіком.
На відміну від вбудованого Deployment об'єкту, що надає інсталяція Kubernetes, Argo Rollouts має такі переваги:
- Blue-Green та Canary стратегії
- Більш гранулярне управління трафіком між версіями (якщо є підтримка ingress/service mesh контролерів в Argo Rollouts)
- Автоматичні роллбеки чи промоушени версій (базуючись на результатах прогону тестів чи значень сервісів збору метрик). Або ж за потреби ручні, якщо цього вимагають процедури
Надалі ми установимо Argo Rollouts в AWS EKS кластер та розберемо декілька базових варіантів організації доставки нової версії та більш складну.
4. ARGO ROLLOUTS INSTALLATION
Щоб рухатись далі нам необхідні працюючий EKS кластер із AWS LB контролером, про установку яких можна почитати тут. Останні версії публічних модулів, котрі були у використанні, зазнали помітних змін, тож я оновив попередній код. Для його застосування скористаємось Тераформом версії 1.13.3:
$ git clone git@github.com:ipeacocks/terraform-aws-example.git
$ cd terraform-aws-example/eks-infra/vpc
$ terraform init && terraform apply --auto-approve
$ cd ../eks
$ terraform init && terraform apply --auto-approve
$ cd ../addons/lb-controller
$ terraform init && terraform apply --auto-approve
На установці Argo Rollouts зупинимось детальніше.
$ cd ../argo-rollouts
$ cat main.tf
data "aws_region" "this" {}
data "terraform_remote_state" "eks" {
backend = "s3"
config = {
bucket = "my-tf-state-2023-06-01"
key = "my-eks.tfstate"
region = "us-east-1"
}
}
data "aws_iam_policy_document" "this" {
statement {
sid = "LBAccess"
effect = "Allow"
actions = [
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeRules",
"elasticloadbalancing:DescribeTags",
"elasticloadbalancing:DescribeTargetHealth"
]
resources = ["*"]
}
}
resource "helm_release" "this" {
name = "argo-rollouts"
repository = "https://argoproj.github.io/argo-helm"
chart = "argo-rollouts"
version = var.helm_package_version
namespace = var.namespace
create_namespace = true
values = [
templatefile("${path.module}/templates/helm/values.yaml.tpl", {
region = data.aws_region.this.region
})
]
}
module "custom_pod_identity" {
source = "terraform-aws-modules/eks-pod-identity/aws"
version = "2.0.0"
name = "eks-argo-rollouts-${data.terraform_remote_state.eks.outputs.cluster_name}-${var.region}"
use_name_prefix = false
attach_custom_policy = true
source_policy_documents = [
data.aws_iam_policy_document.this.json
]
associations = {
one = {
cluster_name = data.terraform_remote_state.eks.outputs.cluster_name
namespace = var.namespace
service_account = "argo-rollouts"
}
}
}
Тут ми вичитали стейт створеного EKS кластеру, дані із якого необхідні для pod identity, що застосовує роль для "--aws-verify-target-group". Із її допомогою Argo Rollouts зможе робити додаткові перевірки AWS таргет груп (чи відповідає перелік адрес подів в сервісі тому, що додано в таргет групу) перед переходом до наступного етапу доставки. Це не обов'язковий функціонал, але завдяки йому можна мінімізувати кількість 5xx-помилок на етапі виливки. Його можна прибрати, якщо активований Pod Readiness Gate.
Далі ми встановили офіційний helm-чарт Argo Rollouts, що використовує templates/helm/values.yaml.tpl у якості параметрів:
$ cat templates/helm/values.yaml.tpl
serviceAccount:
create: true
controller:
extraArgs: ["--aws-verify-target-group"]
extraEnv:
- name: AWS_REGION
value: ${region}
replicas: 2
metrics:
enabled: true
logging:
format: "json"
keepCRDs: false
dashboard:
enabled: true
replicas: 2
# Uncomment if you wish to use ALB/Ingress for external access
# ingress:
# enabled: true
# annotations:
# alb.ingress.kubernetes.io/group.name: eks-addons
# alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
# alb.ingress.kubernetes.io/scheme: internet-facing
# alb.ingress.kubernetes.io/success-codes: "200-302"
# alb.ingress.kubernetes.io/target-type: ip
# # externalDNS is needed
# external-dns.alpha.kubernetes.io/hostname: rollouts-dashboard.example.com
# alb.ingress.kubernetes.io/healthcheck-port: traffic-port
# ingressClassName: "alb"
# hosts:
# - rollouts-dashboard.example.com
# paths:
# - /
# pathType: Prefix
Задля роботи із web-панеллю ми будемо користуватись форвардингом порту, але є можливість активувати Ingress/ALB прямо із хелм чарту (необхідно розкоментувати відповідну секцію, мати власний домен та встановлений ExternalDNS).
Проведемо установку всього написаного вище:
$ cd terraform-aws-example/eks-infra/addons/argo-rollouts
$ terraform init && terraform apply --auto-approve
Буде піднято сам контролер та його веб-панель:
$ aws eks update-kubeconfig --region us-east-1 --name my-eks
$ kubectl get pods -n argo-rollouts
NAME READY STATUS RESTARTS AGE
argo-rollouts-7466f98bb9-knd96 1/1 Running 0 6m45s
argo-rollouts-7466f98bb9-zcr8v 1/1 Running 0 6m46s
argo-rollouts-dashboard-6bc9fff6fc-79dl5 1/1 Running 0 6m46s
argo-rollouts-dashboard-6bc9fff6fc-wk2h8 1/1 Running 0 6m45s
$ kubectl -n argo-rollouts logs -f deploy/argo-rollouts
Found 2 pods, using pod/argo-rollouts-7466f98bb9-zcr8v
{"level":"info","msg":"Argo Rollouts starting","time":"2025-09-08T20:30:01Z","version":{"Version":"v1.8.3+49fa151","BuildDate":"2025-06-04T22:18:04Z","GitCommit":"49fa1516cf71672b69e265267da4e1d16e1fe114","GitTag":"","GitTreeState":"clean","GoVersion":"go1.23.9","Compiler":"gc","Platform":"linux/arm64"}}
{"level":"info","msg":"Creating event broadcaster","time":"2025-09-08T20:30:01Z"}
{"level":"info","msg":"Setting up event handlers","time":"2025-09-08T20:30:01Z"}
{"level":"info","msg":"Setting up experiments event handlers","time":"2025-09-08T20:30:01Z"}
{"level":"info","msg":"Setting up analysis event handlers","time":"2025-09-08T20:30:01Z"}
{"level":"info","msg":"Leaderelection get id argo-rollouts-7466f98bb9-zcr8v_d00332ed-6ec2-47f5-9346-c1abad7f654b","time":"2025-09-08T20:30:01Z"}
{"level":"info","msg":"Starting Healthz Server at 0.0.0.0:8080","time":"2025-09-08T20:30:01Z"}
{"level":"info","msg":"Starting Metric Server at 0.0.0.0:8090","time":"2025-09-08T20:30:01Z"}
{"level":"info","msg":"attempting to acquire leader lease argo-rollouts/argo-rollouts-controller-lock...","time":"2025-09-08T20:30:01Z"}
{"level":"info","msg":"New leader elected: argo-rollouts-7466f98bb9-knd96_eedcd00f-68c1-4258-b565-5c382f128a64","time":"2025-09-08T20:30:01Z}
Переглянемо чи працює web-панель:
$ kubectl -n argo-rollouts port-forward deployment/argo-rollouts-dashboard 3100:3100
Форвардинг панелі можна виконати простіше, але для цього буде необхідний офіційний плагін до kubectl:
$ curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
$ chmod +x ./kubectl-argo-rollouts-linux-amd64
$ sudo mv ./kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts
$ kubectl argo rollouts version
kubectl-argo-rollouts: v1.8.3+49fa151
BuildDate: 2025-06-04T22:15:54Z
GitCommit: 49fa1516cf71672b69e265267da4e1d16e1fe114
GitTreeState: clean
GoVersion: go1.23.9
Compiler: gc
Platform: linux/amd6
$ kubectl argo rollouts dashboard -n argo-rollouts
Цей плагін буде корисним і надалі для управління етапами реліз-процесу, тож його обов'зково треба буде встановити.
Argo Rollouts сетап привносить додаткові CRD, що імплементують додаткову логіку:
$ kubectl get crds | grep argo
analysisruns.argoproj.io 2025-09-09T17:45:42Z
analysistemplates.argoproj.io 2025-09-09T17:45:42Z
clusteranalysistemplates.argoproj.io 2025-09-09T17:45:42Z
experiments.argoproj.io 2025-09-09T17:45:42Z
rollouts.argoproj.io 2025-09-09T17:45:42Z
Наприклад Rollout забезпечує виконання самої логіки доставки, тим самим замінюючи офіційний Deployment, Analysis додає можливість вбудовувати тести між етапами доставки коду, Experiment вміє створювати додаткові репліки для перевірки коду тощо. Власне все для покращення якості релізів та зменшення ризиків.
Загальна схема роботи Argo Rollouts має наступний вигляд:
Тут показаний Canary реліз із залученням сумісного Ingress-контроллера для гранулярного управління трафіком. Як і в базовому прикладі, що був наведений вище, перемикання версії коду відбувається зміною селекторів на об'єкті сервісу за яким йде дві репліки, що управляються Rollout ресурсом. Про всі деталі поговоримо далі.
5. BLUE-GREEN W/O BALANCER
Маючи робочий EKS кластер та всі необхідні контролери, можемо переходити до організації самих процесів доставки коду на Argo Rollouts. У цій частині поговоримо про базовий Blue-Green, без залучення управлінням трафіку на AWS балансувальнику.
Спершу розглянемо сам Blue-Green, для чого візьмемо приклад від офіційного проекту:
$ cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: rollout-bluegreen
spec:
replicas: 2
revisionHistoryLimit: 2
selector:
matchLabels:
app: rollout-bluegreen
template:
metadata:
labels:
app: rollout-bluegreen
spec:
containers:
- name: rollouts-demo
image: argoproj/rollouts-demo:blue
imagePullPolicy: Always
ports:
- containerPort: 8080
strategy:
blueGreen:
activeService: rollout-bluegreen-active
previewService: rollout-bluegreen-preview
autoPromotionEnabled: false
---
kind: Service
apiVersion: v1
metadata:
name: rollout-bluegreen-active
spec:
selector:
app: rollout-bluegreen
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
kind: Service
apiVersion: v1
metadata:
name: rollout-bluegreen-preview
spec:
selector:
app: rollout-bluegreen
ports:
- protocol: TCP
port: 80
targetPort: 8080
EOF
rollout.argoproj.io/rollout-bluegreen created
service/rollout-bluegreen-active created
service/rollout-bluegreen-preview created
Було створено 2 сервіси rollout-bluegreen-active та rollout-bluegreen-preview, що дивляться на ту ж групу подів. Назви цих сервісів прописані в стратегії об'єкту rollout. OCI-образи argoproj/rollouts-demo зібрані лише під x86_64 архітектуру, тож у EKS кластері мають бути відповідні воркери.
$ kubectl argo rollouts get rollout rollout-bluegreen
Name: rollout-bluegreen
Namespace: default
Status: ✔ Healthy
Strategy: BlueGreen
Images: argoproj/rollouts-demo:blue (stable, active)
Replicas:
Desired: 2
Current: 2
Updated: 2
Ready: 2
Available: 2
NAME KIND STATUS AGE INFO
⟳ rollout-bluegreen Rollout ✔ Healthy 19m
└──# revision:1
└──⧉ rollout-bluegreen-5ffd47b8d4 ReplicaSet ✔ Healthy 19m stable,active
├──□ rollout-bluegreen-5ffd47b8d4-7hp7h Pod ✔ Running 18m ready:1/1
└──□ rollout-bluegreen-5ffd47b8d4-nl4n6 Pod ✔ Running 18m ready:1/1
Сервіс має статус Healthy, імедж версії rollouts-demo:blue, кількість подів 2. Все як і планувалось.
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
rollout-bluegreen-active ClusterIP 172.20.179.62 <none> 80/TCP 49m
rollout-bluegreen-preview ClusterIP 172.20.225.120 <none> 80/TCP 49m
До релізу наступної версії імеджа, обидва сервіси мають такі ж селектори, тобто вказують на ті ж поди:
$ kubectl get svc rollout-bluegreen-active -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "5ffd47b8d4"
}
$ kubectl get svc rollout-bluegreen-preview -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "5ffd47b8d4"
}
Rollout об'єкт в секції status відображає які репліки вважаються active та preview на даний момент часу:
$ kubectl get rollout rollout-bluegreen -o json | jq -r ".status.blueGreen"
{
"activeSelector": "5ffd47b8d4",
"previewSelector": "5ffd47b8d4"
}
Та власне сама репліка:
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
rollout-bluegreen-5ffd47b8d4 2 2 2 73m
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
rollout-bluegreen-5ffd47b8d4-7hp7h 1/1 Running 0 73m
rollout-bluegreen-5ffd47b8d4-nl4n6 1/1 Running 0 73m
Друга поки відсутня, адже ще не було релізу, і всюди активна лише репліка 5ffd47b8d4.
Новий реліз починається тоді, коли з'явились зміни в секції .spec.template об'єкту Rollout. Логіка абсолютно така ж як і в Deployment. Установимо нову версію OCI імеджа через kubectl та argo плагін:
$ kubectl argo rollouts set image rollout-bluegreen rollouts-demo=argoproj/rollouts-demo:green
де rollout-bluegreen - це ім'я об'єкту Rollout сервісу, а rollouts-demo - ім'я контейнера.
$ kubectl argo rollouts get rollout rollout-bluegreen
Name: rollout-bluegreen
Namespace: default
Status: ॥ Paused
Message: BlueGreenPause
Strategy: BlueGreen
Images: argoproj/rollouts-demo:blue (stable, active)
argoproj/rollouts-demo:green (preview)
Replicas:
Desired: 2
Current: 4
Updated: 2
Ready: 2
Available: 2
NAME KIND STATUS AGE INFO
⟳ rollout-bluegreen Rollout ॥ Paused 3h16m
├──# revision:2
│ └──⧉ rollout-bluegreen-75695867f ReplicaSet ✔ Healthy 30s preview
│ ├──□ rollout-bluegreen-75695867f-92fdg Pod ✔ Running 30s ready:1/1
│ └──□ rollout-bluegreen-75695867f-bkcfd Pod ✔ Running 30s ready:1/1
└──# revision:1
└──⧉ rollout-bluegreen-5ffd47b8d4 ReplicaSet ✔ Healthy 3h16m stable,active
├──□ rollout-bluegreen-5ffd47b8d4-7hp7h Pod ✔ Running 170m ready:1/1
└──□ rollout-bluegreen-5ffd47b8d4-nl4n6 Pod ✔ Running 170m ready:1/1
Завдяки раніше вказаній опції autoPromotionEnabled: false перемикання на нову версію не відбулось автоматично і процес чекає нашого мануального рішення. Але спочатку подивимось що відбулось:
$ kubectl get svc rollout-bluegreen-active -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "5ffd47b8d4"
}
$ kubectl get svc rollout-bluegreen-preview -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "75695867f"
}
$ kubectl get rollout rollout-bluegreen -o json | jq -r ".status.blueGreen"
{
"activeSelector": "5ffd47b8d4",
"previewSelector": "75695867f"
}
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
rollout-bluegreen-5ffd47b8d4 2 2 2 3h20m
rollout-bluegreen-75695867f 2 2 2 4m45s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
rollout-bluegreen-5ffd47b8d4-7hp7h 1/1 Running 0 174m
rollout-bluegreen-5ffd47b8d4-nl4n6 1/1 Running 0 174m
rollout-bluegreen-75695867f-92fdg 1/1 Running 0 5m12s
rollout-bluegreen-75695867f-bkcfd 1/1 Running 0 5m12s
Selector у сервіса rollout-bluegreen-active не змінився, rollout-bluegreen-preview додав новий rollouts-pod-template-hash, значення якого вказує на новий реплікасет rollout-bluegreen-75695867f. Цей реплікасет піднятий в кількості подів, що дорівнює 2 та має відповідні лейбли:
$ kubectl get pod rollout-bluegreen-75695867f-92fdg -o json | jq -r ".metadata.labels"
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "75695867f"
}
$ kubectl get pod rollout-bluegreen-75695867f-92fdg -o json | jq -r ".spec.containers.[].image"
argoproj/rollouts-demo:green
Якщо коротко, то з'явилась нова група подів під сервісом rollout-bluegreen-preview із новим імеджем, але трафік поки йде на стару версію. Цей новий сервіс можна додатково потестувати чи щось таке, допоки не відбудеться ручний promote:
$ kubectl argo rollouts promote rollout-bluegreen
rollout 'rollout-bluegreen' promoted
$ kubectl argo rollouts get rollout rollout-bluegreen
Name: rollout-bluegreen
Namespace: default
Status: ✔ Healthy
Strategy: BlueGreen
Images: argoproj/rollouts-demo:green (stable, active)
Replicas:
Desired: 2
Current: 2
Updated: 2
Ready: 2
Available: 2
NAME KIND STATUS AGE INFO
⟳ rollout-bluegreen Rollout ✔ Healthy 3h38m
├──# revision:2
│ └──⧉ rollout-bluegreen-75695867f ReplicaSet ✔ Healthy 22m stable,active
│ ├──□ rollout-bluegreen-75695867f-92fdg Pod ✔ Running 22m ready:1/1
│ └──□ rollout-bluegreen-75695867f-bkcfd Pod ✔ Running 22m ready:1/1
└──# revision:1
└──⧉ rollout-bluegreen-5ffd47b8d4 ReplicaSet • ScaledDown 3h38m
Отже основною стала репліка 75695867f, поди попередної 5ffd47b8d4 вже видалені (затримку їх знищення можна конфігурувати, щоб швидко повернутись до неї за необхідності). Селектор сервісу rollout-bluegreen-active, як і preview, тепер буде вказувати на 75695867f (тобто на єдину репліку, як і до початку релізу):
$ kubectl get svc rollout-bluegreen-active -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "75695867f"
}
$ kubectl get svc rollout-bluegreen-preview -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "75695867f"
}
$ kubectl get rollout rollout-bluegreen -o json | jq -r ".status.blueGreen"
{
"activeSelector": "75695867f",
"previewSelector": "75695867f"
}
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
rollout-bluegreen-5ffd47b8d4 0 0 0 3h51m
rollout-bluegreen-75695867f 2 2 2 35m
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
rollout-bluegreen-75695867f-92fdg 1/1 Running 0 35m
rollout-bluegreen-75695867f-bkcfd 1/1 Running 0 35m
$ kubectl get pod rollout-bluegreen-75695867f-92fdg -o json | jq -r ".metadata.labels"
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "75695867f"
}
Вітаю, реліз завершено! Promote релізу також можна робити із web-панелі, про котру я вже згадував:
Вона може бути в нагоді, якщо реліз процес у вашій компанії більш формалізований чи делегується менш кваліфікованим людям без доступу до Kubernetes API.
Додаток argoproj/rollouts-demo має відповідне забарвлення залежно від вказаного тегу, задля більш наочної демонстрації можливостей Argo Rollouts. Тому якщо перейти на порт сервісу можна побачити таку картину:
$ kubectl port-forward svc/rollout-bluegreen-active 8080:80
Колір зелений, адже це версія, котру було вилито.
6. BLUE-GREEN WITH BALANCER
Цього разу поглянемо на складніший варіант, що потребує залучення AWS LB Controller. Якщо ж ви оперуєте в іншому середовищі, то можна обрати інший зі списку, що підтримує Argo Rollouts. Інакше прийдеться задовольнятися базовим функціоналом, про котрий я писав вище.
У якості імеджа будемо використовувати той же rollouts-demo:
$ cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: rollout-bluegreen
spec:
replicas: 2
revisionHistoryLimit: 2
selector:
matchLabels:
app: rollout-bluegreen
template:
metadata:
labels:
app: rollout-bluegreen
spec:
containers:
- name: rollouts-demo
image: argoproj/rollouts-demo:blue
imagePullPolicy: Always
ports:
- containerPort: 8080
strategy:
blueGreen:
activeService: rollout-bluegreen-active
previewService: rollout-bluegreen-preview
autoPromotionEnabled: false
---
kind: Service
apiVersion: v1
metadata:
name: rollout-bluegreen-active
spec:
selector:
app: rollout-bluegreen
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
kind: Service
apiVersion: v1
metadata:
name: rollout-bluegreen-preview
spec:
selector:
app: rollout-bluegreen
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rollout-bluegreen
annotations:
alb.ingress.kubernetes.io/target-type: ip
# external-dns.alpha.kubernetes.io/hostname: bluegreen.example.com
alb.ingress.kubernetes.io/group.name: blue-green-group
alb.ingress.kubernetes.io/success-codes: 200-302
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
spec:
ingressClassName: alb
rules:
- host: bluegreen.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: rollout-bluegreen-active
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rollout-bluegreen-preview
annotations:
alb.ingress.kubernetes.io/target-type: ip
# external-dns.alpha.kubernetes.io/hostname: bluegreen-preview.example.com
alb.ingress.kubernetes.io/group.name: blue-green-group
alb.ingress.kubernetes.io/success-codes: 200-302
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
spec:
ingressClassName: alb
rules:
- host: bluegreen-preview.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: rollout-bluegreen-preview
port:
number: 80
EOF
Цього разу опис майже аналогічний попередньому BlueGreen окрім двох Ingress-об'єктів, котрі, виходячи із анотацій, і обслуговує AWS ALB контролер. У початковій фазі традиційно обидва сервіси, а значить і інгреси, будуть дивитись на єдиний реплікасет/групу подів:
$ kubectl argo rollouts get rollout rollout-bluegreen
Name: rollout-bluegreen
Namespace: default
Status: ✔ Healthy
Strategy: BlueGreen
Images: argoproj/rollouts-demo:blue (stable, active)
Replicas:
Desired: 2
Current: 2
Updated: 2
Ready: 2
Available: 2
NAME KIND STATUS AGE INFO
⟳ rollout-bluegreen Rollout ✔ Healthy 21m
└──# revision:1
└──⧉ rollout-bluegreen-5ffd47b8d4 ReplicaSet ✔ Healthy 21m stable,active
├──□ rollout-bluegreen-5ffd47b8d4-89f6c Pod ✔ Running 21m ready:1/1
└──□ rollout-bluegreen-5ffd47b8d4-h6rmg Pod ✔ Running 21m ready:1/1
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
rollout-bluegreen-active ClusterIP 172.20.211.205 <none> 80/TCP 22m
rollout-bluegreen-preview ClusterIP 172.20.164.253 <none> 80/TCP 22m
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
rollout-bluegreen-5ffd47b8d4 2 2 2 22m
$ kubectl get svc rollout-bluegreen-active -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "5ffd47b8d4"
}
$ kubectl get svc rollout-bluegreen-preview -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "5ffd47b8d4"
}
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
rollout-bluegreen-5ffd47b8d4-89f6c 1/1 Running 0 25m
rollout-bluegreen-5ffd47b8d4-h6rmg 1/1 Running 0 25m
Завдяки AWS ALB контролеру буде створено ALB балансувальник, що буде прослуховувати 80 порт та матиме 2 правила для preview та active (тобто основного) доменів:
Розпочнемо процес релізу:
$ kubectl argo rollouts set image rollout-bluegreen rollouts-demo=argoproj/rollouts-demo:green
rollout "rollout-bluegreen" image updated
І переглянемо ситуацію:
$ kubectl argo rollouts get rollout rollout-bluegreen
Name: rollout-bluegreen
Namespace: default
Status: ॥ Paused
Message: BlueGreenPause
Strategy: BlueGreen
Images: argoproj/rollouts-demo:blue (stable, active)
argoproj/rollouts-demo:green (preview)
Replicas:
Desired: 2
Current: 4
Updated: 2
Ready: 2
Available: 2
NAME KIND STATUS AGE INFO
⟳ rollout-bluegreen Rollout ॥ Paused 36m
├──# revision:2
│ └──⧉ rollout-bluegreen-75695867f ReplicaSet ✔ Healthy 23s preview
│ ├──□ rollout-bluegreen-75695867f-bbcts Pod ✔ Running 22s ready:1/1
│ └──□ rollout-bluegreen-75695867f-m8djd Pod ✔ Running 22s ready:1/1
└──# revision:1
└──⧉ rollout-bluegreen-5ffd47b8d4 ReplicaSet ✔ Healthy 36m stable,active
├──□ rollout-bluegreen-5ffd47b8d4-89f6c Pod ✔ Running 36m ready:1/1
└──□ rollout-bluegreen-5ffd47b8d4-h6rmg Pod ✔ Running 36m ready:1/1
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 172.20.0.1 <none> 443/TCP 93m
rollout-bluegreen-active ClusterIP 172.20.211.205 <none> 80/TCP 37m
rollout-bluegreen-preview ClusterIP 172.20.164.253 <none> 80/TCP 37m
$ kubectl get rs NAME DESIRED CURRENT READY AGE
rollout-bluegreen-5ffd47b8d4 2 2 2 38m
rollout-bluegreen-75695867f 2 2 2 100s
$ kubectl get svc rollout-bluegreen-active -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "5ffd47b8d4"
}
$ kubectl get svc rollout-bluegreen-preview -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "75695867f"
}
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
rollout-bluegreen-5ffd47b8d4-89f6c 1/1 Running 0 38m
rollout-bluegreen-5ffd47b8d4-h6rmg 1/1 Running 0 38m
rollout-bluegreen-75695867f-bbcts 1/1 Running 0 2m2s
rollout-bluegreen-75695867f-m8djd 1/1 Running 0 2m2s
Інгреси як і раніше вказують на ті ж сервіси (Argo Rollouts наразі не оперує ними), піднявся новий реплікасет rollout-bluegreen-75695867f, а отже і його група подів, і на цю групу подів вже дивиться preview сервіс. Знову поглянемо на балансувальник:
По IP-адресам помітно, що це різні поди, як і має бути. Kubectl це підтверджує:
// preview
$ kubectl get pod rollout-bluegreen-75695867f-bbcts -o json | jq -r '.status.podIP'
10.0.27.77
$ kubectl get pod rollout-bluegreen-75695867f-m8djd -o json | jq -r '.status.podIP'
10.0.18.188
// active
$ kubectl get pod rollout-bluegreen-5ffd47b8d4-89f6c -o json | jq -r '.status.podIP'
10.0.29.141
$ kubectl get pod rollout-bluegreen-5ffd47b8d4-h6rmg -o json | jq -r '.status.podIP'
10.0.19.222
Сервіси пов'язуються із таргет групами за допомогою targetGroupBinding ресурсів:
$ kubectl get targetgroupbinding
NAME SERVICE-NAME SERVICE-PORT TARGET-TYPE AGE
k8s-default-rolloutb-813be4e464 rollout-bluegreen-active 80 ip 50m
k8s-default-rolloutb-9cf520ca12 rollout-bluegreen-preview 80 ip 61m
$ kubectl get targetgroupbinding k8s-default-rolloutb-813be4e464 -o yaml
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
...
serviceRef:
name: rollout-bluegreen-active
port: 80
targetGroupARN: arn:aws:elasticloadbalancing:us-east-1:789248082627:targetgroup/k8s-default-rolloutb-813be4e464/5c3694c4a9de62a8
targetType: ip
vpcID: vpc-09061fa28d48455cc
...
$ kubectl get targetgroupbinding k8s-default-rolloutb-9cf520ca12 -o yaml
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
...
serviceRef:
name: rollout-bluegreen-preview
port: 80
targetGroupARN: arn:aws:elasticloadbalancing:us-east-1:789248082627:targetgroup/k8s-default-rolloutb-9cf520ca12/09d2943d1c395c7c
targetType: ip
vpcID: vpc-09061fa28d48455cc
...
Тож цього разу bluegreen-preview.example.com вже буде вказувати на новий реліз, а на bluegreen.example.com досі на попередній (стабільний). Все завдяки продемонстрованим вище AWS rules в відповідному litener-і.
Завершимо реліз, зробивши promote:
$ kubectl argo rollouts promote rollout-bluegreen
rollout 'rollout-bluegreen' promoted
І через 30 секунд (значення за замовчуванням) реліз завершиться:
$ kubectl argo rollouts get rollout rollout-bluegreen
Name: rollout-bluegreen
Namespace: default
Status: ✔ Healthy
Strategy: BlueGreen
Images: argoproj/rollouts-demo:green (stable, active)
Replicas:
Desired: 2
Current: 2
Updated: 2
Ready: 2
Available: 2
NAME KIND STATUS AGE INFO
⟳ rollout-bluegreen Rollout ✔ Healthy 73m
├──# revision:2
│ └──⧉ rollout-bluegreen-75695867f ReplicaSet ✔ Healthy 36m stable,active
│ ├──□ rollout-bluegreen-75695867f-bbcts Pod ✔ Running 36m ready:1/1
│ └──□ rollout-bluegreen-75695867f-m8djd Pod ✔ Running 36m ready:1/1
└──# revision:1
└──⧉ rollout-bluegreen-5ffd47b8d4 ReplicaSet • ScaledDown 73m
bluegreen.example.com буде вказувати вже на нову версію, аналогічно і bluegreen-preview.example.com (адже реліз завершився). Сервіси вказують на новий реплікасет, що став стабільним:
$ kubectl get svc rollout-bluegreen-active -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "75695867f"
}
$ kubectl get svc rollout-bluegreen-preview -o json | jq -r '.spec.selector'
{
"app": "rollout-bluegreen",
"rollouts-pod-template-hash": "75695867f"
}
Кожна таргет група вказує на ті ж адреси/поди:
7. ADDITIONAL HINTS
Для забезпечення виливки без 50X-помилок необхідно активувати Pod Readiness Gate, із котрим поди отримуватимуть статус Ready лише після реєстрації їх у відповідній таргет групі.
Також не варто занижувати значення scaleDownDelaySeconds нижче дефолтного 30 секунд, адже цей час необхідний для того, що AWS таргет групи встигли оновитись до необхідних значень об'єктів Kubernetes.
Офіційна документація попереджає, що повністю уникнути 50X-помилок (при наявності вхідного Ingress/балансувальника) для BlueGreen стратегії неможливо на етапі зміни селекторів, адже Pod Readiness Gate працює лише для створення подів. Загалом це не є проблемою, але клієнські дотатки мають вміти робити ретраї без впливу на кінцевих користувачів.
Якщо ж 50X-ті критичні - то варто використовувати Canary PingPong стратегію (чи Gateway API інтеграції), про яку в тому числі і піде мова у другій частині.
Посилання:
https://argo-rollouts.readthedocs.io/en/stable/
https://rollouts-plugin-trafficrouter-gatewayapi.readthedocs.io/en/latest/
https://github.com/argoproj/argo-rollouts/tree/master/docs
https://github.com/argoproj/argo-rollouts/tree/master/examples
Немає коментарів:
Дописати коментар