ArgoCD 实战:用 GitOps 把 CI/CD 部署搬进 Kubernetes
从安装、Application 声明到 App of Apps 与 CI 配合,把 GitOps 部署体系在 Kubernetes 上落地,并复盘真实事故。
上一篇文章里我们用 GitHub Actions 直接 kubectl set image 发布,那是典型的 push 模式:CI 拿着集群权限,把变更推给集群。这套流程的问题在于——发布完就没人再管集群了。昨天谁手动 kubectl apply 改了个副本数?Git 里写的还是不是线上真实状态?没人说得清。GitOps 换个思路:Git 是唯一事实来源,集群里有个控制器持续盯着「Git 里声明的」和「集群里实际的」,不一致就拉平。ArgoCD 就是这件事在 Kubernetes 上的标准实现。
#GitOps:pull 和 push 到底差在哪
- push 模式:CI 构建镜像 → 用 kubeconfig 直接改集群。缺点:kubeconfig 要分发给 CI,权限面大;发布之后状态漂移没人发现,全靠人肉巡检。
- pull 模式:ArgoCD 的 controller 在集群内主动拉取 Git 仓库,对比期望状态和实际状态,默认每 3 分钟一次,有差异就自动同步。CI 完全不需要集群权限。
一句话:CI 负责把镜像做好,ArgoCD 负责让集群长成 Git 里写的样子。我把这套架构跑了一年多,最直观的感受是:排查「线上为什么和预期不一样」的时间,从小时级降到了分钟级——答案几乎都是「Git 里就是这么写的,你只是没看 Git」。
#安装与登录
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
kubectl get pods -n argocd -w装完会起四个核心组件:
argocd-server:API、Web UI、CLI 后端,也是登录入口。argocd-repo-server:负责拉取和缓存 Git 仓库内容,Application 的渲染结果都从它这里来。argocd-application-controller:核心引擎,持续比对 Git 与集群,执行同步。argocd-redis:缓存 Application 状态和锁,controller 和 server 都要用,别随手删。
登录第一步是拿初始密码,它存在 argocd-initial-admin-secret 里;第一次登录改密之后,这个 secret 就消失了:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d; echo
kubectl -n argocd port-forward svc/argocd-server 8080:443
argocd login 127.0.0.1:8080 --insecure
argocd account update-password生产环境别用 port-forward 裸奔:接 Ingress + HTTPS,再用 SSO(GitHub/OIDC)做登录,admin 账号平时不启用。
#注册仓库:私有仓库的凭据要收敛
ArgoCD 需要读你的 Git 仓库。公开仓库直接 argocd repo add;私有仓库推荐 HTTPS + 只读 PAT,或者 SSH key:
argocd repo add https://github.com/acme/manifests.git --username git --password ghp_xxx # PAT 只开 Contents: Read
argocd repo listPAT 权限务必收窄——repo-server 拿到的是能读整个仓库的凭据,谁拿到它谁就能看到仓库里所有文件。我见过有人把带 repo 写权限的 PAT 配进去,等于给 ArgoCD 开了一个能改代码的后门,完全没必要。
#第一个 Application:声明「从哪来、到哪去」
Application 是 ArgoCD 的核心对象,一条声明写清楚「仓库 + 路径 → 集群 + namespace」:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: demo-api
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/acme/manifests.git
targetRevision: main
path: apps/demo-api
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true三个字段拆开理解:
source:从哪个仓库、哪个分支、哪个路径取期望状态。destination:同步到哪个集群、哪个 namespace。https://kubernetes.default.svc就是当前集群(in-cluster)。syncPolicy.automated:自动同步三件套——sync自动拉平差异、selfHeal把手工改动拉回 Git 状态、prune删除 Git 里已不存在的资源。
用 kubectl 提交,或者用 CLI 操作:
kubectl apply -f application-demo-api.yaml
argocd app list
argocd app get demo-api # 看 Sync Status 和 Health
argocd app sync demo-api # 手动触发同步
argocd app history demo-api # 每次同步对应一个 Git revision
argocd app rollback demo-api 2 # 回滚到历史 revisionapp get 输出里我最常盯两个字段:Sync Status(OutOfSync / Synced)和 Health(Healthy / Degraded)。OutOfSync 说明 Git 和集群不一致,Degraded 说明应用本身有问题——这是两种完全不同的事故,别混着查。
#App of Apps:仓库结构决定可维护性
在一个 Application 里塞 50 个路径,UI 会乱到没法看。标准做法是「App of Apps」:一个根 Application 只管 apps/ 目录下的 Application 清单,每个子 Application 再管自己的资源目录:
manifests/
├── apps/ # 根 Application 管这里
│ ├── demo-api.yaml
│ └── monitoring.yaml
└── demo-api/ # 子 Application 管这里
├── deployment.yaml
└── service.yaml新增一个应用 = 在 apps/ 里加一个文件,根 Application 同步后,子 Application 自动出现。这套结构跑熟之后,整个集群的「部署拓扑」都写在 Git 里,任何人 git log 就能看懂发布历史。
#CI 与 ArgoCD 的分工:CI 只改 Git,不碰集群
和 GitHub Actions 直接 kubectl set image 不同,GitOps 下 CI 的最后一个动作是更新 manifest 仓库里的镜像 tag 并提交:
- name: Update image tag in gitops repo
run: |
cd manifests
sed -i "s|image: ghcr.io/acme/demo-api:.*|image: ghcr.io/acme/demo-api:${{ github.sha }}|" demo-api/deployment.yaml
git config user.name "ci-bot"
git config user.email "ci-bot@example.com"
git commit -am "release demo-api ${{ github.sha }}"
git pushArgoCD 默认 3 分钟轮询一次;想秒级触发,在 GitHub 仓库配 webhook(/api/webhook),有 push 事件立刻同步。这样做的收益:CI 不再持有集群权限,镜像 tag 和线上版本一一对应(argocd app history 里每条都有 commit),出问题回滚就是改 Git,或者 argocd app rollback 一条命令。
#我踩过的坑
- 手工改动被 selfHeal 无情拉回。有人上线时手滑
kubectl scale改了副本数,ArgoCD 三分钟内拉回 Git 状态,他以为是灵异事件。其实这是 selfHeal 在正常工作——集群里的手工改动在 GitOps 里不算数,改配置请走 Git。 - 没开 prune,删除变幽灵。Git 里删了某个 Service,但 Application 没开
prune,资源留在集群里继续被访问,域名解析到一个已废弃的服务上。排查了半天才想起来是孤儿资源。 - 健康检查卡在 Progressing。新版本 Deployment 探针配错,Pod 永远不就绪,ArgoCD 显示 Degraded 但不报错,因为健康状态取决于 Deployment 的进展。这种问题去查
kubectl describe pod的 Events,跟普通排障流程没区别。 - 镜像 tag 用 latest。ArgoCD 认为 Git 没变就不同步,但镜像内容变了,集群里还是旧镜像。GitOps 下镜像 tag 必须是不可变的(用 commit SHA),否则 Git 状态和实际运行对不上,整个 GitOps 的信任就塌了。
#经验
GitOps 不是装个 ArgoCD 就完事,它是一套纪律:Git 是唯一事实来源,集群状态靠自动同步维持,环境差异用 kustomize/Helm 的 overlay 表达,而不是复制一堆 YAML。我的建议是先在非生产跑熟 selfHeal + prune,同时把 webhook、SSO、RBAC 配齐,再让生产环境切过去。等哪天线上出问题,你 git log 就能看到是谁、在什么时候、改了哪个文件导致——这个可追溯性,是 ArgoCD 给我最大的回报。
写于 2026 年 8 月 23 日
- 栏目
- 技术文章
- 约
- 5.8 分钟
- 字数
- 4.3K
- 阅读
- 75
本文为原创记录,转载请注明出处。如果这篇替你省了时间,欢迎留言说说你踩到的坑。
同题 · related
留言 · remarks
00 条还没有留言,来说点什么吧。