一、引言:为什么需要优先级与抢占?

在生产环境的Kubernetes集群中,不同应用对资源的需求和业务重要性各不相同。核心支付服务需要100%的可用性,而批量数据处理任务则可以容忍延迟。当集群资源紧张时,如何确保关键业务优先获得资源?如何避免低优先级任务“挤占”高优先级服务的空间?这正是一个长期困扰集群运维人员的难题。

默认情况下,Kubernetes调度器(kube-scheduler)遵循“先到先得”(FIFO)的原则。这种看似公平的调度机制,在生产环境中可能引发一系列问题:

  • 核心服务被阻塞:一个大型批处理任务占满节点资源,导致新创建的订单服务Pod无法调度。

  • 滚动更新失败:升级支付服务时,因资源不足,新版本Pod无法启动,旧版本又已终止,造成服务中断。

  • 资源碎片化:小规格的高优先级Pod因找不到合适节点而处于Pending状态,浪费集群潜在容量。

Pod优先级(Priority)和抢占(Preemption)机制正是为解决上述问题而设计。这一机制允许你为Pod赋予不同的“权重”,在资源不足时,让高优先级Pod“挤掉”低优先级Pod,实现智能的资源分配策略。Pod优先级与抢占特性已在Kubernetes v1.14中正式发布为稳定版(GA),是生产环境调度治理的核心组件之一。

本文将深入剖析PriorityClass、Pod优先级设置、抢占流程、调度器源码逻辑及最佳实践,从原理到实战,全面解读这一构建高可用云原生应用的核心调度机制。

二、核心概念:优先级与抢占

2.1 优先级(Priority)

Pod优先级表示一个Pod相对于其他Pod的重要性。在Kubernetes中,优先级通过一个32位整数值来表示——值越大,优先级越高。

优先级的作用体现在两个层面:

  1. 调度顺序:调度器将高优先级Pod置于调度队列的前端,优先进行调度尝试。

  2. 抢占决策:当高优先级Pod因资源不足无法调度时,调度器会识别并驱逐低优先级Pod,释放资源。

2.2 抢占(Preemption)

抢占是优先级机制的核心执行手段。当一个高优先级Pod无法被调度时,调度器会尝试抢占(驱逐)较低优先级的Pod,使待调度的Pod可以运行。

抢占机制的执行包含以下几个关键步骤:

  1. 标记优先级:为工作负载Pod添加priorityClassName,声明Pod的优先级。

  2. 模拟抢占:检测到新任务调度资源不足时,调度器模拟驱逐低优先级Pod是否能为高优先级任务释放足够的资源。

  3. 安全驱逐:调度器触发抢占逻辑后,节点上的kubelet安全地驱逐低优先级Pod。

  4. 重新调度:被驱逐的低优先级Pod会被其控制器(如Deployment)重新创建,寻找其他可用节点运行。

2.3 为什么Kubernetes需要这个特性?

Pod优先级和抢占作为Kubernetes 1.14正式GA的调度器特性,其核心价值在于:让用户在不过度配置集群的情况下,为关键工作负载实现高水平的调度置信度,同时在不牺牲核心工作负载可靠性的前提下提升集群资源利用率。

这一特性提供了两条关键路径:

  • 成本可控的调度保障:相比单纯依赖集群自动扩展(Cluster Autoscaler)增加节点,抢占机制可以在数秒内完成调度,对于延迟敏感型服务至关重要。

  • 资源利用率提升:通过在集群中运行非关键工作负载填充资源“空隙”,在关键工作负载需要时将其抢占,实现资源利用的最大化。

三、PriorityClass:优先级的定义模板

3.1 什么是PriorityClass?

PriorityClass是一个无命名空间(cluster-scoped)的对象,它定义了从优先级类名称到优先级整数值的映射。名称在PriorityClass对象元数据的name字段中指定,值在必填的value字段中指定——值越大,优先级越高。

3.2 PriorityClass字段详解

一个完整的PriorityClass包含以下字段:

yaml

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: "此优先级类应仅用于核心生产服务Pod"

各字段说明:

字段类型说明
value必填32位整数值,决定优先级高低。范围从-2,147,483,648到1,000,000,000(含)。值越大,优先级越高
globalDefault可选表示该类是否应用于未指定priorityClassName的Pod。系统中只能存在一个globalDefault: true的PriorityClass。若不存在,未指定优先级的Pod优先级为零
preemptionPolicy可选抢占策略:PreemptLowerPriority(默认)允许抢占低优先级Pod;Never禁止抢占
description可选一个任意字符串,用于向集群用户说明何时应使用此优先级类

