第 22 章 GitOps 深度:Git 就是另一个 etcd

墨:老哥,第 3 章我们说 K8s 的核心循环是”期望状态 vs 实际状态,持续调和”。这章讲一个把这套哲学从集群内部,延伸到你们团队的工作流的实践——GitOps。先抛个问题:你们现在怎么把代码变成线上跑的服务?是不是”CI 构建完,脚本 kubectl apply 推上去”?那这”推”的动作,和 K8s 的”控制循环”哲学冲突吗?


22.1 传统 CI/CD 的”推”模式及其裂缝

经典流水线:代码推 Git → CI 构建镜像 → CI 跑测试 → CI 用 kubectl apply / helm install 把变更推到集群。

墨:这”推”的模式,哪里和 K8s 哲学拧着? K8s 是声明式、集群自调和(第 3 章):你只声明期望,集群自己收敛。但”CI 推一把”是命令式、一次性的——推完 CI 就撤了,它不持续盯着”集群现在是不是还和 Git 里一致”。于是裂缝出现了:

  • 漂移(drift):有人手动 kubectl edit 改了生产(救火),Git 里没记,Git 和集群不一致了,且没人知道;
  • 状态不透明:集群真实状态不在任何”单一真相源”里,散在 etcd + 各种手工改动中;
  • 回滚难:出问题想回滚,得回忆”上次 apply 的是哪个版本”,CI 历史不一定对应;
  • 权限扩散:CI 系统要有集群的写权限(且常是广义),CI 被攻破 = 集群被攻破。

墨:你看,这些问题像不像第 2.2 的”配置漂移”和”人肉维护状态的宿命”? 完全一样。传统 CI/CD 的”推”把”状态该一致”的责任甩回给人,人一忙就漂。GitOps 就是来治这个的。


22.2 GitOps 的核心:Git 作为”期望状态”的真相源

GitOps 的第一性原则:集群的期望状态,100% 存在 Git 里(YAML/Helm/Kustomize)。Git 是单一真相源(single source of truth)。 不是”文档里”、不是”某人脑子里”、不是”CI 变量里”——是 Git。

然后架构从”推”变”拉(pull)”:

  • 集群里跑一个 GitOps 控制器(Argo CD / Flux);
  • 它持续 watch Git 仓库(和 K8s 控制器 watch etcd 同构,第 3/4 章);
  • Git 里的 YAML 变了 → 控制器检测到 → 把它 apply 到集群,调和实际状态;
  • 同时它持续对比集群实际 vs Git 期望,发现漂移(集群被手动改了)→ 自动还原(reconcile 回 Git 的状态)。

墨:你品,这不就是 K8s 控制循环套了一层吗? 对!把”期望状态”从 etcd 挪到了 Git,把”调和者”从 kube-controller 变成 Argo CD,但范式一字不差:

K8s 原生:  期望(etcd) ─watch─> 控制器 ─apply─> 实际(集群) ─观测─> 比对─> 调和
GitOps:    期望(Git)  ─watch─> Argo CD ─apply─> 实际(集群) ─观测─> 比对─> 还原

Git 就是”集群外的 etcd”,Argo CD 就是”集群外的控制器”。 这就是本章标题的意思。GitOps 没发明新范式,它把 K8s 的范式复用到了”组织协作”这层。这也是为什么我前面说”好范式向外辐射”(第 21.3)——GitOps 是又一个证据。


22.3 漂移检测与自愈:手动改也会被还原

墨:有人说”生产急,我先 kubectl edit 改一下,回头补 Git”——但经常忘了补。GitOps 下会怎样?

GitOps 控制器每轮对比”Git 期望”和”集群实际”。你手动 edit 了集群(比如把副本数改成 5,Git 里是 3),控制器下一轮发现漂移 → 自动把集群改回 Git 里的 3。你的手动改动被”无声还原”。

墨:这会不会很吓人——我手动改的被覆盖了? 是的,但这是设计意图。GitOps 的纪律是:“想改集群,只能改 Git,别手动改。” 手动改会被还原,逼你走正确路径(改 Git → PR → 合并 → 自动同步)。这消除了”配置漂移”——集群永远 == Git。代价是:你不能再”临时手动改生产救火”(除非用 GitOps 的”暂停同步”功能临时冻住,改完再补 Git)。这是用”纪律换一致性”,和 K8s 声明式(第 3 章)用”自动闭环换自愈”同源。

22.3.1 和 K8s 自身控制循环的分工

注意:GitOps 控制器调和的是”Git→集群的期望”,K8s 控制器调和的是”集群期望→实际 Pod”。两层:

  • GitOps(Argo CD):保证”集群里的对象定义 == Git 里的定义”;
  • K8s(Deployment 等):保证”实际 Pod == 对象定义”。
  • 漂移发生在不同层:手动改 Deployment 的 replicas=5(Git 说 3)→ Argo CD 还原成 3 → K8s 再把 Pod 数调到 3。

