应直接用 controller-runtime 写 operator;避免 reconcile 中手写 get+update 导致 resourceversion 冲突、缓存不同步或 terminating 卡住,改 status 用 status().update(),改 spec 用 patch() 或 server-side apply,读取关联资源后勿全量 update 而应 patch,加 finalizer 处理删除逻辑。

直接用 controller-runtime 写 Operator,别从 client-go 原生起步——90% 的人会在 Reconcile 里手写 Get+Update 导致 ResourceVersion 冲突、缓存未同步就查对象、或删资源时卡在 Terminating 状态。
为什么 Reconcile 函数总返回 conflict 错误
这是最常被忽略的并发模型问题:Reconcile 不是单次执行,而是被反复调用;每次拿到的 CR 对象都是独立副本,Update() 全量提交会撞上其他协程或用户直改导致的 ResourceVersion 变更。
- 永远不要对 CR 对象调用
Update()—— 改Status用SubResourceClient().Status().Update(),改Spec必须走 server-side apply 或 patch(mgr.GetClient().Patch()) - 读取关联资源(如
Deployment)后,不要直接deployment.Spec.Replicas = xxx再Update(),而应构造jsonpatch或用Apply()避免覆盖字段 -
Reconcile返回非空error会触发重试,但默认无退避;若需延时重试,返回ctrl.Result{RequeueAfter: 10 * time.Second},别用time.Sleep
controller-gen 生成的 CRD 为什么没生效
现象是 kubectl get mysqlclusters.database.example.com 报错 the server doesn't have a resource type "mysqlclusters",但 config/crd/bases/ 下的 YAML 已 kubectl apply 成功。
针对 Kubernetes 仪表板和 Web UI 的浏览器自动化。适用于与 Kubernetes Dashboard、Grafana、ArgoCD UI 或其他 Web 界面交互。需要设置 MCP_BROWSER_ENABLED=true。
- 检查
api/v1/mysqlcluster_types.go里是否漏了//+kubebuilder:object:root=true和//+kubebuilder:subresource:status注释——controller-gen完全依赖这些标记生成 CRD 字段 - 确认
main.go中是否调用了databasev1.AddToScheme(scheme.Scheme),否则mgr.GetClient()根本不认识这个类型,连Get()都会 panic - CRD YAML 中的
spec.version(如v1alpha1)必须和 Go struct 的GroupVersionKind().Version严格一致;多版本 CRD 需额外写 Conversion Webhook,不能靠 client 自动转换
删除自定义资源时控制器不响应
用户执行 kubectl delete mysqlcluster my-db 后,资源卡在 Terminating,控制器日志完全静默。
- 根本原因是没加
Finalizer:在Reconcile中检测到mydb.DeletionTimestamp != nil时,必须先给对象加上mydb.Finalizers = append(mydb.Finalizers, "finalizer.database.example.com")并Update(),再开始清理关联资源(如删StatefulSet) - 清理完成后,再把该
Finalizer从列表中移除并Update()—— Kubernetes 只有看到 Finalizer 清空,才会真正删除对象 - 千万别在加 Finalizer 前就删底层资源,否则清理失败后无法重入,对象永久卡住
Operator 的复杂性不在业务逻辑,而在状态机建模:Spec 是用户声明的“愿望”,Status 是你观测到的“现实”,Finalizer 是你承诺的“善后义务”。这三个字段之间的时序与竞态,才是调试中最耗时间的部分。










