OpenGitOps 四原则 — 为什么是 pull 不是 push
一句话总结
GitOps 不是部署工具的名称,而是运维模型的名称。只有同时满足 OpenGitOps 定义的四项原则——声明式(Declarative)、版本化且不可变(Versioned and Immutable)、自动拉取(Pulled Automatically)、持续协调(Continuously Reconciled)——才是 GitOps。缺少其中任何一项,都只是“把 YAML 放在 Git 中的 CD pipeline”。
为什么需要它
十年前的部署通常是这样:CI server 完成 build 后,由 CI server 执行 kubectl apply,这叫 push 模型。表面看很简单,却会破坏三件事。
第一,集群的钥匙放在 CI 里。Jenkins 或 GitHub Actions runner 必须持有 kubeconfig 或 ServiceAccount token。而 runner 是执行外部代码的机器,由 fork 提交的一条 PR workflow 可能由此获得 production 部署权限。
第二,无法回答“当前集群中运行着什么”。只能翻查 pipeline 日志,而且有人手动修改的内容连日志也不会记录。
第三,pipeline 停止时,状态也停止变化。push 只在 event 到来时运行。如果凌晨 3 点有人手动修改 replicas,而没有任何事件发生,就无人知晓。
Pull 模型一次颠覆了这三点。运行在集群内部的 agent(Argo CD、Flux)会读取 Git。CI 完全不需要任何集群 credential,它只负责“构建 image 并上传到 registry,再向配置 repository commit 一行 tag”。
工作原理
逐项拆解四个原则如下。
| 原则 | 含义 | 违反后的结果 |
|---|---|---|
| 声明式 | 描述期望的最终状态,而不是到达状态的步骤 | 同一 script 运行两次会得到不同结果 |
| 版本化且不可变 | 所有状态都以 commit 留存,已创建的 revision 不再修改 | 无法回答“从什么时候开始变成这样?” |
| 自动拉取 | agent 自行获取获批变更 | 人员忘记部署时,Git 与现实会分离 |
| 持续协调 | 与事件无关地持续比较并收敛 | 手动修改会永久保留 |
考试中经常强调,第三项原则的原文不是“Pushed Automatically”,而是 Pulled Automatically。第四项中的“Continuously”也不是“每次 commit 时”,而是即使没有 commit 也持续运行。Argo CD 默认每 180 秒执行一次该 loop。
“Git 是 SSOT(Single Source of Truth)”也经常被误解。它不是说“所有文件都在 Git 中”,而是声明:“当集群与 Git 不一致时,Git 才是正确状态。” 关键在于方向已经确定。发现 drift 后反过来修改 Git 以匹配集群,不是 GitOps,只是事后补文档。
声明式也并非总是占优。顺序本身至关重要的任务——database schema migration、one-time data correction、certificate initial issuance——无法仅用“最终状态”表达。因此,Argo CD 提供 hook(PreSync/PostSync)作为 imperative escape hatch。准确的理解是:声明式是默认值,命令式是明确标注的例外。
现场会遇到的情况
作者的 7 节点家庭实验室完整采用该模型。Gitea 位于 10.0.0.200,Argo CD 位于 10.0.0.201,Harbor 位于 10.0.0.202,Grafana 位于 10.0.0.203——都来自 MetalLB L2 pool 10.0.0.200-215。网络由不使用 kube-proxy 的 Cilium 1.20 eBPF 处理,上层运行 kube-prometheus-stack 与 CloudNativePG。整个 stack 并不是从集群外 push 进去,而是由内部 pull 得到。
有一次事件同时展示了声明式的威力与边界。该集群的 controlPlaneEndpoint 不是 VIP 或 DNS,而被固定为第一台 control plane 的物理 IP 10.0.0.120。后来即使把 control plane 扩展到三台、拥有三个 etcd member,只要第一台节点故障,kubectl 与 7 台 kubelet 仍会全部无法连接。因为 apiserver certificate SAN 中没有其他节点 IP,TLS validation 从一开始就会失败。
这与 GitOps 的关系在于,它说明:没有写入声明文件的值就不受管理。controlPlaneEndpoint 在创建集群的瞬间确定一次,之后既不在 Git 中,也不属于 reconciliation 对象,因此修改起来极其痛苦。在实际工作中,区分 GitOps 能够管理的区域,与 cluster bootstrap 等外部区域,比单纯使用工具更重要。
接下来阅读什么
下一篇 reading 会准确定义 desired state、drift、reconciliation、convergence 等考试术语。之后的模块会在 /root/cgoa-repo/ 下创建真实 GitOps repository 骨架,并通过 kubectl kustomize 亲眼确认各环境的渲染结果如何不同。