昇腾部署第 D2 课:vLLM Ascend——把推理服务真正跑起来
编制:观澜|日期:2026-10-05|级别:T2 标准级 层级:D 层 · L3 服务(D 层第 2 课,前接 D1《L3 服务层》) 素材来源:vLLM Ascend 官方中文文档(快速开始) + 昇腾官方 MindIE 文档 数据纪律:硬件列表、镜像软件栈、docker run、验证命令与期望输出逐字出自官方文档;标注「归纳」的是便于记忆的整理,非官方口径。
第一段 · 章前定位
D1 给出结论:官方当前推荐 MindIE Motor + vLLM Ascend。
这一课落到最实在的一步:把服务跑起来。
而且它正好交付 A0 的判据 ④:
端口 + curl 服务起得来
这一课的路线是"三步走"(归纳,顺序即官方快速开始的顺序):
① 起容器 → ② 验证容器环境 → ③ 起服务并 curl 验证
为什么走容器这条路(归纳): 官方快速开始给的是预构建镜像方案 —— 因为镜像里已经把整条栈配好了(见第四段), 你不用自己去拼 CANN、PyTorch、TorchNPU、vLLM 的版本关系。
这一课也把前面九课的"版本地狱"一次性绕开 —— B3 讲的三方配套、C1 讲的五维对应,在这个镜像里已经被官方配好了。
第二段 · 学习目标
学完这一课,你应该能回答:
- vLLM Ascend 是什么(它和 vLLM 是什么关系)
- 那个预构建镜像里到底装了什么——为什么它能省掉大量配置
- 怎么选对镜像(硬件系列 → 标签后缀)
- 容器要挂哪些设备与目录(与 B5 讲的有什么异同)
- 怎么验证"容器就绪"和"服务真的起了"(官方给了明确判据)
第三段 · vLLM Ascend 是什么
先看官方怎么描述它
vLLM Ascend 官方文档站点的自我描述(原文):
vLLM Ascend plugin — a community-maintained hardware plugin for running vLLM on the Ascend NPU.
翻译过来:vLLM Ascend 插件 —— 一个社区维护的、用于在昇腾 NPU 上运行 vLLM 的硬件插件。
⭐ 这个模式和 C1 完全一样(归纳)
| C1 的 TorchNPU | 本课的 vLLM Ascend | |
|---|---|---|
| 本体是谁的 | PyTorch(PyTorch 官方的) | vLLM(vLLM 项目的) |
| 昇腾这部分 | TorchNPU 插件 | vLLM Ascend 插件 |
| 装法 | 本体 + 插件 | 本体 + 插件 |
| 包名/命名 | torch + torch_npu |
vllm + vllm_ascend |
归纳:昇腾对"别人的框架/引擎"的接入方式,一直是"插件" —— 不是 fork、不是魔改版。 记住这个模式,你在昇腾生态里看任何组件都能先问一句:"这是本体还是插件?"
vLLM 在整套架构里的位置
结合 D1 的图:
用户的 HTTP 请求
↓
┌──────────────────────┐
│ 服务化框架 MindIE Motor │ ← 管分发/调度/可靠性(D1、D3)
└──────────────────────┘
↓
┌──────────────────────┐
│ 推理引擎 vLLM + │ ← 本课:vLLM 是引擎,
│ vLLM Ascend 插件 │ 插件让它跑在昇腾上
└──────────────────────┘
↓
┌──────────────────────┐
│ TorchNPU / PyTorch │ ← C 层
└──────────────────────┘
↓
CANN(B 层)→ 驱动(A 层)→ 芯片
归纳:vLLM Ascend 是"引擎侧"的昇腾适配层, 它下面还要经过 PyTorch/TorchNPU、CANN、驱动 —— 这就是为什么"一个服务起不来"可能有一堆原因:它压着整摞栈。
第四段 【如果我来做】从查卡到跑通一次推理
4.0 我会按这个顺序做(顺序错了会白干)
接到"把这台机器上的 vLLM Ascend 跑起来"这个任务,我不按知识点的顺序, 按"手伸进去的顺序"排动作 —— 下面八步,每一步都能在后面某一段找到对应命令:
① 先摸底(不装任何东西,先查事实)—— 是哪个系列的卡?宿主机驱动装了没?
② 定方案(做哪一种)—— 卡型 + 宿主机发行版,两个条件定下镜像标签
③ 预热(能在别处先跑一遍就先跑)—— 先把镜像拉下来,拉的过程就是预热
④ 动手(每步立刻验,不攒着一起验)—— 起容器(--device 与权限),起完立刻在容器里验
⑤ 难验证的(找【反面证据】)—— 容器内跑官方校验,认准那一行 OK
⑥ 收口(用完整流程跑一遍当验收)—— 起服务 → 四行日志 → 发一个真实请求
⑦ 出证据(留下可给别人看的东西)—— 启动日志 + curl 返回 + 生成文本
⑧ 诚实(官方没给答案的地方明写"这里要自己查")—— --shm-size 取值、你自己的模型路径
⭐ 这个顺序里最容易被跳过的是 ① 和 ⑤:
- ①:不查卡型就选镜像 —— 卡型是【不可返工的事】(A2 讲过), 标签选错,镜像拉回来就是起不来,而且报的错看不出是标签的问题;
- ⑤:只看"容器进去了、没报错"就当好了 —— 没看到
vLLM Ascend environment: OK这一行, 后面全是白干(--device的坑很隐蔽:容器能进,但里面看不见卡)。
每一步落到哪一段:
| 步 | 我会做什么 | 命令在哪 |
|---|---|---|
| ① | 摸底:卡型 + 驱动/固件版本 | 第五段 ① |
| ② ③ | 选镜像标签、拉镜像(含国内加速) | 第五段 ② ③ |
| ④ | 起容器 | 第六段 |
| ⑤ | 容器就绪校验(认那一行 OK) | 第七段 第 ① 步 |
| ⑥ | 起服务 + 发真实请求 | 第七段 第 ② ③ 步 |
| ⑦ | 出证据(日志 / JSON / 生成文本) | 第七段 第 ③ ④ 步 |
| ⑧ | 官方没给、要自己查的地方 | 第八段 |
4.1 第 ② 步要用的底料:为什么走容器这条路
承上:第 ① 步(摸底)的命令放在第五段 ①,因为那里正好要给"按卡选镜像"。 本段先把这个方案为什么能省事说清 —— 这是后面所有动作的依据。
官方快速开始的原文:
vLLM Ascend 镜像中包含的软件栈: 预构建镜像包含经过验证的 Python 和 Ascend 用户态软件栈, 包括 CANN、NNAL、PyTorch、TorchNPU、vLLM 和 vLLM Ascend。 A2、A3 和 Ascend 950DT 系列产品的镜像还包含匹配的 Triton Ascend 运行时。 Atlas 300I DUO 和 Atlas 200I Pro 不使用 Triton Ascend。
把这份清单对回我们的课件体系(归纳)
| 镜像里包含的 | 属于哪一层 | 我们哪一课讲过 |
|---|---|---|
| CANN | L1 基础软件 | B1 / B2 / B3 / B4 |
| NNAL | L1(CANN 里的加速库) | B1(NNAL 含 ATB、SiP) |
| PyTorch | L2 框架 | C1 |
| TorchNPU | L2(PyTorch 的昇腾插件) | C1 |
| vLLM | L3(推理引擎本体) | 本课 |
| vLLM Ascend | L3(引擎的昇腾插件) | 本课 |
| (驱动/固件) | L0 | A1(不在镜像里,在宿主机) |
⭐ 这张表说明了一件很重要的事:
一个容器镜像,就把 L1 + L2 + L3 三层配好了一起给你。 只剩 L0(驱动/固件)要你自己在宿主机上装好(这与 B5 的结论一致)。
为什么这个方案这么有价值(归纳): 因为它绕开了我们前面几课反复强调的"版本地狱" —— B3 讲驱动/固件/CANN 三方配套、C1 讲框架层的五维对应…… 官方把这些组合验证过、打成一个镜像发出来。 你只要选对镜像标签(下一段),这些对应关系就不用自己推了。
⚠️ 但要注意:官方也说了"有关经过验证的确切版本,请参阅安装指南 > 硬件和软件栈" —— 镜像里的版本是官方组合验证过的,不是你随便挑的。 想换其中某个组件的版本,就要回到 B3/C1 那套配套规则去推。
第五段 · 怎么选对镜像
① 先确认你的硬件在支持列表里(官方表)
官方给出的支持硬件:
| 类型 | 常见产品(官方原文举例) |
|---|---|
| Atlas A2 系列产品 | Atlas 800T A2、Atlas 900 A2 PoD、Atlas 200T A2 Box16、Atlas 300T A2、Atlas 800I A2 等 |
| Atlas A3 系列产品 | Atlas 800T A3、Atlas 900 A3 SuperPoD、Atlas 9000 A3 SuperPoD、Atlas 800I A3 等 |
| Atlas 推理系列产品 | Atlas 300I DUO 和 Atlas 200I Pro |
| Ascend 950DT 系列产品 | 950DT |
⭐ 这一步与 A2 直接呼应: A2 教你"先搞清手上是哪张卡"——现在你知道为什么了,因为镜像也是按系列走的。
怎么"查卡"(第 ① 步的两条命令,在宿主机上敲)
先查有没有卡,再查是哪张卡 —— 这两条查完,第 ② 步的标签才定得下来。
命令 ls /dev/davinci*
含义:看系统里有没有昇腾的设备节点 —— 判断"这台机器到底有没有卡"的第一眼 拆解:
ls(列出目录内容)+ 通配符*(匹配所有以/dev/davinci开头的节点) 用法:ls /dev/davinci*,数清楚列出来几个 期望:列出/dev/davinci0(单卡)或多个节点;A3 是双 DIE,正常应列出两个(如/dev/davinci0/dev/davinci1) 不过会怎样:报No such file or directory→ 驱动没装或没起来(回 A1);A3 只列出一个 → 后面按一个挂,容器里只认一半(见第六段)
命令 npu-smi info -t board -i 0
含义:一次读出第 0 号设备的产品型号 + 驱动版本 + 固件版本 —— 卡型和版本都要在这里定死 拆解:
npu-smi(昇腾设备管理工具)+info(查信息子命令)+-t board(查什么:板卡信息)+-i 0(查哪台:设备编号 0) 用法:先用npu-smi info -l看有哪些设备,再逐个-i <设备ID>查 期望:输出里Product Type(型号)= 你的卡(如 Atlas 800T A2 / Atlas 800T A3);Software Version= 驱动版本;Firmware Version= 固件版本 不过会怎样:报npu-smi: command not found→ 驱动(官方全名「驱动 & npu-smi 工具包」)没装(回 A1);-i填了不存在的设备号 → 报设备不存在 ⚠️ 这一步是【不可返工的事】:卡型定错 → 镜像标签选错 → 拉回来起不来为什么摸底要放在"装任何东西"之前:卡型和版本是不用装东西就能查到的客观事实, 而"要不要装环境"取决于它。先查客观条件、再决定动作 —— 这与 B4「选择题的答案由客观条件决定」同构。
② 选镜像标签(官方原文)
镜像地址形态:quay.io/ascend/vllm-ascend:<TAG>
官方给的启动命令示例里的标签:
- Ubuntu:quay.io/ascend/vllm-ascend:v0.23.0
- openEuler:quay.io/ascend/vllm-ascend:v0.23.0-openeuler
- A3 场景:quay.io/ascend/vllm-ascend:v0.23.0-a3(及 -a3-openeuler)
官方明确提到标签后缀有这些:
保留完整的原始镜像标签,包括诸如
-a3、-310p、-950dt和-openeuler等后缀。
归纳:标签里两类信息(归纳):
| 后缀 | 表示什么 |
|---|---|
-a3 / -310p / -950dt |
对应的硬件系列 |
-openeuler |
对应容器内操作系统(不加则 Ubuntu) |
⭐ 判断法:先看你机器是什么系列(A2/A3/300I/950DT),再看宿主机是什么发行版 —— 两个都定了,标签只剩一个。 (这与 B4 的"装法选择"同构:选择题的答案由客观条件决定,不是偏好。)
③ 国内下载加速(官方给的方法)
vLLM Ascend 镜像默认从 quay.io 下载。如果直接访问速度较慢, 可以使用以下任一镜像仓库来加速下载。
官方给的替换方式(原文):
# Replace with tag you want to pull
TAG=v0.23.0
# use
docker pull m.daocloud.io/quay.io/ascend/vllm-ascend:$TAG
# or
docker pull quay.nju.edu.cn/ascend/vllm-ascend:$TAG
⚠️ 官方提醒(很重要):
仅替换镜像仓库前缀,并保留完整的原始镜像标签, 包括诸如
-a3、-310p、-950dt和-openeuler等后缀。
归纳:只换前面那截地址,后面 <TAG> 一个字都不能动 ——
动了就是另一个镜像(对应错的硬件/系统)。
拉镜像(第 ③ 步的命令)
命令 docker pull m.daocloud.io/quay.io/ascend/vllm-ascend:v0.23.0-a3
含义:把官方预构建镜像拉到本机(这里演示 A3 + 国内加速前缀) 拆解:
docker pull(拉镜像)+m.daocloud.io/(国内加速仓库前缀,官方给的其中一个)+quay.io/ascend/vllm-ascend(原始仓库,一个字没动)+:v0.23.0-a3(标签,-a3是硬件系列后缀) 用法:TAG=v0.23.0-a3; docker pull m.daocloud.io/quay.io/ascend/vllm-ascend:$TAG;只用官方给的两个前缀(m.daocloud.io/quay.nju.edu.cn) 期望:逐层下载,最后拉到本机;用docker images | grep vllm-ascend能看到这个镜像(能看到才算成功) 不过会怎样:报manifest unknown/not found→ 标签写错(把-a3/-310p/-950dt/-openeuler改掉或漏掉,就变成另一个不存在的镜像);拉到一半断网 → 重跑docker pull会接着下 ⚠️ 注意:官方快速开始没有给出docker pull的输出截图,上表"期望"是按 docker 的通用行为写的(能看到镜像即成功),非官方原文。
第六段 · 起容器:官方命令逐行拆解
官方的容器启动命令(原文)
export DEVICE=/dev/davinci0
export MODEL_CACHE="${HOME}/.cache"
mkdir -p "$MODEL_CACHE"
docker run --rm \
--name vllm-ascend \
--shm-size=1g \
--device $DEVICE \
--device /dev/davinci_manager \
--device /dev/devmm_svm \
--device /dev/hisi_hdc \
-v /usr/local/dcmi:/usr/local/dcmi \
-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
-v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ \
-v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info \
-v /etc/ascend_install.info:/etc/ascend_install.info \
-v "$MODEL_CACHE:/root/.cache" \
-p 8000:8000 \
-it "$IMAGE" bash
起容器(第 ④ 步的命令,与上方官方原文是同一条)
命令 docker run --rm --name vllm-ascend --shm-size=1g --device "$DEVICE" --device /dev/davinci_manager --device /dev/devmm_svm --device /dev/hisi_hdc -v /usr/local/dcmi:/usr/local/dcmi -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ -v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info -v /etc/ascend_install.info:/etc/ascend_install.info -v "$MODEL_CACHE:/root/.cache" -p 8000:8000 -it "$IMAGE" bash
含义:起一个交互式容器,把昇腾设备 / 驱动挂载 / 模型缓存 / 8000 端口一次交给它(
$IMAGE、$DEVICE、$MODEL_CACHE要先按前面的export设好) 拆解:四组参数 —— ① 设备:--device "$DEVICE"(卡;若宿主机上已有任务占用该设备,要按 B5 讲的带rwm权限,否则容器起不来)+/dev/davinci_managerdevmm_svmhisi_hdc(设备/内存/通信管理,三个都必需) ② 挂载:dcmi+npu-smi(工具)+ 驱动lib64/与version.info+/etc/ascend_install.info(驱动/固件的安装信息)+$MODEL_CACHE:/root/.cache(模型缓存) ③ 网络:-p 8000:8000(把容器 8000 端口映射到宿主机 8000) ④ 其他:--shm-size=1g(共享内存,快速开始用小模型够用)+--rm(退出即删容器)+-it ... bash(进交互式 shell) 用法:照抄官方那一整段(含前面的export与mkdir -p);A3 把--device写两张卡(见本节 A3 特殊要求) 期望:命令不报错,直接进到容器内的 shell 提示符(如root@xxxx:/#)—— 进到 shell 就说明容器起来了 不过会怎样:报could not select device driver/ 权限被拒 →--device或权限写法问题(回 B5);报设备不存在 → 宿主机/dev/davinci*就没有(回第五段 ① 摸底);端口被占用 → 改-p 8001:8000并同步改后面 curl 的端口⚠️ 进到 shell ≠ 成功:容器起来了不代表里面看得见卡 —— 必须接着做第 ⑤ 步(第七段那行
OK)。
与 B5 学的对照(这是本段最有价值的部分)
B5 讲的是 MindIE 场景的 docker run,本课是 vLLM Ascend 场景的。
把它们并排看,就能看出"哪些是必需的、哪些是场景特有的":
| 挂载/参数 | B5(MindIE 场景) | 本课(vLLM Ascend 场景) | 说明 |
|---|---|---|---|
/dev/davinciN |
✅ | ✅ | 必需(卡) |
/dev/davinci_manager |
✅ | ✅ | 必需(设备管理) |
/dev/devmm_svm |
✅ | ✅ | 必需(内存管理) |
/dev/hisi_hdc |
✅ | ✅ | 必需(通信通道) |
/usr/local/dcmi |
✅ | ✅ | 必需(DCMI 接口) |
/usr/local/bin/npu-smi |
✅ | ✅ | 必需(npu-smi 工具) |
/usr/local/sbin/ |
✅ | 本命令未挂 | 视场景需要 |
| 驱动目录 | /usr/local/Ascend/driver(整目录) |
/usr/local/Ascend/driver/lib64/ + driver/version.info |
挂法不同:一个挂整目录,一个挂具体文件 |
| 安装信息文件 | /etc/Ascend/ascend_cann_install.info |
/etc/ascend_install.info |
⚠️ 两个不同的文件,见下 |
| 模型目录 | /home/weight |
$MODEL_CACHE:/root/.cache |
场景不同 |
| 端口 | --net=host |
-p 8000:8000 |
网络方式不同 |
--shm-size |
500g(MindIE 场景官方建议) |
1g |
差别大,见下 |
⚠️ 一个必须分清的细节:两个"安装信息文件"
这两个文件名很像,但不是同一个东西:
| 文件 | 记的是谁的安装信息 | 出现在哪个场景 |
|---|---|---|
/etc/ascend_install.info |
驱动/固件 的安装信息 | 本课(vLLM Ascend 挂它) |
/etc/Ascend/ascend_cann_install.info |
CANN 的安装信息(B3 讲过) | B5(MindCluster ToolBox 挂它) |
⭐ 归纳:
ascend_install.info不带cann→ 是驱动侧的; 带cann→ 是 CANN 侧的。 看官方命令时别把这两个当成同一个文件写错(写错的后果是服务启动时找不到对应信息)。
关于 --shm-size 的差别(归纳)
| 场景 | 官方给的取值 |
|---|---|
| MindIE 推理(B5) | 500g(官方还说多模态高并发时建议不小于 500g) |
| vLLM Ascend 快速开始(本课) | 1g |
这说明 --shm-size 不是固定值,而是跟场景/负载走的:
快速开始用的是小模型(Qwen3-0.6B),1g 够;真实业务要按负载调。
别把某个教程里的数值当标准值抄。
⭐ A3 的一个特殊要求(官方原文)
A3 容器启动要求:A3 采用双 DIE 设计,需要两个昇腾设备节点, 例如
/dev/davinci0和/dev/davinci1。
归纳:如果你的机器是 A3,--device 要写两张卡 ——
export DEVICE0=/dev/davinci0
export DEVICE1=/dev/davinci1
# ... docker run ... --device "$DEVICE0" --device "$DEVICE1" ...
推测这一步为什么容易栽(归纳):A3 的"一张卡"在系统里表现为两个设备节点。 按 A2 只挂一个,容器里就只认一半。 这条与 A2(产品形态)和 B5(容器挂载)直接呼应。
第七段 · 三步验证:容器 → 环境 → 服务
第 ① 步:验证容器环境(官方命令与判据)
官方原文:
在容器中运行以下命令。当输出包含
vLLM Ascend environment: OK时,表示容器已准备就绪。
npu-smi info
python3 - <<'PY'
import torch
import vllm
import vllm_ascend
assert torch.npu.is_available(), "No available Ascend NPU detected in the container"
print("vLLM Ascend environment: OK")
PY
把这段落成"命令 + 结果" —— 它其实是两条命令(先查卡,再验栈):
命令 npu-smi info
含义:在容器里跑它,看容器能不能看见宿主机交进来的卡(L0 到没到容器里) 拆解:
npu-smi(昇腾设备管理工具)+info(显示设备信息) 用法:进容器后直接敲;要读版本就加-t board -i 0期望:打印一张设备表(型号 / 温度 / 显存 / 进程)—— 有表就说明卡交给了容器 不过会怎样:报command not found→ 没挂/usr/local/bin/npu-smi(回第六段);有命令但列不出卡 →--device漏挂(回第六段 / B5 的rwm)
命令 python3 - <<'PY'(heredoc,正文就是上方官方那段)
含义:验"整摞栈能不能在卡上跑起来" —— 这是容器就绪的官方判据命令 拆解:
python3 -(从标准输入读代码)+<<'PY'(heredoc:一直读到PY这一行为止;'PY'加引号表示不做变量展开) 用法:在容器终端把官方那段(import torch/vllm/vllm_ascend+assert+vLLM Ascend environment: OK—— 这一行就是官方的"容器已准备就绪"判据 不过会怎样:报AssertionError: No available Ascend NPU detected in the container→ 容器里看不到卡(回第六段--device/ A1 驱动);报ModuleNotFoundError→ 不在 vLLM Ascend 镜像里(回第五段选镜像)
⭐ 这条命令一口气验证了整摞栈(归纳):
| 导入/断言 | 验证了哪一层 |
|---|---|
npu-smi info 有输出 |
L0(驱动在宿主机、且卡交给了容器) |
import torch |
L2(PyTorch) |
import vllm |
L3(引擎本体) |
import vllm_ascend |
L3(昇腾插件) |
torch.npu.is_available() 不为假 |
L2 的 TorchNPU + L1 的 CANN(C1 讲过的判据) |
print(...) 打印出 OK |
以上全通 |
官方还给了失败时的提示语:"No available Ascend NPU detected in the container" ——
看到这句,就是"容器里看不到卡",回 B5(--device 挂错/漏挂)或 A1(宿主机驱动)。
第 ② 步:起服务(官方命令)
官方原文(在线服务):
vllm serve Qwen/Qwen3-0.6B &
命令 vllm serve Qwen/Qwen3-0.6B &
含义:起 vLLM 的在线推理服务(放到后台跑) 拆解:
vllm(命令行入口)+serve(起服务子命令)+ 模型参数(模型名或本地路径)+&(放到后台) 用法:默认起在 8000 端口(正好对应第六段的-p 8000:8000);想看前台输出就去掉末尾&期望:看到下面那四行日志 —— 关键是Application startup complete和Uvicorn running on http://0.0.0.0:8000不过会怎样:日志停在Waiting for application startup→ 正在初始化(可能只是慢);久等不过 → 查模型路径与显存;报端口被占用 → 换端口并同步改-p
官方给出的"成功日志":
INFO: Started server process [3594]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
归纳:这四行分别说明什么(归纳):
| 日志行 | 含义 |
|---|---|
Started server process |
进程起来了 |
Waiting for application startup |
正在初始化(这一步最慢,要等) |
Application startup complete |
应用初始化完成 ← 关键 |
Uvicorn running on http://0.0.0.0:8000 |
端口在听了 ← 关键 |
注意端口 8000 —— 这正是第六段 -p 8000:8000 映射出来的那个端口。
第 ③ 步:curl 验证(官方命令与判据)
官方原文:
# 查模型列表
curl --fail http://localhost:8000/v1/models
# 发一个提示
curl --fail http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{"model":"Qwen/Qwen3-0.6B","prompt":"Beijing is a","max_tokens":5,"temperature":0}'
命令 curl --fail http://localhost:8000/v1/models
含义:查服务当前挂了哪些模型 —— 最快的"服务活着吗"检查 拆解:
curl(发一次 HTTP 请求)+--fail(4xx/5xx 直接返回非零退出码)+/v1/models(模型列表接口) 用法:curl --fail -s http://localhost:8000/v1/models | python3 -m json.tool(格式化看) 期望:返回 JSON,data里有Qwen/Qwen3-0.6B这条记录 不过会怎样:报curl: (7) Failed to connect→ 服务没起或端口没映射(查第六段-p);报curl: (22) … 404/500→--fail把错误显出来了(不加--fail时 404 也看着像成功)
命令 curl --fail http://localhost:8000/v1/completions -H "Content-Type: application/json" -d '{"model":"Qwen/Qwen3-0.6B","prompt":"Beijing is a","max_tokens":5,"temperature":0}'
含义:真的发一次推理请求(这才是"服务能不能干活"的验证) 拆解:
-H(加请求头:声明 body 是 JSON)+-d(请求体:model模型 /prompt提示 /max_tokens最多生成几个词 /temperature:0不随机) 用法:照抄官方这条;换问题只改prompt期望:响应里choices[0].text有非空生成文本 —— 这才是推理成功的官方判据 不过会怎样:返回 空补全 → 官方明说「错误响应或空补全不能确认推理成功」→ 查模型与参数;返回错误结构 → 回看服务日志
⭐⭐ 官方给了一条极重要的判据(原文):
确认响应中
choices[0].text包含非空生成文本。 错误响应或空补全不能确认推理成功。
归纳这条为什么重要:
"端口通了"不等于"推理成了"。
一个 HTTP 200 的响应,可能是空补全、可能是错误结构 ——
官方明确要求"看 choices[0].text 里有没有非空的生成文本"。
这一条正好把 A0 的判据 ④ 说完整了: A0 说的是"端口 + curl 服务起得来" —— 而官方在这里加了一句:"返回空内容不算数"。
第 ④ 步(加分):优雅停止(官方原文)
kill -2 $VLLM_PID # 类似于在前台 vLLM 进程中按 Ctrl+C
命令 VLLM_PID=$(pgrep -f "vllm serve"); kill -2 "$VLLM_PID"
含义:优雅停掉后台的 vLLM 服务(等价于前台按
Ctrl+C) 拆解:pgrep -f "vllm serve"(按完整命令行匹配并打出进程号)+kill -2(发 SIGINT 信号,也就是信号 2) 用法:官方写法是分两行VLLM_PID=$(pgrep -f "vllm serve")再kill -2 "$VLLM_PID";kill 前先确认这个 PID 是本示例的服务 期望:官方给的输出四行 ——Shutting down FastAPI HTTP server./Shutting down/Waiting for application shutdown./Application shutdown complete.不过会怎样:pgrep -f "vllm serve"可能同时匹配到别的 vLLM 进程 → 会误停别人的服务(官方特意提醒);杀不动 → 别直接上-9,先回看是不是还有别的前台进程占着
⚠️ 官方特意提醒(原文):
停止服务前请确认进程:如果当前环境中运行着其他
vllm serve进程,pgrep -f "vllm serve"也可能匹配到这些 vLLM 服务。 在运行kill之前,请确认VLLM_PID是本示例启动的服务,以免误停其他正在运行的 vLLM 进程。
归纳:kill -2(而不是 kill -9)给程序机会做优雅退出;
而"先确认 PID"这条提醒,正是我们之前踩过的坑类型(pkill -f 脚本名 会匹配到自己)。
特定硬件的额外要求(官方原文,Atlas 300I DUO / 200I Pro)
此硬件路径的运行时要求:使用
float16;使用FULL_DECODE_ONLY并限制图捕获大小; 不支持enable_npugraph_ex。
vllm serve Qwen/Qwen3-0.6B \
--dtype float16 \
--max-num-seqs 8 \
--compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY","cudagraph_capture_sizes":[1,2,4,8]}' \
--additional-config '{"ascend_compilation_config":{"enable_npugraph_ex":false}}' &
归纳:同样是昇腾,不同硬件系列的启动参数不一样 —— 这与 B2(ops 包按型号分)、C1(五维版本对应)是同一个规律: "先确认是哪张卡,再查它需要什么"。
⚠️ 数据纪律提示:上例是官方针对 Atlas 300I DUO / 200I Pro 给出的示例。 其他系列的参数要求以官方文档对应条目为准,本课不做推广性表述。
第 ⑤ 步(另一条路):离线推理 —— 不开服务也能验一次生成
为什么单列这一步:第 ② ③ 步验的是"服务链路通不通"; 离线推理验的是"引擎本身能不能出文字"。两条路的判据不一样,但都认一条:生成文本非空。
官方原文:以下 Qwen3-0.6B 示例已使用默认模型加载配置验证通过。
在容器终端中,使用以下代码创建 example.py:
from vllm import LLM, SamplingParams
prompts = [
"Hello, my name is",
"The future of AI is",
]
sampling_params = SamplingParams(
temperature=0.0,
max_tokens=32,
)
llm = LLM(model="Qwen/Qwen3-0.6B")
outputs = llm.generate(prompts, sampling_params)
assert len(outputs) == len(prompts)
for output in outputs:
generated_text = output.outputs[0].text
assert generated_text.strip()
print(f"Prompt: {output.prompt!r}, Generated text: {generated_text!r}")
命令 python3 example.py
含义:跑上面那段离线推理示例 —— 不起服务,直接看引擎能不能出文字 拆解:
example.py是官方给的示例脚本(用LLM(...).generate(...))+python3执行它 用法:先在容器里把上面的example.py建好,再python3 example.py期望:先出现插件加载行,其中Platform plugin ascend is activated说明 vLLM 认出了昇腾平台;随后打印Prompt: 'Hello, my name is', Generated text: '…',生成文本非空 不过会怎样:看不到Platform plugin ascend is activated→ 插件没加载(不在 vLLM Ascend 镜像里);由assert generated_text.strip()抛AssertionError→ 生成结果为空(与"空补全不算成功"同一条判据)
官方还提示了一条"看着像报错、其实不是"的输出(原文):
以下消息显示进程在离线推理后退出,不影响推理结果:
(EngineCore pid=970) INFO 05-12 11:36:00 [core.py:1201] Shutdown initiated (timeout=0)
(EngineCore pid=970) INFO 05-12 11:36:00 [core.py:1224] Shutdown complete
ERROR 05-12 11:36:01 [core_client.py:704] Engine core proc EngineCore died unexpectedly, shutting down client.
sys:1: DeprecationWarning: builtin type swigvarlink has no __module__ attribute
归纳:这几行是官方明说"不影响结果"的收尾输出 —— 排障时别把它当成故障(见第八段报错清单)。
如果 Hugging Face 访问受限(官方给的切换法)
官方原文:
如果您的环境无法稳定访问 Hugging Face,模型下载可能会因连接超时、DNS 错误或其他网络问题而失败。 您可以切换到 ModelScope:
命令 export VLLM_USE_MODELSCOPE=True
含义:把模型下载源从 Hugging Face 切到 ModelScope(国内可达) 拆解:
export(定义环境变量,只在当前终端有效)+VLLM_USE_MODELSCOPE(vLLM 认的开关) 用法:先export VLLM_USE_MODELSCOPE=True,再pip install "modelscope>=1.18.1,<1.38";若模型已下到本地,直接把示例里的模型 ID 换成本地目录即可,不用设这个变量 期望:之后vllm serve/LLM(...)从 ModelScope 取模型,不再因 Hugging Face 连不上而失败 不过会怎样:仍报连接超时 / DNS 错误 → 变量没生效(重开终端要重设)或 modelscope 没装成功
第八段 · 出问题怎么查
按"三步"定位
① 容器起不来 / 容器里跑不了 → 本课第六段(挂载)+ B5(容器原理)
② 容器能跑,但环境断言失败 → 看官方提示语:"No available Ascend NPU detected"
③ 服务起不来 / 端口不通 → 看那四行日志停在哪一句
④ 服务起来了但返回空/报错 → 官方判据:看 choices[0].text
排查表
| 现象 | 先查什么 |
|---|---|
容器里 npu-smi 没输出 |
宿主机驱动(A1)+ --device 与 -v 挂载(本课第六段 / B5) |
打印 No available Ascend NPU detected in the container |
容器里看不到卡 —— 设备挂载问题 |
import vllm_ascend 失败 |
镜像版本不对(本课第五段)/ 没在 vLLM Ascend 镜像里 |
日志停在 Waiting for application startup |
正在初始化(可能只是慢);久等不过 → 查模型路径与显存 |
日志有 Application startup complete 但 curl 连不上 |
端口(-p 8000:8000)与监听地址 |
| curl 返回 200 但没有生成文本 | 官方判据:空补全≠成功 → 查模型与参数 |
A3 机器只挂了一个 davinci |
A3 是双 DIE,需要两个设备节点(本课第六段) |
| 想 kill 服务但怕杀错 | 官方提醒:先确认 VLLM_PID 是本示例的(别的 vllm serve 也会被 pgrep 匹配到) |
| 镜像下载很慢 | 换国内镜像仓库前缀,但标签一个字不能改 |
⭐ 一个最省时间的判断法
服务起不来,先看"卡在哪一步",而不是"报的什么错"。
| 卡在哪 | 层次 |
|---|---|
容器里 npu-smi 就没输出 |
L0(驱动 / 容器挂载) |
torch.npu.is_available() 为假 |
L1/L2(CANN / TorchNPU) |
import vllm_ascend 失败 |
L3 的安装(镜像) |
| 日志起不来 / 端口不通 | L3 的启动与配置 |
| 起来了但结果不对 | 参数/模型/硬件适配(不是"没装好") |
这五档的分工,比任何一条报错文本都更能指方向。
常见报错清单(报错原文 + 往哪查)
下表前两条是官方正文里出现的原文;其余是按本课流程归纳的排查方向(非官方报错编号)。 查更多报错用官方案例库:
https://www.hiascend.com/document/caselibrary—— 检索时去掉路径 / pid / 时间戳,只留稳定特征词。
| 报错 / 现象(原文优先) | 官方给的处置 / 往哪查 |
|---|---|
AssertionError: No available Ascend NPU detected in the container |
官方给的失败提示语 → 容器里看不到卡:--device 挂错/漏挂(第六段 / B5),或宿主机驱动没装(A1) |
ERROR [core_client.py:704] Engine core proc EngineCore died unexpectedly, shutting down client. |
官方明说:这是离线推理结束后进程退出的提示,不影响推理结果 —— 别当故障(第七段 第 ⑤ 步) |
npu-smi: command not found(容器内) |
没挂 /usr/local/bin/npu-smi(第六段) |
import vllm_ascend 失败 / ModuleNotFoundError |
拉错镜像、或没在 vLLM Ascend 镜像里(第五段) |
日志停在 Waiting for application startup. |
正在初始化(最慢一步,可能只是慢);久等不过 → 查模型路径 / 显存 |
有 Application startup complete 但 curl 连不上 |
端口 -p 8000:8000 与监听地址(第六段 / 第七段 ③) |
curl 返回 200 但 choices[0].text 为空 |
官方判据:空补全不算推理成功 → 查模型与参数 |
manifest unknown / 镜像拉不到 |
标签写错(-a3/-310p/-950dt/-openeuler 被改动)→ 只换仓库前缀(第五段 ③) |
pgrep -f "vllm serve" 匹配到多个进程 |
官方提醒:kill 前确认 VLLM_PID 是本示例的服务(第七段 ④) |
| A3 机器容器里只认一半的卡 | A3 双 DIE,需要两个设备节点(第六段) |
| 模型下载超时 / DNS 错误 | Hugging Face 不可达 → 切 ModelScope(第七段"如果 Hugging Face 访问受限") |
⭐ 报错位置 ≠ 问题位置:在 L3 报的错,根因常在 L0/L1 —— 先按上面"五档"看卡在哪一步,再查对应层(这正是 E1 的定位法)。
第九段 · 小结与下一课
三句话带走
- vLLM Ascend 是"社区维护的硬件插件",让 vLLM 能跑在昇腾上 —— 和 C1 的 TorchNPU 是同一个接入模式(本体 + 插件)。
- 那个预构建镜像里打包了 CANN、NNAL、PyTorch、TorchNPU、vLLM、vLLM Ascend —— L1+L2+L3 一次配好,只剩 L0(驱动)要你在宿主机装。 这等于官方帮你把 B3/C1 讲的"版本地狱"绕开了。
- 验证要过三关:容器就绪(
vLLM Ascend environment: OK)→ 服务启动(看到Application startup complete+Uvicorn running on)→ 推理真出来(choices[0].text非空)。 官方明说:错误响应或空补全不能确认推理成功。
补充一条:不想起服务时,官方还给了离线推理一条路(
python3 example.py)—— 它验的是"引擎本身能不能出文字",判据同样是生成文本非空 (见第七段 第 ⑤ 步);模型源连不上则切 ModelScope。
本课在体系里的位置
─── L0 硬件与环境(A 层,已完成)─── A0 / A1 / A2 / A3
─── L1 基础软件(B 层,已完成)─── B1 / B2 / B3 / B4 / B5
─── L2 框架(C 层,已完成)─── C1 / C2 / C3
─── L3 服务(D 层)───
D1 L3 服务层:推理服务是怎么跑起来的 ✅
D2 vLLM Ascend:把推理服务真正跑起来 ★ 本课
D3(拟)MindIE Motor:五个子组件各干什么
D4(拟)PD 分离与混部:为什么把 Prefill 和 Decode 拆开
D5(拟)服务起停、端口与调优
─── 排障(E 层)─── E1
本课留下的"钩子"
| 钩子 | 在哪一课填 |
|---|---|
| 单机跑通了,多机/集群怎么调度 | D3(Motor 的 Coordinator / Deployer) |
| PD 分离怎么配(官方有 Mooncake 方案) | D4 |
| 多实例、并发、吞吐怎么调 | D5 |
| 服务日志在哪、怎么排 | E 层 |
课后自测(3 题)
- 为什么"起一个 vLLM Ascend 服务"只需要拉个镜像就能省掉大量配置?镜像里到底装了什么?
- 官方给的"容器就绪"判据是什么字符串?"服务起来了"看哪两行日志?
- curl 返回了 HTTP 200,能不能说"推理成功了"?官方怎么规定的?
附录 A · 本课引用的官方出处
| 内容 | 出处 |
|---|---|
| vLLM Ascend 的自我描述(community-maintained hardware plugin) | vLLM Ascend 官方文档站首页 meta 描述https://docs.vllm.ai/projects/ascend/ |
| 环境要求(Linux / Python >= 3.10, < 3.13 / Docker) | vLLM Ascend 官方《快速开始》https://docs.vllm.ai/projects/ascend/zh-cn/latest/quick_start.html |
| 支持硬件列表(A2 / A3 / 推理系列 / 950DT) | 同上 |
| 镜像包含的软件栈(CANN、NNAL、PyTorch、TorchNPU、vLLM、vLLM Ascend;Triton Ascend 的适用范围) | 同上 |
| 镜像仓库加速方法与「仅替换前缀、保留完整标签」的提醒 | 同上 |
容器启动命令(设备、挂载、-p 8000:8000、--shm-size) |
同上 |
| A3 双 DIE 需要两个设备节点 | 同上(A3 容器启动要求) |
| 容器环境验证命令与「vLLM Ascend environment: OK」判据 | 同上 |
| 在线服务启动命令与成功日志四行 | 同上 |
| curl 验证的两条命令 | 同上 |
镜像拉取命令(export IMAGE=… + docker pull "$IMAGE") |
同上 |
离线推理示例(example.py)与输出(Platform plugin ascend is activated、Generated text) |
同上 |
| 「进程在离线推理后退出,不影响推理结果」的说明 | 同上 |
Hugging Face 受限时切 ModelScope(VLLM_USE_MODELSCOPE + modelscope>=1.18.1,<1.38) |
同上 |
「确认响应中 choices[0].text 包含非空生成文本。错误响应或空补全不能确认推理成功。」 |
同上 |
优雅停止(kill -2)与「停止服务前请确认进程」的提醒 |
同上 |
Atlas 300I DUO / 200I Pro 的额外要求(float16、FULL_DECODE_ONLY、禁用 enable_npugraph_ex) |
同上 |
| MindIE Motor 支持的推理引擎(vLLM 已支持推荐 / SGLang 已支持 POC) | 官方 MindIE《支持的推理引擎》zh/mindie/310/mindiemotor/motordev/docs/zh/user_guide/features/supported_inference_engines.md |
| 「官方推荐 MindIE Motor + vLLM Ascend」 | 官方《MindIE 是什么》(另见 D1) |
⚠️ 数据纪律提示: ① 本课的镜像标签、版本号、
--shm-size取值等均为官方快速开始中的示例值, 会随发版变化 —— 实际使用时以官方当前文档为准; ② 官方已说明「经过验证的确切版本,请参阅安装指南 > 硬件和软件栈」—— 本课未复制该版本清单(它是随版本变化的表); ③ Atlas 300I DUO / 200I Pro 的参数示例仅适用于该硬件,本课未做推广性表述; ④ 标注「归纳」的解读(与 B5 命令的对照表、五档排查法)非官方口径。
附录 B · 本课出现的命令详解
docker pull <镜像>
- 含义:把镜像拉到本机。
- 用法:
docker pull m.daocloud.io/quay.io/ascend/vllm-ascend:v0.23.0-a3 - 期望结果:逐层下载完成。
- 关键提醒:换国内仓库时只改前缀,
<TAG>里的-a3、-310p、-950dt、-openeuler必须原样保留。
python3 - <<'PY' ... PY
- 含义:从标准输入执行 Python 代码(heredoc)。
- 拆解:
<<'PY'= 开始读取直到遇到PY这一行;'PY'加引号表示不做变量展开(原样传递)。 - 用法:官方给出的多行验证代码。
- 期望结果:输出
vLLM Ascend environment: OK。 - 为什么不用
python3 -c "...":多行代码用 heredoc 更清爽,也不用担心引号转义。 assert的作用:条件不成立就抛异常 —— 这里用来"卡住"无法用卡的情况,并带上明确的错误信息。
npu-smi info
- 见 A1 / A3 附录。本课用法:容器内先跑它,确认容器能不能看见卡。
vllm serve <模型> &
- 含义:启动 vLLM 的在线推理服务。
- 拆解:
vllm= 命令行入口;serve= 起服务子命令;模型参数可以是本地路径或模型名;&= 放到后台。 - 用法:
vllm serve Qwen/Qwen3-0.6B & - 期望结果(官方日志四行):
INFO: Started server process [...] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) - 服务端口:默认 8000,与容器
-p 8000:8000对应。
curl --fail <url>
- 含义:发起 HTTP 请求;
--fail让 HTTP 错误码(4xx/5xx)直接以非零退出码失败。 - 拆解:
curl(命令行 HTTP 客户端)+--fail(遇错就失败)。 - 用法:
bash curl --fail http://localhost:8000/v1/models curl --fail http://localhost:8000/v1/completions -H "Content-Type: application/json" -d '{...}' - 为什么要
--fail:否则即使返回 404/500,curl 也返回成功,看起来"通了"其实没通。 - 期望结果:
/v1/models列模型;/v1/completions的响应里choices[0].text有非空文本。 - 官方判据:"错误响应或空补全不能确认推理成功。"
kill -2 <PID>
- 含义:给进程发 SIGINT(信号 2),等同于前台按
Ctrl+C。 - 拆解:
kill发信号;-2= SIGINT。 - 用法:
VLLM_PID=$(pgrep -f "vllm serve")再kill -2 "$VLLM_PID" - 期望结果(官方四行):
INFO: Shutting down FastAPI HTTP server. INFO: Shutting down INFO: Waiting for application shutdown. INFO: Application shutdown complete. - 为什么不用
-9:-9是强杀,不给程序清理机会。 - 官方提醒:
pgrep -f "vllm serve"可能匹配到别的 vLLM 进程,kill 前先确认 PID。
docker run … -it "$IMAGE" bash
- 含义:起交互式容器,把设备 / 挂载 / 端口一次交给它。
- 拆解:
--device(设备:卡 +davinci_manager/devmm_svm/hisi_hdc)+-v(挂载:DCMI、npu-smi、驱动lib64/+version.info、/etc/ascend_install.info、模型缓存)+-p 8000:8000(端口映射)+--shm-size=1g(共享内存)。 - 用法:照抄官方那一整段(含前面
export DEVICE/export MODEL_CACHE/mkdir -p)。 - 期望结果:不报错,进到容器内 shell 提示符。
- 不过会怎样:
could not select device driver/ 权限被拒 →--device写法(设备被占用时按 B5 带rwm);端口占用 → 改映射端口。 - ⚠️ 进到 shell ≠ 成功:容器里看不看得见卡,要另做容器内校验(下一节)。
python3 example.py
- 含义:跑官方给的离线推理示例(不起服务,直接验引擎能不能出文字)。
- 拆解:
example.py是官方示例脚本(LLM(model=…).generate(prompts, SamplingParams(...)))+python3执行。 - 用法:先在容器里建好
example.py,再python3 example.py。 - 期望结果:出现
Platform plugin ascend is activated;随后打印Prompt: …, Generated text: …(文本非空)。 - 不过会怎样:
assert generated_text.strip()抛AssertionError→ 生成结果为空;看不到 plugin 行 → 不在 vLLM Ascend 镜像里。
export VLLM_USE_MODELSCOPE=True
- 含义:Hugging Face 访问不了时,把模型下载源切到 ModelScope。
- 拆解:
export(定义环境变量,只在当前终端有效)+VLLM_USE_MODELSCOPE(vLLM 认的开关)。 - 用法:
export VLLM_USE_MODELSCOPE=True后pip install "modelscope>=1.18.1,<1.38";模型已在本地的不用设,把模型 ID 换成本地目录即可。 - 期望结果:之后取模型走 ModelScope,不再报 HF 连接超时。
- 不过会怎样:仍超时 / DNS 错误 → 变量没生效(重开终端要重设)或 modelscope 没装上。
参数 / 名词速查表
| 项 | 一句话 |
|---|---|
vllm-ascend |
镜像名(vLLM Ascend 插件) |
-a3 / -310p / -950dt |
镜像标签里的硬件系列后缀 |
-openeuler |
镜像标签里的容器操作系统后缀(不加为 Ubuntu) |
/etc/ascend_install.info |
驱动/固件安装信息(本课挂这个) |
/etc/Ascend/ascend_cann_install.info |
CANN 安装信息(B5 挂这个) |
vLLM Ascend environment: OK |
容器就绪的官方判据 |
Application startup complete |
服务起来的关键日志 |
Uvicorn running on http://0.0.0.0:8000 |
端口在听的关键日志 |
choices[0].text |
判断推理是否真成功要看的字段 |
Platform plugin ascend is activated |
vLLM 认出昇腾平台的关键日志(离线推理) |
VLLM_USE_MODELSCOPE=True |
Hugging Face 不可达时切 ModelScope |
--shm-size=1g |
快速开始的共享内存取值(真实业务按负载调) |
kill -2 |
优雅停止(相当于 Ctrl+C) |
附:D1 与 D2 的分工
| 问题 | 在哪一课 |
|---|---|
| 为什么需要服务层、MindIE 是什么、官方推荐哪条路 | D1 |
| vLLM Ascend 是什么、镜像里有什么、怎么选镜像、起容器/起服务/验证服务 | 本课(D2) |
| Motor 五个子组件怎么配合、集群怎么调度 | D3 |
| PD 分离怎么做 | D4 |
| 端口、并发、吞吐调优 | D5 |