2. Kubernetes 调度框架:核心组件、调度周期与扩展点
Kubernetes 调度器(kube-scheduler)是控制平面中负责将 Pod 绑定到合适 Node 的核心组件。本章深入解析调度框架的内部结构,包括调度队列、调度周期与绑定周期,以及两种扩展机制:传统的 Extender 和现代的 Scheduling Framework。
2.1 调度框架核心组件
调度器内部由多个协作组件构成,每个组件承担明确的职责:
| 组件 | 职责 | 关键特性 |
|---|---|---|
| 调度队列 | 管理待调度的 Pod,按优先级和调度重试策略排序 | 三种子队列:activeQ、backoffQ、unschedulableQ |
| 调度周期 | 为 Pod 选择一个最优 Node(Filter + Score) | 可插拔扩展点,支持自定义过滤和打分逻辑 |
| 绑定周期 | 将 Pod 与选定 Node 的绑定关系写入 API Server | 支持异步绑定、预留(Reserve)和预绑定(PreBind) |
| 缓存(Cache) | 维护集群 Node 和 Pod 的实时快照 | 增量更新,减少 API Server 请求压力 |
| 调度策略 | 定义 Filter 和 Score 的规则集合 | 可通过配置或扩展点动态调整 |
架构要点: 调度队列、调度周期、绑定周期三者构成调度器的主循环。缓存组件为调度周期提供一致性的集群视图,避免调度过程中因数据不一致导致错误决策。
2.2 调度队列:三级队列模型
调度队列采用三级队列模型,确保高优先级 Pod 优先调度,同时为调度失败的 Pod 提供退避重试机制:
- activeQ(活跃队列):存放等待调度的 Pod,按优先级排序。调度器每次从 activeQ 头部取出一个 Pod 进行调度。
- backoffQ(退避队列):存放调度失败但仍在退避期内的 Pod。退避时间指数增长(如 1s、2s、4s…),退避结束后移回 activeQ。
- unschedulableQ(不可调度队列):存放因资源不足、节点选择器不匹配等原因无法调度的 Pod。当集群发生变动(如新增 Node、资源释放)时,重新评估并移回 activeQ。
调度队列流转规则:
- 新 Pod 进入 activeQ,按优先级排序。
- 调度失败 → 进入 backoffQ(退避)或 unschedulableQ(不可调度)。
- 退避到期或集群事件触发 → 移回 activeQ 重新调度。
- 调度成功 → 从队列中移除,进入绑定周期。
2.3 调度周期与绑定周期
调度器为每个 Pod 执行两个主要阶段:调度周期(Scheduling Cycle)和绑定周期(Binding Cycle)。调度周期是同步的,绑定周期是异步的,两者通过扩展点实现可插拔。
调度周期(Scheduling Cycle)
调度周期为 Pod 选择一个最优 Node,包含以下步骤:
- PreFilter:预处理 Pod 和集群信息,检查 Pod 是否满足基本条件(如资源请求是否合法)。
- Filter:对所有 Node 执行过滤,排除不满足条件的 Node(如资源不足、端口冲突、节点选择器不匹配)。
- PostFilter:当所有 Node 都被过滤掉时,执行补救逻辑(如抢占低优先级 Pod)。
- PreScore:在打分前进行数据准备或运行检查。
- Score:对通过过滤的 Node 进行打分(0-100),分数越高越优。
- NormalizeScore:对分数进行归一化处理,确保不同打分插件的结果可比较。
- Reserve:在缓存中预留选定 Node 的资源,防止其他 Pod 同时占用。
绑定周期(Binding Cycle)
绑定周期将 Pod 与选定 Node 的绑定关系持久化到 API Server:
- PreBind:执行绑定前的准备工作(如创建存储卷、配置网络)。
- Bind:向 API Server 发送绑定请求(POST /api/v1/namespaces/{ns}/pods/{name}/binding)。
- PostBind:绑定成功后执行清理或通知操作。
关键区别: 调度周期是同步的,调度器必须等待所有 Filter 和 Score 插件执行完毕;绑定周期是异步的,调度器可以同时处理多个 Pod 的绑定请求,提高吞吐量。
2.4 扩展点:Extender 与 Scheduling Framework
Kubernetes 提供两种扩展调度器行为的方式:传统的 Extender 和现代的 Scheduling Framework。两者在架构、灵活性和性能上存在显著差异。
| 特性 | Extender | Scheduling Framework |
|---|---|---|
| 架构 | 外部 HTTP 服务,通过 Webhook 调用 | 内部插件,编译进调度器或通过动态加载 |
| 扩展点 | 仅支持 Filter、Prioritize、Bind | 支持 PreFilter、Filter、PostFilter、PreScore、Score、NormalizeScore、Reserve、PreBind、Bind、PostBind 等 |
| 性能 | 网络调用开销大,不适合高频场景 | 本地调用,延迟低,吞吐量高 |
| 灵活性 | 只能扩展有限逻辑,无法干预调度队列 | 可自定义整个调度流程,包括队列排序、抢占逻辑 |
| 维护性 | 独立部署,版本兼容性需自行管理 | 与调度器同进程,版本由 Kubernetes 管理 |
| 推荐场景 | 遗留系统、快速原型、非核心调度逻辑 | 生产环境、高性能调度、复杂策略 |
Extender 的工作方式
Extender 通过 kube-scheduler 配置文件中的 extenders 字段注册。调度器在 Filter、Prioritize、Bind 阶段向 Extender 发送 HTTP 请求,Extender 返回结果。典型配置示例:
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
extenders:
- urlPrefix: "http://my-extender:8888/"
filterVerb: "filter"
prioritizeVerb: "prioritize"
bindVerb: "bind"
weight: 1
nodeCacheCapable: false
managedResources:
- name: "example.com/resource"
ignoredByScheduler: false
Scheduling Framework 的工作方式
Scheduling Framework 允许开发者编写 Go 插件,实现特定扩展点接口。插件通过 profile 配置注册,支持多 profile 实现不同调度策略。核心接口示例:
// FilterPlugin 接口定义
type FilterPlugin interface {
Name() string
Filter(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *NodeInfo) *Status
}
// ScorePlugin 接口定义
type ScorePlugin interface {
Name() string
Score(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) (int64, *Status)
ScoreExtensions() ScoreExtensions
}
实践建议: 对于网络拓扑感知调度,推荐使用 Scheduling Framework 实现自定义 Filter 和 Score 插件。例如,在 Filter 阶段排除跨拓扑域延迟过高的 Node,在 Score 阶段优先选择与 Pod 亲和性匹配的 Node。Extender 仅适合作为临时方案或与第三方系统集成。
2.5 调度框架扩展点全景图
下表汇总了 Scheduling Framework 的所有扩展点及其在调度周期/绑定周期中的位置:
| 扩展点 | 所属阶段 | 执行时机 | 典型用途 |
|---|---|---|---|
| PreFilter | 调度周期 | Filter 之前 | 检查 Pod 合法性、预处理数据 |
| Filter | 调度周期 | 过滤 Node | 资源检查、端口冲突、节点选择器 |
| PostFilter | 调度周期 | Filter 之后(无可用 Node) | 抢占、重新评估 |
| PreScore | 调度周期 | Score 之前 | 数据准备、运行检查 |
| Score | 调度周期 | 打分 | 资源均衡、亲和性、拓扑感知 |
| NormalizeScore | 调度周期 | Score 之后 | 分数归一化 |
| Reserve | 调度周期 | 选定 Node 后 | 预留资源,防止并发冲突 |
| PreBind | 绑定周期 | Bind 之前 | 创建存储卷、配置网络 |
| Bind | 绑定周期 | 执行绑定 | 写入 API Server |
| PostBind | 绑定周期 | Bind 之后 | 清理、通知 |
扩展点选择策略: 网络拓扑感知调度主要使用 Filter(排除高延迟 Node)和 Score(优先选择拓扑域内 Node)。如果需要预留网络资源,可在 Reserve 扩展点实现。避免在 PreFilter 或 PostFilter 中执行复杂计算,以免影响调度性能。