Kubernetes GitOps 持续交付实战:用 Argo CD 让 Git 成为集群事实源

一、引言:部署成功之后,集群为什么还是"不可复制"

前面的文章已经解决了"怎么把应用部署进 Kubernetes":手写 YAML 能理解对象关系,Helm 能模板化单个服务,Helmfile 能组织多服务栈。但生产团队很快会遇到另一类问题:

  • 测试环境改过的参数,生产环境是不是也改了?
  • 某次故障时有人临时 kubectl edit,一周后谁还记得?
  • Git 里写着 3 个副本,集群里为什么实际是 8 个?
  • 新集群要恢复同一套系统,究竟该以文档、终端历史,还是某个同事的本地目录为准?
  • 一次发布涉及多个对象,如何审计谁改了什么、如何可靠回退?
    这些问题的根源,不是缺少部署工具,而是缺少持续一致的目标状态管理机制。GitOps 给出的答案是:把系统期望状态声明在 Git 中,由集群内的控制器持续比较并收敛实际状态。
    本文以 Argo CD 为例,完成一条可复用的持续交付链路。
    开篇核心认知:GitOps 不是"把 kubectl apply 搬到 CI 里执行",而是拉取式、声明式、持续 reconcile。CI 负责构建与验证,Git 保存目标状态,Argo CD 负责让集群持续接近这个状态。
    二、原理解析:Argo CD 如何把 Git 变成集群事实源
    2.1 Push 与 Pull:交付模型的根本变化
    传统 CI/CD 常采用 Push 模式:
    代码提交 → CI 构建 → CI 持有集群凭据 → kubectl/Helm 修改生产集群
    GitOps 通常采用 Pull 模式:
    代码提交 → CI 构建/测试/推送镜像 → 更新 Git 中的镜像版本
    │
    ▼
    Argo CD 拉取声明
    │
    ▼
    比较并收敛 Kubernetes 状态
    二者最关键的差别是:
  • CI 不必长期持有生产集群的高权限凭据;
  • 集群侧控制器持续运行,而不是只在流水线执行时工作;
  • 人工绕过 Git 的修改可以被识别为漂移;
  • 所有变更天然具备提交记录、评审记录和回滚入口。
    边界必须说清:Argo CD 是持续交付与状态协调工具,不负责编译代码、运行单元测试或构建镜像,这些仍属于 CI。
    2.2 GitOps 的四个基本约束
  1. 声明式:描述 Deployment、Service、 replicas 等目标状态,而不是记录一串操作步骤。
  2. 版本化且不可变:目标状态进入 Git,有明确提交、评审与历史。
  3. 自动拉取:集群中的代理主动获取已批准的目标状态。
  4. 持续协调:不断比较目标与实际,发现偏差后提示或修复。
    因此,Git 中保存的是"想要集群成为什么样",而不是"今天早上执行过哪些命令"。
    2.3 Argo CD 的核心对象与组件
    名称
    作用
    运维关注点
    Application
    一个应用及其 Git 来源、目标集群、同步策略
    最基本的交付单元
    AppProject
    限制可用仓库、目标集群/命名空间、资源类型和角色
    多租户边界,不要所有应用都用 default
    ApplicationSet
    按规则批量生成 Application
    多环境、多集群、目录型应用
    Repository
    Git/Helm/OCI 等来源及凭据
    私有仓库凭据与最小权限
    application-controller
    持续比较、同步、健康评估
    核心 reconcile 控制器
    repo-server
    拉取仓库并渲染 Helm/Kustomize 等清单
    仓库访问与模板渲染问题先看它
    API Server / UI / CLI
    提供操作入口与可视化
    登录、RBAC、审计和日常操作
    一个 Application 的主线可以概括为:
    Git 仓库 + 路径 + 修订版本
    │
    ▼
    渲染 Kubernetes 清单
    │
    ▼
    Desired State(期望) ←→ Live State(实际)
    │
    ▼
    Sync 状态 + Health 状态 + 必要时执行同步
    2.4 Sync 与 Health:两个维度不能混看
  • Sync 状态:Synced 或 OutOfSync,描述 Git 与集群声明是否一致。
  • Health 状态:Healthy、Progressing、Degraded 等,描述工作负载是否健康运行。
    一个应用完全可能 Synced + Degraded:YAML 已正确同步,但镜像拉取失败或探针不通过。也可能 OutOfSync + Healthy:集群被人改了副本数,但应用仍可正常服务。
    排障时先问"是不一致,还是不健康",再走不同路径,不要看到红叉就只点 Sync。
    2.5 自动同步的三个关键开关
    配置
    含义
    主要风险
    automated
    发现 Git 变更后自动同步
    未充分评审的提交会快速进入集群
    selfHeal: true
    集群声明偏离 Git 时自动改回
    会覆盖合法的临时运维调整
    prune: true
    Git 删除资源后,同步时删除集群对应资源
    配错来源或路径可能造成真实资源删除
    生产环境建议按风险分级:开发环境可全自动,生产环境可采用自动同步但人工审批关键变更,或先关闭 prune,待流程成熟后再逐步开启。
    2.6 同步顺序、Hook 与回滚边界
    复杂应用通常不只是 Deployment:
  • CRD 往往要先于自定义资源创建;
  • 数据库迁移可能要在新版本启动前执行;
  • ServiceAccount、RBAC、ConfigMap、工作负载存在先后依赖;
  • 某些资源同步后还需要健康检查或验证。
    Argo CD 提供 Sync Wave、PreSync/Sync/PostSync Hook 等机制控制顺序,但不要把 GitOps 回滚理解成数据库时间机器:
  • git revert 能恢复 Kubernetes 声明;
  • 它不能自动撤销不兼容的数据库 schema;
  • 它不会把 PV 里的数据恢复到旧版本;
  • 有状态变更仍需独立迁移、备份与恢复方案。
    2.7 配置与 Secret 的交付边界
    GitOps 不等于"所有内容都明文放 Git"。密码、令牌和私钥不应直接提交。常见方案包括:
  • External Secrets:从 Vault、云 Secret Manager 等同步;
  • Sealed Secrets:将只能由目标集群解密的密文放 Git;
  • SOPS + KMS:加密文件进入 Git,集群侧解密;
  • CI 渲染敏感值:可用,但要严格防止日志泄露与权限扩大。
    另外,Argo CD 的仓库凭据、SSO/RBAC、项目边界和审计,与"应用如何同步"同样重要。
    三、实战演示:从 Git 部署电商前端并持续收敛
    3.1 场景与目标
    部署一个 shop-web 前端,开发环境声明保存在 Git 的 overlays/dev:
  1. Argo CD 从 Git 渲染并创建应用;
  2. 修改 Git 副本数后自动发布;
  3. 手工扩容造成漂移,由 Argo CD 自动修复;
  4. 从 Git 删除资源后验证 prune;
  5. 用 git revert 回滚应用声明。
    前置:可用 Kubernetes 集群、Git 仓库、已配置推送权限。以下 repoURL 必须替换成你的真实仓库地址。实验可使用 kind/minikube,生产请独立规划高可用、权限和备份。
    3.2 步骤一:安装 Argo CD 并登录
    kubectl create namespace argocd
    kubectl apply -n argocd
    -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