3.3 内置PriorityClass

Kubernetes提供了两个内置的高优先级PriorityClass,用于保护系统关键组件:

名称值用途
system-node-critical2,000,001,000静态Pod(etcd、kube-apiserver、kube-scheduler、controller-manager等)使用此类
system-cluster-critical2,000,000,000插件类Pod(CoreDNS、Calico controller、metrics-server等)使用此类

这两个内置类确保关键系统组件始终优先调度,避免被普通应用抢占。在定义自定义PriorityClass时,务必使用低于上述内置类的优先级值,以防止应用Pod驱逐系统关键Pod。

3.4 创建PriorityClass的方法

方法一:使用kubectl命令行

bash

# 创建一个名为high-priority的优先级类
kubectl create priorityclass high-priority --value=1000 --description="high priority"

# 创建一个全局默认优先级类
kubectl create priorityclass default-priority --value=1000 --global-default=true --description="default priority"

# 创建一个禁止抢占的优先级类
kubectl create priorityclass high-priority --value=1000 --description="high priority" --preemption-policy="Never"

方法二:使用YAML清单文件

yaml

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority-apps
value: 1000000
preemptionPolicy: PreemptLowerPriority
globalDefault: false
description: "Mission Critical apps."

四、Pod优先级使用详解

4.1 在Pod中指定优先级

要为Pod分配特定优先级,需要在Pod的spec中添加priorityClassName字段,指向已创建的PriorityClass:

yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: dev
spec:
  containers:
  - name: web
    image: nginx:latest
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority-apps

在实际生产环境中,通常不需要直接创建Pod,而是将priorityClassName添加到集合对象(如Deployment、StatefulSet、DaemonSet)的Pod模板中。

4.2 调度器如何使用优先级

优先级准入控制器(Priority Admission Controller)负责将priorityClassName转换为对应的整数值,写入Pod的spec.priority字段。

调度器在处理调度队列时,按照以下规则使用优先级:

  1. 排序调度:调度器根据优先级值对Pending的Pod进行排序,高优先级Pod优先被尝试调度。

  2. 抢占触发:当高优先级Pod无法被调度时,触发抢占逻辑。

  3. 队列管理:高优先级Pod在调度队列中占据前端位置。

4.3 globalDefault机制详解

globalDefault是一个强大的特性,但也有其重要的注意事项:

  • 系统中只能存在一个globalDefault: true的PriorityClass。

  • 如果不存在设置了globalDefault的PriorityClass,没有priorityClassName的Pod的优先级为零。

  • 重要:添加globalDefault: true的PriorityClass不会改变现有Pod的优先级。此类PriorityClass的值仅用于添加后创建的Pod。

这一特性在实际生产中曾引发过严重故障。曾有团队在已投入生产的集群上部署了globalDefault: true的PriorityClass,导致大量未指定优先级(即优先级为零)的Pod被批量抢占,造成短暂的服务中断。

4.4 优先级值与取值范围

PriorityClass对象的value字段可以是任何小于或等于10亿的32位整数值。具体范围是从-2,147,483,648到1,000,000,000(含)。更大的数字被保留用于表示关键系统Pod的内置PriorityClass。

建议的优先级划分策略:

优先级范围建议用途
1,000,000,001+保留给系统内置PriorityClass
100,000 - 1,000,000,000业务关键型Pod
1,000 - 100,000一般业务Pod
0 - 1,000开发测试、非关键Pod
负数可抢占式批处理任务

五、抢占机制的深入剖析

5.1 抢占流程全景

一次完整的抢占任务包含以下核心流程:

text

┌─────────────────────────────────────────────────────────────────┐
│                        抢占完整流程                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. 标记任务优先级                                                │
│     └── 为Pod添加priorityClassName                              │
│                                                                  │
│  2. 正常调度尝试                                                  │
│     └── 调度器尝试直接调度Pod                                     │
│                                                                  │
│  3. 资源不足 → 触发抢占                                           │
│     └── 识别到Pod因资源不足无法调度                               │
│                                                                  │
│  4. 模拟抢占                                                      │
│     └── 模拟驱逐低优先级Pod,验证是否能释放足够资源                │
│                                                                  │
│  5. 确定受害者Pod(Victims)                                      │
│     └── 选择要被驱逐的低优先级Pod                                 │
│                                                                  │
│  6. 执行驱逐                                                      │
│     └── kubelet安全驱逐受害者Pod                                  │
│                                                                  │
│  7. 调度高优先级Pod                                               │
│     └── 将高优先级Pod绑定到目标节点                               │
│                                                                  │
│  8. 受害者Pod重新调度                                             │
│     └── Controller重建被驱逐的Pod                                 │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

