controller-runtime 缓存的实际工作原理,以... 笔记

controller-runtime 缓存的实际工作原理,以及为何您的控制器不会导致 API Server 崩溃

Kubernetes 控制器通常使用 Go 语言,借助 kubebuilder 和 controller-runtime 进行开发,能够高效地处理分布式工作负载。然而,随着负载增加,深入理解 controller-runtime 的内部机制变得至关重要,以避免生产环境问题。其核心概念是:Reconciler 中的 r.Get()r.List() 并不直接查询 API Server,而是访问本地内存缓存。该缓存最初通过一次列表操作填充,随后通过 Watch 机制持续维护。这种设计使得本地读取速度极快,避免了对控制平面的压力。然而,其代价是本地缓存可能消耗大量内存,且可能返回陈旧数据。相反,写入操作始终直接指向 API Server,绕过缓存。本地缓存的大小及索引策略直接影响内存使用。低效的 List() 操作可能导致对大量对象进行缓慢的线性扫描。Reconciliation Loop(对等循环)持续比较期望状态与实际状态,以使其保持一致。事件被加入队列,触发 Reconcile 函数,该函数读取当前状态、确定必要操作,并可能生成新事件。缓存的主要目的是为这些对等循环提供快速、最新的对象状态视图。这种基于 Watch 的模型源自 client-go,通过维护单个长连接来接收更新,从而避免了对 API 的持续轮询。controller-runtime 抽象了底层 client-go 组件(如 Reflector、DeltaFIFO 和 Indexer)的复杂性。Manager 负责协调共享缓存、控制器及其他服务。Informer 监控特定的 GVK,维护本地存储(Indexer),并将事件分发给订阅者。ResourceEventHandlers 处理这些事件,更新存储,进而反映在控制器的视图中。Workqueue 管理待由 Worker 处理的对象键,而 Predicates 则在入队前过滤事件。Reflector 是唯一直接与 API Server 交互的组件,执行初始列表操作,随后建立 Watch。它利用 resourceVersion 恢复 Watch 并确保不遗漏任何事件。如果 Watch 连接丢失或 API Server 指示 resourceVersion 过时,则执行重新列表(relist)。DeltaFIFO 充当变更的有序缓冲区,按对象键对 Delta 进行分组。它为自上次 Pop() 调用以来对象累积的所有变更提供数据,确保有序、批量的处理。然而,它不会合并连续的"Added"或"Updated"事件,这可能导致对快速变化的对象进行多次处理轮次。