ARTICLE DETAIL

资讯详情

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

Flagger实战:基于Kubernetes和Istio的自动化金丝雀发布

Flagger实战:基于Kubernetes和Istio的自动化金丝雀发布 金丝雀发布Canary Release这个玩法在云原生圈子里已经不算新鲜事了。但提起 Flagger很多人第一反应是这不是 Weaveworks 开源的渐进式交付工具嘛跟 Argo Rollouts 比到底强在哪为什么大家都在推自动化金丝雀而不是继续用 Jenkins 脚本手工切流量这篇博文我打算从一个实践者的角度把 Flagger 这套渐进式交付体系掰开揉碎讲清楚。不只是贴 YAML而是要说清楚每一步为什么这么做、流量权重怎么调、指标怎么判断、失败了怎么自动回滚。内容适合正在做 Kubernetes 应用发布平台、或者被发版全靠盯、回滚全靠手折磨的运维和研发同学。看完之后你应该能独立把 Flagger 接入现有的 Istio 或 Nginx Ingress 环境并且能把灰度发布做成全自动的。1. 渐进式交付到底是什么渐进式交付Progressive Delivery这个词听着高大上其实本质就是把发布从一个时间点拉长成一个可观测、可控制、可暂停、可回退的过程。传统发布是切流量这种一步到位操作渐进式交付则是把发布拆成多轮小步验证每一轮都只影响一小部分用户并且用实时指标来判断要不要继续往下走。1.1 传统发布方式的痛点我先拿一个最常见的场景举例。假设你有个订单服务跑在 Kubernetes 里Deployment 有 10 个副本上线新版本时如果用kubectl set image直接滚动更新K8s 默认的行为是先起一个新的、等 ready 再杀掉一个旧的整个过程确实无感知。但问题在于滚动更新的健康检查只看容器探针也就是进程活着、端口能通就算过了。它根本不会去管你的订单失败率是不是飙升、P99 延迟是不是从 200ms 涨到了 2s、数据库连接池是不是被打爆了。我见过不止一次新版本代码里有明显 bug比如某个 Redis key 拼错了导致缓存穿透服务本身启动很健康但业务指标全线飘红。滚动更新把整个集群的流量在几分钟内全部切到新版本等监控告警响了再手动回滚这中间已经有大量真实用户受害了。蓝绿发布虽然比滚动更新好一点但同样只解决了快速切换的问题没有解决如何判断新版本真的没问题的问题。1.2 Flagger 眼里的渐进式交付Flagger 要解决的就是这个问题。它会协调 Kubernetes 里的 Deployment、Service、Ingress 或者 Service Mesh 的流量路由规则自动地把流量按比例切给新版本比如先用 5%、再 10%、再 25%、再 50%每一轮间隔一段时间梯度上升。更重要的是每一轮流量调整之前Flagger 都会去查询 Prometheus 或者其他指标源检查新版本的错误率、延迟、请求量等指标是否在健康阈值内。一旦指标异常立即停止发布并把全部流量切回旧版本整个过程不需要人干预。你可以把它理解成发布版的自动驾驶。你只需要告诉它目标版本是谁、要分析哪些指标、每轮加多少流量、总共分几轮剩下的它自己跑。这套机制在 GitOps 和持续交付体系里尤其好用因为发布决策从人盯变成了机器看数据说话。2. 先想清楚为什么选 Flagger 而不是自己写脚本我见过很多团队一开始都是自己写 Shell 脚本或者 Jenkins Pipeline 来做灰度调 Istio VirtualService 的权重sleep 五分钟curl 一下新版本的 Pod 日志看看有没有 ERROR然后继续调权重。说实话这种方案在小规模场景下确实能跑但一旦服务数量变多、发布频率变高就会暴露出一堆问题。2.1 自研脚本的常见陷阱指标判断标准不统一。有人看日志关键字有人看 Prometheus 告警有人靠肉眼盯 Grafana 面板本质都谈不上自动化。没有真正的安全回退机制。脚本一旦跑挂了经常是权重停在某个中间值新旧版本各接一半流量线上处于一个非常尴尬的半发布状态手动恢复反而更慌。无法处理多集群、多服务依赖。你微服务一多A 服务发新版依赖 B 服务的新接口光靠一个脚本管不过来。审计和复盘困难。发布完没有一份清晰的记录说明几点几分流量调到多少、指标如何、为什么暂停事后出了问题很难追溯。2.2 Flagger 和同类工具的对比优势跟 Argo Rollouts 这类工具比Flagger 最大的不同是它不接管你的 Deployment 工作负载本身而是通过监听和协调的方式工作。Argo Rollouts 要你把自己的 Deployment 换成它自定义的 Rollout 资源而 Flagger 只是在你原有的 Deployment 旁边加一个 Canary CRD用它来定义发布策略。Flagger 会帮你管理流量路由并且支持 Istio、Linkerd、NGINX、Traefik、AWS App Mesh、SMI 等多种流量层。如果你已经深度用了 IstioFlagger 基本是最顺滑的选择因为它天然理解 VirtualService、DestinationRule 这些资源能帮你自动生成和调整流量权重。另外Flagger 还内置了指标分析和自动回滚类似成功率低于 99% 就回滚这种规则直接在 CRD 里声明不需要额外写脚本。它还支持 Webhook 扩展可以挂人工审批、集成测试等流程。这一点在生产环境里特别实用后面我会专门讲。3. 环境准备与 Flagger 部署说了这么多理念下面开始实际操作。我的实践环境假设是Kubernetes 1.24已经安装了 Istio 作为服务网格并且有 Prometheus 在收集指标。Flagger 本身是无侵入的但如果你要自动调整 Istio 的流量路由它需要能够读写 Istio 的 CRD所以部署时要保证 RBAC 权限到位。3.1 安装 Flagger安装 Flagger 可以用 Helm也可以直接用 kubectl apply 它的 release manifest。我用的是 Helm因为升级和卸载都干净。helm repo add flagger https://flagger.app helm repo update kubectl create ns istio-system helm upgrade -i flagger flagger/flagger \ --namespaceistio-system \ --set crd.createtrue \ --set meshProvideristio \ --set metricsServerhttp://prometheus.istio-system:9090这里注意几个参数meshProvideristio告诉 Flagger 它要协调的是 Istio 的 VirtualService 和 DestinationRule。metricsServer指向 Prometheus 的地址。Flagger 会用它来查询应用的成功率、延迟等指标。如果你用自建的 Prometheus地址要按实际环境来。安装完成后检查 Pod 状态kubectl -n istio-system get pods | grep flagger正常会看到一个flagger的 Pod 处于 Running 状态。3.2 确认 RBAC 权限Flagger 之所以能创建、修改 VirtualService 和 DestinationRule是因为它的 ServiceAccount 绑定了相应的 ClusterRole。你可以用下面的命令确认kubectl describe clusterrole flagger如果发现自己部署时没带权限后面 Flagger 在分析阶段就会报类似VirtualService.networking.istio.io is forbidden的错误所以这里是第一个容易踩坑的地方。3.3 准备一个测试应用为了演示自动化金丝雀发布我先创建一个非常简单的 Deployment就叫podinfo它是 Flagger 官方推荐用来做演示的测试应用支持返回版本号和健康状态。apiVersion: apps/v1 kind: Deployment metadata: name: podinfo namespace: test spec: selector: matchLabels: app: podinfo template: metadata: labels: app: podinfo spec: containers: - name: podinfo image: stefanprodan/podinfo:6.0.0 ports: - containerPort: 9898 command: - ./podinfo - --port9898 livenessProbe: httpGet: path: /healthz port: 9898 readinessProbe: httpGet: path: /readyz port: 9898Deployment 创建好之后再创建一个 Service。注意这个 Service 是 Flagger 用来做流量切换的app 标签必须跟 Deployment 对应上。apiVersion: v1 kind: Service metadata: name: podinfo namespace: test spec: type: ClusterIP selector: app: podinfo ports: - name: http port: 9898 protocol: TCP targetPort: 9898基础工作准备好后就可以开始配置本次的核心对象Canary CRD。4. 核心配置Canary CRD 逐参数详解Canary CRD 是 Flagger 的灵魂文件。整个渐进式交付的策略都在这一个 YAML 里定义。下面是我常用的一份完整配置后面会逐段拆解。apiVersion: flagger.app/v1beta1 kind: Canary metadata: name: podinfo namespace: test spec: provider: istio targetRef: apiVersion: apps/v1 kind: Deployment name: podinfo service: port: 9898 name: podinfo portDiscovery: true analysis: interval: 1m iterations: 10 threshold: 5 maxWeight: 50 stepWeight: 5 metrics: - name: request-success-rate thresholdRange: min: 99 interval: 1m - name: request-duration thresholdRange: max: 500 interval: 1m webhooks: - name: load-test url: http://flagger-loadtester.test/ metadata: cmd: hey -z 2m -c 10 -q 100 http://podinfo.test:9898/4.1 targetRef 和 servicetargetRef指向我们要发布的工作负载Flagger 会监听它的镜像版本变化。一旦你更新了 Deployment 的镜像 TagFlagger 就开始执行新一轮分析。service定义的是 Flagger 创建的主 Service它会作为所有流量的入口。注意portDiscovery: true这个参数会让 Flagger 自动从 Deployment 暴露的端口里找端口并生成对应 DestinationRule。这个功能挺方便的不用手动列一堆端口了但如果你的应用端口很多但不想全部暴露可以关掉它手动指定。4.2 analysis 参数是发布策略的核心interval表示每轮分析的时间间隔。iterations表示总共要做多少轮。这里有一个关键概念Flagger 的流量权重递增并不是简单的一次加 10% 权重然后等时间而是根据stepWeight和迭代轮次配合来推进的。我这里的配置是maxWeight: 50代表金丝雀流量最多只会放到 50%不会全量 100% 切换。这样设计是有讲究的——在生产环境我们一般不希望第一次使用某个自动化发布工具时就把所有流量都交给它。保守一点可以让金丝雀最大只占一半流量观察一段时间后再人工或者通过后续配置把流量切完。等团队对 Flagger 有信心了再把 maxWeight 调到 100。stepWeight: 5是每一轮迭代增加的权重所以我这个配置下流量变化过程大致是5% → 10% → 15% → ... → 50%总共 10 轮正好和iterations: 10匹配。这里要特别提醒如果iterations * stepWeight达不到maxWeightFlagger 实际只会跑到能跑到的最大权重不会强行超出。比如 maxWeight50、stepWeight10、iterations10那最终也就是到 50%后面的迭代其实是白白等待没有任何实际效果。反过来如果iterations * stepWeight远大于 maxWeight到 maxWeight 之后就停在那个状态直到分析完成。threshold这个参数是个大坑很多人会误会。它表示的是指标连续多少次不满足条件后才触发回滚而不是失败率阈值。比如threshold: 5意思是指标连续 5 次检查都失败Flagger 才判定发布失败并回滚。这个值的意义在于避免因为偶发抖动误杀发布但不要设太大否则真的出了问题系统会带着坏版本继续跑很久。4.3 自定义指标检查metrics里的request-success-rate和request-duration是 Flagger 内置的指标名称分别对应成功率基于 Istio 的响应码和 P99 延迟单位毫秒。request-success-rate配置了thresholdRange: min: 99意思是金丝雀版本的成功率必须维持在 99% 以上只要低于 99% 就算失败。request-duration配置了thresholdRange: max: 500意思是 P99 延迟必须小于 500ms。值得留意的是Flagger 还支持完全自定义 PromQL 查询。如果你有自己业务层面的指标比如订单失败数、支付超时率完全可以通过query字段来定义。比如metrics: - name: order-failure-rate query: | sum(rate(order_failures_total{namespacetest}[1m])) / sum(rate(order_requests_total{namespacetest}[1m])) thresholdRange: max: 0.01这样发布决策就跟业务指标挂钩了比只看 HTTP 状态码进阶得多。不过要注意查询语句的写法和性能Flagger 每个分析周期都会去 Prometheus 拉取数据查询太重会影响分析时长。4.4 不可或缺的流量打底金丝雀发布有个副作用新版本只拿到 5% 的流量时可能根本没有什么请求打进来指标全是空的这就没法判断好坏。所以上面配置里我加了一个webhooks调用一个叫flagger-loadtester的工具持续向服务发请求制造足够的流量。flagger-loadtester是 Flagger 官方提供的一个负载测试工具可以执行hey、ab、curl等命令。它在我的实践中是作为独立 Deployment 跑在集群内的用来主动产生金丝雀流量。这个步骤非常关键否则你会发现发布新版本后Flagger 一直卡在waiting for metrics状态因为没有流量Prometheus 查不到数据。LoadTester 的部署也很简单kubectl apply -f https://raw.githubusercontent.com/fluxcd/flagger/main/artifacts/loadtester/deployment.yaml kubectl -n test rollout status deployment/flagger-loadtester到这里一个基础的自动金丝雀发布环境就搭好了。接下来看它实际跑起来是什么样子。5. 自动化金丝雀分析怎么跑起来配置好之后发布流程不需要任何人工介入。下面我模拟一次完整的发布过程并解释每一步的背后逻辑。5.1 触发一次新版本发布修改 Deployment 的镜像版本kubectl -n test set image deployment/podinfo \ podinfostefanprodan/podinfo:6.1.0Flagger 会 watch 这个 Deployment 的变化。它发现镜像 Tag 从 6.0.0 变成 6.1.0 后并不会直接修改线上 Deployment而是在内部创建一个新的podinfo-primaryDeployment用来承载当前稳定版本创建一个podinfo-canaryDeployment用来跑新版本。主 Service 的 selector 会指向podinfo-primary金丝雀 Service 的 selector 指向podinfo-canary。这一步很多第一次用 Flagger 的人看日志时会懵明明我只定义了一个podinfoDeploymentFlagger 怎么自己又搞出两个 Deployment 来这正是它的工作模式——Flagger 接管你原始的 Deployment将其标记为管理对象然后自行维护 primary 和 canary 两套工作负载。你后续再要发布新版本只需要更新最初那个 Deployment 的镜像Flagger 会负责把新版本同步到 canary Deployment 上。5.2 流量权重的轮次推进接着 Flagger 会修改 Istio 的 VirtualService把权重设置为 5% 给金丝雀。这时候 Grafana 上可以很清楚地看到只有少量请求打到新版本 Pod大多数还是在 primary 上。然后interval: 1m生效。每隔一分钟Flagger 检查一次指标如果都正常就继续把权重上调到 10%、15%、20%……一直递增到 maxWeight。整个过程的日志大概长这样1m: podinfo.test starting canary analysis 1m: podinfo.test pod ready 2m: podinfo.test waiting for rollout to finish: 0 of 1 updated replicas available 3m: podinfo.test promotion completed 4m: podinfo.test advance podinfo canary weight 5 5m: podinfo.test advance podinfo canary weight 10 6m: podinfo.test advance podinfo canary weight 15 ...我这里的权重步长是 5%所以从 5% 到 50% 总共 10 轮大约 10 分钟左右完成。如果你的服务流量本身波动很大可以考虑把步长调大、间隔调长。比如 stepWeight10、interval2m5 轮 10 分钟也能到 50%每轮观察窗口更宽判定更稳。5.3 指标怎么被判断每轮迭代中Flagger 会向 Prometheus 发起查询检查成功率和 P99 延迟。如果你配置了自定义业务指标它同样会拉取并和阈值比较。这里有一个容易误解的地方Flagger 判断的是当前集群中正在运行的 Canary 版本的指标不是整个服务的指标。它通过 Istio 的流量标签来区分请求是发往 primary 还是 canary。所以你在 Prometheus 里执行查询时会看到destination_workloadpodinfo-canary这样的标签这才是 Flagger 真正关心的数据。如果某轮检查失败Flagger 不会立刻回滚而是记录一次失败计数并继续等待下一个 round 再查一次。只有当失败次数连续达到threshold值才会触发回滚。比如 threshold5意味着有 5 个连续的 interval 周期指标都不健康才会认定金丝雀版本有问题。这个设计是为了避免因为瞬时抖动导致的误杀。5.4 自动回滚是怎么发生的回滚发生时Flagger 会把 VirtualService 的权重直接切回 100% 到 primary 版本同时把 canary Deployment 的副本数缩到 0。这个过程非常快通常几秒内完成用户无感。回滚后的日志大概是这样10m: podinfo.test canary failed! 10m: podinfo.test rolling back podinfo to primary 10m: podinfo.test podinfo primary set to stefanprodan/podinfo:6.0.0另外 Flagger 还自带一个特性如果targetRef对应的 Deployment 在分析期间又被更新了它会取消当前发布。这个特性在线发布频繁的场景下很有用避免发布过程中又有人改了代码导致混乱。6. 进阶蓝绿发布、A/B 测试、Webhook 人工审批金丝雀只是 Flagger 支持的发布策略之一。它的 Canary CRD 里通过analysis.strategy字段可以切换不同的发布模式。6.1 金丝雀发布与蓝绿发布的差异默认不写 strategy走的就是金丝雀模式通过权重逐步引流。设置strategy: blue-green则变成蓝绿发布Flagger 会先启动金丝雀版本相当于 blue并等一段时间让指标确认没问题后一次性把全部流量切换过去。这种模式适合那种没法按百分比切流量的场景比如后端数据库 schema 不兼容必须是全量切换才能验证。蓝绿发布还有一个好处是可以配置analysis.webhooks里的rollout钩子在切换流量前执行自定义命令。蓝绿模式下maxWeight和stepWeight就不再适用了它只看iterations个周期内指标是否健康。它的发布过程更激进但回滚机制同样自动一旦预发布版本有问题根本不会切过去。6.2 A/B 测试模式A/B 测试模式对应strategy: canary加上 HTTP 匹配规则。你可以根据 Header、Cookie 或者 URI 参数来切分流量而不是按权重。比如把 User-Agent 为 iPhone 的用户全部导入新版本或者只让带了特殊 Cookie 的测试人员访问新版本。这种模式通常用于业务样式的验证比如新首页改版产品经理想让 10% 的真实用户看到新 UI收集反馈后再全量。配置方式是在 Canary 的service下添加service: port: 9898 match: - headers: x-canary: exact: true这样只有带x-canary: true的请求会进入金丝雀版本。这种发布模式的决策就不是看成功率了而是看业务方最终用数据说话。6.3 用 Webhook 做人工审批和自动验证Flagger 的 Webhook 机制非常灵活。除了前面提到的 load test它还可以作为人工审批门禁。例如在analysis.webhooks里挂一个审批系统Flagger 分析完指标后会先调用 WebhookWebhook 返回 HTTP 200 才继续下一个流量梯度返回非 200 就暂停发布。这对大规模团队很有用。新版本的技术指标虽然正常但产品经理还没确认 UI 改动是否符合预期这时可以通过 Webhook 把发布流程暂停在 50% 的权重上等人点确认再继续。Flagger 把这种流程称为手动门禁manual gating它完全没有破坏自动化的体验只是加了一道必要的控制。Webhook 定义示例webhooks: - name: gate type: rollout url: http://approval-service.test/hook timeout: 5s注意type: rollout表示它是在流量调整后的 rollout 阶段被调用。如果只想要发布前检查用type: pre-rollout如果要在流量全部放完后做一次全量冒烟验证用type: post-rollout。7. 我踩过的坑与排查实录实践 Flagger 的过程中我踩过不少坑。有些是配置层面的有些是环境集成层面的。我把典型问题整理成一个清单给大家做个参考。7.1 常见问题速查表问题现象可能原因解决方法Flagger 创建 Canary 后没有任何反应Deployment 的 Service 标签不匹配检查 Service 的 selector 是否匹配 Deployment 的app标签指标一直显示no data或waiting for metrics没有流量打到金丝雀版本部署 LoadTester 并配置 webhook确认 Prometheus 能采集到 Istio 指标回滚不触发发布一直挂着threshold设置太大或 interval 太短调小 threshold或者调大 intervalVirtualService 被 Flagger 重置有手动修改的痕迹不要手动改 Flagger 管理的 VirtualService金丝雀版本迟迟不创建annotations 冲突或 selector 不匹配检查 Flagger 是否注册了正确的 CRD 版本Webhook 调用失败发布卡住URL 不可达或 LoadTester 未部署kubectl logs里定位 webhook 具体报错7.2 坑一LoadTester 的请求没到金丝雀第一次运行时我配置好 Canary等了好久发现 Flagger 日志一直停在waiting for metrics。排查后发现LoadTester 虽然打请求了但请求没有经过 Istio 的 VirtualService 路由直接打到 Service 层所以没产生金丝雀版本流量的数据。解决办法是让 LoadTester 请求的域名走 Service Mesh 的虚拟域名比如http://podinfo.test:9898/而不是直接http://podinfo:9898/。前者会经过 Istio 的流量规则后者只是在集群内做了一次普通的 DNS 解析加负载均衡。7.3 坑二升级 Flagger 后 CRD 没跟上Flagger 版本升级后Canary CRD 的 schema 可能变了。如果直接升级 Flagger 的 Deployment 而没有同步升级 CRD会导致 Flagger watch 不到 Canary 对象或者发布策略部分字段不生效。我建议每次升级前都重新看下官方 release notes并且同时 apply 新的 CRDkubectl apply -f https://raw.githubusercontent.com/fluxcd/flagger/main/artifacts/flagger/crd.yaml顺序上先升级 CRD再升级 Flagger 本身。否则可能会出现 Flagger 读取到格式不兼容的 CRD 而导致控制器崩溃。7.4 坑三Prometheus 查询超时当 Prometheus 数据量大、查询复杂时Flagger 的指标查询可能超时。这时 Flagger 会报类似context deadline exceeded的错误。建议把所有指标查询尽量加上 namespace 过滤同时控制查询的时间范围不要全量扫。比如把自定义 PromQL 写成sum(rate(order_failures_total{namespacetest}[1m])) / sum(rate(order_requests_total{namespacetest}[1m]))不要省略 namespace 条件更不要直接在rate里扫整个集群。Flagger 每个 interval 都要分析一次查询复杂度直接决定分析的实时性。7.5 坑四流量权重正常递增但业务方说根本没灰度这种情况通常是因为服务入口根本没有走 Istio Gateway而是直接通过 NodePort 或者 LoadBalancer 暴露。Flagger 只能控制 VirtualService 里的流量如果外部流量不经过 VirtualService当然灰度不到。解决办法是让所有入站流量都走 Istio Ingress Gateway业务服务在网格内部通过 VirtualService 暴露。还有一种场景是你在多个命名空间部署了同名 ServiceIngress 路由串了。建议每个服务的 Canary、Service、Deployment 都加上明确的 namespace 前缀避免路由规则冲突。8. 最后分享两个实用小技巧第一个技巧是把 Flagger 的日志和告警对接到团队的即时通讯工具。Flagger 支持配置告警可以在 Canary 分析失败或者回滚时发消息到群里。具体配置是在 Flagger 启动参数加--alert-providerwebhook然后在 Canary CRD 里加notifications配置。这样每次发布失败不是只有你一个人盯着 kubectl 看日志全组都能同步知道责任感一下子就提升了。第二个技巧是第一次接入 Flagger 的时候别急着把 maxWeight 调到 100。先用保守的 50%配合人工 Webhook 门禁跑上一两个版本等团队对这套自动分析、自动回滚的机制建立了信心再慢慢放开。渐进式交付的理念不仅适用于应用发布也适用于你推广这套工具本身。我在实际运维中最大的感受是Flagger 并不是一个装完就忘的组件它需要你对自己的服务指标有足够清晰的定义。哪些指标代表这个服务的健康错误率容忍到多少延迟预期是多少这些想得越清楚Flagger 用得越顺手。如果你正被手工灰度折磨得焦头烂额找个测试服务按上面的流程跑一遍你大概率会回来感谢 Flagger 的。
返回列表