3. 资源模型与抽象:资源定义、表示方法与调度策略
各位同学,今天我们来聊聊算力调度器里最基础、也最容易被忽视的部分——资源模型与抽象。说实话,我见过不少调度器项目,功能做得花里胡哨,但底层资源模型没设计好,最后全崩了。嗯,咱们从根上把它理清楚。
3.1 资源定义:CPU、内存、GPU、网络带宽
先问个问题:什么是资源?在调度器眼里,资源就是能被分配、被计量、被回收的东西。我个人习惯把资源分成两类:可压缩资源和不可压缩资源。
- CPU:可压缩资源。你给一个任务分配1个核,它用不完,别人还能用。超卖风险低。
- 内存:不可压缩资源。分配了8GB就是8GB,用超了直接OOM Kill。我在项目中遇到过,内存超卖导致整个节点雪崩,那叫一个惨。
- GPU:特殊资源。显存不可压缩,算力可以一定程度共享。但要注意,GPU的调度粒度很粗,动不动就是整卡分配,浪费严重。
- 网络带宽:软性资源。很难精确控制,一般用流量整形或QoS来保证。
核心原则:不可压缩资源必须精确记账,可压缩资源可以适当超卖,但要有兜底机制。
3.2 资源表示方法:向量模型 vs 位图模型
资源定义清楚了,怎么表示它?说白了,调度器需要一种数据结构来描述“当前节点还剩多少资源”。我常用的有两种:向量模型和位图模型。
3.2.1 向量模型
把资源看成多维向量。比如一个节点有CPU、内存、GPU三种资源,那它的资源状态就是一个三维向量:[cpu_cores, mem_gb, gpu_count]。
// 向量模型示例
type ResourceVector struct {
CPU float64 // 核心数
Memory float64 // GB
GPU int // 张数
}
// 节点剩余资源
nodeFree := ResourceVector{CPU: 8.0, Memory: 32.0, GPU: 2}
// 任务需求
taskReq := ResourceVector{CPU: 2.0, Memory: 8.0, GPU: 1}
// 能否调度?逐维比较
if nodeFree.CPU >= taskReq.CPU &&
nodeFree.Memory >= taskReq.Memory &&
nodeFree.GPU >= taskReq.GPU {
// 可以调度
}
向量模型的好处是直观、计算快。但有个坑——它假设资源之间是独立的。你想想看,实际场景里CPU和内存是联动的,一个任务要2核CPU,通常也得配4GB内存。我曾经踩过这个坑,只检查单维资源,结果调度出去的任务因为内存不足反复被kill。
我的建议:向量模型适合做快速过滤,但最终决策要结合资源亲和性策略。
3.2.2 位图模型
位图模型更适合细粒度资源,比如CPU核心的分配。每个核心用一个bit表示,0表示空闲,1表示已分配。
// 位图模型示例(16核CPU)
type CPUBitmap uint16
// 初始状态:全部空闲
var freeCPUs CPUBitmap = 0b0000000000000000
// 分配第3核(从0开始计数)
func (b *CPUBitmap) Allocate(core int) {
*b |= 1 << core
}
// 检查第5核是否空闲
func (b *CPUBitmap) IsFree(core int) bool {
return (*b & (1 << core)) == 0
}
位图模型在CPU调度里特别有用。我记得在做一个实时调度器时,需要保证关键任务独占某些核心,位图模型让分配和回收都变成了O(1)操作,性能极好。
注意:位图模型不适合内存这种连续大块资源。你总不能按字节去分配内存吧?那调度器自己就先炸了。
3.3 资源预留与超卖
好,资源怎么表示我们搞清楚了。接下来是调度器设计的核心难题:预留和超卖怎么平衡?
3.3.1 资源预留
预留就是“这块资源我预定了,别人别碰”。典型场景:数据库集群需要保证最低资源。
- 静态预留:启动时就固定好,比如给Kubernetes系统组件预留2核4GB。简单粗暴,但浪费。
- 动态预留:根据负载动态调整。我做过一个方案,用水位线触发预留调整,效果不错,但实现复杂。
3.3.2 资源超卖
超卖就是“账面上有100核,实际敢卖120核”。为什么敢?因为大部分任务用不满。
但超卖是把双刃剑。我曾经在一个离线计算集群里,把超卖比设到了1:5,结果突发流量一来,所有节点内存爆满,任务大面积失败。那次事故让我学到一个教训:超卖必须有限度,而且要有驱逐机制。
超卖安全公式:
可超卖量 = (节点总资源 - 系统预留) × 超卖系数
超卖系数建议:CPU 1.5~2.0,内存 1.0~1.2(几乎不超卖)
3.4 知识体系总览
下面这张图是我自己总结的资源模型核心逻辑,画成SVG方便大家理解。
3.5 避坑指南
最后,分享几个我亲身踩过的坑:
- 不要迷信单一模型:向量模型和位图模型各有适用场景。我见过有人非要用位图管理内存,结果代码写得跟天书一样,性能还差。
- 超卖一定要有上限:我曾经把超卖比设到1:3,想着反正任务用不满。结果某个任务突然峰值,直接把节点打爆了。现在我的原则是:CPU超卖不超过2倍,内存不超过1.2倍。
- 预留资源要动态调整:静态预留太浪费,动态预留又太复杂。折中方案是用水位线触发调整,比如内存使用率超过80%时,自动释放部分预留。
一句话总结:资源模型是调度器的地基。地基没打好,上面盖多高的楼都得塌。向量模型做快速过滤,位图模型做细粒度分配,预留和超卖要平衡好,别贪心。
好了,这一章的内容就到这里。资源模型这块,大家有什么问题可以随时交流。记住,调度器的核心就是“分好资源”,分不好,再好的算法也是白搭。
想要更多资料,联系可可