资源管理基础:CPU、GPU、内存、存储等资源抽象与度量
在多租户AI训练平台中,资源管理是所有调度策略的物理基础。如果对资源的抽象不准确、度量口径不一致,后续任何高级调度算法都如同建立在流沙之上。本章聚焦于CPU、GPU、内存、存储四类核心资源的抽象模型与度量方法,帮助你在设计调度器之前,先建立一套可量化、可比较、可预测的资源视图。
3.1 资源抽象:从物理设备到调度单元
调度器不直接操作物理设备,而是操作资源的抽象表示。Kubernetes 将资源分为两大类:可压缩资源(CPU)与不可压缩资源(内存、GPU显存、存储)。可压缩资源可以按时间片共享,超额分配只会导致性能下降;不可压缩资源一旦耗尽,任务将直接失败或被杀掉。
对于AI训练任务,资源抽象需要额外关注拓扑关联性。GPU与CPU之间的PCIe链路、GPU之间的NVLink/NVSwitch互联、以及NUMA节点亲和性,都会显著影响分布式训练性能。因此,资源抽象不能只是简单的“数量”抽象,还需要包含拓扑标签。
| 资源类型 | 抽象单位 | 可压缩性 | 关键属性 |
|---|---|---|---|
| CPU | 核(Core)/ 毫核(m) | 可压缩 | 架构、频率、NUMA节点 |
| GPU | 卡(Device)/ 显存(MiB) | 不可压缩 | 型号、显存、NVLink拓扑、SM数量 |
| 内存 | 字节(MiB/GiB) | 不可压缩 | 容量、带宽、NUMA亲和性 |
| 存储 | 字节(GiB)/ IOPS / 吞吐 | 不可压缩 | 类型(SSD/HDD)、挂载点、配额 |
在调度器的资源模型中,每个节点上报的是可分配资源(Allocatable),而非物理总量。可分配资源 = 物理容量 - 系统预留 - 平台守护进程占用。例如,一台8卡A100节点,物理显存为 8 × 80GB = 640GB,但系统预留显存(如GPU驱动、监控进程)可能占 2GB/卡,实际可分配显存为 8 × 78GB = 624GB。
3.2 度量口径:如何准确测量与上报
资源度量的核心难点在于“请求值”与“实际使用值”的区分。调度器基于请求值(Request)做决策,而监控系统基于实际使用值(Usage)做运维。两者必须分开记录,否则会出现“资源碎片化”或“超卖失控”。
对于GPU资源,度量需要分层进行:
# 节点级GPU信息(nvidia-smi 输出关键字段)
GPU 0: A100-SXM4-80GB
Memory Usage : 32768 MiB / 81920 MiB
Utilization : 87%
Power Usage : 285 W / 400 W
Temperature : 68 C
BAR1 Memory : 256 MiB
NVLink : 4 links, 25 GB/s per link
在调度器内部,GPU资源通常以“整卡”为最小分配单位,但显存可以细粒度分配。例如,一个训练任务申请“0.5卡”意味着独占一个GPU的SM计算资源,但显存上限为40GB。这种抽象方式需要底层支持MIG(Multi-Instance GPU)或时间片切分。
内存度量相对简单,但需要关注页表开销与大页内存。AI训练框架(如PyTorch)在分布式训练时会使用NCCL通信库,NCCL需要锁定内存(Pinned Memory),这部分内存不可被交换到磁盘。因此,调度器在计算内存可分配量时,应预留一定比例的锁页内存池。
存储度量包含三个维度:容量、IOPS、带宽。对于训练任务,数据集读取的带宽往往比容量更关键。建议在资源模型中为存储增加吞吐等级标签(如高吞吐SSD、标准HDD),而非仅报告容量数字。
3.3 资源建模与调度接口设计
在实现多租户调度器时,资源抽象最终要落到数据结构与API设计上。以下是一个简化的资源描述结构,用于节点上报与任务申请:
// 节点资源上报格式(简化)
{
"nodeId": "gpu-node-03",
"allocatable": {
"cpu": "64",
"memory": "512Gi",
"gpu": "8",
"gpuMemory": "640Gi",
"storage": "10Ti"
},
"topology": {
"numaZones": 2,
"gpuToNuma": [0, 0, 1, 1, 0, 0, 1, 1],
"gpuToGpuLinks": "nvlink-4x25gbps"
}
}
// 任务资源申请格式(简化)
{
"jobId": "job-20240521-001",
"resources": {
"cpu": "16",
"memory": "128Gi",
"gpu": "4",
"gpuMemory": "320Gi",
"storage": "2Ti",
"constraints": {
"gpuModel": "A100",
"gpuTopology": "same-node",
"storageClass": "high-throughput"
}
}
}
设计要点:资源申请必须包含约束条件。例如,4卡训练任务要求4张GPU必须在同一节点(same-node),否则NCCL通信会退化为网络通信,训练效率下降数倍。调度器在匹配资源时,需要同时满足数量与拓扑约束。
3.4 案例:多租户场景下的资源度量陷阱
某AI平台同时运行三个团队的训练任务:团队A使用PyTorch进行NLP模型训练,团队B使用TensorFlow进行推荐模型训练,团队C进行数据预处理(纯CPU任务)。平台调度器报告GPU利用率仅为60%,但团队A反馈训练速度极慢。
排查后发现以下问题:
- 显存与计算资源不匹配:团队A的任务申请了8卡×80GB显存,但实际每个进程只使用20GB显存,其余显存被闲置。调度器认为“显存已分配”,但SM计算利用率只有40%。
- CPU资源被抢占:团队C的数据预处理任务申请了32核CPU,但实际使用了48核(超卖)。这导致团队A的DataLoader线程无法获得足够的CPU时间片,GPU等待数据,利用率下降。
- 存储带宽争抢:团队B在训练前需要加载1TB数据集,从共享存储读取时占满了全部网络带宽,导致团队A的checkpoint写入延迟从2秒增加到30秒。
复盘结论:资源度量不能只看“分配量”,必须监控实际使用量与瓶颈资源。该平台最终调整为:
- GPU显存按“申请量”记账,但增加“显存使用率”监控指标,超过阈值触发告警。
- CPU资源禁止超卖,所有任务必须使用cgroup限制CPU使用上限。
- 存储访问按租户进行流量整形,为高优先级训练任务预留带宽。
3.5 常见误区与设计建议
误区一:将GPU视为单一资源。GPU包含计算单元(SM)、显存、带宽、NVLink等多维资源。仅按“卡”分配会导致显存充足但计算不足,或计算充足但显存溢出的情况。建议至少拆分为“GPU计算份额”与“显存容量”两个维度。
误区二:忽略CPU与GPU的配比。AI训练任务通常需要CPU负责数据加载与预处理。经验值:每张GPU至少需要4-8个CPU核,否则数据加载会成为瓶颈。调度器应支持“CPU:GPU比例”作为任务申请的约束条件。
设计建议:在资源抽象层引入“资源画像”概念。每个租户或任务模板预定义一组资源配比(如“NLP训练画像”:CPU:GPU=8:1,内存:GPU=128GB:1,存储:GPU=2TB:1)。调度器按画像匹配节点,减少资源碎片。
最后,资源度量必须建立统一时钟。所有节点每5秒上报一次资源使用数据,调度器基于最近1分钟窗口的滑动平均值做决策,避免瞬时尖峰导致频繁迁移。同时,为每个租户设置资源配额(Quota)与优先级(Priority),确保公平性与服务质量。