ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Argo Rollouts与Prometheus的指标驱动金丝雀发布实践

基于Argo Rollouts与Prometheus的指标驱动金丝雀发布实践 1. 先搞清楚一个问题金丝雀发布到底卡在哪里我最早做金丝雀发布的时候流程看起来挺自动化写好 Deployment改镜像 tag等滚动更新跑完然后盯着 Grafana 看监控曲线。看到曲线稳了就点一下“全量”看到曲线不对劲就赶紧回滚。这个过程表面上叫“金丝雀发布”实际上等于“发布靠手验收靠看”本质还是人肉决策。后来我把这套流程换成了 Argo Rollouts Prometheus 做指标驱动才算是真正把“这个版本能不能继续放量”这个决策交给了机器。Argo Rollouts 负责发布策略和执行Prometheus 负责提供金丝雀版本质量数据两者配合实现了一个闭环新版本先放少量流量Prometheus 用 PromQL 实时判断成功率、延迟等指标指标达标就自动放量指标劣化就自动回滚整个过程不需要人盯着监控面板。这篇文章写给正在用 Kubernetes 做应用发布、但还在靠人工观察判断发布质量的团队和个人。读完你能理解 Argo Rollouts 的 AnalysisRun 分析机制是怎么运转的能自己写出基于 Prometheus 指标的 AnalysisTemplate知道配置里面哪些参数会踩坑以及我踩过的几个排查起来特别费劲的问题。1.1 自动验收不是“自动跑一下脚本”而是策略内嵌到发布流程很多人对“自动验收”的理解是发布后跑一个接口测试脚本脚本过了就认为版本没问题。这个思路方向对但颗粒度太粗。一次发布过程中真正危险的不是接口通不通而是流量从旧版本切到新版本的过程中用户体验会不会劣化。比如接口成功率从 99.99% 掉到 99%听起来还是很高但放在大流量下面可能已经影响了几千个用户。Argo Rollouts 的实现方式是把质量判断拆成一个一个步骤嵌到发布流程里。你在 Rollout 对象的 canary strategy 里定义发布步骤先给新版本 10% 流量停一下跑一个 AnalysisRun 去查 Prometheus 指标如果指标达标继续放到 50%再停一下再跑一个 AnalysisRun如果指标不达标直接回滚到旧版本。这套逻辑不是发布会完才跑而是在发布过程中持续跑、持续判断。这个区别很重要自动验收不是一个独立的测试阶段而是发布流程的一个组成部分。版本好不好不是发布完成后才知道而是在每次放量前就已经被评估过了。1.2 这套方案解决的三个典型问题第一解决“发布上线只能挑凌晨”的问题。以前我发布都得挑流量低的时候因为没底气怕出问题影响的人太多。有了指标驱动自动验收白天也能发因为新版本在低流量阶段就会被指标卡住出问题最多影响 10% 的用户回滚也是自动的。第二解决“监控曲线看不过来”的问题。发布时你不可能同时盯着十几个面板很多时候旧的告警规则还没触发其实线上已经开始出问题了。把成功率、错误率、延迟写进 AnalysisTemplate 之后相当于每个发布有了一个定制化的质量门禁旧版本该有的告警还是会有但发布专用的判断逻辑不用人看了。第三解决“回滚决策慢”的问题。人工判断回滚需要发现问题、确认问题、再执行 rollback这个链路快则几十秒慢则几分钟。指标驱动下AnalysisRun 判定失败后Argo Rollouts 会自动把流量切回旧版本我在实际操作中见过最顺利的一次从指标异常到回滚完成不到三十秒。2. 机制拆解AnalysisRun 是怎么变成“门禁”的Argo Rollouts 里最能跟普通 Deployment 拉开差距的就是AnalysisTemplate和AnalysisRun。AnalysisTemplate 是模板定义“要查什么指标、怎么查、查到结果怎么判断”AnalysisRun 是一次具体的分析实例发布过程中每触发一次分析就会创建一个 AnalysisRun 对象。我举个例子。你在 Rollout 的 steps 里写了一个 analysis 步骤指定引用名为myapp-success-rate的 AnalysisTemplate。当发布推进到这个步骤时Argo Rollouts 控制器会创建出一个名为类似myapp-7d86b9b6c8-1-analysisrun-1的资源这个资源负责周期性地向 Prometheus 发起 PromQL 查询并把每次查询的结果跟successCondition比较积攒成功或者失败次数最终得出一个结论通过、不通过还是不确定。2.1 一次发布从触发到自动放量的完整时序我梳理一下我自己环境里最典型的发布时序按时间顺序走一遍你更新 Rollout 对象里的 Pod template比如把镜像 tag 从 v1.0.0 改成 v1.0.1。Argo Rollouts 检测到 template hash 变化创建一个新的 ReplicaSet这就是金丝雀 ReplicaSet。控制器按照 canary strategy 里定义的 steps 开始推进。第一个 step 通常就是setWeight: 10指定金丝雀版本被分配到 10% 的流量。发布进入pause步骤保持金丝雀 10% 流量一小段时间让新版本 Pod 接一会儿真实流量。下一个 step 如果是analysis类型控制器就会创建 AnalysisRun。AnalysisRun 按照模板里配置的interval每隔一段时间去 Prometheus 查询指标。每次查询得到的结果如果满足successCondition成功计数加一如果结果不达标失败计数加一如果 Prometheus 返回错误或者超时错误计数加一。当成功计数到达count配置的值AnalysisRun 进入 Successful 状态发布继续往下走可以进入下一个setWeight放量步骤。如果失败计数到failureLimit或者错误计数超限AnalysisRun 进入 Failed/Error 状态发布自动中止并回滚到旧版本。如果分析窗口用完既没攒够成功次数也没达到失败阈值AnalysisRun 进入 Inconclusive 状态发布会暂停在当前位置等待人工介入。这套流程里最值得注意的点是发布不是一个直线而是一个带反馈环的控制流程。每次放量都可以理解成一次“提议”指标通过就是“批准”指标不通过就是“否决”。2.2 AnalysisRun 的判定逻辑成功多少次才算通过AnalysisTemplate 里最核心的字段就是那几个数字我一开始配置的时候没细想后来吃过亏才知道坑在哪。看一个典型配置apiVersion: argoproj.io/v1alpha1 kind: AnalysisTemplate metadata: name: myapp-success-rate spec: metrics: - name: success-rate initialDelay: 30s interval: 15s count: 10 successCondition: result[0] 99.0 failureLimit: 2 provider: prometheus: address: http://prometheus-server.monitoring.svc:80 query: | sum(rate(http_requests_total{appmyapp,status~2[0-9][0-9]|3[0-9][0-9]}[3m])) / sum(rate(http_requests_total{appmyapp}[3m])) * 100我来逐个解释initialDelay发布进入 analysis 步骤后先等多久再开始第一次查询。我习惯配置成 30 到 60 秒等新版本的 Pod 启动完、Prometheus 抓完第一轮数据再开始判断。interval每次查询的间隔。太短会频繁打 Prometheus太长会导致分析周期拖得很久。count需要拿到多少个满足条件的成功样本AnalysisRun 才会判定为通过。successConditionPromQL 返回的结果会以数组形式放在result里result[0]就是第一个值。这个表达式写成一个比较语句返回 true 就是本次样本成功。failureLimit最多允许几次不合格样本。超过这个数就判定失败。provider.prometheus指定 Prometheus 的地址和查询语句。按照上面这个配置整个分析流程是进入分析后等 30 秒然后每 15 秒查一次 Prometheus如果某次查询成功率大于等于 99%成功样本加一如果某次查询低于 99失败样本加一。当成功样本攒够 10 次分析通过当失败样本达到 2 次分析失败。如果查询窗口拉得很长比如 Prometheus 抓取异常导致数据一直空最后可能是 Inconclusive。我当时最困惑的就是count和failureLimit的关系。后来我这样理解count是及格线需要凑满的合格次数failureLimit是容忍度。如果失败次数先到达容忍上限就没有机会再凑合格次数了直接判失败。如果两边都没到阈值但发布可能因为别的原因暂停或者被人工干预了分析就是 Inconclusive。另外AnalysisRun 的状态还有一个容易忽略的 Error 状态。PromQL 语法错误、Prometheus 地址不通、查询结果为空导致下标越界都会让这个分析变成 Error。Error 和 Failed 在发布流程里通常都会被当作“发布失败”处理只不过 Failed 是版本质量不达标Error 是分析系统本身出了问题。这也是为什么我后来写查询语句前一定会先拿到 Prometheus 的查询页面上去手工验证一遍而不是直接写进模板。3. 手把手落地从零写一套指标驱动的金丝雀发布配置接下来直接给一套能跑起来的配置。我的示例环境里有一个微服务应用myapp镜像 tag 从 v1.0.0 升到 v1.0.1通过一个 Service 对外暴露Prometheus 用 kube-prometheus-stack 部署在monitoring命名空间。这套配置的最小集合包括一个 Rollout 对象、一个 Service、一个 AnalysisTemplate以及对应权限。3.1 环境准备Argo Rollouts 安装和权限注意点先用 Helm 安装 Argo Rollouts 控制器helm repo add argo https://argoproj.github.io/argo-helm helm install argo-rollouts argo/argo-rollouts \ --namespace argo-rollouts \ --create-namespace装好控制器之后我建议顺手装一下 kubectl 插件后面观察发布状态会特别方便kubectl argo rollouts version如果是 Linux 下用脚本安装直接去 GitHub Release 页面下载 kubectl-argo-rollouts 二进制放到 PATH 里即可。权限这里有个容易漏的地方Argo Rollouts 默认需要读取和更新 Service、Ingress、ReplicaSet 等资源如果公司集群 RBAC 管得严需要确认argo-rollouts这个 ServiceAccount 的 ClusterRole 有没有配齐。我之前在一个生产集群遇到过发布创建了但一直没反应看日志发现是控制器没有权限更新 Service 的 selector。另外应用要能访问 Prometheus只需要 AnalysisTemplate 里配的 address 能从控制器所在网络访问到即可通常情况下集群内 DNS 地址都通。3.2 Rollout 对象发布步骤怎么设计先看 Service。因为我要用的是副本数加权的金丝雀方案Service 的 selector 不要包含版本相关的标签只保留稳定的公共标签apiVersion: v1 kind: Service metadata: name: myapp-svc namespace: demo spec: selector: app: myapp ports: - name: http port: 80 targetPort: 8080接下来是 RolloutapiVersion: argoproj.io/v1alpha1 kind: Rollout metadata: name: myapp namespace: demo spec: replicas: 10 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - name: myapp image: registry.example.com/myapp:v1.0.1 ports: - containerPort: 8080 readinessProbe: httpGet: path: /health port: 8080 strategy: canary: maxSurge: 1 maxUnavailable: 0 steps: - setCanaryScale: weight: 10 setWeight: 10 - pause: { duration: 2m } - analysis: templates: - templateName: myapp-success-rate - setWeight: 50 - pause: { duration: 30m } - analysis: templates: - templateName: myapp-success-rate - setWeight: 100这里我解释下设计思路。replicas: 10是为了后面用副本数模拟流量比例。maxSurge: 1表示新版本最多额外多起一个 PodmaxUnavailable: 0表示发布过程中旧版本 Pod 不允许下线保证可用性。steps 的设计逻辑是第一段 10% 流量先探路停留 2 分钟看看有没有明显崩溃然后跑一次分析成功率达标才继续。第二段放到 50% 流量停留时间拉长到 30 分钟让更多真实流量经过新版本再跑第二次分析。全部通过才放到 100%。这套步骤的节奏是可以调的核心原则是越到高流量阶段分析越要严格观察时间也越长。如果你的业务流量本身就小10% 可能只有几个请求那就没有统计意义建议把第一次放量的比例调高到 20% 或者 25%或者延长 pause 观察期等足够多的样本进入 Prometheus。你可能注意到我用了setCanaryScalesetWeight组合这是 Argo Rollouts 在没有接入服务网格或者流量路由插件时用副本数模拟流量比例的方式。它会让控制器这边调整 canary ReplicaSet 的副本数让 canary Pod 数量大约占总数量的 10%。比如当前 stable 有 9 个 Podcanary 就起 1 个 Pod这样 Service 后端的 10 个端点里只有 1 个是新版本。如果你只写了setWeightArgo Rollouts 对副本数的调整策略可能跟预期不一致所以我会显式地加上setCanaryScale.weight。3.3 AnalysisTemplatePrometheus 查询怎么写Prometheus 查询语句是这个方案的核心我建议直接使用应用暴露的 HTTP 指标。假如你的应用通过 Prometheus client 暴露了http_requests_total这个 Counter指标上带有status标签代表 HTTP 状态码那么成功率分析模板可以这样写apiVersion: argoproj.io/v1alpha1 kind: AnalysisTemplate metadata: name: myapp-success-rate namespace: demo spec: metrics: - name: success-rate initialDelay: 30s interval: 15s count: 10 successCondition: result[0] 99.0 failureLimit: 2 provider: prometheus: address: http://prometheus-server.monitoring.svc:80 query: | sum(rate(http_requests_total{appmyapp,status~2[0-9][0-9]|3[0-9][0-9]}[3m])) / sum(rate(http_requests_total{appmyapp}[3m])) * 100如果你不是直接用应用暴露的指标而是通过 Nginx Ingress Controller 的指标来判断整体请求成功率查询语句换成下面这种也可以( sum(rate(nginx_ingress_controller_requests{namespacedemo,ingressmyapp-ingress,status~2[0-9][0-9]|3[0-9][0-9]}[3m])) / sum(rate(nginx_ingress_controller_requests{namespacedemo,ingressmyapp-ingress}[3m])) ) * 100注意[3m]这个时间窗口。这是 rate 计算时向后看的时间范围分析阶段每次查询都基于最近 3 分钟的数据计算成功率。窗口太短容易受瞬时抖动影响太长又会让异常被平均掉我自己用的比较多的是 3 到 5 分钟。3.4 实际操作验证从发布到自动验收配置都准备好之后先 apply 初始版本kubectl apply -f rollout.yaml -f service.yaml -f analysis-template.yaml初始版本部署完成之后Rollout 会处于稳定的 Healthy 状态。你可以手动改镜像触发一次发布kubectl -n demo set image rollout/myapp myappregistry.example.com/myapp:v1.0.1然后观察发布进度kubectl argo rollouts get rollout myapp -n demo如果你配置了 analysis 步骤中途可以看到类似这样的输出Step: 2/7 ✓ setCanaryScale weight: 10 ✓ setWeight: 10 ✓ pause ⏳ analysis: myapp-6d9f4f8c7a-1这时通过 kubectl 查看 AnalysisRun 的实时状态kubectl get analysisrun -n demo -l rollout-namemyapp kubectl describe analysisrun analysis-run-name -n demodescribe里会显示每条指标的最新评估结果比如成功计数、失败计数、最近一次查询的值。这一步对排查发布不推进的问题非常关键。如果分析通过Rollout 会继续直到 100% 流量切到新版本旧的 ReplicaSet 会缩放为 0。如果分析失败Rollout 会自动把流量全部切回旧版本你可以从get rollout里看到状态变为 Degraded。4. 指标设计才是这套方案的灵魂查询口径与误判防护YAML 写错了顶多资源起不来PromQL 写错了才是真正麻烦的因为它可能表面看起来正常实际把发布带进沟里。我见过最典型的例子查询返回空数据AnalysisRun 秒变 Error发布直接回滚但版本其实是健康的还有查询条件没过滤 canary 版本把旧版本拖后腿的数据算进新版本里导致新版本莫名其妙被杀掉。指标设计要当成核心工作来做。4.1 成功率查询的三个大坑第一个坑是“除数为零”。Prometheus 的除法在分母为零时不会返回一个合理的数值大多数情况下是 NaN 或者没有数据。AnalysisRun 拿不到结果就会把这次查询记成错误。我常用的规避方式是给分子分母的查询都加上一个极小值垫底或者在 AnalysisTemplate 里用successCondition处理 NaN。比如查询写成这样( sum(rate(http_requests_total{appmyapp,status~2[0-9][0-9]|3[0-9][0-9]}[3m])) / clamp_min(sum(rate(http_requests_total{appmyapp}[3m])), 0.001) ) * 100clamp_min把分母下限卡在 0.001这样即使请求量为零结果也不会是 NaN而是趋近于 0成功率不达标会触发失败而不是 Error。这对发布安全更友好。第二个坑是“标签没对上”。PromQL 是精确匹配标签的appmyapp在 Grafana 里可能跟appmyapp-v1或者appmyapp这种细微差别对不上查询结果就是空。写进 AnalysisTemplate 之前我强烈建议先在 Prometheus 的 Graph 页面手工执行一遍完整查询确认能出数据再往模板里放。第三个坑是“时间窗口对不齐”。Prometheus 默认每 15 秒抓取一次指标。如果你的interval设置的也是 15 秒查询窗口也是 15 秒那每次查询可能只能覆盖一个抓取点数据很不稳定。我会把查询窗口设置成至少 1 分钟以上够覆盖多次抓取并且interval不要小于窗口长度避免对同一段数据重复评估。4.2 多指标联合既要成功率高又要延迟不劣化成功率是发布质量的底线但不是全部。一个版本可能接口全通但响应时间从 50 毫秒涨到 2 秒用户同样会扔手机。我后来给核心服务加上了延迟指标用histogram_quantile算 P99 延迟apiVersion: argoproj.io/v1alpha1 kind: AnalysisTemplate metadata: name: myapp-quality-gate namespace: demo spec: metrics: - name: success-rate initialDelay: 30s interval: 15s count: 10 successCondition: result[0] 99.0 failureLimit: 2 provider: prometheus: address: http://prometheus-server.monitoring.svc:80 query: | sum(rate(http_requests_total{appmyapp,status~2[0-9][0-9]|3[0-9][0-9]}[3m])) / clamp_min(sum(rate(http_requests_total{appmyapp}[3m])), 0.001) * 100 - name: p99-latency initialDelay: 30s interval: 15s count: 10 successCondition: result[0] 500 failureLimit: 2 provider: prometheus: address: http://prometheus-server.monitoring.svc:80 query: | histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{appmyapp}[5m])) by (le) ) * 1000这里把延迟的阈值设为小于等于 500 毫秒单位换算成毫秒方便比较。当一个 AnalysisTemplate 里有多个 metric 时Argo Rollouts 会并行评估所有指标只有当所有指标都满足条件整个 AnalysisRun 才会通过。也就是说成功率过而 P99 延迟不过发布同样会被卡住。这种“多指标互相补充”的做法比单一指标可靠得多。成功率容易掩盖小部分慢请求P99 则能暴露尾延迟问题两者一起看发布质量判断才完整。4.3 怎么避免 AnalysisRun 因为“没数据”误报金丝雀发布刚起 Pod 的那几十秒是最脆弱的Prometheus 还没抓到新 Pod 的指标查询结果自然为空。如果 AnalysisRun 恰好在这时候启动就会把一个“数据还没就绪”的状态当成“查询错误”轻则 Inconclusive 卡住重则误判失败回滚。我对这个问题的处理有三个措施initialDelay至少给 30 秒以上给新 Pod 留够启动时间。如果应用启动本身较慢比如要做缓存预热建议给到 60 秒甚至更长。查询窗口用[3m]这种较长窗口这样 PromQL 需要至少 3 分钟的数据但 AnalysisRun 查询时只要最近有数据就可以从窗口内有值的数据算出来。这个逻辑不一定在所有情况下都成立但实际用下来比[1m]更稳。给 analysis 步骤旁边再加一个前置pause步骤。我通常会在 analysis 步骤前加上至少 1 分钟的 pause让新 Pod 跑一会儿真实流量Prometheus 里攒出数据再启动评估。另外一个很多人不知道的小细节Argo Rollouts 的 Prometheus provider 支持设置timeout字段。如果你发现 AnalysisRun 偶尔会因为查询超时变成 Error可以在 provider 里显式加一个超时兜底provider: prometheus: address: http://prometheus-server.monitoring.svc:80 timeout: 30s5. 真实踩坑记录5 个反复出现的卡点与完整排查过程实操过程中我踩过不少坎其中有些问题排查起来极其费劲。下面这几个是最常见的我按“现象 - 排查 - 解决”的顺序写希望能帮你少走弯路。5.1 AnalysisRun 一直 Running发布像卡死了一样现象发布走到 analysis 步骤状态里显示⏳ analysis然后很久不动。kubectl get analysisrun一直是 Running。排查过程我先看 AnalysisRun 的 describe发现成功计数和失败计数都在慢慢涨但是这个分析里count配置的是 30interval是 15 秒意味着要连续查询 30 次都成功至少需要 7 分半钟。这期间不是说有问题而是配置本身把分析周期拉得太长。再加上 Prometheus 那边查询窗口是 5 分钟每次查询计算量也不小感觉就像卡住了。解决把count从 30 改成 10interval保持 15 秒整个分析周期压缩到 2 分半左右发布节奏明显顺畅。这里我要提醒的是分析周期不等于观察周期你完全可以在 analysis 之前加一个长 pause 让流量先烧一会儿但在 analysis 里没必要设置那么多样本数量够统计稳定性就行。5.2 PromQL 查询结果为空AnalysisRun 报 Error现象AnalysisRun 状态变成 Error发布被回滚。查看 describe看到的错误信息类似evaluation status: failed to evaluate query: ...或者no data found。排查过程我先把 AnalysisTemplate 里的 query 复制到 Prometheus 页面里执行结果没有数据。开始以为是 namespace 名字写错后来发现是标签问题应用暴露的指标上根本没有status标签而是用的code标签。查询里写status~2[0-9][0-9]自然匹配不到任何数据。解决先在 Prometheus 页面里执行http_requests_total{appmyapp}看返回的标签列表确认状态码标签叫什么再改查询语句。这里我养成了一个习惯任何 PromQL 必须先在 Prometheus 页面里跑通确认有数据、有正确的标签再写进 AnalysisTemplate。5.3 改了 ConfigMap 发现发布不触发现象我只更新了 ConfigMap然后发现 Rollout 没有任何反应Pod 没有重建新配置没有生效。排查过程Argo Rollouts 的触发机制跟 Deployment 一样只看 Pod template 是否变化。ConfigMap 的变更不会引起 Pod template hash 变化自然不触发新的 ReplicaSet。我用kubectl argo rollouts get rollout看状态还是 Old才发现原因。解决如果你要让 ConfigMap 的变更触发发布有两个办法一是给 Rollout 加一个 annotation比如rollout.argoproj.io/restart: true手动重启二是做一个“配置版本”标签比如config-version: v2更新 ConfigMap 的同时更新 Pod template 的 label 或者环境变量强制触发。我后来采用的是第二种因为语义更清晰发布记录里也能看到是配置变更导致的发布。5.4 Service 把所有流量都打到了 canary 上现象发布刚开始我只设置了setWeight: 10但实际观察发现 canary Pod 被打到的流量远远超过 10%几乎接近全量。排查过程这个坑是副本数加权方案特有的。我的 Service selector 用的是app: myapp公共标签stable 和 canary Pod 都能匹配到。但如果 stable ReplicaSet 的副本数已经被缩成 0那整个 Service 后端就只剩 canary Pod流量自然全打到 canary。通过kubectl get replicasets -n demo -o wide看到 stable RS 的 desired 是 0才知道是我在 Rollout 的 steps 里没有控制好 scale 策略。解决确认 steps 里加入setCanaryScale.weight并且理解 Argo Rollouts 的缩放策略。setCanaryScale有三种取值方式weight按权重比例算副本数pods直接指定 canary 副本数disabled表示让 canary 跟 stable 副本数一致。如果你不写控制器可能按默认方式处理导致稳定副本数被缩掉。我的建议是金丝雀阶段用weight或者pods显式控制不要依赖默认值。5.5 analysis 步骤通过了但最终发布还是回滚现象第一次 analysis 跑完显示 Successful发布继续放量。放到 50% 之后第二次 analysis 还没跑完发布就回滚了。排查过程我查看 Rollout 状态和事件发现第二次 analysis 很快生成了但失败计数在很短时间内连续增长到failureLimit。于是怀疑是查询窗口覆盖了新旧版本切换时的瞬时波动。当时第一次 analysis 在 10% 流量阶段新版本只占一小部分流量整体成功率被旧版本主导自然很稳。第二次 analysis 放到 50% 后新版本的异常请求占比变大查询窗口又同时覆盖了新旧两个时段的数据导致结果突变。解决这是一个正常的业务质量波动不是配置问题。我后来把 50% 阶段的pause时间从 30 分钟调整成了 1 小时并把查询窗口从[3m]改成[5m]让评估结果更平滑。关键是理解一个事实analysis 通过不等于版本健康它只是说明当前观察窗口内质量达标。放量越大暴露问题的概率越大所以你需要在更大的流量比例上停留更久而不是机械地等到 analysis 通过就急着全量。6. 方案落地前我建议你先想清楚这几件事技术配置本身并不复杂真正难的是把指标驱动发布这套逻辑接入团队现有的发布流程里去。我总结三个容易忽略但非常重要的问题。6.1 你的指标采集能不能支撑“版本维度”的质量判断Argo Rollouts 的 AnalysisRun 只会执行你写好的 PromQL它不会魔法般地知道哪个指标属于哪个版本。如果你应用暴露的指标里没有版本相关的标签比如versionv1.0.1或者revision2那你就只能用 Service 级或者 Deployment 级的整体指标做判断。这在小流量阶段尤其要命新版本 10% 流量里只要有一个请求失败对整体成功率的影响很小可能根本达不到你设的失败阈值反过来如果旧版本本身就在缓慢劣化新版本可能被无辜牵连。我在线上环境里的做法是应用在暴露业务指标时除了固定的app标签还必须带上version或revision标签这个版本号从环境变量注入。这样 PromQL 里就能用versionv1.0.1精确过滤出金丝雀版本的数据。如果你的应用是 Java 这种改起来成本高的至少要在网关注入版本相关的 header然后在 Ingress 指标里通过自定义标签区分。这一步没做好后面全是盲人摸象。6.2 发布失败之后团队有没有明确的处置流程自动回滚是双刃剑。回滚太快可能把一次本来只是短暂抖动但最终能恢复的发布砍掉回滚太慢又失去了自动化的意义。Argo Rollouts 默认在 AnalysisRun 判定失败后立即回滚但你得想清楚回滚失败版本之后做什么是保留 canary ReplicaSet 用来排查问题还是直接缩掉新版本日志还要不要保留监控告警要不要在回滚后自动恢复我的建议是回滚后不要马上缩掉 canary ReplicaSet先保留一段时间让排查人员还能看到失败的现场。如果你的 Argo Rollouts 版本支持可以在滚动更新后调整 canary ReplicaSet 的副本数来保留现场。另外通知机制一定要有。Argo Rollouts 本身支持通过 Notifications 发消息到钉钉、Slack 之类的平台至少要让发布负责人第一时间知道自动回滚发生了否则一群人还在等发布结果实际上早就回滚了。6.3 小流量阶段的指标波动要不要单独给阈值我踩过一个反复出现的坑金丝雀 10% 流量阶段成功率是 99.8%到了 50% 阶段掉到 99.2%两次 analysis 用同一个阈值结果第二次被误杀。原因很简单流量越大样本越多真实的边缘情况越容易暴露。所以我会在大流量阶段适当放宽阈值或者延长观察窗口让版本在真实流量里跑一段时间再下结论。具体怎么放宽不同业务不一样。我自己的经验是第一次 analysis 卡质量下限用比较严格的阈值把明显的崩溃挡在外面第二次 analysis 关注趋势比如成功率不能比旧版本低超过 0.5 个百分点。你甚至可以在两次 analysis 里引用不同的 AnalysisTemplate或者通过 AnalysisTemplate 的 args 传不同阈值进去。7. 最后分享一个提高排查效率的小技巧发布自动化跑起来之后你不可能每次都靠kubectl盯着界面看尤其是发布失败需要快速定位的时候。我自己会用一个简单的命令组合把 AnalysisRun 的关键信息在几十秒内拉出来kubectl get analysisrun -n demo -l rollout-namemyapp -o json | jq .items[-1].status.metrics这个命令能直接看到最近一次分析实例里每条指标的评估状态、消息和最近一次查询的值。配合watch用效果更好watch -n 5 kubectl get analysisrun -n demo -l rollout-namemyapp -o json | jq .items[-1].status.metrics另外Argo Rollouts 的 kubectl 插件里有一个很实用的命令可以直接查看当前 Rollout 的发布历史和分析状态kubectl argo rollouts get rollout myapp -n demo线上发布的时候我习惯把这个命令的输出接到一个终端标签页里再留一个标签页给kubectl get events --sort-by.lastTimestamp看到异常第一时间判断是版本质量问题还是配置问题。这套组合让我在指标驱动发布上线后的半年里几乎没有再靠肉眼看 Grafana 判断过“要不要回滚”这个问题。
返回列表