5.2 抢占触发的时机

抢占逻辑是在正常调度流程无法找到合适节点时触发的。具体来说,当kube-scheduler通过预选(Filter)阶段发现没有任何节点能满足Pod的调度条件(主要是资源不足),且该Pod的优先级类允许抢占时,抢占流程才会启动。

5.3 受害者选择算法

当多个节点都可能成为抢占目标时,调度器需要选择一个最优的节点和一组受害者Pod。调度器采用以下标准来评估候选节点:

  1. PDB(Pod Disruption Budget)破坏最小化:优先选择破坏PDB限制最少的节点。PDB是Kubernetes中用于保护应用可用性的机制,它定义了Pod在自愿性中断期间可以同时终止的最大数量。

  2. 受害者优先级最低化:优先选择优先级最低的受害者Pod集合。调度器会计算所有候选节点,找出需要牺牲的总优先级最低、同时能释放足够资源的组合。

  3. 节点选择:在破坏PDB最少、受害者优先级最低之间取得平衡,最终选择最优节点。

调度器采用以下方式模拟抢占:将调度失败的节点中的低优先级Pod“预驱逐”,判断驱逐后是否能让高优先级Pod调度成功,这些节点将进入备选列表。然后,调度器将高优先级Pod依次放入备选节点,再按优先级将预驱逐的Pod重新添加回来,直到节点资源耗尽。在此过程中,计算驱逐每个Pod需要破坏多少PDB限制,最终选择破坏PDB限制最少、受害者Pod优先级总和最低的节点。

5.4 安全驱逐与优雅终止

当受害者Pod被选中后,调度器会调用API Server删除这些Pod。kubelet收到删除请求后,执行标准的Pod优雅终止流程:

  1. 执行preStop生命周期钩子(如果配置了)

  2. 向容器发送SIGTERM信号

  3. 等待terminationGracePeriodSeconds(默认30秒)

  4. 如果Pod在宽限期内未终止,发送SIGKILL强制终止

Pod的默认终止时间为30秒,但如果Pod配置了terminationGracePeriodSeconds或preStop钩子,将覆盖默认值。

5.5 被驱逐Pod的重建

被驱逐的低优先级Pod会被其控制器(如Deployment、ReplicaSet、StatefulSet等)自动重新创建,并重新进入调度队列,寻找其他可用节点。如果集群资源持续紧张,重新调度的Pod可能仍无法运行,处于Pending状态。

5.6 提名Pod机制

当抢占发生后,高优先级Pod会被设置为“提名Pod”(Nominated Pod),其.status.nominatedNodeName字段会被更新为被抢占的目标节点名称。这意味着该Pod在后续的调度周期中会优先尝试调度到该节点。这种机制确保了抢占行为的一致性,防止多个调度周期之间的资源竞争。

六、抢占策略详解

6.1 默认策略:PreemptLowerPriority

Kubernetes社区默认的抢占策略是PreemptLowerPriority,即调度器会尝试从节点上驱逐优先级较低的Pod,以便为优先级更高的Pod腾出资源。这一策略的核心行为是:

  1. 首先尝试直接调度Pod

  2. 资源不足时模拟资源抢占

  3. 确定可抢占的低优先级Pod

  4. 执行驱逐操作

  5. 完成高优先级Pod的调度

6.2 禁止抢占:preemptionPolicy: Never

如果设置preemptionPolicy: Never,该PriorityClass的Pod将不会抢占其他Pod。这可能导致一个场景:当资源受限时,一个优先级较低的Pod可能先被调度,即使有更高优先级的Pod在等待。

使用Never策略的场景包括:

  • 非关键批处理任务:不希望影响其他业务Pod运行

  • 测试环境:避免抢占干扰测试结果

  • 低优先级基线负载:填充集群资源“空隙”,不主动抢占他人

6.3 高级抢占策略(ACK Scheduler扩展)

