2. 调度器工作原理:调度队列、调度周期与绑定周期、调度器扩展点(Scheduling Framework)
Kubernetes 调度器(kube-scheduler)的核心职责是为每个待调度的 Pod 找到一个最合适的 Node。理解其内部工作原理,是掌握亲和性规则与网络拓扑通信机制的基础。本章将深入剖析调度器的三大核心模块:调度队列、调度周期与绑定周期,以及调度框架(Scheduling Framework)的扩展点机制。
2.1 调度队列:Pod 的等待与排序
调度队列是调度器的入口,负责管理所有等待调度的 Pod。它并非简单的 FIFO 队列,而是根据 Pod 的优先级、调度重试次数等因素进行动态排序。
| 队列类型 | 说明 | 关键特性 |
|---|---|---|
| activeQ(活跃队列) | 存放所有等待调度的 Pod,按优先级排序。 | 高优先级的 Pod 会优先被调度。 |
| backoffQ(回退队列) | 存放调度失败的 Pod,等待一段时间后重试。 | 重试间隔呈指数级增长,避免频繁重试。 |
| unschedulableQ(不可调度队列) | 存放因资源不足、节点不匹配等原因无法调度的 Pod。 | 当集群状态发生变化(如新节点加入)时,会重新评估。 |
关键机制:调度器通过 PodInformer 监听 API Server 中的 Pod 变化。当一个新的 Pod 被创建且
spec.nodeName 为空时,它会被加入 activeQ。调度器会持续从 activeQ 中取出 Pod 进行调度。
2.2 调度周期与绑定周期:一次完整的调度决策
一次完整的调度过程被分为两个逻辑阶段:调度周期(Scheduling Cycle)和绑定周期(Binding Cycle)。这两个阶段是串行执行的,但调度器可以并行处理多个 Pod 的调度周期。
性能优化点:调度周期是“找”节点的过程,绑定周期是“用”节点的过程。由于绑定周期涉及与 API Server 的交互(写入
binding 资源),将其异步化可以显著提升调度器的吞吐量。
2.3 调度框架(Scheduling Framework)与扩展点
Kubernetes 1.19 之后引入了 Scheduling Framework,将调度器的核心逻辑抽象为一系列可插拔的扩展点(Extension Points)。这使得用户可以自定义调度逻辑,而无需修改调度器核心代码。
调度框架将调度周期和绑定周期细分为多个扩展点,每个扩展点可以注册多个插件。插件按顺序执行,任何一个插件都可以否决(拒绝)当前的调度决策。
2.4 核心扩展点详解
| 扩展点 | 所属阶段 | 作用 | 典型插件示例 |
|---|---|---|---|
| QueueSort | 调度队列 | 对 activeQ 中的 Pod 进行排序。 | PrioritySort |
| PreFilter | 调度周期 | 预处理 Pod 信息,为 Filter 做准备。 | NodeResourcesFit |
| Filter | 调度周期 | 过滤掉不满足条件的 Node(预选)。 | NodeAffinity, TaintToleration |
| PostFilter | 调度周期 | 当 Filter 后无可用 Node 时,尝试替代方案。 | Preemption(抢占) |
| PreScore | 调度周期 | 为 Score 阶段准备评分数据。 | — |
| Score | 调度周期 | 对通过 Filter 的 Node 进行打分(优选)。 | NodeResourcesBalancedAllocation |
| NormalizeScore | 调度周期 | 对 Score 结果进行归一化处理。 | — |
| Reserve | 调度周期 | 在选定 Node 上预留资源,防止冲突。 | VolumeBinding |
| Permit | 调度周期 | 批准或延迟绑定操作。 | — |
| PreBind | 绑定周期 | 在绑定前执行准备工作。 | VolumeBinding |
| Bind | 绑定周期 | 将 Pod 绑定到 Node(写入 API Server)。 | DefaultBinder |
| PostBind | 绑定周期 | 绑定后的清理或通知工作。 | — |
注意:自定义插件必须实现对应的接口,并通过
--config 参数传递给 kube-scheduler。错误的插件实现可能导致调度器崩溃或调度决策异常。
2.5 调度框架的插件执行流程
以下 SVG 展示了调度框架中,一个 Pod 从进入调度队列到完成绑定的完整插件执行流程。
2.6 自定义调度器与多调度器
Kubernetes 支持在一个集群中运行多个调度器实例。Pod 可以通过 spec.schedulerName 字段指定使用哪个调度器。默认调度器的名称为 default-scheduler。
实践建议:对于需要特殊调度策略的工作负载(如 GPU 任务、延迟敏感型服务),建议编写自定义调度器插件,而不是修改默认调度器。这样可以保持集群的稳定性和可维护性。
# 示例:Pod 指定使用自定义调度器
apiVersion: v1
kind: Pod
metadata:
name: my-gpu-pod
spec:
schedulerName: my-custom-scheduler # 指定调度器名称
containers:
- name: gpu-container
image: nvidia/cuda:11.0-base
resources:
limits:
nvidia.com/gpu: 1
2.7 本章小结
- 调度队列:通过 activeQ、backoffQ、unschedulableQ 三级队列管理 Pod 的调度状态,确保高优先级 Pod 优先被处理。
- 调度周期与绑定周期:调度周期负责“找”节点,绑定周期负责“用”节点。两者串行执行,但调度周期可以并行处理多个 Pod。
- Scheduling Framework:通过 12 个扩展点实现了调度逻辑的完全可插拔。用户可以通过编写插件来定制 Filter、Score、Bind 等行为。
- 多调度器支持:允许在集群中运行多个调度器实例,满足不同工作负载的差异化调度需求。
下一阶段:在理解了调度器的工作原理后,我们将进入第 3 章,深入探讨节点亲和性(nodeAffinity)与 Pod 间亲和性/反亲和性(podAffinity/podAntiAffinity)的配置与实战案例。