能跑起来的最小控制器必须完成三步:注册Scheme(调用AddKnownTypes避免no kind注册错误)、启动Manager(作为Client/Cache/Scheme容器)、编写Reconcile函数(幂等处理状态收敛);缺一不可。

能跑起来的最小控制器,三步:注册 Scheme、启动 Manager、写 Reconcile 函数。缺一不可,漏掉 Scheme 注册会 panic 报 no kind "MyResource" is registered。
初始化 controller-runtime Manager 并注册 Scheme
Manager 是控制器的运行时容器,它管理 Client、Cache、Scheme 和生命周期。没它,client.Get 或 client.List 都无法工作。
- 必须调用
mgr.GetScheme().AddKnownTypes()注册你操作的资源类型(包括 CRD 的 Go struct),否则 client 无法反序列化响应 - 对自定义资源,要先 import 对应的 API 包(如
example.com/myoperator/api/v1alpha1),再调用AddKnownTypes(myv1alpha1.SchemeGroupVersion, &myv1alpha1.MyDatabase{}) - 别跳过
mgr.AddHealthzCheck和mgr.AddReadyzCheck—— 生产环境里 readiness probe 失败会导致 Pod 被反复重启
监听资源并绑定 Reconcile 逻辑
Controller 默认不监听任何资源,必须显式声明“我对谁感兴趣”。只调 For() 不够,不接 .Complete(r) 就不会真正启动 Informer。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 监听自定义资源:
ctrl.NewControllerManagedBy(mgr).For(&myv1alpha1.MyDatabase{}).Complete(r) - 监听关联资源(比如 MyDatabase 创建后要查 Secret):
.Owns(&corev1.Secret{}),这样 Secret 变更也会触发该 CR 的 Reconcile - 想监听所有命名空间的 Pod?用
.For(&corev1.Pod{});只监听某 namespace?得在Reconcile函数里手动检查req.Namespace,或改用Watches+ 自定义 EventHandler - 忘记
.Complete()的典型现象:日志里没有Starting workers,Reconcile完全不被调用
编写幂等、可重试的 Reconcile 函数
Reconcile 不是“收到事件就创建资源”,而是“读取当前状态 → 计算偏差 → 打补丁修复”。它会被反复调用,必须能安全重入。
立即学习“go语言免费学习笔记(深入)”;
- 读取 CR 实例用
r.Get(ctx, req.NamespacedName, &cr),别用client.Get直接传 manager.Client —— 注入的 client 已带缓存和权限上下文 - 更新 Status 必须走
r.Status().Update(ctx, &cr),直接client.Update会被 API Server 拒绝 - 修改 Spec 关联资源(如 Deployment)时,优先用
client.Patch(ctx, obj, client.MergeFrom(original)),避免resourceVersion conflict - 遇到
client.IsNotFound(err)要单独处理,不能直接 return error,否则会无限重试并刷爆日志
最容易被忽略的是:CRD 必须先 kubectl apply -f config/crd/bases/(或 make install),RBAC 必须包含 get/list/watch 权限,否则控制器连事件都收不到——它不会报错,只会安静地不干活。

