kubectl wait --for=condition=Available deployment/argocd-server

-n argocd --timeout=300s

kubectl get pods -n argocd

将界面转发到本地:

kubectl port-forward svc/argocd-server -n argocd 8080:443

另开终端获取初始管理员密码:

kubectl get secret argocd-initial-admin-secret -n argocd

-o jsonpath='{.data.password}' | base64 -d

echo

浏览器访问 https://localhost:8080,用户名为 admin。生产环境应立即接入正式身份系统、修改初始凭据,并限制端口转发与网络暴露。

3.3 步骤二:准备 Git 仓库与 Kustomize 配置

仓库结构:

shop-gitops/

├── base/

│ ├── deployment.yaml

│ ├── service.yaml

│ └── kustomization.yaml

└── overlays/

└── dev/

└── kustomization.yaml

base/deployment.yaml:

apiVersion: apps/v1

kind: Deployment

metadata:

name: shop-web

spec:

replicas: 1

selector:

matchLabels:

app: shop-web

template:

metadata:

labels:

app: shop-web

spec:

containers:

  • name: web

image: nginx:1.27-alpine

ports:

  • containerPort: 80

resources:

requests:

cpu: 100m

memory: 128Mi

limits:

cpu: 500m

memory: 256Mi

readinessProbe:

httpGet:

path: /

port: 80

livenessProbe:

httpGet:

path: /

port: 80

base/service.yaml:

apiVersion: v1

kind: Service

metadata:

name: shop-web

spec:

selector:

app: shop-web

ports:

  • name: http

port: 80

targetPort: 80

base/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1

kind: Kustomization

resources:

  • deployment.yaml
  • service.yaml
    overlays/dev/kustomization.yaml:
    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    namespace: shop-dev
    resources:
  • .../.../base
    replicas:
  • name: shop-web
    count: 1
    推送前先本地渲染,避免把明显错误交给控制器:
    kubectl kustomize overlays/dev