两层控制循环串起来,最终”Git 定义 == 实际 Pod”。声明式范式在”组织→集群→运行时”三层链式生效。


22.4 多环境与渐进发布:Git 的分支/目录即环境

墨:dev/test/prod 三个环境,GitOps 怎么管? 用 Git 的结构代表环境:

  • 一个 repo 里 envs/dev/、envs/test/、envs/prod/ 三个目录,分别被三个 Argo CD 实例(或同一实例的不同 Application)watch;
  • 代码合到 dev 目录 → dev 集群更新;合到 prod 目录 → prod 更新;
  • 或者一个 Application 用 Kustomize overlay 区分环境(base + 各环境 patch)。

渐进发布(金丝雀)怎么做? 经典 GitOps 是”声明即真理”,但金丝雀要”逐步放量”。做法:

  • Argo Rollouts(第 7.4 提过)管”按权重逐步切流”,它的期望状态写在 Git;
  • 或 GitOps 控制器配”sync wave / 分析”:先 sync 一小部分,跑分析(如错误率没升),再 sync 剩余——把”渐进”也变成”声明 + 自动校验”。

墨:这比手动 kubectl rollout 好在哪? 好在所有环境的”当前状态”都在 Git 里可查、可 diff、可审计、可回滚。你不知道”生产现在跑的啥版本”?看 Git 的 prod 目录。想知道”上周三生产啥配置”?看 Git 历史。Git 成了集群的外部状态账本。


22.5 审计与回滚:Git 历史即审计

墨:出事故要复盘”谁、啥时候、改了啥”,GitOps 下怎么查? Git 的 commit 历史就是审计日志:谁 push 的、改了哪个 YAML、改前改后 diff、关联的任务单(commit message 里)。比翻 K8s 的 audit log(第 4.8)更贴近”业务意图”。

回滚更是 Git 的强项:git revert 上次提交,Argo CD 检测到 Git 变 → 自动把集群还原到旧版本。 回滚 = 一次 Git 操作,不是”凭记忆重新 apply 旧 YAML”。且因为 Git 是真相源,回滚后集群必然 == 回滚后的 Git,确定性回滚(不像手动 apply 可能漏文件)。

演化看点: “用版本控制系统当状态真相源 + 自动同步”的思想早就有(如 Puppet 的 master),但 GitOps 把它和 K8s 声明式、和 PR 工作流(code review)深度结合,形成现代实践。Argo CD/Flux 是 CNCF 毕业项目。这又是”声明式 + 控制循环”范式在”交付流程”层的胜利——你越理解第 3 章,越觉得 GitOps 理所当然。


22.6 安全:Git 权限 = 集群权限

墨:GitOps 下,集群的写权限在谁手里? 在 GitOps 控制器(Argo CD) 手里(它有集群的 SA token,第 4.3/13)。人类不再直接有集群写权限——你想改集群,改 Git,控制器去 apply。

这反转了传统 CI/CD 的权限模型:

  • 传统:CI 系统有集群写权限(风险大,CI 被攻=集群被攻);
  • GitOps:只有 Argo CD 有写权限,且它的行为是”严格按 Git 同步”(不能随意改)。人类通过 Git 的 PR 权限 + code review 间接控制集群。

好处: 攻击面从”CI 系统 + 一堆人的 kubeconfig”收敛到”Git 仓库 + Argo CD”。且 Git 的 access 是公司统一管(第 4.3 OIDC 同源)。密钥外置:Git 里不存 Secret 明文(第 12 章),用 Sealed Secrets / External Secrets(第 12.3.2),Git 只存引用。这把”Git 是真相源”和”密钥不在 Git”协调好了——Git 存所有声明的”形状”,秘密在 Vault/外部库。


22.7 GitOps 的代价与适用边界

墨:GitOps 这么好,有没有坑? 有:

  • Git 仓库成了单点:Git 挂了,没法同步(但集群继续跑,只是不能变更;且 Git 通常高可用);
  • 延迟:Git→集群有同步延迟(控制器轮询/watch Git),紧急变更比 kubectl apply 慢一点;
  • 密钥管理复杂:Secret 外置要配套(Sealed Secrets 等),初设成本高;
  • 大集群仓库膨胀:所有 YAML 都进 Git,仓库变大、PR 多;
  • 学习曲线:团队要接受”只能改 Git 不能手动改”的纪律,初期抵触。

