使用 Argo Rollouts 进行金丝雀发布
Argo Rollouts 使用 Rollout 取代 Kubernetes Deployment,并按照声明的步骤推进发布。它会更新两个 Service,使其分别选择稳定版和金丝雀 ReplicaSet,然后修改路由权重,让网关按指定比例把流量发送到各版本。
本指南会在 20% 流量处创建人工暂停,然后推进到 50%,最后完成发布;还会演示如何中止有问题的版本并恢复期望的 Pod 模板。请选择 Gateway API HTTPRoute 或 ApisixRoute,并在整篇指南中保持一致。
Argo Rollouts 将其内置 APISIX 流量路由器标记为 alpha。Gateway API 也把本指南使用的外部 Argo Rollouts Gateway API 插件列为 alpha,且该插件没有把 APISIX 或 API7 列入已测试的提供方。在采用任一路径前,请在非生产集群中验证具体版本、权限、提升和中止行为。
前提条件
- 完成设置 Ingress Controller 和网关。
- 安装 Helm、
kubectl和 Argo Rollouts kubectl 插件。 - 确保网关能够访问应用命名空间中的 Service。
- 使用
HTTPRoute时,创建一个状态为Programmed=True的Gateway,其监听器允许应用命名空间中的 Route。 - 使用
ApisixRoute时,为 APISIX 自定义资源配置IngressClass。 - 确定由哪些指标或人工批准决定生产发布能否继续。
以下命令使用 Argo Rollouts Chart 2.41.1(安装 Argo Rollouts v1.9.1)和 Gateway API 流量路由插件 v0.16.0。如果使用更新版本,请先查看其发布说明并验证兼容性。
安装 Argo Rollouts
添加 Chart 仓库:
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
根据所选 Route API 安装控制器。
- HTTPRoute
- ApisixRoute
APISIX 流量路由器内置于 Argo Rollouts:
helm upgrade --install argo-rollouts argo/argo-rollouts \
--version 2.41.1 \
--namespace argo-rollouts \
--create-namespace
Gateway API 流量路由器是外部插件。将以下配置保存为 argo-rollouts-values.yaml。镜像摘要固定了本指南测试过的多架构 v0.16.0 插件制品。
controller:
replicas: 1
initContainers:
- name: copy-gateway-api-plugin
image: ghcr.io/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi@sha256:af5aaba7e34c2b8eb0d52128ac91cb913f065b710c7dea88db59d626a96c53c2
command:
- /bin/sh
- -c
args:
- cp /bin/rollouts-plugin-trafficrouter-gatewayapi /plugins/
volumeMounts:
- name: gateway-api-plugin
mountPath: /plugins
trafficRouterPlugins:
- name: argoproj-labs/gatewayAPI
location: file:///plugins/rollouts-plugin-trafficrouter-gatewayapi
volumes:
- name: gateway-api-plugin
emptyDir: {}
volumeMounts:
- name: gateway-api-plugin
mountPath: /plugins
podSecurityContext:
runAsNonRoot: true
fsGroup: 999
安装带有该插件的 Argo Rollouts:
helm upgrade --install argo-rollouts argo/argo-rollouts \
--version 2.41.1 \
--namespace argo-rollouts \
--create-namespace \
--values argo-rollouts-values.yaml
等待控制器就绪:
kubectl rollout status deployment/argo-rollouts \
--namespace argo-rollouts \
--timeout 2m
创建稳定版和金丝雀 Service
将以下资源保存为 echo-services.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: rollout-demo
---
apiVersion: v1
kind: Service
metadata:
name: echo-stable
namespace: rollout-demo
spec:
ports:
- name: http
port: 80
targetPort: http
selector:
app: echo
---
apiVersion: v1
kind: Service
metadata:
name: echo-canary
namespace: rollout-demo
spec:
ports:
- name: http
port: 80
targetPort: http
selector:
app: echo
Argo Rollouts 会把稳定版或金丝雀 ReplicaSet 哈希添加到这些选择器中。不要自行设置或持续协调 rollouts-pod-template-hash。
应用该文件:
kubectl apply -f echo-services.yaml
创建 Route
- HTTPRoute
- ApisixRoute
创建 echo-route.yaml。如果安装使用不同的值,请 替换主机和 ingressClassName:
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: echo
namespace: rollout-demo
spec:
ingressClassName: apisix
http:
- name: echo
match:
hosts:
- echo.example.com
paths:
- /*
backends:
- serviceName: echo-stable
servicePort: 80
- serviceName: echo-canary
servicePort: 80
创建 echo-route.yaml。请根据环境替换 Gateway 名称、命名空间和主机:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: echo
namespace: rollout-demo
spec:
parentRefs:
- name: <gateway-name>
namespace: <gateway-namespace>
hostnames:
- echo.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: echo-stable
port: 80
- name: echo-canary
port: 80
插件需要更新 HTTPRoute 的权限。将以下命名空间级权限保存为 echo-route-rbac.yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: argo-rollouts-gateway-api
namespace: rollout-demo
rules:
- apiGroups:
- gateway.networking.k8s.io
resources:
- httproutes
verbs:
- get
- update
- patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: argo-rollouts-gateway-api
namespace: rollout-demo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: argo-rollouts-gateway-api
subjects:
- kind: ServiceAccount
name: argo-rollouts
namespace: argo-rollouts
应用权限:
kubectl apply -f echo-route-rbac.yaml
不要设置两个后端的 weight 字段。Argo Rollouts 会初始化并负责这些字段。
应用 Route:
kubectl apply -f echo-route.yaml
创建 Rollout
把所选 Route API 对应的 Rollout 保存为 echo-rollout.yaml。
- HTTPRoute
- ApisixRoute
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: echo
namespace: rollout-demo
spec:
replicas: 5
revisionHistoryLimit: 2
selector:
matchLabels:
app: echo
strategy:
canary:
stableService: echo-stable
canaryService: echo-canary
trafficRouting:
apisix:
route:
name: echo
rules:
- echo
steps:
- setWeight: 20
- pause: {}
- setWeight: 50
- pause:
duration: 1m
- setWeight: 100
template:
metadata:
labels:
app: echo
spec:
containers:
- name: echo
image: hashicorp/http-echo:1.0.0
args:
- -listen=:5678
- -text=stable
ports:
- name: http
containerPort: 5678
readinessProbe:
httpGet:
path: /
port: http
resources:
requests:
cpu: 10m
memory: 16Mi
rules 条目必须与 ApisixRoute 中的 HTTP 规则名称完全一致。
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: echo
namespace: rollout-demo
spec:
replicas: 5
revisionHistoryLimit: 2
selector:
matchLabels:
app: echo
strategy:
canary:
stableService: echo-stable
canaryService: echo-canary
trafficRouting:
plugins:
argoproj-labs/gatewayAPI:
httpRoute: echo
namespace: rollout-demo
steps:
- setWeight: 20
- pause: {}
- setWeight: 50
- pause:
duration: 1m
- setWeight: 100
template:
metadata:
labels:
app: echo
spec:
containers:
- name: echo
image: hashicorp/http-echo:1.0.0
args:
- -listen=:5678
- -text=stable
ports:
- name: http
containerPort: 5678
readinessProbe:
httpGet:
path: /
port: http
resources:
requests:
cpu: 10m
memory: 16Mi
被引用的 HTTPRoute 规则必须同时包含稳定版和金丝雀后端 Service。当 Route 包含多条规则时,只会修改同时包含这两个 Service 的规则。
应用 Rollout:
kubectl apply -f echo-rollout.yaml
由于没有旧版本可供比较,第一个版本会立即成为稳定版。等待其进入健康状态:
kubectl argo rollouts get rollout echo \
--namespace rollout-demo \
--watch
验证初始 Route
检查 Route 状态和权重。
- HTTPRoute
- ApisixRoute
kubectl get apisixroute echo \
--namespace rollout-demo \
-o jsonpath='{range .spec.http[0].backends[*]}{.serviceName}={.weight}{"\n"}{end}'
kubectl get httproute echo \
--namespace rollout-demo \
-o jsonpath='{range .spec.rules[0].backendRefs[*]}{.name}={.weight}{"\n"}{end}'
kubectl get httproute echo \
--namespace rollout-demo \
-o jsonpath='{range .status.parents[*].conditions[*]}{.type}={.status} observedGeneration={.observedGeneration}{"\n"}{end}'
只有当 Route 针对当前 generation 报告 Accepted=True 和 ResolvedRefs=True 时,才能继续。
稳定版 Service 的权重应为 100,金丝雀 Service 的权重应为 0。
在另一个终端中转发网关端口。请根据安装替换 Service 名称和命名空间:
kubectl port-forward service/<gateway-service> \
9080:80 \
--namespace <gateway-namespace>
验证稳定版响应:
curl -H 'Host: echo.example.com' "http://127.0.0.1:9080/"
响应应为 stable。
发布新版本
修改 Pod 模板以启动发布:
kubectl patch rollout echo \
--namespace rollout-demo \
--type json \
--patch '[{"op":"replace","path":"/spec/template/spec/containers/0/args/1","value":"-text=canary"}]'
观察 Rollout,直至它在 20% 流量处暂停:
kubectl argo rollouts get rollout echo \
--namespace rollout-demo \
--watch
使用验证初始 Route中的命令,确认实时权重为 80 和 20。然后通过网关发送一组示例请求:
for request in $(seq 1 100); do
curl -sS -H 'Host: echo.example.com' "http://127.0.0.1:9080/"
done | sort | uniq -c
结果应接近 80 个稳定版响应和 20 个金丝雀响应。样本量较小时不会得到精确比例。
检查应用和指标后,继续发布:
kubectl argo rollouts promote echo --namespace rollout-demo
发布会推进到 50%,等待一分钟后完成。完成后,新提升的 ReplicaSet 会接收全部流量并成为稳定版本。
添加自动分析
人工暂停可以演示路由流程,但生产发布应评估服务级指标。请添加 Argo Rollouts AnalysisTemplate,并添加查询指标提供方的 analysis 步骤。可用的检查包括:
- 此 Route 的 HTTP 请求成功率。
- 包含足够请求的时间窗口内的尾延迟。
- 上游连接失败和超时。
- 结账完成率或认证失败等应用专用信号。
- 最小请求数,防止把空结果误判为成功。
请统一配置 failureLimit、consecutiveSuccessLimit、测量间隔和指标查询。观察窗口应足够长,能够代表正常流量;指标中断时应停止推进,而不是静默批准。有关各提供方的资源,请参阅 Argo Rollouts 分析文档。
中止并恢复发布
立即中止不安全的发布:
kubectl argo rollouts abort echo --namespace rollout-demo
Argo Rollouts 会把 Route 恢复为最后一个稳定 ReplicaSet 权重 100、金丝雀权重 0。确认 Route 权重和网关实时响应后,才能认为恢复完成。
中止操作不会还原 Rollout 中期望的 Pod 模板。开始下一次发布前,请恢复最后一个正常镜像、参数、配置或 Git 版本。本示例可执行:
kubectl patch rollout echo \
--namespace rollout-demo \
--type json \
--patch '[{"op":"replace","path":"/spec/template/spec/containers/0/args/1","value":"-text=stable"}]'
如果 Rollout 由 Git 管理,请还原或修正 Git 版本,不要保留仅存在于实时集群中的补丁。
配合 GitOps 运维
虽然 Argo CD 和 Argo Rollouts 都使用 Argo 名称,但它们是不同的控制器。Argo CD 声明并协调资源;Argo Rollouts 执行发布状态机。同一发布流程也可以使用其他 GitOps 控制器,或完全不使用 GitOps 控制器。
必须允许 Argo Rollouts 更新 Service 选择器和 Route 后端权重。不要在 Git 中声明 weight 字段,并在测试发布期间检查 GitOps 应用的实时差异。Gateway API 插件会临时向 HTTPRoute 添加 rollouts.argoproj.io/gatewayapi-canary=in-progress 标签,可用于识别活动发布,但不会转移无关 Route 字段的所有权。
不要忽略整个 Route 或 Service。如需忽略规则,应仅限于两个后端权重字段或由 Rollouts 管理的 Service 选择器哈希。范围过大的规则可能掩盖意外的主机、路径、插件、过滤器 或后端变更。
故障排查
使用以下检查区分 Route 配置问题、网关同步延迟和有意暂停的发布。
Route 权重没有变化
验证特定 Route 的引用和权限。
- HTTPRoute
- ApisixRoute
trafficRouting.apisix.route.name与ApisixRoute名称匹配。rules下的每个条目都与该 Route 中的 HTTP 规则名称匹配。- 控制器可以在应用命名空间中获取和更新
apisixroutes.apisix.apache.org。
trafficRouting.plugins.argoproj-labs/gatewayAPI.httpRoute和namespace能够标识该 Route。- 一条 Route 规则同时包含稳定版和金丝雀 Service。
- 插件二进制文件已复制并在 Argo Rollouts 控制器 Pod 中初始化。
RoleBinding向 Argo Rollouts ServiceAccount 授予获取、更新和修补 Route 的权限。
对于任一 Route 类型,请确认 Service 名称与 Rollout 匹配 ,且没有其他协调器恢复权重。检查控制器事件和日志:
kubectl describe rollout echo --namespace rollout-demo
kubectl logs deployment/argo-rollouts --namespace argo-rollouts
权重变化但流量没有变化
检查 Route 状态、Service 端点和 Pod 标签:
kubectl get endpointslice --namespace rollout-demo \
--label kubernetes.io/service-name=echo-canary
kubectl get pods --namespace rollout-demo --show-labels
两个 Service 都必须选择预期的 ReplicaSet,且 Ingress Controller 必须接受并同步 Route 的当前 generation。评估流量前,还要确认请求主机和路径与 Route 匹配,并且网关已经收到更新。
发布一直处于暂停状态
空的 pause: {} 没有持续时间,需要执行提升。提升前请检查 Rollout 状态。不要仅为了清除异常状态而移除安全暂停。
清理
删除示例应用:
kubectl delete namespace rollout-demo
仅当没有其他 Rollout 依赖 Argo Rollouts 时,才能将其删除:
helm uninstall argo-rollouts --namespace argo-rollouts
kubectl delete namespace argo-rollouts
该 Chart 使用 helm.sh/resource-policy: keep 标记 Argo Rollouts CRD,因此卸载 Release 不会删除它们。如果其他安装可能使用这些 CRD,请保留它们。只有确认不存在 Rollout、AnalysisTemplate、AnalysisRun 或 Experiment 资源后,才能单独删除。