Перейти к содержимому
HoginHogin
Назад

Argo Rollouts + Gateway API: канарейка с авто-откатом без service mesh

9 мин чтения

Канарейка с автоматическим откатом долго была привилегией кластеров с service mesh: резать живой трафик по процентам умели VirtualService в Istio и SMI-сплиты в Linkerd, а у кого mesh не было — выкручивались аннотациями ingress-контроллера. Argo Rollouts это ограничение снял: официальный плагин trafficrouter-gatewayapi режет трафик прямо в стандартном HTTPRoute, а AnalysisTemplate по метрикам Prometheus сам решает — продвигать релиз или откатывать. Service mesh не нужен, нужен Gateway API, который в 2026 году уже есть почти у всех.

Почему сейчас: начиная с Rollouts 1.9 плагин поддерживает maxTrafficWeight — шкалу весов можно растянуть до миллиона и резать трафик тоньше 1%, а экосистема (Cilium, kgateway, VPC Lattice) строит интеграции поверх Gateway API. Прогрессивная доставка переехала с mesh-специфичных ресурсов на стандартный API кластера, и порог входа рухнул.

Содержание

Открыть содержание

Rollout вместо Deployment: что меняется

Deployment со стратегией RollingUpdate заменяет поды по расписанию и никогда не спрашивает метрики — эту проблему мы подробно разбирали в статье про Flagger. Argo Rollouts решает её на уровне манифеста: вместо Deployment вы описываете Rollout — тот же pod template, но стратегия canary с явными шагами. Совместимость с Argo CD из коробки: для GitOps-пайплайна это просто ещё один CRD в git.

В канареечном режиме Rollout держит два ReplicaSet — стабильный и canary — и два Service с одинаковым селектором: web-stable и web-canary. Нет, они не выберут одни и те же поды: Rollout сам вешает подам каждого ReplicaSet метку rollouts-pod-template-hash, поэтому стабильный Service цепляет старые поды, canary — новые. А трафик между ними режет не kube-proxy, а Gateway — по весам в HTTPRoute с двумя backendRefs:

Как режется трафик: HTTPRoute с двумя backendRefs

Ключевая механика: плагин argoproj-labs/gatewayAPI внутри контроллера переписывает поле weight у обоих backendRefs, когда Rollout переходит на следующий шаг. Gateway — Envoy Gateway, Cilium, Traefik, kgateway, любой, реализующий Gateway API v1 — честно исполняет проценты. В git лежит манифест без весов; фактическое состояние меняет контроллер: шаги декларативны, исполнение автоматическое.

AnalysisTemplate: метрики, которые валят релиз

Сдвиг трафика — половина канарейки. Вторая половина — решение «продвигать или нет», и его Argo Rollouts тоже забирает у человека. AnalysisTemplate — это CR с описанием метрики: какой запрос гонять в Prometheus, как часто, при каком значении измерение считается проваленным и сколько провалов терпим.

Когда Rollout доходит до шага analysis, создаётся объект AnalysisRun, который блокирует дальнейшее продвижение: каждые interval он выполняет запрос и сверяет результат с условием. Проваленных измерений больше failureLimit — AnalysisRun помечается Failed, Rollout уходит в abort: плагин мгновенно возвращает 100% веса на стабильный backendRef, canary-поды масштабируются в ноль, статус Rollout — Degraded. Никакого kubectl rollout undo в 3 часа ночи — откат делает контроллер за секунды.

Канарейка по шагам: анализ метрик решает — дальше или откат

Метрики не обязаны быть инфраструктурными: запрос — произвольный PromQL, так что порогом может быть доля успешных оплат или конверсия, а не только p95 и error-rate.

Сравнение в одной таблице

Deployment + RollingUpdateRollout + Gateway API
Сдвиг трафикапо мере готовности подовшагами 5 → 25 → 50 → 100% через HTTPRoute
Анализ метрикнет, только алерты постфактумAnalysisRun на каждом шаге
Решение об откатечеловек по алертуконтроллер по порогу из git
Минимальный blast-radius~25% трафика (maxUnavailable)5% или тоньше
Service meshне нужен, но и сплитов нетне нужен — режет стандартный HTTPRoute
GitOpsобраз в git, дальше вслепуюте же манифесты + Argo CD из коробки
Откатминуты и человексекунды и контроллер

Что нужно, чтобы поднять канарейку

Предусловия: кластер с реализацией Gateway API v1 (Envoy Gateway, Cilium, Traefik, kgateway) и Prometheus с метриками приложения. Дальше четыре шага.

1. Контроллер, CLI и плагин. Ставим Argo Rollouts и kubectl-плагин:

kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts \
  -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
kubectl krew install rollouts

Плагин подключается через ConfigMap с фиксированным именем argo-rollouts-config:

apiVersion: v1
kind: ConfigMap
metadata:
  name: argo-rollouts-config   # имя фиксировано
  namespace: argo-rollouts
data:
  trafficRouterPlugins: |-
    - name: "argoproj-labs/gatewayAPI"
      location: "https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/releases/download/v0.5.0/gatewayapi-plugin-linux-amd64"

Применить, перезапустить контроллер (kubectl -n argo-rollouts rollout restart deployment argo-rollouts) и проверить в логах строку Downloading plugin argoproj-labs/gatewayAPI. Для офлайн-кластеров есть вариант с init-контейнером и образом из ghcr.io — см. документацию плагина.

2. RBAC. Плагину нужны права читать Services и патчить HTTPRoute (и GRPCRoute, если используете):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: argo-rollouts-gatewayapi
rules:
  - apiGroups: [""]
    resources: [services]
    verbs: [get]
  - apiGroups: [gateway.networking.k8s.io]
    resources: [httproutes, grpcroutes]
    verbs: [get, list, patch, update]

