使用 Flagger 自动执行金丝雀发布
Flagger 可以自动分析 Kubernetes Deployment 的金丝雀版本。它会协调候选版本和主版本、加权路由、测试流量、指标以及提升或回滚。
本指南配置的金丝雀发布每次增加 10% 流量,最高达到 30%;要求请求成功率为 99%,最大请求耗时为 500 毫秒。请选择使用 HTTPRoute 的 Gateway API 提供方,或使用 ApisixRoute 的 APISIX 提供方,并在整篇指南中保持一致。
Gateway API 将 Flagger 集成列为公开预览阶段。采用 Gateway API 路径前,请在非生产集群中验证具体的控制器、网关和 Flagger 版本,以及初始化、提升和回滚行为。
了解 Flagger 管理的资源
对于名为 podinfo 的目标 Deployment,Flagger 会管理以下资源:
| 资源 | 用途 |
|---|---|
podinfo Deployment | 源 Pod 模板;Flagger 初始化后会将其缩容,并用于候选版本 |
podinfo-primary Deployment | 最近一次成功提升的 Pod 模板 |
podinfo、podinfo-primary 和 podinfo-canary Service | 分别作为公共、稳定版和金丝雀 Service |
生成的 HTTPRoute 或 ApisixRoute | 由 Flagger 控制稳定版和金丝雀权重 |
不要把生成的资源加入其他 Helm Release、Kustomize Base 或 GitOps 应用。请继续修改源 Deployment;Flagger 会把成功的变更复制到主 Deployment。
提供方决定 Flagger 如何构建发布 Route:
- 使用
gatewayapi:v1时,Flagger 根据Canary中的主机名和Gateway引用生成名为podinfo的HTTPRoute。 - 使用
apisix时,Flagger 把源ApisixRoute复制为名为podinfo-podinfo-canary的生成 Route。
前置条件
- 完成设置 Ingress Controller 和网关。
- 安装 Helm 和
kubectl。 - 确保网关能够访问应用命名空间中的 Service。
- 使用
HTTPRoute时,创建一个状态为Programmed=True的Gateway,其监听器允许应用命名空间中的 Route。 - 使用
ApisixRoute时,确保能够安全修改集群范围的默认IngressClass。 - 使用已公开 APISIX Prometheus 指标的 APISIX 网关或 API7 Gateway。
以下命令使用 Flagger Chart 1.44.0 和负载测试器 Chart 0.38.0。如果使用更新版本,请先查看发布说明并验证兼容性。
公开 APISIX 指标
分析会查询 apisix_http_status 和 apisix_http_latency_bucket。稍后安装的 Prometheus 服务器通过标准抓取注解发现网关 Pod。
如果使用 Chart 2.16.0 安装 APISIX,请把以下配置添加到网关 Release Values 中:
apisix:
prometheus:
enabled: true
podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9091"
prometheus.io/path: /apisix/prometheus/metrics
对于 API7 Gateway Chart 3.10.11,请改为添加以下配置:
pluginAttrs:
prometheus:
enable_export_server: true
export_addr:
ip: 0.0.0.0
port: 9091
apisix:
podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9091"
prometheus.io/path: /apisix/prometheus/metrics
显式配置 API7 导出地址后 ,Prometheus Pod 能够访问该端点,而不再局限于网关 Pod 内部。请通过负责网关 Release 的 Helm 或 GitOps 工作流应用这些配置。不要对 GitOps 管理的 Release 单独运行 helm upgrade。
如果使用其他 Chart 版本或 APISIX 软件包,请在应用前确认 Prometheus 属性和 Pod 注解的 Values 路径。不同 Chart 的 Values 路径并不通用。
验证网关 Pod 注解:
kubectl get pods --namespace <gateway-namespace> \
--selector <gateway-pod-label-selector> \
-o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.annotations.prometheus\.io/scrape}{"\t"}{.metadata.annotations.prometheus\.io/port}{"\n"}{end}'
对于上述 Chart 版本,APISIX 使用 app.kubernetes.io/name=apisix,API7 Gateway 使用 app.kubernetes.io/name=gateway。
从网关 Pod 转发指标端口,并确认指标可用:
kubectl port-forward pod/<gateway-pod> \
9091:9091 \
--namespace <gateway-namespace>
如果 网关 Chart 公开了指标 Service,也可以改为转发 Service 端口:
kubectl port-forward service/<gateway-metrics-service> \
9091:9091 \
--namespace <gateway-namespace>
在另一个终端中:
curl "http://127.0.0.1:9091/apisix/prometheus/metrics"
只有当端点返回 Prometheus 文本指标后,才能继续。
安装 Flagger 和 Prometheus
添加仓库:
helm repo add flagger https://flagger.app
helm repo update
选择安装的默认提供方。本指南中的每个 Canary 都会设置 spec.provider 并覆盖 meshProvider,因此一个 Flagger 控制器可以协调两种提供方。只有需要独立的命名空间范围或运维隔离时,才使用多个安装。
- HTTPRoute
- ApisixRoute
helm upgrade --install flagger flagger/flagger \
--version 1.44.0 \
--namespace flagger-system \
--create-namespace \
--set meshProvider=apisix \
--set prometheus.install=true
helm upgrade --install flagger flagger/flagger \
--version 1.44.0 \
--namespace flagger-system \
--create-namespace \
--set meshProvider=gatewayapi:v1 \
--set prometheus.install=true
该 Chart 从 crds 目录安装 CRD。使用 Helm 3 时不要设置 crd.create=true;这个旧版 Helm 2 选项会把相同 CRD 作为 Helm 模板再次渲染。
等待两个 Deployment 就绪:
kubectl rollout status deployment/flagger \
--namespace flagger-system \
--timeout 2m
kubectl rollout status deployment/flagger-prometheus \
--namespace flagger-system \
--timeout 2m
确认 Flagger 可以查询 Prometheus:
kubectl logs deployment/flagger --namespace flagger-system | \
grep 'all the metrics providers are available'
配置 Route 提供方
- HTTPRoute
- ApisixRoute
APISIX 提供方生成的 ApisixRoute 不包含 spec.ingressClassName。只有当 APISIX IngressClass 是集群默认类时,此版本的 Ingress Controller 才会处理该 Route。
列出现有的 IngressClass:
kubectl get ingressclass \
-o custom-columns=NAME:.metadata.name,CONTROLLER:.spec.controller,DEFAULT:.metadata.annotations.ingressclass\.kubernetes\.io/is-default-class
如果 APISIX 类应作为集群默认类,请为其添加注解。如果类名称不是 apisix,请替换相应值:
kubectl annotate ingressclass apisix \
ingressclass.kubernetes.io/is-default-class=true \
--overwrite
确保没有其他类仍被标记为默认类。这是集群范围的决策。如果 APISIX 不能成为唯一默认类,请使用 Gateway API 提供方,或者在 Flagger 能为生成的 Route 保留 spec.ingressClassName 之前不要使用此集成。
Gateway API 未定义 APISIX 插件配置,且生成的 HTTPRoute 由 Flagger 管理。请通过 Route 的 GatewayProxy 启用 Prometheus 插件,把以下条目添加到现有 spec.plugins 列表:
spec:
plugins:
- name: prometheus
enabled: true
config:
disable: false
prefer_name: true
这会为与该 GatewayProxy 关联的 Route 全局启用 Prometheus 指标。应用到共享生产网关前,请评估指标基数和公开范围的影响。
通过负责该资源的工作流应用更新后的 GatewayProxy,然后验证普通网关请求会生成 apisix_http_status 样本。prefer_name 配置会生成本指南指标模板所选择的 Route 标签。
部署示例应用
创建 podinfo-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo
namespace: flagger-demo
spec:
minReadySeconds: 5
progressDeadlineSeconds: 120
selector:
matchLabels:
app: podinfo
template:
metadata:
labels:
app: podinfo
spec:
containers:
- name: podinfo
image: ghcr.io/stefanprodan/podinfo:6.14.0
ports:
- name: http
containerPort: 9898
readinessProbe:
httpGet:
path: /readyz
port: http
resources:
requests:
cpu: 10m
memory: 32Mi
创建命名空间并应用 Deployment:
kubectl create namespace flagger-demo
kubectl apply -f podinfo-deployment.yaml
如果 Gateway 监听器按标签选择命名空间,请先向 flagger-demo 添加所需标签。生产发布策略应使用不可变镜像摘要。版本标签能让教程更易读,但可变标签无法证明评估的是哪个制品。
安装负载测试器
负载测试器会在整个分析期间执行 Flagger Webhook 中的命令。请将其安装到应用命名空间:
helm upgrade --install flagger-loadtester flagger/loadtester \
--version 0.38.0 \
--namespace flagger-demo
kubectl rollout status deployment/flagger-loadtester \
--namespace flagger-demo \
--timeout 2m
生产环境应使用有代表性的流量或经过审查的测试命令。请确认负载测试不会修改数据、压垮依赖或绕过正常授权。
配置路由和指标
- HTTPRoute
- ApisixRoute
将以下源 Route 保存为 podinfo-route.yaml,并根据环境替换主机:
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: podinfo
namespace: flagger-demo
spec:
ingressClassName: apisix
http:
- name: podinfo
match:
hosts:
- podinfo.example.com
methods:
- GET
paths:
- /*
backends:
- serviceName: podinfo
servicePort: 80
plugins:
- name: prometheus
enable: true
config:
disable: false
prefer_name: true
Flagger 要求选定的 HTTP 规则恰好包含一个源后端。它会复制该规则,并用生成的主 Service 和金丝雀 Service 替换该后端。
应用 Route:
kubectl apply -f podinfo-route.yaml
不要创建源 HTTPRoute。Flagger 会根据 Canary 中的主机名和 gatewayRefs 生成该资源。
Gateway API 提供方内置的 HTTP 观察器不会查询 APISIX 网关指标。将以下 APISIX 专用查询保存为 apisix-metric-templates.yaml:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: apisix-request-success-rate
namespace: flagger-system
spec:
provider:
type: prometheus
address: http://flagger-prometheus.flagger-system:9090
query: |
sum(
rate(
apisix_http_status{
route=~"{{ namespace }}_{{ target }}_.+",
code!~"5.."
}[{{ interval }}]
)
)
/
sum(
rate(
apisix_http_status{
route=~"{{ namespace }}_{{ target }}_.+"
}[{{ interval }}]
)
) * 100
---
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: apisix-request-duration
namespace: flagger-system
spec:
provider:
type: prometheus
address: http://flagger-prometheus.flagger-system:9090
query: |
histogram_quantile(
0.99,
sum(
rate(
apisix_http_latency_bucket{
type="request",
route=~"{{ namespace }}_{{ target }}_.+"
}[{{ interval }}]
)
) by (le)
)
应用模板:
kubectl apply -f apisix-metric-templates.yaml
该模板会选择生成的 Route 标签,例如 flagger-demo_podinfo_0-0。如果你的标签不同,请检查实时 apisix_http_status 样本,并在开始发布前把表达式限定到生成的 Route。
创建金丝雀策略
把所选 Route API 对应的策略保存为 podinfo-canary.yaml。请根据环境替换主机和网关 Service 地址。
- HTTPRoute
- ApisixRoute
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: flagger-demo
spec:
provider: apisix
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
routeRef:
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
name: podinfo
progressDeadlineSeconds: 120
service:
port: 80
targetPort: http
analysis:
interval: 10s
threshold: 5
maxWeight: 30
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 30s
- name: request-duration
thresholdRange:
max: 500
interval: 30s
webhooks:
- name: load-test
type: rollout
url: http://flagger-loadtester.flagger-demo/
timeout: 5s
metadata:
cmd: >-
hey -z 1m -q 10 -c 2 -host podinfo.example.com
http://<gateway-service>.<gateway-namespace>/
请把 gatewayRefs 的名称和命名空间替换为应接收生成 Route、且状态为 Programmed=True 的 Gateway:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: flagger-demo
spec:
provider: gatewayapi:v1
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
progressDeadlineSeconds: 120
service:
port: 80
targetPort: http
hosts:
- podinfo.example.com
gatewayRefs:
- name: <gateway-name>
namespace: <gateway-namespace>
analysis:
interval: 30s
threshold: 5
maxWeight: 30
stepWeight: 10
metrics:
- name: apisix-success-rate
templateRef:
name: apisix-request-success-rate
namespace: flagger-system
thresholdRange:
min: 99
interval: 1m
- name: apisix-request-duration-p99
templateRef:
name: apisix-request-duration
namespace: flagger-system
thresholdRange:
max: 500
interval: 1m
webhooks:
- name: smoke-test
type: pre-rollout
url: http://flagger-loadtester.flagger-demo/
timeout: 5s
metadata:
type: bash
cmd: >-
curl -fsS -d anon
http://podinfo-canary.flagger-demo/token | grep token
- name: load-test
type: rollout
url: http://flagger-loadtester.flagger-demo/
timeout: 5s
metadata:
cmd: >-
hey -z 2m -q 20 -c 2 -host podinfo.example.com
http://<gateway-service>.<gateway-namespace>/
自定义分析名称有意避开 Flagger 保留名称 request-success-rate 和 request-duration。使用任一保留名称时,都会在引用的 APISIX 模板之前调用 Gateway API 提供方的通用内置观察器,可能产生误导性的 no values found 失败。
示例使用较短间隔和 五次失败检查,使行为容易观察,同时为 Prometheus 收集第一批样本留出时间。生产配置应考虑正常流量、指标延迟、应用预热和误回滚成本。
应用策略:
kubectl apply -f podinfo-canary.yaml
验证初始化
等待 Canary 阶段变为 Initialized:
kubectl get canary podinfo --namespace flagger-demo --watch
检查生成的 Route。
- HTTPRoute
- ApisixRoute
kubectl get apisixroute podinfo-podinfo-canary \
--namespace flagger-demo \
-o yaml
Route 应包含权重为 100 的 podinfo-primary 和权重为 0 的 podinfo-canary,其状态应包含 Accepted=True 条件。否则请检查默认 IngressClass。
kubectl get httproute podinfo \
--namespace flagger-demo \
-o yaml
Route 应包含权重为 100 的 podinfo-primary 和权重为 0 的 podinfo-canary。只有当它针对当前 generation 报告 Accepted=True 和 ResolvedRefs=True 时,才能继续。
发布新版本前,验证 Prometheus 已有生成 Route 的样本:
kubectl port-forward service/flagger-prometheus \
9090:9090 \
--namespace flagger-system
在另一个终端中:
curl -G "http://127.0.0.1:9090/api/v1/query" \
--data-urlencode 'query=count by (route) (apisix_http_status)'
如果 Route 尚未收到流量,请先通过负载测试 Webhook 使用的同一网关主机和路径发送请求。
自动提升版本
更新源 Deployment:
kubectl set image deployment/podinfo \
podinfo=ghcr.io/stefanprodan/podinfo:6.14.1 \
--namespace flagger-demo
观察 Canary:
watch kubectl get canary podinfo --namespace flagger-demo
在另一个终端中,使用适合所选 Route 类型的命令观察生成 Route 的权重:
watch kubectl get apisixroute podinfo-podinfo-canary \
--namespace flagger-demo \
-o jsonpath='{range .spec.http[0].backends[*]}{.serviceName}={.weight}{" "}{end}{"\n"}'
对于 HTTPRoute,改为观察后端引用:
watch kubectl get httproute podinfo \
--namespace flagger-demo \
-o jsonpath='{range .spec.rules[0].backendRefs[*]}{.name}={.weight}{" "}{end}{"\n"}'
指标检查通过时,Flagger 应依次把权重推进到 10、20 和 30。随后它会把金丝雀 Pod 模板复制到 podinfo-primary,将路由恢复为 100/0,并把 Canary 标记为 Succeeded。
验证已提升的镜像:
kubectl get deployment podinfo-primary \
--namespace flagger-demo \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
验证自动回滚
在生产环境依赖回滚策略之前,请先在安全环境中测试。触发另一个候选版本,等待其权重大于零,然后通过同一 Route 生成 5xx 响应:
kubectl set env deployment/podinfo \
'PODINFO_UI_COLOR=#ff0000' \
--namespace flagger-demo
# 等待 kubectl get canary 报告权重大于零。
kubectl exec deployment/flagger-loadtester \
--namespace flagger-demo \
-- \
hey -z 2m -q 20 -c 2 \
-host podinfo.example.com \
http://<gateway-service>.<gateway-namespace>/status/500
观察 Flagger 事件:
kubectl describe canary podinfo --namespace flagger-demo
kubectl logs deployment/flagger --namespace flagger-system --follow
达到失败检查阈值后,Flagger 应执行以下操作:
- 将生成的 Route 恢复为主版本权重
100、金丝雀权重0。 - 缩容失败的金丝雀 Deployment。
- 在
podinfo-primary中保留先前镜像。 - 将 Canary 阶段标记为
Failed。
回滚后,请通过网关确认应用流量。Canary 为 Failed 说明策略已经介入,但并不能单独证明所有依赖都已恢复。
设计生产分 析
网关检查只是起点。请为服务专用指标添加 MetricTemplate 资源,并在适当时结合 Webhook。生产策略应考虑:
- 计算百分比之前所需的最小请求数。
- 查询窗口和抓取延迟。第一批样本到达前可能出现初始
no values found事件,不能将其视为成功。 - 相同 Route 标签下近期的失败。新发布开始时可能观察到仍处于指标查询窗口内的旧样本。
- 低流量服务,可能需要合成流量或更长间隔。
- 冷启动、缓存、连接池和依赖预热。
- 仅靠网关 HTTP 状态无法发现的业务和依赖指标。
- 针对
Failed、卡在Progressing以及指标提供方不可用状态的告警。 - 高风险环境的人工批准。
失败阈值应足够低以限制影响,又要足够高以容忍预期测量延迟。用分析间隔乘以允许的失败检查次数,可以估算 Flagger 拒绝失败发布所需的最长时间。
Route 和提供方限制
对于 APISIX 提供方:
- 生成的 Route 不会保留
spec.ingressClassName,因此需要默认 APISIXIngressClass。 - 选定的源 HTTP 规则必须恰好包含一个后端。
- Flagger 通过其维护的 APISIX CRD 模型复制源 Route,该模型可能落后于新版 Ingress Controller 增加的字段。
- 额外 Route 插件和复杂 插件配置都应经过初始化、提升和回滚测试。Flagger issue #1388 记录了与数值型 APISIX 插件配置相关的资源冲突循环。
对于 Gateway API 提供方:
- Flagger 生成并管理
HTTPRoute,不会接管现有 Route。 - Route 行为必须能够通过 Flagger
Canary服务配置和受支持的 Gateway API 字段表达。 - APISIX 专用分析需要自定义指标模板;通用 Gateway API 观察器并非 APISIX 指标集成。
- 生成的标准
HTTPRoute无法挂载 Route 级 Prometheus 插件,因此本指南在GatewayProxy范围启用该插件。
每次升级 Flagger 后都应检查生成的资源。不要假设 Canary 成功准入就代表 Route、策略和指标行为仍完全相同。
故障排查
使用以下检查定位 Route 准入、指标收集、协调或发布进度问题。
生成的 Route 未被接受
对于 ApisixRoute,检查生成的 Route 和默认 IngressClass:
kubectl get apisixroute podinfo-podinfo-canary \
--namespace flagger-demo \
-o yaml
kubectl get ingressclass -o yaml
对于 HTTPRoute,检查其父级条件,并确认 Gateway 监听器允许该 Route 命名空间:
kubectl get httproute podinfo --namespace flagger-demo -o yaml
kubectl get gateway <gateway-name> \
--namespace <gateway-namespace> \
-o yaml
Flagger 报告没有指标值
验证 Prometheus 目标、网关注解、Route 流量和 Route 标签:
kubectl port-forward service/flagger-prometheus \
9090:9090 \
--namespace flagger-system
curl -G "http://127.0.0.1:9090/api/v1/query" \
--data-urlencode 'query=apisix_http_status'
负载测试 URL 必须经过 Canary 所测量的同一 Route。直接请求 Pod 或 Service 不会生成 APISIX Route 指标。对于 HTTPRoute,还要确认自定义分析名称不是保留的内置指标名称。
Route 更新重复或冲突
检查生成的 Route 和 Flagger 日志中的重复更新。不要用 Git 或其他包管理器管理生成的 Route。对于 APISIX 提供方,可简化额外插件配置,以找出无法通过 Flagger 中 APISIX 类型往返保留的字段。不要全局禁用协调冲突处理。
提升时间超出预期
Flagger 等待 Pod 就绪、第一批指标样本、Webhook 或下一个分析间隔时可能暂停推进。请检查 Canary 事件,不要只根据 maxWeight 和 stepWeight 估算进度。
清理
删除示例命名空间:
kubectl delete namespace flagger-demo
仅当没有其他 Canary 资源依赖 Flagger 时,才能将其删除:
helm uninstall flagger --namespace flagger-system
kubectl delete namespace flagger-system
Helm 会保留从 Chart crds 目录安装的 CRD。如果其他安装可能使用 Flagger CRD,请保留它们。只有确认不存在 Canary、MetricTemplate 或 AlertProvider 资源后,才能单独删除。
如果选择了 ApisixRoute,只有当 APISIX 不应再作为集群默认类时,才移除默认注解:
kubectl annotate ingressclass apisix \
ingressclass.kubernetes.io/is-default-class-
如果选择了 HTTPRoute,只有当没有其他监控工作流依赖全局 Prometheus 插件时,才将其移除。