在阿里云ACK等企业级Kubernetes发行版中,通过ACK Scheduler组件支持以下增强的抢占策略:

策略说明
DefaultKubernetes社区默认抢占机制
ElasticQuota基于弹性配额树(ElasticQuotaTree)进行抢占,支持Gang调度场景。将整个Gang中未调度的Pod组合成“待抢占列表”,仅当列表中所有Pod都能通过资源抢占被调度时,整个Gang才会被处理
Auto优先使用ElasticQuota策略,在无法生效时自动回退到Default策略
None完全禁用抢占

七、源码深度解析

7.1 kube-scheduler调度框架概述

kube-scheduler是Kubernetes中的调度程序,负责从API Server获取待调度的Pod列表,并为它们找到最合适的运行节点。调度框架包含以下几个关键阶段:

text

调度框架核心阶段
├── Filter阶段(预选)
│   ├── PreFilter:初步过滤,轻量级检查
│   ├── Filter:复杂过滤条件
│   └── PostFilter:处理无合适节点的场景(触发抢占)
├── Score阶段(优选)
│   ├── PreScore:准备评分所需数据
│   ├── Score:计算各节点得分
│   └── NormalizeScore:得分规范化处理
├── Reserve阶段
│   ├── Reserve:检测并预留节点资源
│   └── Unreserve:逆序释放预留资源(回滚)
├── Permit阶段(准入)
│   └── 审批/拒绝/等待状态管理
└── Bind阶段
    ├── PreBind:绑定前准备工作
    ├── Bind:正式绑定操作
    └── PostBind:善后处理

7.2 抢占入口源码分析

抢占逻辑的入口位于调度器的主循环中。当通过正常调度流程无法找到合适节点后(主要是没有合适的节点通过预选阶段),系统会判断是否需要进行抢占调度。具体代码位于pkg/scheduler/scheduler.go文件中,核心方法是preempt。

go

// 抢占入口简化流程
func (sched *Scheduler) preempt(preemptor *v1.Pod, scheduleErr error) (string, error) {
    // 检查抢占特性是否启用
    if !util.PodPriorityEnabled() || sched.config.DisablePreemption {
        return "", nil
    }
    
    // 获取更新后的Pod信息
    preemptor, err := sched.config.PodPreemptor.GetUpdatedPod(preemptor)
    
    // 执行核心抢占逻辑
    node, victims, nominatedPodsToClear, err := 
        sched.config.Algorithm.Preempt(preemptor, sched.config.NodeLister, scheduleErr)
    
    // 如果找到可抢占节点
    if node != nil {
        // 更新Pod的NominatedNodeName
        sched.config.SchedulingQueue.UpdateNominatedPodForNode(preemptor, nodeName)
        sched.config.PodPreemptor.SetNominatedNodeName(preemptor, nodeName)
        
        // 驱逐受害者Pod
        for _, victim := range victims {
            sched.config.PodPreemptor.DeletePod(victim)
        }
    }
    
    // 清理过期的提名Pod信息
    for _, p := range nominatedPodsToClear {
        sched.config.PodPreemptor.RemoveNominatedNodeName(p)
    }
}

7.3 Preempt()核心算法

genericScheduler.Preempt()方法是抢占算法的核心实现,其主要步骤包括:

  1. 节点筛选:从所有节点中筛选出可能通过抢占容纳高优先级Pod的候选节点

  2. 模拟驱逐:对每个候选节点,模拟驱逐低优先级Pod(包括其依赖的Pod)

  3. PDB评估:计算驱逐操作会破坏多少PDB限制

  4. 节点选择:选择破坏PDB最少、受害者优先级总和最低的节点

  5. 返回结果:返回最佳节点、受害者Pod列表、以及需要清除提名状态的Pod

7.4 优先级队列实现

调度器维护了一个优先级队列(Priority Queue),其中包含多个子队列:

  • ActiveQ:存放可立即调度的Pod,按优先级排序

  • BackoffQ:存放调度失败后需要退避等待的Pod

  • UnschedulableQ:存放不可调度的Pod(如资源不足)

调度器会启动后台goroutine,定期将BackoffQ和UnschedulableQ中的Pod移回ActiveQ,以便在条件满足时重新尝试调度。

7.5 关键数据结构

go

