LabHub
学习 学习路径 课程

GitOps 与 Argo CD

GitOps — 把定义状态的权力交给仓库

在 LabHub 中继续学习

一句话总结

GitOps 不是某个部署工具的名称,而是一项所有权声明:只有 Git 仓库有权定义集群状态。

概念图: 只有 Git 仓库有权定义集群状态。 · 无法复现 · 仓库中没有的内容不应存在于集群,仓库中存在的内容必须存在于集群。 · pull 模式

为什么需要了解这些

使用 kubectl apply 部署的团队,必然会遇到这个问题:“当前生产环境运行的究竟是哪一个提交?”却没有人能自信回答。因为上周故障期间,有人通过 kubectl scale 增加了 Pod,有人使用 kubectl edit 修改了环境变量,而这些变更没有记录在任何地方。时间一长,集群就会漂移到仓库中不存在的状态,这就是漂移。

漂移真正的成本不是“配置略有不同”,而是无法复现。需要重建集群时,即使原样应用仓库内容,也无法得到之前相同的系统。作者把家庭实验室从 3 个节点扩展到 7 个节点时,新节点上残留着旧集群的内容(旧版 kubelet、旧证书);从人们开始依靠记忆手动对齐差异的那一刻起,错误就出现了。

GitOps 通过一条规则扭转这个问题:仓库中没有的内容不应存在于集群,仓库中存在的内容必须存在于集群。 这样,“当前运行着什么”就成为可以通过 git log 回答的问题。

工作原理

GitOps 有四项原则,必须全部满足。

原则 核心问题 违反后的症状
声明式 期望状态是否写成代码 无法复现、配置漂移
版本管理 所有变更是否都保留为提交 不知道谁在何时为何修改
自动应用 是否无需人工操作即可应用 发布延迟、人为错误
自我修复 出现偏差时是否自动恢复 漂移累积、环境不一致

第三项采用 pull 模式尤其重要。CI 直接向集群推送的 push 模式,必须把集群凭据交给 CI。pull 模式由集群内的代理读取仓库,凭据不会离开集群。这项选择会彻底改变安全边界。

实际工作还要增加两条规则。第一,将应用代码仓库与清单仓库分开。 如果放在同一仓库,每次应用 CI 运行都会触发同步,代码评审与发布评审也会混在一个 PR 中。第二,不使用 :latest 标签。 如果同一个提交在昨天和今天启动的镜像不同,该仓库就不再能够定义状态。镜像标签必须使用提交哈希、语义化版本等一经确定就不再改变的值。

生产现场中的常见情况

第一,提交消息是判断回滚目标的唯一依据。 故障期间,人们查看的不是代码,而是 git log --oneline 的一屏输出。如果十条消息都是 fixupdate,就无法判断应回滚哪个提交。GitOps 中的回滚就是 git revert,而选择 revert 对象,本质上就是阅读提交消息。

第二,紧急手工修改必须回到代码中。 如果通过 kubectl scale 救活服务,就必须二选一:把该值提交到仓库,或恢复到仓库基准。若保持不管,下一次同步会悄悄撤销修改,再次触发同一故障。这与 Ansible 使用 -e 或 Terraform 手工修改导致的事故结构完全相同。

第三,仓库结构就是权限边界。 作者的家庭实验室把清单存放在 Gitea(10.0.0.200),由 ArgoCD(10.0.0.201)读取。按应用、环境划分目录后,未来可以沿用这些边界划分评审者和发布权限。反过来,如果所有内容都堆在同一目录,就没有划分权限的位置。

GitOps 真正困难的三个地方

原则很简单,但投入运维后,总会在同样三个地方遇到阻碍。

第一,秘密不能提交到 Git。 因此,“一切都在 Git 中”的原则最先被打破。 解决方案有三种:加密后提交(SOPS、sealed-secrets)、引用外部存储(External Secrets),或完全从集群外注入。前两种更符合 GitOps,但必须从一开始就承认:无论选择哪一种,仍然需要管理密钥的位置。

第二,有些值会由集群自行修改。 HPA 会调整 replicas,Webhook 会添加注解,Operator 会写入状态。如果 Git 持续撤销这些变化,就会产生无休止的回退循环。应明确配置忽略这些字段。

spec:
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers: ["/spec/replicas"]

第三,紧急手工修复会被悄悄撤销。 故障时通过 kubectl edit 临时阻止问题后,如果忘记处理,自动同步会在几分钟后恢复原状态。因此,手工修改后必须把事实记录到 Git,这种习惯应成为流程的一部分。暂时关闭自动同步也是方法,但如果忘记重新启用,从那一刻起 Git 与现实就会分离。

Synced 绿灯表示“与 Git 相同”,并不表示“运行正常”。 即使清单已按原样应用,Pod 仍可能处于 CrashLoopBackOff。健康状态必须另行检查,部署是否真正完成则要通过镜像摘要或修订版本确认。

回滚是 GitOps 最大的收益。 回滚变成撤销一个提交,因此变更内容与方式会自动留下记录。不过,数据库模式等不可回滚的变更不在这一收益范围内,这类变更必须拆分发布并保持前后兼容。

下一次实验要做什么

/root/gitops/repo 创建并提交清单仓库,把声明应用到 gitops-lab 命名空间。随后故意使用 kubectl scale 制造漂移,确认 kubectl diff 如何发现差异(存在差异时退出码为 1),再恢复到仓库基准。最后,亲自编写执行 diff → apply → 记录已应用提交的同步脚本,手动体验 ArgoCD 控制器究竟替你完成了哪些工作。