4. kube-scheduler 调度策略:默认调度框架、调度队列、抢占与优先级、自定义调度器扩展点
kube-scheduler 是 Kubernetes 控制面中负责 Pod 与 Node 匹配的核心组件。本章深入解析其内部调度策略,涵盖默认调度框架的流水线、调度队列的排序机制、基于优先级的抢占逻辑,以及通过扩展点实现自定义调度行为的工程方法。
4.1 默认调度框架:调度周期与绑定周期
Kubernetes 自 v1.19 起将调度器重构为可扩展的“调度框架”(Scheduling Framework)。每个调度周期分为两个阶段:调度周期(Scheduling Cycle)和绑定周期(Binding Cycle)。调度周期为 Pod 选择一个合适的 Node,绑定周期将决策结果持久化到 API Server。
关键概念:调度框架将调度流程拆解为多个扩展点(Extension Points),每个扩展点可以插入自定义插件。默认调度器内置了超过 20 个插件,覆盖从队列排序到节点预留的全流程。
4.2 调度队列:三种队列与排序策略
调度器内部维护三种队列来管理待调度 Pod:
| 队列名称 | 数据结构 | 作用 | 出队条件 |
|---|---|---|---|
| activeQ | 堆(Heap) | 存放所有等待调度的 Pod,按优先级排序 | 调度器每次从中取出优先级最高的 Pod |
| backoffQ | 堆(Heap) | 存放调度失败的 Pod,等待退避重试 | 退避时间到期后移回 activeQ |
| unschedulableQ | Map + 集合 | 存放因资源不足、污点/容忍等不可调度 Pod | 集群拓扑变化(如新 Node 加入)时重新评估 |
性能要点:activeQ 使用最小堆实现,Pod 优先级越高(Priority 值越大),出队越早。backoffQ 的退避时间呈指数增长(base=1s,max=10s),避免频繁重试导致调度器 CPU 飙升。
4.3 抢占与优先级:PodPriority 与抢占策略
当高优先级 Pod 无法调度时,调度器会触发抢占(Preemption),驱逐低优先级 Pod 以释放资源。抢占流程分为四个步骤:
- 筛选候选节点:找出所有可能通过驱逐 Pod 来满足高优先级 Pod 需求的 Node。
- 选择受害者:在候选节点上选择优先级最低的 Pod 作为驱逐目标,优先驱逐优先级低于抢占者的 Pod。
- 执行驱逐:通过 API Server 删除受害者 Pod,触发优雅终止(Graceful Shutdown)。
- 重新调度:高优先级 Pod 进入 activeQ 头部,等待调度到释放资源的 Node。
注意:抢占不是万能的。如果集群中所有 Node 都无法满足 Pod 的硬约束(如 nodeSelector、亲和性),抢占也不会成功。此外,频繁抢占会导致低优先级 Pod 被反复驱逐,影响服务稳定性。
4.4 自定义调度器扩展点:插件开发与配置
调度框架允许通过实现特定扩展点接口来开发自定义插件。常见的扩展点包括:
| 扩展点 | 接口方法 | 典型用途 |
|---|---|---|
| Filter | Filter(ctx, state, pod, node) *Status |
实现自定义资源过滤(如 GPU 型号匹配) |
| Score | Score(ctx, state, pod, node) (int64, *Status) |
实现自定义打分策略(如网络延迟感知) |
| Reserve | Reserve(ctx, state, pod, nodeName) *Status |
预留资源,防止并发调度冲突 |
| Permit | Permit(ctx, state, pod, nodeName) *Status |
实现调度准入控制(如配额检查) |
| PreBind | PreBind(ctx, state, pod, nodeName) *Status |
绑定前执行操作(如挂载存储卷) |
以下是一个自定义 Filter 插件的代码示例:
package myplugin
import (
"context"
"k8s.io/kubernetes/pkg/scheduler/framework"
)
type MyFilter struct{}
func (m *MyFilter) Name() string {
return "MyFilter"
}
func (m *MyFilter) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
// 检查 Node 是否包含特定标签
if _, exists := nodeInfo.Node().Labels["accelerator"]; !exists {
return framework.NewStatus(framework.Unschedulable, "Node missing accelerator label")
}
return framework.NewStatus(framework.Success, "")
}
// 注册插件
func New(_ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
return &MyFilter{}, nil
}
配置方式:自定义插件通过 KubeSchedulerConfiguration 的
profiles[].plugins 字段启用。可以指定插件在哪个扩展点生效,以及是启用(enabled)还是禁用(disabled)。多调度器 Profile 支持为不同 Pod 使用不同的调度策略。
4.5 性能验证:调度延迟与吞吐量
验证调度策略性能时,重点关注以下指标:
- 调度延迟(Scheduling Latency):从 Pod 创建到调度器完成绑定的时间,目标 < 100ms(P99)。
- 调度吞吐量(Throughput):每秒调度的 Pod 数量,目标 > 100 Pods/s(1000 Node 集群)。
- 抢占成功率:高优先级 Pod 触发抢占后成功调度的比例,目标 > 95%。
- 队列深度:activeQ 和 backoffQ 的瞬时长度,避免队列积压导致调度饥饿。
验证工具:使用
kubectl get events --field-selector reason=FailedScheduling 查看调度失败事件;使用 kube-scheduler --v=5 开启详细日志;使用 Prometheus 监控 scheduler_scheduling_algorithm_duration_seconds 和 scheduler_e2e_scheduling_duration_seconds 指标。