// PriorityClass核心数据结构
type PriorityClass struct {
    metav1.TypeMeta
    metav1.ObjectMeta
    Value            int32
    GlobalDefault    bool
    Description      string
    PreemptionPolicy *PreemptionPolicy
}

// Pod中的优先级字段
type PodSpec struct {
    PriorityClassName string
    // 注:Priority值由准入控制器填充,不在Spec中定义
}

type PodStatus struct {
    NominatedNodeName string  // 提名节点
}

八、优先级与QoS的协同与区分

8.1 QoS分类回顾

Kubernetes中的QoS(服务质量)分为三类,基于Pod的CPU/内存requests和limits配置:

QoS类判定条件特点
Guaranteed所有容器的requests=limits(CPU和内存)最高稳定性,确保不因资源压力被杀
Burstable至少一个容器的requests < limits中等稳定性
BestEffort没有任何requests/limits最低稳定性,优先被驱逐

8.2 优先级与QoS的关系

在Pod驱逐决策中,优先级和QoS共同作用但层次不同:

kubelet节点压力驱逐(Node Pressure Eviction) 的驱逐顺序(从高到低):

  1. BestEffort且优先级最低的Pod

  2. Burstable且优先级最低的Pod

  3. Guaranteed且优先级最低的Pod

调度器抢占(Scheduler Preemption) 的驱逐决策主要基于优先级值,QoS类仅在优先级相同的情况下作为辅助参考。

二者的区别在于:

  • QoS决定了Pod在节点资源紧张时被kubelet主动驱逐的优先级

  • Priority决定了Pod在调度过程中被调度器被动抢占的优先级

九、PodDisruptionBudget(PDB)对抢占的限制

9.1 PDB的工作原理

PodDisruptionBudget是一种保护机制,用于限制在自愿性中断期间可以同时终止的Pod数量。在抢占场景中,当调度器试图驱逐Pod时,必须检查PDB约束。

PDB配置示例:

yaml

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-pdb
spec:
  minAvailable: 2  # 至少保持2个Pod可用
  selector:
    matchLabels:
      app: myapp

9.2 PDB对抢占的影响

调度器在模拟抢占时会:

  1. 检查PDB约束:如果驱逐某个Pod会导致违反PDB约束(例如,可用的Pod数量低于minAvailable),该Pod不会被选为受害者。

  2. 计算PDB破坏成本:在每个候选节点上,计算驱逐操作会破坏多少PDB限制,优先选择破坏最少的节点。

  3. 优先级权衡:在PDB破坏最少和受害者优先级最低之间进行权衡,做出最优决策。

十、优先级在kubelet驱逐中的应用

10.1 节点压力驱逐概述

除了调度器层面的抢占,kubelet还会在节点资源压力(如磁盘空间不足、内存耗尽)时主动驱逐Pod。在这种场景下,Pod优先级同样被纳入驱逐决策。

10.2 驱逐决策算法

kubelet的驱逐管理器(Eviction Manager)按照以下维度评估Pod的驱逐优先级:

  1. QoS类:BestEffort > Burstable > Guaranteed

  2. 优先级值:值越小越容易被驱逐

  3. Pod资源用量:相对于requests的过量使用程度

  4. PDB约束:保护关键应用的可用性

10.3 节点资源预留

为确保关键系统Pod不受节点压力影响,kubelet支持配置--system-reserved和--kube-reserved来预留资源。高优先级的系统Pod(如使用system-node-critical类)在这些预留资源中运行,可有效避免被节点压力驱逐。

十一、Cluster Autoscaler与优先级抢占的协同

11.1 两种扩容方式的对比

维度Cluster Autoscaler抢占(Preemption)
响应时间分钟级(节点创建+初始化)秒级(直接驱逐+调度)
成本影响增加节点,成本上升不增加节点,成本不变
适用场景持续性的资源不足临时的资源紧张或高峰突发
限制条件仅适用于云环境适用于所有环境

11.2 优先级自动扩缩容的最佳实践

结合Pod优先级与Cluster Autoscaler可以实现成本可控的自动扩缩容:

  1. 设置集群最大节点数:在Autoscaler配置中设置--max-size,控制成本上限。

  2. 配置优先级范围:关键工作负载使用高优先级,非关键工作负载使用低优先级或负优先级。

  3. 负优先级的妙用:为非关键批处理任务分配负优先级,Cluster Autoscaler不会因这些Pending的Pod而增加节点。

11.3 过度配置(Over-provisioning)策略

