用 controller-runtime 定义和注册 crd 需先通过 scheme 注册类型(如 schemebuilder.register(&myapp{}, &myapplist{})),调用 clientgoscheme.addtoscheme(scheme) 和 apiextensionsv1.addtoscheme(scheme) 加载 api scheme,再用 controller-gen 自动生成 crd yaml;reconcile 中必须使用 mgr.getclient() 操作资源,禁止调用 kubectl 或 shell 命令;ownerreference 要用 metav1.newcontrollerref() 生成并设 controller: true 和 blockownerdeletion: true;webhook 可自签证书,dnsnames 必须匹配 service 名(如 my-webhook.my-system.svc),cabundle 填 base64 编码的 cert.pem。

怎么用 controller-runtime 定义和注册 CRD
CRD 不是靠手写 YAML 然后 kubectl apply 就完事的——至少在 Go 控制器开发中,你得先让 Go 程序“认识”这个资源。核心是用 controller-runtime 的 Builder 和 SetupWebhookWithManager(如果需要 webhook)配合 CRD 生成工具。
常见错误:直接把 apiextensions/v1 的 CRD YAML 丢进 mgr.Add,程序启动就 panic,报错类似 no kind "CustomResourceDefinition" is registered for version "apiextensions.k8s.io/v1"——这是因为没提前加载 API Scheme。
- 必须调用
clientgoscheme.AddToScheme(scheme)和apiextensionsv1.AddToScheme(scheme),否则控制器连 CRD 自身都解析不了 - 你的自定义结构体(如
MyApp)要实现metav1.Object和runtime.Object,通常嵌入metav1.TypeMeta和metav1.ObjectMeta - 用
schemeBuilder.Register(&MyApp{}, &MyAppList{})把类型注册进 Scheme,否则 client.Get/Watch 会返回no kind错误 - CRD YAML 最好由
controller-gen自动生成:运行make manifests(依赖// +kubebuilder:object:root=true注释),避免手写时 group/version/kind 拼错
为什么 Reconcile 函数里不能直接用 kubectl 或 shell 命令
Reconcile 是控制器的核心循环,它必须是纯 Go 实现、可重入、无副作用的逻辑。任何调用 kubectl、exec.Command("kubectl") 或写临时文件的行为,都会导致不可控的并发问题、权限泄漏和调试黑洞。
典型现象:本地 go run main.go 能跑通,一部署到集群就反复报 connection refused 或 Unauthorized——因为 Pod 里没装 kubectl,ServiceAccount 也没对应 RBAC 权限。
- 所有 Kubernetes 资源操作必须走
mgr.GetClient()返回的 client.Client,它基于 rest.Config 构建,自动携带 RBAC 凭据 - 创建 Deployment 时别手动拼 YAML 字符串,用
appsv1.Deployment{ObjectMeta: ..., Spec: ...}结构体赋值,再client.Create(ctx, &dep) - 注意 namespace 隔离:CR 对象本身有
.Namespace,但你要创建的子资源(如 Secret)必须显式设ObjectMeta.Namespace = cr.Namespace,否则默认跑在 default ns
如何安全地做 OwnerReference 关联与级联删除
不设 OwnerReference,CR 删了,它创建的 Pod 还在跑;设错了,可能删掉不该删的资源(比如误关联到 kube-system 的 ConfigMap)。关键不是“加不加”,而是“加得准不准”。
错误示例:ownerRefs: [{Name: "hardcoded-name", UID: ""}] —— UID 为空会导致 Kubernetes 忽略该引用,级联删除失效。
- 必须用
metav1.NewControllerRef(cr, groupVersionKind)生成 OwnerReference,其中groupVersionKind要和 CRD 的spec.version及spec.names.kind严格一致 - OwnerReference 的
BlockOwnerDeletion: true和Controller: true必须同时设,缺一不可;否则垃圾回收器不认这是“受控资源” - 如果你的控制器要管理跨 namespace 资源(比如 ClusterScope CR 创建 namespaced Service),注意 OwnerReference 不支持跨 ns 引用——此时只能靠 finalizer + 手动清理
Webhook 怎么避开 cert-manager 也能跑通 TLS
很多教程默认让你装 cert-manager,但实际开发阶段没必要。K8s AdmissionReview 要求 HTTPS,但证书可以自己签、自己挂,不用走 ACME。
最常卡住的地方:webhook server 启动了,kubectl get mutatingwebhookconfigurations 显示 failurePolicy: Fail,但一创建 CR 就超时,日志里没有请求进来——基本是证书 CN 或 DNSNames 配置和 webhook service 的 DNS 名不匹配。
- service 名必须是
<name>.<namespace>.svc</namespace></name>,证书的DNSNames必须包含它(例如my-webhook.my-system.svc) - 用
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out cert.pem -subj "/CN=my-webhook.my-system.svc"直接生成,比搞 cfssl 简单 - cert 和 key 以 secret 挂进 webhook pod 后,要在 MutatingWebhookConfiguration 的
webhooks[].clientConfig.caBundle字段填入 base64 编码后的cert.pem内容(不是 ca.crt) - local test 时可用
hostNetwork: true+localhost:9443绕过 service DNS,但上线前务必切回 service 地址
真正麻烦的从来不是写代码,而是 CRD 的 group 名拼错、webhook 证书的 CN 写成 IP、或者 OwnerReference 的 UID 在 Reconcile 中被当成字符串硬编码——这些地方一错,现象都是“没反应”,查日志也看不到请求进来。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











