【Kubernetes】(二十一)PriorityClass - 详解

目录

一、为什么需要 PriorityClass?先看一个 “资源危机” 场景

二、PriorityClass 核心概念:3 个关键知识点要理清

1. PriorityClass 是 “集群级资源”,不是 “命名空间级”

2. 优先级用 “value 值” 表示,范围有明确限制

3. Pod 通过 “priorityClassName” 关联 PriorityClass

三、PriorityClass 实战:从创建到验证,3 步搞定核心服务保护

1. 第一步:创建 3 个不同优先级的 PriorityClass

2. 第二步:部署不同优先级的 Pod,模拟业务场景

3. 第三步:模拟资源不足,验证驱逐逻辑

方法 1:通过 “资源压力 Pod” 耗尽节点内存

方法 2:直接通过 kubectl debug 触发驱逐(更快速)

观察驱逐结果

四、PriorityClass 进阶:2 个关键配置,避免 “优先级滥用”

1. 用 “preemptionPolicy” 控制 “抢占行为”

2. 警惕 “优先级饥饿” 问题,合理规划 value 值

五、总结:PriorityClass 是 “集群稳定性的基石”


在 Kubernetes(K8s)集群中,“资源竞争” 是常态 —— 当节点资源(CPU、内存)不足时,kubelet 会按照默认策略驱逐 Pod 以释放资源。但默认策略往往 “一刀切”,可能误删核心服务(如数据库、API 网关),导致业务中断。而PriorityClass的出现,正是为了解决这一问题:它通过给 Pod 定义 “优先级权重”,让 K8s 在调度和驱逐时 “心中有数”,优先保障高优先级服务的稳定,成为集群资源管理的 “核心调度员”。今天我们就从概念、原理到实战,全面拆解 PriorityClass 的作用与用法。

一、为什么需要 PriorityClass?先看一个 “资源危机” 场景

假设你的 K8s 集群中有两类服务:

  • 核心服务:订单数据库(mysql-order),一旦中断会导致交易失败,直接影响收入;
  • 非核心服务:日志采集(log-collector),临时中断仅影响日志分析,不影响业务流程。

某天集群节点内存使用率飙升至 95%,kubelet 触发 “资源不足驱逐” 逻辑。如果没有优先级配置,kubelet 可能随机驱逐 Pod—— 万一误删了 mysql-order,将造成严重业务损失;而理想状态是:优先驱逐 log-collector 这类非核心 Pod,保留 mysql-order 的运行。

这就是 PriorityClass 的核心价值:给 Pod 赋予 “优先级身份”,让 K8s 在两个关键阶段做出合理决策:

  • 调度阶段:高优先级 Pod 优先被调度到资源充足的节点,避免因资源不足等待;
  • 驱逐阶段:资源不足时,低优先级 Pod 先被驱逐,为高优先级 Pod “腾出资源”。

没有 PriorityClass 时,所有 Pod 默认优先级为 “0”,相当于 “人人平等”,无法体现服务的核心程度;有了 PriorityClass,我们可以给核心服务设置 “高权重”,非核心服务设置 “低权重”,让资源分配更贴合业务优先级。

二、PriorityClass 核心概念:3 个关键知识点要理清

在使用 PriorityClass 前,需要先理解它的本质、优先级范围和关联逻辑,避免配置时踩坑。

1. PriorityClass 是 “集群级资源”,不是 “命名空间级”

PriorityClass 不属于任何命名空间,而是集群全局可见的资源 —— 这意味着一旦创建,集群内所有命名空间的 Pod 都可以引用它。这样设计的原因很简单:核心服务可能分布在不同命名空间(如订单服务在 order-ns,支付服务在 pay-ns),需要统一的优先级标准来管理。

2. 优先级用 “value 值” 表示,范围有明确限制

