Argo Rollout 实战:Kubernetes 蓝绿发布与金丝雀发布的自动化控制

发布时间:2026/9/9 3:47:20
Argo Rollout 实战:Kubernetes 蓝绿发布与金丝雀发布的自动化控制 如果你在一个稍微有点规模的 Kubernetes 集群上做过版本发布大概率经历过这种“明明没人动线上环境但流量就是莫名其妙地变差了”的瞬间。我自己的团队从最早的手工改镜像、滚动更新、到后来引入 Argo Rollout 去做蓝绿发布和金丝雀发布整个过程走了不少弯路今天这篇就专门聊聊用 Argo Rollout 把发布流程做成“全自动”这件事它究竟解决了什么问题、底层是怎么设计的、蓝绿和金丝雀怎么配置、以及我踩过的那些坑。文章尽量保留现场细节配置可以直接抄但更重要的是理解每段配置背后的判断逻辑。1. 发布翻车回忆录Deployment 滚动更新那股失控劲1.1 一次让我失眠的线上事故那年我们还在用原生 Deployment 做滚动更新发布流程听起来很标准CI 构建镜像、推送、然后kubectl set image触发滚动。某天下午我改了一个 API 的内部缓存逻辑自测和测试环境都过了灰度前也跑过压测。上线后五分钟监控告警突然爆炸——接口 P99 延迟从 80ms 飙到了 1200ms错误率直接到 8%。我先看了看滚动状态新 Pod 起来了一部分老的还在缩容流量还是通过同一个 Service 打到所有 Ready 的 Pod 上。问题就出在这里——新版本有一个隐藏 bug只有压力上来后才触发而 readiness probe 在低流量阶段根本测不出来。等到流量逐渐迁移到新 Pod故障也跟着迁移过去了。我手动把 Deployment 回滚到上一个版本但整个故障窗口已经持续了二十多分钟。事后复盘根因不只是代码 bug更深层的原因是发布动作本身缺少“流量控制”和“质量门禁”。Deployment 的滚动更新本质上只保证了“Pod 就绪数量不下跌”它不知道什么是业务健康更不可能在指标异常时自动停下。1.2 蓝绿和金丝雀两套被验证过的“刹车系统”蓝绿发布的核心思路是把旧版本和新版本当成两个完全隔离的“泳道”。旧版本蓝色泳道继续承接线上流量新版本绿色泳道先完整部署出来等确认没问题后再把流量入口从蓝色泳道一次性切到绿色泳道。这个方案的优点在于切换动作干净利落回滚也简单——把流量切回去就行缺点则是资源占用翻倍因为新旧两套环境是同时运行的。金丝雀发布则是渐进式的先让一小部分流量进入新版本比如 5%、10%观察一段时间。如果指标健康再逐步放量到 20%、50%直到 100%。它比蓝绿更符合“循环渐进”的发布哲学但难点在于“按比例放量”需要比 Service 更细粒度的流量路由能力否则就只能靠控制副本数去近似比例。坦白说这两种策略在传统架构里都能手搓但手搓的问题是Service / Ingress / LoadBalancer 的改动分散在多个脚本里没法统一管理“观察指标”这一步依赖人工盯监控人不可能 24 小时盯着不放回滚动作要人去执行一旦告警和操作延迟故障就扩大了。所以我们需要一个能把“发布动作”和“指标判断”串联起来的控制器这就是 Argo Rollout 出现的场景。1.3 标题里的“全自动”到底指什么很多人听到“全自动发布”第一反应是“人完全不用管了”这其实是个误区。真正的全自动指的是发布流程里的流量切换、指标验证、放量、回滚这些机械性步骤由控制器自动完成人的角色从“执行者”变成“审批者和例外处理者”。Argo Rollout 能自动完成的事包括新版本跑起来后自动暂停、在蓝绿模式下自动把 activeService 切到新版本、在金丝雀模式下按预设权重逐步放量、在分析指标失败时自动回滚。你可以选择完全无人值守也可以在某些步骤插入人工确认窗口。它是一种“可控的全自动”不是“放养式自动”。2. Argo Rollout 的内核逻辑复用 ReplicaSet但把方向盘换成你的2.1 Rollout CRD长得像 Deployment骨子里不一样Argo Rollout 引入了一个新的 CRD叫做Rollout。第一次看到它的 YAML 时你会发现它和 Deployment 几乎长一样有replicas、有selector、有template。这其实是故意设计的为的是降低迁移成本。但在strategy字段上它不再是Recreate或RollingUpdate而是blueGreen或canary。它底层的 Pod 编排仍然走 ReplicaSet但 Argo Rollout 的 Controller 不会像 Deployment Controller 那样“死板地”按滚动策略去扩缩容。Controller 会精确维护两份 ReplicaSet一份是当前稳定版本一份是新版本。发布进度、暂停、自动回滚都发生在 Controller 内部的状态机里。简单类比一下Deployment 像一个只管把车从 A 开到 B 的司机不管你路况如何Argo Rollout 像一个带导航和定速巡航的司机知道什么时候踩油门、什么时候踩刹车、什么时候倒车。两者都在开车但驾驶策略完全不同。2.2 Controller 与 kubectl 插件发布状态怎么看要落地这套全自动发布你需要在 Kubernetes 集群里装两部分组件Argo Rollouts Controller负责监听 Rollout 资源、执行发布策略、调用 Kubernetes API 做扩缩容、调用 Service / Ingress / Service Mesh 做流量切换。kubectl 插件kubectl argo rollouts方便你查看发布状态、手动 promote / abort / retry。Controller 的官方安装方式很简单kubectl create namespace argo-rollouts kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml验证安装kubectl get pods -n argo-rollouts kubectl rollout status deployment/argo-rollouts -n argo-rolloutskubectl 插件如果环境里有 krew可以直接kubectl krew install rollouts如果没有 krew也可以去 GitHub Releases 页面下载二进制然后放进 PATHcurl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64 chmod x kubectl-argo-rollouts-linux-amd64 sudo mv kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts安装完之后你可以启动一个本地 Dashboardkubectl argo rollouts dashboard它会起一个本地 Web 服务默认在 3100 端口浏览器打开就能看到所有 Rollout 的状态、步骤进度、指标分析结果。我强烈建议真正跑发布的时候开着这个 Dashboard它比kubectl get pod直观太多。2.3 发布状态的判断链路从 AnalysisTemplate 到 AnalysisRunArgo Rollout 能实现“自动回滚”靠的不是写死在代码里的 if-else而是AnalysisTemplate这个 CRD。你可以把AnalysisTemplate理解为“发布过程中的监控规则模板”。通常它包含一个或多个指标比如从 Prometheus 查询的成功率、错误率、P99 延迟从日志系统查询的错误数量自定义 HTTP 请求的健康检查结果。当一次发布走到某一步时Argo Rollout Controller 会根据AnalysisTemplate生成一个AnalysisRun对象这个对象会周期性执行模板里定义的查询并把结果和successCondition做比较。如果满足条件发布继续如果失败超过阈值Controller 自动中止发布并回滚。这就是整个“智能发布”的核心发布流程不再根据时间硬切而是根据真实的业务指标决策。3. 蓝绿发布完整落地activeService 与 previewService 的编排3.1 创建蓝绿策略需要哪些前置资源做蓝绿发布之前先要理解 Argo Rollout 蓝绿策略里两个 Service 的分工activeService始终指向当前稳定版本。这个 Service 通常被 Ingress、Gateway 或集群内部服务引用是正式流量入口。previewService只在发布期间指向新版本。它不承载正式流量但方便你在正式切换前做验证比如通过临时端口访问新版本。在配置 Rollout 之前先创建这两个 Service。下面是一个比较标准的例子apiVersion: v1 kind: Service metadata: name: demo-app-active spec: selector: app: demo-app ports: - port: 80 targetPort: 8080apiVersion: v1 kind: Service metadata: name: demo-app-preview spec: selector: app: demo-app ports: - port: 80 targetPort: 8080注意平时不需要给这两个 Service 加上版本相关的 selector labelArgo Rollouts Controller 会在发布过程中自动把新版本的 Pod hash label 注入到对应的 Service selector 里。你只需要保证 Service 的 selector 至少包含 Rollout selector 中的标签比如这里的app: demo-app。3.2 Rollout 配置示例一次从 1.0 到 2.0 的蓝绿发布下面是蓝绿策略的核心配置。我将它拆成几个片段解释但实际操作时这是一个完整 YAML。apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata: name: demo-app spec: replicas: 4 revisionHistoryLimit: 2 selector: matchLabels: app: demo-app template: metadata: labels: app: demo-app spec: containers: - name: demo-app image: registry.example.com/demo-app:1.0.0 ports: - containerPort: 8080 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 3 periodSeconds: 5 strategy: blueGreen: activeService: demo-app-active previewService: demo-app-preview autoPromotionEnabled: true autoPromotionSeconds: 120 scaleDownDelaySeconds: 300关键配置含义如下autoPromotionEnabled: true表示新版本就绪后自动切换流量不需要人工输入kubectl argo rollouts promote。autoPromotionSeconds: 120表示新版本完全 Ready 后等 120 秒再切换 activeService。这个等待期的意义是留出时间给缓存预热、JIT 编译、连接池建立等很多故障其实都发生在“刚就绪但还没准备好接流量”的瞬间。scaleDownDelaySeconds: 300表示流量切换完成后旧版本 ReplicaSet 再过 300 秒才缩容。这个时间窗口用于连接排空也给监控留下观察时间。先应用这个 Rollout它会创建稳定的 1.0.0 版本。此时有两个 Serviceactive 和 preview 都指向 1.0.0因为还没有新版本出现。3.3 新版本发布、自动提升、回滚全流程现在模拟一次发布把镜像更新到 2.0.0kubectl argo rollouts set image demo-app demo-appregistry.example.com/demo-app:2.0.0查看发布状态kubectl argo rollouts get rollout demo-app --watch你会看到类似这样的状态Name: demo-app Status: ✔ Paused Strategy: BlueGreen Images: stable: registry.example.com/demo-app:1.0.0 preview: registry.example.com/demo-app:2.0.0 (pending promotion) Replicas: Desired: 4 Current: 6 Preview: 4 Active: 4这里的 Current 是 6是因为新旧两套 ReplicaSet 同时存在多余的副本会等 promotion 完成后再按scaleDownDelaySeconds清理。120 秒后Controller 会自动把demo-app-active这个 Service 的 selector 切换成新版本同时让 preview Service 指向旧版本。此时demo-app-active开始把正式流量转发到 2.0.0 的 Pod。如果你在发布过程中发现异常想立刻终止蓝绿发布最直接的方式是推进回旧版本kubectl argo rollouts undo demo-app这个命令会让 Controller 把 activeService 切回 1.0.0并把 2.0.0 缩容。整个回滚动作发生在秒级因为旧版本 Pod 本来就在 running。3.4 在蓝绿发布里加上“前置质量门禁”只有时间等待还不够最好在切流量之前做一轮自动验证。Argo Rollout 提供了prePromotionAnalysis和postPromotionAnalysis字段分别代表“切换前分析”和“切换后分析”。strategy: blueGreen: activeService: demo-app-active previewService: demo-app-preview prePromotionAnalysis: templates: - templateName: healthy-check postPromotionAnalysis: templates: - templateName: success-rate如果prePromotionAnalysis失败发布会被中止流量不会切过去如果postPromotionAnalysis失败Controller 会自动回滚到上一个稳定版本。这样蓝绿发布就从“按时间切”进化成了“按业务指标切”。4. 金丝雀发布完整落地流量权重、AnalysisTemplate 和自动放量4.1 金丝雀的关键控制的是流量而不只是副本数蓝绿发布的特点是切换彻底而金丝雀发布的精髓在于“逐步放量”。原生 Kubernetes 的 Service 本身只是负载均衡器它把所有 Ready 的 Pod 都当作对等后端无法精确按百分比分流。所以金丝雀发布要解决的核心问题是如何把一个版本的一部分流量引入新版本并且这个比例可精确控制。Argo Rollout 提供了两条路线副本数近似路线在不使用 Service Mesh / Ingress 流量路由的情况下Controller 通过控制新旧 ReplicaSet 的副本数量来近似流量比例。比如你有 10 个副本想要 10% 的金丝雀流量那就让新版只保留 1 个 Pod旧版保留 9 个。流量路由精确路线通过 Nginx Ingress、Istio 等组件做按权重的流量路由。Controller 动态更新路由规则让 10% 的请求真正进入新版本不依赖副本数。生产环境我更推荐这种路线尤其当服务副本数较少时。4.2 直接可落地的金丝雀配置范例这里我写一个基于 Nginx Ingress 流量路由的配置。前提条件是你已经安装了兼容版本的 Nginx Ingress Controller并且入口流量是通过 Ingress 进来的。先创建两个 Servicedemo-canary-stable和demo-canary-canary。它们同样只需要包含基础标签Controller 会在发布过程中自动注入对应的版本 hash 到 selector 里。apiVersion: v1 kind: Service metadata: name: demo-canary-stable spec: selector: app: demo-canary ports: - port: 80 targetPort: 8080 --- apiVersion: v1 kind: Service metadata: name: demo-canary-canary spec: selector: app: demo-canary ports: - port: 80 targetPort: 8080然后创建 RolloutapiVersion: argoproj.io/v1alpha1 kind: Rollout metadata: name: demo-canary spec: replicas: 10 selector: matchLabels: app: demo-canary template: metadata: labels: app: demo-canary spec: containers: - name: demo-canary image: registry.example.com/demo-canary:1.0.0 ports: - containerPort: 8080 strategy: canary: stableService: demo-canary-stable canaryService: demo-canary-canary trafficRouting: nginx: stableIngress: demo-canary-ingress steps: - setWeight: 10 - pause: {duration: 10m} - analysis: templates: - templateName: success-rate - setWeight: 40 - pause: {duration: 10m} - analysis: templates: - templateName: success-rate - setWeight: 80 - pause: {duration: 5m} - analysis: templates: - templateName: success-rate - setWeight: 100这套流程的含义是先把 10% 流量切到新版本暂停 10 分钟观察执行success-rate分析通过后继续把权重提升到 40%再暂停 10 分钟分析通过后提升到 80%再暂停 5 分钟分析通过后切到 100%发布完成。注意setWeight: 100是最后一个权重步骤它会让 Controller 把 stable Service 的 selector 完全切到新版本。4.3 用 Prometheus 指标做发布判断刚才配置里引用的success-rate分析模板需要单独创建。下面是我常用的一个模板作用是从 Prometheus 查询 HTTP 成功率要求成功率不低于 99%。apiVersion: argoproj.io/v1alpha1 kind: AnalysisTemplate metadata: name: success-rate spec: metrics: - name: success-rate initialDelay: 30s interval: 1m count: 5 failureLimit: 3 successCondition: result[0] 0.99 provider: prometheus: address: http://prometheus.monitoring.svc.cluster.local:9090 query: | 1 - ( sum(rate(http_requests_total{appdemo-canary, status~5..}[5m])) / sum(rate(http_requests_total{appdemo-canary}[5m])) )这里initialDelay是发布比例调整后等待多少秒才开始查询interval是每次查询间隔count是总共查询几次failureLimit是允许失败几次才判定为不健康successCondition里的result[0]表示最近一次查询结果的第一个数值。整个自动化的闭环就是setWeight调整入口流量 - 等待分析 - 分析通过则继续下一个 weight - 分析失败则自动中止和回滚。4.4 手动控制仍然必要即便流程看起来全自动我仍然建议保留“人工介入”的通路。Argo Rollout 支持两类人工操作# 推进到下一步 kubectl argo rollouts promote demo-canary # 放弃这次发布回到上一个稳定版本 kubectl argo rollouts abort demo-canary发布策略里也可以显式加入pause: {}不带 duration这样 Controller 会一直暂停等待人工执行 promote。对于一些高风险版本我倾向于把第一个analysis之前的步骤设成“人工确认”后面再交给自动化这是一种折中但非常稳妥的做法。5. 把发布接入流水线真正打通自动构建、自动发布、自动回滚5.1 CI 侧怎么配合构建镜像与更新 RolloutArgo Rollout 本身不负责构建镜像它只关心“镜像版本发生变化”。所以最自然的集成方式是 CI 流程在推送新镜像后直接更新 Rollout 的镜像字段。如果你用的是最直接的 kubectlkubectl -n production set image rollout/demo-app demo-appregistry.example.com/demo-app:${GIT_SHA}如果你走 GitOps 流程在配置仓库里用 Kustomize 管理cd config/overlays/production kustomize edit set image registry.example.com/demo-app:${GIT_SHA} git add . git commit -m release: demo-app ${GIT_SHA} git push origin main配置仓库一旦变更Argo CD / Flux 这类 GitOps 工具会同步配置到集群Argo Rollout 感知到镜像变化后自动开启新一轮发布。这个链路的好处是所有发布动作都有 Git 可追溯记录回滚也变成一个 commit 的问题。5.2 和 Argo CD 联动时的健康检查注意点Argo CD 和 Argo Rollout 是两个独立项目但配合得非常好。Argo CD 默认不认识Rollout这种资源的健康状态需要在argocd-cmConfigMap 里注册自定义健康检查让 Argo CD 能把Degraded、Aborted状态映射成对应的同步状态。下面是一个简化写法apiVersion: v1 kind: ConfigMap metadata: name: argocd-cm namespace: argocd data: resource.customizations.health.argoproj.io_Rollout: | hs {} hs.status Healthy if obj.status ~ nil then if obj.status.phase ~ nil then hs.status obj.status.phase if obj.status.phase Degraded then hs.message obj.status.message end end end return hs配置完以后当金丝雀发布过程中 AnalysisRun 失败导致 Rollout 进入 Aborted 状态Argo CD 的 UI 上会直接标红而不需要你专门去看 Argo Rollout 的 Dashboard。5.3 团队协作时的权限与审批“全自动”不代表“所有人都可以一键发布”。我建议在 Kubernetes RBAC 层面做分工开发人员只能触发set image或提交 Git不能直接删 Rollout、不能改发布策略。发布负责人可以执行promote、abort、undo这些操作。SRE / 平台组才能修改策略里的 weights、pause 时长、AnalysisTemplate。在金丝雀策略里需要人工确认时可以把某个步骤设成无时限 pause由发布负责人在 Dashboard 或命令行里确认。这样既保留了自动化效率又不会让线上发布沦为“无人负责”。6. 真实生产环境的坑这些问题让我吃过亏6.1 副本数少时金丝雀权重严重失真这是最容易掉进去的坑。原生 Service 模式下金丝雀权重靠副本数近似如果服务副本数不够多权重就是“纸面数字”。举个例子假设总副本数是 3你想放 10% 金丝雀流量Controller 遇到小数副本时通常只能向上取整结果就是 1 个新版本 Pod、2 个旧版本 Pod但实际流量占比却是 33%而不是 10%。总副本数设置金丝雀权重实际新版本 Pod 数实际流量占比1010%110%510%120%310%133%220%150%所以如果你的服务只有两三个副本请务必使用 Nginx Ingress 或 Istio 这类流量路由方案或者直接上蓝绿发布不要在原生 Service 模式下硬做金丝雀。6.2 分析模板查不到数据会让发布卡死Argo Rollout 在分析指标时有个隐蔽的细节如果 Prometheus 查询结果为空AnalysisRun 会进入 Inconclusive 状态而不是 Failed 状态。Inconclusive 不会触发自动回滚但也不会继续放量发布就会静默卡住。我在一次发布中就是被这个问题坑了整整半小时新版本流量只有 5%查询时间段内没有错误请求Prometheus 返回了空数组结果result[0]取不到值步骤停在那里Dashboard 看起来是“在等待分析”但实际什么都查不到。解决方式有两个方向在 AnalysisTemplate 的 metric 里给需要数据依赖的指标加上required: true这样查询不到数据时直接判定失败宁可按失败处理也不要卡死。在查询语句里用avg_over_time或者默认填充技巧确保返回一个有效数值。另外successCondition的表达式最好先在 Prometheus UI 里手工执行验证确认返回数值的类型和大小都在预期范围内。别等到发布时才发现表达式语法不对。6.3 蓝绿切换后别急着缩容旧版本scaleDownDelaySeconds这个参数在很多团队里会被设成 0理由是省资源。但我建议至少给到 300 秒原因有两个第一Service 的 endpoint 更新不是瞬时的。kube-proxy 和 Ingress Controller 感知到 Service selector 变化、替换后端 Pod 列表需要一段时间。如果旧的 Pod 立即被删连接排空来不及正在处理的请求会被掐断。第二即使流量已经切到新版本你也需要一点时间观察监控曲线确认新版本没有“隐藏的地雷”。如果真的出问题旧版本 Pod 还在运行回滚成本极低如果已经被缩容就要重新拉镜像、重新等就绪故障恢复时间会明显拉长。6.4 从 Deployment 平滑迁移到 Rollout 的正确姿势很多团队第一次用 Argo Rollout 时直接把 Deployment 的kind改成Rollout然后 apply。这个做法在“不发布新版本”的情况下不会出问题因为 Rollout 会进入 Healthy 状态并接管现有 Pod。但在触发第一次发布时要注意旧 Deployment 产生的 ReplicaSet 仍然存在如果处理不当可能出现新旧两个 ReplicaSet 同时被 Service 选中、流量被反复漂移的情况。我建议迁移时按这个顺序操作先创建好 Rollout 对应的 Service 资源确认 selector 和当前 Deployment 一致。将 Deployment 的 replicas 缩到 0避免它继续干预apply 新的 Rollout 资源确认 Pod 就绪删除旧 Deployment。如果是生产环境不敢直接删可以先把流量切到一个临时 Service验证 Rollout 没问题后再删旧的。6.5 缓存预热与数据库兼容蓝绿和金丝雀都绕不开业务层最后说一个工具解决不了的问题就算 Argo Rollout 把流量控制得再精细如果新版本和旧版本之间有数据库 schema 不兼容、缓存的 key 格式不一致切换的瞬间仍然可能出事故。所以在设计发布策略时要把业务上的“前向兼容”也纳入自动化流程数据库迁移应该在发布前单独执行并且保证和旧版本代码兼容缓存的序列化格式要保证新旧版本互相可读异步消息的 topic 或事件格式变化要考虑多版本共存期间的情况。Argo Rollout 能帮你避免“流量切换失误”但代替不了兼容性测试。我见过太多团队把全部精力花在配置 Argo Rollout 上结果第一次全自动发布就被一个数据库字段问题打倒。我自己的习惯是每次做重大版本发布前把prePromotionAnalysis或金丝雀分析步骤里加一个自定义检查模板专门跑一条“冒烟用例”模拟真实用户调用核心链路。比如调用下单接口、查询订单列表、读写 Redis。只有这条链路通过才允许继续推进发布。这样才算真正把“发布”和“业务健康”绑在一起。