本文以 Ascend 310P3(Atlas 300I Pro)为例,介绍昇腾 NPU vNPU 的基本使用方法。
内容先从原生 Docker 开始,演示手动创建 vNPU 并挂载到容器;再介绍 Ascend Docker Runtime 下的静态挂载和动态虚拟化。
vNPU 原理介绍
什么是 Ascend vNPU
昇腾的虚拟化实例叫 AVI(Ascend Virtual Instance)。它可以按照固定的虚拟化模板,把一个物理 NPU 划分成多个 vNPU,再把 vNPU 提供给容器使用。
从使用者的角度,原生 Docker 下主要需要完成三件事:
选择虚拟化模式和 vNPU 模板; 创建或指定要使用的 vNPU; 把设备和 Ascend 驱动环境挂载到容器。
和 NVIDIA MIG 的类比
Ascend vNPU 可以类比 NVIDIA 的 MIG(Multi-Instance GPU),两者都是把一块物理加速卡切分成多个相互隔离的硬件实例,再分别分配给容器或任务使用。不过这只是概念上的类比,不代表两者的命令、资源模型和实现方式完全相同:
对比维度 Ascend vNPU NVIDIA MIG 虚拟实例 AVI / vNPU MIG Instance 划分方式 根据 vir01、vir02 等模板划分 AICORE、内存、AICPU 和媒体处理资源 根据 GPU 支持的 profile 划分计算核心、显存和相关硬件资源 创建方式 使用 npu-smi 或 Ascend Docker Runtime 使用 NVIDIA 工具或 NVIDIA Container Toolkit 相关能力 容器视角 容器内看到一个 310Pvir01 等 vNPU 设备 容器内看到分配到的 MIG GPU 设备 共同点 都可以把硬件资源切成更小的实例,供多个容器隔离使用 都可以把硬件资源切成更小的实例,供多个容器隔离使用
本文后面的命令只针对 Ascend vNPU,NVIDIA MIG 仅作为帮助理解的参照。
不同场景下的使用方式
vNPU 的底层能力相同,但不同运行环境负责的事情不一样:
原生 Docker :通过 npu-smi 手动创建 vNPU,再手动把 vNPU 设备和 Ascend 驱动环境挂载到容器中,下面会完整展开。
Ascend Docker Runtime :支持静态和动态两种方式。静态方式仍然需要提前创建 vNPU,但设备和驱动挂载由 Runtime 完成;动态方式则在启动容器时通过 ASCEND_VISIBLE_DEVICES、ASCEND_VNPU_SPECS 等参数指定设备和模板,不需要提前创建 vNPU。
集群调度组件 :可以使用 Ascend Device Plugin 配合 Kubernetes Scheduler、Volcano 或其他调度组件。Device Plugin 负责设备发现、资源上报和分配,调度器负责选择节点和设备;动态方式下,组件还可以根据任务请求自动配置 vNPU。
简单说,原生 Docker 是“用户自己选、自己挂”,Ascend Docker Runtime 是“Runtime 帮你挂”,集群调度则是在此基础上再由调度器统一管理资源。三种方式的角色关系先在这里建立起来,具体命令见下面的实测过程。
测试环境
本次使用环境关键信息如下:
操作系统:CTYunOS 2.0.1 主机名:lixd-npu-test1 NPU:Ascend 310P3 / Atlas 300I Pro Ascend Driver: 24.1.0.1 架构: ARM64 查询 NPU 和 vNPU 模板
先确认 NPU 设备和芯片信息:
1
2
3
4
5
6
7
8
9
10
11
12
[ root@lixd-npu-test1 ~] # npu-smi info -l
Total Count : 1
NPU ID : 1536
Product Name : IT21PDDA011
Serial Number : 2106030728ZEP500xxxx
Chip Count : 1
[ root@lixd-npu-test1 ~] # npu-smi info -m
NPU ID Chip ID Chip Logic ID Chip Name
1536 0 0 Ascend 310P3
1536 1 - Mcu
本机的关键结果是:
1
2
3
4
NPU ID: 1536
Chip ID: 0
Chip Name: Ascend 310P3
Product Name: Atlas 300I Pro
再确认产品类型:
1
npu-smi info -t product -i 1536
1
2
3
4
5
NPU ID : 1536
Chip Count : 1
Product Type : Atlas 300I Pro
Chip ID : 0
再查询当前产品支持的 vNPU 模板:
1
npu-smi info -t template-info -i 1536
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
+------------------------------------------------------------------------------------------+
|NPU instance template info is: |
|Name AICORE Memory AICPU VPC VENC JPEGD |
| GB PNGD VDEC JPEGE |
|==========================================================================================|
|vir01 1 3 1 1 0 2 |
| 0 1 1 |
+------------------------------------------------------------------------------------------+
|vir02 2 6 2 3 1 4 |
| 0 3 2 |
+------------------------------------------------------------------------------------------+
|vir02_1c 2 6 1 3 0 4 |
| 0 3 2 |
+------------------------------------------------------------------------------------------+
|vir04 4 12 4 6 2 8 |
| 0 6 4 |
+------------------------------------------------------------------------------------------+
|vir04_3c 4 12 3 6 1 8 |
| 0 6 4 |
+------------------------------------------------------------------------------------------+
|vir04_3c_ndvpp 4 12 3 0 0 0 |
| 0 0 0 |
+------------------------------------------------------------------------------------------+
|vir04_4c_dvpp 4 12 4 12 3 16 |
| 0 12 8 |
+------------------------------------------------------------------------------------------+
本机可用的模板包括 vir01、vir02、vir02_1c、vir04、vir04_3c、vir04_3c_ndvpp 和 vir04_4c_dvpp。本文选择 vir01 做最小验证。
原生 Docker 下的 vNPU Demo
原生 Docker 的使用 vNPU 分成两步:先用 npu-smi 创建 vNPU,再把生成的 vNPU 设备映射到容器。
安装 Docker
使用 dnf 安装 Docker:
1
2
3
dnf install -y docker
systemctl enable --now docker
systemctl is-active docker
确认 Docker 服务和服务端架构:
1
2
docker version
docker info
创建 vNPU
本机使用 NPU ID 1536、Chip ID 0 和 vir01 模板:
1
2
npu-smi set -t create-vnpu -i 1536 -c 0 -f vir01
npu-smi info -t info-vnpu -i 1536 -c 0
输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Status : OK
Message : Create vnpu success
+-------------------------------------------------------------------------------+
| NPU resource static info as follow: |
| Format:Free/Total NA: Currently, query is not supported. |
| AICORE Memory AICPU VPC VENC VDEC JPEGD JPEGE PNGD |
| GB |
|===============================================================================|
| 7/8 18/21 6/7 11/12 3/3 11/12 14/16 7/8 NA/NA |
+-------------------------------------------------------------------------------+
| Total number of vnpu: 1 |
+-------------------------------------------------------------------------------+
| Vnpu ID | Vgroup ID | Container ID | Status | Template Name |
+-------------------------------------------------------------------------------+
| 100 | 0 | 000000000000 | 0 | vir01 |
+-------------------------------------------------------------------------------+
创建成功后,本机得到 vNPU ID 100,对应设备节点为 /dev/vdavinci100。
这里需要区分三个容易混淆的编号:
物理 NPU ID :宿主机 npu-smi 使用的设备编号,例如本文中的 1536,用于 -i 参数;vNPU ID :创建 vNPU 后返回的实例编号,例如本文中的 100;容器内设备编号 :宿主机的 /dev/vdavinci100 映射到容器内的 /dev/davinci100 后,容器内 npu-smi 使用的是容器视角下的可见设备编号。这三个编号不一定相同,不能把宿主机物理 NPU ID、vNPU ID 和容器内显示的设备编号混用。
启动容器挂载 vNPU
启动容器前,先确认 vNPU 设备节点和基础设备节点已经存在:
1
2
3
4
ls -l /dev/vdavinci100 \
/dev/davinci_manager \
/dev/devmm_svm \
/dev/hisi_hdc
本次实测可以看到以下设备节点:
1
2
3
4
crw-rw---- 1 HwHiAiUser HwHiAiUser 238, 0 Sep 4 15:58 /dev/davinci_manager
crw-rw---- 1 HwHiAiUser HwHiAiUser 235, 0 Sep 4 15:58 /dev/devmm_svm
crw-rw---- 1 HwHiAiUser HwHiAiUser 511, 0 Sep 4 15:58 /dev/hisi_hdc
crw------- 1 HwHiAiUser HwHiAiUser 236, 100 Sep 4 16:24 /dev/vdavinci100
然后我们启动容易,使用 –device 参数把 vNPU 挂载到容器:
1
2
3
4
5
6
7
8
9
10
docker run --rm \
--device= /dev/vdavinci100:/dev/davinci100 \
--device= /dev/davinci_manager \
--device= /dev/devmm_svm \
--device= /dev/hisi_hdc \
-v /usr/local/sbin/npu-smi:/usr/local/sbin/npu-smi:ro \
-v /usr/local/Ascend:/usr/local/Ascend:ro \
-e LD_LIBRARY_PATH = /usr/local/Ascend/ascend-toolkit/latest/lib64:/usr/local/Ascend/driver/lib64/driver:/usr/local/Ascend/driver/lib64 \
python:3.7-slim-buster \
npu-smi info
容器内输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
+--------------------------------------------------------------------------------------------------------+
| npu-smi 24.1.0.1 Version: 24.1.0.1 |
+-------------------------------+-----------------+------------------------------------------------------+
| NPU Name | Health | Power(W) Temp(C) Hugepages-Usage(page) |
| Chip Device | Bus-Id | AICore(%) Memory-Usage(MB) |
+===============================+=================+======================================================+
| 13 310Pvir01 | OK | NA 51 0 / 0 |
| 0 0 | 0000:06:00.0 | 0 229 / 2690 |
+-------------------------------+-----------------+------------------------------------------------------+
+-------------------------------+-----------------+------------------------------------------------------+
| NPU Chip | Process id | Process name | Process memory(MB) |
+-------------------------------+-----------------+------------------------------------------------------+
| No running processes found in NPU 13 |
+--------------------------------------------------------------------------------------------------------+
输出中的 310Pvir01 表明容器内已经看到 vir01 类型的 vNPU。
这里的参数可以简单分为两类。它们不是用来创建 vNPU 的,vNPU 已经在前一步创建完成:
设备映射(--device) :把 /dev/vdavinci100 以及
/dev/davinci_manager、/dev/devmm_svm、/dev/hisi_hdc 等 Ascend 设备节点交给容器,
这样容器才能访问已经创建的 vNPU。工具和运行库(-v、LD_LIBRARY_PATH) :把宿主机的 npu-smi 和
/usr/local/Ascend 驱动/工具库以只读方式提供给精简镜像,并让动态链接器能找到这些库。这里的挂载是为了让精简镜像能够执行 npu-smi。 Ascend Docker Runtime Demo
安装 Ascend Docker Runtime
原生 Docker 路径验证完成后,再安装 Ascend Docker Runtime,继续验证 Runtime 的静态挂载和动态虚拟化。
本次使用 MindCluster 6.0.0 提供的 ARM64 安装包。官方安装包地址见 MindCluster v6.0.0 Release 。
1
2
3
4
5
6
7
8
9
mkdir -p /tmp/ascend-docker-runtime
cd /tmp/ascend-docker-runtime
wget -O Ascend-docker-runtime_6.0.0_linux-aarch64.run \
https://gitee.com/ascend/mind-cluster/releases/download/v6.0.0/Ascend-docker-runtime_6.0.0_linux-aarch64.run
sha256sum Ascend-docker-runtime_6.0.0_linux-aarch64.run
chmod u+x Ascend-docker-runtime_6.0.0_linux-aarch64.run
./Ascend-docker-runtime_6.0.0_linux-aarch64.run --install
命令输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
1faddb734d347a2ac85065c02e162f1e0d1e051954d805543df9ce5ca0a3ee92 Ascend-docker-runtime_6.0.0_linux-aarch64.run
Uncompressing ascend-docker-runtime 0%
Uncompressing ascend-docker-runtime 62%
Uncompressing ascend-docker-runtime 100%
[INFO] installing ascend docker runtime
[INFO] platform(aarch64) matched!
[INFO] install executable files success
[INFO] install scene is 'docker'.
[INFO] /etc/docker/daemon.json modify success
[INFO] Ascend Docker Runtime has been installed in: /usr/local/Ascend/Ascend-Docker-Runtime
[INFO] The version of Ascend Docker Runtime is: 6.0.0
[INFO] please reboot daemon and container engine to take effect
[INFO] Ascend Docker Runtime install success
安装完成后重载并重启 Docker:
1
2
3
systemctl daemon-reload
systemctl restart docker
docker info --format 'server={{.ServerVersion}} arch={{.Architecture}} default-runtime={{.DefaultRuntime}}'
1
server=25.0.3 arch=aarch64 default-runtime=ascend
Ascend Docker Runtime:静态 vNPU
安装 Runtime 后,可以复用前面通过 npu-smi 创建的静态 vNPU。与原生 Docker 的区别是:不再手工写 --device,而是把 vNPU ID 交给 Runtime,由 Runtime 完成设备注入。
本机前面创建的 vNPU ID 是 100。设置 ASCEND_RUNTIME_OPTIONS=VIRTUAL,告诉 Runtime ASCEND_VISIBLE_DEVICES 指向的是已经存在的 vNPU:
1
2
3
4
5
6
7
8
docker run --rm --runtime= ascend \
-e ASCEND_VISIBLE_DEVICES = 100 \
-e ASCEND_RUNTIME_OPTIONS = VIRTUAL \
-v /usr/local/sbin/npu-smi:/usr/local/sbin/npu-smi:ro \
-v /usr/local/Ascend:/usr/local/Ascend:ro \
-e LD_LIBRARY_PATH = /usr/local/Ascend/ascend-toolkit/latest/lib64:/usr/local/Ascend/driver/lib64/driver:/usr/local/Ascend/driver/lib64 \
python:3.7-slim-buster \
npu-smi info
容器内输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
+--------------------------------------------------------------------------------------------------------+
| npu-smi 24.1.0.1 Version: 24.1.0.1 |
+-------------------------------+-----------------+------------------------------------------------------+
| NPU Name | Health | Power(W) Temp(C) Hugepages-Usage(page) |
| Chip Device | Bus-Id | AICore(%) Memory-Usage(MB) |
+===============================+=================+======================================================+
| 13 310Pvir01 | OK | NA 51 0 / 0 |
| 0 0 | 0000:06:00.0 | 0 229 / 2690 |
+-------------------------------+-----------------+------------------------------------------------------+
+-------------------------------+-----------------+------------------------------------------------------+
| NPU Chip | Process id | Process name | Process memory(MB) |
+-------------------------------+-----------------+------------------------------------------------------+
| No running processes found in NPU 13 |
+--------------------------------------------------------------------------------------------------------+
这里由 Runtime 负责把静态 vNPU 注入容器,容器内 npu-smi 显示 310Pvir01,命令中不再出现原生 Docker 的 --device=/dev/vdavinci100:/dev/davinci100。
Ascend Docker Runtime:动态 vNPU
Ascend Docker Runtime 可以在容器启动时根据环境变量自动创建 vNPU,并在容器退出后释放动态实例。动态验证和上面的静态验证是两条独立路径,动态启动时设备从没有预创建 vNPU 的状态开始。
关闭 vNPU 配置恢复
动态 Runtime 需要关闭 vNPU 配置恢复功能:
1
npu-smi set -t vnpu-cfg-recover -d 0
输出如下:
1
2
Status : OK
Message : The VNPU config recover mode Disable is set successfully.
启动动态 vNPU 容器
通过 ASCEND_VISIBLE_DEVICES 选择物理设备,通过 ASCEND_VNPU_SPECS 指定 vNPU 模板:
1
2
3
4
5
6
7
8
docker run --rm --runtime= ascend \
-e ASCEND_VISIBLE_DEVICES = 0 \
-e ASCEND_VNPU_SPECS = vir01 \
-v /usr/local/sbin/npu-smi:/usr/local/sbin/npu-smi:ro \
-v /usr/local/Ascend:/usr/local/Ascend:ro \
-e LD_LIBRARY_PATH = /usr/local/Ascend/ascend-toolkit/latest/lib64:/usr/local/Ascend/driver/lib64/driver:/usr/local/Ascend/driver/lib64 \
python:3.7-slim-buster \
npu-smi info
容器内输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
+--------------------------------------------------------------------------------------------------------+
| npu-smi 24.1.0.1 Version: 24.1.0.1 |
+-------------------------------+-----------------+------------------------------------------------------+
| NPU Name | Health | Power(W) Temp(C) Hugepages-Usage(page) |
| Chip Device | Bus-Id | AICore(%) Memory-Usage(MB) |
+===============================+=================+======================================================+
| 13 310Pvir01 | OK | NA 51 0 / 0 |
| 0 0 | 0000:06:00.0 | 0 229 / 2690 |
+-------------------------------+-----------------+------------------------------------------------------+
+-------------------------------+-----------------+------------------------------------------------------+
| NPU Chip | Process id | Process name | Process memory(MB) |
+-------------------------------+-----------------+------------------------------------------------------+
| No running processes found in NPU 13 |
+--------------------------------------------------------------------------------------------------------+
输出中的 310Pvir01 表明 Runtime 已经在容器内注入动态创建的 vir01 vNPU。
容器退出后再次查询:
1
npu-smi info -t info-vnpu -i 1536 -c 0
输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
+-------------------------------------------------------------------------------+
| NPU resource static info as follow: |
| Format:Free/Total NA: Currently, query is not supported. |
| AICORE Memory AICPU VPC VENC VDEC JPEGD JPEGE PNGD |
| GB |
|===============================================================================|
| 8/8 21/21 7/7 12/12 3/3 12/12 16/16 8/8 NA/NA |
+-------------------------------------------------------------------------------+
| Total number of vnpu: 0 |
+-------------------------------------------------------------------------------+
| Vnpu ID | Vgroup ID | Container ID | Status | Template Name |
+-------------------------------------------------------------------------------+
结果显示当前 vNPU 数量为 0,说明动态实例随容器生命周期自动释放。
小结:两种 Docker 运行方式
Docker 方式的职责可以概括为:
方式 设备和模板 容器内注入 原生 Docker 静态 vNPU 用户执行 npu-smi 创建 用户手工 --device 和挂载驱动,容器内执行 npu-smi Ascend Docker Runtime 静态 vNPU 用户执行 npu-smi 创建,Runtime 接收 vNPU ID Runtime 根据 ASCEND_VISIBLE_DEVICES 和 ASCEND_RUNTIME_OPTIONS=VIRTUAL 注入,容器内执行 npu-smi Ascend Docker Runtime 动态 vNPU 启动容器时设置 ASCEND_VISIBLE_DEVICES、ASCEND_VNPU_SPECS Runtime 自动创建实例并注入,容器内执行 npu-smi
这两种 Docker 方式使用的是同一套底层 vNPU 能力,区别主要在于 vNPU 创建和设备注入是否由 Runtime 自动完成。本文只负责把原生 Docker 和 Ascend Docker Runtime 的基础能力跑通。