PriorityClass 通过value字段定义优先级权重,value 值越大,优先级越高,但 value 的取值范围有严格规定:

  • 用户可配置范围1 ~ 1000000000(10 亿),这个区间的优先级由用户自定义,用于区分不同业务的重要程度;
  • 系统保留范围1000000001 ~ 10000000000(10 亿 + 1 ~ 100 亿),属于 K8s 系统组件专用(如 kube-proxy、coredns),用户不能使用,避免干扰系统运行;
  • 默认优先级:未引用 PriorityClass 的 Pod,默认优先级为0,低于所有用户配置的优先级。

举个例子:我们可以给核心服务设置value: 100000,非核心服务设置value: 1000,这样在资源竞争时,100000 的 Pod 会被优先保障。

3. Pod 通过 “priorityClassName” 关联 PriorityClass

PriorityClass 本身不直接作用于 Pod,而是需要 Pod 通过spec.priorityClassName字段 “主动引用”—— 就像给 Pod “佩戴优先级徽章”。例如:

# 核心服务Pod引用高优先级PriorityClass
apiVersion: v1
kind: Pod
metadata:
  name: mysql-order
  namespace: order-ns
spec:
  containers:
  - name: mysql
    image: mysql:8.0
    resources:
      requests:
        cpu: "1"
        memory: "2Gi"
  priorityClassName: high-priority  # 引用名为“high-priority”的PriorityClass

当 Pod 引用 PriorityClass 后,K8s 会自动将 PriorityClass 的value值注入到 Pod 的spec.priority字段(用户无需手动设置),后续调度和驱逐逻辑会基于这个priority值判断。

三、PriorityClass 实战:从创建到验证,3 步搞定核心服务保护

理解概念后,我们通过 “创建 PriorityClass→部署不同优先级 Pod→模拟资源不足验证驱逐逻辑” 的流程,实战 PriorityClass 的用法。

1. 第一步:创建 3 个不同优先级的 PriorityClass

首先创建 3 个 PriorityClass,分别对应 “核心服务”“一般服务”“非核心服务”,覆盖常见业务场景:

# priority-classes.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority  # 核心服务优先级
value: 100000  # 高权重
globalDefault: false  # 是否作为所有未引用Pod的默认优先级(全局只能有一个true)
description: "用于核心服务,如数据库、API网关,资源不足时最后被驱逐"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: medium-priority  # 一般服务优先级
value: 10000  # 中权重
globalDefault: false
description: "用于一般业务服务,如用户管理、商品查询,优先级中等"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: low-priority  # 非核心服务优先级
value: 1000  # 低权重
globalDefault: false
description: "用于非核心服务,如日志采集、监控代理,资源不足时优先被驱逐"

应用配置到集群:

kubectl apply -f priority-classes.yaml

验证创建结果:

kubectl get priorityclasses.scheduling.k8s.io  # 可简写为kubectl get pc
# 示例输出:
# NAME               VALUE        GLOBAL-DEFAULT   AGE
# high-priority      100000       false            2m
# medium-priority    10000        false            2m
# low-priority       1000         false            2m

这里有个关键参数globalDefault: false:如果设置为true,所有未引用priorityClassName的 Pod 会默认使用该 PriorityClass 的优先级。注意:集群中只能有一个 PriorityClass 的 globalDefault 为 true,否则会冲突。

2. 第二步:部署不同优先级的 Pod,模拟业务场景

接下来创建 3 个 Pod,分别引用上面的 3 个 PriorityClass,模拟核心、一般、非核心服务:

# priority-pods.yaml
# 1. 核心服务:订单数据库(引用high-priority)
apiVersion: v1
kind: Pod
metadata:
  name: core-mysql
  namespace: default
spec:
  containers:
  - name: mysql
    image: mysql:8.0
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "123456"
    resources:
      requests:  # 声明资源需求,避免调度到资源不足的节点
        cpu: "500m"  # 0.5核CPU
        memory: "1Gi"  # 1GB内存
      limits:
        cpu: "1"
        memory: "2Gi"
  priorityClassName: high-priority
