应选用 controller-runtime 而非直接使用 client-go,因其在 client-go 基础上封装了 Informer 启动、Reconcile 重试、OwnerReference 绑定、Finalizer 清理等易错控制循环基建,提升开发效率与可靠性。

为什么不用 client-go 原生写,而要选 controller-runtime
直接上 client-go 写 Operator 是可行的,但你会反复处理 Informer 启动、Reconcile 重试、OwnerReference 绑定、Finalizer 清理这些固定逻辑。controller-runtime 就是把它们打包成可复用的抽象——它不是替代 client-go,而是建在它之上,帮你绕开最易出错的控制循环基建。
常见错误现象:Reconcile 函数里手动调 Get + Update 却没处理冲突(ResourceVersion mismatch),或忘记加 Finalizer 导致删除卡住;又或者 Informer 没等缓存同步就进 Reconcile,查不到关联对象。
- 用
ctrl.NewControllerManagedBy(mgr)启动控制器,它自动处理缓存同步等待 -
Reconciler实现必须返回ctrl.Result和error:非空error触发重试,ctrl.Result.RequeueAfter控制延时,不要用time.Sleep - 所有对象操作走
mgr.GetClient()(不是mgr.GetAPIReader()),它带写权限且支持结构化 patch
如何让自定义资源 CRD 被 controller-runtime 正确识别
CRD 文件本身只是 Kubernetes 的 schema 定义,controller-runtime 不会自动加载它。你得在 Go 代码里显式注册 Go struct 到 Scheme,否则 mgr.GetClient().Get() 会报 no kind "MyApp" is registered for version "example.com/v1"。
使用场景:本地开发调试时 CRD 已 kubectl apply,但 controller 启动失败;或者测试中用 envtest 启嵌入式 API server,忘了注册类型。
立即学习“go语言免费学习笔记(深入)”;
- 在
main.go初始化mgr前,调用myappv1.AddToScheme(scheme.Scheme)(该函数由controller-gen自动生成) - 确保你的 CRD YAML 中
spec.version和 Go struct 的GroupVersionKind().Version严格一致(比如都是v1,不是v1alpha1) - 如果用了多版本 CRD,scheme 注册只认一个版本(通常是存储版本),其他版本需靠 Conversion Webhook,别指望 client 自动转
Reconcile 函数里怎么安全地更新状态字段而不覆盖 spec
Operator 最常踩的坑:用 Update() 全量更新 CR,结果把用户刚改的 spec.replicas 覆盖回旧值。Kubernetes 的推荐做法是只 patch status 子资源,且必须用 Status() 客户端。
性能影响:直接 Update() CR 会触发整个对象的 etcd 写+全量 watch 事件;而 Status().Update() 只写 status 字段,watch 事件也仅限 status 变更。
- 永远用
client.Status().Update(ctx, instance)更新状态,不要用client.Update(ctx, instance) - 更新前先深拷贝原对象:
newInst := instance.DeepCopy(),再改newInst.Status.Conditions或newInst.Status.ObservedGeneration - 记得在 CRD YAML 里开启
subresources.status: {},否则Status().Update()会 404
本地调试时如何避免反复 apply CRD 和 RBAC
每次改代码都 kubectl apply -f config/crd + config/rbac 很慢,而且容易漏掉 kubectl delete 清理,导致新旧权限混杂、Webhook 失效。
controller-runtime 提供 envtest 包,能启动轻量级 fake API server,完全跳过集群依赖。但它默认不加载你的 CRD —— 必须手动注册。
- 在 test 文件里用
testEnv.Start()前,调scheme.Scheme.AddKnownTypes(myappv1.GroupVersion, &myappv1.MyApp{}, &myappv1.MyAppList{}) - RBAC 权限不用配:envtest 下 client 有全量权限,测试 focus 在逻辑,不是鉴权
- 真实集群调试时,用
make install(kubebuilder 生成)或kustomize build config/crd | kubectl apply -f -确保 CRD 和 RBAC 一次到位
最容易被忽略的是:controller-runtime 的 log 默认不输出到终端,调试时加一句 ctrl.SetLogger(zap.New(zap.UseDevMode(true))),否则 log.Info() 全静默。










