【Kubernetes】(二十一)PriorityClass - 详解
目录
一、为什么需要 PriorityClass?先看一个 “资源危机” 场景
二、PriorityClass 核心概念:3 个关键知识点要理清
1. PriorityClass 是 “集群级资源”,不是 “命名空间级”
3. Pod 通过 “priorityClassName” 关联 PriorityClass
三、PriorityClass 实战:从创建到验证,3 步搞定核心服务保护
1. 第一步:创建 3 个不同优先级的 PriorityClass
方法 2:直接通过 kubectl debug 触发驱逐(更快速)
四、PriorityClass 进阶:2 个关键配置,避免 “优先级滥用”
1. 用 “preemptionPolicy” 控制 “抢占行为”
五、总结: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” 相关的信息,大部分问题都能通过日志定位到原因(如资源不足、抢占被禁止等)。
浙公网安备 33010602011771号