title: K8s 系列 | 第 20 天:PriorityClass 与抢占式调度机制
date: 2026-07-06 08:00:00
tags:
– Kubernetes
– K8s系列
– DevOps
– PriorityClass
– 调度
– 资源管理
– 生产运维
第 20/30 天
K8s 系列 | 第 20 天:PriorityClass 与抢占式调度机制
引言
在 Kubernetes 集群中,Pod 调度遵循默认的 FIFO(先进先出)顺序——谁先创建谁先被调度。但在真实生产环境中,并非所有 Pod 都具有相同的重要性:关键系统组件(如 CoreDNS、监控 Agent)必须优先运行,而批处理任务可以在资源空闲时再执行。如果集群资源紧张,低优先级的批处理任务不应该阻塞高优先级的业务 Pod。
Kubernetes 的 PriorityClass(优先级类) 和 抢占式调度(Preemption) 机制正是为解决这一问题而设计的。本文将从原理到实战,深入解析如何通过优先级管理保障关键业务 Pod 的调度稳定性。
一、核心概念
1.1 什么是 PriorityClass?
PriorityClass 是一个集群级别的资源对象(scheduling.k8s.io/v1 API 组),用于定义 Pod 的优先级值。优先级值是一个 32 位整数,值越高代表 Pod 越重要。当调度器无法为高优先级 Pod 找到可用节点时,会尝试抢占(Evict) 该节点上优先级较低的 Pod,腾出资源来运行高优 Pod。
📌 取值范围:优先级可以设置为 -2147483648 到 1000000000 之间的任何整数。Kubernetes 系统预留了较高和较低的范围给关键系统组件。
1.2 抢占式调度的工作原理
抢占式调度并非在每个调度周期都发生——它仅在以下条件同时满足时触发:
- 有一个待调度的 Pod(称为 preemptor,抢占者)
- 当前集群中没有节点能满足该 Pod 的资源请求
- 存在至少一个节点,如果能驱逐该节点上的部分低优先级 Pod,就能满足 preemptor 的需求
触发抢占后,调度器会执行以下步骤:
- 节点筛选:找出所有可能通过驱逐低优 Pod 来满足需求的节点
- 受害者选择:在每个候选节点上,按优先级从低到高选择要驱逐的 Pod
- 驱逐执行:向选中的受害者 Pod 发送优雅终止信号(SIGTERM)
- 资源释放:等待受害者 Pod 终止后,preemptor 被调度到该节点
# 一个简单的 priorityclass 示例
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "关键业务 Pod 使用的高优先级"
1.3 优先级与 QoS 的关系
PriorityClass 与 Kubernetes 的 QoS(服务质量)机制是两个独立的维度:
| 维度 | 机制 | 影响范围 |
|---|---|---|
| PriorityClass | 调度优先级 + 抢占能力 | 调度阶段决策 |
| QoS (Guaranteed/Burstable/BestEffort) | 节点资源不足时的驱逐顺序 | 运行阶段决策 |
当节点资源耗尽时,kubelet 会先驱逐 BestEffort Pod,再驱逐 Burstable Pod,最后驱逐 Guaranteed Pod。但如果结合 PriorityClass,优先级高的 Pod 即使 QoS 等级更低,也会在节点压力下比低优先级的 Guaranteed Pod 更晚被驱逐。
二、PriorityClass 的创建与管理
2.1 创建 PriorityClass 对象
PriorityClass 是集群级别的资源,不属于任何 Namespace。
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: production-critical
value: 1000000
globalDefault: false
description: "生产环境关键业务 Pod"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: production-normal
value: 500000
globalDefault: false
description: "生产环境普通业务 Pod"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: batch-jobs
value: 100000
globalDefault: false
description: "批处理任务,优先级最低"
创建命令:
# 创建上述三个 PriorityClass
kubectl apply -f priority-classes.yaml
# 查看集群中已有的 PriorityClass
kubectl get priorityclass
# 查看某个 PriorityClass 的详细信息
kubectl describe priorityclass production-critical
2.2 系统内置 PriorityClass
Kubernetes 自带两个系统 PriorityClass(在 1.14+ 版本中):
| 名称 | 值 | 用途 |
|---|---|---|
system-cluster-critical |
2000000000 | 集群关键组件(CoreDNS, metrics-server 等) |
system-node-critical |
2000001000 | 节点级关键组件(kubelet, container-runtime 等) |
这些值远高于用户自定义的 PriorityClass,确保系统组件永远不会被用户 Pod 抢占。
2.3 为 Pod 指定 PriorityClass
在 Pod 或 Deployment 的 spec 中通过 priorityClassName 字段引用:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
priorityClassName: production-critical # 引用 PriorityClass
containers:
- name: nginx
image: nginx:1.25
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
⚠️ 注意:如果 Pod 未指定
priorityClassName,则使用集群默认 PriorityClass(即globalDefault: true的那个)。如果没有设置默认 PriorityClass,则 Pod 的默认优先级为 0。
三、抢占式调度实战
3.1 模拟资源紧张场景
让我们创建一个实验来观察抢占行为。首先在一个 2 核节点上部署多个低优先级 Pod,填满资源:
# 创建低优先级 PriorityClass
cat <<EOF | kubectl apply -f -
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: low-priority
value: 1000
globalDefault: false
description: "低优先级 Pod,可被抢占"
EOF
# 部署 3 个低优先级 Pod,每个请求 800m CPU
# 在一个 2 核节点上,3 个 Pod 请求 2400m CPU,会超分但可能部分 pending
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: low-priority-workload
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: low-priority
template:
metadata:
labels:
app: low-priority
spec:
priorityClassName: low-priority
containers:
- name: stress
image: polinux/stress
command: ["stress"]
args: ["--cpu", "1", "--vm", "1", "--vm-bytes", "128M", "--timeout", "600s"]
resources:
requests:
cpu: "800m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
EOF
3.2 部署高优先级 Pod 触发抢占
# 创建高优先级 PriorityClass
cat <<EOF | kubectl apply -f -
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "高优先级 Pod,可抢占低优 Pod"
EOF
# 部署一个高优先级 Pod,需要 1.5 核 CPU
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: high-priority-preemptor
namespace: default
spec:
priorityClassName: high-priority
containers:
- name: critical-app
image: nginx:1.25
resources:
requests:
cpu: "1500m"
memory: "256Mi"
limits:
cpu: "2"
memory: "512Mi"
EOF
3.3 观察抢占事件
# 查看调度器日志,观察抢占事件
kubectl logs -n kube-system $(kubectl get pods -n kube-system -l component=kube-scheduler -o name | head -1) | grep -i preempt
# 查看被抢占 Pod 的事件
kubectl get events --sort-by='.lastTimestamp' | grep -i preempt
# 查看高优先级 Pod 的调度详情
kubectl describe pod high-priority-preemptor
# 查看被驱逐的 Pod 状态(应该处于 Terminating)
kubectl get pods -o wide | grep low-priority
输出示例中你会看到类似这样的事件:
2m33s Normal Preempted pod/low-priority-workload-xxx Preempted by kube-system/kube-scheduler-xxx
2m33s Normal Preempting pod/high-priority-preemptor preempting 1 pods on node worker-1
3.4 抢占行为的关键细节
✅ 非抢占式等待:从 Kubernetes 1.15 开始,Pod 可以设置 spec.preemptionPolicy: Never,表示该 Pod 不会抢占其他 Pod,而是排队等待资源释放。适用于批处理任务、CI/CD Job 等不需要紧急运行的 Pod。
apiVersion: v1
kind: Pod
metadata:
name: non-preempting-pod
spec:
priorityClassName: high-priority
preemptionPolicy: Never # 不抢占,仅用于调度时的优先级排序
containers:
- name: app
image: nginx:1.25
resources:
requests:
cpu: "1"
memory: "512Mi"
✅ 优雅终止:被抢占的 Pod 会收到 SIGTERM 信号,并等待 terminationGracePeriodSeconds(默认 30s)完成优雅关闭。如果超时未退出,则被 SIGKILL 强制终止。
✅ PDB 尊重:如果被抢占的 Pod 有 PodDisruptionBudget(PDB),调度器会尊重 PDB 的限制。例如,如果 PDB 要求某个应用至少有 2 个副本运行,那么抢占过程中调度器不会同时驱逐超过 PDB 允许数量的 Pod。
四、PriorityClass 的最佳实践
4.1 生产环境推荐的优先级分层
优先级范围 | 用途示例
─────────────────┼──────────────────────────────
1000000000 | 系统关键组件(系统保留)
900000000 | 集群基础设施(ingress-controller, cert-manager, monitoring)
800000000 | 在线关键业务(用户请求路径上的服务)
500000000 | 在线普通业务(非关键路径的后端服务)
100000000 | 离线批处理(日志分析、数据清洗)
50000000 | 开发测试 Pod
0 | 默认优先级
4.2 实施建议
-
创建统一的 PriorityClass 清单:作为集群初始化的一部分,在同一份 YAML 中定义所有优先级层,并纳入 GitOps 管理。
-
避免过多层级:3~5 个层级通常足够。过多层级会增加调度复杂性,且实际效果不明显。
-
为关键命名空间设置默认 PriorityClass:
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: production-default
value: 500000
globalDefault: false # 不要设置为全局默认,否则所有未指定优先级的 Pod 都会使用这个值
description: "production 命名空间的默认优先级"
然后通过 Admission Controller(如 Kyverno 或 OPA/Gatekeeper)自动注入:
# Kyverno 策略:为 production 命名空间的 Pod 注入 PriorityClass
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: inject-priority-class
spec:
rules:
- name: assign-production-priority
match:
resources:
kinds:
- Pod
namespaces:
- production
mutate:
patchStrategicMerge:
spec:
priorityClassName: production-default
- 监控抢占事件:频繁的抢占说明集群资源规划不合理,应考虑扩容或优化 Pod 资源请求。
# 监控抢占事件频率
kubectl get events --all-namespaces --field-selector reason=Preempted --watch
4.3 常见陷阱与注意事项
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 优先级反转 | 低优先级的 Critical Pod 被高优先级的非 Critical Pod 抢占 | 仔细规划优先级数值,留足安全间距 |
| 抢占风暴 | 抢占一个 Pod 后引发连环抢占 | 使用 PDB 限制最小可用副本数 |
| 优先级膨胀 | 每个团队都要求最高优先级 | 建立审批流程,通过 Namespace quota 间接控制 |
| 未设置优先级 | 所有 Pod 默认优先级为 0 | 设置 globalDefault PriorityClass 或通过准入控制器注入 |
五、PriorityClass 与其它调度特性的协同
5.1 与 PodDisruptionBudget 的交互
PDB 限制了自愿中断(包括抢占)时允许的最大不可用副本数:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-app-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: web
如果某个节点上有 3 个 web-app 副本,PDB 要求至少 2 个可用,则抢占器一次最多只能驱逐 1 个 web-app Pod。调度器会循环尝试,直到满足资源需求。
5.2 与 Cluster Autoscaler 的协作
当 Cluster Autoscaler 与 PriorityClass 配合使用时,会产生更智能的扩容决策:
- 如果高优先级 Pod 因资源不足无法调度,但抢占失败(没有足够低优先级 Pod 可抢占),Cluster Autoscaler 会触发节点扩容
- 低优先级 Pod 阻塞调度时,Cluster Autoscaler 可能不会立即扩容,因为低优 Pod 的调度优先级较低
建议在生产环境中将 PriorityClass 与 Cluster Autoscaler 配合使用,实现”高优保障 + 弹性扩容”的完美组合。
总结
本文深入解析了 Kubernetes PriorityClass 与抢占式调度机制:
| 要点 | 说明 |
|---|---|
| PriorityClass | 定义 Pod 调度优先级的集群资源对象 |
| 抢占式调度 | 高优先 Pod 通过驱逐低优先 Pod 获取资源 |
| preemptionPolicy | 设为 Never 可使 Pod 不抢占,仅用于优先级排序 |
| PDB 协同 | PDB 限制抢占时对应用的破坏程度 |
| 最佳实践 | 3~5 个优先级层 + 统一管理 + 抢占事件监控 |
合理使用 PriorityClass 可以有效保障关键业务 Pod 的调度优先级,避免”低优先级任务占着资源不让”的尴尬局面。但也要记住:优先级机制是”冷启动”的权宜之计,持续的资源规划和容量管理才是根本解决之道。
下期预告
第 21 天我们将探讨 资源配额与 LimitRange:多租户资源管控,看如何通过 ResourceQuota 和 LimitRange 防止单个 Namespace 耗尽集群资源,实现多团队环境下的公平分配。
系列目录
- K8s 系列 | 第 1 天:Kubernetes 是什么?核心概念与架构全景解析
- K8s 系列 | 第 2 天:手把手搭建你的第一个 K8s 集群(kubeadm 实战)
- K8s 系列 | 第 3 天:Pod 详解:K8s 最小的调度单元与生命周期管理
- K8s 系列 | 第 4 天:Deployment 与 ReplicaSet:声明式应用管理
- K8s 系列 | 第 5 天:Service 与网络基础:ClusterIP、NodePort、LoadBalancer 详解
- K8s 系列 | 第 6 天:Namespace 与资源配额:多租户隔离基础
- K8s 系列 | 第 7 天:ConfigMap 与 Secret:配置管理最佳实践
- K8s 系列 | 第 8 天:Volume 与 PersistentVolume:存储抽象层的核心机制
- K8s 系列 | 第 9 天:StorageClass 与动态存储供给实战
- K8s 系列 | 第 10 天:StatefulSet:有状态应用的部署与管理
- K8s 系列 | 第 11 天:Ingress 与 Ingress Controller:外部流量接入全攻略
- K8s 系列 | 第 12 天:NetworkPolicy:K8s 网络安全策略与微隔离
- K8s 系列 | 第 13 天:Headless Service 与服务发现机制深度解析
- K8s 系列 | 第 14 天:CSI 存储插件与生产存储选型指南
- K8s 系列 | 第 15 天:污点与容忍度:掌控 Pod 调度
- K8s 系列 | 第 16 天:Node Affinity 与 Pod Affinity:精细化调度策略实战
- K8s 系列 | 第 17 天:HPA 水平自动扩缩:基于 CPU/内存/自定义指标的弹性伸缩
- K8s 系列 | 第 18 天:VPA 与 Cluster Autoscaler:资源与集群层的自动扩缩
- K8s 系列 | 第 19 天:Job 与 CronJob:批处理与定时任务实战
- K8s 系列 | 第 20 天:PriorityClass 与抢占式调度机制 ← 本篇
- K8s 系列 | 第 21 天:资源配额与 LimitRange:多租户资源管控(敬请期待)















暂无评论内容