Kubernetes 异构算力调度与虚拟化实战:Volcano + HAMi 在昇腾 NPU 集群的落地

摘要:整卡独占部署的 NPU 集群利用率低、小任务没地方跑,是昇腾集群运维的普遍痛点。本文介绍两条互补的解决路径:用 Volcano(CNCF 批量调度系统)解决"任务怎么排队、怎么整体调度",用 HAMi(CNCF 孵化项目,异构 AI 计算虚拟化中间件)解决"一张卡怎么切开给多个任务"。含项目定位、部署步骤、昇腾 NPU 切分示例、Volcano+HAMi 协同配置与踩坑记录。


一、背景:整卡独占的算力浪费

在昇腾 910B/910C 集群上部署多模态模型(详见本系列部署篇)时,采用的原则是"每个模型单实例、整卡/多卡独占"。这套方案稳定、排障简单,但问题也很明显:

  1. 大模型吃不满整卡:推理场景下 NPU 利用率往往只有 20%-50%,显存还有大量余量;
  2. 小任务无卡可用:一个只需要 2GB 显存的小任务,也得等一整张 910B 空闲才能跑;
  3. 多任务排队靠人工:没有队列和优先级机制,多个训练/推理任务只能运维手动协调。

要解决这个问题,需要从两个层面同时下手:

层面解决的问题代表组件
调度层任务排队、优先级、公平调度、Gang 全有或全无、队列配额Volcano
设备层单卡显存/算力切分、多任务共享、资源隔离HAMi

两个组件各管一段、天然互补,这也是当前昇腾/信创集群资源池化的主流组合。


二、Volcano 与 HAMi 项目定位

2.1 Volcano:Kubernetes 原生批量调度系统

项目说明
项目地址https://github.com/volcano-sh/volcano
社区地位CNCF 项目(2019 年上海 KubeCon 开源,2020 年 4 月进入 CNCF,2022 年 4 月进入 Incubating);已从"批量调度"演进为 AI 原生统一调度平台
最新版本v1.15.0(2026-05),支持 Kubernetes 1.35
核心能力多层级队列管理与配额、优先级/抢占、公平调度(DRF)、Gang Scheduling(PodGroup 全有或全无)、异构设备(GPU/NPU)调度、网络拓扑感知
生态事实Apache Spark 3.3 起将 Volcano 作为 Kubernetes 默认批量调度器;支持 PyTorch/TF/Ray/Flink 等框架

核心组件:volcano-scheduler(调度器)、volcano-controller(控制器,管理 Job/PodGroup)、vcctl(命令行工具)。

2.2 HAMi:异构 AI 计算虚拟化中间件

项目说明
项目地址https://github.com/Project-HAMi/HAMi
社区地位前身为 4paradigm 的 k8s-device-plugin;2024 年 8 月进入 CNCF Sandbox,2026 年 7 月晋升 CNCF Incubating
最新版本v2.10.0(2026-08)
支持硬件NVIDIA GPU、昇腾 Ascend(910A / 910B2 / 910B3 / 310P)、海光 DCU、寒武纪 MLU 等
昇腾能力v2.9.0 引入 HAMi-core 模式:用户态拦截 ACL(Ascend Computing Language)调用,实现 MB 级显存 + 百分比级算力的细粒度软切分,单张 910C 可同时服务多个任务;v2.10.0 支持 vNPU 硬切分 + 软切分自动选择

2.3 两者的分工

应用层:Volcano Job / PodGroup / 普通 Pod(schedulerName: volcano)
        │
调度层:Volcano Scheduler ── 队列 / 优先级 / Gang / 公平调度
        │   └── deviceshare 插件 ── 设备切分决策(HAMi vGPU / Ascend vNPU)
        │
设备层:节点上的 HAMi device plugin(或 volcano-vgpu-device-plugin)
        │   ├── vNPU 硬切分:按卡模板切分,隔离强
        │   └── HAMi-core 软切分:用户态拦截,粒度细
        │
硬件层:昇腾 910B / 910C / NVIDIA GPU / 海光 DCU 等

一句话总结:Volcano 管"哪些任务一起跑、按什么顺序跑、占多少配额",HAMi 管"一张卡切成几份、每份多少显存和算力、互相不干扰"。


三、Volcano 部署与核心概念

3.1 安装 Volcano

# 方式一:Helm(推荐)
helm repo add volcano-sh https://volcano-sh.github.io/helm-charts
helm repo update
helm install volcano volcano-sh/volcano -n volcano-system --create-namespace

# 方式二:官方 Installer YAML(指定版本)
kubectl apply -f https://raw.githubusercontent.com/volcano-sh/volcano/v1.15.0/installer/volcano-development.yaml

# 验证
kubectl get pods -n volcano-system
# 预期:volcano-scheduler-xxx、volcano-controller-xxx 均为 Running

注意:Volcano 安装后,还需要给命名空间打标签,让 Volcano 管理该命名空间的 Pod(否则调度器不接管):

kubectl label ns <your-namespace> volcano.sh/enable="true"

3.2 核心概念