---
# 2. 一般服务:用户API(引用medium-priority)
apiVersion: v1
kind: Pod
metadata:
  name: normal-user-api
  namespace: default
spec:
  containers:
  - name: user-api
    image: nginx:alpine  # 用nginx模拟API服务
    resources:
      requests:
        cpu: "300m"
        memory: "512Mi"
      limits:
        cpu: "500m"
        memory: "1Gi"
  priorityClassName: medium-priority
---
# 3. 非核心服务:日志采集(引用low-priority)
apiVersion: v1
kind: Pod
metadata:
  name: noncore-log-collector
  namespace: default
spec:
  containers:
  - name: log-collector
    image: busybox:1.35  # 用busybox模拟日志采集
    command: ["sleep", "3600"]  # 让Pod持续运行
    resources:
      requests:
        cpu: "200m"
        memory: "256Mi"
      limits:
        cpu: "300m"
        memory: "512Mi"
  priorityClassName: low-priority

应用配置并查看 Pod 状态:

kubectl apply -f priority-pods.yaml
# 查看Pod状态,确保都处于Running
kubectl get pods -o wide
# 查看Pod的优先级(通过-o jsonpath提取spec.priority字段)
kubectl get pod core-mysql -o jsonpath='{.spec.priority}{"\n"}'  # 输出100000
kubectl get pod normal-user-api -o jsonpath='{.spec.priority}{"\n"}'  # 输出10000
kubectl get pod noncore-log-collector -o jsonpath='{.spec.priority}{"\n"}'  # 输出1000

可以看到,K8s 已自动将 PriorityClass 的value注入到 Pod 的spec.priority字段,优先级配置生效。

3. 第三步:模拟资源不足,验证驱逐逻辑

为了验证 “低优先级 Pod 先被驱逐”,我们需要模拟节点资源不足的场景。这里有两种常用方法:

方法 1:通过 “资源压力 Pod” 耗尽节点内存

创建一个 “内存吞噬者” Pod,故意申请大量内存,触发节点资源不足:

# stress-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: memory-stress
  namespace: default
spec:
  containers:
  - name: stress
    image: polinux/stress  # 专门用于压力测试的镜像
    command: ["stress", "--vm", "1", "--vm-bytes", "3Gi"]  # 申请3GB内存
    resources:
      limits:
        memory: "3Gi"  # 限制最大内存3GB
  priorityClassName: low-priority  # 低优先级,避免影响核心Pod

应用后,观察节点资源和 Pod 状态:

# 1. 应用压力Pod
kubectl apply -f stress-pod.yaml
# 2. 查看节点内存使用率(替换node-name为实际节点名)
kubectl describe node  | grep "Memory Usage"
# 当内存使用率超过阈值(默认90%左右),kubelet开始驱逐Pod
# 3. 持续观察Pod状态,看哪个先被驱逐
watch kubectl get pods

方法 2:直接通过 kubectl debug 触发驱逐(更快速)

如果觉得压力测试麻烦,可以通过kubectl debug在节点上运行临时容器,手动消耗内存:

# 在目标节点上运行临时容器,执行内存消耗命令
kubectl debug node/ -it --image=busybox:1.35 -- /bin/sh
# 在临时容器中执行:通过dd命令消耗内存(1GB=1024*1024*1024字节)
dd if=/dev/zero of=/dev/shm/test bs=1M count=2048  # 消耗2GB内存
观察驱逐结果

无论哪种方法,最终都会看到:

  • 首先被驱逐的是noncore-log-collector(low-priority,优先级 1000);
  • 如果资源仍不足, next 被驱逐的是memory-stress(同样 low-priority);
  • normal-user-api(medium-priority,10000)和core-mysql(high-priority,100000)始终保持 Running,直到所有低优先级 Pod 被驱逐完毕。

此时查看被驱逐 Pod 的事件日志,可以确认驱逐原因是 “优先级低”:

kubectl describe pod noncore-log-collector | grep "Reason"
# 输出类似:Reason: Evicted,Message: Pod was evicted due to low priority to free up resources for higher priority pods

