一句话总结
GitOps 将系统的期望状态以声明式配置的形式存储在 Git 仓库中,由运行在集群内的控制器持续将实际状态同步到期望状态.
部署不再是执行一条推送命令,而是一次 Git 提交--集群自动对齐,Git 同时成为事实源,审计日志和回滚机制.
前置回顾
阶段一结束时,流水线已能产出可信且可追溯 的镜像(如 ghcr.io/org/myapp:a1b2c3d),使用 commit SHA 作为不可变 tag(第 03 篇),并通过了多重质量门禁(第 04 篇).本篇解决的核心问题是:这个镜像如何部署到集群.这构成了阶段一(镜像生产)和阶段二(持续交付)之间的衔接.阅读本篇前,建议先了解 Kubernetes 基本资源对象(Deployment,Service,ConfigMap).
传统 Push 部署模式的问题
阶段一结束后,最直接的部署方式是在流水线末尾追加一个 deploy 步骤,直接向集群下发命令:
|
这种方案可以工作,但引入了三个结构性问题:
问题一:配置漂移(Configuration Drift).集群状态可能因手动操作而偏离 Git 中的定义--有人用 kubectl scale 调整了副本数,用 kubectl edit 修改了环境变量.时间一长,Git 中记录的配置与集群实际运行的状态不再一致.当被问及"线上到底是什么配置"时,只能登录集群逐一 describe 资源来确认.
问题二:CI 系统持有集群凭证.流水线需要 kubectl 访问权限,这意味着 CI 系统必须持有集群管理员级别的凭证.一旦 CI 系统被攻破,集群将面临直接风险(这在第 02 篇中已作为凭证管理的核心安全议题讨论过).
问题三:回滚依赖人工追溯.上线后发现故障,需要确定上一个可用版本是什么,包含哪些变更.运维人员需要翻阅 CI 日志和沟通记录来拼凑信息,操作低效且容易出错.
这三个问题的根源一致:部署是一次性的推送动作,推送完成后没有任何机制持续守护集群的期望状态.
基础:声明式与调谐循环
理解 GitOps 的前提是理解 Kubernetes 的声明式(Declarative)设计.两种运维范式的对比:
| 命令式(Imperative) | 声明式(Declarative) | |
|---|---|---|
| 表达的内容 | 如何做:一系列操作指令 | 要什么:最终的期望状态 |
| 示例 | kubectl scale --replicas=3 |
YAML 中声明 replicas: 3 |
| 谁负责达成目标 | 操作者,逐条执行命令 | 控制器(Controller),持续调谐 |
Kubernetes 的控制器持续运行一个调谐循环(Reconciliation Loop):对比期望状态与实际状态,发现差异则执行操作以消除偏差.声明需要三个副本,其中某个 Pod 故障后,控制器会自动创建新的 Pod 来补足--运维人员无需介入.
命令式操作类似于每次手动调节油门和档位来维持车速;声明式操作类似于设定巡航速度后将调节工作交给车辆的控制系统--你只声明目标速度,调速策略由系统持续执行.
GitOps 在此基础上做了进一步延伸:既然 Kubernetes 已经是声明式的,那么期望状态本身也应被管理起来--将所有期望状态的 YAML 文件纳入 Git,使 Git 成为期望状态的唯一来源(Single Source of Truth).
GitOps 的四项原则
OpenGitOps 标准定义了四项原则:
| 原则 | 含义 | 解决的问题 |
|---|---|---|
| 声明式 | 整个系统的期望状态以声明式方式描述 | -(基础前提) |
| 版本化且不可变 | 期望状态存储在 Git 中,具备完整历史记录,可审计 | 问题三:回滚以 git revert 完成,历史可追溯 |
| 自动拉取 | 软件代理自动将已批准的变更拉取到目标系统 | 问题二:集群主动拉取,无需向外部系统暴露凭证 |
| 持续调谐 | 代理持续比对并纠正实际状态与期望状态之间的偏差 | 问题一:配置漂移被自动检测并纠正 |
四项原则合在一起表达了同一个主张:Git 中的配置即系统真相,由代理持续监控集群状态,任何偏离 Git 定义的变更都会被自动拉回.
Pull 模型与 Push 模型
这是 GitOps 区别于传统部署模式的根本机制差异.
Push 模型(传统方式):外部 CI 系统主动将变更推入集群
|
Pull 模型(GitOps):集群内部署的代理主动拉取 Git 仓库并自我对齐
|
Pull 模型带来三个实质性改进:
- 凭证不离开集群:ArgoCD 部署在集群内部,对集群的变更是内部操作,无需将集群凭证提供给外部 CI 系统.攻击面因此大幅缩减.
- 持续而非一次性:Push 是推送后即结束;Pull 的代理持续运行,不断比对 Git 与集群状态,任何时间点产生的偏差都会被检测并纠正.
- 自然适配多集群:每个集群独立运行自己的代理,各自拉取相同或不同的 Git 仓库,无需通过单一中心节点向数十个集群推送变更.
CI 与 CD 的职责分离
GitOps 使得 CI 和 CD 的职责边界变得明确:CI(阶段一)负责将源代码构建为可信镜像并更新 Git 仓库中的镜像 tag,仅需 Git 写入权限;CD(ArgoCD)负责将 Git 中声明的状态同步到集群,完全在集群内部完成.这延续了第 02 篇 OIDC,第 04 篇镜像签名所遵循的安全原则--没有任何环节需要长期持有集群的管理员凭证.
ArgoCD 核心对象:Application
ArgoCD 是当前最广泛使用的 GitOps 工具.其核心是一个名为 Application 的自定义资源(Custom Resource),本质上定义了一组映射关系:将某个 Git 仓库的某个路径下的配置,同步到某个集群的某个命名空间.
|
提交该 Application 后,ArgoCD 将持续监控 myapp-config 仓库中 k8s/ 目录的内容,将其中的所有 Deployment,Service,ConfigMap 等资源同步到集群.Application 的状态分为三类:
| 状态 | 含义 |
|---|---|
| Synced | 集群实际状态与 Git 中声明的期望状态一致 |
| OutOfSync | 存在差异(Git 中有新提交尚未同步,或集群被手动修改) |
| Healthy / Degraded | 同步后资源本身的健康状态(Pod 是否正常运行等) |
实践建议:双仓库策略
注意上述 repoURL 指向的是 myapp-**config** 而非应用源代码仓库.成熟的 GitOps 实践通常将二者分离:源码仓库存放应用代码,触发阶段一的 CI 流水线;配置仓库存放 Kubernetes YAML,由 ArgoCD 监控.CI 构建出新镜像后,向配置仓库提交一次变更以更新镜像 tag--这次提交即成为本次部署操作的不可篡改记录.下一节将说明这条链路的完整自动化过程.
自动同步,自愈与回滚
自动同步(Auto-Sync)
启用 syncPolicy.automated 后,ArgoCD 在检测到 Git 有新提交时,会在数分钟内(或通过 webhook 即时触发)自动将变更同步到集群,无需人工操作.此机制将阶段一末尾"镜像如何上集群"的问题完全自动化:
|
自愈(Self-Heal)
selfHeal: true 直接应对前述"问题一:配置漂移".若有人通过 kubectl edit 手动修改了集群中的副本数,ArgoCD 检测到实际状态与 Git 定义的偏差后,会立即将集群恢复到 Git 中声明的值.
自愈意味着手动修改会被覆盖
启用自愈后,在集群上的任何临时手动变更都会被 ArgoCD 还原.这是有意为之的设计--它强制所有变更都通过 Git 完成.好处是杜绝了"某人手动改了什么却未记录"的漂移问题;代价是紧急情况下无法直接通过 kubectl 快速修复(需先修改 Git 仓库或临时关闭自愈).这是 GitOps 工作纪律的体现,而非缺陷.
修剪(Prune)与回滚
prune: true:当从 Git 中删除一个资源定义后,ArgoCD 也会将集群中对应的资源删除,避免残留不再被管理的资源.
在 GitOps 模型下,回滚是 Git 操作的自然延伸:
|
相比前述"问题三:回滚依赖人工追溯",此时回滚即一次标准的 Git 操作,且执行者,执行时间,回退了什么内容,全部记录在 Git 历史中.这正是第 01 篇中 DORA 指标"故障恢复时间"得以大幅压缩的技术基础.
规模化:ApplicationSet 与 App of Apps
管理少量应用时,手工编写 Application 资源是可行的.但当微服务数量达到数十个,需要跨 dev/staging/prod 等多套集群时,手工维护数百个 Application 将带来显著的维护负担.以下两种模式用于解决此问题.
ApplicationSet:模板化批量生成
ApplicationSet 是一个"生成 Application 的工厂".定义一个模板配合一个生成器(generator),由其自动展开为一组 Application.例如,为每个环境各生成一个 Application:
|
这与第 02 篇的可复用 Workflow,第 06 篇的模板化配置理念一致:定义一次,批量展开,修改模板即同时更新所有实例.
App of Apps:层级化管理
App of Apps 模式的核心思想是创建一个根 Application,其指向的 Git 路径中存放的并非普通 Kubernetes 资源,而是一组子 Application 定义.同步根 Application 即同步其下所有子 Application,进而部署整个平台.
App of Apps 类似于软件的分层安装机制:用户执行一次顶层安装操作(根 Application),该操作按照声明顺序自动安装所有子模块(各个子 Application).整个平台的部署操作收敛为同步一个根 Application.
快速回顾
- 传统 Push 部署的三个问题:配置漂移,CI 持有集群凭证,回滚依赖人工追溯.根源在于推送是一次性的,没有持续守护期望状态的机制.
- 声明式 + 调谐循环是 GitOps 的技术基础:声明期望状态,由控制器持续将实际状态拉向期望状态.
- GitOps 四项原则:声明式,版本化不可变,自动拉取,持续调谐--"Git 是真相,代理持续纠偏".
- Pull 优于 Push:集群内代理主动拉取,凭证不离开集群,持续调谐,天然支持多集群.CI 负责构建镜像和更新 Git,CD 负责将 Git 同步到集群,职责清晰,互不持钥.
- ArgoCD Application 定义"将某 Git 路径同步到某集群命名空间"的映射.提供自动同步,自愈(覆盖手动漂移),prune 和通过
git revert实现的回滚能力. - 规模化模式:ApplicationSet(模板批量生成)和 App of Apps(层级化管理)用于管理多应用,多环境的复杂部署拓扑.
配置仓库中的镜像 tag 由谁来更新?是手动操作吗?
由 CI 流水线自动完成.阶段一的 CI 在构建并推送 myapp:a1b2c3d 后,在流水线末尾执行以下步骤:克隆配置仓库,将 YAML 中的镜像 tag 替换为新 commit SHA,提交并推送.也有专用工具(如 ArgoCD Image Updater)可监听镜像仓库并自动提交此类变更.关键在于,这次"更新 tag 的提交"即为本次部署操作的不可篡改记录--谁部署了哪个版本,在什么时间,均可从 Git 历史中获取.
GitOps 是否适用于有状态应用和数据库变更?
无状态应用是 GitOps 的最佳适用场景.有状态应用的声明式部分(StatefulSet,PVC 定义等)同样可以由 GitOps 管理.但数据库的 DDL 变更,数据迁移等操作具有一次性,有序,不可逆的特点,本质不属于声明式范畴,通常不直接交由 GitOps 的调谐循环处理,而应配合专门的 migration 工具或人工审批流程.判断某项操作是否适合声明式管理,是正确使用 GitOps 的关键.
动手练习
- ArgoCD 本地部署:使用 k3d 创建本地集群,部署 ArgoCD(应用官方 manifest),打开其 Web UI.
- Application 初体验:创建一个配置仓库,放置一个简单应用的 Deployment 和 Service 定义.创建 Application 指向该仓库,观察从 OutOfSync 到 Synced 的状态变化过程.
- 自愈验证:启用
selfHeal后,手动执行kubectl scale修改副本数,观察 ArgoCD 将其恢复为 Git 中声明值的时间和过程. - 自动同步与回滚:修改 Git 仓库中的副本数(如从 2 改为 3)并推送,确认集群自动跟随变更.然后
git revert该提交,验证"回滚即 Git 操作"这一模式. - (进阶)ApplicationSet:使用 ApplicationSet 为 dev 和 prod 两个 namespace 各生成一个 Application,体验"定义一次,批量展开"的效果.