小Cの已经记不起来的博客

使用 Argo Rollouts 实现 Kubernetes 金丝雀发布指标分析

前阵子把 Argo Rollouts 接进集群,本来只是想按 10%、50%、100% 把流量慢慢放出去,结果发现光有步骤还不够——你总得知道新版本到底是真没问题,还是只是错误还没暴露出来。没有指标分析的金丝雀发布,说白了就是手动暂停,pause 完心里还是没底。

分析模板和 AnalysisRun 到底在干什么

Argo Rollouts 的指标分析不是它自己去抓业务指标,而是按你给的查询去问 Prometheus,然后拿返回值和 successCondition 比。每次查询叫一次 measurement,成功次数达到 count 就算这个分析通过;失败次数超过 failureLimit,AnalysisRun 就失败,Rollout 会中止这次发布,把流量切回 stable,停在 Degraded 状态等你看日志。

分析逻辑写在 AnalysisTemplate 里,Rollout 在某个金丝雀步骤里引用它,Argo Rollouts 就会自动创建一个 AnalysisRun。一般不用手写 AnalysisRun,除非你想单独验证查询。

先把 Prometheus 查询写明白

我这边用了 Istio,所以指标里能直接按 destination_service 区分 stable 和 canary。查询大概是下面这样,先算 canary 服务的成功率:

sum(rate(istio_requests_total{
  destination_service=~"my-app-canary.default.svc.cluster.local",
  response_code!~"5.*"
}[1m]))
/
sum(rate(istio_requests_total{
  destination_service=~"my-app-canary.default.svc.cluster.local"
}[1m]))

别急着往 YAML 里抄,先去 Prometheus 页面手跑一遍,看看返回值是不是你想要的。我第一次 label 写错,查询直接返回空,AnalysisRun 那边看起来就像“指标不满足”,其实是我自己查错了。这个坑后面还会说。

写一个 AnalysisTemplate

下面这个模板就是把上面的查询参数化,canary-service 从 Rollout 传进来:

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: canary-success-rate
spec:
  args:
    - name: canary-service
  metrics:
    - name: success-rate
      interval: 30s
      count: 5
      successCondition: result[0] >= 0.99
      failureLimit: 2
      provider:
        prometheus:
          address: http://prometheus.monitoring.svc:9090
          query: |
            sum(rate(istio_requests_total{
              destination_service=~"{{args.canary-service}}",
              response_code!~"5.*"
            }[1m]))
            /
            sum(rate(istio_requests_total{
              destination_service=~"{{args.canary-service}}"
            }[1m]))

这里 interval: 30s、count: 5 表示大概查 5 次,每次间隔 30 秒;failureLimit: 2 是我给的容错,失败超过 2 次就判失败。成功率阈值我设在 99%,低流量服务可以适当放宽,不然一点抖动就能把你发布卡死。

挂到 Rollout 的金丝雀步骤里

Rollout 里要让 stable 和 canary 各有一个 Service,trafficRouting 那边我用了 Istio,其他网关就换成对应的配置:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: my-app
spec:
  replicas: 10
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: registry.example.com/my-app:v2
          ports:
            - containerPort: 8080
  strategy:
    canary:
      stableService: my-app-stable
      canaryService: my-app-canary
      trafficRouting:
        istio:
          virtualService:
            name: my-app
            routes:
              - primary
      steps:
        - setWeight: 10
        - pause:
            duration: 2m
        - analysis:
            templates:
              - templateName: canary-success-rate
            args:
              - name: canary-service
                value: my-app-canary.default.svc.cluster.local
        - setWeight: 50
        - pause:
            duration: 5m
        - analysis:
            templates:
              - templateName: canary-success-rate
            args:
              - name: canary-service
                value: my-app-canary.default.svc.cluster.local

my-app-stable 和 my-app-canary 这两个 Service 得提前建好,selector 不要写死某个版本 label,不然 Rollouts 改不了后端 Pod,流量权重也切不过去。

看它跑起来

发布的时候我一般开两个窗口,一个盯 Rollout:

kubectl argo rollouts get rollout my-app -n default --watch

另一个看 AnalysisRun:

kubectl get analysisrun -n default
kubectl describe analysisrun <analysisrun-name> -n default

正常的话能看到 Step 停在 analysis 那一步,AnalysisRun 里每隔 30 秒出一条 measurement。成功次数够了就继续往下走;失败超过 failureLimit,Rollout 会 abort,流量回到 stable。这个过程比单纯 pause 踏实多了,至少不是“等两分钟,感觉没事,继续”。

几个我踩过的坑

第一,Prometheus 查询一定要先手跑。返回空数组、label 写错、服务名少个 namespace,这些都会让分析结果看起来很怪。别问我怎么知道的。

第二,failureLimit 别设 0。低流量服务偶尔一条失败就够你回滚了,我一开始设 0,然后就没有然后了。

第三,分析窗口别太短。拿 10 秒的 rate 去查一个 QPS 不高的服务,曲线能把你吓死,至少 1 分钟起步,再配合 count 多看几次。

第四,指标别只看错误率。金丝雀有时候错误率没上来,但 P95 延迟已经炸了,最好在 metrics 里再加一条延迟查询,阈值按业务来。反正我现在每次改 Rollout 之前,都会先去 Prometheus 里把 query 手跑一遍,确认返回值不是空的,再 apply。省得等 AnalysisRun 失败了再回头翻 label。

评论

还没有评论。

发表评论

提交后评论将经过自动审核,审核通过后公开展示。

未在播放