如果你习惯了用 kubectl get 去看那些清爽的 YAML,那么当你第一次手贱敲下 kubectl get application quarkus-svc -n argocd -o yaml,看到 ArgoCD 的 Live Manifest 时,你大概率会骂娘。

一份几十行的 Helm Values 配置,在这个文件里像复读机一样被抄了足足 5 遍。散落在 speclast-applied-configurationhistoryoperationStatestatus.sync.comparedTo 里。

初看觉得这是屎山代码,严重浪费 etcd 的存储空间。特别是那个 status.sync.comparedTo,很多人都有一个灵魂拷问:

“既然判断应用是否同步,为什么不直接拿 Git 里的蓝图(Spec)去和集群现实对账?或者直接和 K8s 底层的出厂快照(last-applied)对账?非要在 Status 里单独再抄一份复印件,这不是脱裤子放屁吗?”

这个问题非常毒辣。它直接切开了 Kubernetes 声明式架构最核心的大动脉——控制器的异步状态机设计


一、 为什么不能直接和“出厂快照(last-applied)”对账?

K8s 老鸟都知道,当你用 kubectl apply 时,K8s 会在资源的 annotation 里打上一个 kubectl.kubernetes.io/last-applied-configuration。它是用来做三向合并(3-Way Merge)的,防止线上的人工热修被误删。

那 ArgoCD 为什么不直接拿它来对账?

因为颗粒度不对,且网络开销会把集群拖死。

出厂快照是打在**每一台具体的物理机器(Deployment、Service、Ingress 等)**身上的。假设你的微服务包含了 50 个 K8s 资源对象。如果 ArgoCD 想知道“这个应用现在到底处于什么状态”,它难道要每隔两秒钟,跨过公网(比如从阿里云连到腾讯云),把远端集群里这 50 个资源的 annotation 全部抓回来扒一遍?

这不叫对账,这叫 DDOS 攻击。

ArgoCD 作为一个宏观大管家,它必须在自己的控制层(Application 层)保留一份宏观的参数基准线,也就是我们说的 comparedTo


二、 核心命题:为什么不直接看 Spec 蓝图?

既然不能看底层,那就看顶层。Application.spec 里面明明白白写着从 GitHub 拉下来的最新图纸参数,ArgoCD 界面上的“绿灯(Synced)”和“黄灯(OutOfSync)”,直接拿 spec 和远端集群比对不就行了?

绝对不行。如果你这么干,ArgoCD 的前端 UI 每天会崩溃一万次。

这里的核心痛点是:Kubernetes 是一个极度彻底的“异步(Asynchronous)系统”。

在 K8s 的权限与职责铁律中:

  • Spec(期望区):只有**用户(人)**有权修改。它代表你想干嘛。
  • Status(状态区):只有**控制器(机器)**有权修改。它代表系统目前处理到了哪一步。

这二者之间,存在着致命的异步时间差

并发撕裂的灾难场景

假设 ArgoCD 直接拿 Spec 去渲染 UI 状态,会发生什么?

  1. 稳态:Git 里 Replicas: 1,集群也是 1,UI 显示绿灯(Synced)。
  2. 主人发令:你突然 git push,把 Replicas 改成了 10。
  3. 瞬间撕裂:ArgoCD 的 Webhook 瞬间触发,Application.spec 立刻变成了 10。但是!ArgoCD 的后台同步控制器(Controller)此时可能正在处理别的队列,还没来得及看这张新图纸
  4. UI 崩溃报警:此时前端 UI 刷新,拿最新的 Spec(10)和集群现实(1)一比,瞬间爆红闪烁,显示严重的 OutOfSync
  5. 排障地狱:值班运维收到报警一看,吓出一身冷汗,以为线上丢了 9 个 Pod。但实际上,线上稳如老狗,仅仅是管家还没开始干活而已!

三、 comparedTo:管家的“免责护身符”

为了彻底屏蔽这种“老板改了主意,但员工还没开始执行”造成的异步撕裂,ArgoCD 设计了 status.sync.comparedTo 这个绝妙的字段。

它本质上是:ArgoCD 产生当前 UI 状态时,那一瞬间所使用的“定格参数快照”。

我们用一张时序图,来看看加入了 comparedTo 之后,系统是如何优雅应对并发的:

目标 K8s 集群(真实环境)Application Status(comparedTo快照)ArgoCD Controller(后台管家)ArgoCD Web UI(前端面板)Application Spec(期望图纸)目标 K8s 集群(真实环境)Application Status(comparedTo快照)ArgoCD Controller(后台管家)ArgoCD Web UI(前端面板)Application Spec(期望图纸)阶段一:稳态 (Synced)阶段二:用户突发修改,触发异步时间差完美屏蔽了控制器还没开始干活时的“伪报警”阶段三:控制器苏醒,开始干活研发/运维 (User)1. UI 渲染强依赖 Status 区快照1绿灯 (因为 comparedTo 完全 == K8s 真实状态)22. 推送代码,修改 Replicas=10 (瞬间生效)33. UI 定时刷新4依然绿灯!(因为相比于 comparedTo 旧快照,K8s 并未偏离)54. 控制器拉取队列,拿到最新 Spec65. 覆盖重写 comparedTo = 最新 Spec76. UI 再次刷新8黄灯 OutOfSync (因为 comparedTo 已变,但 K8s 还没变)97. 跨云下发 Deployment,扩容到 10108. 集群扩容完成,真实状态追平 comparedTo119. UI 渲染:重回绿灯 Synced12研发/运维 (User)

看明白了吗?
ArgoCD 的 UI 界面根本不看 Spec。它只看管家在 Status 区留下的 comparedTo 复印件。

Spec 改变的那一瞬间,UI 不会乱报警,因为它比对的基准还是老快照。只有当控制器真真切切地开始处理这个任务,并把自己的工作纲领更新到 comparedTo 时,UI 才会名正言顺地显示出 OutOfSync

在分布式系统里,这叫做最终一致性(Eventual Consistency)的决断基准


四、 结语

在代码里讲究 DRY(Don’t Repeat Yourself)。但在云原生控制器的设计里,规则被颠覆了。

一份配置抄五遍,不是因为架构师蠢,而是因为要把一切运行时状态、历史审计和异步决断依据,全部静态地落盘在一个物理对象中。只有这样,无论控制器怎么奔溃、网络怎么延迟,只要重新读取这段 YAML,系统就能瞬间找回前世今生的记忆,知道此时此刻谁是对的,谁是错的。

弄懂了 comparedTo,你才算真正摸到了 Kubernetes “面向状态编程”的脉门。

更多推荐