4、调度缓存(Scheduler Cache):NodeInfo、PodInfo的存储结构、Assumed Pod与实际绑定状态
调度缓存(Scheduler Cache)是 kube-scheduler 在调度周期中用于快速决策的核心内存数据结构。它存储了集群中所有节点的实时快照以及待调度 Pod 的中间状态。理解 NodeInfo、PodInfo 的存储结构以及 Assumed Pod 与实际绑定状态之间的差异,是排查调度一致性问题的基础。
4.1 调度缓存的核心数据结构
调度缓存主要由两个核心数据结构组成:NodeInfo 和 PodInfo。它们被组织在一个名为 schedulerCache 的全局对象中,该对象内部维护了一个 map[string]*NodeInfo 的哈希表,以节点名称为键。
4.1.1 NodeInfo 存储结构
NodeInfo 并非简单的节点元数据,而是一个聚合了节点资源、拓扑信息以及已调度 Pod 列表的复合结构。其关键字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
node |
*v1.Node |
节点原始对象,包含标签、注解、Taints 等元数据。 |
requested |
*Resource |
当前节点上所有 Pod(包括 Assumed Pod)已请求的资源总量(CPU、内存、扩展资源等)。 |
allocatable |
*Resource |
节点可分配资源总量,来自 node.Status.Allocatable。 |
pods |
[]*PodInfo |
该节点上所有 Pod 的 PodInfo 列表,包括 Assumed Pod 和已绑定 Pod。 |
usedPorts |
util.HostPortInfo |
节点上已占用的主机端口映射,用于端口冲突检查。 |
imageStates |
map[string]*ImageStateSummary |
节点上已拉取的镜像摘要,用于镜像局部性评分。 |
topologyInfo |
*TopologyInfo |
节点拓扑信息,如 NUMA 节点、PCIe 拓扑等(用于拓扑感知调度)。 |
关键点: NodeInfo 中的
requested 字段是动态变化的。每当一个 Pod 被 Assumed(假设调度)到该节点,requested 就会立即增加该 Pod 的资源请求量。这是调度器实现“抢占式资源计算”的基础。
4.1.2 PodInfo 存储结构
PodInfo 是调度缓存中关于单个 Pod 的轻量级表示,它比完整的 Pod 对象更精简,专注于调度决策所需的信息。
type PodInfo struct {
Pod *v1.Pod
// 调度器内部状态
StartTime time.Time
// 资源请求的预计算值,避免重复计算
ResourceRequest *Resource
// 端口请求的预计算值
PortsRequested []*v1.ContainerPort
// 该 Pod 是否处于 Assumed 状态
Assumed bool
// 该 Pod 是否已通过 API Server 确认绑定
Bound bool
}
PodInfo 中的 Assumed 和 Bound 布尔字段是调度一致性的核心哨兵。它们共同决定了 Pod 在缓存中的生命周期阶段。
4.2 Assumed Pod 与实际绑定状态
调度器采用“乐观绑定”策略来提升吞吐量。这意味着调度器在将 Pod 绑定到节点的请求发送给 API Server 后,不会等待 API Server 的确认,而是立即在本地缓存中将该 Pod 标记为 Assumed。这种设计引入了两个关键状态:
- Assumed Pod: 调度器已做出调度决策,并向 API Server 发送了绑定请求,但尚未收到 API Server 的确认响应。此时,Pod 在缓存中已占用节点资源,但 etcd 中可能尚未持久化绑定记录。
- 实际绑定状态: API Server 成功处理了绑定请求,将 Pod 的
spec.nodeName写入 etcd,并返回确认给调度器。此时,Pod 在缓存中从 Assumed 状态转换为 Bound 状态。
一致性风险: 如果 API Server 处理绑定请求失败(例如网络超时、etcd 写入冲突),而调度器已经将 Pod 标记为 Assumed 并占用了节点资源,就会导致缓存与实际集群状态不一致。这种不一致是调度一致性问题的主要来源之一。
4.3 状态转换流程与缓存更新
下图展示了 Pod 从调度决策到缓存确认的完整状态转换流程:
4.4 缓存一致性的关键机制
为了维护 Assumed 状态与实际绑定状态之间的一致性,调度器实现了以下关键机制:
- 异步绑定确认(Binding Confirmation): 调度器在收到 API Server 的绑定成功响应后,会调用
cache.AssumePod的逆操作cache.FinishBinding,将 Pod 从 Assumed 状态转换为 Bound 状态。 - 过期回滚(Expiration & Rollback): 如果调度器在超时时间内(默认 30 秒)未收到 API Server 的确认,会触发回滚操作,从缓存中移除该 Assumed Pod 并释放其占用的资源。
- 周期性同步(Periodic Sync): 调度器会定期(默认 5 分钟)从 API Server 全量同步集群状态,用 etcd 中的真实数据覆盖缓存,修复任何残留的不一致。
- 事件驱动更新(Event-driven Update): 当 API Server 推送 Pod 更新事件(如
nodeName变更)时,调度器会立即更新缓存,确保 Assumed 状态与实际状态对齐。
排查要点: 当怀疑调度一致性问题时,应重点检查以下指标:
scheduler_cache_assumed_pods:当前处于 Assumed 状态的 Pod 数量。如果该值持续不为零,说明存在绑定延迟或失败。scheduler_cache_rollbacks_total:缓存回滚的总次数。频繁回滚表明网络或 API Server 存在瓶颈。scheduler_binding_duration_seconds:绑定操作的耗时分布。高延迟可能导致 Assumed 状态过期。
4.5 存储结构对比总结
下表总结了 NodeInfo 与 PodInfo 在调度缓存中的角色差异:
| 维度 | NodeInfo | PodInfo |
|---|---|---|
| 粒度 | 节点级别,聚合所有 Pod 的资源占用 | Pod 级别,单个 Pod 的资源请求与状态 |
| 核心字段 | requested、allocatable、pods |
ResourceRequest、Assumed、Bound |
| 更新时机 | Pod Assumed/Bound/Removed 时更新 | 调度决策、绑定确认、回滚时更新 |
| 一致性影响 | 资源超卖风险(Assumed 未释放) | 状态漂移风险(Assumed 与 Bound 不一致) |
| 排查指标 | node_resource_usage 与缓存对比 |
assumed_pods 与 bound_pods 差值 |