昇腾部署第 B5 课:CANN 装进容器——镜像怎么选、怎么自己搭
编制:观澜|日期:2026-10-05|级别:T2 标准级 层级:B 层 · L1 基础软件(B 层第 5 课,前接 B4《七种安装方式》) 素材来源:昇腾官方文档 MCP 实时抓取(CANN 9.2.0-beta.2 / MindIE 3.1.0 官方文档) 数据纪律:设备路径、挂载参数、权限说明、版本要求逐字出自官方文档;标注「归纳」的是便于记忆的整理,非官方口径。
第一段 · 章前定位
B4 讲过七种装法,其中第七种叫 netdocker —— 在线安装(Docker 镜像)。
但"用容器装"这句话本身,藏着一个和裸机完全不同的前提:
驱动和固件不在容器里,在宿主机上。 容器里的程序要用卡,靠的不是"容器里装了驱动",而是"宿主机把卡交给了容器"。
这一课讲的就是"交"这件事 ——
它具体表现为 docker run 命令里那几行 --device= 和 -v。
为什么这一课必须单独讲:
- 容器是目前最主流的部署方式(B4 的七种装法里,容器方式在两种 OS 上都支持)。
- 容器里的报错特别容易误判 ——
明明宿主机
npu-smi好好的,容器里就是看不见卡; 明明算子库装好了,容器里就是找不到。 因为"卡"和"环境"是两件分别要处理的事。 - A3 讲过
npu-smi装在/usr/local/bin/和/usr/local/sbin/(宿主机上)—— 容器里要能用npu-smi,得把宿主机那份挂进去。 这一课就把这个"为什么"讲清楚。
第二段 · 学习目标
学完这一课,你应该能回答:
- 容器场景和裸机场景的根本差别——驱动在哪里
- 容器要"看见"卡,必须挂哪几个设备
- 官方的
docker run命令每一行的作用 --device的权限为什么必须是rwm,用rw会怎样(官方有具体说明)- 镜像从哪来、怎么自己搭、内网怎么办
第三段 · 根本差别:驱动在宿主机,不在容器里
先说结论
| 裸机 | 容器 | |
|---|---|---|
| 驱动 / 固件 | 装在这台机器上 | 在宿主机上(容器里不装) |
| CANN | 装在这台机器上 | 容器里装(或镜像里已带) |
| 卡 | 本来就是这台机器的 | 靠 --device 从宿主机"交"进来 |
官方原文印证这一点(MindIE 镜像安装的前提条件):
宿主机已经安装过 NPU 驱动和固件。如未安装,下载固件与驱动, 根据产品系列和型号选择对应版本的社区版本或商用版本的固件与驱动。
注意是"宿主机已经安装过" —— 容器方案默认驱动在宿主机上备好。
这条差别解释了两个常见困惑
困惑一:容器里能不能装驱动? 不需要,也不该。 驱动是"让操作系统认得这张卡"的(A1 的内容), 而容器共用宿主机内核——卡已经被宿主机认出来了,容器直接拿结果用。
困惑二:为什么容器里的 npu-smi 要找宿主机要?
因为 npu-smi 是随驱动一起装到宿主机的(A3 讲过:装在 /usr/local/sbin/ 和 /usr/local/bin/)。
容器里没有驱动,自然也没有 npu-smi ——
所以要显式地把宿主机那份挂进来(第五段)。
归纳(一句话): 裸机部署是"我在自己家里装水电";容器部署是"我租了个房间,水电从房东那儿接"。 接不上,房间再漂亮也没电。
第四段 · 容器要"看见"卡:三个设备
官方给出的设备清单(MindIE 镜像安装命令)
| 设备路径 | 作用(归纳) |
|---|---|
/dev/davinci0、/dev/davinci1 … |
每一张 AI 处理器(编号从 0 开始) |
/dev/davinci_manager |
设备管理接口 |
/dev/hisi_hdc |
主机与设备间的通信通道 |
/dev/devmm_svm |
内存管理相关 |
怎么知道要挂几个
davinci? 官方给了方法(见第八段): 先在宿主机上执行ls /dev/ | grep davinci, 输出里的davinciX,X 就是设备编号。
从设备名能读出昇腾的命名习惯(归纳)
davinci是达芬奇架构的名字(A2 讲过 Ascend 是芯片名,这里是架构名)davinci0…davinciN对应第 0 张到第 N 张卡davinci_manager是"管理"那个(不是运算的)hisi_hdc里的hisi= 海思
记住这个结构的好处:看到 --device=/dev/davinci7 就知道那是第 8 张卡,
而 davinci_manager / hisi_hdc / devmm_svm 是每台机器一份、不按卡编号。
第五段 · 官方的 docker run:逐行拆解(本课核心)
以下是官方 MindIE 镜像安装文档给出的启动命令(Atlas 800I A2/A3 推理服务器场景):
docker run -it -d --net=host --shm-size=500g \
--name <container-name> \
-w /home \
--device=/dev/davinci0:rwm \
--device=/dev/davinci1:rwm \
--device=/dev/davinci2:rwm \
--device=/dev/davinci3:rwm \
--device=/dev/davinci_manager:rwm \
--device=/dev/hisi_hdc:rwm \
--device=/dev/devmm_svm:rwm \
-v /usr/local/Ascend/driver:/usr/local/Ascend/driver:ro \
-v /usr/local/dcmi:/usr/local/dcmi:ro \
-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi:ro \
-v /usr/local/sbin/:/usr/local/sbin:ro \
-v /home/weight:/home/weight:ro \
{IMAGE_ID} bash
官方补充:
{IMAGE_ID}为镜像 ID,镜像安装完成后执行docker images命令即可查看对应 ID。 官方也说明:容器启动命令仅供参考,可根据需求自行修改。
逐行拆开
| 行 | 作用(归纳) |
|---|---|
-it -d |
-i 交互 + -t 终端 + -d 后台运行(起好就返回,不占着你的终端) |
--net=host |
用宿主机的网络(省去端口映射的麻烦) |
--shm-size=500g |
共享内存大小。官方注:多模态理解模型、业务最大并发数较高时,建议不小于 500g |
--name <container-name> |
给容器起个名字(以后 docker exec 进去要靠它) |
-w /home |
容器启动后的工作目录 |
--device=/dev/davinci0:rwm |
把第 0 张卡交给容器(:rwm 见下) |
| …(多张卡各一行) | 有几张卡要交给容器,就写几行 |
--device=/dev/davinci_manager:rwm |
设备管理接口 |
--device=/dev/hisi_hdc:rwm |
通信通道 |
--device=/dev/devmm_svm:rwm |
内存管理相关 |
-v /usr/local/Ascend/driver:...:ro |
把宿主机的驱动目录挂进来(只读) |
-v /usr/local/dcmi:...:ro |
DCMI 接口目录(A3 讲过 DCMI 是什么),只读 |
-v /usr/local/bin/npu-smi:...:ro |
把宿主机的 npu-smi 挂进来,只读 |
-v /usr/local/sbin/:/usr/local/sbin:ro |
sbin 目录(npu-smi 官方说也装在这里——A3),只读 |
-v /home/weight:/home/weight:ro |
挂模型权重目录(只读) |
{IMAGE_ID} bash |
用哪个镜像、启动什么(这里是 bash) |
⭐ 最关键的一行说明:为什么是 :rwm 而不是 :rw
官方专门解释了这件事,原文如下:
对于
--device参数,挂载权限设置为rwm,而非权限较小的rw或r,原因如下:
- 对于 Atlas 800I A2 推理服务器:若设置挂载权限为
rw,可以正常进入容器, 同时也可以使用npu-smi命令查看 npu 占用信息,并正常运行 MindIE 业务; 但如果挂载的 npu 上有其它任务占用,则使用npu-smi命令会打印报错, 且无法运行 MindIE 任务(此时torch.npu.set_device()会失败)。- 对于 Atlas 800I A3 超节点服务器:若设置挂载权限为
rw, 进入容器后使用npu-smi命令会打印报错,且无法运行 MindIE 任务 (此时torch.npu.set_device()会失败)。
归纳三件事:
rwm里的m= mknod(允许在设备上创建设备节点)—— 这是"能管卡"和"只能读写卡"的区别。rw的问题不是"立刻报错",而是"有别的任务占用时才出问题" —— 这种坑最难查:单机空跑正常,一上生产就出问题。- 官方给出了一条具体的失败线索:
torch.npu.set_device()失败 —— 遇到这个报错,想想--device的权限是不是写成rw了。
这一条值得单独记:
--device权限写rw是"能用但不可靠",不是"不能用"。
第六段 · 还要挂什么:两个不太显眼但很重要的东西
① DCMI 目录(/usr/local/dcmi)
A3 讲过:DCMI 是设备管理 API 接口(给程序调用的那种),
和 npu-smi(命令行工具)是兄弟。
所以官方的 docker run 里也挂了它:
-v /usr/local/dcmi:/usr/local/dcmi:ro
意义(归纳):容器里跑的管理类程序、监控类程序,靠的是 DCMI。
② 全局安装配置文件(ascend_cann_install.info)
B3 讲过这个文件:
官方原文:可在如下目录查看是否存在全局配置文件
ascend_cann_install.info: - root 用户:/etc/Ascend- 非 root 用户:${HOME}/Ascend
在容器场景,官方文档里出现了把它挂进容器的示例:
-v /etc/Ascend/ascend_cann_install.info:/etc/Ascend/ascend_cann_install.info:ro
为什么这件事重要(归纳): B3 说"安装路径记在这个全局配置文件里" —— 容器里如果没有这个文件,程序就不知道 CANN 装在哪。 把它挂进去,容器里的工具才能正确解析安装路径。
归纳(一句好记的话): 裸机部署时,环境靠环境变量 + 全局配置文件;容器部署时,这两样都要"带过去"。 B1 讲的是环境变量(
source set_env.sh), 这一节讲的是全局配置文件(-v挂载)——同一件事的两个方面。
第七段 · 镜像从哪来、怎么自己搭
三条路(归纳)
| 路子 | 适合谁 | 说明 |
|---|---|---|
| ① 官方镜像仓库下载 | 大多数人 | 官方给出镜像下载页面;需登录华为账号后按设备形态选版本 |
| ② 官方发布的基础镜像 | 想自己定制的 | 有些组件(如算子开发工具链)官方只提供容器化部署方式 |
| ③ 自己构建 | 有定制需求 | 官方文档给了构建 CANN 容器镜像的示例,可以按它修改 Dockerfile 定制 |
关于路径 ③(官方原文)
本章节以在容器镜像 openEuler 20.03 上为例, 制作包含 CANN 软件包 Toolkit 和 Kernels 的 CANN 容器镜像。 可以参考本节内容定制修改 Dockerfile,自定义制作 CANN 容器镜像。
归纳:官方给了 Dockerfile 样板,你可以改 —— 这是自建镜像的起点。
关于路径 ②(一个值得注意的官方限制)
官方在某个算子开发工具链的学习环境安装指南里明确指出:
鉴于算子编译环境依赖复杂,本教程仅支持 CANN 容器化部署方式, 不支持裸机或虚拟机等非容器环境。
这条的意义(归纳): "必须用容器"不只是偏好,有些场景是硬要求—— 因为依赖太复杂,容器是唯一能保证一致性的方式。
自己搭镜像时,两条硬约束
① 宿主机 Docker 版本
官方原文(MindIE 镜像安装前提条件): 用户在宿主机自行安装 Docker(版本要求大于或等于 24.x.x)。
② 宿主机与容器的操作系统兼容性
官方原文(针对 Atlas 200I Pro 加速模块): - Ubuntu 22.04 宿主机支持运行 Ubuntu 24.04 容器镜像 - openEuler 22.03 宿主机支持运行 openEuler 24.03 容器镜像 请根据宿主机操作系统选择兼容的容器镜像版本。
归纳:宿主机 OS 和容器 OS 不是随便配的—— 装之前先查兼容性(这与 A2/B3 讲的"先查配套"是同一条纪律)。
内网怎么办(官方给了方案)
官方文档专门有一节讲内网环境无公网访问权限的应对方案,含:
| 官方小节 | 对应做法(归纳) |
|---|---|
| Docker 镜像在隔离内网的获取方法 | 镜像怎么带进内网 |
| 传输容器启动脚本 | 脚本也要一起带 |
| 传输示例代码仓库 | 代码仓库也要带 |
归纳成一句:内网部署要把"镜像 + 启动脚本 + 代码"三样一起搬过去, 而不是只搬镜像。
第八段 · 出问题怎么查
先搞清要挂几张卡(官方方法)
官方原文:挂载 davinci 设备时,首先通过
ls /dev/ | grep davinci命令查询当前的 davinci 设备, 即davinciX,X 即为 davinci 设备编号。
所以顺序是:先在宿主机上查有哪些设备 → 再决定 docker run 里写几行 --device。
排查表
| 现象 | 先查什么 |
|---|---|
容器里 npu-smi 说 command not found |
没挂宿主机的那份(/usr/local/bin/npu-smi、/usr/local/sbin/) |
容器里 npu-smi 能用,但有任务时就报错 |
--device 权限是不是 rw(官方明确说要用 rwm) |
torch.npu.set_device() 失败 |
同上——这是官方给出的具体失败线索 |
| 容器里程序不知道 CANN 装在哪 | 全局配置文件 ascend_cann_install.info 挂了吗 |
| 想挂 8 张卡不知道编号 | 宿主机 ls /dev/ \| grep davinci |
| 起容器时提示 Docker 版本不支持 | 官方要求 ≥ 24.x.x |
| 宿主机 OS 和镜像 OS 不匹配 | 查官方兼容性说明(官方举了 Ubuntu / openEuler 两组) |
| 内网机器拉不到镜像 | 走官方"内网应对方案":镜像+脚本+代码一起搬 |
| 多模态模型跑起来内存不够 | --shm-size 调大(官方建议多模态高并发时 ≥500g) |
⭐ 一个最省时间的判断法
容器里出问题,先分清是"没接上"还是"没装对"。
| 类型 | 表现 | 查 |
|---|---|---|
| 没接上(宿主机的资源没进来) | npu-smi 找不到 / 看不见卡 / 不知道 CANN 在哪 |
docker run 里的 --device 和 -v |
| 没装对(容器里的软件问题) | 能找到命令,但报算子、版本、库的错 | B1–B4(CANN 本身的问题) |
"能看见的路径"和"能用的软件"是两件事——先确认前者,再查后者。
第九段 · 小结与下一课
三句话带走
- 容器场景的根本差别:驱动在宿主机,CANN 在容器里。
容器看得见卡,靠的是
--device把/dev/davinci*交进来。 --device权限必须用rwm,不能用rw。 官方说得很具体:写rw不是立刻不可用,而是"有别的任务占用时就报错", 并且torch.npu.set_device()会失败。- 裸机靠"环境变量 + 全局配置文件"定位,容器要"把这两样带进去" ——
source set_env.sh(B1)属于前者,-v .../ascend_cann_install.info属于后者(B3)。
本课在体系里的位置
─── L0 硬件与环境(A 层,已完成)───
A0 一张地图 / A1 驱动与固件 / A2 产品线与形态 / A3 HDK 到底指什么
─── L1 基础软件(B 层,本课完结)───
B1 CANN 是什么、装在哪、环境变量怎么配 ✅
B2 算子库与算子包:为什么会"找不到算子" ✅
B3 版本对应关系与多版本共存 ✅
B4 七种安装方式:为什么有七种装法 ✅
B5 CANN 装进容器:镜像怎么选、怎么自己搭 ★ 本课
B 层走完了 —— 回头看这条线
A0 有哪几层、谁依赖谁
↓
A1 卡怎么被操作系统认出来(驱动/固件) ← L0
A2 认出来的是张什么卡(产品线/形态)
A3 HDK 这一片包含什么(四个工具)
↓
B1 CANN 是中间那一层,装在哪、环境变量怎么配 ← L1
B2 算子库按型号分发,"找不到算子"先想起这个
B3 版本由 cann 软链接指向,升级单向、降级要重装
B4 七种装法是三个维度的组合,Conda 不装驱动
B5 容器里驱动在宿主机,靠 --device 把卡交进来
一句话总结 L1:CANN 是框架与芯片之间的那一层; 这一层的所有复杂度,都来自"它必须同时对上两边"——对上芯片(B2 的型号量)、对上版本(B3 的配套)。
本课留下的"钩子"
| 钩子 | 在哪一课填 |
|---|---|
| 框架层怎么被装进容器、和 CANN 怎么配合 | C 层(L2 框架) |
| 容器里怎么跑推理服务(MindIE 的实际使用) | D 层(L3 服务) |
| 容器里的日志在哪、怎么导出来 | E 层(排障) |
| 特权容器 / AVI 虚拟化实例在容器里的表现 | F 场景实战(A3 提过部署场景分类) |
课后自测(3 题)
- 容器里到底要不要装 NPU 驱动?为什么?
--device=/dev/davinci0:rw和rwm有什么区别?为什么rw的问题在空跑时看不出来?- 容器里
npu-smi报 command not found,和容器里npu-smi能跑但报错——这两种情况的排查方向有什么不同?
附录 A · 本课引用的官方出处
| 内容 | 出处 |
|---|---|
完整的 docker run 命令(设备、挂载、--shm-size、{IMAGE_ID} 说明) |
官方 MindIE《安装指南 · 方式一:镜像安装》zh/mindie/310/mindiellm/llmdev/mindie_llm/user_guide/install/source/image_usage_guide.md |
rwm 与 rw 的差异说明(含 torch.npu.set_device() 失败这条线索) |
同上 |
| 前提条件:宿主机已安装 NPU 驱动和固件;Docker ≥ 24.x.x | 同上 |
| 宿主机与容器镜像的操作系统兼容性(Ubuntu / openEuler 两组) | 同上 |
ls /dev/ \| grep davinci 查设备编号 |
官方 MindCluster《ToolBox 工具 · 容器内使用》zh/mindcluster/2610/toolbox/toolboxug/toolboxug_0008.html |
挂载全局配置文件 ascend_cann_install.info 的示例 |
同上 |
ascend_cann_install.info 的位置(root /etc/Ascend、非 root ${HOME}/Ascend) |
官方《参数说明》instg_0043.html(另见 B3) |
| 构建 CANN 容器镜像(可改 Dockerfile 自定义) | 官方《安装 CANN(容器场景)· 自行构建容器镜像》zh/300Vtest/300VG/300V_0030.html(Ubuntu 例)/ 300V_0031.html(openEuler 例) |
| 「仅支持 CANN 容器化部署方式,不支持裸机或虚拟机」 | 官方《昇腾 AI 算子开发工具链学习环境安装指南》zh/CANNCommunityEdition/920beta2/devaids/optool/docs/zh/quick_start/installation_guide.md |
| 内网环境无公网访问权限的应对方案(镜像 / 脚本 / 代码) | 同上 |
npu-smi 装在 /usr/local/sbin 与 /usr/local/bin |
官方《Ascend HDK 接口和命令工具简介》(另见 A3) |
⚠️ 数据纪律提示: ① 本课的
docker run命令出自官方 MindIE 镜像安装文档, 是特定产品(Atlas 800I A2/A3)特定场景(MindIE 推理)的示例; 其他产品/组件的挂载要求可能不同,以对应产品的官方文档为准; ② 官方明确说明"容器启动命令仅供参考,可根据需求自行修改"; ③ 本课未给出镜像名与标签清单——以官方镜像仓库实际提供的为准; ④ 标注「归纳」的解读(设备清单的作用、判断法)非官方口径。
附录 B · 本课出现的命令详解
docker run -it -d --name <name> ... {IMAGE_ID} bash
- 含义:以某个镜像启动一个容器。
- 拆解(常用部分):
| 片段 | 含义 |
|---|---|
-i |
保持标准输入打开(interactive) |
-t |
分配一个伪终端(tty) |
-d |
后台运行(detached),起好就返回 |
--name |
给容器命名(以后 docker exec 靠它) |
--net=host |
使用宿主机网络 |
--shm-size |
共享内存大小 |
--device=<路径>[:权限] |
把宿主机的设备交给容器(权限常见 r/rw/rwm) |
-v <宿主路径>:<容器路径>[:ro] |
挂载目录/文件(:ro = 只读) |
-w <路径> |
容器内的工作目录 |
| - 用法:见第五段的官方完整命令。 | |
- 期望结果:docker ps 能看到容器在跑。 |
|
- 不用 -d 会怎样:容器占着当前终端,退出终端容器就停。 |
ls /dev/ | grep davinci
- 含义:列出所有包含
davinci的设备节点,得到卡的数量与编号。 - 拆解:
ls /dev/(列出设备目录)+|(管道)+grep davinci(筛出含 davinci 的行)。 - 用法:
ls /dev/ | grep davinci - 期望结果:形如
/dev/davinci0、/dev/davinci1… 的列表。 - 官方口径:「挂载 davinci 设备时,首先通过该命令查询当前的 davinci 设备,
即
davinciX,X 即为 davinci 设备编号」。 - 不用会怎样:不知道有几个设备、编号是几 →
--device写多或写少。
docker images
- 含义:列出本机已有的镜像(含 IMAGE ID)。
- 用法:
docker images - 期望结果:表格里有
IMAGE ID一列。 - 官方用途:「镜像安装完成后,执行
docker images命令,即可查看对应 ID, 替换在上述命令中即可」 —— 也就是把{IMAGE_ID}换成实际 ID。 - 不用会怎样:不知道
docker run最后该写什么。
docker exec -it <容器名> bash
- 含义:进入一个已经在跑的容器(新开一个终端)。
- 拆解:
exec= 在已运行容器里执行命令;-it= 交互 + 终端;bash= 要执行的命令。 - 用法:
docker exec -it <container-name> bash - 为什么需要:官方命令用了
-d(后台起),所以要靠exec进去。 - 不用会怎样:容器在后台跑着,你进不去看。
npu-smi info(容器内)
- 含义:同 A1(查设备信息)。
- 本课的用法:这是"容器有没有正确拿到卡"的第一道验收。
- 期望结果:容器内能正常列出设备。
- ⚠️ 注意:如果
--device权限写成rw,空闲时可能正常,有任务占用时报错(官方原文)—— 别因为"刚才还是好的"就排除权限问题。
参数速查表
| 命令 / 参数 | 用途 |
|---|---|
ls /dev/ \| grep davinci |
查卡的数量与编号(决定写几行 --device) |
--device=...:rwm |
把设备交给容器(权限用 rwm) |
-v ...:ro |
挂载宿主机目录/文件到容器(只读) |
--shm-size |
共享内存大小(多模态高并发时官方建议 ≥500g) |
docker images |
查镜像 ID |
docker exec -it <名> bash |
进入运行中的容器 |
容器内 npu-smi info |
验证"卡接上了没" |
容器内 echo $ASCEND_HOME_PATH |
验证"CANN 环境配了没"(B1) |
附:B 层五课的分工(避免重复)
| 问题 | 在哪一课 |
|---|---|
| CANN 是什么、有哪些包、装在哪、环境变量怎么配 | B1 |
| 算子库装了什么、为什么按型号分、"找不到算子"怎么查 | B2 |
版本怎么配套、cann 软链接、升级/回退规则、冷补丁 |
B3 |
| 七种装法从哪来、各装什么、怎么选、离线包怎么装 | B4 |
容器里驱动在哪、怎么把卡交进去、rwm 为什么不能写 rw、镜像怎么来 |
本课(B5) |