跳到主要内容

使用 Argo Rollouts 进行金丝雀发布

Argo Rollouts 使用 Rollout 取代 Kubernetes Deployment,并按照声明的步骤推进发布。它会更新两个 Service,使其分别选择稳定版和金丝雀 ReplicaSet,然后修改路由权重,让网关按指定比例把流量发送到各版本。

本指南会在 20% 流量处创建人工暂停,然后推进到 50%,最后完成发布;还会演示如何中止有问题的版本并恢复期望的 Pod 模板。请选择 Gateway API HTTPRouteApisixRoute,并在整篇指南中保持一致。

集成成熟度

Argo Rollouts 将其内置 APISIX 流量路由器标记为 alpha。Gateway API 也把本指南使用的外部 Argo Rollouts Gateway API 插件列为 alpha,且该插件没有把 APISIX 或 API7 列入已测试的提供方。在采用任一路径前,请在非生产集群中验证具体版本、权限、提升和中止行为。

前提条件

  • 完成设置 Ingress Controller 和网关
  • 安装 HelmkubectlArgo Rollouts kubectl 插件
  • 确保网关能够访问应用命名空间中的 Service。
  • 使用 HTTPRoute 时,创建一个状态为 Programmed=TrueGateway,其监听器允许应用命名空间中的 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 安装控制器。

Gateway API 流量路由器是外部插件。将以下配置保存为 argo-rollouts-values.yaml。镜像摘要固定了本指南测试过的多架构 v0.16.0 插件制品。

argo-rollouts-values.yaml
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

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

创建 echo-route.yaml。请根据环境替换 Gateway 名称、命名空间和主机:

echo-route.yaml
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

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

echo-rollout.yaml
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 状态和权重。

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=TrueResolvedRefs=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中的命令,确认实时权重为 8020。然后通过网关发送一组示例请求:

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 请求成功率。
  • 包含足够请求的时间窗口内的尾延迟。
  • 上游连接失败和超时。
  • 结账完成率或认证失败等应用专用信号。
  • 最小请求数,防止把空结果误判为成功。

请统一配置 failureLimitconsecutiveSuccessLimit、测量间隔和指标查询。观察窗口应足够长,能够代表正常流量;指标中断时应停止推进,而不是静默批准。有关各提供方的资源,请参阅 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 的引用和权限。

  • trafficRouting.plugins.argoproj-labs/gatewayAPI.httpRoutenamespace 能够标识该 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,请保留它们。只有确认不存在 RolloutAnalysisTemplateAnalysisRunExperiment 资源后,才能单独删除。

相关内容