26、Git与DevOps:基础设施即代码(IaC)的版本控制、GitOps 理念与实践
聊到 DevOps,很多人第一反应是 CI/CD 流水线、自动化部署。但说实话,真正让 DevOps 落地生根的,是「基础设施即代码」这个理念。我刚开始接触这个概念时,也觉得不就是把服务器配置写成脚本嘛,有啥了不起的?直到有一次,我手动修改了生产环境的 Nginx 配置,结果忘了备份……嗯,那次事故让我彻底明白了:基础设施必须像应用代码一样,纳入版本控制。
什么是基础设施即代码(IaC)?
说白了,就是用代码来定义和管理你的服务器、网络、负载均衡器这些基础设施。你想想看,以前运维同学登录服务器,敲命令改配置,全靠经验和记忆力。现在呢?我们把所有配置写成声明式文件,存在 Git 仓库里。谁改了什么,什么时候改的,一目了然。
我个人习惯把 IaC 分为两类:
- 声明式:你告诉系统「我要什么状态」,系统自己去搞定。比如 Terraform、CloudFormation。
- 命令式:你告诉系统「每一步怎么做」。比如 Ansible、Puppet 的某些模式。
在实际项目中,我更推荐声明式。为什么?因为声明式文件天然适合 Git 的 diff 和回滚。你改一行代码,Git 就能告诉你这次变更会影响哪些资源。
IaC 的版本控制:不只是存代码
把 Terraform 文件扔进 Git 仓库,这只是第一步。真正要做好 IaC 的版本控制,有几个坑你必须避开。
这里我总结了几条 IaC 版本控制的最佳实践:
| 实践 | 说明 |
|---|---|
| 模块化 | 把通用的基础设施(如 VPC、数据库)拆成独立模块,方便复用 |
| 环境隔离 | dev、staging、production 用不同的分支或目录管理 |
| 状态文件管理 | Terraform 的 state 文件不要提交到 Git,用远程后端(如 S3) |
| 版本锁定 | 锁定 provider 和模块的版本,避免「昨天还能用,今天就不行了」 |
| 代码审查 | 基础设施变更必须走 PR 审查流程,跟应用代码一样 |
GitOps:让 Git 成为唯一的真相来源
GitOps 是 IaC 的进阶玩法。它的核心理念很简单:你想要的系统状态,全部声明在 Git 仓库里。然后有个自动化工具,不断对比 Git 里的状态和实际运行的状态,发现不一致就自动修正。
我刚开始用 GitOps 时,总觉得多此一举。直到有一次,某个同事手痒,在生产服务器上临时改了防火墙规则。如果是传统方式,可能过几天就忘了这回事。但 GitOps 的自动同步机制立刻发现了差异,直接回滚了那个手动修改。嗯,那一刻我彻底服了。
GitOps 的核心工作流大概是这样的:
这个流程里,最核心的角色是 GitOps Operator(比如 Argo CD、Flux)。它就像一个尽职的保安,不停地在 Git 仓库和实际环境之间做对比。一旦发现有人偷偷改了生产环境,它立刻把状态「拉」回 Git 里定义的样子。
GitOps 的三大原则
我在团队里推行 GitOps 时,发现很多人容易忽略它的底层原则。这里我用自己的话总结一下:
- 声明式描述:整个系统的期望状态,必须用声明式的方式描述清楚。不是「怎么做」,而是「最终长什么样」。
- 版本化且不可变:所有变更都通过 Git 提交,历史可追溯。一旦部署,就不允许手动修改。
- 自动拉取:Operator 主动从 Git 拉取状态,而不是被动等待推送。这能确保「Git 里有什么,系统里就有什么」。
实际案例:用 Git 管理 Kubernetes 集群
Kubernetes 是 GitOps 最典型的应用场景。你想想看,一个 K8s 集群里可能有几十个 Deployment、Service、ConfigMap,如果全靠 kubectl apply 手动管理,迟早会出乱子。
我习惯的做法是这样的:
- 把所有 K8s 资源定义(YAML 文件)放在一个 Git 仓库里
- 用 Kustomize 或 Helm 做环境差异化配置
- 部署 Argo CD 作为 GitOps Operator
- 每次修改 YAML 文件 → 提交 PR → 合并到 main 分支 → Argo CD 自动同步到集群
这里有个简单的 Argo CD Application 定义示例:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/my-org/k8s-manifests.git
targetRevision: main
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
注意看 syncPolicy 里的 selfHeal: true,这就是 GitOps 的「自愈」能力。如果有人手动改了集群里的 Pod 副本数,Argo CD 会自动把它改回来。
避坑指南:GitOps 不是银弹
说了这么多好处,我也得泼点冷水。GitOps 在某些场景下并不适用:
- 频繁变动的配置:比如需要实时调整的限流参数,每次改都要走 Git 提交,效率太低
- 紧急故障恢复:有时候生产环境挂了,你等不及 PR 审查和 CI 流程,需要直接登录服务器改配置
- 有状态应用:数据库、缓存这类有状态服务,GitOps 的自动回滚可能导致数据丢失
我曾经在一个金融项目里,就因为 GitOps 的自动同步机制,差点把生产数据库的配置回滚了。后来我们加了一条规则:有状态服务的配置变更,必须手动确认,不启用自动同步。
总结一下
Git 和 DevOps 的结合,说白了就是让基础设施的管理变得像软件开发一样规范。IaC 让你能「用代码定义基础设施」,GitOps 让你能「用 Git 驱动基础设施的变更」。这两者加起来,就是 DevOps 里「持续交付」的最后一公里。
我个人觉得,未来几年 GitOps 会成为基础设施管理的标配。就像当年 Git 取代 SVN 一样,GitOps 也会逐步取代传统的运维方式。如果你还没开始尝试,不妨从一个小项目入手,感受一下「提交代码就能改变世界」的爽快感。
一句话记住: 基础设施即代码是基础,GitOps 是灵魂。没有 Git 的基础设施,就像没有刹车的跑车——跑得快,但停不下来。