Ascend NPU 实战(一):原生 Docker 与 Ascend Docker Runtime VNPU

本文以 Ascend 310P3(Atlas 300I Pro)为例,介绍昇腾 NPU vNPU 的基本使用方法。 内容先从原生 Docker 开始,演示手动创建 vNPU 并挂载到容器;再介绍 Ascend Docker Runtime 下的静态挂载和动态虚拟化。

vNPU 原理介绍

什么是 Ascend vNPU

昇腾的虚拟化实例叫 AVI(Ascend Virtual Instance)。它可以按照固定的虚拟化模板,把一个物理 NPU 划分成多个 vNPU,再把 vNPU 提供给容器使用。

从使用者的角度,原生 Docker 下主要需要完成三件事:

  1. 选择虚拟化模式和 vNPU 模板;
  2. 创建或指定要使用的 vNPU;
  3. 把设备和 Ascend 驱动环境挂载到容器。

昇腾 NPU 虚拟化架构

和 NVIDIA MIG 的类比

Ascend vNPU 可以类比 NVIDIA 的 MIG(Multi-Instance GPU),两者都是把一块物理加速卡切分成多个相互隔离的硬件实例,再分别分配给容器或任务使用。不过这只是概念上的类比,不代表两者的命令、资源模型和实现方式完全相同:

对比维度Ascend vNPUNVIDIA MIG
虚拟实例AVI / vNPUMIG Instance
划分方式根据 vir01vir02 等模板划分 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_DEVICESASCEND_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         |
+------------------------------------------------------------------------------------------+

本机可用的模板包括 vir01vir02vir02_1cvir04vir04_3cvir04_3c_ndvppvir04_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 0vir01 模板:

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。
  • 工具和运行库(-vLD_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 IDRuntime 根据 ASCEND_VISIBLE_DEVICESASCEND_RUNTIME_OPTIONS=VIRTUAL 注入,容器内执行 npu-smi
Ascend Docker Runtime 动态 vNPU启动容器时设置 ASCEND_VISIBLE_DEVICESASCEND_VNPU_SPECSRuntime 自动创建实例并注入,容器内执行 npu-smi

这两种 Docker 方式使用的是同一套底层 vNPU 能力,区别主要在于 vNPU 创建和设备注入是否由 Runtime 自动完成。本文只负责把原生 Docker 和 Ascend Docker Runtime 的基础能力跑通。

0%