概念API作用
Queuescheduling.volcano.sh/v1beta1资源配额容器,支持多层级队列、权重、抢占策略
PodGroupscheduling.volcano.sh/v1beta1Gang 调度单元,minMember 个 Pod 全部满足才调度(全有或全无)
Jobbatch.volcano.sh/v1alpha1Volcano 批量作业,含 task 模板、minAvailable、队列归属
vcctl-命令行工具,vcctl queue create

3.3 示例:队列 + Volcano Job(昇腾 NPU)

创建队列:

apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
  name: train-queue
spec:
  weight: 1
  reclaimable: false
  policy: besteffort
  capability:              # 队列资源配额
    cpu: "64"
    memory: 256Gi
    huawei.com/Ascend910: "8"

创建批量训练作业:

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: npu-train-job
spec:
  schedulerName: volcano    # 关键:走 Volcano 调度
  minAvailable: 2           # Gang 调度,2 个 worker 全部就绪才启动
  queue: train-queue
  policies:
    - event: PodFailed
      action: RestartJob
  tasks:
    - name: worker
      replicas: 2
      template:
        spec:
          containers:
            - name: train
              image: ascend-pytorch:24.0.RC1
              command: ["bash", "-c", "python train.py"]
              resources:
                limits:
                  huawei.com/Ascend910: "1"
          restartPolicy: Never

提交并观察:

kubectl apply -f queue.yaml -f job.yaml
vcctl job list
kubectl get podgroup
kubectl get pods -o wide | grep npu-train

四、HAMi 部署与昇腾 NPU 切分

4.1 安装 HAMi

# 1) 给被管理的节点打标签(HAMi 只管理打了标签的节点)
kubectl label nodes <node-name> gpu=on

# 2) 添加 Helm 仓库并安装
helm repo add hami-charts https://project-hami.github.io/HAMi/
helm repo update
helm install hami hami-charts/hami -n kube-system

# 3) 验证 scheduler 和 device plugin 都正常
kubectl get pods -n kube-system | grep hami

4.2 昇腾的两种切分模式

模式原理粒度适用
vNPU 硬切分按设备模板(vir02/vir04/vir08/vir16 等)把整卡物理切分显存按模板固定(如 910B 模板 2184/4369/8738/17476 MB),AI Core 按比例需要强隔离、稳定的生产推理
HAMi-core 软切分用户态拦截 ACL 调用,纯软件虚拟化,无需改应用代码MB 级显存 + 百分比级算力,最细粒度多任务共享一张卡、最大化利用率

v2.10.0 起:不带 huawei.com/vnpu-mode 注解的 Pod 落到模板节点走 vNPU 硬切分、落到 hami-core 节点走软切分;带显式注解则按注解走。

注意(官方 FAQ):昇腾 npu=1 时不支持拆分,整卡独占;拆分粒度取决于卡类型模板(910A / 910B2 / 910B3 / 310P 各不相同)。

4.3 资源申请示例

软切分(HAMi-core):申请 1 张 910B 的 4GB 显存

apiVersion: v1
kind: Pod
metadata:
  name: npu-share-test
spec:
  containers:
    - name: test
      image: ascend-pytorch:24.0.RC1
      command: ["bash", "-c", "sleep 86400"]
      resources:
        limits:
          huawei.com/Ascend910B: "1"          # 请求 1 张 910B
          huawei.com/Ascend910B-memory: "4096" # 请求 4096MB 显存(不指定则整卡)

按算力申请(910B3):1 张卡 + 40% 算力

resources:
  limits:
    huawei.com/Ascend910B3: "1"
    huawei.com/Ascend910B3-memory: "28672"
    huawei.com/Ascend910B3-core: "40"    # 请求 40% 的 AI Core 算力

910 系列(910A/910B 通用名)

resources:
  limits:
    huawei.com/Ascend910: "1"            # 请求 1 个 NPU
    huawei.com/Ascend910-memory: "2000"  # 请求 2000m 设备内存

提示:Ascend910-memory 同时限定了算力配额——请求的显存占整卡的比例即算力比例。910B 推理场景单卡 64GB,若任务只用 8GB,即可在一张卡上切 8 个实例。

4.4 验证切分效果

# 看 Pod 被分配到哪张卡
kubectl get pod npu-share-test -o jsonpath='{.status.containerStatuses[0].deviceIDs}'

# 节点上确认容器可见的设备
kubectl exec -it npu-share-test -- npu-smi info

五、Volcano + HAMi 协同:调度层 × 设备层

单装 HAMi 解决"怎么切",单装 Volcano 解决"怎么排",两者配合才能真正把 NPU 池化用起来。官方提供两条集成路径:

5.1 路径一:Volcano vGPU(NVIDIA 场景)

使用 volcano-vgpu-device-plugin(基于 NVIDIA Device Plugin + HAMi-core 硬隔离),此时无需再装完整 HAMi。需要 Volcano > 1.9:

# volcano-scheduler-configmap 中启用 vGPU
conf: |
  actions: "enqueue, allocate, backfill"
  tiers:
    - plugins:
        - name: priority
        - name: gang
        - name: conformance
    - plugins:
        - name: drf
        - name: deviceshare
          arguments:
            deviceshare.VGPUEnable: true      # 启用 vGPU
            deviceshare.SchedulePolicy: binpack  # binpack / spread
        - name: predicates
        - name: proportion

