title: Argo Rollouts实战:基于ArgoCD的渐进式发布完整指南 date: 2025-08-12 23:11:08 categories:
- devops/argo tags:
- devops
- argo
- ArgoRollout
- Kubernetes
- GitOps disableNunjucks: true
Argo Rollouts实战:基于ArgoCD的渐进式发布完整指南
概述
Argo Rollouts是一个Kubernetes控制器和一组CRD,用于提供高级部署能力,如蓝绿部署、金丝雀发布和渐进式交付。与Kubernetes原生的Deployment相比,Argo Rollouts提供了更细粒度的流量控制和发布策略,能够显著降低应用发布风险。
本文将详细介绍如何在生产环境中部署和使用Argo Rollouts,结合ArgoCD实现GitOps工作流下的自动化渐进式发布。
核心优势
- 渐进式发布:支持分阶段逐步切换流量,降低发布风险
- 流量控制:支持基于权重和请求头的精细流量分割
- 自动化回滚:发布失败时可快速回滚到稳定版本
- 可视化监控:提供直观的发布状态和流量分布视图
- GitOps集成:与ArgoCD无缝集成,实现声明式部署管理
一、环境准备
1.1 ArgoCD部署
准备域名及SSL证书
配置域名解析
在/etc/hosts中添加域名解析,将ArgoCD域名解析到Ingress的外部IP:
x.x.x.x argo-devops.hua-ri.cn创建TLS密钥
创建用于HTTPS访问的TLS Secret:
kubectl create namespace argokubectl create secret tls argocd-server-tls -n argo \ --cert=/Users/king/hua-ri.cn.pem \ --key=/Users/king/hua-ri.cn.key验证Secret创建成功:
kubectl get secrets -n argo部署ArgoCD
获取IngressClass
kubectl get ingressclass期望输出:
NAME CONTROLLER PARAMETERS AGEack-nginx k8s.io/ack-ingress-nginx <none> 50d配置Helm Values
创建customs.yaml配置文件:
## Globally shared configurationglobal: domain: argo-devops.hua-ri.cn
## Serverserver: replicas: 1 ingress: enabled: true controller: generic ingressClassName: "ack-nginx" hostname: "argo-devops.hua-ri.cn" path: / pathType: Prefix annotations: nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/force-ssl-redirect: "true" tls: true使用Helm部署
helm upgrade --install argocd argo/argo-cd --version 7.3.5 \ --namespace argo \ --create-namespace \ -f customs.yaml访问ArgoCD
获取初始密码:
kubectl get secret -n argo argocd-initial-admin-secret \ -o jsonpath="{.data.password}" | base64 -d使用CLI登录:
argocd login argo-devops.hua-ri.cn --grpc-web1.2 Argo Rollouts部署
kubectl create namespace argo-rolloutskubectl apply -n argo-rollouts \ -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml验证部署成功:
kubectl get pods -n argo-rollouts二、应用架构设计
2.1 测试应用设计
为了演示Argo Rollouts的金丝雀发布功能,我们设计一个简单的HTTP服务,通过不同的版本返回不同的响应内容。
应用特性
- 提供HTTP接口
/api/check,返回当前版本信息 - v1版本返回
{"message": "v1"} - v2版本返回
{"message": "v2"} - 通过修改镜像标签实现版本切换
2.2 应用代码实现
main.go
package main
import ( "bytes" "compress/gzip" "context" "crypto/tls" "encoding/json" "fmt" "io" "io/ioutil" "log" "net/http" "net/http/httputil" "net/url" "strings" "time"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" argoio "github.com/argoproj/argo-cd/v2/util/io" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin")
const ( ARGOCD_HOST = "https://bkce7-dev.hua-ri.cn:8080/" ARGOCD_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmdvY2QiLCJzdWIiOiJxaXlhbjphcGlLZXkiLCJuYmYiOjE3NDU4Mzc1NjgsImlhdCI6MTc0NTgzNzU2OCwianRpIjoiNDM1NDgwY2ItNjliYy00Y2Q2LTk3NjYtMGU0ZDA0MzU5MWFhIn0.F-1ZZThXCqFnG_Kgxgy6z11Hmgl6g7mcEitaKQPyP8k")
type JSONResponse struct { Code int `json:"code"` Result bool `json:"result"` Msg string `json:"message"`}
type successResponse struct { JSONResponse Data interface{} `json:"data"`}
type ERRresponse struct { Error string `json:"message" example:"message"`}
func Error(message string) ERRresponse { return ERRresponse{Error: message}}
func ArgoCDErrorResponse(argocderr ArgoCDError) ERRresponse { return ERRresponse{Error: argocderr.Message}}
type ArgoCDError struct { Error error `json:"error"` Message string `json:"message"` Code int `json:"code"`}
func BuildArgoCDError(body []byte) (argocderr ArgoCDError, err error) { err = json.Unmarshal(body, &argocderr) if err != nil { return ArgoCDError{ Message: string(body), }, nil } return}
func wrapResponse(r *http.Response) (err error) { if strings.Contains(r.Request.URL.Path, "api/v1/secrets") { return nil } body, err := readBody(r) if err != nil { return } switch r.StatusCode { case http.StatusOK: var newbody interface{} err = json.Unmarshal(body, &newbody) if err != nil { return } resp := successResponse{ JSONResponse: JSONResponse{ Code: 0, Result: true, }, Data: newbody, } err = writeBody(r, resp) default: argocderr, err := BuildArgoCDError(body) if err != nil { err = writeBody(r, Error(string(body))) } else { err = writeBody(r, ArgoCDErrorResponse(argocderr)) } } return}
func readBody(r *http.Response) (body []byte, err error) { if r.Header.Get("Content-Encoding") == "gzip" { reader, err := gzip.NewReader(r.Body) if err != nil { log.Print("create reader content[gzip] failed") } defer reader.Close() body, err = io.ReadAll(reader) if err != nil { fmt.Printf("read gzip body failed,status:%s,error:%s", r.Status, err.Error()) } r.Header.Del("Content-Encoding") } else { body, err = ioutil.ReadAll(r.Body) if err != nil { fmt.Printf("read body failed,status:%s,error:%s", r.Status, err.Error()) } } return}
func writeBody(r *http.Response, res interface{}) error { updatedBody, err := json.Marshal(&res) if err != nil { fmt.Printf("unmarshal new gitops response body failed,body:%v,error:%s", res, err.Error()) } buf := bytes.NewBuffer(updatedBody) r.Body = ioutil.NopCloser(buf) r.Header["Content-Length"] = []string{fmt.Sprint(buf.Len())} return nil}
func argocdProxy(c *gin.Context) { request := c.Request writer := c.Writer
proxyURL, err := url.Parse(ARGOCD_HOST) if err != nil { return } proxyTarget := *proxyURL proxy := httputil.NewSingleHostReverseProxy(proxyURL) proxy.ModifyResponse = wrapResponse
request.Host = proxyTarget.Host request.URL.Scheme = proxyTarget.Scheme request.URL.Host = proxyTarget.Host request.Header.Set("Authorization", "Bearer "+ARGOCD_TOKEN)
request.URL.Path = strings.Replace(request.URL.Path, "/gitops", "", 1) request.URL.RawPath = strings.Replace(request.URL.RawPath, "/gitops", "", 1)
tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } proxy.Transport = tr proxy.ServeHTTP(writer, request)}
func testCall() interface{} { ctx := context.Background() clientOpts := argocdclient.ClientOptions{ ServerAddr: "bkce7-dev.hua-ri.cn:8080", Insecure: true, GRPCWebRootPath: "/", Headers: []string{ "Authorization: Bearer " + ARGOCD_TOKEN, "username: qiyan", }, AuthToken: ARGOCD_TOKEN, } client, _ := argocdclient.NewClient(&clientOpts)
conn, appIf := client.NewApplicationClientOrDie() defer argoio.Close(conn) app, err := appIf.List(ctx, &applicationpkg.ApplicationQuery{ Projects: []string{"default"}, }) if err != nil { return err.Error() } return app}
func main() { r := gin.Default() r.GET("/api/check", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "v1", }) }) r.GET("/user", func(c *gin.Context) { c.JSON(http.StatusOK, map[string]interface{}{ "code": 0, "result": true, "data": map[string]string{ "username": "qiyan", "type": "bk", }, }) }) r.GET("/argocd/applications", func(c *gin.Context) { c.JSON(http.StatusOK, map[string]interface{}{ "code": 0, "result": true, "data": testCall(), }) }) gitopsrouter := r.Group("gitops") gitopsrouter.Use(cors.New(cors.Config{ AllowOrigins: []string{"http://bkce7-dev.hua-ri.cn:8888", "http://power.hua-ri.cn:5000"}, AllowFiles: true, AllowWebSockets: true, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowHeaders: []string{"Origin", "Authorization", "x-token", "x-csrftoken", "Content-Type"}, ExposeHeaders: []string{"Content-Length", "Content-Type"}, AllowCredentials: true, MaxAge: 12 * time.Hour, })) gitopsrouter.Any("*subpath", argocdProxy)
r.Run()}核心接口说明
r.GET("/api/check", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "v1", })})这个接口是版本标识的关键,通过修改返回的message值来区分不同版本。
Dockerfile
# builderFROM public-registry-vpc.cn-hangzhou.cr.aliyuncs.com/public/golang:v1.22.2_20240416 AS builder
WORKDIR /srcUSER rootCOPY . .
RUN go env -w GOPROXY="https://goproxy.cn,direct"RUN go env -w GOSUMDB="off"RUN go mod downloadRUN go build -o demo main.go
# runtimeFROM public-registry-vpc.cn-hangzhou.cr.aliyuncs.com/public/alpine:3.14
ENV LANG en_US.UTF-8ENV TZ Asia/Shanghai
WORKDIR /appCOPY --from=builder /src/demo /app/demoENTRYPOINT ["/app/demo"]Makefile
# Go parametersIMAGE_REPO=dev-registry-vpc.cn-hangzhou.cr.aliyuncs.com/ops/qiyanIMAGE_TAG=v1
all:: server
server:: go run main.go
build:: go build main.go
test:: go test ./...
build-image: docker build --platform=linux/amd64 -t ${IMAGE_REPO}:${IMAGE_TAG} -f Dockerfile . --push2.3 镜像构建
构建v1版本
修改Makefile和main.go:
IMAGE_REPO=dev-registry-vpc.cn-hangzhou.cr.aliyuncs.com/ops/qiyanIMAGE_TAG=v1r.GET("/api/check", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "v1", })})执行构建:
make build-image构建v2版本
修改Makefile和main.go:
IMAGE_REPO=dev-registry-vpc.cn-hangzhou.cr.aliyuncs.com/ops/qiyanIMAGE_TAG=v2r.GET("/api/check", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "v2", })})执行构建:
make build-image三、Helm Chart设计
3.1 Chart结构
charts/ingress/├── Chart.yaml├── values.yaml├── templates/│ ├── ingress.yaml│ ├── rollout.yaml│ └── service.yaml└── .helmignore3.2 配置文件详解
values.yaml(顶级)
ingress: enabled: true这是Helm Chart的顶级开关,控制整个应用的启用状态。
charts/ingress/values.yaml
enabled: falselb: lb-bp1r1dsywnqktp1hxih38replicaCount: 1image: dev-registry-vpc.cn-hangzhou.cr.aliyuncs.com/ops/qiyanimageTag: v2port: 9999关键配置说明
enabled:控制ingress子chart的启用状态imageTag:控制要部署的镜像版本,这是测试Argo Rollouts时的关键参数port:服务端口,需要与容器端口对应
charts/ingress/Chart.yaml
apiVersion: v2name: ingressdescription: A Helm chart for Kubernetestype: applicationversion: 0.1.0appVersion: "1.16.0"charts/ingress/.helmignore
.DS_Store.git/.gitignore.bzr/.bzrignore.hg/.hgignore.svn/*.swp*.bak*.tmp*.orig*~.project.idea/*.tmproj.vscode/3.3 模板文件详解
templates/ingress.yaml
{{- if .Values.enabled }}apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: rollouts-demo-stable namespace: {{ .Release.Namespace }}spec: ingressClassName: ack-nginx rules: - host: bkce7-dev.hua-ri.cn http: paths: - backend: service: name: rollouts-demo-stable port: number: 9999 path: / pathType: Prefix{{- end }}配置要点
ingressClassName: ack-nginx:使用阿里云ACK托管的nginx ingresshost: bkce7-dev.hua-ri.cn:访问域名backend.port: 9999:必须与Service的port字段一致
templates/service.yaml
{{- if .Values.enabled }}apiVersion: v1kind: Servicemetadata: name: rollouts-demo-stable namespace: {{ .Release.Namespace }}spec: ports: - name: tcp port: {{ .Values.port }} protocol: TCP targetPort: 8080 selector: app: argocd-demo sessionAffinity: None type: ClusterIP
---apiVersion: v1kind: Servicemetadata: name: rollouts-demo-canary namespace: {{ .Release.Namespace }}spec: ports: - name: tcp port: {{ .Values.port }} protocol: TCP targetPort: 8080 selector: app: argocd-demo sessionAffinity: None type: ClusterIP{{- end }}关键配置
- 生成两个ClusterIP Service:
rollouts-demo-stable:稳定版本服务,金丝雀结束后的流量终点rollouts-demo-canary:金丝雀版本服务,只接收按权重或header分流的流量
targetPort: 8080:必须与容器的containerPort一致selector: app: argocd-demo:必须与rollout.yaml中的label匹配
templates/rollout.yaml
{{- if .Values.enabled }}apiVersion: argoproj.io/v1alpha1kind: Rolloutmetadata: name: rollouts-demo namespace: {{ .Release.Namespace }}spec: replicas: 5 strategy: canary: canaryService: rollouts-demo-canary stableService: rollouts-demo-stable trafficRouting: nginx: stableIngress: rollouts-demo-stable additionalIngressAnnotations: canary-by-header: X-Canary canary-by-header-value: iwantsit steps: - setCanaryScale: replicas: 1 - pause: { } - setWeight: 20 - pause: { } - setWeight: 40 - pause: { } - setWeight: 60 - pause: { } revisionHistoryLimit: 2 selector: matchLabels: app: argocd-demo template: metadata: labels: app: argocd-demo spec: containers: - name: rollouts-demo image: {{ .Values.image}}:{{ .Values.imageTag | default .Chart.AppVersion }} imagePullPolicy: Always ports: - name: http containerPort: 8080 protocol: TCP resources: requests: memory: 32Mi cpu: 5m securityContext: privileged: false terminationMessagePath: /dev/termination-log terminationMessagePolicy: File dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler securityContext: { } terminationGracePeriodSeconds: 30 imagePullSecrets: - name: dev-registry{{- end }}Rollout核心配置详解
Rollout是Argo Rollouts的核心CRD,功能等价于Deployment + 渐进式发布策略。
1. 基础配置
apiVersion: argoproj.io/v1alpha1kind: Rolloutmetadata: name: rollouts-demo namespace: {{ .Release.Namespace }}spec: replicas: 5 revisionHistoryLimit: 2 selector: matchLabels: app: argocd-demoreplicas: 5:期望副本数revisionHistoryLimit: 2:保留的ReplicaSet数量,用于回滚selector.matchLabels:必须与template.metadata.labels匹配
2. 金丝雀策略
strategy: canary: canaryService: rollouts-demo-canary stableService: rollouts-demo-stable trafficRouting: nginx: stableIngress: rollouts-demo-stable additionalIngressAnnotations: canary-by-header: X-Canary canary-by-header-value: iwantsitcanaryService:金丝雀服务名称stableService:稳定服务名称trafficRouting.nginx:使用nginx-ingress进行流量拆分stableIngress:必须指向已存在的IngressadditionalIngressAnnotations:自定义灰度规则canary-by-header: X-Canary:基于请求头的流量路由canary-by-header-value: iwantsit:只有携带X-Canary: iwantsit的请求才会路由到金丝雀版本
3. 灰度步骤
steps: - setCanaryScale: replicas: 1 - pause: { } - setWeight: 20 - pause: { } - setWeight: 40 - pause: { } - setWeight: 60 - pause: { }灰度步骤详解:
setCanaryScale(replicas: 1):先扩1个新版本Podpause{}:人工卡点,需要手动promotesetWeight: 20:20%流量切换到新版本pause{}:人工卡点setWeight: 40:40%流量切换到新版本pause{}:人工卡点setWeight: 60:60%流量切换到新版本pause{}:人工卡点,完成后自动切换到100%
4. Pod模板
template: metadata: labels: app: argocd-demo spec: containers: - name: rollouts-demo image: {{ .Values.image}}:{{ .Values.imageTag | default .Chart.AppVersion }} imagePullPolicy: Always ports: - name: http containerPort: 8080 protocol: TCPimage:使用Helm values中的image和imageTagcontainerPort: 8080:容器端口imagePullPolicy: Always:总是拉取最新镜像
四、实战测试:v1到v2的渐进式发布
4.1 初始状态:v1版本全量运行
配置
修改charts/ingress/values.yaml:
imageTag: v1部署
通过ArgoCD同步配置,观察Rollout状态:
kubectl get rollout -n <namespace>kubectl get pods -n <namespace>验证
测试接口返回:
curl http://bkce7-dev.hua-ri.cn/api/check预期返回:{"message":"v1"}
4.2 版本变更:v1到v2
配置变更
修改charts/ingress/values.yaml:
imageTag: v2同时修改rollout.yaml中的steps.setCanaryScale.replicas为2:
steps: - setCanaryScale: replicas: 2 - pause: { } - setWeight: 20 - pause: { } - setWeight: 40 - pause: { } - setWeight: 60 - pause: { }同步配置
通过ArgoCD同步配置,观察Rollout状态变化。
4.3 灰度步骤一:金丝雀Pod启动
状态描述
- 创建新的ReplicaSet,镜像版本为v2
- 启动2个v2版本的Pod
- v1版本的5个Pod继续运行
- 流量全部路由到v1版本
验证测试
不带请求头测试:
for i in {1..50}; do curl http://bkce7-dev.hua-ri.cn/api/check; done结果:全部返回{"message":"v1"}
带请求头测试:
for i in {1..50}; do curl http://bkce7-dev.hua-ri.cn/api/check -H 'X-Canary: iwantsit'; done结果:全部返回{"message":"v2"}
错误请求头测试:
for i in {1..50}; do curl http://bkce7-dev.hua-ri.cn/api/check -H 'X-Canary: huari'; done结果:全部返回{"message":"v1"}
说明
- 不带
X-Canary请求头:流量全部到v1版本 - 带
X-Canary: iwantsit请求头:流量全部到v2版本 - 带
X-Canary: huari请求头:流量全部到v1版本(header值不匹配)
4.4 灰度步骤二:20%流量到新版本
状态描述
- Pod数量不变:v1版本5个,v2版本2个
- 通过nginx-ingress的流量权重配置,20%流量路由到v2版本
验证测试
不带请求头测试:
for i in {1..50}; do curl http://bkce7-dev.hua-ri.cn/api/check; done结果示例:
{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v2"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v1"}{"message":"v2"}...统计:v2版本占比约9/50(18%),与20%的权重基本一致
带请求头测试:
for i in {1..50}; do curl http://bkce7-dev.hua-ri.cn/api/check -H 'X-Canary: iwantsit'; done结果:全部返回{"message":"v2"}
说明
- 不带请求头:按权重分配流量,约20%到v2版本
- 带正确请求头:流量全部到v2版本(header优先级高于权重)
4.5 灰度步骤三:40%流量到新版本
状态描述
- Pod数量不变
- 流量权重调整为40%
验证测试
不带请求头测试:
for i in {1..50}; do curl http://bkce7-dev.hua-ri.cn/api/check; done统计:v2版本占比约21/50(42%),与40%的权重基本一致
带请求头测试:
for i in {1..50}; do curl http://bkce7-dev.hua-ri.cn/api/check -H 'X-Canary: iwantsit'; done结果:全部返回{"message":"v2"}
4.6 灰度步骤四:60%流量到新版本
状态描述
- Pod数量不变
- 流量权重调整为60%
验证测试
不带请求头测试:
for i in {1..50}; do curl http://bkce7-dev.hua-ri.cn/api/check; done统计:v2版本占比约29/50(58%),与60%的权重基本一致
带请求头测试:
for i in {1..50}; do curl http://bkce7-dev.hua-ri.cn/api/check -H 'X-Canary: iwantsit'; done结果:全部返回{"message":"v2"}
4.7 灰度步骤五:100%流量到新版本
状态描述
- v2版本ReplicaSet扩容到5个Pod
- v1版本的5个Pod被终止
- 流量全部切换到v2版本
验证测试
不带请求头测试:
for i in {1..50}; do curl http://bkce7-dev.hua-ri.cn/api/check; done结果:全部返回{"message":"v2"}
带请求头测试:
for i in {1..50}; do curl http://bkce7-dev.hua-ri.cn/api/check -H 'X-Canary: iwantsit'; done结果:全部返回{"message":"v2"}
说明
- 发布完成,v2版本成为新的稳定版本
- v1版本的Pod已全部清理
- 无论是否带请求头,流量都路由到v2版本
五、核心概念深入解析
5.1 Rollout vs Deployment
Deployment
- 原生Kubernetes资源
- 支持RollingUpdate和Recreate策略
- 流量切换是瞬间的,无法精细控制
- 回滚需要手动操作
Rollout
- Argo Rollouts提供的CRD
- 支持金丝雀、蓝绿、分析等多种策略
- 支持基于权重和请求头的精细流量控制
- 支持自动化分析和回滚
- 与ArgoCD深度集成,支持GitOps
5.2 流量路由机制
基于权重的流量分割
通过nginx-ingress的nginx.ingress.kubernetes.io/canary-weight注解实现:
annotations: nginx.ingress.kubernetes.io/canary-weight: "20"基于请求头的流量路由
通过nginx-ingress的nginx.ingress.kubernetes.io/canary-by-header注解实现:
annotations: nginx.ingress.kubernetes.io/canary-by-header: "X-Canary" nginx.ingress.kubernetes.io/canary-by-header-value: "iwantsit"优先级
请求头路由的优先级高于权重路由。如果请求匹配了header规则,将直接路由到金丝雀版本,忽略权重配置。
5.3 灰度步骤设计
步骤类型
setCanaryScale:设置金丝雀Pod数量setWeight:设置流量权重pause:暂停,等待人工确认analysis:执行分析任务setHeaderRoute:设置基于请求头的路由
最佳实践
- 初始阶段:少量金丝雀Pod,观察稳定性
- 逐步放量:按20%、40%、60%的节奏增加流量
- 人工卡点:在每个阶段设置pause,确保可控
- 自动化分析:集成Prometheus等监控工具,自动判断是否继续
5.4 Service设计
双Service架构
Argo Rollouts需要两个Service:
- stableService:指向稳定版本的Pod
- canaryService:指向金丝雀版本的Pod
流量路由
- Ingress指向stableService
- nginx-ingress根据权重或header规则,将部分流量转发到canaryService
- 金丝雀发布完成后,canaryService的Pod成为新的stableService
六、生产环境最佳实践
6.1 监控和告警
关键指标
- Pod健康状态
- 流量分布比例
- 错误率和响应时间
- 资源使用情况
告警配置
- 金丝雀Pod启动失败
- 错误率超过阈值
- 响应时间显著增加
6.2 回滚策略
自动回滚
配置分析任务,当指标异常时自动回滚:
steps: - analysis: templates: - templateName: success-rate args: - name: service-name value: my-service手动回滚
kubectl rollout undo rollout rollouts-demo -n <namespace>6.3 安全考虑
权限控制
- 为Argo Rollouts配置最小权限
- 限制RBAC权限范围
镜像安全
- 使用私有镜像仓库
- 配置imagePullSecrets
- 定期扫描镜像漏洞
6.4 性能优化
资源限制
resources: requests: memory: "64Mi" cpu: "10m" limits: memory: "128Mi" cpu: "100m"Pod反亲和性
affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - argocd-demo topologyKey: kubernetes.io/hostname七、常见问题排查
7.1 流量未按预期路由
排查步骤
- 检查Ingress注解是否正确
- 验证nginx-ingress版本是否支持canary注解
- 检查Service的selector是否正确
- 查看nginx-ingress日志
7.2 金丝雀Pod无法启动
排查步骤
- 检查镜像是否可拉取
- 验证资源配置是否合理
- 查看Pod日志
- 检查节点资源是否充足
7.3 发布卡在pause步骤
解决方案
# 手动promotekubectl argo rollouts promote rollouts-demo -n <namespace>
# 或者跳过当前步骤kubectl argo rollouts promote rollouts-demo -n <namespace> --skip-current-step八、总结
Argo Rollouts为Kubernetes提供了强大的渐进式发布能力,通过本文的实战测试,我们验证了以下核心特性:
- 精细的流量控制:支持基于权重和请求头的流量分割
- 分阶段发布:通过steps配置实现可控的灰度过程
- GitOps集成:与ArgoCD无缝集成,实现声明式部署
- 快速回滚:发布失败时可快速回滚到稳定版本
- 可视化监控:提供直观的发布状态和流量分布
适用场景
- 高可用性要求:需要零停机时间的应用发布
- 复杂应用:需要充分测试的新功能发布
- A/B测试:需要对比不同版本效果的场景
- 风险控制:需要逐步放量的生产环境
通过合理配置灰度步骤和监控告警,Argo Rollouts能够显著降低应用发布风险,提升系统稳定性。
九、卸载清理
9.1 卸载ArgoCD
helm uninstall argocd --namespace argokubectl delete namespace argokubectl delete clusterrole argocd-serverkubectl delete clusterrolebinding argocd-serverkubectl delete clusterrole,clusterrolebinding -l app.kubernetes.io/instance=argocdkubectl delete crd applications.argoproj.io appprojects.argoproj.io applicationsets.argoproj.io9.2 卸载Argo Rollouts
kubectl delete -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yamlkubectl delete namespace argo-rolloutskubectl delete crd rollouts.argoproj.io analysisruns.argoproj.io analysistemplates.argoproj.io experiments.argoproj.io十、参考资源
部分信息可能已经过时