Свяжите роль с ServiceAccount argo-rollouts через ClusterRoleBinding — и всё, кластер готов.

3. Два Service и HTTPRoute. Селекторы одинаковые — разрулит Rollout:

apiVersion: v1
kind: Service
metadata:
  name: web-stable
  namespace: prod
spec:
  selector: { app: web }
  ports: [{ port: 80, targetPort: http }]
---
apiVersion: v1
kind: Service
metadata:
  name: web-canary
  namespace: prod
spec:
  selector: { app: web }
  ports: [{ port: 80, targetPort: http }]
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web
  namespace: prod
spec:
  parentRefs: [{ name: eg }]
  rules:
    - matches: [{ path: { type: PathPrefix, value: / } }]
      backendRefs:
        - { name: web-stable, kind: Service, port: 80 }
        - { name: web-canary, kind: Service, port: 80 }

Веса в манифесте не указаны — их допишет плагин при первом же rollout.

4. AnalysisTemplate и сам Rollout. Шаблон анализа p95 с failureLimit: 2:

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: p95-latency
  namespace: prod
spec:
  args:
    - name: service
  metrics:
    - name: p95
      interval: 1m
      count: 3
      failureLimit: 2
      failureCondition: result[0] >= 500
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            histogram_quantile(0.95,
              sum by (le) (rate(http_request_duration_seconds_bucket
                {service="{{args.service}}", version="canary"}[2m]))
            ) * 1000

Три замера с интервалом в минуту; два замера хуже 500 мс — и AnalysisRun валится в Failed. Теперь Rollout с шагами 5/25/50/100:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: web
  namespace: prod
spec:
  replicas: 10
  selector: { matchLabels: { app: web } }
  template:
    metadata: { labels: { app: web } }
    spec:
      containers:
        - name: web
          image: registry.example.com/web:v1
          ports: [{ name: http, containerPort: 8080 }]
  strategy:
    canary:
      stableService: web-stable
      canaryService: web-canary
      maxSurge: 1
      maxUnavailable: 0
      trafficRouting:
        plugins:
          argoproj-labs/gatewayAPI:
            httpRoute: web
            namespace: prod
      steps:
        - setWeight: 5
        - pause: { duration: 2m }
        - analysis:
            templates: [{ templateName: p95-latency }]
            args: [{ name: service, value: web }]
        - setWeight: 25
        - pause: { duration: 5m }
        - analysis:
            templates: [{ templateName: p95-latency }]
            args: [{ name: service, value: web }]
        - setWeight: 50
        - pause: { duration: 5m }
        - analysis:
            templates: [{ templateName: p95-latency }]
            args: [{ name: service, value: web }]
        - setWeight: 100

Каждый шаг — поднять вес, дать метрикам накопиться, спросить Prometheus. Хотите ручной гейт — вставьте pause: {} без duration: Rollout замрёт и будет ждать kubectl argo rollouts promote. А если нужна точность тоньше процента, добавьте в trafficRouting поле maxTrafficWeight: 10000 (Rollouts 1.9+): шаг setWeight: 100 станет означать 1% трафика.

Как проверить авто-откат

Прогон на «сломанном» релизе — единственная честная проверка. Деплоим версию, которая укладывает p95:

kubectl argo rollouts set image web web=registry.example.com/web:v2-broken
kubectl argo rollouts get rollout web -n prod --watch

Ожидаемая картина: вес поднимается до 5%, пауза, AnalysisRun делает замеры; на третьем замере (два провальных при failureLimit: 2) статус меняется на Failed, Rollout — на Degraded, стрелка веса возвращается на 0. Сверьте фактическое состояние маршрута:

kubectl -n prod get httproute web -o jsonpath='{.spec.rules[0].backendRefs}'

После отката там 100/0 — весь трафик снова на web-stable. События с причиной (kubectl -n prod describe rollout web) покажут, какая метрика и насколько превысила порог. Годовая практика: такой негативный прогон стоит гонять после каждой смены порогов — AnalysisTemplate, который ни разу не валял релиз, вы не проверяли.

Rollouts или Flagger

Мы уже писали о Flagger — это ближайший и равноценный конкурент, тоже без mesh, тоже на Gateway API. Честная разница такая. Flagger оборачивает существующий Deployment, не меняя его вид, — миграция нулевая; у него встроенные webhook-нагрузочные тесты и родная экосистема Flux. Rollouts заменяет Deployment на собственный CR — миграция касается манифеста, зато шаги с паузами и ручными гейтами описываются явно, AnalysisRun — отдельный переиспользуемый объект, есть blue-green и Experiment, kubectl-плагин с живым UI. Практическое правило: у вас Flux и «не трогайте Deployment» — берите Flagger; у вас Argo CD и нужны паузы с ручным промоутом — Rollouts. Оба дают одно и то же главное: откат за секунды по метрикам вместо отката за минуты по пейджеру.

Грабли

Итог

Mesh для канареек больше не обязателен: любой кластер с Gateway API получает полный цикл progressive delivery — шаги, паузы, метрики и авто-откат — ценой одного контроллера и плагина. Плата: замена Deployment на Rollout, RBAC для плагина и аккуратный ignoreDifferences для Argo CD. Отдача: сломанный релиз видят 5% пользователей вместо всех, а откат происходит по порогу из git за секунды — без дежурного с пейджером. Если Gateway API у вас уже стоит, попробовать можно за один вечер.


Поделиться:

Следующая статья
DORA-2026: ИИ усиливает то, что у вас есть — нестабильный пайплайн он ухудшает, а не чинит