откатывать канарейку при превышении burn rate по SLO

Автоматический анализ канареечных релизов и откат по SLO в Kubernetes

14 минут

Запустить канареечный релиз просто; продвинуть или откатить по фактам — нет. Свяжите Flagger или Argo Rollouts с математикой burn rate по SLO, чтобы откат срабатывал при ускоренном расходе бюджета ошибок — а не после ночного просмотра панели.

Почему канареечные релизы ломаются без решений по SLO

Прогрессивная выкатка даёт механизм: часть трафика на новую версию, наблюдение, продвижение или откат. Логики принятия решений обычно нет. Ручной контроль — пятнадцать минут на панели и кнопка «продвинуть» — не масштабируется и добавляет задержку человека. Статические пороги вроде «ошибок больше пяти процентов пять минут» не связаны с целями SLO и часто слишком мягкие: к пяти процентам вы уже могли сжечь заметную долю месячного бюджета ошибок. Канарейка выглядит здоровой сама по себе, а суммарное SLO по всем репликам проседает — потому что никто не сравнивает версии с расходом бюджета. Откат ведут себя по-разному: одни сервисы откатываются сами, другие ждут оператора. Команды либо слепо доверяют канарейке, либо навешивают столько ручных ворот, что процесс не быстрее blue-green.

Burn rate по SLO как ворота продвижения

SLO — источник истины для promote и rollback, а не проценты «на глаз». Опишите SLI в Git через Sloth или аналог, измеряйте burn rate бюджета ошибок в окне канарейки и пусть Flagger или Argo Rollouts наращивают вес только пока burn в пределах политики. При превышении многократного порога, выведенного из цели SLO, контроллер возвращает трафик на stable автоматически. CI пушит теги образов; контроллер канарейки проверяет здоровье. GitOps применяет желаемое состояние, но анализ не даёт плохой версии получить весь трафик. Цепочка: сборка и push, канарейка на десяти процентах, Prometheus оценивает burn rate каждые тридцать секунд, вес растёт при успехе или откат при серии провалов.

Определение SLO в коде через Sloth

Храните PrometheusServiceLevel рядом с манифестами сервиса: цели, запросы и имена алертов под версионным контролем. SLO доступности 99,9% даёт бюджет ошибок 0,1%. Burn rate — текущая доля ошибок, делённая на допустимую; 14,4× за час исчерпывает месячный бюджет примерно за час, 72× за пять минут ловит быстрые регрессии. Sloth генерирует recording rules и многократные burn-rate алерты, которые можно переиспользовать в шаблонах анализа канарейки.

YAML · SLO доступности в Sloth
apiVersion: sloth.slok.dev/v1
kind: PrometheusServiceLevel
metadata:
  name: my-service-slo
  namespace: production
spec:
  service: "my-service"
  labels:
    team: "platform"
  slos:
    - name: "availability"
      objective: 99.9
      description: "99.9% of requests succeed"
      sli:
        events:
          errorQuery: |
            sum(rate(http_requests_total{
              namespace="production",
              service="my-service",
              code=~"5.."
            }[5m]))
          totalQuery: |
            sum(rate(http_requests_total{
              namespace="production",
              service="my-service"
            }[5m]))
      alerting:
        name: "MyServiceHighErrorBudgetBurn"
        labels:
          severity: "critical"
        annotations:
          summary: "High error budget burn rate"
        pageAlert:
          labels:
            severity: "page"

Анализ канарейки в Flagger с метрикой burn rate

Flagger наращивает вес канарейки по расписанию и опрашивает Prometheus через MetricTemplate. PromQL должен возвращать числовой burn rate, а порог задавайте в thresholdRange у метрики Canary — не сравнение «больше 14,4» внутри запроса. Размечайте метрики так, чтобы трафик канарейки и stable различались: метка version, subset mesh или метка пода от ingress. Без разделения burn rate смешивает версии и скрывает регрессию на доле канарейки.

YAML · Flagger Canary с порогом burn rate
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: my-service
  namespace: production
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-service
  service:
    port: 8080
  progressDeadlineSeconds: 600
  analysis:
    interval: 30s
    threshold: 5
    maxWeight: 50
    stepWeight: 10
    metrics:
      - name: request-success-rate
        thresholdRange:
          min: 99
        interval: 30s
      - name: slo-burn-rate
        templateRef:
          name: slo-budget-burn
        thresholdRange:
          max: 14.4
        interval: 30s
  revertOnFailure: true
YAML · Flagger MetricTemplate для burn rate
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
  name: slo-budget-burn
  namespace: production
