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集群的关键。

发表评论 取消回复