应优先使用client-go的watch或informer机制而非list轮询,因轮询易错过状态跳变且触发限流;watch必须带有效resourceversion(首次通过list获取)并指定labelselector和namespace,否则易报410 gone或403 forbidden;收到watch.error需立即list重试;informer内置缓存、重连与事件去重,适合长期运行;监听pod删除应检测deletiontimestamp而非仅依赖deleted事件。

直接用 client-go 的 Watch 或 Informer,别轮询 List()——轮询既错过状态跳变,又容易被 API Server 限流。
Watch 收不到事件?检查 ResourceVersion 和 LabelSelector
裸调 Watch() 几乎必然失败:它必须带有效的 ResourceVersion 启动,否则返回 410 Gone 或重复推送全量历史事件。
- 先执行一次
List(),从返回的*corev1.PodList中取.ResourceVersion - 再用这个值发起
Watch(),且务必带上LabelSelector和namespace参数,否则默认监听全集群 Pod,权限不足会报403 Forbidden,权限够了也会拖慢性能 - 收到
watch.Event.Type == watch.Error时,大概率是连接断开或ResourceVersion过期,需立即重新List()→ 更新 RV → 再Watch()
为什么 Informer 比裸 Watch 更适合长期运行
如果你不止要“收到事件”,还要频繁查当前有哪些 Pod、某个 Pod 最新 Phase 是什么,裸 Watch 就得自己维护缓存、处理重连、去重、丢事件恢复——cache.NewSharedIndexInformer 全都内置了。
- 初始化后必须调
informer.Run(stopCh),否则缓存不更新,informer.GetIndexer().List()返回的是空或旧快照 -
AddEventHandler()回调里的obj是*corev1.Pod指针,Informer 内部复用内存 —— 别直接保存指针,应深拷贝或只取pod.Name、pod.Status.Phase等字段 - 若需跨 namespace 监听,工厂要用
cache.NewSharedInformerFactory(clientset, 0),而不是限定 namespace 的NewFilteredSharedInformerFactory
怎么才算“Pod 正在被删除”?别等 Deleted 事件
Deleted 事件只在对象从 etcd 彻底删除后才触发,但 kubectl delete pod 后 Pod 先进入 Terminating 状态(仍在 API Server 中),可能卡住数秒才真正消失。依赖 Deleted 会导致告警延迟或漏判。
- 真正可靠的信号是
pod.ObjectMeta.DeletionTimestamp != nil—— 只要非空,就代表删除流程已启动 - 同时监听
pod.Status.Phase变更:比如从Running→Failed或Unknown,也应纳入异常处理逻辑 - 注意:Pod 进入
Terminating后,其Status.Phase不一定立刻变,但DeletionTimestamp一定会设
最易被忽略的一点:本地调试时 rest.InClusterConfig() 必然失败,它只适用于 Pod 内运行场景;本地必须用 clientcmd.BuildConfigFromFlags("", "/path/to/kubeconfig"),否则证书解码或 TLS 验证会静默出错。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