spec:
  provider:
    type: prometheus
    address: http://prometheus.monitoring:9090
  query: |
    (
      sum(rate(http_requests_total{
        namespace="production",
        service="{{ target }}",
        version="canary",
        code=~"5.."
      }[5m]))
      /
      sum(rate(http_requests_total{
        namespace="production",
        service="{{ target }}",
        version="canary"
      }[5m]))
    )
    /
    (1 - 0.999)

Альтернатива: AnalysisTemplate в Argo Rollouts

Argo Rollouts запускает AnalysisRun по метрикам AnalysisTemplate между шагами канарейки. Тот же PromQL для burn rate, successCondition сравнивает result с порогом окна — 14,4 для политики на час, выше для коротких окон. Сочетайте с маршрутизацией Istio, NGINX или ALB, чтобы stable и canary Service получали взвешенный трафик. Статусы Rollout и AnalysisRun фиксируют каждый pass или fail для разбора после инцидента.

YAML · Rollout с SLO-анализом
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: my-service
  namespace: production
spec:
  replicas: 3
  strategy:
    canary:
      canaryService: my-service-canary
      stableService: my-service-stable
      trafficRouting:
        istio:
          virtualService:
            name: my-service-vsvc
            routes:
              - primary
      analysis:
        templates:
          - templateName: slo-check
        startingStep: 2
        args:
          - name: service-name
            value: my-service
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: slo-check
spec:
  args:
    - name: service-name
  metrics:
    - name: slo-budget-burn-rate
      interval: 30s
      count: 5
      successCondition: result[0] < 14.4
      failureLimit: 3
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            (
              sum(rate(http_requests_total{
                namespace="production",
                service="{{args.service-name}}",
                code=~"5.."
              }[5m]))
              /
              sum(rate(http_requests_total{
                namespace="production",
                service="{{args.service-name}}"
              }[5m]))
            )
            /
            (1 - 0.999)

Полный цикл: выкатка, откат и алертинг

Запускайте релиз обновлением образа Deployment; Flagger или Rollouts начинают анализ с десяти процентов трафика. Каждый интервал — ошибки и burn rate; три провала подряд откатывают на stable. Алертируйте в Prometheus на повышенный burn в окне канарейки, чтобы дежурный видел причину автоотката — тихие откаты маскируют повторяющиеся проблемы сборки. Логируйте снимки метрик и время решения при каждом promote или abort.

Bash · запуск канарейки сменой образа
kubectl set image deployment/my-service \
  my-service=my-registry/my-service:v2.1.0 \
  -n production

# Flagger или Rollouts: 90% stable / 10% canary, метрики каждые 30s
PromQL · burn rate бюджета ошибок для SLO 99,9%
(
  sum(rate(http_requests_total{
    namespace="production",
    service="my-service",
    code=~"5.."
  }[5m]))
  /
  sum(rate(http_requests_total{
    namespace="production",
    service="my-service"
  }[5m]))
)
/
(1 - 0.999)
YAML · алерт Prometheus в окне канарейки
groups:
  - name: canary-analysis
    rules:
      - alert: CanaryHighBudgetBurn
        expr: |
          (
            sum(rate(http_requests_total{
              namespace="production",
              service="my-service",
              version="canary",
              code=~"5.."
            }[5m]))
            /
            sum(rate(http_requests_total{
              namespace="production",
              service="my-service",
              version="canary"
            }[5m]))
          ) / (1 - 0.999) > 14.4
        for: 2m
        labels:
          severity: warning
          canary: "true"
        annotations:
          summary: "Burn rate канарейки превышает политику SLO на 1 ч; возможен откат"

Практика: пороги, разделение ролей, учения и связь с инцидентами

Выводите каждый порог из цели SLO и окна оценки — зафиксируйте арифметику в README репозитория. Разделяйте триггеры деплоя в CI и проверку в Flagger или Rollouts: один инструмент выкатывает артефакты, другой держит ворота трафика. Начинайте строго — ложный откат безопаснее продвижения с регрессией. Используйте burn rate в нескольких окнах: пять минут при 72× и час при 14,4× отсекают всплески, но ловят устойчивую деградацию. Раз в квартал проверяйте откат намеренно плохой сборкой или хаос-экспериментом; неотработанный путь ломается в бою. При автооткате создавайте тикет или зовите дежурного — повторяющиеся тихие abort часто означают кривые тесты или метки. Держите серии canary и stable раздельными в Prometheus, согласуйте ворота с правилами продвижения в GitOps и относитесь к панелям SLO как к политике релиза, а не к экрану, который никто не смотрит в час выкатки.

Деление трафика и feature flags — база; эта статья добавляет пороги SLO поверх прогрессивной выкатки с canary и feature flags в Kubernetes.

Пороги burn rate имеют смысл только после определения SLI и бюджета ошибок — см. гайд по SLO, SLI и error budget для платформенных команд.