读《Kubernetes 源码剖析》:控制循环才是真正的架构遗产

《Kubernetes 源码剖析》不是一本适合新手的书。它默认你已经会用 K8s,然后带你去看”这些东西为什么这样设计”。读完第一章,我意识到那些我以为自己理解的东西,其实只是停留在 API 层面——我知道怎么写 YAML,但不知道为什么这样写是对的。

真正有价值的,是理解 Kubernetes 的架构模式。这个模式已经远超容器编排这个应用场景本身。


一个本质性的设计决策

Kubernetes 的所有组件,从表面看是功能划分:有人负责调度,有人负责状态维护,有人负责网络……但从实现看,它们共享同一个底层模式:

List-Watch + 控制循环(Reconciliation Loop)

每个控制器做的事情可以用伪代码写成三行:

1
2
3
4
5
期望状态 = 从 API Server 读取目标配置
实际状态 = 观察当前真实情况
if 期望状态 != 实际状态 {
采取行动使实际状态趋近期望状态
}

这个循环是 Kubernetes 的神经系统。kube-controller-manager 里的 Deployment controller、StatefulSet controller、Namespace controller,每一个都是这个模式的一个实例。kube-scheduler 本质上也是一个控制循环——它监视 Pending 状态的 Pod,做出调度决策,将 Pod 绑定到 Node。

这个设计的深意在于:它把”如何达到目标”从”目标是什么”里分离出来了。

用户描述期望状态(我要 3 个副本),系统负责达成。这不只是 API 设计风格的问题,它改变了整个错误处理模型——节点故障、网络抖动、容器崩溃,都不需要用户处理,因为控制循环会持续修正偏差。


组件边界的设计逻辑

Kubernetes 把控制平面拆成几个独立进程,不是技术偏好,是一个清晰的设计选择:

kube-apiserver 是唯一有权读写 etcd 的组件。所有其他组件——scheduler、controller-manager、kubelet——都通过 API Server 访问集群状态,不直接触碰存储层。这条边界让整个系统的一致性模型变得可推理:你只需要关心 API Server 的一致性保证,而不是每个组件各自的存储访问。

kube-scheduler 故意设计成无状态的。它只做一件事:把 nodeName 字段为空的 Pod 绑定到合适的 Node。调度完成后,它对 Pod 的生命周期不再负责。这种职责切割让调度策略可以独立演化——你可以替换整个调度器而不影响其他组件。

kubelet 是控制平面和数据平面的边界。它是唯一真正和容器运行时交互的组件,也是控制平面指令的最终执行者。它通过 CRI/CNI/CSI 三个接口向外暴露扩展点,而不是把具体实现锁死在内部——这是为什么 Docker、containerd、CRI-O 都能作为运行时工作的原因。


Watch 机制:避免轮询的工程选择

一个常见的误解:以为各组件是”定时轮询”API Server 拿到最新状态。实际上是 List-Watch:

  • List:启动时全量拉取资源,建立本地缓存(Informer)
  • Watch:之后只接收增量变更事件(ADD/UPDATE/DELETE)

这个机制把 O(n) 的轮询开销压到了接近 O(1) 的事件驱动。在一个运行几千个 Pod 的集群里,轮询的延迟和 API Server 压力是不可接受的。Watch 机制是让 K8s 在大规模场景下可用的前提。

Informer 还有一个关键细节:它在本地维护一份缓存,控制器的 Reconcile 方法读的是这份本地缓存,而不是每次都打 API Server。这把读操作和写操作的路径完全分开了。


这个模式的边界在哪里

理解了 Kubernetes 的架构,自然会想把这套模式用到其他地方。但它也有明显的局限:

Reconciliation 适合收敛到稳态的场景,不适合强顺序的工作流。 K8s 的控制循环假设状态是可以反复比对和修正的。如果你的业务逻辑需要”先做 A,再做 B,B 依赖 A 的输出”这种严格顺序,强行套控制循环会带来额外复杂度。这就是为什么 Tekton/Argo Workflow 需要在 K8s 之上构建有向图调度层——纯控制循环不够用。

etcd 作为中心存储是性能瓶颈。 所有状态变更都要过 API Server → etcd,这在极高频率的状态更新场景下会成为限制。这也是为什么对于 AI 推理集群的任务调度,不少团队选择绕开 K8s 原生调度路径另起炉灶。

控制循环的延迟是事件驱动的,不是实时的。 一个 Pod 从被调度到真正 Running,中间要经过多个控制循环的接力,每个循环都有自己的队列和处理延迟。对于 SLA 要求在毫秒级的场景,这个模型不适用。


读源码的真实收益

读这本书之前,我用 K8s 主要是配置层面的理解——知道该写什么 YAML,知道哪些参数能调。读之后,边界感更清晰了:遇到问题时能更快判断是调度问题、控制器问题还是节点问题,而不是对着 kubectl describe 盲猜。

更重要的收益是设计模式层面的。控制循环 + 声明式 API + 可替换的扩展点——这套组合在容器编排之外的场景里仍然有效。我现在在做的 AI Agent 基础设施,很多地方也在用类似的 Reconciliation 模型来管理 Agent 的生命周期和状态。模式可以迁移,具体实现不能。

读原厂写的源码剖析,比读第三方文章有一个优势:你能看到设计者为什么这样选择,而不只是这样选择了什么。


读《Kubernetes 源码剖析》:控制循环才是真正的架构遗产
https://www.krli.org/2024/09/22/读书笔记-kubernetes源码剖析-1/
作者
李科燃
发布于
2024年9月22日
许可协议