适用边界: GitOps 适合长期运行、多环境、要强一致性和可审计的集群。一次性实验集群、纯 CI 推的临时环境,不一定值得。又是”能力 vs 复杂度”——GitOps 用复杂度换”一致性+审计+回滚确定性”,值不值看团队规模和合规要求(第 21.5 同构取舍)。


22.8 一道串联:GitOps 是全书哲学的集大成

你回看全书,GitOps 把多少章串起来了:

  • 声明式 + 控制循环(第 3):GitOps 直接复用;
  • API Server 的”唯一入口 + RBAC”(第 4):GitOps 控制器是唯一的集群写者,人走 Git+review;
  • 不可变配置/immutable(第 12.5):Git 里的 YAML 是不可变的真相,手动改被还原;
  • 密钥外置(第 12.3):Git 不存秘密;
  • Operator/CRD(第 14):Argo CD 自己就是个 Operator,watch Git(自定义源)调和;
  • 演化(第 18):GitOps 是”声明式范式从集群内延伸到组织流程”的演化,没改 K8s 一行核心。

墨:你发现没,GitOps 不是”新东西”,是”把本书第 3 章的哲学,坚持到了极致、推到了组织层”。 对。所以你学透第 3 章,GitOps 是顺理成章。这也是为什么我让你先立脊椎(03)——后面所有上层实践都是那根脊椎的生长。


22.9 本章演化线小结

  • 传统”推”式 CI/CD 有漂移/不透明/回滚难/权限扩散裂缝,是配置漂移的现代版;
  • GitOps:Git = 期望状态真相源,控制器 pull/watch Git 调和集群,同构 K8s 控制循环;
  • 漂移自愈:手动改被自动还原,用纪律换一致性;Git=集群外 etcd;
  • 两层循环:GitOps( Git→对象 ) + K8s( 对象→Pod ),链式和到实际;
  • 多环境用 Git 目录/overlay;渐进发布用 Argo Rollouts + 分析;
  • Git 历史即审计,回滚=git revert 确定性还原;权限收敛到 Git+review;密钥外置;
  • 代价:Git 单点/延迟/密钥复杂/仓库膨胀;是声明式哲学在组织层的集大成。

22.10 本章思考题

🔧 思考题 1

你们用传统 CI 推模式,生产被某人 kubectl edit 改了副本数救火但忘了补 Git,三个月后没人知道生产真实配置。改用 GitOps(Argo CD)后,同样的事会发生什么?如果这人”先改 Git 再合并”和”先手动改集群”两种路径,在 GitOps 下结果有何不同?

【参考答案】 GitOps 下:(1) 若”先手动改集群”(副本 3→5):Argo CD 每轮对比 Git(期望3) vs 集群(实际5),发现漂移 → 自动把集群还原成 3。救火效果被覆盖(除非临时 freeze 同步)。三个月后 Git 和集群仍一致(都是3),没有”无人知的漂移”——这正是 GitOps 要的。(2) 若”先改 Git 再合并”(PR 改期望 3→5,review 后合并):Argo CD 检测 Git 变 → 自动 apply 集群到 5。结果集群=5 且 Git=5,一致且可追溯(PR 记录谁、为啥)。两种路径对比:手动改被还原(不可持久、易引发”改了又没改”的困惑),改 Git 被采纳(持久、可审计)。GitOps 的纪律就是”唯一合法路径是改 Git”——手动改要么被还原、要么需配合 freeze 机制。这题考你理解 GitOps 如何用”自动还原漂移”消除配置漂移,和第 2.2/3 的”状态一致性”一脉相承。

🔧 思考题 2

GitOps 控制器(Argo CD)自己需要集群写权限(SA token)。如果 Argo CD 被攻破,后果多严重?对比传统 CI 推模式的”CI 系统有写权限”,哪种权限模型更安全?给出加固建议。

【参考答案】 Argo CD 被攻破 = 攻击者能通过它改集群任意对象(因为它有广泛写权限)→ 很严重。但对比传统 CI 推:(1) 传统模式里,CI 系统 + 所有有 kubeconfig 的人都有写权限,攻击面大(CI 被攻、某人 kubeconfig 泄露都中);(2) GitOps 模式里,写权限收敛到唯一的 Argo CD(且它的行为是”严格按 Git 同步”,不能直接乱改——攻击者要改集群得先改 Git 或攻 Argo CD 本身)。所以 GitOps 把”分散的写权限”收敛成”单一的、行为受控的写权限”,攻击面更小、且 Git 的 code review 是额外关卡。加固:(a) Argo CD 用专用 SA + 最小 RBAC(只够同步它管的命名空间,别给 cluster-admin);(b) Argo CD 自身高可用+强认证(OIDC);(c) Git 仓库强保护(branch protection、required review、签名 commit);(d) 秘密切外置(第 12.3,Argo CD 不持有真 Secret);(e) 用 Argo CD 的 RBAC 限制”谁能 sync 哪个 Application”。结论:GitOps 权限模型更安全(收敛+受控+审计),但 Argo CD 自身是高危目标,须重点防护。这题考”权限收敛”是安全核心(呼应第 4/13 的 RBAC 最小权限)。