一个典型的应用模式是使用低优先级“占位Pod”来实现过度配置:

  1. 创建一个优先级值为负数的PriorityClass

  2. 部署占位Pod(如Pause Container)使用该优先级

  3. 占位Pod占用部分节点资源,但优先级极低

  4. 当真正的高优先级业务Pod需要资源时,调度器会自动抢占这些占位Pod

  5. 被抢占的占位Pod会触发Cluster Autoscaler扩容新节点

  6. 新节点加入后,占位Pod在新节点上重新运行

这种策略确保了集群始终保持一定的“缓冲容量”,能快速响应突发业务需求。

十二、生产实践与最佳实践

12.1 优先级分层设计

建议根据业务重要性设计多层级的优先级策略:

层级优先级值用途preemptionPolicy
系统关键层2,000,000,000+系统组件(etcd、apiserver等)PreemptLowerPriority
平台服务层1,000,000 - 2,000,000,000Ingress、Service Mesh、监控等PreemptLowerPriority
核心业务层100,000 - 1,000,000支付、订单、用户中心等PreemptLowerPriority
一般业务层10,000 - 100,000普通微服务PreemptLowerPriority
开发测试层1,000 - 10,000开发测试环境PreemptLowerPriority(或Never)
可抢占批处理负数 - 1,000CI/CD、数据处理、定时任务Never或低优先级
占位Pod负值过度配置占位Never

12.2 系统组件保护

对于需要绝对保护的DaemonSet(如日志采集、网络插件等),必须设置较高的优先级。但是,内置的system-cluster-critical和system-node-critical通常仅用于kube-system命名空间的系统组件,不应在业务命名空间中滥用。

12.3 避免的陷阱

陷阱1:globalDefault的误用

在生产集群中,避免在运行时添加globalDefault: true的PriorityClass。这可能导致大量未显式指定优先级的Pod被批量抢占,引发服务中断。

解决方案:

  • 在集群初始化时规划好默认优先级

  • 始终为生产工作负载显式指定priorityClassName

  • 如需修改globalDefault,采用分阶段灰度发布策略

陷阱2:无控制器的孤儿Pod

没有控制器管理(即ownerReferences为空)的Pod被抢占后不会被自动重建,可能导致服务永久性中断。

解决方案:

  • 始终使用Deployment、StatefulSet等控制器管理Pod

  • 避免直接创建裸露Pod

  • 使用Kubernetes原生的工作负载API

陷阱3:高优先级与Never策略的冲突

设置preemptionPolicy: Never的高优先级Pod可能会因为低优先级Pod已经占用资源而永远无法调度。

解决方案:

  • 谨慎使用Never策略

  • 仅在低优先级的“填隙”类Pod上使用

  • 关键Pod应保留默认的PreemptLowerPriority策略

陷阱4:抢占的时序依赖

当集群资源足够运行时,调度器可能会跳过基于优先级的评估,导致高优先级Pod在低优先级Pod之后被调度。

解决方案:

  • 通过资源请求限制确保资源紧张状态触发抢占

  • 使用Pod拓扑扩展约束精细化调度

  • 监控调度队列,及时发现异常

12.4 安全考虑

在一个并非所有用户都可信的集群中,恶意用户可以创建最高优先级的Pod,导致其他Pod被驱逐或无法被调度。管理员应使用ResourceQuota限制用户创建高优先级Pod。

ResourceQuota配置示例:

yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pod-priority-limit
spec:
  hard:
    pods: "100"
  scopeSelector:
    matchExpressions:
    - operator: In
      scopeName: PriorityClass
      values: ["high-priority", "critical-priority"]

12.5 多集群与命名空间级别的优先级规划

在多租户或多环境集群中,优先级规划需要更精细的设计:

  1. 租户隔离:通过ResourceQuota限制每个租户可使用的高优先级Pod数量

  2. 环境隔离:开发、测试、生产环境使用独立的PriorityClass名称空间(虽然PriorityClass本身是集群范围的,但可以在命名空间级别配合RBAC和Quota进行限制)

  3. 优先级命名规范:使用有意义的命名,如prod-critical、staging-default、dev-low

十三、故障排查与调试

13.1 查看PriorityClass

bash

# 列出所有PriorityClass
kubectl get priorityclass

# 查看具体PriorityClass详情
kubectl describe priorityclass high-priority

