Skip to content

异构加速卡统一资源管理:NVIDIA/昇腾/寒武纪混布调度 ​

实验目标 ​

集群里同时出现 NVIDIA、昇腾、寒武纪等不同厂商加速卡时,"装好各自驱动"只是第一步。真正的挑战在资源管理:

  • 如何让调度器"认识"每一种卡,并把它们当成可统一调度的资源?
  • 如何避免异构卡混布导致的隐性性能问题(某类作业被调度到不兼容的卡上)?
  • 如何做到多厂商共存时的可观测与可审计(谁在用哪种卡、用了几块、健康如何)?

本篇从 Slurm 视角逐段拆解异构加速卡统一资源管理的落地方法,覆盖异构资源建模、GRES 扩展、混布策略、性能隔离与可观测性。命令与配置为通用实践,具体字段以所用版本与厂商文档为准。

第一步:异构资源建模——先让调度器"数得清" ​

1. GRES 按类型注册,不混在一个资源名下 ​

Slurm 通过 GRES(Generic RESource)描述加速卡。异构集群的第一原则:每类卡一个 GRES 类型,而不是把所有卡都叫 gpu。

bash
# /etc/slurm/gres.conf(每节点按实际卡型配置)
# NVIDIA 节点
NodeName=node[01-20] Name=gpu Type=a100 File=/dev/nvidia[0-7]
# 昇腾节点
NodeName=node[21-30] Name=ascend Type=910B File=/dev/davinci[0-7]
# 寒武纪节点
NodeName=node[31-40] Name=mlu Type=mlu370 File=/dev/mlu[0-7]
bash
# slurm.conf 对应声明
GresTypes=gpu,ascend,mlu
NodeName=node[01-20] Gres=gpu:8
NodeName=node[21-30] Gres=ascend:8
NodeName=node[31-40] Gres=mlu:8
PartitionName=hetero Nodes=node[01-40] Default=YES MaxTime=INFINITE State=UP

要点:

  • 用 Type 区分卡型(a100/910B/mlu370),同一类型内再按型号细分(如 Type=a100 vs Type=h20);
  • GRES 命名建议语义化(gpu/ascend/mlu),并在文档中固化,避免团队各叫各的;
  • sinfo -o "%n %G" 可以快速核对每个节点注册的 GRES 是否符合实际卡数。

2. 作业申请与约束 ​

用户侧按 GRES 类型申请,调度器按类型匹配节点:

bash
# 申请 NVIDIA 卡
salloc --gres=gpu:4 ...
# 申请昇腾卡
salloc --gres=ascend:8 ...
# 混布节点申请多类卡
salloc --gres=gpu:2,ascend:4 ...
bash
# 防止异构卡作业跑错节点:用约束或特性标签
sbatch --gres=gpu:4 --constraint=a100 script.sh
# 或按分区隔离(见第三步策略)

第二步:驱动与运行环境——多厂商共存的"地基" ​

异构集群最容易翻车的是环境层,三个纪律:

  1. 每类卡一套独立驱动栈:NVIDIA(CUDA 驱动)、昇腾(CANN)、寒武纪(CNToolkit)互不干扰地共存于同一 OS。用模块化(module)或容器镜像隔离,避免全局 PATH/LD_LIBRARY_PATH 互相污染。
  2. 容器化是异构调度的最佳载体:用 Singularity/Apptainer 或 Enroot 把不同厂商的运行时封装进镜像,调度器只负责资源,运行时由镜像自洽。NVIDIA 用 --nv 挂 GPU,昇腾/寒武纪用各自 runtime 参数。
  3. 驱动与固件基线统一:与 01 章联动——每类卡的驱动版本、固件版本在全网一致,并记录在交付基线中;异构集群的排障复杂度随厂商数量指数上升,基线不一致等于埋雷。

工程观察:混布集群的故障第一来源不是调度器,而是环境串扰——同一节点上 CUDA 与 CANN 的库互相覆盖、PYTHONPATH 冲突、容器 runtime 选错。环境隔离做得越干净,异构集群越稳定。

