Controller 注册后的完整调用时序
整个流程分五个阶段:注册 → 构造 → 启动 Informer → 并发启动 Controller → 阻塞运行。
完整时序图
scss
kube-controller-manager 进程启动
│
├─ 阶段 0: 注册 (启动时立即执行,一次性)
│ NewControllerDescriptors()
│ [controller_descriptor.go:138]
│ │
│ └─ 返回 map[string]*ControllerDescriptor ← 仅元数据,不实例化
│ (49 个 Descriptor,包含 name + constructor)
│
├─ 阶段 1: Leader Election (阻塞等待)
│ [controllermanager.go:397]
│ │
│ └─ leaderElectAndRun() → 竞争 Lease 锁
│ │
│ ├─ 未获取到锁 → 阻塞在 acquire(),每 2s 重试
│ │
│ └─ 获取到锁 → OnStartedLeading 回调
│ │
│ ▼
│ ┌─────────────────────────────────────────────────────────┐
│ │ 阶段 2: 构造所有 Controller (单线程,顺序执行) │
│ │ [controllermanager.go:268-298] │
│ │ │
│ │ run := func(ctx, controllerDescriptors) { │
│ │ ① CreateControllerContext() ← 创建共享上下文 │
│ │ ② BuildControllers() ← 逐个构造 │
│ │ ③ InformerFactory.Start() ← 启动 Informer │
│ │ ④ RunControllers() ← 并发启动 │
│ │ } │
│ └─────────────────────────────────────────────────────────┘
│ │
│ ▼
│ ① CreateControllerContext() [controllermanager.go:530]
│ │
│ ├─ 创建 SharedInformerFactory (共享 Informer)
│ ├─ 创建 metadata Informer Factory
│ ├─ 等待 API Server 就绪 (10s 超时)
│ └─ 创建 RESTMapper (用于 GC Controller)
│
│ ② BuildControllers() [controllermanager.go:626]
│ │
│ ├─ 第 1 个: SA Token Controller (特殊处理,必须最先)
│ │ └─ constructor() → NewServiceAccountTokenController()
│ │
│ └─ 第 2~49 个: 按注册顺序构造
│ for _, desc := range controllerDescriptors {
│ if desc.RequiresSpecialHandling() → skip
│ if !IsControllerEnabled(desc) → skip
│ if FeatureGate 未启用 → skip
│ desc.BuildController() → constructor()
│ }
│
│ ③ InformerFactory.Start(stopCh) [controllermanager.go:284]
│ │
│ └─ 启动所有 Informer 的 Watch 连接
│ 此时 Controller 还没开始 Run,但 Informer 已在缓存数据
│
│ ④ close(controllerContext.InformersStarted) [controllermanager.go:287]
│ │
│ └─ 广播信号: "Informer 已就绪,Controller 可以开始消费数据了"
│
│ ⑤ RunControllers() [controllermanager.go:706]
│ │
│ └─ 并发启动所有 Controller (详见下方)
│
└─ 阶段 3: 阻塞运行,直到 ctx 被取消
RunControllers 的并发启动细节
controllermanager.go#L710-L795
Go
go
func RunControllers(ctx, controllerCtx, controllers, jitter, shutdownTimeout) bool {
// ① 每个 Controller 一个 goroutine,全部并发启动
for _, controller := range controllers {
go func(ctrl Controller) {
// ② Jitter: 随机延迟后启动,防止所有 Controller 同时发起 List 请求
time.Sleep(wait.Jitter(
controllerCtx.ComponentConfig.Generic.ControllerStartInterval.Duration, // 默认 0s
controllerStartJitterMaxFactor, // 默认 1.0
))
// ③ 调用 Controller.Run(ctx),阻塞直到 ctx 被取消
ctrl.Run(ctx)
}(controller)
}
// ④ 等待 ctx 被取消(SIGTERM / Leader Election 丢失)
<-ctx.Done()
// ⑤ 等待所有 Controller 在 shutdownTimeout 内优雅退出
// 超时则强制返回 false
}
关键设计:
- Jitter 防惊群 :所有 Controller 不会同时启动,而是随机错开。默认
ControllerStartInterval为 0,jitter factor 为 1.0,所以实际效果是 0~0 秒的随机延迟(默认无延迟),但生产环境可配置 - 每个 Controller 的
Run()是阻塞的 :以 ReplicaSet Controller 为例,它的Run()方法内部启动 worker goroutine 处理队列中的事件,然后阻塞在<-ctx.Done()
controllerLoop 包装器
每个 Controller 的构造函数返回的是 *controllerLoop(controller_utils.go#L48-L67):
Go
go
type controllerLoop struct {
name string
run runFunc // func(ctx context.Context)
}
func (loop *controllerLoop) Name() string { return loop.name }
func (loop *controllerLoop) Run(ctx context.Context) { loop.run(ctx) }
以 Deployment Controller 为例(apps.go#L145-L147):
Go
go
return newControllerLoop(func(ctx context.Context) {
dc.Run(ctx, int(controllerContext.ComponentConfig.DeploymentController.ConcurrentDeploymentSyncs))
}, controllerName), nil
// ↑
// dc.Run() 内部启动 N 个 worker goroutine,然后阻塞在 <-ctx.Done()
// 所以 controllerLoop.Run() 也是阻塞的
完整时间线总结
scss
t=0 进程启动,NewControllerDescriptors() 注册 49 个 Descriptor
│
t=1 Leader Election 开始,阻塞等待获取 Lease 锁
│ (可能立即获取,也可能等待数秒到数分钟)
│
t=N 获取到 Leader 锁,OnStartedLeading 回调触发
│
├─ CreateControllerContext() ~100ms (创建 Informer、等 API Server)
├─ BuildControllers() ~500ms (构造 49 个 Controller)
│ ├─ SA Token Controller 最先
│ └─ 其余 48 个按注册顺序
├─ InformerFactory.Start() ~即时 (启动 Watch,开始缓存)
├─ close(InformersStarted) ~即时 (通知 Controller 数据就绪)
└─ RunControllers() ~即时 (49 个 goroutine 并发启动)
│
t=N+1 所有 Controller 并发运行中
│ (每个 Controller 内部启动 N 个 worker 处理事件队列)
│
t=M 收到 SIGTERM 或 Leader Election 丢失
│
├─ ctx 被取消
├─ 每个 Controller.Run() 返回
├─ 等待 shutdownTimeout (默认 0s = 不限时)
└─ 进程退出
总结
| 阶段 | 时机 | 操作 | 并发性 |
|---|---|---|---|
| 注册 | 进程启动时 | NewControllerDescriptors() 创建 Descriptor map |
单次同步 |
| 构造 | Leader 获取后 | BuildControllers() 逐个调用 constructor |
单线程顺序 |
| Informer 启动 | 构造完成后 | InformerFactory.Start() 启动所有 Watch |
内部并发 |
| Controller 启动 | Informer 就绪后 | RunControllers() 每个 Controller 一个 goroutine |
全部并发 |
| 运行 | 启动后 | 每个 Controller 内部启动 N 个 worker | 全部并发 |
| 退出 | ctx 取消后 | 等待所有 goroutine 返回 | 并发退出 |