Kubernetes 调度器深度实战:从调度框架到自定义调度器开发

调度器是 Kubernetes 集群的"决策大脑",它决定了 Pod 应该运行在哪个 Node 上。本文深入剖析 kube-scheduler 的内部机制、调度框架设计、核心算法,并通过 Go 代码实战演示如何开发自定义调度器与高级调度策略。

一、调度全景:从 Pod 创建到绑定

理解调度器,首先要看清 Pod 生命周期中的调度路径。当用户执行 kubectl apply -f pod.yaml 时,背后发生的是:


用户请求 → API Server (准入/校验) → etcd 持久化
    ↓ (Pod 状态为 Pending)
调度器 Watch API Server → 发现未绑定 Pod → 调度周期 → Bind
    ↓
目标节点 kubelet → CRI 创建容器 → 上报 Running

关键洞察:kube-scheduler 不直接与 kubelet 交互。它只负责通过 API Server 将 Pod 的 spec.nodeName 更新为目标节点,kubelet 监听到该字段变更后才会启动容器。这种基于声明式的间接通信解耦了调度逻辑与执行逻辑,是整个调度架构灵活性的基石。

一个常被误解的点是:调度器处理的不仅是新创建的 Pod。Node 故障驱逐、手动迁移、抢占(preemption)、重调度(descheduler)都会重新进入调度 pipeline。因此调度器实际上是一个持续运行的调和循环(reconciliation loop)。

二、调度框架:可扩展的插件架构

从 Kubernetes 1.15 开始,kube-scheduler 引入了调度框架(Scheduling Framework),将调度过程拆解为一系列可扩展的阶段。

2.1 调度周期与绑定周期

每个调度周期(Scheduling Cycle)是互斥的、单 Pod 的处理过程。绑定周期(Binding Cycle)则处理 Node 绑定的异步验证:


Scheduling Cycle(串行)
├── QueueSort          // 排序等待队列中的 Pod
├── PreFilter          // 预检:校验 Pod 与集群状态兼容性
├── Filter             // 过滤:淘汰不满足硬约束的 Node
├── PreScore           // 打分前置:标准化 Node 元数据
├── Score              // 打分:对候选 Node 量化评估
├── NormalizeScore     // 归一化分数到 [0, 100]
├── Reserve            // 预留:标记目标 Node 资源
├── Permit             // 准入:等待/拒绝/允许
└── PostBind           // 绑定后置:通知插件绑定完成

Binding Cycle(异步,可与下一个调度周期并行)
├── PreBind            // 绑定前置:创建 PVC 等依赖资源
├── Bind               // 执行绑定:调用 API Server 更新 nodeName
└── PostBind           // 绑定后置:日志/指标上报

注意 Permit 阶段的精妙设计:它可以返回 Wait 让 Pod 在队列中等待固定超时,这实现了类似"批处理准入"的能力——例如等待多个 Pod 同时就绪后再一起调度,这正是 Gang Scheduling 的基础。

2.2 核心源码结构

追踪调度框架的实现,核心入口在 pkg/scheduler/scheduler.go:


// 调度器主循环的核心绑定逻辑
func (sched *Scheduler) scheduleOne(ctx context.Context) {
    // 1. 从优先队列中取出 Pod
    podInfo := sched.NextPod()
    
    // 2. 执行 Scheduling Cycle
    scheduleResult, err := sched.SchedulePod(ctx, podInfo.Pod)
    
    // 3. 异步执行 Binding Cycle
    go func() {
        err := sched.BindPod(ctx, scheduleResult)
    }()
}

而插件注册的机制在 pkg/scheduler/framework/runtime/framework.go:


type Framework interface {
    // QueueSort 插件:决定 Pod 调度顺序
    QueueSortPlugin
    
    // 过滤阶段:淘汰不满足约束的 Node
    FilterPlugin
    
    // 打分阶段:为 Node 评分 [0, MaxNodeScore]
    ScorePlugin
    
    // 预订阶段:预留资源
    ReservePlugin
    
    // 准入控制:决定 Pod 是否/何时允许绑定
    PermitPlugin
}

三、核心调度算法详解

3.1 过滤阶段:谓词判定

过滤阶段内置的预置谓词函数是最小的准入集,典型实现包括:


// predicates 插件的关键过滤逻辑(K8s 1.29  迁移至 framework 插件)

// NodeResourcesFit: 校验 CPU/Memory/Ephemeral Storage 是否充足
func nodeResourcesFit(pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
    // 计算节点可分配资源
    allowedPodNumber := nodeInfo.AllowedPodNumber()
    if len(nodeInfo.Pods) 1                        

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部
0.366264s