Skip to content

源码实证:Deployment controller 如何收敛 ​

专题索引 / 上一篇:源码阅读:从行为问题追到架构边界

本页固定阅读 Kubernetes v1.31.0。问题只有一个:Deployment 的 spec.replicas 改为 5 后,controller 怎样从事件到状态收敛,而不是在 API 请求返回时假装已经完成。

text
Deployment / ReplicaSet / Pod 事件
  -> informer handler
  -> workqueue 中的 namespace/name key
  -> worker
  -> syncDeployment(key)
  -> 读取当前 Deployment、ReplicaSet、Pod
  -> 按状态选择 scale、rollout 或 status 路径
  -> API 写入引发下一轮事件

Deployment controller 并不直接把某个进程启动成容器。它修改 ReplicaSet 与 Deployment 状态;ReplicaSet controller、scheduler、kubelet 再各自完成后续收敛。因此 API 写入成功、ReplicaSet 创建成功、Pod 创建成功、Pod Ready、Deployment available 是不同完成点。

1. 组装:controller manager 把依赖注入 controller ​

cmd/kube-controller-manager/app/apps.go 注册 Deployment controller,并在 startDeploymentController 中传入三份 informer:Deployment、ReplicaSet、Pod,以及专用 client。随后以配置的 ConcurrentDeploymentSyncs 启动 dc.Run。

text
controller manager
  -> NewDeploymentController(deployment informer, rs informer, pod informer, client)
  -> dc.Run(ctx, worker count)

这处组装就是架构边界:controller 不自己创建 watch 连接,也不自行管理 API 客户端配置;它接收已组装的缓存和客户端,因此更容易测试、替换和统一治理。

2. 事件:不是把对象交给业务逻辑 ​

在 deployment_controller.go,构造函数创建名为 deployment 的 rate-limiting queue,为三份 informer 注册 handler,并保存 lister 与 cache-sync 函数。

Deployment 的 add/update/delete handler 都调用 enqueueDeployment。enqueue 只用 controller.KeyFunc 生成 namespace/name key 并执行 queue.Add(key),源码见 400-408 行。

text
对象变化 -> key "namespace/name" -> queue

这里不把 Deployment 对象副本塞进队列。处理时对象可能早已更新,正确动作是由 key 重新取当前状态。ReplicaSet 与 Pod 的事件也会反向 enqueue 所属 Deployment:controller 不假定只有 Deployment 自身变化才需要重算。

3. worker:同一 key 的串行、不同 key 的并发 ​

Run 先等待三份 informer cache 同步,才启动多个 worker。worker 与 processNextWorkItem 从队列取 key,调用 syncHandler(ctx, key),然后依据错误结果 Forget 或 AddRateLimited。

源码注释明确:同一 key 不会并发调用 syncHandler。多个 worker 仍能同时处理不同 Deployment,因此这个设计同时避免单对象竞态,又不把整个集群串行化。

text
Get(key)
  -> syncHandler(key)
  -> Done(key)
  -> 成功:Forget(key)
  -> 可重试失败:AddRateLimited(key)
  -> 超过重试预算:记录错误后 Forget(key)

“超过重试预算后移出队列”不是代表系统已经正确;它代表这次自动重试停止。后续对象事件、人工修改或控制器重启仍可能再次触发收敛。告警与 status/事件是把这种未收敛状态暴露给运维的必要补充。

4. syncDeployment:从当前事实选择分支 ​

syncDeployment 先拆 key,从 dLister 读取当前 Deployment;对象不存在时正常结束。读出的 cache 对象会 DeepCopy,避免 controller 修改 informer cache。

随后它做两类事实收集:

text
getReplicaSetsForDeployment
  -> 根据 ControllerRef 管理、adopt 或 orphan ReplicaSet

getPodMapForDeployment
  -> 读取匹配 selector 的 Pod
  -> 依 ReplicaSet UID 分组

getReplicaSetsForDeployment 在需要 adopt 前会做一次 uncached recheck,防止基于过期 cache 接管一个已经删除或重建的 Deployment。这说明“从 lister 读当前状态”不是盲信 cache;关键生命周期操作会补足更强的一致性检查。

主要分支如下:

条件处理
Deployment 正在删除仅同步 status
spec.paused=true更新暂停相关 condition,再同步状态
有 rollback 请求先执行 rollback,等待后续 enqueue
识别为 scaling event同步 ReplicaSet 副本与状态
Recreate 策略rolloutRecreate
RollingUpdate 策略rolloutRolling

这张分支表比“Deployment 会滚动发布”更接近真实架构:controller 的核心不是固定创建 Pod,而是对当前对象状态选择下一步能安全缩小差异的动作。

5. 一个 scale=5 的时间线 ​

text
t0 用户更新 Deployment spec.replicas=5
t1 API Server 持久化并 watch 通知 informer
t2 handler 将 Deployment key 入队
t3 worker 从 cache 读 Deployment、RS、Pod
t4 controller 判断为 scaling event,更新受管 ReplicaSet
t5 ReplicaSet controller 创建/删除 Pod 对象
t6 scheduler 绑定 Node,kubelet 启动容器
t7 Pod Ready 变化触发相关 controller 再次同步
t8 Deployment status 反映 observed generation、可用副本和 condition

t4 以后由不同控制器和 Node 承担。若 Pod 因镜像、配额、存储、网络或探针失败,Deployment controller 的正确行为不是绕过这些边界直接重试“启动容器”,而是从后续对象状态重新判断 rollout/scale 与 status。

6. 这段源码给设计者的规则 ​

  1. 事件只携带足以定位对象的 key;工作时重新读取事实。
  2. 一个资源键需要串行化,资源之间应允许并行。
  3. 先等 cache 初始同步,再开始基于 cache 做决策。
  4. ownership 不只是删除便利,它决定谁可以安全 adopt、更新和收敛关联资源。
  5. 把全局资源准备、对象控制和 Node 执行拆给不同组件,完成状态必须逐层观察。

可复现阅读与验证 ​

text
git clone --branch v1.31.0 --depth 1 https://github.com/kubernetes/kubernetes.git
rg -n "NewDeploymentController|syncDeployment|processNextWorkItem|enqueue\(" \
  pkg/controller/deployment/deployment_controller.go
rg -n "startDeploymentController" cmd/kube-controller-manager/app/apps.go

源码阅读完成后,应把这个静态链与真实集群证据对齐:Deployment/ReplicaSet/Pod 的 generation、observedGeneration、conditions、Events、controller 日志和最终 Pod Ready。只看源码不能证明某个集群的 CNI、CSI、镜像仓库或 Node 是否健康。

资料 ​

本文的函数和行号固定到 v1.31.0;升级到其他版本前,应重新确认注册路径、queue 类型、同步分支和 API 状态语义。

学习规划与研究资料库