D 层部署指南 · L3服务
一页读懂这一层:让模型能被别人用、扛得住、坏不了。 这一层要装的是推理服务框架(MindIE Motor 或 vLLM Ascend)。 装完的标准:端口在听 → 模型列表能拿 → 真发一次请求、返回里有非空文本。
本指南是 D 层 5 课的浓缩版(D1 L3 服务层 / D2 vLLM Ascend / D3 MindIE Motor / D4 PD 分离 / D5 可用性与调优), 只保留"装什么、干什么用、怎么装、怎么验"。
一、这一层的职能
| 项 | 内容 |
|---|---|
| 层号 | L3(服务层) |
| 一句话职责 | 把模型做成"能被别人用的服务"。 |
| 和 L2 的分界 | L2 解决"能不能算",L3 解决"能不能被别人用、扛得住、坏不了"。 |
| 装完的判据 | 真发一次请求,返回的 choices[0].text 里有非空文本 |
| 拿掉它会怎样 | 模型能跑通,但没法对外提供服务(端口起不来、curl 没响应) |
| 谁在用它 | L4 应用层 —— 你的业务系统调的就是这一层的接口 |
L2 vs L3 的分工(用一张表说清):
| 在脚本里跑模型(L2) | 做成推理服务(L3) | |
|---|---|---|
| 谁来调用 | 你自己,写死在一个进程里 | 任何人,通过网络 |
| 并发 | 一次一个 | 服务层管 |
| 多卡 | 你自己分配 | 服务层调度 |
| 挂了怎么办 | 进程退出,你自己重启 | 要有健康检查和自愈 |
| 换模型 | 改代码重跑 | 换配置 |
二、这一层装什么(软件清单)
⭐⭐ 先看一条官方路线变更(这是这一层最重要的一条)
官方原文:MindIE LLM 组件将暂停后续功能演进,现有功能维持维护状态, 不再支持新特性和新模型。 推荐使用 MindIE Motor + vLLM Ascend 部署推理服务。
| 线路 | 是什么 | 建议 |
|---|---|---|
| 主线 | MindIE Motor + vLLM Ascend | ⭐ 官方推荐,练习重点放这里 |
| 维护线 | MindIE LLM | 了解即可(网上大量教程基于它,会出现"照着做但官方文档找不到对应章节") |
两条路线的软件清单
| 路线 | 装几个包 | 是什么 |
|---|---|---|
| vLLM + vLLM Ascend | 2 个 | vLLM(社区主流)+ vLLM Ascend(昇腾硬件插件) |
| MindIE Motor | 0 个(用镜像) | 官方服务化框架,镜像是打包好的 |
MindIE 家族三件套(分清楚,别看错)
MindIE(昇腾推理引擎)
├── MindIE Motor → 服务化 / 调度 / 集群 ← 本层的主角
├── MindIE SD → 多模态(图 / 视频生成)
└── MindIE LLM → 大模型推理 SDK ← ⚠️ 维护线
| 组件 | 一句话 |
|---|---|
| Motor | 管"怎么服务"(请求分发、调度、集群) |
| SD | 管"多模态"(视觉生成) |
| LLM | 管"大模型怎么跑得快"(模型库 + 优化器) |
⚠️ 一个命名变化(官方原文): 原 MindIE Motor 代码仓自 3.1.0 版本起正式更名为 MindIE Motor CPP, 该组件仅对接 MindIE LLM 推理引擎。 → 看文档别把它俩当成两个东西(同一个组件的更名,但对接的引擎不同)。
⭐ vLLM Ascend 镜像里有什么(官方原文)
预构建镜像包含经过验证的 Python 和 Ascend 用户态软件栈, 包括 CANN + NNAL + PyTorch + TorchNPU + vLLM。
⭐ 这一条很关键:
用官方镜像,等于 A / B / C 三层都替你装好了。 这是"跳过前面卡住的步骤、直接练 L3"的合法路径。
三、每个东西干什么用(一句话)
| 东西 | 一句话功能 |
|---|---|
| vLLM | 社区主流的推理引擎 |
| vLLM Ascend | 昇腾硬件适配插件(和 TorchNPU 是同一种模式:给现成框架加昇腾支持) |
| MindIE Motor | 分布式调度与服务化框架 |
| Coordinator / Controller | 控制面 —— 管状态、调度、决策、可靠性 |
| 推理引擎(vLLM 等) | 数据面 —— 真正算 |
| Prefill | "一次性把题读懂"(处理输入 prompt) |
| Decode | "一个字一个字写答案"(逐 token 生成) |
| KV Cache | Decode 阶段的主要内存访问对象 —— 也是 PD 分离要传的东西 |
官方给 MindIE Motor 的定位(原文):
MindIE Motor 是面向大模型推理的分布式调度与服务化框架, 致力于通过高性能调度、资源池化、故障自愈,提升大模型推理集群的 吞吐、资源利用率与服务可靠性。
⭐ 官方这句话里三个词,正好对应 L3 要解决的三件事:
| 官方用词 | 解决什么 |
|---|---|
| 高性能调度 | 请求太多怎么办 |
| 资源池化 | 卡怎么分、怎么用满 |
| 故障自愈 | 坏了怎么办 |
四、怎么装(安装方法)
4.1 路线一:vLLM Ascend(推荐,走镜像)
⭐ 为什么走镜像:镜像里 CANN + NNAL + PyTorch + TorchNPU + vLLM 全都配好了 —— 省掉前面三层最耗时的版本配套工作。
命令 docker run(官方快速开始给的方式)
含义:起一个带完整软件栈的推理容器 拆解:最关键的是
--device(从宿主机"交"卡给容器) ⚠️ 权限必须rwm,不能用rw—— 用rw容器能进,但里面看不见卡(坑非常隐蔽) 用法:按官方快速开始的命令,把镜像标签换成你卡型号对应的那个 期望:容器进得去,且里面能看到卡 不过会怎样:容器能进但内部npu-smi看不到卡 → 回去查--device的参数
命令 vllm serve <模型路径> --port <端口>
含义:起推理服务 拆解:
serve(起服务的子命令)+ 模型路径 +--port(服务端口) 用法:镜像里通常给了启动脚本,先看脚本再改参数 期望:服务监听端口 不过会怎样:看日志的第一条报错,不是最后一条
4.2 路线二:MindIE Motor(用官方镜像,装 0 个包)
⚠️ 前提条件(官方原文):
宿主机已经安装过 NPU 驱动和固件。
⭐ 两个关键概念(记住这两个就能读懂后面所有配置)
① 控制面与数据面解耦(官方原文):
MindIE Motor 采用 控制面(Controller/Coordinator)与数据面(推理引擎)解耦的架构, 可对接多种大模型推理引擎。
| 控制面 | 数据面 | |
|---|---|---|
| 是谁 | Controller、Coordinator | 推理引擎(vLLM 等) |
| 管什么 | 状态、调度、决策、可靠性 | 真正算 |
| 换掉会怎样 | —— | 换引擎不动控制面 |
→ 好处:换引擎只需要改一个配置项(engine_type),不用换掉整个服务框架 ✓
(官方说"当前支持 vLLM、SGLang 两种引擎")
② 角色与端口的千位段规律(部署的人必须懂这个)
为什么必须懂:L3 的故障几乎都是"某个角色没起来"或"角色之间连不上"。 不知道有几个角色,就不知道要去哪台机器上查。
| 千位段 | 角色 |
|---|---|
| 1xxx | Coordinator |
| 2xxx | Controller |
| 3xxx | Prefill |
| 4xxx | Decode |
⭐ 一条顺序纪律:先起 Coordinator + Controller,再起 P/D 或 union ——
后面的角色要用 COORDINATOR_SERVICE、CONTROLLER_SERVICE 去连这两个地址。
4.3 PD 分离还是 PD 混部(别默认上全套)
| 部署模式 | Engine 容器 | 跨角色 KV 传输 |
|---|---|---|
| PD 混部 | union 容器(一个容器同时干两件事) | 不需要 |
| PD 分离 | Prefill 和 Decode 分开的容器 | 需要 |
为什么要把 Prefill 和 Decode 拆开 —— 因为这两个阶段的"性格"完全不同(官方定义):
Prefill 阶段:对输入 prompt 执行一次完整前向传播,生成初始隐藏状态, 计算密集型;每个新输入序列都需执行一次 Prefill。
Decode 阶段:基于 Prefill 结果逐步生成后续 token, 单步计算量较小,但需反复执行直至生成结束,访存密集型(以 KV Cache 等内存访问为主)。
| Prefill | Decode | |
|---|---|---|
| 干什么 | 处理输入 prompt,一次完整前向 | 逐 token 往后生成 |
| 特性(官方用词) | 计算密集型 | 访存密集型 |
| 执行次数 | 每个新输入序列执行一次 | 反复执行直到生成结束 |
| 每次的"量" | 一次很大(整个 prompt) | 单步计算量较小,但次数多 |
⭐ 一句话记住差别:
Prefill 是"一次性把题读懂",Decode 是"一个字一个字写答案"。
⭐ 分离的代价:必须传 KV Cache —— 这是 PD 分离绕不过去的成本。
五、怎么验证装对了(验证方法)
⭐⭐ 官方给的核心判据(这一条最容易做错)
官方原文:确认响应中
choices[0].text包含非空生成文本。 错误响应或空补全不能确认推理成功。
→ 意思是:
"端口通了"不等于"推理成了"。 一个 HTTP 200 的响应,可能是空补全、可能是错误结构。
四道检测(逐级加严)
命令 ss -lntp
含义:看服务有没有真在监听端口 拆解:
ss(套接字统计)+-l(只看 listening)+-n(端口用数字)+-t(TCP)+-p(显示进程) 期望:看到推理服务端口处于 LISTEN,且有对应进程 不过会怎样:端口不在 → 服务没起来(去看日志的第一条报错)
命令 curl -s --max-time 10 http://127.0.0.1:<端口>/v1/models
含义:看服务认不认模型 拆解:
-s(静默)+--max-time 10(最多等 10 秒) 期望:返回 JSON,里面有模型名 不过会怎样:超时 → 服务没就绪;Connection refused→ 端口不对
命令 curl -X POST http://127.0.0.1:<端口>/v1/chat/completions -H "Content-Type: application/json" -d '{"model":"<模型名>","messages":[{"role":"user","content":"你好"}]}'
含义:真发一次请求 —— 这是本层的最终验收 拆解:
-X POST(POST 方法)/-H(请求头)/-d(请求体 JSON) 期望:返回 JSON,choices[0].text里有非空生成文本 不过会怎样: - 返回 200 但内容是空的 → 不算成功(官方明说) - 返回{"detail":"Service is not available"}→ 官方说明:多为 P/D 或 Coordinator 尚未完全就绪 —— 等,不是坏
命令 VLLM_PID=$(pgrep -f "vllm serve"); kill -2 "$VLLM_PID"
含义:优雅停掉服务(等价于前台按
Ctrl+C) 拆解:pgrep -f(按完整命令行找进程)+kill -2(发 SIGINT 信号,不是强杀) 期望:服务干净退出 不过会怎样:用kill -9强杀 → 可能留下占用,下次起不来
⭐ 验收清单(五条全过 = 这一层完工)
| # | 判据 | 通过标准 |
|---|---|---|
| ① | 端口在听 | ss -lntp 能看到 |
| ② | 模型列表能拿 | curl .../v1/models 有返回 |
| ③ | ⭐ 有非空生成文本 | choices[0].text 不是空的 |
| ④ | 显存占用合理 | npu-smi info 看 |
| ⑤ | 能关掉、能重启 | kill -2 干净退出 |
⚠️ 第 ③ 条是本层的核心判据 —— 前两条过了但第三条不过,不算装好。
六、三个最容易踩的坑
| # | 坑 | 症状 | 怎么办 |
|---|---|---|---|
| 1 | --device 用了 rw 而不是 rwm |
容器能进,但里面看不见卡(隐蔽) | 改 rwm 重起容器 |
| 2 | 只看"端口通了"就以为成了 | 返回 200 但内容是空 | 必须看 choices[0].text 有没有内容 |
| 3 | PD 混部/分离,少起了一个容器 | 请求进不来,服务"看着起来了" | JOB_NAME 必须一致,同实例的多个容器要一起拉起 |
⭐ 官方默认就开着六项能力(遇到"奇怪现象"先想这条)
官方原文:
user_config.json与env_config.json全量示例已默认开启 主备倒换、异常实例重启、服务限流、虚推、KV 亲和性调度、KV 池化等能力。
| 分类 | 能力 | 解决什么 |
|---|---|---|
| 可用性与自愈 | 主备倒换 | 服务器级故障 |
| 异常实例重启 | 实例级故障 | |
| 保护与探测 | 服务限流 | 过载 |
| 虚推健康检查 | 静默故障 | |
| 性能调优 | KV Cache 亲和调度 | 重复前缀的重复计算 |
| KV 池化 | 显存压力 |
⭐ 注意那句"全量示例已默认开启" —— 意味着你拿官方示例跑起来时,这六项已经在工作了。 所以有些现象(例如"没请求时它也在动")可能正是虚推健康检查在干活,不是故障。
⚠️ 一条硬约束:虚推仅允许在 ERROR 日志级别下开启(官方要求)。
什么是"静默故障"(虚推要防的就是它)
静默故障 = 进程卡死但服务看似正常。
这是 L3 里最危险的一类问题: 端口还在听、进程还在、日志没报错,但请求就是没响应或响应很慢。 虚推健康检查就是用来探测这种状态的。
⚠️ 服务"没就绪"时的官方报错文本:
{"detail":"Service is not available"} —— 多为 P/D 或 Coordinator 尚未完全就绪 ✓
七、这一层和其他层的关系
↑ L4 应用层(你的业务系统) ← 调的就是 L3 的接口
│
│ ★ 你在这一层 装:推理服务框架(镜像形态最常见)
│ 验:真发一次请求,返回里有非空文本
│
↓ L2 框架(PyTorch + TorchNPU) ← 服务建在框架之上
↓ L1 基础软件(CANN) ← 算子库在这
↓ L0 硬件与驱动(HDK) ← 卡得先被认出来
| 关系 | 说明 |
|---|---|
| 它依赖谁 | L0–L2 全部 —— 但官方镜像把这三层都打包了,所以能"跳着练" |
| 谁依赖它 | L4 应用层 |
| 官方镜像的价值 | 一份镜像 = L0~L3 的软件栈(只有 L0 的驱动必须在宿主机上) |
附 · 命令速查(复制即用)
# 起(vLLM 路线,容器形态)
docker run ... --device=/dev/davinci0:rwm --device=/dev/davinci_manager:rwm ...
# ⚠️ 权限必须是 rwm,不能用 rw
# 服务内部
vllm serve <模型路径> --port <端口>
# 验(四级)
ss -lntp # ① 端口在听
curl -s --max-time 10 http://127.0.0.1:<端口>/v1/models # ② 模型列表
curl -X POST http://127.0.0.1:<端口>/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"<模型名>","messages":[{"role":"user","content":"你好"}]}'
# ⭐ ③ 必须看 choices[0].text 有没有非空文本
npu-smi info # ④ 显存占用
# 停(优雅)
VLLM_PID=$(pgrep -f "vllm serve"); kill -2 "$VLLM_PID"
# 巡检(MindIE Motor)
kubectl get pods -o wide # 看 Pod 状态
npu-smi info watch -s u # 实时看利用率
⭐ 判据速记:
- 端口在听 ✓ → 服务起来了
- choices[0].text 非空 ✓ → 推理真的成了(这才是完工)
- {"detail":"Service is not available"} → 未就绪,等 ✓
附 · 本层的"包从哪下"
| 要下什么 | 地址 |
|---|---|
| vLLM Ascend | 官方文档站:https://docs.vllm.ai/projects/ascend/(⚠️ 不归昇腾文档站管) |
| MindIE | 昇腾社区软件下载 → MindIE 那一栏(镜像形态) |
| 模型权重 | ModelZoo / ModelScope / HuggingFace |
⚠️ 起服务前先确认三件事:
1. 卡型号 —— 决定选哪个镜像标签
2. npu-smi info 有表 —— 驱动在宿主机上备好了
3. --device 权限是 rwm —— 否则容器里看不见卡
本层浓缩自:
昇腾部署课件_D1_L3服务层/_D2_vLLM_Ascend/_D3_MindIE_Motor/_D4_PD分离/_D5_可用性与调优上一层:C 层 · L2 框架 下一层:E 层 · 排障(贯穿各层)