teg 从手动部署到GitOps只需四步

葫芦的运维日志

浏览量 28 2026/02/25 18:06

从手动部署到 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:GetAuthorizationTokenecr:BatchCheckLayerAvailabilityecr: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 → 重启 的方式部署,是时候升级了。

毕竟,凌晨三点的电话,谁都不想接。

葫芦的运维日志

打赏

留言板

留言提交后需管理员审核通过才会显示

© 冰糖葫芦甜(bthlt.com) 2025 王梓打赏联系方式陕ICP备17005322号-1