12.6 抢占、优先级与排队
一句话
抢占是 “用低优任务的命换高优任务的 SLO”:它必须配优先级、配额与可重跑三件套,否则只是把 “排队” 变成 “反复被杀再重试”,成本更高、体验更差。
抢占的四种动作
| 动作 | 做法 | 成本 | 适用 |
|---|---|---|---|
| 驱逐(Evict) | 杀掉低优 Pod,腾出资源 | 任务从头再来 | 无状态、可重跑 |
| 暂停(Pause/Freeze) | 冻结进程(cgroup freezer) | 占用磁盘/内存快照 | 短时抢占、可恢复 |
| 迁移(Migrate) | 活迁移到其他节点 | 网络与停机 | 有状态、支持迁移 |
| 降级(Throttle) | 限 CPU/内存,不驱逐 | 低优变慢 | 可容忍变慢的服务 |
K8s 的默认抢占是 驱逐:高优 Pod 进入 Pending 时,调度器找可驱逐的低优 Pod,删除后重排。
实测:不抢占 vs 抢占
模拟 8 个槽位、200 个时间片、低优任务持续占满、高优任务随机到达:
| 策略 | 高优未获资源次数 | 低优被抢占次数 |
|---|---|---|
| 不抢占 | 192 | 0 |
| 抢占 | 0 | 192 |
数字很直白:不抢占时高优任务几乎全部在等(192/200);抢占把它压到 0,代价是 192 次低优任务被踢——抢占不是 “减少总工作量”,而是 “重新分配等待”。
高优等待 ↓ 的代价 = 低优重跑次数 ↑
只有当 “高优等待的成本 > 低优重跑的成本” 时,抢占才划算
代码:优先级与抢占策略(K8s)
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata: {name: critical}
value: 1000000
preemptionPolicy: PreemptLowerPriority # 允许抢占;Never 则只排队
globalDefault: false
---
apiVersion: v1
kind: Pod
spec:
priorityClassName: critical
terminationGracePeriodSeconds: 10 # 被驱逐时给多少时间收尾
kubectl get events --field-selector reason=Preempted # 看谁被抢占、被谁抢占
kubectl describe pod <pending-pod> | grep -A3 "Preempt"
反模式
| 反模式 | 后果 |
|---|---|
| 没有 PriorityClass,全部同优先级 | 抢占无处发生,关键任务照样排队 |
| 给所有任务都设高优先级 | 等于没优先级,抢占变成互杀 |
| 低优任务不可重跑却被驱逐 | 数据丢失/任务失败,需要人工补偿 |
| 抢占不做 PDB 保护 | 一次性驱逐整个服务,可用性归零 |
不设 terminationGracePeriodSeconds | 进程被 SIGKILL,未落盘数据丢失 |
| 抢占后不重排 | 高优拿到了资源,低优永远排在后面 |
| 没有 “抢占次数” 监控 | 低优被反复踢,吞吐下降却无人知 |
三条铁律
🔑 抢占是 “转移等待”,不是 “消除等待”;先算两侧成本。
🔑 被抢占方必须可重跑/可恢复,否则抢占就是制造故障。
🔑 优先级要分层且稀缺:只有少数级别,才有区分度。
自检
Q1 为什么 “给所有任务都设高优先级” 等于没有优先级?
▸ 答案:因为抢占的前提是 “存在可被抢占的低优任务”。当所有任务同级时,调度器没有 “可牺牲” 的对象,抢占退化成普通排队——优先级只有在 “有低优可让路” 时才起作用。
Q2 幂等重跑与抢占是什么关系?
▸ 答案:抢占会让任务中途消失,重新调度后从头执行。若任务不幂等(如重复扣款、重复发消息),重跑就会产生副作用。所以 “可抢占” 的前提是 “任务可安全重跑”——要么幂等,要么有检查点/断点续传。
Q3 PDB(PodDisruptionBudget)在抢占里起什么作用?
▸ 答案:它限制 “自愿中断时一次能驱逐多少”,保证服务不至于全灭。抢占属于自愿中断,调度器会尊重 PDB;没有 PDB 时,一次抢占可能把整个 Deployment 的副本全踢掉,瞬间不可用。
Q4 为什么 “暂停(freeze)” 比 “驱逐” 更贵?
▸ 答案:因为暂停要保留进程的内存与状态:内存不能释放、快照要落盘(或留在内存),恢复时还要考虑 “暂停期间外部状态已变”。它适合 “秒级抢占、很快恢复” 的场景;长时间暂停会拖垮节点。
Q5 抢占会不会导致 “低优任务永远排不上”?
▸ 答案:会,如果没有额外保护。常见缓解:配额保证(低优也有最小份额)、抢占频率限制(同一任务短时间内不被反复抢占)、老化(等待越久优先级越高)。没有这些,低优任务会被高优 “持续挤出去”。
Q6 如何量化 “抢占是否值得”?
▸ 答案:算两笔账:高优等待的损失(SLO 违约、业务损失/分钟)× 等待时间,对比低优重跑的损失(重算成本、被浪费的进度)× 被抢占次数。前者更大就抢占,反之用排队 + 扩容更划算。
Q7 在线服务与离线任务混部时,抢占该怎么设?
▸ 答案:离线任务设低优先级 + 可抢占,在线服务设高优先级 + 不抢占;同时给离线任务最小配额(避免被饿死)与弹性借用(在线空闲时离线用满)。这就是 “混部” 的标准做法(见 39.1 成本模型与单位经济)。
关联
- 前置:12.1 资源调度的目标与抽象、12.5 公平调度算法 (MMF / DRF)
- 并列:12.2 单体调度 (Borg)、12.4 共享状态调度 (Omega)
- 下游:7.6 自愈、滚动更新与回滚、39.1 成本模型与单位经济