kubernetes_scheduler

kube-scheduler

简介

kube-scheduler 是 Kubernetes 的核心组件之一,负责 Pod 的调度(Scheduling)。其主要职责是为尚未绑定节点的 Pod 选择合适的节点进行运行。调度器在 Kubernetes 中扮演“任务分派员”的角色,核心目标是满足工作负载的资源需求(如 CPU、内存等),同时遵循用户定义的调度策略和约束(如 Pod 的 nodeSelectoraffinity 等)。

工作流程

一、Pod 监听

  1. 初始化 Informer

    • kube-scheduler 启动时,会创建一个 Pod Informer,用于监听和缓存 Pod 的变化(包括新增、修改、删除事件)。

    • 仅监听那些尚未绑定节点(即 spec.nodeName 为空)的 Pod。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      func NewScheduler(...) (*Scheduler, error) {
      // ...existing code...
      // 创建 Pod Informer
      podInformer := informerFactory.Core().V1().Pods()

      // 初始化调度器
      sched := &Scheduler{
      podQueue: queue, // 调度队列
      podInformer: podInformer, // Pod Informer
      podLister: podInformer.Lister() // 用于从缓存中获取 Pod 列表
      // ...existing code...
      }
      // ...existing code...
      }
  2. 通过 List 和 Watch 获取数据

    • List:调度器启动时,通过 API Server 全量获取当前所有未绑定节点的 Pod。
    • Watch:随后通过 Watch API 订阅 Pod 的增量更新事件(新增、修改、删除)。

二、接收调度请求

  • 当用户创建 Pod 时,若 Pod 未绑定到特定节点(即 .spec.nodeName 为空),kube-scheduler 会将其视为待调度对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    // 在 Informer 初始化过程中,设置过滤器
    podInformer := informerFactory.Core().V1().Pods().Informer()

    // 仅监听未绑定节点的 Pod
    podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
    AddFunc: func(obj interface{}) {
    pod := obj.(*v1.Pod)
    if pod.Spec.NodeName == "" {
    // Pod 未被绑定,加入调度队列
    sched.AddPodToQueue(pod)
    }
    },
    UpdateFunc: func(oldObj, newObj interface{}) {
    oldPod := oldObj.(*v1.Pod)
    newPod := newObj.(*v1.Pod)
    if oldPod.Spec.NodeName == "" && newPod.Spec.NodeName == "" {
    // Pod 更新但仍未绑定
    sched.AddPodToQueue(newPod)
    }
    },
    DeleteFunc: func(obj interface{}) {
    pod := obj.(*v1.Pod)
    if pod.Spec.NodeName == "" {
    // Pod 被删除
    sched.DeletePodFromQueue(pod)
    }
    },
    })

三、可调度节点筛选(Filter 阶段)

kube-scheduler 会根据一系列过滤规则(Filter Plugins,原称 Predicates),筛选出所有满足 Pod 调度需求的候选节点。例如:

常见的 Filter 插件包括:

  • PodFitsHostPorts:检查目标节点上是否有 Pod 使用了相同的 hostPort,避免端口冲突。
  • PodFitsResources:检查节点资源(CPU、内存、GPU 等)是否满足 Pod 的 requests
  • HostName:若 Pod 指定了 spec.nodeName,则只匹配该节点。
  • MatchNodeSelector:节点需满足 Pod 的 nodeSelector 条件。
  • NoVolumeZoneConflict:检查 Pod 所需的 Volume 是否可在目标节点所在的可用区挂载。
  • MatchInterPodAffinity:检查 Pod 的亲和性或反亲和性规则。
  • NoDiskConflict:验证节点上是否存在 Volume 冲突。
  • PodToleratesNodeTaints:检查 Pod 是否能容忍节点的 Taint。
  • CheckNodeMemoryPressure:节点是否处于内存压力状态。
  • CheckNodeDiskPressure:节点是否处于磁盘压力状态。
  • NoVolumeNodeConflict:检查节点是否满足 Pod 所需 Volume 的挂载条件。

四、候选节点评分(Score 阶段)

  • 对通过过滤的候选节点,调度器会根据一系列优先级规则(Scoring Plugins,原称 Priorities)为每个节点打分。
  • 评分目标是从所有可用节点中选择最优节点。例如:
    • 优先将 Pod 调度到负载较低的节点。
    • 优先调度到与数据存储位置更近的节点。
    • 避免过多 Pod 调度到同一节点,防止资源热点。

常见的 Scoring 插件包括:

  • SelectorSpreadPriority:将同一类型的 Pod 尽量分布到不同节点,实现负载均衡和高可用。
  • InterPodAffinityPriority:优先调度到与目标 Pod 拓扑接近的节点。
  • LeastRequestedPriority:优先调度到资源请求最少的节点。
  • BalancedResourceAllocation:优先选择 CPU 和内存使用率均衡的节点。
  • NodePreferAvoidPodsPriority:优先避开有 preferAvoidPods 标记的节点。
  • NodeAffinityPriority:优先调度到匹配 NodeAffinity 的节点。
  • TaintTolerationPriority:优先调度到能容忍特定 Taint 的节点。
  • ImageLocalityPriority:优先调度到已缓存所需镜像的节点。
  • MostRequestedPriority:优先调度到已使用较多资源的节点,提高资源利用率。
  • EqualPriority:所有节点分配相同优先级(用于测试或无特殊需求场景)。

