Kubernetes调度器深度剖析:Pod调度策略与自定义调度器

引言

Kubernetes(简称K8s)已成为容器编排平台的事实标准。在K8s集群中,Scheduler(调度器)负责将新创建的Pod分配到合适的Node上运行。调度决策直接影响集群的资源利用率、应用性能和可靠性。

默认的kube-scheduler虽然功能强大且通用,但面对特定场景(如GPU调度、拓扑感知、能耗优化)时,需要深入了解其机制并进行定制。本文将深入解析K8s调度器的工作原理、调度策略和扩展方法。

一、调度器架构

1.1 调度框架

K8s 1.15引入的Scheduling Framework(调度框架)是调度器可扩展性的核心。整个调度Pipeline分为两个阶段:

  • Scheduling Cycle:串行执行,为单个Pod选择最佳Node
  • Binding Cycle:并行执行,将调度决策持久化(写入etcd)

Scheduling Cycle包含以下Extension Points(扩展点):

扩展点阶段作用
QueueSort队列排序定义Pod的出队顺序
PreFilter预过滤检查集群状态,提前剪枝
Filter过滤排除不满足条件的Node
PreScore预评分计算评分时的预处理数据
Score评分对Node打分排序
Permit准入批准/拒绝/等待调度决策
Reserve预留为Pod预留资源
PreBind预绑定绑定前的准备工作
Bind绑定将Pod绑定到Node
PostBind绑定后绑定完成后的清理
Unreserve释放释放预留资源(Bind失败时)

1.2 调度队列

Pod等待调度时进入调度队列,队列分三层:

  • activeQ:活跃队列,Pod等待被调度(堆结构,按优先级排序)
  • podBackoffQ:退避队列,调度失败的Pod等待重试(指数退避)
  • unschedulableQ:不可调度队列,暂时无法调度的Pod(等待Node/事件触发)

二、默认调度策略详解

2.1 过滤策略(Predicates)

kube-scheduler在过滤阶段排除不满足条件的Node:

  • NodeName/NodeSelector:精确匹配节点名称或标签
  • NodeAffinity:节点亲和性(required/preferred)
  • PodAffinity/PodAntiAffinity:Pod间亲和/反亲和
  • Taints和Tolerations:污点和容忍
  • Resources:Node资源是否充足(CPU、Memory、GPU等)
  • Volume:存储卷绑定约束
  • TopologySpreadConstraints:拓扑分布约束

2.2 评分策略(Priorities/Scores)

对过滤后的Node进行评分:

  • LeastRequestedPriority:倾向资源利用率低(空闲多)的Node
  • MostRequestedPriority:倾向资源利用率高的Node(减少碎片化)
  • BalancedResourceAllocation:CPU和Memory均衡使用的Node
  • ImageLocalityPriority:已有容器镜像的Node(减少镜像拉取)
  • InterPodAffinityPriority:Pod亲和加分
  • SelectorSpreadPriority:同一Service/ReplicaSet的Pod分散到不同Node
  • NodeAffinityPriority:Node亲和加分

2.3 调度Command Profile

K8s v1.26+引入Scheduling Profiles,支持在同一个调度器内配置多套策略组合。默认Profile包括:

  • default-scheduler:标准生产配置
  • bin-packing:偏向Node资源紧凑分配
  • low-latency:偏向延迟敏感工作
  • elastic:适合弹性伸缩场景

3 高级调度技术

3.1 多调度器

K8s支持同时运行多个调度器。在Pod的spec.schedulerName字段指定:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-workload
spec:
  schedulerName: custom-gpu-scheduler  # 使用自定义调度器
  containers:
  - name: ml-trainer
    image: pytorch/pytorch:2.0
    resources:
      limits:
        nvidia.com/gpu: 2

3.2 Descheduler(重调度器)

Descheduler在运行时重新平衡资源。它定期检查:

  • Node资源高/低水位(低利用Node上的Pod迁移)
  • Pod拓扑违反(如反亲和关系被违反)
  • 重复Pod(多副本在同一Node)
  • 污点违反(新污点Node上的Pod驱逐)

3.3 Dynamic Resource Allocation (DRA)

K8s v1.26+ alpha引入DRA:允许Pod运行时动态申请/释放资源,不局限于Node上的静态资源。这对GPU分时共享、FPGA等场景极具价值。

四、自定义调度器实践

4.1 开发自定义调度器

自定义调度器本质上是一个监听API Server的Controller。核心步骤:

// 伪代码:自调度器核心逻辑
for {
    // 1. Watch未指定调度器的Pod
    pod := watchUnscheduledPods()
    
    // 2. 过滤Node(实现你的过滤逻辑)
    feasibleNodes := filterNodes(pod, allNodes)
    
    // 3. 评分Node(实现你的评分逻辑)
    bestNode := scoreNodes(pod, feasibleNodes)
    
    // 4. 绑定Pod到Node
    bindPod(pod, bestNode)
}

4.2 Scheduling Framework扩展

更优雅的方式是编写Scheduling Framework插件,无需重写整个调度器:

// 示例:自定义评分插件
type MyCustomScorePlugin struct{}

func (pl *MyCustomScorePlugin) Name() string {
    return "MyCustomScore"
}

func (pl *MyCustomScorePlugin) Score(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) (int64, *Status) {
    nodeInfo := pl.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
    // 自定义评分逻辑:根据自定义指标或业务需求评分
    customMetric := getMetric(nodeInfo.Node())
    score := calculateScore(customMetric)
    return score, nil
}

func (pl *MyCustomScorePlugin) ScoreExtensions() ScoreExtensions {
    return pl
}

func (pl *MyCustomScorePlugin) NormalizeScore(ctx context.Context, state *CycleState, pod *v1.Pod, scores NodeScoreList) *Status {
    // 归一化分数
    return nil
}

4.3 Kubebuilder模式实现

使用Kubebuilder或Operator SDK可以快速构建调度器的管理平台:

  • 定义SchedulerPool CRD声明调度池配置
  • Controller监听SchedulerPool变化
  • 动态注册Scheduling Framework插件配置

五、最佳实践

5.1 资源管理最佳实践

  • 务必设置Requests和Limits:Requests用于调度,Limits用于运行时限制
  • QoS分级:Guaranteed(==)、Burstable(<=)、BestEffort(无设置)
  • 避免单个Pod占用Node全部资源,为系统守护进程保留资源(system-reserved/kube-reserved)

5.2 亲和性最佳实践

  • preferredDuringSchedulingIgnoredDuringExecution比required更灵活,适合生产
  • PodAntiAffinity确保高可用:跨Node、跨可用区、跨区域分布副本
  • TopologySpreadConstraints比PodAntiAffinity表达能力更强

5.3 HPA与调度器协同

  • HPA自动扩缩Replicas数量,Scheduler将新Pod分配到合适的Node
  • li>Cluster Autoscaler在Node资源不足时自动添加Node li>VPA(垂直Pod自动扩缩)动态调整Pod资源,需配合调度器再次调度

总结

K8s调度器是一个精密的多阶段Pipeline系统。从默认的kube-scheduler到多调度器架构,再到Scheduling Framework扩展机制,K8s为不同场景提供了丰富的定制空间。理解调度框架的扩展点和实际生产中的约束条件(资源、拓扑、污点、亲和性),是构建高效弹性K8s集群的关键。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部