kube-scheduler 深度解析:Pod 调度的艺术

kube-scheduler 深度解析:Pod 调度的艺术

1. 调度器的职责

kube-scheduler 负责为新创建的 Pod 选择最合适的 Node。它监听 apiserver 中未绑定节点的 Pod,通过一系列过滤和打分算法,选出最优节点,然后将绑定关系写回 apiserver。

1
2
3
4
5
6
7
8
9
10
11
12
新 Pod 创建(nodeName 为空)


kube-scheduler

过滤(Filter)→ 打分(Score)→ 绑定(Bind)


Pod.spec.nodeName = "node-3"


kubelet 在 node-3 上启动 Pod

2. 调度框架(Scheduling Framework)

Kubernetes 1.15+ 引入了调度框架,将调度过程拆分为多个扩展点:

1
2
3
4
5
6
7
8
9
10
┌─────────────────────────────────────────────────────────────┐
│ 调度周期(Scheduling Cycle) │
│ │
PreFilter → Filter → PostFilter → PreScore → Score
│ → Reserve → Permit │
├─────────────────────────────────────────────────────────────┤
│ 绑定周期(Binding Cycle) │
│ │
│ PreBind → Bind → PostBind │
└─────────────────────────────────────────────────────────────┘

各扩展点说明

扩展点 阶段 说明
PreFilter 调度 预处理 Pod 信息,检查前置条件
Filter 调度 过滤不可用节点(核心过滤)
PostFilter 调度 Filter 全部失败后触发(抢占)
PreScore 调度 打分前的预处理
Score 调度 对可用节点打分(0-100)
Reserve 调度 预留资源(乐观锁)
Permit 调度 允许/拒绝/等待绑定
PreBind 绑定 绑定前操作(如挂载卷)
Bind 绑定 将 Pod 绑定到节点
PostBind 绑定 绑定后清理工作

3. 过滤阶段(Filter)

过滤阶段淘汰不满足条件的节点,剩余节点进入打分阶段。

3.1 内置过滤插件

1
2
3
4
5
6
7
8
9
10
11
NodeUnschedulable     → 节点是否被标记为不可调度
NodeName → Pod 是否指定了 nodeName
NodePorts → 节点端口是否冲突
NodeAffinity → 节点亲和性规则
NodeResourcesFit → 节点资源是否充足(CPU/内存/GPU)
VolumeBinding → 存储卷是否可以绑定
VolumeRestrictions → 卷限制(如 AWS EBS 只能挂载一次)
VolumeZone → 卷的可用区是否匹配
PodTopologySpread → Pod 拓扑分布约束
InterPodAffinity → Pod 间亲和性/反亲和性
TaintToleration → 污点容忍

3.2 资源过滤详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 资源充足性检查(伪代码)
func NodeResourcesFit(pod *Pod, node *Node) bool {
requested := pod.Spec.Containers.Resources.Requests
allocatable := node.Status.Allocatable

// 检查 CPU
if node.usedCPU + requested.CPU > allocatable.CPU {
return false // 过滤掉
}
// 检查内存
if node.usedMemory + requested.Memory > allocatable.Memory {
return false
}
return true
}

4. 打分阶段(Score)

对过滤后的节点打分(0-100),选出最高分节点。

4.1 内置打分插件

插件 说明 默认权重
NodeResourcesBalancedAllocation 资源均衡分配 1
NodeResourcesLeastAllocated 优先选择资源使用少的节点 1
NodeAffinity 节点亲和性偏好 2
InterPodAffinity Pod 亲和性偏好 2
PodTopologySpread 拓扑分布偏好 2
TaintToleration 污点容忍偏好 1
ImageLocality 优先选择已有镜像的节点 1

4.2 最终得分计算

1
最终得分 = Σ (插件得分 × 插件权重) / Σ 权重

5. 节点亲和性(Node Affinity)

5.1 硬性要求(requiredDuringScheduling)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values:
- amd64
- key: node-type
operator: In
values:
- gpu

5.2 软性偏好(preferredDuringScheduling)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: zone
operator: In
values:
- us-east-1a
- weight: 20
preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssd

6. Pod 亲和性与反亲和性

6.1 Pod 亲和性(将 Pod 调度到一起)

1
2
3
4
5
6
7
8
9
10
11
12
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cache
topologyKey: kubernetes.io/hostname
# 调度到与 app=cache 的 Pod 同一节点

6.2 Pod 反亲和性(将 Pod 分散部署)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: kubernetes.io/hostname
# 尽量不与同类 Pod 调度到同一节点

7. 污点与容忍(Taints & Tolerations)

