2. Kubernetes 调度基础:调度器工作原理、调度框架、调度队列与调度周期
Kubernetes 调度器(kube-scheduler)是控制平面中负责将 Pod 绑定到合适 Node 的核心组件。理解其内部工作原理、可扩展的调度框架、调度队列机制以及完整的调度周期,是进行调度优化和实现多租户发布窗口隔离的理论基础。
2.1 调度器工作原理概述
kube-scheduler 作为一个独立的二进制进程运行,通过 Kubernetes API 监听(Watch)未调度的 Pod(即 spec.nodeName 为空),并为每个待调度 Pod 选择一个最优的 Node,然后将绑定结果写入 API Server。其核心工作流程遵循“过滤 → 打分 → 绑定”的三段式模型。
| 阶段 | 名称 | 描述 | 关键动作 |
|---|---|---|---|
| 1 | 过滤(Predicates / Filtering) | 遍历所有 Node,排除不满足 Pod 调度约束的节点。 | 检查资源充足性、端口冲突、节点选择器、亲和性规则等。 |
| 2 | 打分(Priorities / Scoring) | 对通过过滤的 Node 进行优先级评分,选出最优节点。 | 计算资源利用率、Pod 分布均衡性、亲和性权重等。 |
| 3 | 绑定(Binding) | 将 Pod 与选定的 Node 进行绑定,更新 API Server 中的 Pod 对象。 | 创建 Binding 对象,更新 spec.nodeName。 |
2.2 调度框架(Scheduling Framework)
从 Kubernetes v1.19 开始,调度器引入了可插拔的调度框架。该框架定义了一组扩展点(Extension Points),允许开发者通过编写调度插件(Plugin)来定制调度行为,而无需修改调度器核心代码。这是实现自定义调度策略(如租户感知调度、窗口隔离调度)的关键机制。
2.2.1 核心扩展点
调度框架在调度周期的不同阶段插入了多个扩展点。插件可以在一个或多个扩展点注册并执行逻辑。
| 扩展点 | 所属阶段 | 插件作用 | 典型用例 |
|---|---|---|---|
| QueueSort | 调度队列 | 对调度队列中的 Pod 进行排序。 | 实现优先级抢占、租户公平排队。 |
| PreFilter | 过滤前 | 预处理 Pod 信息,检查 Pod 是否满足调度前提条件。 | 验证 Pod 的卷、网络等前置依赖。 |
| Filter | 过滤 | 排除不满足条件的 Node。 | 节点资源检查、污点容忍、节点亲和性。 |
| PostFilter | 过滤后 | 当过滤阶段没有可用 Node 时执行,通常用于抢占。 | 低优先级 Pod 抢占高优先级 Pod 的资源。 |
| PreScore | 打分前 | 生成可共享的打分状态数据。 | 计算全局资源视图。 |
| Score | 打分 | 对每个 Node 进行评分(0-100)。 | 资源均衡、镜像本地性、亲和性权重。 |
| NormalizeScore | 打分后 | 对多个 Score 插件的评分结果进行修正或组合。 | 权重归一化。 |
| Reserve | 绑定前 | 在选定 Node 上预留资源,防止并发冲突。 | 确保资源原子性分配。 |
| Permit | 绑定前 | 批准或延迟 Pod 的绑定,实现门控机制。 | 发布窗口隔离:在窗口外延迟绑定。 |
| PreBind | 绑定 | 在正式绑定前执行预处理。 | 挂载网络卷、配置网络策略。 |
| Bind | 绑定 | 执行最终的 Pod 与 Node 绑定操作。 | 默认绑定器或自定义绑定逻辑。 |
Permit 扩展点是实现“发布窗口隔离”的核心位置。通过编写一个 Permit 插件,可以检查当前时间是否在租户允许的发布窗口内,如果不在,则返回 Wait 状态,将 Pod 挂起,直到窗口打开或超时。
2.3 调度队列(Scheduling Queue)
调度队列是调度器的输入缓冲区,负责管理所有等待调度的 Pod。调度器并非为每个 Pod 立即启动一个调度周期,而是将 Pod 放入队列中,按特定顺序取出处理。队列的管理直接影响调度的公平性和效率。
2.3.1 队列类型与结构
调度器内部维护了三种逻辑队列:
- ActiveQ(活跃队列): 存放所有准备好被调度的 Pod。调度器的主循环从这个队列中取出 Pod 进行处理。
- BackoffQ(回退队列): 存放调度失败的 Pod。这些 Pod 在等待一段时间(回退时间)后,会被重新放回 ActiveQ 重试。
- UnschedulableQ(不可调度队列): 存放因集群资源不足或节点不匹配而无法调度的 Pod。当集群拓扑发生变化(如新增节点、资源释放)时,这些 Pod 会被重新评估并移回 ActiveQ。
| 队列名称 | 存储对象 | 触发重新入队条件 | 作用 |
|---|---|---|---|
| ActiveQ | 等待调度的 Pod | Pod 被创建、BackoffQ 超时、UnschedulableQ 触发事件 | 主调度循环的输入源 |
| BackoffQ | 调度失败的 Pod | 回退计时器到期(指数退避) | 避免频繁重试导致调度器过载 |
| UnschedulableQ | 因资源不足无法调度的 Pod | 集群事件(新增 Node、删除 Pod、资源更新) | 避免无效轮询,仅在集群状态变化时重试 |
2.3.2 队列排序与优先级
ActiveQ 默认是一个基于堆(Heap)实现的优先级队列。Pod 的排序规则由 QueueSort 扩展点插件决定。默认的排序策略基于 Pod 的优先级(Priority)和创建时间戳。高优先级的 Pod 会排在前面,相同优先级下,创建时间更早的 Pod 优先。
QueueSort 插件实现基于租户的公平调度(如轮询、加权公平队列),确保每个租户在调度队列中都有机会被处理。
2.4 调度周期(Scheduling Cycle)
一个完整的调度周期是指调度器从 ActiveQ 中取出一个 Pod 开始,到成功绑定或决定放弃该 Pod 为止的完整过程。它由多个阶段组成,并严格遵循调度框架定义的扩展点顺序。
2.4.1 调度周期阶段详解
- 出队: 从 ActiveQ 中取出优先级最高的 Pod。
- PreFilter: 执行 PreFilter 插件,进行前置检查(如 Pod 是否已被调度、卷是否就绪)。
- Filter: 执行 Filter 插件,遍历所有 Node,生成候选节点列表。如果候选节点为空,则进入 PostFilter(抢占逻辑)或将 Pod 放入 UnschedulableQ。
- PreScore/Score/NormalizeScore: 对候选节点进行打分和归一化,选出得分最高的节点。
- Reserve: 在选定节点上预留资源,防止并发调度冲突。
- Permit: 执行 Permit 插件。如果所有 Permit 插件都批准,则继续;如果有插件返回
Wait,则 Pod 进入等待状态,直到被批准或超时。 - PreBind/Bind: 执行绑定前的预处理(如挂载卷),然后执行 Bind 插件将 Pod 绑定到 Node。
- PostBind: 绑定成功后执行清理或通知操作。
2.5 调度器配置与多调度器
Kubernetes 支持在一个集群中运行多个调度器实例。Pod 可以通过 spec.schedulerName 字段指定使用哪个调度器。默认调度器的名称为 default-scheduler。
# Pod 指定使用自定义调度器
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
schedulerName: my-custom-scheduler # 指定调度器名称
containers:
- name: my-container
image: nginx
2.6 本章小结
- 调度器工作原理 遵循“过滤 → 打分 → 绑定”的三段式模型,核心是找到满足约束的最优节点。
- 调度框架 提供了可插拔的扩展点,特别是
Permit扩展点,是实现发布窗口隔离的关键技术。 - 调度队列 管理 Pod 的排队和重试逻辑,通过自定义
QueueSort插件可以实现租户间的公平调度。 - 调度周期 是一个完整的、原子性的处理流程,理解其内部阶段有助于定位调度瓶颈和设计自定义插件。