[AI生成] kube-scheduler核心优化总结
一、整体架构演进
kube-scheduler 从早期的单体调度器演进到基于 Scheduler Framework 的插件化架构,核心优化思路围绕减少锁竞争、提升缓存命中率、并行化调度和降低无效调度四个方向展开。
1.1 调度流程概览
Pod 进入调度队列
↓
出队(Pop)
↓
调度循环(Scheduling Cycle)
├── PreFilter(前置过滤)
├── Filter(节点过滤/预选)
├── PostFilter(后置过滤/抢占)
├── PreScore(前置打分)
├── Score(节点打分/优选)
├── Reserve(预留资源)
└── Permit(许可)
↓
绑定循环(Binding Cycle)
├── WaitOnPermit(等待许可)
├── PreBind(前置绑定)
├── Bind(绑定)
└── PostBind(后置绑定)
1.2 两个独立控制循环
kube-scheduler 内部有两个独立的控制循环:
- 调度循环(Scheduling Loop):不断从队列中取出 Pod,为其选择最合适的节点。
- 节点/集群信息更新循环:通过 informer 监听 Node、Pod 等资源变化,实时更新调度缓存。
两个循环通过 scheduler cache 解耦,调度循环只读缓存,更新循环写缓存,避免了频繁的锁竞争。
二、调度队列优化
2.1 三种队列设计
kube-scheduler 维护三个 Pod 队列,各司其职:
| 队列 | 作用 | 数据结构 |
|---|---|---|
| activeQ(活跃队列) | 存放等待调度的 Pod,按优先级排序 | 堆(Heap) |
| backoffQ(退避队列) | 存放调度失败后等待重试的 Pod,指数退避 | 堆(Heap) |
| unschedulableQ(不可调度队列) | 存放完全不可调度的 Pod,等待集群变化后重新入队 | Map + 双向链表 |
2.2 Queue Hint(队列提示)
引入版本:Kubernetes v1.28 开始逐步引入,v1.30+ 逐步成熟。
核心思想:传统方式下,任何集群变化(如节点新增、Pod 删除等)都会将 unschedulableQ 中的所有 Pod 全部移回 activeQ 重试,造成大量无效调度。Queue Hint 让每个 Filter 插件告诉调度器:哪些变化会影响我对 Pod 的调度决策,从而只移动真正可能调度成功的 Pod。
工作机制:
// 每个 Filter 插件可以实现 EventsToRegister 方法
type FilterPlugin interface {
Filter(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *NodeInfo) *Status
EventsToRegister() []ClusterEvent
}
- 每个插件声明自己关心的集群事件类型(如 NodeAdd、PodUpdate 等)
- 当集群发生变化时,调度器只唤醒那些受该变化影响的 Pod
- 大幅减少 unschedulableQ → activeQ 的无效搬迁
收益:在大规模集群中,unschedulable Pod 数量巨大时,Queue Hint 能显著降低调度器负载,提升调度吞吐量。
2.3 队列间 Pod 倒换机制
Node 增加 / Pod 删除 / 资源变化等事件
↓
unschedulableQ 中受影响的 Pod(Queue Hint 过滤)
↓
根据是否在退避期决定:
├── 退避期内 → backoffQ
└── 退避期外 → activeQ
↓
backoffQ 到期后自动移入 activeQ
三、调度缓存优化
3.1 Snapshot 快照机制
调度循环开始时,会从 scheduler cache 中 snapshot(拍快照),得到一份当前集群状态的只读副本。后续整个调度过程(Filter、Score 等)都基于这份快照进行,不再访问共享缓存。
优势:
- 无锁读取:调度过程中不需要加锁,大幅减少锁竞争
- 一致性:单次调度内看到的集群状态是一致的
- 并行安全:多个调度协程可以并行工作,各自持有自己的快照
3.2 缓存分层设计
schedulerCache
├── Node 缓存(节点信息、已分配资源)
├── Pod 缓存(已调度 Pod 的资源占用)
├── Assume 机制(预占缓存)
└── Snapshot(只读快照)
3.3 Assume 机制
Pod 调度成功后,在真正 Bind 之前,先在缓存中 Assume(假设) 该 Pod 已经绑定到节点上,更新节点的已分配资源。这样后续调度能立即感知到该节点资源变化,不需要等待 API Server 的回调。
如果后续 Bind 失败,会调用 Forget 回滚 Assume 的结果。
四、预选(Filter)优化
4.1 并行化 Filter
对多个节点的 Filter 检查是并行执行的,利用多核 CPU 提升预选速度:
// 并行对所有节点执行 Filter
workqueue.ParallelizeUntil(ctx, 16, int(allNodes), checkNode)
默认并发度是 16(parallelism 参数),可通过配置调整。
4.2 节点短路(Short-circuit)
- 一旦某个 Filter 插件判定节点不通过,后续 Filter 不再执行
- 按固定顺序执行 Filter,计算量小的前置,快速排除不合格节点
- 节点数量较多时,采用 nominatedNode 优先检查机制
4.3 预选节点数量优化(percentageOfNodesToScore)
大规模集群中,不需要对所有节点都执行完整的预选+优选流程。调度器找到一定比例的可调度节点后就停止预选,直接进入优选阶段。
- 默认策略:动态计算,节点越多,预选比例越低
- 100 节点以下:100%(全量预选)
- 5000 节点时:约 10%(500 个节点)
- 可通过
percentageOfNodesToScore配置项调整
目的:在保证调度质量的前提下,减少不必要的计算。
五、优选(Score)优化
5.1 并行化 Score
与 Filter 类似,对多个节点的 Score 计算也是并行执行的。
5.2 归一化打分
每个 Score 插件输出的分数范围不同(有的 0-10,有的 0-100),调度器会将其归一化到 0-100 的统一区间,然后按权重加权求和。
// 归一化公式
normalizedScore = int64(score) * int64(MaxNodeScore) / int64(maxScore)
5.3 常用打分插件及其权重
| 插件 | 默认权重 | 作用 |
|---|---|---|
| NodeResourcesBalancedAllocation | 1 | 资源均衡度 |
| NodeResourcesFit | 1 | 资源匹配度(支持 LeastAllocated / MostAllocated) |
| PodTopologySpread | 2 | Pod 拓扑分布 |
| InterPodAffinity | 2 | Pod 间亲和/反亲和 |
| NodeAffinity | 1 | 节点亲和性 |
| TaintToleration | 1 | 污点容忍 |
| ImageLocality | 1 | 镜像本地性 |
六、Scheduler Framework 插件化
6.1 扩展点一览
Scheduler Framework 定义了多个扩展点,开发者可以通过实现插件接口来自定义调度逻辑,无需修改内核代码。
| 扩展点 | 阶段 | 说明 |
|---|---|---|
| QueueSort | 队列排序 | 决定 Pod 在 activeQ 中的排序 |
| PreFilter | 前置过滤 | 计算 Pod 级别的元信息,供后续 Filter 使用 |
| Filter | 节点过滤 | 判断节点是否满足 Pod 要求 |
| PostFilter | 后置过滤 | 调度失败后执行,如抢占、降级 |
| PreScore | 前置打分 | 计算打分所需的公共信息 |
| Score | 节点打分 | 为可调度节点打分排序 |
| Reserve | 资源预留 | Assume Pod 到节点前调用 |
| Permit | 许可 | 批准/拒绝/延迟 Pod 的绑定 |
| PreBind | 前置绑定 | 绑定前执行额外操作 |
| Bind | 绑定 | 执行实际的绑定操作 |
| PostBind | 后置绑定 | 绑定成功后调用 |
| Unreserve | 预留回滚 | 绑定失败时回滚 Reserve 操作 |
6.2 多配置文件(Multi-profile)
kube-scheduler 支持同时运行多个调度配置文件(profile),每个 profile 可以:
- 有独立的插件列表和配置
- 有独立的调度队列
- 有独立的调度循环
不同类型的 Pod 可以选择不同的 schedulerName,走不同的调度逻辑,互不影响。
七、抢占(Preemption)优化
7.1 抢占流程
当高优先级 Pod 无法调度时,触发抢占逻辑:
- 选出所有可能的候选节点
- 计算每个节点上需要驱逐的低优先级 Pod
- 选择代价最小的节点(驱逐 Pod 最少、优先级最低等)
- 驱逐选定的 Pod,将高优先级 Pod 调度上去
7.2 抢占优化点
- 候选节点筛选:只考虑有低优先级 Pod 的节点
- dry-run 抢占:在缓存中模拟抢占,不实际驱逐 Pod
- nominatedNodeName:抢占成功后标记候选节点,下次优先调度
八、性能调优参数
8.1 关键配置项
| 参数 | 默认值 | 说明 |
|---|---|---|
kube-api-burst |
100 | API Server 请求突发量 |
kube-api-qps |
50 | API Server 请求 QPS |
percentageOfNodesToScore |
0(动态计算) | 参与优选的节点比例 |
schedulerName |
default-scheduler | 调度器名称 |
workers |
数量 = CPU 核数 | 并行调度协程数 |
8.2 大规模集群建议
- 调高 workers:多核机器上增加并行调度协程数
- 降低 percentageOfNodesToScore:集群很大时(>1000 节点),适当降低预选节点比例
- 启用 Queue Hint:v1.28+ 启用,减少无效重调度
- 合理配置插件权重:根据业务场景调整,减少不必要的复杂计算
- 分层调度:使用多 profile,不同业务走不同调度逻辑
九、核心优化总结
| 优化方向 | 技术手段 | 收益 |
|---|---|---|
| 减少锁竞争 | Snapshot 快照、调度循环只读缓存 | 调度过程无锁,吞吐提升 |
| 并行化 | Filter/Score 并行执行、多 workers | 利用多核,缩短单次调度时间 |
| 降低无效调度 | Queue Hint、percentageOfNodesToScore | 减少不必要的计算和重调度 |
| 插件化架构 | Scheduler Framework、多 profile | 灵活扩展,不修改内核 |
| 缓存优化 | Assume 机制、分层缓存 | 实时感知资源变化,减少 API 调用 |
| 智能重调度 | backoff 退避、unschedulableQ 分级 | 避免失败 Pod 频繁重试 |
kube-scheduler 的优化思路本质上是经典的空间换时间(缓存、快照)、并行化(多核利用)和减少无效计算(Hint、短路)三大范式在调度场景下的具体实践。
浙公网安备 33010602011771号