去年初我们团队接手了一个日均订单量在 80 万左右的电商系统重构,当时面临一个很现实的问题:原来的部署方式是用 Jenkins 写 Pipeline,脚本里硬编码了各种 kubectl apply,大促前扩容经常要手动改副本数,有一次因为脚本里的一个变量没替换,导致预发环境把生产配置拉过去了,差点出了事故。那次之后,我们决定彻底切到 GitOps 模式,当时在 Jenkins X 和 ArgoCD 之间纠结了很久,最后选了 ArgoCD v2.12(2024 年 10 月刚发布的稳定版),现在回头看这个决定太对了。
先说说为什么没选 Jenkins X。我们当时试跑了两周 Jenkins X,发现它太重了。Jenkins X 本身依赖 Tekton 做 CI,还要装一堆扩展组件,光是初始化一个集群就要跑 20 多个 Pod,内存占用直接飙到 4GB 以上。我们那个订单系统的微服务有 32 个,每个服务每次构建都要触发 Tekton 的 Task,有一次下午 3 点集中发版,Tekton 的 PipelineRun 队列排了 17 个,等最后一个跑完已经 5 点半了,开发同学都在旁边干着急。更麻烦的是它的 GitOps 实现是“隐式”的,它会在 Git 里自动生成很多它自己的配置文件,我们后来想回滚一个服务的版本,翻了半天仓库都没找到到底哪个文件控制着部署,因为它的配置分散在 .jx 目录和各个服务的 charts 目录里,新人接手根本理不清。
再看 ArgoCD v2.12,我们测下来最直观的感受是“轻”。核心组件就三个:argocd-server、argocd-repo-server、argocd-application-controller,初始部署完内存占用才 1.2GB。它的声明式设计太符合我们的习惯了——所有部署配置都放在一个 Git 仓库里,我们叫它 gitops-config,里面每个目录对应一个环境:dev/、test/、prod/,每个目录下是对应服务的 Kustomize overlay。比如订单服务的生产配置就在 prod/order-service/ 里,里面只有 kustomization.yaml 和 replica-patch.yaml,改副本数直接改这个文件提交就行,ArgoCD 会自动同步到集群。
我们当时做了个简单的性能对比,同样部署 32 个微服务到 3 个环境(dev/test/prod),Jenkins X 平均耗时 12 分钟,ArgoCD v2.12 只要 4 分半。而且 ArgoCD 的同步是实时的,我们之前用 Jenkins 的时候,提交代码到 Git 还要等 Jenkins 的 webhook 触发,有时候 webhook 丢了还要手动点构建,现在只要 git push 完,ArgoCD 最多 30 秒就能检测到变更开始同步。
这里放一下我们当时装 ArgoCD v2.12 的 Helm 配置,是我们调了半天的参数,特意把 server.service.type 改成了 LoadBalancer(我们用的阿里云 ACK,直接拿 SLB 暴露),还开了 dex 集成公司的 LDAP 做 SSO:
当时有个小插曲,我们第一次装的时候没限制 repo-server 的资源,结果它同步一个大仓库(我们那个 gitops-config 仓库有 2GB 多,存了很多历史 Helm Chart)的时候直接把节点内存吃满了,后来加了上面的资源限制才稳定。现在这个配置跑了 3 个月,没出过一次 OOM。
我们那个订单系统之前的环境管理简直是一团乱麻。开发同学本地改完代码,打个镜像 tag 是 v1.2.3-dev,然后手动改预发环境的 Deployment 镜像,有时候改完忘了提交到 Git,下次预发环境重建就又回到了旧版本。去年双 11 大促前,我们预发环境测订单支付回调没问题,结果上生产的时候发现回调地址配的是预发的域名,排查了 40 分钟才找到是某个开发手动改了生产的 ConfigMap 没同步到 Git,那次之后我们下定决心做多环境 GitOps 改造。
改造的核心思路是“环境即目录,配置即代码”。我们把所有环境的配置都收归到 gitops-config 仓库,结构是这样的:
每个环境对应 ArgoCD 里的一个 Application,比如生产的订单服务 Application 配置是这样的:
这里有个细节我们踩过坑:targetRevision 一定要和环境对应,开发环境我们用 dev 分支,测试用 test,生产用 main,而且生产分支设置了保护规则,必须两个 reviewer approve 才能合并。之前有个开发图省事,直接把 dev 分支的 targetRevision 改成了 main,结果他开发环境的变更直接同步到生产了,幸好当时 ArgoCD 的 RBAC 拦住了——我们给开发同学只开了 dev 和 test 环境的 sync 权限,生产的 sync 只有我和另一个运维有,不然就出大事了。
改造完最明显的变化是发版效率。之前大促前我们要花 2 小时逐个环境改配置、手动部署,现在只要把 base 里的镜像 tag 从 v1.2.3 改成 v1.2.4,然后合并到对应环境的分支,ArgoCD 自动同步。去年双 11 我们订单系统的 QPS 峰值到了 1.2 万,需要把订单服务的副本数从 10 扩到 25,我直接在 overlays/prod/order-service/replica-patch.yaml 里把 replicas 改成 25,提交后 1 分钟不到,生产集群的副本数就自动加上了,比之前手动 kubectl scale 快了太多,而且有 Git 记录可追溯,谁什么时候改了什么一目了然。
还有个实际的问题我们解决了很久:之前订单服务的配置里有数据库密码、支付密钥这些敏感信息,开发同学经常直接写在 ConfigMap 里提交到 Git。后来我们用 ArgoCD v2.12 支持的 Secret 加密集成,把敏感信息用 SealedSecrets 加密后存在 Git 里,ArgoCD 同步的时候自动解密成 Kubernetes Secret。比如支付密钥的 SealedSecret 是这样的:
我们给 SealedSecrets 的控制器配了只有生产集群能解密的密钥,就算这个加密文件泄露了,别人也解不出来。之前有个开发不小心把 gitops-config 仓库的权限开给了外包团队,我们也不担心敏感信息泄露,因为加密后的内容他们没用。
我们的微服务数量从 32 个涨到 57 个的时候,手动给每个服务写 Application yaml 已经扛不住了——每次加一个新服务,就要写一份 Application 配置,还要改三个环境的,太费时间。后来我们用了 ArgoCD v2.12 的 ApplicationSet,直接根据 Git 仓库的目录结构动态生成 Application,省了超多重复工作。
我们的 gitops-config 仓库里 overlays 目录的结构是 overlays/{env}/{service},比如 overlays/prod/order-service、overlays/test/payment-service,然后写了一个 ApplicationSet,用 git 生成器自动扫描这个目录结构:
这个配置跑起来之后,只要在 overlays/prod/ 下加一个新的服务目录,比如 overlays/prod/new-service/,ArgoCD 会自动检测到,然后生成一个叫 new-service-prod 的 Application,完全不用手动写配置。之前我们加一个新服务要 10 分钟,现在只要 1 分钟,而且不会漏配环境。
不过这里有个性能问题我们遇到过:我们的 gitops-config 仓库有 2GB 多,里面存了很多旧的 Helm Chart 包,ApplicationSet 每次扫描都要拉整个仓库,导致 argocd-repo-server 的 CPU 占用经常到 80% 以上,同步延迟有时候能到 2 分钟。后来我们按照社区的最佳实践,把配置存到 OCI Registry 里,不用 Git 存大文件了。
我们用的是 Harbor 作为 OCI Registry,把每个服务的 Kustomize 配置打包成 OCI 镜像,推送到 Harbor 里。比如订单服务的配置,我们写个 Dockerfile 打包:
然后构建推送到 Harbor:
然后在 Application 里把 source 改成 OCI Registry 的地址,不用 Git 仓库了:
改完之后效果立竿见影,argocd-repo-server 的 CPU 占用直接降到了 20% 以下,同步延迟稳定在 30 秒以内。因为 OCI Registry 拉镜像比拉 Git 大仓库快太多了,我们测过,拉 2GB 的 Git 仓库要 40 秒,拉 10MB 的 OCI 镜像只要 2 秒。
还有个细节:ArgoCD v2.12 支持 OCI Registry 的签名验证,我们给所有推到 Harbor 的配置镜像都做了 Cosign 签名,然后在 ArgoCD 里配置了签名验证,只有签名合法的镜像才会同步。之前有个黑客试图往我们的 Harbor 里推恶意配置镜像,结果 ArgoCD 直接拒绝了同步,因为签名不对,这个特性在 2024 年的 GitOps 安全趋势里也是重点,确实有用。
我们现在的流程是:开发改完代码,CI 构建完应用镜像,同时把对应的 GitOps 配置打包成 OCI 镜像推到 Harbor,然后更新 Application 的 targetRevision 到新的镜像 tag,ArgoCD 自动同步。整个流程下来,从代码提交到生产部署完成,最快只要 5 分钟,而且全程可追溯,有 Git 记录和 OCI 镜像的 digest 可以查,比之前的 Jenkins 流程靠谱太多了。
去年双十一大促前,我们那个包含 300+ 微服务定义的 Monorepo 仓库,在 ArgoCD v2.9 版本下同步一次全量仓库需要 5 分钟。每次发布新版本,开发同学都在群里催:"怎么还没同步完?" 我盯着 ArgoCD 的日志,发现它每次都在重复 git fetch 整个仓库,哪怕我只改了一个 Deployment 的副本数。
为什么全量同步不行?
我们的 Git 仓库当时已经膨胀到 2.1GB,包含 5 年的历史镜像标签和 Helm Chart 包。ArgoCD 默认的 reconciliation 机制是轮询式全量对比,每次都要把整个 Git 树加载到内存里做 diff。在 16G 内存的 ArgoCD 服务器上,光是解析这些 YAML 就要吃掉 12G,频繁的 Full GC 直接导致 UI 响应超时。
升级到 ArgoCD v2.12.0 (2024年10月发布) 后,我直接启用了它的 增量同步(Incremental Sync) 特性。配合 git-submodule 和 helm dependency update 的优化,我们把同步耗时压到了 30 秒以内。
核心思路是:只同步变更的路径,而不是整个仓库。
首先,在 argocd-cm 这个 ConfigMap 里开启增量同步开关。这是 v2.12 的隐藏特性,默认是关闭的。
接着,我重构了 Git 仓库的目录结构。以前是扁平化存放,现在改为按业务域拆分,并利用 ApplicationSet 的 Git 文件生成器来精准定位变更。
为什么这样做?
以前改一个 orders/payment 的配置,ArgoCD 会扫描 orders 下所有 50 个服务的目录。现在用了 directories 生成器配合路径参数,ArgoCD 只会盯着 orders/payment 这一个目录。结合 Git Webhook(我们在 GitLab 配置了 push 事件触发 ArgoCD 的 /api/webhook),ArgoCD 不再盲目轮询,而是等着被通知。
我还写了一个小脚本,用来清理 Git 历史里的二进制文件,把仓库体积从 2.1GB 压到了 400MB。这直接让 git clone 阶段的速度提升了 4 倍。
现在,我们的订单系统在大促期间发布频率是每分钟 2 次,同步延迟稳定在 30 秒左右。如果不做这个增量优化,光是 Git 拉取就要 5 分钟,根本无法响应业务的快速迭代需求。
上个月,我们测试环境的 ArgoCD 突然大面积飘红。具体表现是:负责 staging 环境的 Application 全部显示 PermissionDenied。我第一反应是 Master 节点权限变了,但查了半天,发现是 RBAC 配置里的命名空间权限没下发。
事故背景
我们有个新来的实习生,为了图省事,在 argocd-rbac-cm 里给开发组配了 * 权限。结果他手一抖,把 policy.default: role:readonly 改成了 role:admin,导致测试环境的 ArgoCD 试图去操作生产集群的 kube-system 命名空间,直接被生产集群的 RBAC 拦截了。
我登录 ArgoCD 容器内部,手动执行同步命令,看到了真实的 403 错误:
为什么会发生?
ArgoCD 的 Controller 使用的是一个统一的 ServiceAccount。如果 RBAC 配置不当,这个 Account 就会拥有过大的权限。在 v2.12 版本中,虽然支持了更细粒度的 RBAC,但默认配置仍然是粗粒度的。
我立刻回滚了 argocd-rbac-cm,并重新设计了多租户隔离方案。核心原则是:每个环境(Dev/Test/Prod)使用独立的 ArgoCD Project,且绑定独立的 ClusterRole。
我创建了独立的 ClusterRole 和 RoleBinding,限制 ArgoCD 只能操作特定命名空间。
这次事故给我的教训
不要给 ArgoCD 的 Controller 赋予 cluster-admin 权限。我现在的做法是,利用 ArgoCD v2.12 支持的项目级别的同步权限(Project Scoped Sync)。这样,即使 Git 里的配置写错了命名空间,ArgoCD 也会因为权限不足而拒绝执行,而不是报错或者造成破坏。
现在的配置下,开发同学只能看到自己项目的 Application,且同步操作被严格限制在 staging-* 的命名空间内。这种隔离设计让后续再也没出现过跨环境误删资源的事故。
最近在给公司的 MLOps 平台做架构升级,我发现 ArgoCD v2.12 的一个新特性让我眼前一亮:支持 OCI Registry 作为配置源。以前我们训练完模型,生成的 InferenceService 配置文件还得手动提交到 Git,现在可以直接推送到 Harbor 里,ArgoCD 直接拉取镜像清单来部署。
在 MLOps 场景下,模型版本迭代非常快。我们以前用 Jenkins 打包,每次更新模型版本都要改 Git 里的 values.yaml。现在,我利用 ArgoCD 的 OCI 支持,把模型服务的 Helm Chart 打包成 OCI 镜像。
为什么这么做?
AI 模型的配置文件(如 InferenceService 的 YAML)是随着模型训练结果动态生成的,不适合存在 Git 里做版本控制(Git 存的是代码,不是二进制制品的元数据)。用 OCI Registry 存配置,完美契合了 2024-2026 年 GitOps 与 AI/MLOps 融合 的趋势。如果不这么做,我们需要维护一个巨大的 Git 仓库来存这些临时配置文件,很快就会变得无法维护。
另一个让我兴奋的点是边缘计算。我们公司有很多部署在工厂车间的边缘 K8s 节点(K3s),网络极不稳定。以前用 ArgoCD,只要边缘节点断网,状态就一直 OutOfSync。
在 v2.12 里,我尝试了 轻量化部署模式。我在边缘节点只部署了一个 argocd-repo-server 的轻量版,并开启了 self-healing 的本地缓存模式。
实际效果
在工厂网络抖动时,边缘端的 ArgoCD 不再疯狂报错,而是利用本地缓存来维持应用的运行状态。一旦网络恢复,它会自动从中心仓库拉取最新的配置进行修复。这正好对应了 边缘计算适配 的趋势:GitOps 不再强依赖稳定的中心网络,而是具备了一定的自治能力。
我的判断
接下来两年,ArgoCD 的核心竞争力不在于同步速度(这已经是基础功能了),而在于 多集群联邦编排 和 安全性。比如默认启用签名验证(SLSA 标准),以及支持跨集群的流量调度。如果你现在还在用 v2.8 之前的版本,真的建议升级,因为 v2.12 在资源占用和同步效率上的优化,是实打实能省下服务器成本的。
去年我接了个跨境电商的活,他们日活大概 80 万,订单服务跑在 3 个 K8s 集群上。之前他们用脚本 + 人肉 kubectl 发版,每次上线我都提心吊胆。我决定把这套东西迁移到 ArgoCD v2.12,做纯 GitOps 模式。
刚开始挺顺,直到有天凌晨,预发环境的订单服务突然同步失败,页面一直转圈。我第一反应是 Git 仓库挂了,但检查后发现仓库正常。我翻了 ArgoCD 的日志,发现是 RBAC 权限问题:新加的一个命名空间,我在 ArgoCD 的 ClusterRole 里忘了配 resourceNames,导致 controller 没有权限更新那个 namespace 下的 Deployment。那次排查花了将近 40 分钟,后来我干脆把多租户的权限拆成按项目分 Role,每个 Application 只绑自己那一份,再也没出过类似问题。
性能上我也吃过亏。一开始他们把 2GB 的 Helm Chart 仓库直接挂给 ArgoCD,同步一次要 5 分钟。我后来改成 增量同步 + OCI Registry 存 Chart,把大仓库拆小,同步时间直接压到 30 秒以内。
* 真适合用的场景:多环境、多集群、团队超过 5 人、对回滚和审计有要求。
* 没必要上的场景:个人小项目、单集群、一天发不了一次版,上 ArgoCD 只会让你觉得它在“吃资源”。
* 选型坑:别为了“赶时髦”上 GitOps,如果你们团队连 Git 分支规范都没统一,先别急着动 ArgoCD。
如果你正准备学它,我建议你先拿一个 不重要的测试服务 跑一遍完整流程,从 Git 提交到自动同步,再到手动回滚。别一上来就动生产环境,那是真的会睡不好觉的。