И снова здравствуйте! Я – Евгений Симигин, занимаюсь внедрением DevOps-практик в Центре компетенций по разработке облачных и интернет-решений МТС Digital. Как я и обещал – это вторая часть нашего обзора Argo Rollouts, из которой вы узнаете о том, как в процессе выкатки нового релиза организовать еще и тестирование. Первая часть статьи – по ссылке.
Рассмотренные в первой половине статьи механизмы послужат нам существенным подспорьем, однако итоговое решение – прошло все успешно или нет – принимается на основе звонков от разгневанных пользователей алертов от системы мониторинга. Тут на помощь нам спешат analysis и experiments.Они позволят вклиниться в шаги выкатки и выставить критерии для принятия решения: идем мы дальше или откатываем ревизию.
Прежде всего мы задаем «правила игры» – то, что мы будем анализировать и критерии «что такое хорошо, что такое плохо». Для этого служат AnalysisTemplate
ClusterAnalysisTemplate
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: error-rate
spec:
args:
- name: service-name
- name: api-token
valueFrom:
secretKeyRef:
name: token-secret
key: apiToken
- name: api-url
value: http://example/measure
- name: host
metrics:
- name: error-rate
interval: 5m
successCondition: result[0] <= 0.95
failureLimit: 3
provider:
prometheus:
address: http://prometheus.example.com:9090
query: |
sum(irate(
istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}",response_code=~"5.*"}[5m]
)) /
sum(irate(
istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}"}[5m]
))
- name: webmetric
successCondition: result == 'true'
provider:
web:
# placeholders are resolved when an AnalysisRun is created
url: "{{ args.api-url }}?service={{ args.service-name }}"
headers:
- key: Authorization
value: "Bearer {{ args.api-token }}"
jsonPath: "{$.results.ok}"
- name: http-benchmark
failureLimit: 100
interval: 5s
provider:
job:
spec:
template:
spec:
containers:
- name: load-tester
image: argoproj/load-tester:latest
command: [sh, -xec]
args:
- |
wrk -t1 -c1 -d5 -s report.lua http://{{args.host}}/color
jq -e '.errors_ratio <= 0.05' report.json
restartPolicy: Never
backoffLimit: 0
Прошу прощения за огромный шаблон (его пришлось склеить из трех), но это сделано для максимальной наглядности.
args:
– можно задать переменные для шаблона и при запуске экземпляра анализа их можно переопределить. Значения можно взять из секретовmetrics:
– массив тестов. Метрики поддерживают разные провайдеры. В примере показаны istio, web и job.Если что-то не прошло нашу проверку – откатываем изменения (поведение можно менять)
Для метрики
http-benchmark
запускается отдельный контейнер (по ссылке вы можете посмотреть файл report.lua, который формирует структуру отчета). Провайдерjob
позволяет нам подпихнуть любой контейнер с нашими автотестами и прогнать их без переключение реального трафика (для этого используется experiment).Шаблонизация очень удобна, если ваши сервисы поставляются разными командами: так вы задаете единые критерии для всех. Особенно это помогает с теми, у кого нет времени на написание автотестов.
Подключаем шаблон к Rollout. Сначала рассмотрим поведение при canary-стратегии:
strategy:
canary:
analysis:
templates:
- templateName: error-rate # имя шаблона
- templateName: Mytemplate # можно подключать несколько шаблонов сразу
clusterScope: true #это служит для подключения ClusterAnalysisTemplate
startingStep: 2 # Ждём когда rollout дойдёт до 2 шага (40%) и стартуем
args:
- name: service-name
value: guestbook-svc.default.svc.cluster.local
steps:
- setWeight: 20
- pause: {duration: 10m}
- setWeight: 40
- pause: {duration: 10m}
- setWeight: 60
- pause: {duration: 10m}
- setWeight: 80
- pause: {duration: 10m}
В данном манифесте показан пример анализа в фоне: Rollout потихоньку подменяет поды, а инстанс анализа делает своё дело до тех пор, пока не завершится накат новой ревизии или же возникнет ошибка и тогда нас ждёт Rollback. Есть другой вариант применения – Inline Analysis. Он позволяет нам вклиниться в steps
, анализироваться столько сколько хочется и только потом переходить на другие шаги.
steps:
- setWeight: 20
- pause: {duration: 5m}
- analysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: guestbook-svc.default.svc.cluster.local
Тут будет необходим тюнинг шаблона анализа, без interval
и count
ваш тест выполнится один раз
metrics:
- name: success-rate
successCondition: result[0] >= 0.95
interval: 60s
count: 5
provider:
prometheus:
address: http://prometheus.example.com:9090
query: ...
Тут уже в зависимости от количества новых подов, мы можем выбрать различные наборы и продолжительность наших тестов. А теперь рассмотрим BlueGreen – стратегию: в данном случае мы можем проводить проверку до переключения трафика и после:
strategy:
blueGreen:
activeService: active-svc
previewService: preview-svc
prePromotionAnalysis: # прогоняем тесты и переключем
templates:
- templateName: smoke-tests
#...или...
scaleDownDelaySeconds: 600 # сколько живёт текущая реплика после переключения
postPromotionAnalysis: # прогоняем тесты после переключения
templates:
- templateName: smoke-tests
В первом случае контроллер просто не переключит трафик, во втором будет производиться откат, поэтому рекомендуется увеличить (в разумных пределах) scaleDownDelaySeconds,
чтобы контроллер не уничтожал сразу предыдущую реплику. В случае проблем он быстро перекинет лейблы и мы не будем ожидать старта контейнеров.
Как всегда есть исключения из правил: метрика вроде бы есть, но если не выполняется – давайте не будем из-за нее останавливать релиз. Для этого служит механизм dry-run:
можно пометить метрики и выкатка не зафейлится, если все плохо.
Пример dry-run метрик
#В манифесте AnalysisTemplate
...
dryRun:
- metricName: .*
metrics:
- name: total-5xx-errors
# в манифестах rollout
...
steps:
- analysis:
templates:
- templateName: random-fail
- templateName: always-pass
dryRun:
- metricName: .*
Еще есть состояние inconclusive или, говоря по-русски, «нипанятна» – состояние, когда заданы success и failed границы, но мы не попали. В таком случае Rollout встает на паузу и ждет ручного вмешательства. На мой взгляд, лучше не допускать дырки в диапазонах и однозначно задавать критерии.
metrics:
- name: success-rate
successCondition: result[0] >= 0.90
failureCondition: result[0] < 0.50
Мы рассмотрели Analysis, который позволял нам подстелить соломку в случае плохого релиза, а теперь пора перейти к целому «стогу» – experiments. Согласно документации, в первом случае мы переключаем реальный клиентский трафик (хотя в случае BlueGreen prePromotionAnalysis ничего не переключается). Experiments позволяет нам развернуть произвольное количество replicaset в с разными контейнерами, прогнать по ним тесты, сравнить и принять решение о судьбе релиза.
длинная портянка из документации
apiVersion: argoproj.io/v1alpha1
kind: Experiment
metadata:
name: example-experiment
spec:
# Duration of the experiment, beginning from when all ReplicaSets became healthy (optional)
# If omitted, will run indefinitely until terminated, or until all analyses which were marked
# `requiredForCompletion` have completed.
duration: 20m
# Deadline in seconds in which a ReplicaSet should make progress towards becoming available.
# If exceeded, the Experiment will fail.
progressDeadlineSeconds: 30
# List of pod template specs to run in the experiment as ReplicaSets
templates:
- name: purple
# Number of replicas to run (optional). If omitted, will run a single replica
replicas: 1
selector:
matchLabels:
app: canary-demo
color: purple
template:
metadata:
labels:
app: canary-demo
color: purple
spec:
containers:
- name: rollouts-demo
image: argoproj/rollouts-demo:purple
imagePullPolicy: Always
ports:
- name: http
containerPort: 8080
protocol: TCP
- name: orange
replicas: 1
minReadySeconds: 10
selector:
matchLabels:
app: canary-demo
color: orange
template:
metadata:
labels:
app: canary-demo
color: orange
spec:
containers:
- name: rollouts-demo
image: argoproj/rollouts-demo:orange
imagePullPolicy: Always
ports:
- name: http
containerPort: 8080
protocol: TCP
# List of AnalysisTemplate references to perform during the experiment
analyses:
- name: purple
templateName: http-benchmark
args:
- name: host
value: purple
- name: orange
templateName: http-benchmark
args:
- name: host
value: orange
- name: compare-results
templateName: compare
# If requiredForCompletion is true for an analysis reference, the Experiment will not complete
# until this analysis has completed.
requiredForCompletion: true
args:
- name: host
value: purple
Также можно запускать автономные эксперименты не привязанных к процессу выкатки. Это может быть востребовано у тестировщиков – запустил/забыл/вспомнил/сравнил. Вот так можно подглядывать за экспериментом:
Способ подключения из rollout
похож на AnalysisTemplate
:
strategy:
canary:
steps:
- experiment:
duration: 1h
templates:
- name: baseline
specRef: stable
- name: canary
specRef: canary
analyses:
- name : mann-whitney
templateName: mann-whitney
args:
- name: baseline-hash
value: "{{templates.baseline.podTemplateHash}}"
- name: canary-hash
value: "{{templates.canary.podTemplateHash}}"
Можно сделать еще интереснее: развернуть N-экспериментальных replicaset
, переключить какой-то процент боевого трафика на них и посмотреть, что из этого получится (работает только на SMI, ALB, Istio):
steps:
- experiment:
duration: 1h
templates:
- name: experiment-baseline
specRef: stable
weight: 5
- name: experiment-canary
specRef: canary
weight: 5
Есть еще удобная функция – не писать весь spec
в манифесте rollout
, а прицепиться к существующему deployment:
# манифест rollout
spec:
replicas: 5
selector:
matchLabels:
app: rollout-ref-deployment
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: rollout-ref-deployment
strategy:
#...
Обратите внимание, что rollout
не управляет количеством реплик deployment
. После наката, у вас будет задвоение (но это позволяет избежать простоя) и если все прошло успешно, вы уменьшаете число реплик у deployment
до нуля. С этого момента deployment
служит по сути шаблоном spec:
все дальнейшие изменения вы делаете в нем (но число реплик всегда должно быть 0), а rollout контроллер отслеживает изменения и порождает replicaset.
Я собрал для вас основные выдержки из документации, которые интересны для специалистов по автоматизации и они помогут вам быстро вникнуть в решение и начать применять его на практике. Примеры из документации можете посмотреть здесь и здесь (тут встречаются старые апи), есть гайд по миграции.
Если у вас есть замечания, благодарности, или вы хотите поделиться опытом – добро пожаловать в комментарии.
Всем удачных релизов!