第三步:混布策略——三类典型布局 ​

策略布局适用场景权衡
A. 按厂商分区每类卡独立 Partition(gpu / ascend / mlu)业务边界清晰、占比稳定简单可靠;卡型比例变化时要人工迁移节点
B. 单一大分区 + 约束所有节点一个分区,作业用 --constraint/特性选卡卡型需求动态变化调度灵活;约束写错会调度失败,需强规范
C. 混布节点(每节点多类卡)单节点同时插不同厂商卡显存/算力互补、密度要求高环境串扰风险最大,只建议容器化 + 强隔离时采用

推荐:多数团队从 A(分区隔离) 起步,业务稳定后演进到 B(约束调度);C 只在有明确密度诉求且容器化成熟后尝试。策略选择直接影响排障成本——异构集群宁可"调度不够灵活",也不要"环境不可控"。

第四步:性能隔离与健康检查 ​

1. 性能隔离 ​

  • 算力/显存粒度:昇腾、寒武纪卡同样支持显存与算力切分(类似 MIG/vGPU),是否开放切分取决于业务模型(推理多租户 vs 训练独占);
  • 卡间互联:异构混布时注意同厂商卡的互联组网(NVLink、HCCS、MLU-Link)与跨厂商流量分离——异构混布不等于异构互连,跨厂商卡之间通常无高速互联,训练作业要保证"卡都在同一互联域";
  • 网络平面:计算网络(RDMA)与管理网络分离,异构卡的 RDMA 驱动(NVIDIA InfiniBand / 昇腾 RoCE / 寒武纪 RoCE)各自独立配置。

2. 健康检查 ​

bash
# 每类卡各自的健康命令,汇总进统一巡检脚本
nvidia-smi --query-gpu=index,utilization.gpu,memory.used,clocks.gr --format=csv
npu-smi info                       # 昇腾
cnmon info                         # 寒武纪
  • 巡检脚本输出统一格式(节点、卡型、索引、利用率、显存、温度、错误计数),落库或落文件;
  • 告警阈值按卡型分别设定:不同卡的温度/利用率语义不同,不要用同一阈值一刀切;
  • ECC/错误计数按卡型采集,纳入交付基线(01 章)与运行巡检。

第五步:可观测与可审计 ​

  • 调度审计:sacct 记录每个作业申请的 GRES 类型与数量,按卡型输出资源利用率报表;
  • 账本与配额:多团队混用异构资源时,按 GRES 类型分别计量(NVIDIA 卡和昇腾卡的"额度"不能混算),配额与账单口径在平台层统一;
  • 资源视图:用 sinfo -o "%P %n %G" + 巡检数据组合成"哪类卡、哪批节点、健康状态"的运营视图,供容量规划(哪些卡该扩容、哪些利用率低)。

混布检查清单 ​

  • [ ] GRES 按卡型分类型注册,sinfo -G 与实际卡数一致
  • [ ] 每类卡独立驱动栈,容器化封装运行时,无环境串扰
  • [ ] 混布策略明确(分区/约束/混布节点)并写入交付文档
  • [ ] 同厂商卡互联域保证,跨厂商流量分离
  • [ ] 健康巡检按卡型统一格式采集,阈值分别设定
  • [ ] sacct 按 GRES 类型可审计,配额按卡型分别计量

工程启示 ​

  • 异构管理的核心是"建模":先把每种卡在调度器里建模清楚(类型/数量/特性),后面的调度、隔离、审计才有基础。
  • 环境隔离 > 调度技巧:异构集群的稳定性上限由环境隔离决定,调度策略只能在其上优化。
  • 先分区后融合:异构混布是演进出来的,不是一步到位的;从分区隔离起步,验证成熟后再放开约束调度。

下一篇预告:异构集群算力就绪后,目光转向"一行 Python 到底经历了什么"——GPU 内核从源码到机器码的编译管线。下一篇《CUDA 编译管线深入:nvcc → PTX → SASS 的优化空间》拆解内核编译产物与优化判断。

AI-HPC 智算超算专家 · Contact: openaihpc@gmail.com