首页
/ Kubernetes Controller-Manager 通用库解析:k8s.io/controller-manager 的定位、结构与核心抽象

Kubernetes Controller-Manager 通用库解析:k8s.io/controller-manager 的定位、结构与核心抽象

2026-09-07 19:55:46作者:侯霆垣

导读

kube-controller-manager 与 cloud-controller-manager 是 Kubernetes 控制面中最庞大的两个组件,二者在控制器生命周期管理、API Server 连通性、健康检查、指标暴露、leader election 等方面存在大量重复逻辑。k8s.io/controller-manager 正是从二者公共代码中抽离出的“控制器管理器通用库”,目标是以一套经过生产验证的公共骨架,服务 Kube-Controller-Manager、Cloud-Controller-Manager 乃至其他自研控制器管理器。本文以 staging/src/k8s.io/controller-manager/README.md 为骨架,结合该库在本仓库中的源码与配置实现,讲解它的定位、目录结构、控制器抽象接口、通用启动框架、可插拔配置项与 leader migration 机制,帮助读者理解控制器管理器类组件的通用工程范式,并掌握如何复用该库构建自己的控制器管理器。

这个库是什么:控制器管理器的公共底座

Kubernetes 中“控制器管理器”泛指这样一类进程:它负责启动并管理一批“控制器(controller)”,每个控制器持续 watch 某类资源并朝期望状态收敛,同时进程对外提供 /healthz/metrics/debug/pprof 等运维端点。README 明确指出该库的 Purpose:

  • 它包含控制器管理器的通用代码
  • 主要服务于 Kube-Controller-ManagerCloud-Controller-Manager
  • 同时也欢迎其他控制器管理器使用这份代码。

来源与演进:staging 仓库的诞生背景

README 的 “Where does it come from?” 章节说明了它的出身:该包由 kube-controller-manager 与 cloud-controller-manager 的共同代码提炼而来,承载了当前社区对“如何正确地构建一个控制器管理器”的既有认知。

从本仓库实际布局看,它的载体正是 Kubernetes 特有的 staging 外部仓库暂存区(external repository staging area)。源码真实主路径位于:

README 头部声明:本目录是自动发布的只读暂存仓库,代码变更发生在主仓库对应位置,合入 k8s.io/kubernetes 后再同步到这里;所有 Issue 与 PR 都应提交到主 Kubernetes 仓库而非此处。这一机制保证了版本跟随与单点维护,也让 k8s.io/controller-manager 可以作为独立 module 被第三方以 go.mod 依赖引入。

当前仓库内的目录骨架

从源码结构看,该库自底向上可分为几层,本仓库中的实际目录如下:

