应使用 client-go 的 dynamicclient 配合 restmapper 获取 gvr 后调用 watch,避免硬编码路径、轮询或错误的命名空间处理;事件中用 unstructured 安全取值,并确保 scheme 正确注册 crd 类型。

怎么用 client-go 监听 CRD 资源变化
直接用 Watch 方法,但别用 List + 轮询——性能差、易丢事件、K8s server 侧压力大。CRD 对象本质是 REST 资源,client-go 的 DynamicClient 支持泛型监听,不需要为每个 CRD 写结构体。
- 必须先通过
RESTMapper获取 CRD 的GroupVersionResource(GVR),不能硬编码路径;否则遇到多版本 CRD 或 API group 变更就 404 -
DynamicClient.RestaPIs().Get().Namespace(…)这类写法错:CRD 本身是集群作用域,但它的实例(Custom Resource)可能命名空间级或集群级,得看 CRD 定义里的scope字段 - Watch 返回的
watch.Interface需手动处理watch.Event类型,event.Object是*unstructured.Unstructured,字段访问用object.Object["spec"],别试图转成 struct(除非你生成了 clientset)
为什么 informer 同步失败常卡在 “no matches for kind”
这是典型的 GVR 注册缺失。informer 依赖 scheme 知道怎么解码响应体,而默认 scheme 不认识你的 CRD 类型。
- 别只调
addKnownTypes:得用scheme.AddKnownTypes(gv, &MyCR{}, &MyCRList{}),且gv必须和 CRD 的spec.group+spec.versions[*].name严格一致(比如example.com/v1alpha1) - 如果 CRD 启用了多个版本(如 v1alpha1 和 v1),informer 默认只认
storage: true的那个版本,其他版本 watch 会返回404 Not Found或空列表 - 用
kubectl get crd <crd-name> -o yaml</crd-name>核对spec.names.kind和spec.group,大小写、复数形式(如MyResourcesvsMyResource)错一个就匹配不上
如何安全处理 CRD 版本升级时的字段变更
CRD 升级后旧实例仍存在,新 controller 可能读到字段缺失或类型不一致的数据。不能假设 object.Object["spec"] 一定有某个 key。
- 所有字段访问前加
unstructured.NestedFieldNoCopy或unstructured.NestedString等安全取值函数,它们返回(value, bool),避免 panic - 如果 CRD 从
string改成int,旧对象里该字段还是字符串,unstructured.NestedInt64会返回 0 和 false,得 fallback 处理 - 别在
Watch回调里做耗时操作(如 HTTP 请求),用 worker queue 解耦;否则 event 积压导致resourceVersion落后,触发 relist,又可能撞上旧数据
controller-runtime 的 Builder.Watch 为什么没反应
常见原因是没正确注册 Scheme 或没设置 RBAC 权限。controller-runtime 底层还是 client-go,只是封装了 informer 生命周期。
立即学习“go语言免费学习笔记(深入)”;
- 确保
mgr.GetScheme().AddKnownTypes(...)在 manager 启动前调用,且传入的类型指针和 CRD 的kind一致(比如&MyCR{},不是MyCR{}) - RBAC 的
rules必须包含apiGroups: ["example.com"],资源名写myresources(复数、小写、连字符分隔),不是 CRD 名myresources.example.com - 如果 CRD 是集群作用域,
Namespaced: false;若误设为 true,informer 会静默跳过所有事件,日志里连 warning 都没有
最麻烦的是 resourceVersion 过期后自动 relist 期间,API server 可能返回旧版对象(特别是启用了 conversion webhook 但未生效时)。这时候字段存在性、类型都不可信,得靠业务逻辑兜底校验。










