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 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
|
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
|
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 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
|
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
| apiVersion: kubescheduler.config.k8s.io/v1 kind: KubeSchedulerConfiguration parallelism: 16
|
10.2 百分比过滤
大集群中不需要对所有节点打分,只需找到足够好的节点:
1 2 3
| percentageOfNodesToScore: 50
|
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
| kubectl describe pod <pod-name>
kubectl logs -n kube-system kube-scheduler-<node> -f
kubectl describe node <node-name> kubectl top nodes
|
13. 总结
kube-scheduler 的核心是两阶段调度:
- Filter(过滤):淘汰不满足硬性条件的节点
- Score(打分):在可用节点中选出最优节点
调度策略的优先级:
1 2 3
| nodeName(直接指定)> nodeSelector > nodeAffinity > podAffinity/AntiAffinity > topologySpreadConstraints > taint/toleration > 资源充足性
|
合理使用亲和性、污点和拓扑约束,可以实现精细化的工作负载分布策略。