核心使命与设计理念
4.1 What - StatesInformer 是什么?
StatesInformer 是 Koordlet 中的状态同步和缓存模块,维护节点上所有 Pod、Node、NodeSLO 等资源的本地视图。
核心职责:
- Watch 并缓存本节点的所有 Pod 信息
- Watch 并缓存节点本身的信息(labels、capacity、allocatable)
- Watch 并缓存节点的 NodeSLO 配置
- 为其他模块提供高速的状态查询接口
- 维护状态一致性和增量同步
4.2 Why - 为什么需要状态同步?
问题 1:频繁查询 API Server 导致性能问题
对比分析:
不使用缓存方案:
┌────────────────────────────────┐
│ QOSManager 每秒工作循环 │
├────────────────────────────────┤
│ for pod in pods: │
│ api_call() → 查询 API Server │
│ 延迟: ~100ms │
│ × 200 个 Pod │
│ = 20s 延迟!无法按时执行 │
│ │
│ 额外问题: │
│ ├─ API Server QPS 爆炸 │
│ ├─ etcd 压力增大 │
│ └─ 网络带宽浪费 │
└────────────────────────────────┘
使用缓存方案:
┌────────────────────────────────┐
│ QOSManager 每秒工作循环 │
├────────────────────────────────┤
│ for pod in cache.GetAllPods(): │
│ 内存查询 → 延迟: <1ms │
│ × 200 个 Pod │
│ = 200ms 延迟(可接受) │
│ │
│ 好处: │
│ ├─ API Server 压力小 │
│ ├─ 本地操作速度快 │
│ └─ 容错能力强 │
└────────────────────────────────┘
性能对比:
查询 200 个 Pod 的时间
├─ 直接查询 API Server: ~20000ms
├─ 使用本地缓存: ~200ms
└─ 性能提升: 100 倍
问题 2:API Server 故障时需要继续工作
场景时间线:
T=0:00 API Server 升级开始(不可用 5 分钟)
T=0:01 Koordlet 的 Watch 连接断开
├─ 不使用缓存: 无法继续工作
└─ 使用缓存: 继续使用旧状态管理资源
T=0:02 新 Pod 创建请求来了
├─ 不使用缓存: 无法感知,无法配置隔离
└─ 使用缓存: 根据旧状态,但基本资源管理继续进行
T=0:05 API Server 恢复,Watch 重连
├─ 不使用缓存: 需要完全重新初始化
└─ 使用缓存: 增量同步新变化,快速恢复
总体影响:
├─ 无缓存: Pod 被驱逐、资源被误用、SLO 违反
└─ 有缓存: 短期内基于旧状态继续工作,影响较小
问题 3:需要追踪状态变化历史
应用场景:
RuntimeHooks 决策示例:
├─ 判断 Pod 是否刚创建
│ └─ 如果 < 1 秒前创建,可能还未完全初始化
│ └─ 需要使用保守的隔离参数
│
├─ 判断 Pod 是否即将被删除
│ └─ 如果有删除时间戳,可能即将关闭
│ └─ 不必进行复杂的资源配置
│
└─ 计算 Pod 的运行时长
└─ 新 Pod (< 1min): 无历史数据,谨慎处理
└─ 运行中 Pod (> 1hour): 有充分历史,正常处理
QOSManager 决策示例:
├─ 最近被驱逐的 Pod
│ └─ 如果上次驱逐后不到 1 分钟,不立即重新抑制
│
└─ Pod 的内存趋势
└─ 需要看历史数据判断内存是否持续增长
4.3 How - 高效状态同步的实现
StatesInformer 的完整实现
4.4 核心数据结构
// StatesInformer 的主要组件
type StatesInformer interface {
// Pod 查询接口
GetPodByUID(uid types.UID) *corev1.Pod
GetPodByNamespace(namespace, name string) *corev1.Pod
GetPodsByNode(nodeName string) []*corev1.Pod
GetAllPods() []*corev1.Pod
// Node 查询接口
GetNode() *corev1.Node
GetNodeSLO() *slov1alpha1.NodeSLO
// QoS 查询接口
GetPodQoSClass(pod *corev1.Pod) extension.QoSClass
GetPodPriority(pod *corev1.Pod) int32
// 状态变化回调
RegisterEventHandler(handler PodEventHandler)
}
内部数据结构:
┌─────────────────────────────────────────────┐
│ StatesInformer 缓存结构 │
├─────────────────────────────────────────────┤
│ │
│ 1. podMap │
│ Key: Pod UID │
│ Value: Pod 对象 + 元数据 │
│ Size: ~50 MB(200 Pod 节点) │
│ Access: O(1) │
│ │
│ 2. podByNamespace │
│ Key: namespace/name │
│ Value: Pod UID │
│ 目的:快速按名称查询 │
│ │
│ 3. podListByNode │
│ Key: nodeName │
│ Value: [Pod UID 列表] │
│ 目的:快速查询一个节点上的所有 Pod │
│ │
│ 4. nodeCache │
│ 单个 Node 对象缓存 │
│ Size: ~100 KB │
│ │
│ 5. nodeSLOCache │
│ 单个 NodeSLO 对象缓存 │
│ Size: ~50 KB │
│ │
│ 6. eventHandlers │
│ Pod 事件的回调列表 │
│ ├─ Pod 创建回调 │
│ ├─ Pod 更新回调 │
│ └─ Pod 删除回调 │
│ │
└─────────────────────────────────────────────┘
4.5 Watch 机制的优化
Watch 流程:
API Server
│ 推送增量变化(Add/Update/Delete 事件)
↓
Informer Framework
│ 事件缓冲、合并、去重
↓
EventHandler
│ 业务逻辑处理
│
├─→ OnAdd(pod)
│ ├─ podMap[pod.UID] = pod
│ ├─ podByNamespace[ns/name] = pod.UID
│ ├─ podListByNode[node] += pod.UID
│ └─ 触发 Pod 创建回调
│
├─→ OnUpdate(oldPod, newPod)
│ ├─ 检查关键字段是否变化
│ │ ├─ 资源 request/limit
│ │ ├─ QoS 标签
│ │ └─ 优先级
│ ├─ 更新缓存
│ └─ 触发 Pod 更新回调(仅关键字段变化)
│
└─→ OnDelete(pod)
├─ podMap.delete(pod.UID)
├─ podByNamespace.delete(ns/name)
├─ podListByNode[node] -= pod.UID
└─ 触发 Pod 删除回调
↓
StatesInformer 缓存保持最新状态
Watch 的关键优化:
1. 增量更新机制
├─ 只同步变化的对象,不是全量重新加载
└─ 减少网络带宽和 CPU 消耗
2. 事件合并
├─ 如果 Pod 快速连续更新多次
├─ 只保留最后的状态
└─ 避免重复处理
3. 去重和验证
├─ 检测出 Pod 虽然更新但关键字段未变
├─ 不触发 ResourceExecutor 的重复执行
└─ 保证幂等性
4. Resync 机制
├─ 周期性(默认 30 分钟)重新同步全量数据
├─ 用于检测 Watch 丢失的事件
└─ 保证最终一致性
4.6 查询性能分析
查询操作的性能:
操作 时间复杂度 实际延迟 使用场景
────────────────────────────────────────────────────────────
GetPodByUID() O(1) <0.1ms 最常用,快速查询
GetPodsByNode() O(n) <1ms 获取节点上所有 Pod
GetAllPods() O(n) <5ms 遍历操作
GetPodByNamespace() O(1) <0.1ms 按名称查询
GetNode() O(1) <0.1ms 查询节点信息
GetNodeSLO() O(1) <0.1ms 查询 SLO 配置
n = 节点上的 Pod 数量(通常 100-500)
内存占用分析:
资源 单位大小 200 Pod 节点 500 Pod 节点
────────────────────────────────────────────────────────────
Pod 对象 ~5 KB 1 MB 2.5 MB
Pod 元数据 ~2 KB 0.4 MB 1 MB
索引(UID) ~40 bytes 8 KB 20 KB
索引(名称) ~60 bytes 12 KB 30 KB
索引(节点) ~40 bytes 8 KB 20 KB
Node 缓存 ~100 KB 100 KB 100 KB
NodeSLO 缓存 ~50 KB 50 KB 50 KB
──────────────────────────────────────────────────────────
总计 ~52 MB ~64 MB
4.7 事件处理的生产案例
案例:Pod 创建到资源配置的完整流程
时间线:
T=0:00 用户执行 kubectl apply -f pod.yaml
└─ Pod 对象发送到 API Server
T=0:05 API Server 持久化 Pod,创建事件
└─ Watch 推送 Pod Added 事件
T=0:10 Koordlet StatesInformer 收到事件
├─ EventHandler.OnAdd(pod)
├─ podMap[uid] = pod
├─ podByNamespace[ns/name] = uid
├─ podListByNode[node] += uid
└─ 触发已注册的回调函数们:
│
├─→ RuntimeHooks.OnPodAdded()
│ └─ 计算该 Pod 的隔离参数
│ └─ 为容器创建准备数据
│
├─→ MetricAdvisor.OnPodAdded()
│ └─ 注册该 Pod 的指标采集
│
└─→ QOSManager.OnPodAdded()
└─ 更新 Pod 集合
└─ 下次周期时应用 QoS 策略
T=0:15 kubelet 监听到 Pod 变化,开始创建容器
└─ 调用 CRI 创建容器
T=0:20 如果启用了 RuntimeProxy/NRI,会在容器创建时
└─ 注入 StatesInformer 提供的隔离参数
T=1:00 Pod 正常运行,StatesInformer 继续监听变化
├─ 如果 Pod 资源请求变化(通常不变)
└─ OnUpdate 事件,触发重新配置
T=N:00 Pod 被删除
└─ OnDelete 事件
└─ 清理缓存、停止监控
案例:API Server 故障恢复
时间线:
T=10:00 API Server 正常,StatesInformer 同步状态
├─ 缓存中有 200 个 Pod
└─ Watch 连接活跃
T=10:05 API Server 开始升级,Watch 连接断开
├─ 网络层错误,连接关闭
└─ StatesInformer 保持旧缓存,标记为 "Stale"
T=10:06-10:10 API Server 升级期间
├─ StatesInformer 使用旧缓存继续工作
├─ 新的 Pod 请求可能无法感知(最多 5 分钟)
├─ QOSManager 仍基于旧状态执行
└─ 但 Pod 基本的资源配置保持稳定
T=10:11 API Server 升级完成,Watch 重连成功
├─ 开始接收增量事件
├─ 检测新创建的 Pod(在故障期间)
├─ 检测被删除的 Pod
└─ 5 分钟内的所有变化被同步
T=10:30 Resync 触发(30 分钟周期)
├─ 全量重新同步所有 Pod
├─ 对比缓存和 API Server 状态
├─ 修复任何不一致的状态
└─ 恢复到完全一致
故障影响评估:
├─ 无缓存系统: 5 分钟内无法管理,SLO 严重违反
└─ 有缓存系统: 基本继续工作,5 分钟后完全恢复
与其他模块的协作
4.8 StatesInformer 被谁使用?
┌──────────────────────┐
│ StatesInformer 缓存 │
└──────────────────────┘
↑ Watch 更新
│
┌─────────────────────────────────────────────┐
│ Koordlet 各模块的使用 │
├─────────────────────────────────────────────┤
│ │
│ 1. RuntimeHooks │
│ GetPodByUID() → 获取 Pod request/limit │
│ GetPodQoSClass() → 获取 QoS 等级 │
│ 用途:容器创建时注入隔离参数 │
│ │
│ 2. MetricAdvisor │
│ GetAllPods() → 遍历所有 Pod │
│ GetPodQoSClass() → 按 QoS 分类采集 │
│ 用途:采集时知道哪些 Pod 是 LS/BE │
│ │
│ 3. QOSManager │
│ GetAllPods() → 每秒查询所有 Pod 状态 │
│ GetPodQoSClass() → 判断如何抑制/驱逐 │
│ GetPodPriority() → 驱逐时的优先级 │
│ 用途:制定 QoS 策略 │
│ │
│ 4. ResourceExecutor │
│ GetPodByUID() → 查询 Pod 的容器 ID │
│ 用途:执行 CGroup 更新时定位容器 │
│ │
│ 5. PredictServer │
│ GetAllPods() → 历史数据分析 │
│ 用途:基于 Pod 历史预测未来需求 │
│ │
└─────────────────────────────────────────────┘
4.9 一致性保证
缓存与 API Server 的一致性:
三级一致性保证:
1. 最终一致性
└─ 高频事件: 通过 Watch 增量同步
└─ 延迟: 通常 < 100ms
└─ 保证: 一定会同步,但可能有延迟
2. 周期同步
└─ 每 30 分钟 Resync 全量数据
└─ 用于修复 Watch 丢失的事件
└─ 保证: 最坏情况 30 分钟内恢复一致
3. 主动验证
└─ 关键操作前,可选性验证 API Server
└─ 如果缓存不确定,查询 API Server
└─ 保证: 关键决策的准确性
实际应用:
├─ 正常情况: 使用缓存(快速)
├─ 关键决策: 可选验证 API Server(准确)
└─ 故障恢复: 优先用缓存,等待同步(可靠)
生产调优指南
4.10 常见性能问题与优化
问题 1:Watch 延迟过大
症状:Koordlet 感知 Pod 变化延迟 > 1 秒
可能原因:
├─ API Server 负载过高
├─ 网络延迟
├─ Watch 缓冲区满
诊断方法:
$ kubectl logs pod/koordlet-xxx -n koordinator-system | \
grep "watch\|delay" | tail -20
$ # 查看 API Server 的 QPS
$ kubectl top pod -n kube-system | grep apiserver
解决方案:
1. 增加 API Server 资源
2. 减少 Pod 变动频率
3. 调整 Watch 缓冲区大小:
--watch-buffer-size=2000
问题 2:内存占用过高
症状:StatesInformer 内存占用 > 200 MB
可能原因:
├─ 节点上 Pod 过多(> 1000 个)
├─ 缓存保留的历史数据过多
├─ 内存泄漏
诊断方法:
$ kubectl top pod koordlet-xxx -n koordinator-system
解决方案:
1. 减少单节点 Pod 数量(分散到多节点)
2. 减少缓存保留时间:
--cache-ttl=600(从 3600 改为 600)
3. 定期 GC:
--gc-interval=30m
问题 3:查询响应时间长
症状:GetAllPods() 耗时 > 50ms
可能原因:
├─ Pod 数量过多(> 1000)
├─ 频繁的全量遍历
优化方案:
1. 使用索引查询而不是遍历:
GetPodsByNode(nodeName) ✅
GetAllPods() ❌ (低效)
2. 缓存查询结果(如果数据不需要实时)
3. 按 QoS 等级分组查询:
GetPodsByQoS(qos) // 内部实现优化
4.11 生产配置建议
# Koordlet StatesInformer 配置
spec:
containers:
- name: koordlet
args:
# Watch 配置
- --watch-timeout=5m # Watch 超时时间
- --watch-buffer-size=2000 # Watch 缓冲区大小
# Resync 配置
- --resync-interval=30m # 多久全量同步一次
# 缓存配置
- --cache-ttl=3600 # 缓存数据保留时间
- --gc-interval=30m # GC 周期
# 性能配置
- --query-timeout=1s # 查询超时
- --batch-update-size=100 # 批量更新大小
4.12 监控指标
# Koordlet StatesInformer 关键指标
# Pod 缓存大小
koordlet_pod_cache_size
# 最后一次同步的时间(Age)
koordlet_states_informer_sync_age_seconds
# Watch 连接状态(0=断开,1=连接)
koordlet_watch_connection_status
# 事件处理延迟
koordlet_event_handler_latency_milliseconds
# 查询操作延迟
koordlet_cache_query_latency_milliseconds{operation="GetAllPods"}
koordlet_cache_query_latency_milliseconds{operation="GetPodByUID"}
总结 - 章节要点汇总
4.13 关键概念速查
| 概念 | 含义 | 性能指标 |
|---|---|---|
| 缓存 | 本地内存中 Pod/Node/NodeSLO 的副本 | ~50-100 MB |
| Watch | API Server 推送的增量事件流 | < 100ms 延迟 |
| Resync | 定期全量同步以修复不一致 | 30 分钟周期 |
| 最终一致性 | 缓存与 API Server 最终一致 | 最坏 30 分钟 |
| 事件回调 | Pod 变化时的处理函数 | < 10ms 执行 |
4.14 最佳实践
- 总是使用缓存查询,避免直接查询 API Server
- 在关键决策前,可选性验证 API Server
- 使用索引查询而不是全量遍历
- 定期检查缓存与 API Server 的一致性
- 监控 Watch 连接状态,及时发现问题
本章要点:
- 理解 StatesInformer 的核心职责和设计理念
- 掌握高效的状态同步机制
- 学会性能优化和故障排查
- 理解缓存的一致性保证机制