informer 的核心是 list-watch + 本地缓存 + 事件分发三件套;未调用 run() 则缓存为空,hassynced() 永不为 true;podinformer.lister().get() 返回 nil 或 notfound 最常见原因是缓存尚未同步完成,需等待 waitforcachesync() 完成。

Informer 不是“监听器封装”,而是 List-Watch + 本地缓存 + 事件分发的三件套组合;不启动 Run(),缓存永远为空,HasSynced() 永远返回 false。
为什么 podInformer.Lister().Get() 返回 nil 或 NotFound?
最常见原因是缓存尚未同步完成。Informer 启动后需先执行一次全量 List,再持续 Watch,这个过程不是瞬时的。
-
HasSynced()必须在Informer.Run()启动后、且首次全量同步完成后才返回true;直接调用Get()而不等待同步,大概率命中空缓存 - 忘记调用
informerFactory.Start()或漏掉informerFactory.WaitForCacheSync(),会导致所有 Lister 查询失败 - 若指定了命名空间(如
WithNamespace("default")),但目标 Pod 不在该命名空间,Get()也会返回NotFound,而非 panic - 对象已删除但事件尚未从 DeltaFIFO 消费完毕,缓存中仍存在旧快照,此时
Get()可能返回过期对象 —— 这是正常现象,由 Indexer 的最终一致性模型决定
SharedInformerFactory 怎么避免重复 Watch 同一资源?
共享的核心在于:同一 SharedInformerFactory 实例下,对相同资源类型(如 *corev1.Pod)调用多次 .Pods().Informer(),返回的是同一个 SharedIndexInformer 实例,底层共用 Reflector、DeltaFIFO 和 Indexer。
针对 Kubernetes 仪表板和 Web UI 的浏览器自动化。适用于与 Kubernetes Dashboard、Grafana、ArgoCD UI 或其他 Web 界面交互。需要设置 MCP_BROWSER_ENABLED=true。
- 多个控制器共用一个 factory 时,只需一次
List+ 一次Watch连接,API Server 压力显著降低 - 不同 factory 实例之间不共享 —— 即使配置完全相同,也会各自建立 Watch 连接,这是常见性能误判点
- 若需跨 namespace 监听,不要用
WithNamespace()限制,而应使用informers.WithTweakListOptions()注入自定义选项,例如过滤 label - factory 启动后,新增的 Informer(如后续调用
.Services())会自动加入已运行的 Reflector 循环,无需额外Run()
如何安全地在 Handler 中访问缓存对象?
Handler 函数(如 AddFunc)收到的对象是 Reflector 从 API Server 解码后的原始 runtime.Object,它和 Indexer 缓存中的对象**不是同一个内存地址**,也不受 Indexer 锁保护。
- 不要在 Handler 中直接修改传入的
obj(如pod.Labels["processed"] = "true"),这会污染解码缓存,影响其他 Handler - 需要读取关联资源(如通过 Pod 找对应 Node)时,务必通过
lister.Get()或lister.ByNamespace().List()从 Indexer 获取 —— 它们线程安全且返回不可变副本 - 若 Handler 中需触发异步处理(如入队),推荐用
cache.MetaNamespaceKeyFunc(obj)生成 key,而不是直接传obj,避免 goroutine 持有已过期对象引用 - 切忌在 Handler 中调用
clientset.CoreV1().Pods().Get()—— 这绕过了缓存,既慢又增加 API Server 负载
ResyncPeriod 设为 0 真的安全吗?
设为 0 表示禁用周期性 resync,能减少无谓的全量 List 请求,但会放大两个风险:
- Watch 连接意外断开且未被及时发现时(如网络抖动),DeltaFIFO 可能停滞,缓存逐渐偏离真实状态,而 resync 是唯一的兜底校验机制
- 某些极端场景下(如 etcd compact 导致 resourceVersion 失效),Reflector 可能静默降级为仅靠 List 续连,此时没有 resync 就无法恢复 watch 流程
- 生产环境建议保留 resync,但拉长周期(如
2 * time.Hour),并配合健康检查:定期比对lister.List()结果与预期数量,或监听controller.HasSynced()波动 - 如果业务逻辑本身具备幂等性和最终一致性(如大多数 Operator 场景),禁用 resync 是可接受的,但必须确保 Watch 断连能被
OnStoppedHandler捕获并重启 Informer
真正容易被忽略的,是 Informer 的“启动时序” —— Start()、WaitForCacheSync()、HasSynced() 三者必须严格按顺序使用,且 WaitForCacheSync() 的 channel 关闭时机依赖所有注册的 Informer,任何一个没加进 factory,都会让整个 sync 卡住。