四、PriorityClass 进阶:2 个关键配置,避免 “优先级滥用”

在实际使用中,仅配置优先级还不够,还需要通过两个进阶配置,避免 “优先级滥用” 导致的问题。

1. 用 “preemptionPolicy” 控制 “抢占行为”

默认情况下,当高优先级 Pod 调度时,如果所有节点资源都不足,K8s 会触发 “抢占”(Preemption)逻辑:删除节点上低优先级 Pod,为高优先级 Pod 腾出资源。但有些场景下,我们不希望高优先级 Pod “强制抢占”(比如非核心服务正在处理关键任务,临时中断会有风险),这时可以通过preemptionPolicy字段控制抢占行为。

preemptionPolicy有两个可选值:

  • PreemptLowerPriority(默认):允许抢占低优先级 Pod;
  • Never:禁止抢占,高优先级 Pod 会一直处于 “Pending” 状态,直到节点有空闲资源。

示例:给 “一般服务” 配置禁止抢占,避免干扰非核心服务的关键任务:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: medium-priority-no-preempt
value: 10000
preemptionPolicy: Never  # 禁止抢占
description: "一般服务,不允许抢占低优先级Pod,避免临时中断非核心任务"

当引用该 PriorityClass 的 Pod 调度时,如果节点资源不足,不会驱逐低优先级 Pod,而是等待资源释放,适合对 “非核心服务友好” 的场景。

2. 警惕 “优先级饥饿” 问题,合理规划 value 值

“优先级饥饿” 是指:高优先级 Pod 长期占用资源,导致低优先级 Pod 永远无法被调度(一直 Pending)。例如:给核心服务设置value: 100000,且核心服务长期满负载运行,低优先级的日志采集 Pod 可能永远处于 Pending 状态。

为避免这一问题,建议:

  • 合理划分优先级层级:不要只分 “高 / 低” 两级,可细分为 “核心(100000)→ 重要(50000)→ 一般(10000)→ 非核心(1000)”,避免某一层级长期占用资源;
  • 控制高优先级 Pod 的资源请求:核心服务的resources.requests不要过大,避免 “一个核心 Pod 占满节点资源”;
  • 定期清理无用高优先级 Pod:避免测试环境的高优先级 Pod 长期留在集群,占用资源。

五、总结:PriorityClass 是 “集群稳定性的基石”

PriorityClass 看似简单,却是 K8s 集群 “资源治理” 的关键工具,它的核心价值可以总结为 3 点:

  • 业务对齐:将 “服务重要程度” 转化为 K8s 可识别的 “优先级权重”,让资源分配贴合业务需求;
  • 风险可控:资源不足时,优先保护核心服务,减少业务中断风险;
  • 灵活适配:通过preemptionPolicy等配置,适配不同场景的资源调度需求。

在实际落地时,建议:

  • 先梳理服务优先级:和业务团队一起明确 “核心 / 一般 / 非核心” 服务清单,避免拍脑袋配置;
  • 从小范围试点:先在非核心服务集群验证 PriorityClass 的驱逐逻辑,再推广到生产核心集群;
  • 结合资源限制使用:PriorityClass 配合resources.limits/requests,既能保障核心服务,又避免资源浪费。

如果你还没在集群中配置 PriorityClass,建议从 “核心数据库、API 网关” 这类关键服务开始,给它们装上 “优先级引擎”,让集群在资源竞争时 “不再盲目”,真正做到 “核心服务不中断,非核心服务可牺牲”。

最后,如果你在配置中遇到问题(比如 Pod 引用 PriorityClass 后仍 Pending、驱逐顺序不符合预期),可以通过kubectl describe pod <pod-name>查看事件日志,重点关注 “Scheduler” 相关的信息,大部分问题都能通过日志定位到原因(如资源不足、抢占被禁止等)。

posted @ 2025-11-04 11:22  ycfenxi  阅读(0)  评论(0)    收藏  举报