目录 职责
controller 控制器需要实现的通用接口(InterfaceDebuggableHealthCheckable
app 启动期辅助代码:等待 API Server、控制器启停判断、ControllerContext、HTTP handler 链构建
options 通用命令行 Flag 与配置填充(--controllers、QPS/Burst、leader election 等)
configconfig/v1config/v1alpha1config/v1beta1 内部配置类型与版本化 API 类型,含 LeaderMigrationConfiguration
pkg/clientbuilder 为控制器按需构造 Kubernetes client 的构建器
pkg/healthz 健康检查器封装,支持“先注册无名的检查、再由管理器命名挂载”
pkg/informerfactory 同时支持 typed 与 metadata-only informer 的工厂抽象
pkg/leadermigration 控制器在不同管理器组件间迁移 leader 身份时的安全切换机制
pkg/features 控制器管理器侧 FeatureGate 注册

控制器的三类能力接口

README 强调该库封装了“构建控制器管理器的正确姿势”。落到代码上,这份“契约”首先体现在 controller/interfaces.go,它定义了一个被管理器管理的控制器可选的三种能力:

// Interface 定义被控制器管理器管理的基础控制器
type Interface interface {
    Name() string   // 返回控制器的规范名称
}

只有 Name() 是必须实现的最基本契约,管理进程据此识别、启停并组织控制器。在此之上,库通过两个可选接口向管理器暴露扩展能力:

  • Debuggable:若控制器实现了 DebuggingHandler() http.Handler,管理进程可在启动时把返回的 handler 挂载到 /debug/controllers/{controllerName}/,用于暴露该控制器的调试信息;
  • HealthCheckable:若控制器实现了 HealthChecker() healthz.UnnamedHealthChecker,管理器可把该检查挂到 /healthz 端点,实现“控制器粒度”的健康上报。

这种“基础接口 + 可选能力接口”的设计,让公共管理框架保持最小侵入,又给了具体控制器按需注册调试与健康检查的扩展点——这正是一个可复用底座应当具备的形态。

启动期的公共骨架:等待 API Server、上下文与启停判断

控制器管理器进程启动时有一套高度重复的“仪式”,该库将其中最关键的三块抽成公共代码,均位于 app 包内。

WaitForAPIServer:先确认 API Server 可用再干活

app/helper.go 中的 WaitForAPIServer 以秒级间隔轮询 API Server 的 /healthz,直至返回 HTTP 200 或超时。实现上它通过 discovery REST client 发起 GET /healthz,失败时记录错误并继续轮询,超时后把最后一次错误一并返回:

func WaitForAPIServer(client clientset.Interface, timeout time.Duration) error {
    err := wait.PollImmediate(time.Second, timeout, func() (bool, error) {
        // ...GET /healthz,非 200 视为“尚未就绪”继续等待
    })
    // 超时时将 lastErr 包装返回
}

这一函数保证了管理器不会在控制面 API 尚未就绪时启动一批注定失败的控制循环,是任何控制平面组件“优雅启动”的第一步。

ControllerContext:传给每个控制器的启动上下文

app/controllercontext.go 定义了 ControllerContext,它是每个控制器初始化时拿到的“万物引用”:

type ControllerContext struct {
    ClientBuilder                 clientbuilder.ControllerClientBuilder
    InformerFactory               informers.SharedInformerFactory
    ObjectOrMetadataInformerFactory informerfactory.InformerFactory
    RESTMapper                    *restmapper.DeferredDiscoveryRESTMapper
    Stop                          <-chan struct{}
    InformersStarted              chan struct{}
    ResyncPeriod                  func() time.Duration
    ControllerManagerMetrics      *controllersmetrics.ControllerManagerMetrics
}

值得注意的两个设计细节:

  1. ResyncPeriod 被设计为函数而非固定值,注释解释其意图:让不同控制器拿到的 resync 周期各不相同,避免多个控制器“步调一致”地同时向 API Server 发起 list 请求造成 thundering herd;
  2. InformersStarted 通道在全部控制器初始化完成后才被关闭,在此之前单个控制器不应启动共享 informer,从而保证事件消费与控制器注册的顺序安全。

通用 HTTP 端点:/healthz、/metrics 与 profiling

控制器管理器对外暴露的运维端点也是公共的,见 app/serve.goNewBaseHandler 基于 apiserver 的 PathRecorderMux 注册一组标准端点:

  • /healthz:挂接健康检查 handler,控制器管理器自身及各控制器的健康状态在此汇总;
  • /metrics:暴露 Prometheus 指标(含 workqueue 指标,源码中通过匿名 import k8s.io/component-base/metrics/prometheus/workqueue 完成注册);
  • EnableProfiling 开启时安装 pprof profiling 路由,EnableContentionProfiling 开启时还会把 block profile rate 置为 1 以捕获锁竞争;
  • /configz:暴露当前运行时配置。

BuildHandlerChain 则把认证、鉴权、RequestInfo、Cache-Control、HTTP 日志与 panic recovery 等过滤层按固定顺序包裹在最终 handler 上,使得 KCM 与 CCM 的 Web 层行为保持一致。

通用命令行配置:--controllers 开关体系与客户端限流

控制器的启停、API 通信参数、leader election 等通用配置全部集中在 options/generic.goGenericControllerManagerConfigurationOptions 中,它由 KCM 与 CCM 共同持有。

常用 Flag 一览

AddFlags 注册的通用 Flag(映射自 GenericControllerManagerConfiguration 字段):

Flag 类型 含义
--controllers StringSlice 启用/禁用控制器列表,详见下文
--min-resync-period Duration reflector 的 resync 周期在 MinResyncPeriod2*MinResyncPeriod 之间随机取值,用于打散对 API Server 的周期性请求
--kube-api-content-type String 与 apiserver 通信时请求的 Content-Type
--kube-api-qps Float32 与 apiserver 通信的 QPS 上限
--kube-api-burst Int32 与 apiserver 通信的突发(burst)上限
--controller-start-interval Duration 依次启动各控制器的间隔,避免瞬时并发启动风暴

此外还通过 options.BindLeaderElectionFlags 注入 --leader-elect 等一系列 leader election 参数,并挂接 debugging 与 leader-migration 两组 FlagSet。

--controllers 的语义与 IsControllerEnabled 判定

Flag 帮助文本给出了完整语义:'*' 启用全部默认开启的控制器,'foo' 启用名为 foo 的控制器,'-foo' 禁用名为 foo 的控制器。真正执行判断的是 app/helper.go 中的 IsControllerEnabled

func IsControllerEnabled(name string, disabledByDefaultControllers sets.String, controllers []string) bool {
    for _, ctrl := range controllers {
        if ctrl == name        { return true }  // 显式启用
        if ctrl == "-"+name    { return false } // 显式禁用
        if ctrl == "*"         { hasStar = true }
    }
    if !hasStar { return false }              // 无星号且未点名 → 不启用
    return !disabledByDefaultControllers.Has(name) // 星号下排除默认禁用项
}

其判定规则可归纳为:逐条扫描 Flag 列表,先显式点名者胜出;若出现 * 则默认开启除“默认禁用集”以外的全部控制器;既无点名也无 * 则一律不启用。这保证了未显式声明的控制器不会被意外拉起,同时默认禁用列表中的高危控制器不会因 * 而误启。

ApplyTo:Flag → 配置对象 + 控制器别名归一化

ApplyTo 负责把 Flag 值写入 GenericControllerManagerConfiguration,其中有一段对“控制器别名”的归一化处理:遍历用户传入的名称,去掉可选的 - 前缀后在 controllerAliases 映射中查找规范名并替换,再恢复 - 前缀写回。也就是说,KCM/CCM 中用户可能习惯用旧名(如 service 指代 service-controller),配置层会在落盘前统一收敛为规范控制器名,供后续启停判断使用。

Leader Migration:跨组件迁移控制器时的锁交接机制

README 提到库直接服务于 KCM 向 CCM 演进的过程——而这正是 pkg/leadermigration 子系统存在的理由。在“云厂商逻辑逐步从 KCM 迁往 CCM”的历史阶段,同一控制器可能先后由两个管理器进程负责,若两者同时持有 leader 锁会产生双主风险。该库以一套 迁移配置 + 锁过滤 机制规避:

  • 版本化配置类型定义在 config/v1/types.goLeaderMigrationConfiguration 包含一个 LeaderName(保护本次迁移的 leader election 资源名,例如 1-20-KCM-to-1-21-CCM)与一组 ControllerLeaders
  • 每个 ControllerLeaderConfiguration 声明:某个控制器(Name)在迁移期间应运行于哪个组件(Component,取 kube-controller-managercloud-controller-manager'*'——后者表示允许参与迁移的任意组件运行该控制器);
  • pkg/leadermigration 目录下的 filter.gomigrator.go(另有 migrator_test.go 等配套测试)在启动期根据该配置决定:哪些控制器的 leader 锁在“旧组件”上被让出、哪些在“新组件”上被接管,从而实现滚动迁移过程中安全的领导权交接。

通用配置中的迁移开关

迁移配置还暴露在通用选项中:options/generic.goLeaderMigration 字段持有 migration.LeaderMigrationOptions,并通过独立的 leader-migration FlagSet 注入 Flag,且在 Validate 中对 LeaderElection.ResourceLock 施加约束(强制使用 leases,代码注释“Lock the ResourceLock using leases”说明这是为废弃旧 ResourceLock 留下的保护性校验)。

支撑性基础包:健康检查、客户端构建与 informer 工厂

除启动骨架外,该库还沉淀了一批被控制器直接引用的基础组件。

healthz:先注册无名检查,再由管理器统一命名

healthz.go 提供一个精巧的“命名解耦”:UnnamedHealthChecker 是不带名称的检查器,NamedHealthChecker(name, check) 可将其包装为带名检查。这样控制器只需关心“检查逻辑本身”,而“挂到什么名字下、暴露在哪个端点”完全由管理器决定——与 controller/interfaces.goHealthCheckable 的设计一一对应。

clientbuilder:按需构造控制器专用客户端

pkg/clientbuilder 提供 ControllerClientBuilder,允许控制器在 ControllerContext.ClientBuilder 上按自身命名空间、身份等约束获取独立的 clientset,避免所有控制器共享同一份凭证与限流窗口。

informerfactory:typed 与 metadata 的统一视图

pkg/informerfactory/informer_factory.go 实现 InformerFactory 接口:对给定 GroupVersionResource 优先从 typed informer factory 取 informer,取不到则回退到 metadata informer factory(仅同步对象元数据)。这一“typed 优先、metadata 兜底”的策略正好对应 ControllerContext.ObjectOrMetadataInformerFactory 的注释说明——当前绝大多数通用控制器只消费对象元数据,需要全量对象的未来控制器则可通过持有 dynamic client 的工厂扩展。

兼容性边界与使用约束:README 的“禁区”清单

README 最后以两个明确的“Things you should NOT do”划定使用边界,任何复用该库的开发者都应牢记:

  1. 不要直接修改 pkg 下的任何文件。这些文件由 k8s.io/kubernetes/staging/src/k8s.io/controller-manager 驱动生成/同步,改动需进入主仓库对应位置,本目录对直接贡献是只读的;
  2. 不要期待兼容性保证。README “Compatibility” 章节明确声明:该仓库目前没有任何兼容性保证,它直接支撑 Kubernetes,分支会跟随 Kubernetes 主版本并与之保持兼容;随着各层被更干净地分离,社区会重新评估兼容性承诺,并设定了“未来让该库更容易被使用”的目标。

从这两条约束可以读出该库的工程定位:它是一份随 Kubernetes 主版本演进、当前快速变化中的共享实现,而非稳定发布的 SDK。第三方如需依赖它,应以 vendor/跟随 Kubernetes 版本的方式使用,并接受其内部 API 可能随版本调整。

小结:从公共库到自研控制器管理器的复用路径

回到 README 的初衷——把 KCM 与 CCM 的共同认知沉淀为可复用代码。纵观本仓库中的 controller 接口app 启动骨架options 通用配置leader migration 配置,一套自研控制器管理器可以直接复用的部件已相当完整:以 Interface/Debuggable/HealthCheckable 定义控制器能力,借 ControllerContext 注入 client、informer 与限流随机化,用 --controllers 语义统一启停管理,通过 NewBaseHandler 获得与官方组件一致的运维端点,再以 leader migration 支撑灰度接管。而 README 同时提醒:复用它的前提是接受“无兼容性保证 + 随主版本演进”的现实。理解这两面,才算真正掌握了 Kubernetes 控制器管理器类组件的构建范式。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388