crd控制器开发必须过三关:yaml定义合法(spec.versions数组、openapi v3类型名严格)、scheme正确注册(addtoscheme顺序及路径准确)、reconcile能watch对象(status更新用client.status().update);漏一关则kubectl apply成功但reconcile不触发或报no kind错误。

直接上结论:CRD控制器不是写个结构体再调client.Update就完事,必须过三关——CRD YAML 合法、Go 类型被 Scheme 正确注册、Reconcile 能真正 watch 到对象。漏一关,kubectl apply 成功但控制器静默不触发,或者报 no kind "MyApp" is registered。
CRD YAML 怎么写才不会被 kubectl apply 拒绝
K8s 1.16+ 已彻底废弃 spec.version,只认 spec.versions 数组,且校验极严。
-
spec.versions至少含一个元素,每个必须有name(如v1)、served: true、storage: true(仅一个版本可设) - OpenAPI v3 类型名必须严格:用
integer不是int,boolean不是bool,string不能写成str -
properties下不能直接写type: object;必须显式声明properties: {}或additionalProperties: false - 常见错误:
error validating "crd.yaml": unknown field "version"—— 就是还在用老写法
Go 结构体和 Scheme 注册怎么配才不 panic
Controller 启动就 panic 或 Reconcile 根本不触发,90% 是 Scheme 注册顺序或路径错了。
- 先调
clientgoscheme.AddToScheme(scheme),再调你自己的appsv1.AddToScheme(scheme)(路径必须是你生成的 client-go 包,比如example.com/api/v1) - 你的 CR struct 必须嵌入
metav1.TypeMeta和metav1.ObjectMeta,并实现runtime.Object - 字段必须带完整 tag:
json:"replicas,omitempty" yaml:"replicas,omitempty";指针类型 +omitempty是底线,否则 API Server 拒绝解析 - 如果用了多版本(如
v1alpha1+v1),每个版本的 struct 都得单独AddToScheme,否则 watch 旧版本对象会静默失败
Reconcile 函数里怎么读写 Status 才不冲突
直接改 .Status 再 client.Update,必报 object has been modified —— 这不是并发问题,是 K8s 乐观锁强制校验 ResourceVersion。
- 永远用
r.Client.Status().Update(ctx, cr)更新 status,不要用r.Client.Update - CR struct 中定义
Status字段时,必须加+kubebuilder:subresource:status注释,否则controller-gen不生成 status 相关逻辑 - 如果 Status 计算依赖 Spec,先
client.Get读全量对象,再用obj.DeepCopy()构造新实例,避免污染本地缓存引用 - 别在 Reconcile 开头就
Get;加个if obj.DeletionTimestamp != nil提前 return,省掉无谓请求
为什么不能在 Reconcile 里调 kubectl 或 exec.Command
本地跑通、集群部署就崩,八成是因为混用了 shell 命令。
- Pod 里没装
kubectl,ServiceAccount 也没对应 RBAC 权限,exec.Command("kubectl")必然connection refused或Unauthorized - 所有资源操作必须走
mgr.GetClient()返回的client.Client,它基于rest.Config构建,自动携带凭据 - 创建 Deployment 别手动拼 YAML 字符串,用
appsv1.Deployment{...}结构体构造 - OwnerReference 必须用
metav1.NewControllerRef()生成,并设controller: true和blockOwnerDeletion: true
最易被忽略的点:CRD 的 spec.names.plural(比如 databases)必须和 dynamic client 构造 schema.GroupVersionResource 时传的 Resource 字段完全一致——小写、复数、无空格。错一个字母,dynamicClient.Resource(gvr) 就查不到东西。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