🔧 思考题 3

你说”Git 是集群外的 etcd”。那么 Git 和 etcd 作为”状态真相源”有何本质异同?如果 Git 仓库丢了(没备份),和 etcd 丢了,恢复方式有何不同?

【参考答案】 同:(1) 都是期望状态的真相源,系统朝它收敛;(2) 都有版本历史(Git commit / etcd MVCC revision);(3) 都是”单一可信写入点”(Git 经 PR 合并、etcd 经 API Server)。异:(1) 层级:etcd 存”集群运行时对象的当前期望”(Deployment 定义),Git 存”集群应有的全部声明”(含这些对象 + 配置 + 跨环境);etcd 是 GitOps 同步的下游(Git→etcd→实际),Git 是更上游的真相源。(2) 人可读/编辑:Git 是人类可审阅的文本 + PR 流程,etcd 不是给人直接编的。(3) 故障语义:etcd 是强一致、低延迟、控制面核心;Git 是最终一致(push 后同步有延迟)、人类协作层。丢失恢复:(a) etcd 丢:用 etcd 快照恢复(第 5.9),因为 etcd 是运行时真相,无它集群状态没了但 Pod 仍在跑;(b) Git 丢:因 Git 只是”声明源”、集群实际状态还在 etcd 里,可从集群当前状态反推/用 Velero 备份重建 Git,或用”从集群 export 当前 YAML 重新提交 Git”。即:etcd 丢=丢运行时状态需快照;Git 丢=丢”声明账本”但能从集群现状重建(因为集群现状是 Git 的物化)。这题考你分清”两层真相源(声明 vs 运行时)“及各自丢失的恢复路径。

🔧 思考题 4(进阶)

GitOps 的”pull 模式”(控制器 watch Git 主动同步)和传统的”push 模式”(CI 主动 apply)在”防火墙/网络”上有个关键差异:集群在内网、Git 在公网 SaaS(如 GitHub)时,pull 模式天然能穿防火墙(集群出站连 Git),而 push 模式需 CI 能入站连集群(常要暴露集群 API)。请从这个角度评两种模式的网络安全性,并指出 GitOps 私有化部署时的注意点。

【参考答案】 网络安全性:push 模式要求 CI(常在外网/CI 平台)主动连入集群 API Server——意味着要么集群 API Server 暴露公网(大攻击面),要么 CI 跑在集群网络内(牺牲 CI 平台便利)。pull 模式里,集群内的 Argo CD 出站连 Git(如 GitHub),不需要暴露集群 API 给外网——出站连接通常被防火墙允许,入站不开放。所以从网络边界看,pull 模式更安全(不暴露控制面)。这甚至是 GitOps 流行的关键工程原因之一(不止哲学,是防火墙现实)。私有化注意点:(1) 若 Git 也私有(自托管 GitLab 在集群网络内),要确保 Argo CD 能连 Git(网络策略/NetworkPolicy 放行,第 9.7/13.6);(2) Git 私有化后它也成单点,要自建高可用+备份(第 16/22.5);(3) Argo CD 连 Git 用凭证(SSH key/ token)要妥善管(第 12 密钥外置思想);(4) 若集群完全离线,可用”离线 Git 镜像 + 定时同步”。结论:GitOps 的 pull 模式在”不暴露控制面”上天生优于 push,但私有 Git 要自己补”Git 的高可用与连通”,把”依赖外部 SaaS 的便利”换成”自己运维 Git 的责任”——又是便利换控制的老权衡(第 16.8)。


22.11 小结

  • 传统 push CI/CD 有漂移/不透明/回滚难/权限散,是配置漂移现代版;
  • GitOps:Git=真相源,控制器 pull 调和集群,同构 K8s 控制循环(Git=集群外 etcd);
  • 漂移自愈逼走”改 Git”正路;两层循环链式和到实际 Pod;
  • 多环境用 Git 结构,渐进发布用 Rollouts+分析;Git 历史即审计、回滚确定性;
  • 权限收敛到 Argo CD + Git review,密钥外置;代价在 Git 单点/延迟/复杂;
  • 是全书声明式哲学在组织流程层的集大成。

下一章讲多集群与联邦——当”一个集群”不够(故障域、合规、混合云、规模),怎么管”一群集群”。这把 K8s 的抽象从”节点”抬到”集群”。