BalancedResourceAllocation 插件为例,其实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func (b *BalancedAllocation) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
nodeInfo, err := b.nodeInfoLister.Get(nodeName)
if err != nil {
return 0, framework.NewStatus(framework.Error, err.Error())
}
return b.score(pod, nodeInfo.Node())
}

func (b *BalancedAllocation) score(pod *v1.Pod, node *v1.Node) int64 {
// 计算 CPU 和内存的使用率
cpuFraction := node.UsedCPU() / node.TotalCPU()
memoryFraction := node.UsedMemory() / node.TotalMemory()
// 计算得分
score := 100 - int64(math.Abs(cpuFraction-memoryFraction)*100)
return score
}

五、决策与绑定(Bind 阶段)

  • 从评分最高的节点中选择一个节点,作为该 Pod 的最终调度目标。
  • 调度器将调度结果写入 etcd,将 Pod 绑定到目标节点。

调度流程可概括为:过滤(Filter) → 评分(Score) → 绑定(Bind)


Kubernetes 的 QoS 类(Quality of Service)

Kubernetes 根据 Pod 的资源请求和限制自动确定其 QoS 类,QoS 类影响调度、资源管理和优先级。主要分为三类:

1. Guaranteed

  • 所有容器的 requestslimits 必须完全相等。

  • 特点:最高优先级,资源保障最强,适用于关键性应用。

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    apiVersion: v1
    kind: Pod
    metadata:
    name: guaranteed-pod
    spec:
    containers:
    - name: app
    image: nginx
    resources:
    requests:
    memory: "500Mi"
    cpu: "0.5"
    limits:
    memory: "500Mi"
    cpu: "0.5"

2. Burstable

  • 至少有一个容器设置了 requests,但 requestslimits 不完全相等。

  • 特点:可获得至少 requests 的资源,超出部分可被回收。

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    apiVersion: v1
    kind: Pod
    metadata:
    name: burstable-pod
    spec:
    containers:
    - name: app
    image: nginx
    resources:
    requests:
    memory: "200Mi"
    cpu: "0.2"
    limits:
    memory: "500Mi"
    cpu: "0.5"

3. BestEffort

  • 所有容器都未设置 requestslimits

  • 特点:最低优先级,仅在资源充足时调度。

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    apiVersion: v1
    kind: Pod
    metadata:
    name: besteffort-pod
    spec:
    containers:
    - name: app
    image: nginx

QoS 类与调度行为

  • 资源分配优先级:Guaranteed > Burstable > BestEffort
  • 节点驱逐优先级:BestEffort 最先被驱逐,Guaranteed 最后被驱逐。
  • 调度优先级:调度器优先分配高 QoS 的 Pod。

调度阶段 QoS 决策举例

调度器会依次检查:

  • 节点是否满足 Pod 的 requests(按 QoS 优先顺序)。
  • 节点剩余容量能否满足 Pod 的 limits
  • 结合 Taints、Tolerations、亲和性等规则综合评估。

QoS 调度优化建议

  • 关键应用使用 Guaranteed,明确资源上下界。
  • 配合 Taints 和 Tolerations,将高 QoS 应用调度到专用节点。
  • 通过 kube-reservedsystem-reserved 预留关键资源。
  • 使用 ResourceQuota 限制低 QoS 资源消耗。

requests 与 limits

1. 概念

  • requests:容器运行的最低资源需求,调度器据此判断节点是否有足够资源。
  • limits:容器运行的资源上限,Kubelet 和容器运行时据此限制资源使用。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
containers:
- name: example-container
image: nginx
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"

2. 调度器如何使用 requests

  • 节点可用资源 = 节点总资源 - 所有已分配 Pod 的 requests
  • 调度器过滤掉可用资源不足的节点,仅在满足 requests 的节点中选择。

3. Kubelet 如何使用 limits

  • limits.cpu 通过 Cgroup 的 CPU Shares 和 CPU Quota 实现。
  • limits.memory 是硬限制,超出会被 OOM 杀死。

4. 最佳实践

  • requests 设为容器最低需求,limits 设为最大允许值。
  • 避免 requests 和 limits 差距过大,防止资源超分配或浪费。
  • 配合 ResourceQuota 和 LimitRange 管理资源。

Pod 调度到指定 Node

1. NodeSelector

  • 通过为节点打标签,并在 Pod 的 spec.nodeSelector 字段指定标签,实现将 Pod 调度到特定节点。

示例