7.1 污点(Taint)

1
2
3
4
5
6
7
# 给节点打污点
kubectl taint nodes node1 key=value:NoSchedule
kubectl taint nodes node1 key=value:PreferNoSchedule
kubectl taint nodes node1 key=value:NoExecute

# 删除污点
kubectl taint nodes node1 key=value:NoSchedule-

污点效果:

  • NoSchedule:不调度新 Pod(已有 Pod 不受影响)
  • PreferNoSchedule:尽量不调度
  • NoExecute:不调度且驱逐已有不容忍的 Pod

7.2 容忍(Toleration)

1
2
3
4
5
6
7
8
9
spec:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"

# 容忍所有污点(慎用)
- operator: "Exists"

7.3 系统内置污点

1
2
3
4
5
6
7
node.kubernetes.io/not-ready          # 节点未就绪
node.kubernetes.io/unreachable # 节点不可达
node.kubernetes.io/memory-pressure # 内存压力
node.kubernetes.io/disk-pressure # 磁盘压力
node.kubernetes.io/pid-pressure # PID 压力
node.kubernetes.io/unschedulable # 节点不可调度
node.kubernetes.io/network-unavailable # 网络不可用

8. 拓扑分布约束(TopologySpreadConstraints)

将 Pod 均匀分布到不同的拓扑域(节点、可用区等):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spec:
topologySpreadConstraints:
- maxSkew: 1 # 最大偏差
topologyKey: kubernetes.io/hostname # 拓扑键
whenUnsatisfiable: DoNotSchedule # 不满足时拒绝调度
labelSelector:
matchLabels:
app: web
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone # 可用区级别
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: web

9. 优先级与抢占(Priority & Preemption)

9.1 PriorityClass

1
2
3
4
5
6
7
8
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "高优先级业务 Pod"
preemptionPolicy: PreemptLowerPriority # 可抢占低优先级 Pod
1
2
spec:
priorityClassName: high-priority

9.2 抢占流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
高优先级 Pod 无法调度


PostFilter 插件触发抢占逻辑


寻找可以通过驱逐低优先级 Pod 来腾出空间的节点


选择"代价最小"的节点(驱逐 Pod 数量最少)


设置低优先级 Pod 的 nominatedNodeName


低优先级 Pod 被优雅终止(graceful termination)


高优先级 Pod 调度到该节点

10. 调度器性能优化

10.1 并行调度

1
2
3
4
# scheduler 配置
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
parallelism: 16 # 并行调度 goroutine 数

10.2 百分比过滤

大集群中不需要对所有节点打分,只需找到足够好的节点:

1
2
3
percentageOfNodesToScore: 50  # 只对 50% 的节点打分
# 节点数 < 100:100%
# 节点数 > 5000:5%

10.3 调度队列

1
2
3
activeQ(活跃队列)    → 待调度的 Pod
backoffQ(退避队列) → 调度失败后退避等待的 Pod
unschedulableQ(不可调度队列)→ 当前无法调度的 Pod

11. 自定义调度器

11.1 调度器配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
plugins:
filter:
enabled:
- name: NodeResourcesFit
- name: MyCustomFilter # 自定义插件
score:
enabled:
- name: NodeResourcesBalancedAllocation
weight: 1
disabled:
- name: NodeResourcesLeastAllocated

11.2 指定调度器

1
2
spec:
schedulerName: my-custom-scheduler # 使用自定义调度器

12. 常见调度问题排查

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看 Pod 调度失败原因
kubectl describe pod <pod-name>
# 关注 Events 部分:
# Warning FailedScheduling 0/3 nodes are available:
# 1 node(s) had taint {node-role.kubernetes.io/master: },
# 2 Insufficient memory.

# 查看调度器日志
kubectl logs -n kube-system kube-scheduler-<node> -f

# 查看节点资源使用情况
kubectl describe node <node-name>
kubectl top nodes

13. 总结

kube-scheduler 的核心是两阶段调度

  1. Filter(过滤):淘汰不满足硬性条件的节点
  2. Score(打分):在可用节点中选出最优节点

调度策略的优先级:

1
2
3
nodeName(直接指定)> nodeSelector > nodeAffinity > 
podAffinity/AntiAffinity > topologySpreadConstraints >
taint/toleration > 资源充足性

合理使用亲和性、污点和拓扑约束,可以实现精细化的工作负载分布策略。


kube-scheduler 深度解析:Pod 调度的艺术
https://k8s.chucz.asia/kube-scheduler深度解析/
作者
K8s Engineer
发布于
2026年1月7日
许可协议