东方智慧AI 算力
AI算力 · 昇腾部署课件 D2

昇腾部署第 D2 课:vLLM Ascend——把推理服务真正跑起来

2026-10-05

编制:观澜|日期: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 讲的五维对应,在这个镜像里已经被官方配好了。


第二段 · 学习目标

学完这一课,你应该能回答:

  1. vLLM Ascend 是什么(它和 vLLM 是什么关系)
  2. 那个预构建镜像里到底装了什么——为什么它能省掉大量配置
  3. 怎么选对镜像(硬件系列 → 标签后缀)
  4. 容器要挂哪些设备与目录(与 B5 讲的有什么异同)
  5. 怎么验证"容器就绪"和"服务真的起了"(官方给了明确判据)

第三段 · 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_manager devmm_svm hisi_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 + print)整段粘贴回车 期望:最后打印 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 的定位法)。


第九段 · 小结与下一课

三句话带走

  1. vLLM Ascend 是"社区维护的硬件插件",让 vLLM 能跑在昇腾上 —— 和 C1 的 TorchNPU 是同一个接入模式(本体 + 插件)。
  2. 那个预构建镜像里打包了 CANN、NNAL、PyTorch、TorchNPU、vLLM、vLLM Ascend —— L1+L2+L3 一次配好,只剩 L0(驱动)要你在宿主机装。 这等于官方帮你把 B3/C1 讲的"版本地狱"绕开了。
  3. 验证要过三关:容器就绪(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 题)

  1. 为什么"起一个 vLLM Ascend 服务"只需要拉个镜像就能省掉大量配置?镜像里到底装了什么?
  2. 官方给的"容器就绪"判据是什么字符串?"服务起来了"看哪两行日志?
  3. 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
选修 · 昇腾 AI 部署实操 本机读完 0/51
选修 C · 昇腾 AI 部署实操
从零把昇腾 AI 部署学一遍:先看懂分几层,再逐层装起来,最后出问题能自己定位到哪一层。不需要先有卡就能开始。
看完整学习路径(6 站 + 3 门选修) · 进度只存在你这台设备的浏览器里,不用登录
辽ICP备2026019944号-1