CRD定义必须写在Go类型中而非YAML文件里,因其由controller-gen从+kubebuilder:validation等注释及JSON标签自动生成;手动修改config/crd/bases/下的YAML会被覆盖,且缺失tag或注释将导致CRD注册失败、控制器无法响应事件。

CRD 定义必须写在 Go 类型里,不是 YAML 文件中
很多人第一次跑 kubebuilder create api 后,直接去改 config/crd/bases/ 下的 YAML,结果 make manifests 一执行就覆盖掉——因为 CRD YAML 是从 Go 结构体里的注释(如 //+kubebuilder:validation:Minimum=1)和字段 tag 自动生成的。
常见错误现象:kubectl apply -f 成功但控制器收不到事件,或 client.Get 返回 NotFound;根本原因是 CRD 没正确注册,而根源常是 Go 类型里缺 json: tag 或漏了 +kubebuilder 注释。
-
Spec和Status字段必须显式声明,且带json:"spec"/json:"status"tag,否则 controller-runtime 无法序列化 - 所有可配置字段建议加
// +kubebuilder:default:=...和// +kubebuilder:validation:...,否则 API Server 不校验输入,容易引发运行时 panic - 不要手动编辑
config/crd/bases/下的文件——它只是生成物,修改后会被覆盖
Reconcile 函数必须处理 resourceVersion 冲突
并发 reconcile 或快速更新 CR 时,client.Update() 极易触发 409 Conflict 错误,报错信息类似 the object has been modified; please apply your changes to the latest version and try again。这不是 bug,是 Kubernetes 的乐观锁机制在起作用。
典型错误做法:拿到 CR 实例 → 改 Status 字段 → 直接 r.Update(ctx, &cr) → 忽略错误或简单重试一次就放弃。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 更新
Status必须用r.Status().Update(ctx, &cr),不能走普通Update - 更新 Spec 或其他字段优先用
client.Patch(ctx, &obj, client.MergeFrom(&oldObj)),只提交变更部分,天然避开 conflict - 如果必须全量
Update(比如重建整个对象),务必包一层retry.RetryOnConflict,否则第一次失败就退出,状态卡死 - 永远检查
err != nil后立刻return ctrl.Result{}, err,别吞掉IsNotFound或IsForbidden这类关键错误
RBAC 权限不全会导致 Reconcile 根本不触发
控制器启动成功、日志里没报错,但你改了 CR,Reconcile 就是不执行——大概率是 RBAC 缺权限。Kubernetes 不会主动告诉你“watch 被拒绝”,而是静默跳过事件监听。
常见错误现象:make deploy 后控制器 Pod Running,但 kubectl get events -n my-operator-system 看不到任何关于你的 CR 的事件;kubectl auth can-i list mydatabases.database.example.com 返回 no。
-
config/rbac/role.yaml必须包含对目标 CR 的get、list、watch权限,缺watch就不会触发 reconcile - 如果控制器要操作 Deployment/Service 等内置资源,对应
resources和verbs也得列全,比如deployments/status是单独的子资源,漏掉就无法更新 Ready 状态 - 本地调试用
make run ENABLE_WEBHOOKS=false,避免证书问题干扰权限验证路径
OwnerReference 绑定错误会让子资源变成“孤儿”
Operator 创建的 Deployment、Secret 等资源,如果不打上正确的 ownerReferences,删除 CR 后这些资源不会自动清理,下次创建同名 CR 还可能因命名冲突失败。
典型错误:手动生成 Deployment 对象后直接 client.Create,没调用 ctrl.SetControllerReference(&mycr, &dep, r.Scheme)。
- 必须在创建子资源前调用
ctrl.SetControllerReference,传入当前 CR 实例、待创建对象、以及 reconciler 的Scheme - 确保 CR 实例已通过
r.Get加载(含metadata.UID),否则SetControllerReference会静默失败 - 子资源的
namespace必须和 CR 一致(除非明确支持跨 namespace 管理),否则 OwnerReference 无效

















