4. Scheduler调度策略与扩展

Kubernetes Scheduler 是控制面的核心组件之一,负责将待调度的 Pod 绑定到最合适的 Node 上。调度决策直接影响集群的资源利用率、应用性能和稳定性。本章深入解析默认调度器的工作流程、核心调度算法,并探讨如何通过自定义调度器与调度框架(Scheduling Framework)实现扩展。

4.1 默认调度器工作流程

默认调度器(kube-scheduler)采用“两阶段”调度模型:筛选(Filtering) 和 评分(Scoring)。调度器为每个待调度的 Pod 执行以下步骤:

  1. 监听队列:从内部调度队列中取出一个待调度的 Pod。
  2. 预选(Predicates / Filtering):遍历所有 Node,应用一组预选策略,过滤掉不满足 Pod 调度约束的 Node。
  3. 优选(Priorities / Scoring):对通过预选的 Node 进行评分,计算每个 Node 的优先级分数。
  4. 绑定(Binding):选择分数最高的 Node,将 Pod 绑定到该 Node 上。
调度流程关键点:调度器在筛选阶段会快速排除不可用节点,减少评分阶段的计算量。评分阶段则通过加权求和的方式,从多个维度评估节点的“适合度”。
默认调度器工作流程 默认调度器工作流程 调度队列 筛选(Filtering) 评分(Scoring) 绑定(Binding) Node 列表 筛选阶段过滤不可用节点 → 评分阶段对剩余节点排序 → 绑定到最优节点

4.2 调度算法:节点筛选与评分

4.2.1 节点筛选(Predicates)

筛选阶段通过一系列预选策略判断 Node 是否满足 Pod 的基本运行条件。常见的预选策略包括:

预选策略 说明 检查维度
PodFitsResources 检查 Node 剩余 CPU、内存是否满足 Pod 请求 资源容量
PodFitsHostPorts 检查 Node 上请求的主机端口是否已被占用 端口冲突
PodMatchNodeSelector 检查 Pod 的 nodeSelector 或 nodeAffinity 是否匹配 Node 标签 节点亲和性
PodToleratesNodeTaints 检查 Pod 是否容忍 Node 上的 Taints 污点容忍
CheckNodeCondition 检查 Node 是否处于 Ready、DiskPressure 等正常状态 节点健康
CheckVolumeBinding 检查 Pod 请求的 PVC 是否可被 Node 上的 PV 满足 存储绑定
筛选阶段优化:默认调度器会按策略的“计算成本”排序,先执行轻量级策略(如 CheckNodeCondition),再执行资源密集型策略(如 PodFitsResources),以尽早过滤掉不可用节点。

4.2.2 节点评分(Priorities)

评分阶段对通过筛选的 Node 进行打分,分数越高表示 Node 越适合运行该 Pod。评分策略通常基于资源利用率、亲和性、拓扑分布等维度。常见的评分策略包括:

评分策略 权重 评分逻辑
LeastRequestedPriority 1 优先选择资源(CPU+内存)请求量最少的节点,促进资源均衡
BalancedResourceAllocation 1 优先选择 CPU 和内存使用率最均衡的节点
NodeAffinityPriority 2 根据 Pod 的 nodeAffinity 规则匹配程度加分
InterPodAffinityPriority 2 根据 Pod 间的亲和性/反亲和性规则加分
ImageLocalityPriority 1 优先选择已缓存 Pod 所需镜像的节点
NodePreferAvoidPodsPriority 10000 根据节点注解(scheduler.alpha.kubernetes.io/preferAvoidPods)进行高权重加分

最终得分 = Σ (每个评分策略的分数 × 该策略的权重)。调度器选择得分最高的 Node 进行绑定;若多个 Node 得分相同,则随机选择一个。

4.3 自定义调度器与调度框架

当默认调度器无法满足特定业务需求(如 GPU 拓扑感知、网络延迟优化、自定义资源约束)时,Kubernetes 提供了两种扩展方式:自定义调度器 和 调度框架(Scheduling Framework)。

4.3.1 自定义调度器

