最轻量主流路径是controller-runtime;需手动注册Scheme(含CRD类型)、调用Complete()启动监听、确保Reconcile幂等快速,并等待cache同步后才触发。

用 Go 开发 Kubernetes 控制器,最轻量、最主流的路径是 controller-runtime,不是从 client-go 手搓 Informer 和 Workqueue。它把底层复杂性包掉了,但关键初始化和资源注册逻辑你必须亲手写对——漏一步,Reconcile 就 panic 或收不到事件。
初始化 manager 时必须注册 Scheme 和 Client
manager 是整个控制器的运行容器,但它默认只认识内置资源(Pod、Deployment 等)。一旦你要操作 CRD,就必须提前把它的 Go 类型注册进 mgr.GetScheme(),否则 r.Client.Get() 会直接 panic:
no kind "MyApp" is registered for version "example.com/v1"
常见做法是在 main() 里调用:
立即学习“go语言免费学习笔记(深入)”;
if err := myv1.AddToScheme(mgr.GetScheme()); err != nil {
log.Fatal(err)
}注意:AddToScheme 是由 kubebuilder 或 controller-gen 自动生成在 api/.../zz_generated.deepcopy.go 附近的函数,不是手写的。没生成?说明没跑 make generate。
监听资源必须 For() + Complete(),不能只写 For()
ctrl.NewControllerManagedBy(mgr).For(&corev1.Pod{}) 只是声明“我想看 Pod”,但不启动任何 Informer。真正注册监听、拉取缓存、触发事件,靠的是后面的 .Complete(r)。
没加 Complete() 的典型表现:
- 日志里看不到
Starting workers -
Reconcile函数永远不会被调用 - 手动
kubectl applyCR 后毫无反应
如果你要监听多个资源(比如 CR 变了要查它所属的 Service),别在 Reconcile 里反复 r.Client.Get() —— cache 可能还没热加载完,容易返回 NotFound。应该用 Watches() 显式注册额外 Watch:
ctrl.NewControllerManagedBy(mgr).
For(&myv1.MyApp{}).
Owns(&corev1.Service{}).
Complete(r)这样 Service 的变更也会触发同一 Reconciler,且走的是 cache 查询,快且可靠。
Reconcile 必须幂等、快速、不阻塞
Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) 是你的业务入口,但它不是一次性的“处理完就结束”。Kubernetes 会反复调用它,可能因网络抖动重试、cache 刷新、甚至人为修改资源而再次触发。
所以:
- 不要在
Reconcile里做耗时同步操作(如 HTTP 请求、大文件读写);该异步的用 Goroutine + Channel 或外部队列解耦 - 每次执行都应先
Get当前资源状态,再对比期望值;避免“假设上次已创建成功”这类状态依赖 - 遇到
NotFound错误,用client.IgnoreNotFound(err)而不是直接返回 error,否则会进重试死循环 - 返回
ctrl.Result{RequeueAfter: 30 * time.Second}表示延迟重入,比无限重试更可控
本地调试时尤其要注意:manager 启动后,cache 默认要等全量同步完成才开始投递事件。如果没等 mgr.GetCache().WaitForCacheSync(ctx) 就开始测试,Reconcile 可能永远不触发——这不是 bug,是设计使然。
本地调试需绕过 RBAC,且注意 kubeconfig 权限
在集群外运行控制器(比如本地 IDE 启动),靠 kubeconfig 连 API Server。这时候你得确保当前用户有对应资源的 get、list、watch、update 权限,否则 Reconcile 里 r.Client.Update() 会报 Forbidden。
快速验证方式:
- 用
kubectl auth can-i --list检查权限 - 临时给当前 user 绑定
cluster-admin(仅开发环境):kubectl create clusterrolebinding debug-me --clusterrole=cluster-admin --user=your-user-name - 确认
kubeconfig指向正确上下文:kubectl config current-context
最容易被忽略的一点:CRD 必须已在集群中 kubectl apply -f crd.yaml 成功注册,且 STATUS 是 Accepted。否则 manager 启动时虽然不报错,但后续所有对该 CR 的操作都会静默失败。


















