【Kubernetes从入门到精通】第63篇:Informer机制——K8s高性能事件驱动的核心,没有它控制器早累死了

发布时间:2026/8/21 8:38:07
【Kubernetes从入门到精通】第63篇:Informer机制——K8s高性能事件驱动的核心,没有它控制器早累死了 上一篇【第62篇】Controller Manager——K8s的自动驾驶仪下一篇【第64篇】Scheduler深度解析——Pod调度算法的高考阅卷摘要上篇我们讲了控制器的协调循环提到它靠Informer第一时间知道资源变了。但Informer到底怎么做到的为什么几十个控制器并发跑API Server没被查爆答案藏在两个设计里List-Watch先全量列一遍再增量监听变化和本地缓存每个组件在内存里存一份资源副本查状态不用打API Server。这篇文章拆开Informer的内部架构——Reflector侦察兵、DeltaFIFO变化队列、Indexer本地索引缓存三大件讲清增量同步和Resync兜底机制以及SharedInformerFactory怎么让多个控制器共用一份缓存。一、为什么需要Informer1.1 没有Informer的灾难【如果控制器直接查API Server】 50个控制器每个每秒查一次: GET /api/v1/pods → API Server GET /api/v1/services → API Server GET /api/v1/endpoints → API Server ... → API Server被轮询打死 → 网络带宽爆炸 → 延迟高、吞吐低 而且每次都返回全量列表(可能几千个对象) → 极其浪费1.2 Informer的解法【Informer 本地缓存 增量通知】 每个组件(controller/kubelet)启动一个Informer: 1. List: 一次性拉全量(sync cache) 2. Watch: 之后只收变化事件(ADDED/MODIFIED/DELETED) 3. 本地维护一份缓存(Indexer) 控制器查状态 → 直接读本地缓存(不打API Server!) 资源变化 → Watch事件秒级通知 → 触发调谐 → API Server压力骤降 → 控制器响应飞快要点Informer的核心价值是**“把读压力从API Server转移到本地内存”**。全量List只在启动时做一次之后全是轻量的Watch增量事件。这就是为什么K8s能轻松支撑成百上千个控制器和数以万计的Pod——它们大部分读操作都命中本地缓存。二、Informer内部架构2.1 三大件【Informer 内部流水线】 API Server │ │ List-Watch ▼ ┌─────────────────────────────────────────┐ │ 1. Reflector (侦察兵) │ │ • List: 拉全量 → 存Indexer │ │ • Watch: 收增量事件 │ │ • 把事件投进 DeltaFIFO │ └─────────────────┬───────────────────────┘ ▼ ┌─────────────────────────────────────────┐ │ 2. DeltaFIFO (变化队列) │ │ • 存放发生了什么变化 │ │ • 类型: Added/Updated/Deleted/Sync │ │ • FIFO: 先来先处理 │ └─────────────────┬───────────────────────┘ ▼ ┌─────────────────────────────────────────┐ │ 3. Indexer (本地索引缓存) │ │ • 内存里存对象(按namespace/name索引) │ │ • 控制器查状态从这里读(不打API Server) │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 4. EventHandler (你的回调) │ │ • OnAdd / OnUpdate / OnDelete │ │ • 把 key 塞进 WorkQueue → 触发Reconcile│ └─────────────────────────────────────────┘2.2 代码视角// 一个典型的Informer使用(伪代码)informer:cache.NewSharedIndexInformer(cache.ListWatch{// Reflector用的List/Watch函数ListFunc:func(){returnclient.List(...)},WatchFunc:func(){returnclient.Watch(...)},},v1.Pod{},// 关心Podtime.Minute*30,// resync周期cache.Indexers{cache.NamespaceIndex:cache.MetaNamespaceIndexFunc},)// 注册事件回调informer.AddEventHandler(cache.ResourceEventHandlerFuncs{AddFunc:func(obj){key:getKey(obj)workqueue.Add(key)// 塞进工作队列},UpdateFunc:func(old,new){workqueue.Add(getKey(new))},DeleteFunc:func(obj){workqueue.Add(getKey(obj))},})informer.Run(stopCh)// 启动开始List-Watch三、List-Watch与增量同步3.1 全量增量的配合【List-Watch 时序图】 T0: Informer启动 ├─ List /pods → 拿到全部1000个Pod │ → 写入Indexer缓存 │ → 注意记住 resourceVersion1000 │ T1: Watch /pods?resourceVersion1000 (从此版本开始监听) ├─ 收到 ADDED pod-1001 → 缓存1, 通知handler ├─ 收到 MODIFIED pod-5 → 缓存更新, 通知handler ├─ 收到 DELETED pod-3 → 缓存-1, 通知handler │ (后续只收增量永远不拉全量了) ⚠️ 如果Watch连接断了: 从断点resourceVersion重新Watch 若断太久(etcd已压缩旧历史) → 重新List要点resourceVersion是这里的灵魂——它像数据库的日志偏移量。List时记下当前的resourceVersionWatch从那个点继续保证不丢事件、不重复。如果连接断了从断点续传断太久etcd的历史被压缩了就重新List一把。这套机制保证了最终一致性——缓存最终和etcd完全一致。四、Resync兜底纠正4.1 为什么需要Resync【Resync 解决事件丢了的问题】 场景某个UPDATE事件因为网络抖动丢了 → Indexer缓存和实际状态不一致 → 但没人通知handler去调谐 Resync机制: • 每隔一段时间(如30分钟)定时触发 • 把Indexer里所有对象假装成Updated事件 • 重新塞进WorkQueue → 重新调谐 • 即使没真的变化也强制重新核对一遍 → 保证即使丢了事件最终也会纠正 → 这是K8s最终一致性的保险丝五、SharedInformerFactory共享缓存5.1 多个控制器共用一份【SharedInformerFactory——别各搞各的】 没有共享(浪费): Controller A 起一个 Pod Informer (一份缓存) Controller B 起一个 Pod Informer (又一份缓存) Controller C 起一个 Pod Informer (又又一份) → 3份相同缓存3次List-Watch浪费! 有共享(省): SharedInformerFactory 起一个 Pod Informer 多个控制器共用这一份缓存 这一个Watch → 1份缓存1次List-Watch所有控制器受益// 共享Informer工厂factory:informers.NewSharedInformerFactory(client,0)podInformer:factory.Core().V1().Pods()// 所有控制器共用这个// Controller A 注册handlerpodInformer.Informer().AddEventHandler(handlerA)// Controller B 注册handlerpodInformer.Informer().AddEventHandler(handlerB)// 只启动一次 Watch但两个handler都能收到事件factory.Start(stopCh)要点SharedInformerFactory是K8s性能优化的关键一招——让同一个进程里的多个控制器共享一份Informer一份缓存、一次Watch。controller-manager、kubelet、Operator框架(client-go)全都用它。既省API Server压力又省内存。理解Informer是读懂K8s源码和写Operator的必经之路。本篇小结Informer是K8s高性能事件驱动的核心用List-Watch启动全量之后增量维护本地缓存让控制器读状态不打API Server、变化秒级感知。内部三件套ReflectorList-Watch侦察兵把事件投进DeltaFIFO变化队列再更新Indexer本地索引缓存最后触发你的EventHandler把key塞进WorkQueue。resourceVersion保证增量不丢不重Resync定时兜底纠正丢事件保证最终一致。SharedInformerFactory让同进程多控制器共享一份缓存——既省API压力又省内存。下篇深入Scheduler——Pod调度算法的高考阅卷。上一篇【第62篇】Controller Manager——K8s的自动驾驶仪下一篇【第64篇】Scheduler深度解析——Pod调度算法的高考阅卷