从手动部署到 GitOps 只需四步:ArgoCD + GitLab CI + ECR + EKS 实战
你有没有经历过这样的场景:凌晨三点,运维同事打电话说线上挂了,你迷迷糊糊打开电脑,发现是下午某次部署改坏了什么东西,但没人记得改了什么,也没人知道怎么回滚。于是你开始翻 企业微信 聊天记录、翻 Jenkins 构建日志、翻 kubectl 的 history……
如果你点头了,恭喜你,你需要 GitOps。
GitOps 的核心思想很简单:Git 仓库就是你集群的"真相"。集群里跑什么版本、多少副本、什么配置,全部写在 Git 里。想部署?改 Git。想回滚?git revert。想审计?git log。凌晨三点的电话?不存在的。
这篇文章带你从零搭建一条完整的 GitOps 流水线。不讲虚的,全是能直接复制粘贴的配置。
一、先看全景图,心里有个数
| 角色 | 工具 | 干什么的 |
| 搬砖工 | GitLab CI | 编译代码、构建镜像、推到仓库、通知部署 |
| 仓库管理员 | AWS ECR | 存 Docker 镜像,给 EKS 拉取 |
| 保安队长 | ArgoCD | 盯着 Git 仓库,有变化就自动同步到集群 |
| 工地 | AWS EKS | 跑你的服务,对外提供能力 |
整个流程长这样:
你 push 代码
↓
GitLab CI:收到!开始干活
↓ 构建 Docker 镜像
↓ 推送到 ECR
↓ 改 manifest 仓库里的镜像 tag
↓
ArgoCD(每 3 分钟巡逻一次):咦,Git 变了!
↓ 自动 kubectl apply
↓
EKS:新版本上线 [done]
↓
你:喝口咖啡
注意这里有个关键设计:CI 不直接操作集群。CI 只负责改 Git 仓库,ArgoCD 负责把 Git 里的状态同步到集群。这就是 push 模式和 pull 模式的区别。好处是 CI 不需要集群凭证,安全性大大提升。
二、ECR:给镜像找个家
ECR 就是 AWS 版的 Docker Hub,但它跟 EKS 是亲兄弟,拉镜像不用额外配置认证(前提是 Node Role 有 ECR 权限,后面会讲)。
2.1 Terraform 创建 ECR 仓库
# ecr.tf
resource "aws_ecr_repository" "app" {
name = "my-app"
image_tag_mutability = "IMMUTABLE" # 推荐:禁止覆盖已有 tag
image_scanning_configuration {
scan_on_push = true # 推送时自动扫描漏洞
}
tags = {
Environment = "production"
}
}
# 生命周期策略:自动清理旧镜像,不然账单会教你做人
resource "aws_ecr_lifecycle_policy" "app" {
repository = aws_ecr_repository.app.name
policy = jsonencode({
rules = [{
rulePriority = 1
description = "保留最近 30 个镜像,其余自动删除"
selection = {
tagStatus = "any"
countType = "imageCountMoreThan"
countNumber = 30
}
action = {
type = "expire"
}
}]
})
}
output "ecr_url" {
value = aws_ecr_repository.app.repository_url
# 输出类似:123456789.dkr.ecr.ap-southeast-1.amazonaws.com/my-app
}
两个小建议:
- image_tag_mutability 设为 IMMUTABLE:防止有人偷偷覆盖 latest 标签导致线上跑的版本不可追溯。每次构建用 commit SHA 或时间戳做 tag
- lifecycle_policy 必须配:ECR 按存储量收费,不清理的话几个月下来镜像堆积如山
2.2 手动验证 ECR(可选)
# 登录 ECR
aws ecr get-login-password --region ap-southeast-1 | \
docker login --username AWS --password-stdin 123456789.dkr.ecr.ap-southeast-1.amazonaws.com
# 构建并推送测试镜像
docker build -t my-app:test .
docker tag my-app:test 123456789.dkr.ecr.ap-southeast-1.amazonaws.com/my-app:test
docker push 123456789.dkr.ecr.ap-southeast-1.amazonaws.com/my-app:test
# 能推上去就说明 ECR 没问题
三、GitLab CI:让搬砖工自动化
这一步是整条流水线的发动机。GitLab CI 要做三件事:构建镜像、推到 ECR、更新 manifest 仓库的镜像 tag。
3.1 项目结构
我们需要两个 Git 仓库(这是 GitOps 的标准做法):
| 仓库 | 内容 | 谁在用 |
| my-app(应用仓库) | 业务代码 + Dockerfile + .gitlab-ci.yml | 开发人员 |
| my-app-manifests(配置仓库) | K8s YAML / Kustomize 配置 | ArgoCD 监听 |
为什么要分两个仓库?因为应用代码的提交频率远高于配置变更。如果放一起,每次改个 README 都会触发 ArgoCD 同步,你的集群会忙到冒烟。
3.2 应用仓库的 Dockerfile
# Dockerfile — 多阶段构建,最终镜像尽量小
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server ./cmd/server
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata
COPY --from=builder /app/server /usr/local/bin/server
EXPOSE 8080
CMD ["server"]
3.3 .gitlab-ci.yml 完整配置
重头戏来了。这个文件定义了整个 CI 流程:
# .gitlab-ci.yml
variables:
AWS_REGION: ap-southeast-1
ECR_REGISTRY: 123456789.dkr.ecr.ap-southeast-1.amazonaws.com
ECR_REPO: my-app
MANIFEST_REPO: git@gitlab.com:myteam/my-app-manifests.git
# IMAGE_TAG 用 commit SHA 前 8 位,保证唯一且可追溯
IMAGE_TAG: ${CI_COMMIT_SHORT_SHA}
stages:
- test
- build
- deploy
# ========== 阶段一:跑测试 ==========
unit-test:
stage: test
image: golang:1.21-alpine
script:
- go test ./... -v -cover
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
# ========== 阶段二:构建镜像推 ECR ==========
build-and-push:
stage: build
image: docker:24-dind
services:
- docker:24-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
# 安装 AWS CLI(轻量版)
- apk add --no-cache aws-cli
# 登录 ECR
- aws ecr get-login-password --region $AWS_REGION |
docker login --username AWS --password-stdin $ECR_REGISTRY
script:
- echo "构建镜像 $ECR_REGISTRY/$ECR_REPO:$IMAGE_TAG"
- docker build -t $ECR_REGISTRY/$ECR_REPO:$IMAGE_TAG .
- docker push $ECR_REGISTRY/$ECR_REPO:$IMAGE_TAG
- echo "镜像推送完成"
rules:
- if: $CI_COMMIT_BRANCH == "main"
# ========== 阶段三:更新 manifest 仓库 ==========
update-manifest:
stage: deploy
image: alpine:3.19
before_script:
- apk add --no-cache git openssh-client
# 配置 SSH(用于 push manifest 仓库)
- eval $(ssh-agent -s)
- echo "$DEPLOY_SSH_KEY" | ssh-add -
- mkdir -p ~/.ssh
- ssh-keyscan gitlab.com >> ~/.ssh/known_hosts
- git config --global user.email "ci@myteam.com"
- git config --global user.name "GitLab CI"
script:
- git clone $MANIFEST_REPO /tmp/manifests
- cd /tmp/manifests
# 用 sed 替换镜像 tag(Kustomize 方式见下文)
- |
sed -i "s|image: $ECR_REGISTRY/$ECR_REPO:.*|image: $ECR_REGISTRY/$ECR_REPO:$IMAGE_TAG|g" \
overlays/production/kustomization.yaml
- git add .
- git commit -m "chore: update image to $IMAGE_TAG [skip ci]"
- git push origin main
- echo "Manifest 仓库已更新,等待 ArgoCD 同步"
rules:
- if: $CI_COMMIT_BRANCH == "main"
3.4 GitLab CI 变量配置
在 GitLab 项目的 Settings → CI/CD → Variables 里添加:
| 变量名 | 说明 | 类型 |
| AWS_ACCESS_KEY_ID | 有 ECR push 权限的 IAM 用户 AK | Variable (Masked) |
| AWS_SECRET_ACCESS_KEY | 对应的 SK | Variable (Masked) |
| DEPLOY_SSH_KEY | 能 push manifest 仓库的 SSH 私钥 | File |
安全提示:给 CI 用的 IAM 用户只需要 ecr:GetAuthorizationToken、ecr:BatchCheckLayerAvailability、ecr:PutImage 等 ECR 推送权限,千万别给 Admin。最小权限原则,老生常谈但真的重要。
四、Manifest 仓库:集群的"圣经"
这个仓库是 ArgoCD 的数据源,也是你集群状态的唯一真相。我们用 Kustomize 来管理不同环境的配置差异。
4.1 目录结构
my-app-manifests/
├── base/ # 基础配置(所有环境共享)
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── hpa.yaml
│ └── kustomization.yaml
└── overlays/
├── staging/ # 测试环境覆盖
│ └── kustomization.yaml
└── production/ # 生产环境覆盖
├── kustomization.yaml
└── ingress.yaml
4.2 base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
serviceAccountName: my-app
containers:
- name: my-app
image: 123456789.dkr.ecr.ap-southeast-1.amazonaws.com/my-app:latest
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
4.3 base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
type: ClusterIP
4.4 base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- hpa.yaml
4.5 overlays/production/kustomization.yaml
这是 CI 会自动修改的文件:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
resources:
- ../../base
- ingress.yaml
images:
- name: 123456789.dkr.ecr.ap-southeast-1.amazonaws.com/my-app
newTag: abc1234f # ← CI 自动更新这里
replicas:
- name: my-app
count: 3 # 生产环境 3 副本
patches:
- patch: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
Kustomize 的好处是:base 里写通用配置,overlay 里只写差异。staging 可能只要 1 个副本和更少的资源,production 要 3 个副本和更多资源。改一处 base,所有环境自动继承。
五、EKS 节点拉 ECR 镜像的权限
这一步很多人会忘,然后 Pod 一直 ImagePullBackOff,对着屏幕挠头半小时。
EKS 节点要从 ECR 拉镜像,Node Group 的 IAM Role 必须有 ECR 读取权限。如果你用 Terraform 管理 EKS,确保 Node Role 挂了这个策略:
# 节点 IAM Role 的 ECR 权限
resource "aws_iam_role_policy_attachment" "node_ecr" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
role = aws_iam_role.eks_node.name
}
# 如果是跨账号拉镜像(镜像在账号 A,EKS 在账号 B),需要额外配置
# ECR 仓库侧(账号 A)添加 Repository Policy:
resource "aws_ecr_repository_policy" "cross_account" {
repository = aws_ecr_repository.app.name
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "AllowCrossAccountPull"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::ACCOUNT_B_ID:root"
}
Action = [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability"
]
}]
})
}
验证节点能不能拉镜像:
# 跑一个测试 Pod
kubectl run ecr-test \
--image=123456789.dkr.ecr.ap-southeast-1.amazonaws.com/my-app:test \
--restart=Never
# 看状态
kubectl get pod ecr-test -w
# 如果一直 ImagePullBackOff,看详细错误
kubectl describe pod ecr-test
# 常见错误:
# "no basic auth credentials" → Node Role 没有 ECR 权限
# "manifest unknown" → 镜像 tag 不存在
# "repository does not exist" → ECR 仓库名写错了
# 测试完清理
kubectl delete pod ecr-test
六、ArgoCD:保安队长上岗
终于到主角了。ArgoCD 是一个 Kubernetes 原生的 GitOps 持续交付工具。它做的事情很纯粹:盯着 Git 仓库,发现变化就同步到集群。就像一个强迫症保安,每隔 3 分钟检查一次门锁有没有跟钥匙对上。
6.1 安装 ArgoCD 到 EKS
# 创建 namespace
kubectl create namespace argocd
# 安装 ArgoCD(用官方 stable manifest)
kubectl apply -n argocd -f \
https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# 等待所有 Pod 就绪
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/part-of=argocd \
-n argocd --timeout=300s
# 查看安装结果
kubectl get pods -n argocd
# 应该看到:
# argocd-server-xxx Running
# argocd-repo-server-xxx Running
# argocd-application-controller-xxx Running
# argocd-redis-xxx Running
# argocd-dex-server-xxx Running
6.2 暴露 ArgoCD UI
生产环境推荐用 Ingress + TLS,这里先用 LoadBalancer 快速验证:
# 方式一:改 Service 类型为 LoadBalancer
kubectl patch svc argocd-server -n argocd \
-p '{"spec": {"type": "LoadBalancer"}}'
# 获取访问地址
kubectl get svc argocd-server -n argocd
# EXTERNAL-IP 列就是你的访问地址
# 方式二:本地端口转发(开发测试用)
kubectl port-forward svc/argocd-server -n argocd 8443:443
# 然后浏览器打开 https://localhost:8443
6.3 获取初始密码并登录
# 初始密码存在 secret 里
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d
# 用户名:admin
# 密码:上面命令的输出
# 安装 ArgoCD CLI(可选但推荐)
# macOS
brew install argocd
# 登录
argocd login <ARGOCD_SERVER> --username admin --password <PASSWORD>
# 第一件事:改密码!
argocd account update-password
6.4 连接 GitLab 仓库
ArgoCD 需要能访问你的 manifest 仓库。如果是私有仓库,需要配置 SSH 密钥:
# 方式一:CLI 添加
argocd repo add git@gitlab.com:myteam/my-app-manifests.git \
--ssh-private-key-path ~/.ssh/argocd_deploy_key
# 方式二:通过 K8s Secret(推荐,可以用 Terraform/Helm 管理)
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: gitlab-repo-creds
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repository
stringData:
type: git
url: git@gitlab.com:myteam/my-app-manifests.git
sshPrivateKey: |
-----BEGIN OPENSSH PRIVATE KEY-----
你的私钥内容
-----END OPENSSH PRIVATE KEY-----
EOF
6.5 创建 ArgoCD Application
这是把所有东西串起来的最后一步。Application 告诉 ArgoCD:监听哪个仓库、哪个路径、同步到哪个集群的哪个 namespace。
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app-production
namespace: argocd
# 可选:加入 ArgoCD 项目进行权限管理
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: git@gitlab.com:myteam/my-app-manifests.git
targetRevision: main
path: overlays/production # Kustomize overlay 路径
destination:
server: https://kubernetes.default.svc # 部署到当前集群
namespace: production
syncPolicy:
automated:
prune: true # 自动删除 Git 里已移除的资源
selfHeal: true # 有人手动 kubectl edit?自动恢复
allowEmpty: false # 防止误删所有资源
syncOptions:
- CreateNamespace=true # namespace 不存在就自动创建
- PrunePropagationPolicy=foreground
- PruneLast=true # 先创建新资源,再删除旧的
retry:
limit: 3
backoff:
duration: 5s
factor: 2
maxDuration: 3m
# 应用配置
kubectl apply -f argocd-application.yaml
# 或者用 CLI
argocd app create my-app-production \
--repo git@gitlab.com:myteam/my-app-manifests.git \
--path overlays/production \
--dest-server https://kubernetes.default.svc \
--dest-namespace production \
--sync-policy automated \
--auto-prune \
--self-heal
几个关键配置解释:
- selfHeal: true:如果有人手贱用 kubectl 直接改了集群里的东西,ArgoCD 会自动恢复成 Git 里的状态。这就是 GitOps 的灵魂——Git 是唯一真相
- prune: true:如果你从 Git 里删了一个 Service,ArgoCD 会自动从集群里也删掉。不开这个的话,删除操作需要手动处理
- PruneLast: true:更新时先创建新版本,确认就绪后再删旧版本,避免服务中断
七、完整流程验证:从 push 到上线
配置都搞完了,来走一遍完整流程,确保每个环节都通:
# 1. 改点代码,push 到 main
git add .
git commit -m "feat: add /api/v2/users endpoint"
git push origin main
# 2. 去 GitLab 看 CI 流水线
# Pipeline 应该依次执行:test → build → deploy
# build 阶段日志里应该看到:
# "构建镜像 123456789.dkr.ecr.../my-app:abc1234f"
# "镜像推送完成"
# deploy 阶段日志里应该看到:
# "Manifest 仓库已更新,等待 ArgoCD 同步"
# 3. 检查 manifest 仓库
cd /tmp && git clone git@gitlab.com:myteam/my-app-manifests.git
cat my-app-manifests/overlays/production/kustomization.yaml
# newTag 应该已经变成最新的 commit SHA
# 4. 看 ArgoCD 同步状态
argocd app get my-app-production
# Status: Synced
# Health: Healthy
# 如果显示 OutOfSync,等 3 分钟或手动触发:
argocd app sync my-app-production
# 5. 验证 Pod 是否更新
kubectl get pods -n production -l app=my-app -o wide
kubectl describe pod -n production -l app=my-app | grep Image
# Image 应该是最新的 tag
# 6. 验证服务可用
kubectl port-forward svc/my-app -n production 8080:80
curl http://localhost:8080/api/v2/users
# 能正常返回就说明整条流水线跑通了
如果一切顺利,从你 push 代码到新版本上线,整个过程大约 5-8 分钟(构建 2-3 分钟 + ArgoCD 同步 3 分钟)。而且全程不需要你做任何操作。
八、回滚:GitOps 最爽的部分
传统部署回滚:找到上一个版本号 → 登录 Jenkins → 找到对应的 Job → 点 Build → 祈祷。
GitOps 回滚:
# 方式一:Git revert(推荐,有完整审计记录)
cd my-app-manifests
git log --oneline -5
# abc1234 chore: update image to def5678f
# 9876543 chore: update image to abc1234f ← 回滚到这个
git revert HEAD
git push origin main
# ArgoCD 自动同步,3 分钟内回滚完成
# 方式二:ArgoCD CLI 回滚到历史版本
argocd app history my-app-production
# ID DATE REVISION
# 3 2025-02-25 10:30:00 abc1234
# 2 2025-02-24 15:20:00 9876543 ← 回滚到这个
argocd app rollback my-app-production 2
# 方式三:ArgoCD UI 上点点点
# 打开 ArgoCD UI → 选择 Application → History → 选版本 → Rollback
三种方式,最推荐第一种。因为 git revert 会在 Git 历史里留下记录,谁在什么时候回滚了什么版本,一目了然。方式二和三虽然快,但绕过了 Git,审计记录不完整。
九、踩坑指南:我替你踩过的坑
搭这套东西的过程中,我踩了不少坑。把它们列出来,希望你能少走弯路。
坑 1:ECR Token 12 小时过期
ECR 的登录 token 只有 12 小时有效期。如果 GitLab Runner 缓存了旧 token,构建会报 no basic auth credentials。
# 解决:每次构建都重新登录,不要缓存 token
before_script:
- aws ecr get-login-password --region $AWS_REGION |
docker login --username AWS --password-stdin $ECR_REGISTRY
# 每次都执行,不依赖缓存
坑 2:ArgoCD 同步超时
大型应用(几十个资源)同步时可能超时,默认超时是 3 分钟。
# 在 Application 里加超时配置
spec:
syncPolicy:
syncOptions:
- ServerSideApply=true # 大资源用服务端 Apply,更快更稳
retry:
limit: 5
backoff:
duration: 10s
factor: 2
maxDuration: 5m
坑 3:Kustomize 版本不匹配
ArgoCD 内置的 Kustomize 版本可能跟你本地的不一样,导致本地 kustomize build 没问题但 ArgoCD 报错。
# 查看 ArgoCD 用的 Kustomize 版本
kubectl exec -n argocd deploy/argocd-repo-server -- kustomize version
# 确保本地用同一版本测试
kustomize version
坑 4:Git commit message 里的 [skip ci] 不生效
CI 更新 manifest 仓库时,commit message 里加了 [skip ci],但 manifest 仓库也配了 CI 导致无限循环。
# 解决:manifest 仓库的 .gitlab-ci.yml 加规则
workflow:
rules:
- if: $CI_COMMIT_MESSAGE =~ /\[skip ci\]/
when: never
- when: always
坑 5:ImagePullBackOff 排查清单
# 按顺序排查:
# 1. 镜像存在吗?
aws ecr describe-images --repository-name my-app \
--image-ids imageTag=abc1234f
# 2. Node Role 有 ECR 权限吗?
# 检查 Node Group 的 IAM Role 是否挂了 AmazonEC2ContainerRegistryReadOnly
# 3. 跨 Region 了吗?
# ECR 是 Region 级别的服务,确保 EKS 和 ECR 在同一个 Region
# 4. 跨账号了吗?
# 如果是,需要在 ECR 仓库添加 Repository Policy(前面讲过)
# 5. VPC Endpoint 配了吗?
# 如果 EKS 节点在私有子网且没有 NAT Gateway,需要配 ECR VPC Endpoint
坑 6:ArgoCD 检测不到变化
# 可能原因:
# 1. webhook 没配,只能等 3 分钟轮询
# 2. 仓库凭证过期
# 3. path 配错了
# 手动触发同步
argocd app sync my-app-production
# 查看 ArgoCD 的仓库连接状态
argocd repo list
# 强制刷新
argocd app get my-app-production --refresh
十、生产环境加固
上面的配置能跑起来,但要上生产还需要一些加固。
10.1 ArgoCD Webhook(实时同步)
默认 ArgoCD 每 3 分钟轮询一次 Git 仓库。配置 Webhook 后可以实现秒级同步:
GitLab Webhook 配置:
URL: https://argocd.yourdomain.com/api/webhook
Secret Token: 你设置的密钥
Trigger: Push events
Branch: main
# ArgoCD ConfigMap 添加 webhook 密钥
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-secret
namespace: argocd
data:
webhook.gitlab.secret: "你的webhook密钥"
10.2 通知:部署成功/失败告警
ArgoCD Notifications 可以在同步成功或失败时发通知到 Slack、钉钉、企业微信等:
# 安装 ArgoCD Notifications
kubectl apply -n argocd -f \
https://raw.githubusercontent.com/argoproj-labs/argocd-notifications/release-1.0/manifests/install.yaml
# 配置 Slack 通知(ConfigMap)
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-notifications-cm
namespace: argocd
data:
service.slack: |
token: $slack-token
trigger.on-sync-succeeded: |
- when: app.status.sync.status == 'Synced'
send: [app-sync-succeeded]
trigger.on-sync-failed: |
- when: app.status.sync.status == 'Unknown'
send: [app-sync-failed]
template.app-sync-succeeded: |
slack:
attachments: |
[{
"color": "#18be52",
"title": "[OK] {{.app.metadata.name}} 部署成功",
"text": "版本: {{.app.status.sync.revision}}\n环境: production"
}]
template.app-sync-failed: |
slack:
attachments: |
[{
"color": "#E96D76",
"title": "[FAIL] {{.app.metadata.name}} 部署失败",
"text": "请检查 ArgoCD 控制台"
}]
10.3 RBAC:多团队权限管理
# ArgoCD RBAC ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
data:
policy.csv: |
# 开发团队:只能看和同步自己的应用,不能删除
p, role:developer, applications, get, */*, allow
p, role:developer, applications, sync, */*, allow
# 运维团队:完全控制
p, role:ops, applications, *, */*, allow
p, role:ops, clusters, *, *, allow
p, role:ops, repositories, *, *, allow
# 绑定 GitLab 组到角色
g, myteam:developers, role:developer
g, myteam:ops, role:ops
10.4 多环境管理:ApplicationSet
如果你有 staging、production、canary 多个环境,一个个创建 Application 太累。用 ApplicationSet 一次搞定:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: my-app-envs
namespace: argocd
spec:
generators:
- list:
elements:
- env: staging
replicas: "1"
autoSync: "true"
- env: production
replicas: "3"
autoSync: "true"
template:
metadata:
name: 'my-app-{{env}}'
spec:
project: default
source:
repoURL: git@gitlab.com:myteam/my-app-manifests.git
targetRevision: main
path: 'overlays/{{env}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{env}}'
syncPolicy:
automated:
prune: true
selfHeal: true
十一、监控:别让流水线裸奔
流水线搭好了不代表万事大吉。没有监控的流水线就像没有仪表盘的车,跑着跑着翻了都不知道。
ArgoCD 自带的监控指标
ArgoCD 暴露了 Prometheus 格式的 metrics,直接接入你的监控体系:
# ServiceMonitor(如果你用 Prometheus Operator)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: argocd-metrics
namespace: argocd
spec:
selector:
matchLabels:
app.kubernetes.io/part-of: argocd
endpoints:
- port: metrics
interval: 30s
关键告警规则
# PrometheusRule
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: argocd-alerts
namespace: argocd
spec:
groups:
- name: argocd
rules:
# 应用同步失败超过 10 分钟
- alert: ArgocdAppSyncFailed
expr: |
argocd_app_info{sync_status="OutOfSync"} == 1
for: 10m
labels:
severity: critical
annotations:
summary: "ArgoCD 应用 {{ $labels.name }} 同步失败超过 10 分钟"
# 应用健康状态异常
- alert: ArgocdAppUnhealthy
expr: |
argocd_app_info{health_status!="Healthy"} == 1
for: 5m
labels:
severity: warning
annotations:
summary: "ArgoCD 应用 {{ $labels.name }} 健康状态异常: {{ $labels.health_status }}"
总结
回顾一下我们搭建的这条流水线:
| 步骤 | 做了什么 | 核心配置 |
| 1. ECR | 创建镜像仓库 + 生命周期策略 | Terraform aws_ecr_repository |
| 2. GitLab CI | 构建镜像 → 推 ECR → 更新 manifest | .gitlab-ci.yml 三阶段 |
| 3. Manifest 仓库 | Kustomize 管理多环境配置 | base + overlays 结构 |
| 4. EKS 权限 | Node Role 挂 ECR 读取策略 | AmazonEC2ContainerRegistryReadOnly |
| 5. ArgoCD | 安装 + 连仓库 + 创建 Application | Application YAML + syncPolicy |
| 6. 加固 | Webhook + 通知 + RBAC + 监控 | 各种 ConfigMap |
整条链路的精髓在于关注点分离:开发只管写代码和 push,CI 只管构建和更新配置,ArgoCD 只管同步集群。每个环节各司其职,出了问题也容易定位。
GitOps 不是银弹,但它确实解决了传统部署的几个核心痛点:部署不可追溯、回滚困难、环境漂移、权限混乱。如果你的团队还在用 ssh 到服务器 → git pull → 重启 的方式部署,是时候升级了。
毕竟,凌晨三点的电话,谁都不想接。
留言板
留言提交后需管理员审核通过才会显示