# 查看PriorityClass的完整YAML
kubectl get priorityclass high-priority -o yaml

13.2 检查Pod优先级配置

bash

# 查看所有Pod的优先级类名
kubectl get pods -A -o jsonpath='{.items[*].spec.priorityClassName}'

# 查看Pod的详细调度信息(包括优先级值)
kubectl describe pod <pod-name>

# 查看Pod的提名节点状态
kubectl get pod <pod-name> -o jsonpath='{.status.nominatedNodeName}'

13.3 查看调度器日志

bash

# 获取kube-scheduler Pod的日志
kubectl logs -n kube-system -l component=kube-scheduler

# 如果集群中有多个调度器实例,指定具体的Pod名称
kubectl logs -n kube-system kube-scheduler-<node-name>

调度器日志会包含抢占决策相关的信息,包括:

  • 哪些Pod触发了抢占

  • 哪些节点被评估为候选节点

  • 哪些受害者Pod被选中驱逐

  • 抢占失败的原因

13.4 查看集群事件

bash

# 查看集群中的所有事件(包括抢占事件)
kubectl get events -A --watch

# 查看特定命名空间的事件
kubectl get events -n <namespace>

# 按事件类型过滤
kubectl get events --field-selector type=Warning

抢占相关的事件类型包括:

  • Preempted:Pod被抢占

  • FailedScheduling:调度失败

  • Scheduled:成功调度

13.5 常见问题诊断

问题现象可能原因诊断命令解决方案
高优先级Pod长期Pending资源不足但无法抢占(可能因PDB限制)kubectl describe pod <pod>检查PDB配置,调整优先级或放宽PDB约束
低优先级Pod被频繁抢占优先级层次过于陡峭kubectl get events | grep Preempted调整优先级跨度,增加中间层级
优先级被“忽略”资源充足时不会触发抢占查看调度器日志了解抢占仅在资源不足时触发
系统组件被抢占优先级设置不当kubectl get priorityclass检查系统组件优先级,确保高于业务Pod
Pod频繁重启节点压力驱逐kubectl describe node检查节点资源使用情况,考虑扩容

十四、未来展望与演进方向

14.1 调度框架的持续演进

Kubernetes调度框架(Scheduling Framework)为调度器的可扩展性提供了坚实基础。未来,自定义调度插件将能够更精细地控制优先级和抢占行为,包括:

  • 自定义抢占策略插件

  • 更细粒度的节点资源预留

  • 跨集群的优先级协调

14.2 与弹性配额的结合

在云原生环境中,Priority与ElasticQuota(弹性配额)的结合将成为趋势。通过弹性配额树,可以在团队、命名空间之间动态分配资源,配合优先级实现更精细的资源管理。

14.3 机器学习驱动的智能调度

随着集群规模的增长和负载模式的复杂化,基于机器学习(ML)的智能调度正在兴起。通过分析历史负载模式,智能调度系统可以:

  • 预测资源需求,提前调整优先级

  • 动态优化抢占策略,减少对低优先级业务的冲击

  • 在成本和性能之间自动寻找最优平衡点

14.4 跨集群与联邦调度

在多集群、多云环境中,Pod优先级和抢占机制需要扩展到跨集群层面。联邦调度器将能够:

  • 在集群间动态迁移低优先级Pod

  • 根据全局优先级在集群层面进行资源分配

  • 实现跨云的优先级协调与资源调度

十五、总结

Pod优先级和抢占机制是Kubernetes调度生态中不可或缺的组成部分,它使集群从简单的“先到先得”调度模式,进化为能够识别业务重要性的智能调度系统。

核心要点回顾:

  1. PriorityClass是定义优先级的模板,支持全局默认值和抢占策略配置,是优先级调度的基础构件。

  2. 抢占流程包括模拟抢占、受害者选择、安全驱逐和重新调度四个阶段,每个阶段都有精密的算法支撑。

  3. PDB保护确保关键应用不会因抢占而完全不可用,在灵活性和稳定性之间取得平衡。

  4. 调度框架为优先级和抢占提供了可扩展的实现基础,未来的自定义插件将带来更多可能性。

  5. 生产实践要求合理规划优先级分层、谨慎使用globalDefault、始终使用控制器管理Pod,并配合ResourceQuota保障集群安全。

  6. 故障排查需要综合运用kubectl命令、调度器日志和集群事件分析,快速定位抢占失败的原因。

更多推荐