git add .

git commit -m "feat: add shop web gitops manifests"

git push origin main

3.4 步骤三:创建 Application

shop-dev-application.yaml

apiVersion: argoproj.io/v1alpha1

kind: Application

metadata:

name: shop-dev

namespace: argocd

spec:

project: default

source:

repoURL: https://git.example.com/platform/shop-gitops.git # 替换

targetRevision: main

path: overlays/dev

destination:

server: https://kubernetes.default.svc

namespace: shop-dev

syncPolicy:

automated:

prune: true

selfHeal: true

syncOptions:

  • CreateNamespace=true

kubectl apply -f shop-dev-application.yaml

kubectl get application shop-dev -n argocd -w

等待状态变为 Synced / Healthy 后验证:

kubectl get pods,svc -n shop-dev

kubectl port-forward -n shop-dev svc/shop-web 8081:80

curl http://localhost:8081/

这里的 automated.prune: true 是为了完整演示。生产环境请先理解其删除语义,不要无审查地复制。

3.5 步骤四:通过 Git 发布副本变更

修改 overlays/dev/kustomization.yaml:

replicas:

  • name: shop-web
    count: 2
    提交并推送:
    git add overlays/dev/kustomization.yaml
    git commit -m "chore: scale dev shop web to two replicas"
    git push origin main
    Argo CD 默认会周期检查仓库,也可在界面点击 Refresh 加速发现。验证:
    kubectl get application shop-dev -n argocd
    kubectl get deployment shop-web -n shop-dev
    kubectl get pods -n shop-dev
    发布依据是一次 Git 提交,而不是某人的终端操作。
    3.6 步骤五:制造漂移,验证 Self-Heal
    绕过 Git,直接扩容到 5:
    kubectl scale deployment shop-web -n shop-dev --replicas=5
    kubectl get deployment shop-web -n shop-dev
    由于 selfHeal: true,Argo CD 会检测到实际副本数偏离 Git,并将其恢复为 2。再次查看:
    kubectl get application shop-dev -n argocd
    kubectl get deployment shop-web -n shop-dev
    这说明 GitOps 管的不是"一次部署",而是"持续一致"。如果确需临时扩容,应通过 Git 修改,或由明确排除在 GitOps 管理外的 HPA 控制对应字段。
    3.7 步骤六:理解 Prune 的删除语义
    假设 Git 中不再需要 Service,可从 base/kustomization.yaml 的 resources 删除 - service.yaml,并将仓库中该文件删除后推送:
    git rm base/service.yaml

同时编辑 base/kustomization.yaml,删除 service.yaml 引用

git add base/kustomization.yaml

git commit -m "chore: remove shop service"

git push origin main

因为启用了 prune: true,Argo CD 同步后会删除集群中的对应 Service。

危险提醒:误改 source.path、仓库分支或资源跟踪标签,可能让 Argo CD 认为大量资源"已从 Git 消失"。生产开启 prune 前,必须做好项目权限、目录保护、评审和恢复预案。

3.8 步骤七:用 Git 回滚,而不是 rollout undo

若刚才副本变更引入问题,应回退对应提交:

git revert

git push origin main

Argo CD 获取新提交后,将集群同步到恢复后的声明。这样 Git 历史同时保留"错误变更"和"回退变更",便于审计。

再次强调:这恢复的是声明,不会自动回滚数据库数据或已产生的外部副作用。

3.9 进阶:生产环境的最小项目边界

不要在多团队场景下让所有 Application 共用无限权限的 default 项目。可用 AppProject 限制来源与目标:

apiVersion: argoproj.io/v1alpha1

kind: AppProject

metadata:

name: shop-team

namespace: argocd

spec:

description: Shop team delivery boundary

sourceRepos:

destinations:

namespace: shop-*

clusterResourceWhitelist:

  • group: ""

kind: Namespace

namespaceResourceWhitelist:

  • group: ""
    kind: "
    "

实际生产还应进一步收敛可创建的命名空间资源,配合 Argo CD RBAC、SSO、Kubernetes RBAC 与准入策略,避免"GitOps 平台变成新的 cluster-admin 分发器"。

四、排错实战:按仓库、渲染、同步、健康四层定位

4.1 高频问题对照表

现象

优先根因

定位动作

处理方向

仓库无法拉取

URL/分支错误、凭据无效、网络或证书问题

查看 Application 条件与 repo-server 日志

修正来源与最小权限凭据

ComparisonError

