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 从调度决策到缓存确认的完整状态转换流程:

调度缓存状态转换流程 调度缓存状态转换流程 调度队列 调度决策 乐观绑定 调度周期 绑定请求 缓存 Assumed 资源立即占用 API Server 处理 etcd 写入 缓存 Bound 状态确认 立即标记 异步确认 成功响应 缓存回滚 释放 Assumed 资源 失败/超时 ⚠ 一致性关键点 Assumed 状态是乐观锁的核心,但也是缓存不一致的根源。 回滚机制必须及时释放 Assumed 资源,否则会导致节点资源超卖。

4.4 缓存一致性的关键机制

为了维护 Assumed 状态与实际绑定状态之间的一致性,调度器实现了以下关键机制:

  1. 异步绑定确认(Binding Confirmation): 调度器在收到 API Server 的绑定成功响应后,会调用 cache.AssumePod 的逆操作 cache.FinishBinding,将 Pod 从 Assumed 状态转换为 Bound 状态。
  2. 过期回滚(Expiration & Rollback): 如果调度器在超时时间内(默认 30 秒)未收到 API Server 的确认,会触发回滚操作,从缓存中移除该 Assumed Pod 并释放其占用的资源。
  3. 周期性同步(Periodic Sync): 调度器会定期(默认 5 分钟)从 API Server 全量同步集群状态,用 etcd 中的真实数据覆盖缓存,修复任何残留的不一致。
  4. 事件驱动更新(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 差值