核心是用Informer建本地缓存+事件驱动;List/Watch因无重连、无缓冲、无法感知中间状态而不可靠,必须用cache.NewInformer封装并检查DeletionTimestamp等字段而非仅依赖Delete事件。

Go 程序监控 Kubernetes 资源,核心不是“轮询 API”,而是用 Informer 建立本地缓存 + 事件驱动模型;直接调 List() 或裸 Watch() 必踩性能、可靠性、语义丢失三重坑。
为什么不能直接用 client-go 的 List/Watch
很多人一上来就写 clientset.CoreV1().Pods("default").List() 或手动 Watch(),结果在生产环境频繁超时、漏事件、CPU 拉满。根本原因有三个:
-
List()每次都全量拉取,集群 Pod 过千时单次耗时秒级,且无法感知中间变更 - 裸
Watch()不自动重连、不处理410 Gone、不缓冲事件,网络抖动就断连失联 - Watch 返回的
Delete事件只在对象从 etcd 彻底删除后触发,但Terminating状态早几秒就该告警——你等不到它
必须用 cache.NewInformer 构建本地缓存
Informer 是 client-go 封装好的“带缓存的 Watch 客户端”,它自动做重连、版本控制、事件队列和本地对象快照。关键点:
- 初始化时传入
cache.NewListWatchFromClient(),底层仍走 REST API,但封装了分页、资源版本(resourceVersion)续传逻辑 - 注册
AddFunc/UpdateFunc/DeleteFunc,其中UpdateFunc能捕获Phase变更(比如从Running→Failed),比等Delete更及时 - 缓存对象是深拷贝,可安全并发读取;修改需走
clientset显式更新,避免状态污染 - 示例中监听 Pod 删除,应检查
pod.DeletionTimestamp != nil,而非只等DeleteFunc触发
controller-runtime 的 Reconcile 适合什么场景
如果你的需求是“当某 Pod 出现异常时,自动创建一个调试 Job 并打标签”,那就该切到 controller-runtime。它不是替代 Informer,而是基于 Informer 构建的更高层抽象:
立即学习“go语言免费学习笔记(深入)”;
-
Reconcile()函数接收req ctrl.Request(含 namespace/name),你要做的只是查缓存、判断状态、决定是否 Patch/Update/Create - 框架自动处理冲突重试(
RequeueAfter)、Leader Election(多副本防重复执行)、Finalizer 清理(避免资源残留) - 别在
Reconcile()里做耗时操作(如 HTTP 请求、DB 查询);必须做就起 goroutine + context 控制超时,否则阻塞整个队列 - 如果只是读配置、上报指标、发告警——用纯 Informer 足够;只有要“主动干预集群状态”才引入 controller-runtime
证书与权限问题最常卡住本地调试
本地跑监控程序连不上集群,90% 是认证失败。注意两套路径互不兼容:
- 集群内运行(如 sidecar):用
rest.InClusterConfig(),依赖挂载的/var/run/secrets/kubernetes.io/serviceaccount/token;若容器以非 root 用户运行,必须显式声明securityContext.runAsUser并确保该 UID 对 token 文件有读权限,否则报open /var/run/secrets/.../token: permission denied - 本地开发:用
clientcmd.BuildConfigFromFlags("", kubeconfigPath),它会自动解码certificate-authority-data、处理insecure-skip-tls-verify: true;千万别自己拼rest.Config,容易漏字段 - RBAC 权限必须最小化:监控 Pod 就只给
get/list/watchonpods,别直接绑cluster-admin
真正难的不是写通 Informer,而是想清楚你要响应什么语义:是“对象存在”还是“对象处于某种状态”?后者必须检查字段(如 DeletionTimestamp、Phase、Conditions),而不是只看事件类型。


