Git 路径不存在、模板渲染失败

本地 kubectl kustomize / helm template

先让本地渲染通过

OutOfSync 长期不变

未开启自动同步、字段持续漂移

看 diff、同步策略和事件

手工 Sync 或修复漂移来源

Synced + Degraded

声明一致但工作负载异常

查 Pod Events、镜像、探针、日志

走常规应用排障,不是反复 Sync

SyncFailed

RBAC、准入拒绝、资源冲突、Hook 失败

看 Application 操作状态与 Events

按报错修复权限或资源

CRD 资源创建失败

CRD 与 CR 顺序不对

看同步波次和 CRD 建立状态

分 Application 或配置 Wave

副本数反复变化

HPA 与 Git 同时管理 replicas

看 HPA 与 Argo CD diff

明确字段唯一管理者

资源被意外删除

prune 或来源路径变更

看同步审计、Git 提交和 Application 来源

停止同步、恢复声明、收紧 prune

Namespace 卡在 Terminating

Finalizer/资源清理阻塞

查 namespace 与残留资源

先定位阻塞资源,不盲目强删

回滚后数据仍异常

把声明回滚误当数据回滚

查迁移与数据备份

执行独立数据恢复流程

4.2 常用诊断命令

应用整体状态

kubectl get application -n argocd

kubectl describe application shop-dev -n argocd

控制器与仓库渲染日志

kubectl logs deployment/argocd-application-controller -n argocd --tail=100

kubectl logs deployment/argocd-repo-server -n argocd --tail=100

本地复现模板渲染

kubectl kustomize overlays/dev

检查实际工作负载

kubectl get events -n shop-dev --sort-by=.lastTimestamp

kubectl describe deployment shop-web -n shop-dev

kubectl logs deployment/shop-web -n shop-dev --tail=100

4.3 一张排错决策树

text 复制代码
Application 异常
  │
  ├─ 仓库拉不到? ───────→ 查 URL、分支、凭据、网络、repo-server
  │
  ├─ 渲染失败? ─────────→ 本地 Kustomize/Helm 渲染复现
  │
  ├─ SyncFailed? ───────→ 查 RBAC、准入、Hook、CRD 顺序、资源冲突
  │
  ├─ OutOfSync? ────────→ 查 diff、自动同步、selfHeal、外部控制器争用
  │
  └─ Synced 但 Degraded? → 回到 Pod/镜像/探针/存储/网络常规排障
五、总结:GitOps 的核心不是工具,而是变更纪律
Argo CD 把"应用目标状态"从分散的本地目录和终端命令,收敛到可评审、可审计、可持续协调的 Git 工作流中。本篇实战覆盖了安装、Kustomize 应用接入、自动发布、漂移修复、Prune 和 Git 回滚,也划清了三条重要边界:
1. GitOps 不是 CI:构建、测试、镜像扫描仍由流水线承担;
2. Git 是声明事实源,不是运行时全部事实:健康状态、监控指标和数据内容仍在集群与外部系统;
3. 声明可回滚,不代表数据可回滚:有状态变更必须配套迁移与备份策略。
相关推荐
全栈陈序员2 小时前
【Git】从入门到精通(六)—— git commit:提交到版本库
大数据·git·elasticsearch
真上帝的左手2 小时前
30. 技术专题-‌应用框架组件-搜索引擎-ES
大数据·分布式·elasticsearch·搜索引擎·中间件
Elasticsearch2 小时前
两条规则,一次故障,一条告警:变点检测如何减少告警噪声
elasticsearch
无言听风雨丶11 小时前
Kubernetes 控制平面深度剖析:从 kubectl apply 到 etcd 写入的全链路
平面·kubernetes·etcd
openFuyao12 小时前
openFuyao x 复旦大学软件工程实验室 Panel 回顾:Agentic时代,集群调度技术体系将如何演进?
kubernetes·openfuyao
螺蛳粉 螺蛳粉13 小时前
第 05 篇:etcd 深入:Raft 共识、watch 机制与快照备份恢复实战(底座认知 05)
docker·云原生·容器·kubernetes·etcd·分布式存储·kube-apiserver
相思难忘成疾15 小时前
从零搭建 Kubernetes 1.30 集群:kubeadm 部署一主多从实战(RHEL 9.3 + Docker + flannel)
linux·运维·kubernetes
weirong16 小时前
给 K8s 加一个自动值班员:分级故障自愈引擎
kubernetes
云和数据.ChenGuang17 小时前
Python 闭包详解:从原理到实战
linux·开发语言·人工智能·python·kubernetes