自定义调度器是一个独立的调度程序,可以完全替代默认调度器,也可以与默认调度器共存。Pod 通过 spec.schedulerName 字段指定使用哪个调度器。

apiVersion: v1
kind: Pod
metadata:
  name: custom-scheduled-pod
spec:
  schedulerName: my-custom-scheduler
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      requests:
        cpu: "1"
        memory: "512Mi"

自定义调度器的实现方式:

  • 基于 kube-scheduler 源码修改:直接 Fork 官方调度器代码,修改调度逻辑。
  • 基于调度框架扩展点:使用调度框架提供的插件机制,只实现特定扩展点。
  • 完全独立开发:使用 client-go 监听 Pod 和 Node 事件,自行实现调度算法。
注意:自定义调度器需要自行处理 Pod 的绑定操作(通过 Kubernetes API 创建 Binding 对象),并确保与默认调度器的调度域不冲突。

4.3.2 调度框架(Scheduling Framework)

调度框架是 Kubernetes 1.19+ 引入的插件化调度架构,将调度流程拆分为多个扩展点(Extension Points),允许用户通过插件注入自定义逻辑,而无需修改调度器核心代码。

调度框架的主要扩展点包括:

扩展点 阶段 插件作用
QueueSort 调度队列排序 自定义 Pod 在调度队列中的排序逻辑
PreFilter 筛选前 预处理 Pod 信息,或提前拒绝明显不可调度的 Pod
Filter 筛选 实现自定义的节点过滤逻辑
PostFilter 筛选后 当所有节点都被过滤掉时,执行替代方案(如抢占)
PreScore 评分前 预处理评分所需的数据
Score 评分 实现自定义的节点评分逻辑
NormalizeScore 评分归一化 对多个评分插件的分数进行归一化处理
Reserve 预留 在绑定前预留节点资源,防止并发调度冲突
Permit 许可 批准或延迟 Pod 的绑定操作
PreBind 绑定前 在绑定前执行预处理(如挂载卷)
Bind 绑定 实现自定义的绑定逻辑
PostBind 绑定后 绑定完成后执行清理或通知
调度框架扩展点 调度框架扩展点(Scheduling Framework Extension Points) QueueSort PreFilter / Filter / PostFilter PreScore / Score / NormalizeScore Reserve / Permit PreBind / Bind / PostBind 插件示例:自定义 GPU 拓扑感知调度 Filter 扩展点:过滤掉 GPU 显存不足的节点 Score 扩展点:优先选择 GPU 拓扑距离最近的节点

4.3.3 调度框架插件开发示例

以下是一个简单的调度框架插件示例,实现一个自定义的 Filter 插件,拒绝所有标签为 environment=production 的节点:

package myplugin

import (
    "context"
    "k8s.io/kubernetes/pkg/scheduler/framework"
)

type MyFilterPlugin struct{}

var _ framework.FilterPlugin = &MyFilterPlugin{}

func (pl *MyFilterPlugin) Name() string {
    return "MyFilterPlugin"
}

func (pl *MyFilterPlugin) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
    node := nodeInfo.Node()
    if node.Labels["environment"] == "production" {
        return framework.NewStatus(framework.Unschedulable, "production nodes are not allowed")
    }
    return framework.NewStatus(framework.Success, "")
}

注册插件后,在调度器配置中启用:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: my-scheduler
    plugins:
      filter:
        enabled:
          - name: MyFilterPlugin
最佳实践:优先使用调度框架扩展点实现自定义调度逻辑,而非开发完全独立的调度器。调度框架提供了更低的维护成本、更好的兼容性,以及与原调度器共享调度队列和缓存的能力。

4.4 调度策略对比与选型

方案 适用场景 复杂度 维护成本 灵活性
默认调度器 通用工作负载,无需特殊调度逻辑 低 低 低
调度框架插件 需要特定调度策略(如 GPU、网络、存储感知) 中 中 高
自定义调度器 完全替代调度逻辑,或需要与外部系统深度集成 高 高 极高

选择调度方案时,应优先评估默认调度器是否满足需求;若需扩展,优先使用调度框架插件;仅在调度框架无法满足的极端场景下,才考虑开发独立的自定义调度器。