1
kubectl label nodes node1 disktype=ssd
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx
image: nginx
nodeSelector:
disktype: ssd
  • 支持多个键值对,所有条件需同时满足(AND 逻辑)。

2. NodeSelector 局限性

  • 仅支持简单 AND 逻辑,无法表达复杂调度规则。
  • 节点标签变更不会自动触发 Pod 重新调度。
  • 推荐使用 NodeAffinity、Taints/Tolerations 或自定义调度器实现更复杂需求。

NodeAffinity

1. 概念

  • NodeAffinity 是基于节点标签的调度约束,分为硬约束(requiredDuringSchedulingIgnoredDuringExecution)和软约束(preferredDuringSchedulingIgnoredDuringExecution)。

2. 语法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-example
spec:
containers:
- name: demo-container
image: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node1
- node2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
  • matchExpressions 支持 In、NotIn、Exists、DoesNotExist、Gt、Lt 等操作符。
  • 多个 nodeSelectorTerms 之间为 OR 关系,matchExpressions 内为 AND 关系。

PodAffinity 与 PodAntiAffinity

1. 概念

  • PodAffinity:指定 Pod 应调度到与特定 Pod 接近的节点。
  • PodAntiAffinity:指定 Pod 应避免与特定 Pod 同节点。

2. 应用场景

  • 优化服务间局部性、降低延迟、提升高可用性。

3. 配置示例

Pod Affinity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: webserver
topologyKey: "kubernetes.io/hostname"
containers:
- name: nginx
image: nginx

Pod Anti-Affinity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: webserver
topologyKey: "kubernetes.io/hostname"
containers:
- name: nginx
image: nginx
  • topologyKey 可指定亲和性作用域,如节点、可用区、区域等。

Taints 与 Tolerations

1. 概念

  • Taints(污点):为节点设置特殊标记,限制哪些 Pod 可调度到该节点。
  • Tolerations(容忍):Pod 声明可容忍哪些污点,从而允许被调度到带有对应污点的节点。

Taint 结构:Key、Value、Effect(NoSchedule、PreferNoSchedule、NoExecute)

1
kubectl taint nodes node1 dedicated=special-workload:NoSchedule

Toleration 结构:

1
2
3
4
5
tolerations:
- key: "dedicated"
operator: "Equal"
value: "special-workload"
effect: "NoSchedule"

2. 工作机制

  • 节点有 Taint 时,只有带有匹配 Toleration 的 Pod 才能调度到该节点。
  • Taints 主动限制,Tolerations 被动声明。

3. 应用场景

  • 专用节点调度、节点维护、负载隔离等。

故障转移(Failover)

Kubernetes 通过 Taints 和 Tolerations 实现节点故障时的 Pod 驱逐与重新调度。

1. 节点不可用检测

  • 节点心跳超时(默认 40 秒),Node Controller 会为节点添加 node.kubernetes.io/unreachable:NoExecute Taint。

2. Pod 行为

  • 无 Toleration:Pod 立即被驱逐并重新调度。
  • 有 Toleration 且无 tolerationSeconds:Pod 无限期保留。
  • 有 Toleration 且有 tolerationSeconds:Pod 在宽限期后被驱逐。

3. 故障转移流程

  1. 节点失联,自动添加 NoExecute Taint。
  2. Pod 检查 Toleration,决定是否驱逐。
  3. 被驱逐的 Pod 重新调度到健康节点。
  4. 节点恢复后,Taint 自动移除。

4. 调优建议

  • 合理设置心跳间隔和 tolerationSeconds
  • 无状态应用可设置较短容忍时间,有状态应用可适当延长。
  • 配合 Liveness/Readiness Probe 区分节点与 Pod 健康。

优先级调度(PriorityClass)与多调度器

1. PriorityClass

  • 通过 PriorityClass 对象为 Pod 设置调度优先级,value 越大优先级越高。
  • 支持抢占机制,高优先级 Pod 可驱逐低优先级 Pod。

示例:

1
2
3
4
5
6
7
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000
globalDefault: false
description: "This priority class is for high-priority workloads."
1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Pod
metadata:
name: high-priority-pod
spec:
priorityClassName: high-priority
containers:
- name: nginx
image: nginx

2. 多调度器(Multiple Schedulers)

  • 支持部署多个调度器,Pod 通过 spec.schedulerName 指定使用哪个调度器。

示例:

1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Pod
metadata:
name: custom-scheduled-pod
spec:
schedulerName: custom-scheduler
containers:
- name: nginx
image: nginx

3. 生产经验总结

  • 小集群高并发场景可通过多调度器分摊压力。
  • 使用 PriorityClass 控制任务优先级,结合资源配额和自定义调度器隔离高风险工作负载。
  • 持续监控调度器性能,优化缓存刷新机制,避免调度延迟和资源倾斜。

kubernetes_scheduler
https://mfzzf.github.io/2025/03/18/kubernetes-scheduler/
作者
Mzzf
发布于
2025年3月18日
许可协议