Pod 侧启用软切分(HAMi-core 模式):

apiVersion: v1
kind: Pod
metadata:
  annotations:
    volcano.sh/vgpu-mode: "hami-core"
spec:
  schedulerName: volcano
  containers:
    - name: cuda-container
      resources:
        limits:
          nvidia.com/gpu: 1
          nvidia.com/gpu-mem: "4096"   # 4GB 显存
          nvidia.com/gpucores: "50"    # 50% 算力

5.2 路径二:Ascend vNPU(昇腾场景)

Volcano 调度器启用 deviceshare 插件 + HAMi 的昇腾 vNPU 能力:

# volcano-scheduler-configmap 中启用 Ascend vNPU
conf: |
  actions: "enqueue, allocate, backfill"
  tiers:
    - plugins:
        - name: priority
        - name: gang
        - name: conformance
    - plugins:
        - name: drf
        - name: predicates
        - name: deviceshare
          arguments:
            deviceshare.AscendHAMiVNPUEnable: true   # 启用昇腾 vNPU
            deviceshare.SchedulePolicy: binpack
            deviceshare.KnownGeometriesCMNamespace: kube-system
            deviceshare.KnownGeometriesCMName: hami-scheduler-device

然后按第四节的方式申请 huawei.com/Ascend910B-memory 等资源,Volcano 会结合队列配额与 HAMi 上报的设备切分信息完成调度。

5.3 协同后的典型效果

  • 多租户队列:A 团队训练队列、B 团队推理队列,各自配额互不抢占;
  • Gang 调度:8 卡分布式训练任务,8 个 worker 全部就绪才启动,避免部分卡空转等待;
  • 卡级共享:一张 910B(64GB)按 8GB/份切给 8 个推理任务,利用率从 30% 提升到 80%+;
  • 优先级抢占:高优任务入队时,可回收低优任务占用的设备资源。

六、踩坑记录

以下问题来自官方文档与社区实践,部署前建议逐条核对:

坑点现象处理
官方 ascend-device-plugin 与 HAMi 同时部署节点资源名冲突、Pod 调度错乱二选一:用 HAMi 就别再装华为官方 device plugin(CANN 侧只保留一个上报通道)
节点未打 gpu=on 标签HAMi 不管理该节点,申请切分资源永远 Pending检查 kubectl get nodes --show-labels,补齐标签
npu=1 想拆分分配失败或整卡独占官方 FAQ 明确 npu=1 不支持拆分;小任务先申请 -memory 再验证模板
切分粒度与卡型号不匹配910A/910B2/910B3 模板不同,申请的资源名写错直接调度失败先查 HAMi 官方设备模板,按实际卡型号写 huawei.com/Ascend910B3 等精确资源名
Gang 调度任务一直 PendingPodGroup minMember/minAvailable 不满足(如资源不够凑齐全部 worker)检查队列配额和节点剩余资源;临时调小 minAvailable 验证
Volcano 版本过旧vGPU/昇腾 vNPU 特性不生效volcano-vgpu 需要 Volcano > 1.9;昇腾 vNPU 协同建议用较新版本(1.15+)
HAMi-core 软切分与 CANN 版本兼容部分算子运行异常软切分依赖 ACL 用户态拦截,上线前用代表性算子/模型做一轮回归
Helm/镜像拉取慢(内网环境)安装卡住配置代理,或提前拉取镜像离线导入;helm 仓库也可镜像到内网
队列 capability 资源名不一致配额不生效Queue 里写的资源名必须与 HAMi 上报一致(huawei.com/Ascend910 系列)
与存量"整卡独占"部署冲突已运行的服务占着整卡,切分任务无卡可切先停掉独占服务或规划专用节点,再逐步过渡到共享池

七、总结与后续

Volcano + HAMi 的组合,把昇腾集群从"一模型一集群"的粗放模式,升级为"队列配额 + 卡级共享 + Gang 调度"的资源池模式:

  • Volcano:批量调度的事实标准,队列/优先级/Gang/公平调度一站式解决;
  • HAMi:异构虚拟化中间件,一张昇腾卡切成多个细粒度实例,多芯适配(昇腾/海光/寒武纪)贴合信创诉求;
  • 两者互补:调度层决策"往哪跑",设备层解决"怎么共享",是昇腾 NPU 集群资源池化的成熟路径。

后续可以继续展开的方向:

  1. 多芯异构统一池:同一集群纳管昇腾 + 海光 + NVIDIA,用统一资源名做跨芯调度;
  2. 任务队列治理:结合公司多租户场景设计队列层级与配额模型。

参考资料

  • Volcano 官网与文档:https://volcano.sh
  • Volcano GitHub:https://github.com/volcano-sh/volcano
  • HAMi 官网与文档:https://project-hami.io
  • HAMi GitHub:https://github.com/Project-HAMi/HAMi
  • 昇腾社区 NPU 虚拟化参考实践:https://www.hiascend.com
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