【架构实战】GitOps实践:让运维更优雅
字数统计:约3600字
一、真实故事引入:一次误删引发的运维革命
2024年春天,我们团队负责维护一个拥有23个微服务的K8s生产集群,当时的运维方式还停留在"半自动化"阶段:修改ConfigMap、调整Deployment副本数、更新镜像版本等操作,都需要运维人员登录跳板机,手动执行kubectl apply -f命令。
有一次,新入职的运维同学在处理一个紧急需求时,误执行了kubectl delete deployment order-service -n prod,导致订单服务整个被删除,集群中订单相关的Pod全部消失,线上订单功能瘫痪了12分钟。虽然我们通过etcd备份快速恢复了Deployment,但这次事故让我们惊出一身冷汗------如果备份不是最新的,或者误操作的是数据库相关的资源,后果不堪设想。
事故复盘时我们意识到:所有的配置变更都不应该直接操作集群,而应该像管理代码一样管理配置,所有的变更都有记录、可回滚。于是我们引入了GitOps理念,用Argo CD作为核心工具,把所有的K8s资源配置都存储在Git仓库中,从此运维变成了"提交PR、合并代码"的简单操作,再也没出现过误删、配置不一致的问题。
二、概念原理:GitOps到底是什么?
GitOps是Weaveworks在2017年提出的一种云原生运维理念,核心思想是"把Git作为系统的唯一事实来源",所有的系统配置、应用部署、环境变更都通过Git来管理,通过自动化工具将Git中的配置同步到生产环境。
2.1 GitOps四大核心原则
- 声明式配置:所有的系统状态都用声明式的配置文件(如K8s的YAML)描述,而不是命令式的脚本
- 版本控制为唯一真相源:所有的配置都存储在Git仓库中,只有Git中的配置才是合法的,任何直接修改集群的操作都会被自动纠正
- 自动化同步:有专门的工具(如Argo CD、Flux)自动监听Git仓库的变更,将配置同步到目标集群
- 闭环自愈:当集群的实际状态和Git中的期望状态不一致时,自动化工具会自动纠正,确保状态一致
2.2 GitOps vs 传统CI/CD
很多人会混淆GitOps和传统CI/CD,两者的核心区别:
- 传统CI/CD:聚焦"构建→测试→部署"的流程,部署是流程的终点,部署后流程结束
- GitOps:聚焦"配置管理→自动同步→状态保持",部署只是同步的一个环节,之后会持续监控状态一致性
两者不是替代关系,而是互补:CI/CD负责构建制品,GitOps负责部署和管理制品的生命周期。
2.3 核心工具选型
目前主流的GitOps工具有两个:
- Argo CD:Weaveworks开源,UI友好,支持多集群,可视化程度高,社区活跃
- Flux CD:CNCF毕业项目,轻量级,和K8s原生集成更好,适合云原生深度用户
我们选择了Argo CD,主要是因为它的UI界面对非技术人员更友好,且支持多租户管理。
三、配置代码:从零搭建GitOps体系
3.1 部署Argo CD到K8s集群
bash
# 1. 创建专用命名空间
kubectl create namespace argocd
# 2. 部署Argo CD(稳定版本)
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# 3. 等待所有Pod就绪
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=argocd-server -n argocd --timeout=300s
# 4. 获取初始管理员密码
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# 5. 暴露Argo CD UI(临时用NodePort,生产建议用Ingress)
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort"}}'
3.2 配置Git仓库与Application
我们的GitOps配置仓库结构采用base + overlays的kustomize标准结构:
gitops-config/
├── base/
│ ├── coupon-service/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── kustomization.yaml
│ └── order-service/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
└── overlays/
├── test/
│ ├── coupon-service/
│ │ ├── patch-replicas.yaml # 测试环境2副本
│ │ └── kustomization.yaml
│ └── order-service/
│ ├── patch-replicas.yaml
│ └── kustomization.yaml
└── prod/
├── coupon-service/
│ ├── patch-replicas.yaml # 生产环境5副本
│ ├── patch-resources.yaml # 生产环境资源限制更高
│ └── kustomization.yaml
└── order-service/
├── patch-replicas.yaml
└── kustomization.yaml
base/coupon-service/kustomization.yaml示例:
yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
commonLabels:
app: coupon-service
overlays/prod/coupon-service/kustomization.yaml示例:
yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../../base/coupon-service
patches:
- path: patch-replicas.yaml
- path: patch-resources.yaml
patch-replicas.yaml示例:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: coupon-service
spec:
replicas: 5
3.3 创建Argo CD Application
我们通过Argo CD的CRD(自定义资源)来管理每个应用的同步规则,创建coupon-service-prod.yaml:
yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: coupon-service-prod
namespace: argocd
spec:
project: default
# 配置源:Git仓库信息
source:
repoURL: https://gitlab.example.com/infra/gitops-config.git
targetRevision: main
path: overlays/prod/coupon-service
# 如果用kustomize,需要指定kustomize版本
kustomize:
version: v1.27.0
# 目标集群信息
destination:
server: https://kubernetes.default.svc # 默认是Argo CD所在的集群,跨集群需要配置集群凭证
namespace: prod
# 同步策略
syncPolicy:
automated:
prune: true # 自动删除Git中不存在的资源
selfHeal: true # 当集群状态不一致时自动纠正
syncOptions:
- CreateNamespace=true # 如果目标命名空间不存在则自动创建
- PrunePropagationPolicy=foreground
# 健康检查
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # 忽略副本数的差异(如果需要手动扩缩容的话)
部署这个Application:
bash
kubectl apply -f coupon-service-prod.yaml
四、实战案例:用GitOps管理23个微服务的完整流程
我们的生产集群有23个微服务,所有配置都存储在gitops-config仓库中,完整流程如下:
4.1 日常变更流程(以调整优惠券服务副本数为例)
- 运维或开发人员在
gitops-config仓库中,修改overlays/prod/coupon-service/patch-replicas.yaml的副本数从5调整为8 - 提交PR到
main分支,经过团队code review后合并 - Argo CD每3分钟自动检测Git仓库变更(也可配置Webhook即时触发)
- 检测到变更后,Argo CD对比Git中的期望状态和集群中的实际状态,发现副本数不一致
- Argo CD自动执行
kubectl set replicas deployment/coupon-service 8 -n prod - 同步完成后,Argo CD UI显示"Healthy"状态,同时发送企业微信通知
整个过程不需要登录任何服务器,从提交代码到变更生效,最快1分钟完成,且所有的变更都有Git提交记录,可审计、可回滚。
4.2 紧急回滚场景
有一次我们上线一个新版本的订单服务,发现有一个严重的bug,需要回滚到上一个版本:
- 在Git仓库中找到上一个版本的commit hash:
a1b2c3d - 执行
git revert a1b2c3d或者直接git reset --hard a1b2c3d(根据团队规范) - 合并到
main分支,Argo CD自动同步,将订单服务回滚到上一个版本 - 整个过程5分钟完成,比原来的
kubectl rollout undo更可追溯,因为Git中有明确的回滚记录
4.3 多环境管理
我们的配置仓库有test、pre-prod、prod三个环境的overlays,修改配置时只需要同步修改三个目录下的文件,或者用一个通用的base加环境特定的patch,避免配置漂移。Argo CD中可以直观看到每个环境的应用状态,一键同步某个环境的所有应用。
五、踩坑实录:GitOps落地过程中的那些坑
5.1 Git仓库权限配置错误
- 现象 :Argo CD同步时报
permission denied错误,无法拉取Git仓库 - 原因:Argo CD使用的SSH密钥没有Git仓库的读取权限
- 解决 :
-
在Argo CD中创建Secret存储SSH私钥:
kubectl create secret generic git-creds --from-file=ssh-privatekey=~/.ssh/id_rsa -n argocd -
在Application中引用这个Secret:
yamlsource: repoURL: git@gitlab.example.com:infra/gitops-config.git targetRevision: main path: overlays/prod/coupon-service helm: # 如果是Helm的话 kustomize: # 如果是kustomize的话 # 配置凭证 auth: sshPrivateKeySecret: name: git-creds key: ssh-privatekey
-
5.2 同步时资源冲突
- 现象 :同步时报
object is being deleted错误,无法更新资源 - 原因:之前手动删除了某个资源,Argo CD还在处理删除流程,新的同步请求冲突
- 解决 :等待删除流程完成,或者手动删除Argo CD中的资源缓存:
argocd app terminate-op coupon-service-prod
5.3 镜像版本漂移问题
- 现象:有时候发现集群中的镜像版本和Git中的不一致,Argo CD没有自动纠正
- 原因 :我们在Application中配置了
ignoreDifferences忽略了镜像版本的差异,导致Argo CD不检测镜像变化 - 解决 :去掉不必要的
ignoreDifferences配置,只忽略确实需要手动调整的部分(如副本数)
5.4 Secret管理难题
-
现象:K8s的Secret是base64编码,不是加密,不能直接存在Git中
-
解决 :引入Bitnami SealedSecrets,将加密后的Secret存在Git中,Argo CD同步时,SealedSecrets控制器自动解密成正常的Secret:
bash# 安装SealedSecrets控制器 kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml # 加密Secret kubectl create secret generic db-secret --from-literal=username=admin --from-literal=password=123456 --dry-run=client -o yaml | kubeseal -o yaml > sealed-db-secret.yaml把
sealed-db-secret.yaml存在Git中即可,安全且可追溯。
5.5 跨集群同步问题
- 现象:Argo CD部署在集群A,要同步配置到集群B,报连接超时
- 原因:集群B的API Server地址是内网地址,Argo CD所在的集群A无法访问
- 解决:在Argo CD中配置集群B的kubeconfig,或者给集群B的API Server配置公网访问(生产不建议),或者用VPC对等连接打通两个集群的网络。
六、总结与思考
6.1 核心总结
GitOps让我们实现了"运维即代码",核心价值:
- 可追溯:所有变更都有Git提交记录,谁改了什么、什么时候改的、为什么改,一目了然
- 可回滚 :改错了只需要
git revert,不需要手动操作集群,回滚时间从原来的30分钟缩短到5分钟 - 减少人为错误:杜绝直接操作集群的行为,所有变更都经过code review和自动化同步
- 环境一致性:测试、预发、生产环境的配置都来自同一个base,避免配置漂移
6.2 思考题
- GitOps适合所有场景吗?比如有状态服务(数据库、消息队列)的配置是否适合用GitOps管理?
- 如果Git仓库被恶意篡改,如何防止错误的配置同步到生产环境?(提示:签名验证、分支保护规则)
- GitOps和IaC(基础设施即代码)是什么关系?如何结合使用?
6.3 个人观点
GitOps不是什么高深的技术,而是一种"把运维当代码管"的理念,它让运维变得更优雅、更可控。但落地GitOps也不是一蹴而就的,需要团队转变理念:
- 开发人员要学习写K8s YAML,不能再只关注代码
- 运维人员要从"执行命令的人"变成"维护Git配置和自动化工具的人"
- 团队要建立完善的Git workflow,比如分支保护、PR review、自动化测试
另外,GitOps工具不是万能的,Argo CD和Flux各有优劣,选择适合自己团队的才是最好的。对于小团队,如果只有几个服务,用Flux更轻量;对于中大型团队,Argo CD的UI和多租户能力更有优势。