昇腾部署第 D3 课:MindIE Motor——五个子组件与集群的骨架
编制:观澜|日期:2026-10-05|级别:T2 标准级 层级:D 层 · L3 服务(D 层第 3 课,前接 D2《vLLM Ascend》) 素材来源:昇腾官方 MindIE 3.1.0 文档(《MindIE 是什么》《支持的推理引擎》《docker-only 部署多容器 PD 服务》) 数据纪律:组件职责、部署形态、端口规划、配置结构逐字出自官方文档;标注「归纳」的是便于记忆的整理,非官方口径。
第一段 · 章前定位
D2 把"一个服务"跑起来了 —— 但那是最简单的情形:
一张卡、一个容器、一条
vllm serve。
真实业务不长这样:
| 单机最小跑通(D2) | 真实生产 |
|---|---|
| 1 个进程 | 多个角色、分布在多个节点 |
| 用户直连引擎 | 请求先到一个入口,再被分发 |
| 没有"高可用"概念 | 要能扩容、能自愈、能观测 |
| 一个端口 | 一堆端口,各有分工 |
中间缺的那一层,就是 MindIE Motor(D1 讲过它是"分布式调度与服务化框架")。
这一课回答:
Motor 由哪几个部分组成?凭什么说它能"调度"和"服务化"? 为什么官方文档里"部署"这件事分成了好几种形态? 那些端口(1025/2026/3026…)分别是谁的?
为什么部署的人必须懂这个: L3 的故障几乎都是"某个角色没起来"或"角色之间连不上"。 不知道有几个角色,就不知道要去哪台机器上查。
第二段 · 学习目标
学完这一课,你应该能回答:
- Motor 的架构原则(官方原话)+ 这条原则带来的好处
- 五个子组件各干什么,哪些是"控制面"、哪些是"数据面"
- 两种部署形态(K8s / Docker-only)的区别与选择
- PD 分离与 PD 混部的官方对照(Engine 容器、KV 传输、启动方式)
- 配置文件是哪几个、端口怎么规划
第三段 · 架构原则:控制面与数据面解耦
官方原文(《支持的推理引擎》):
MindIE Motor 采用 控制面(Controller/Coordinator)与数据面(推理引擎)解耦的架构, 可对接多种大模型推理引擎。
这一句话里有一个关键设计(归纳)
| 控制面 | 数据面 | |
|---|---|---|
| 是谁 | Controller、Coordinator | 推理引擎(vLLM 等) |
| 管什么 | 状态、调度、决策、可靠性 | 真正算 |
| 换掉会怎样 | —— | 换引擎不动控制面 |
为什么"解耦"这件事值得记(归纳):
正因为解耦,官方才能说"当前支持 vLLM、SGLang 两种引擎" —— 换引擎只需要改一个配置项(
engine_type),不用换掉整个服务框架。
这与 B1 讲的 CANN 是同一个思路: 分层的目的,就是让"换掉一部分"不至于牵动全局。
第四段 【如果我来做】从单机摸底到集群跑起来
4.0 我会按这个顺序做(顺序错了会白干)
这一段回答"接到一个 Motor 部署需求,我会怎么动手" —— 顺序本身就是经验。
① 先摸底(不装任何东西,先查事实)
只查两件事:【这是一台机器还是多台】、【镜像在不在本机】
⚠️ 跳过的后果:按多机多容器的端口方案规划完,发现手里只有一台机器
→ 落地命令见 4.0.1
② 定方案(做哪一种)
单容器 / 多容器 × PD 混部 / PD 分离 —— 别默认上"多容器 + PD 分离"全套
环境没有 K8s → 走 Docker-only;只想先看清组件怎么协作 → 规模最小的那一档
→ 两种形态见第五段,混部与分离的差别见第六段
③ 预热(能在别处先跑一遍就先跑)
先用官方现成配置(deepseek_v4_flash 等)在最小规模把链路走通,再换自己的模型
→ 见第七段 ②
④ 动手(每步立刻验,不攒着一起验)
先起 Coordinator 和 Controller(这两个不起来,后面的容器起了也没用)
再起 union,或者起 kv_store + P/D
→ 命令见第五段、第六段
⑤ 难验证的(找反面证据)
"容器起来了"不等于"服务就绪" —— 官方原文:返回 {"detail":"Service is not available"} 就是没就绪
反面证据 = 拿一个本来就不该通的端口去连,它必须连不上
⑥ 收口(用完整流程跑一遍当验收)
按第七段 ⑤ 的 curl 完整发一次 chat/completions,看到流式 JSON 才算这一轮通过
⑦ 出证据(留下可给别人看的东西)
curl 的回显、docker logs 的输出、端口规划表 —— 这三样能说清"服务确实通了"
⑧ 诚实(官方没给答案的地方明写"这里要自己查")
官方给了端口规划表,但没给"你的模型该用哪个端口"的推导过程 —— 这里要自己查
⭐ 这个顺序里最容易被跳过的是 ① 和 ⑤:
- ① 没做 → 方案定错,后面全白干;
- ⑤ 没做 → 拿"容器是 Running"当验收,而官方明说
Running只表示调度并启动成功,不等于业务就绪。
4.0.1 路径第 ① 步的落地:两条摸底命令
命令 ls /dev/davinci[0-9]*
含义:列出本机被识别到的昇腾芯片设备节点 —— 官方
start_docker.sh就是用这条来自动探测可见卡的 拆解:ls(列出)+/dev/davinci[0-9]*(通配符:davinci 后面跟一位数字开头的所有节点) 用法:ls /dev/davinci[0-9]*(想数清楚再跟| wc -l) 期望:打印出/dev/davinci0、/dev/davinci1… 一串节点,节点个数≈这台机器的卡数 不过会怎样:报No such file or directory→ 驱动或设备没就绪(回 A1 查);只出来一个节点 → 这台机器就一张卡,别按多机方案规划端口
命令 docker images
含义:确认 MindIE Motor 镜像已经在本机 —— 官方原文:「待镜像导入后,请使用以下命令查看 docker 镜像是否存在」 拆解:
docker images(列出本地镜像) 用法:镜像还没导入时先docker load -i xxxx.tar,再执行docker images期望:列表里能找到你要用的 MindIE Motor 镜像(后面start_docker.sh里的IMAGE_NAME就填它) 不过会怎样:列表里没有 → 镜像没导进来,或导到了另一个节点;镜像名写错 → 容器起不来
4.1 五个子组件(官方表,逐个说清)
官方在《MindIE 是什么》的表 1 里给出了 Motor 的五个子组件,原文如下:
| 子组件 | 官方说明 |
|---|---|
| Controller | 控制器,作为集群的状态管控器和决策大脑,负责全局业务状态管控及 RAS 能力决策 |
| Coordinator | 调度器,作为用户推理请求的统一入口,负责接收高并发请求,执行请求调度、管理与转发,是整个集群的数据流枢纽 |
| Deployer | 部署器,基于 Kubernetes 的推理服务部署参考脚本,提供服务启动、停止、弹性伸缩等能力 |
| EngineServer | 节点推理服务入口,提供统一的 RESTful EndPoints,包括 OpenAI 接口、Metrics 等;北向对接 Coordinator 和 Controller,南向对接 vLLM-Ascend 框架 |
| NodeManager | 节点级服务管理器,主要提供节点级服务进程启动和节点级健康状态管理能力 |
4.2 用一张图串起来(归纳)
用户请求
│
▼
┌────────────────────────┐
│ Coordinator(调度器) │ ← 统一入口、数据流枢纽
│ 接收高并发 → 调度转发 │
└────────────────────────┘
│ ▲
▼ │ 北向对接
┌──────────────┐ ┌──────────────┐
│ EngineServer │ │ Controller │ ← 决策大脑(状态管控 + RAS)
│ (节点入口) │ └──────────────┘
│ OpenAI 接口 │ ▲
│ Metrics │ │
└──────────────┘ │
│ │
▼ ┌──────────────┐
┌──────────────┐ │ NodeManager │ ← 节点级:进程启动 + 健康状态
│ vLLM-Ascend │ └──────────────┘
│ (推理引擎) │
└──────────────┘
四个记住的要点(归纳):
| 你要做的事 | 该盯哪个组件 |
|---|---|
| 请求进不来 / 分发不均 | Coordinator(统一入口、数据流枢纽) |
| 服务对外暴露的接口(OpenAI 风格) | EngineServer(RESTful EndPoints、Metrics) |
| 集群状态、决策、可靠性(RAS) | Controller |
| 某个节点上的进程起不来 / 健康状态异常 | NodeManager |
| 部署、停止、弹性伸缩 | Deployer(基于 K8s 的参考脚本) |
⭐ 一个很实用的观察:
EngineServer才是"提供端口和 OpenAI 接口"的那个 —— 如果你在排"curl 8000 端口不通",EngineServer是直接嫌疑对象。 Coordinator 管的是请求怎么到这来,EngineServer 管的是它怎么对外说话。
4.3 术语小注:RAS 是什么
官方原文用的是"RAS 能力决策"。 RAS 是可靠性领域的常见缩写(Reliability Availability Serviceability,可靠性 / 可用性 / 可服务性)。 官方在"关键功能特性"里也写了"增强 PD 分离服务的可靠性、可用性和可服务性" —— 两处口径一致。
第五段 · 两种部署形态:K8s 与 Docker-only
官方文档里,"怎么部署 Motor"有两条明显不同的路。
形态一:基于 Kubernetes
官方对应的章节名是:「基于 K8s 多容器 PD 分离服务部署」, 并说明它"通过完整详细的部署案例,指导开发者体验基于 Motor 的 PD 分离服务部署, 并指导生产环境配置优化实践"。
配套的组件是 Deployer(官方定义就是"基于 Kubernetes 的推理服务部署参考脚本")。
这条路的三条命令(确认镜像 → 拉起服务 → 看角色起没起来):
命令 docker load -i xxxx.tar
含义:把导出的镜像文件载入本机 —— 官方原文:「获取镜像后,请使用以下命令将镜像加载至服务器」 拆解:
docker load(从文件载入镜像)+-i xxxx.tar(读入哪个镜像包) 用法:docker load -i motor.tar,紧跟一条docker images确认(见 4.0.1) 期望:回显Loaded image: <镜像名:标签>,且docker images列表里查得到它 不过会怎样:报open xxxx.tar: no such file or directory→ 包不在当前目录或名字不对;报docker: command not found→ 这台机器没有 docker ⚠️ K8s 形态要多做一件事:镜像要加载到集群的所有节点(官方原文:「将镜像加载至 K8s 集群的所有节点」)—— 只在一个节点 load,Pod 调度到别处就拉不到镜像
命令 python3 deploy.py --config_dir ../infer_engines/vllm
含义:拉起 PD 分离服务 —— K8s 形态的"动手"入口(官方《服务部署与验证》第 1 步) 拆解:
deploy.py(部署入口脚本,在examples/deployer/下)+--config_dir(指定含user_config.json与env.json的目录) 用法:先kubectl create namespace <namespace>(<namespace>须与user_config.json中job_id一致,默认mindie-motor,官方原文)→cd examples/deployer→python3 deploy.py --config_dir ../infer_engines/vllm期望:命令跑完不报错,随后kubectl get pods -n <namespace> -o wide能看到四类角色的 Pod 不过会怎样:报找不到配置文件 →--config_dir指错;namespace 与job_id不一致 → Pod 建到了别处,get pods里什么也看不到 ⭐ 官方对这条路的定位:「通过完整详细的部署案例,指导开发者体验基于 Motor 的 PD 分离服务部署,并指导生产环境配置优化实践」—— 它比 Docker-only 多了编排这一层
命令 kubectl get pods -n <namespace> -o wide
含义:看 K8s 形态下每个角色起没起来 —— 这一形态的"数人头" 拆解:
kubectl get pods(列 Pod)+-n <namespace>(命名空间)+-o wide(多打几列,能看出落在哪个节点) 用法:kubectl get pods -n mindie-motor -o wide期望:看到四类名字的 Pod 且状态Running——mindie-motor-controller-xxxxx(服务管理,监控各实例健康)、mindie-motor-coordinator-xxxx(请求调度,业务入口)、vllm-px-xxxx(P 实例 / Prefill)、vllm-dx-xxxx(D 实例 / Decode)—— 这四个名字正好对上 4.1 的四个组件 不过会怎样:报No resources found→ namespace 与job_id对不上;状态ImagePullBackOff→ 该节点上没有镜像(回上一条的"所有节点都要 load");状态CrashLoopBackOff→ 容器起来又退出,去看日志 ⚠️ 官方原话:「Pod 状态为Running仅表示已成功调度并启动,是否业务就绪仍需结合日志进一步确认」
形态二:Docker-only(不用 K8s)
官方原文(标题与特性介绍):
docker-only 部署多容器 PD 服务
本文档描述在不使用 Kubernetes deployer、仅用 Docker 容器 + 宿主机挂载配置 的方式 部署多容器 MindIE Motor PD 推理服务的端到端流程,同时适用于 PD 混部和 PD 分离。
这条路的两条准备命令(先把脚本和配置摆到一处,再谈启动):
命令 sh prepare.sh
含义:准备阶段的入口脚本 —— 把启动脚本、配置文件拷到同一个目录(
CONFIGMAP_PATH),再据此加载环境变量 拆解:官方prepare.sh里四个变量必须先改成真实绝对路径 ——EXAMPLES_PATH(examples 所在目录)、CONFIGMAP_PATH(启动脚本目录,需挂载进容器)、USER_CONFIG_PATH(user_config.json路径)、ENV_PATH(env.json路径) 用法:改完四个变量后sh prepare.sh期望:$CONFIGMAP_PATH下出现boot.sh、common.sh、hccl_tools.py、各角色的roles/*.sh,以及拷进来的user_config.json、env.json—— 官方注释写得很直白:「将准备好的 user_config.json 和 env.json 配置文件拷贝到 $CONFIGMAP_PATH 目录下」 不过会怎样:报No such file or directory→ 四个变量里有一个还是"xxx"没改;目录里少了某个*.sh→ 对应角色启动时找不到脚本而失败
命令 python $EXAMPLES_PATH/deployer/startup/set_env_docker.py --configmap_path $CONFIGMAP_PATH
含义:把
user_config.json与env.json的内容变成环境变量,作用于容器启动脚本(官方注释:「加载 user_config.json 和 env.json 中的环境变量,并作用于容器启动脚本」) 拆解:python(官方示例用的是python)+set_env_docker.py(环境变量加载脚本)+--configmap_path(指向prepare.sh里那个目录) 用法:python $EXAMPLES_PATH/deployer/startup/set_env_docker.py --configmap_path $CONFIGMAP_PATH期望:命令正常回显、没有Traceback—— 说明两个配置文件都能被解析 不过会怎样:报JSONDecodeError→user_config.json或env.json被改坏了(逗号、括号、注释写错);报找不到文件 →--configmap_path指错目录 ⚠️ 官方还留了一条:环境变量加载过、配置又改了的话,prepare.sh里有一串sed -i会先把旧的环境变量函数删掉再重新加载 —— 只重跑加载命令,可能读到的还是旧值
两条路的对照(归纳)
| K8s 形态 | Docker-only 形态 | |
|---|---|---|
| 编排 | Kubernetes | Docker + 宿主机挂载配置 |
| 对应组件 | Deployer(K8s 参考脚本) | —— |
| 官方定位 | "生产环境配置优化实践" | 端到端流程(更直接) |
| 适合 | 集群化、要弹性伸缩 | 想先跑通、或环境没有 K8s |
归纳怎么选: 先跑通用 Docker-only,生产集群用 K8s —— 这与 B4 的"七种装法"、C1 的"whl vs 源码"是同一种思路: 先按最简单的路跑通,再按需要上复杂度。
第六段 · PD 分离 vs PD 混部(官方对照表)
官方原文(docker-only 文档给出的对照表):
| 部署模式 | Engine 容器 | 跨角色 KV 传输 | 启动方式 |
|---|---|---|---|
| PD 分离 | Prefill 和 Decode 容器 | 需要 | 分别拉起 P/D 实例,按需拉起 KV Cache Store |
| PD 混部 | union 容器 | 不需要 | 每个 union 实例按其占用节点分别拉起 |
从这张表读三件事(归纳)
① 混部 = 一个容器干两件事(union),分离 = 两个容器各干一件。 "union"(联合)这个词本身就说明了。
② 分离之后多出一件事:跨角色 KV 传输。 官方还提到"按需拉起 KV Cache Store" —— 也就是说 PD 分离会引入一个额外的存储组件。
③ 启动方式的差别,本质是"实例怎么划分"。
起服务的命令:混部和分离到底差在哪(官方示例,可逐字对照)
官方原文:「两种模式均先启动 Coordinator 和 Controller」——
命令 COORDINATOR_SERVICE="<IP0>" CONTROLLER_SERVICE="<IP1>" JOB_NAME="" ROLE="coordinator" POD_IP="<IP0>" CONTAINER_NAME="docker_coordinator" sh start_docker.sh
含义:拉起控制面的调度器角色 —— 用户请求的统一入口 拆解:
ROLE="coordinator"(起哪个角色)+COORDINATOR_SERVICE/CONTROLLER_SERVICE(这两个地址都填部署节点的 IP)+POD_IP(容器用 host 网络,填宿主机 IP)+CONTAINER_NAME(容器名,方便后面docker logs)+JOB_NAME=""(控制面角色不需要实例名) 用法:换三处就是 Controller 那条 ——ROLE="controller"、POD_IP="<IP1>"、CONTAINER_NAME="docker_controller"期望:容器启动并保持在运行状态,日志里能看到服务开始监听 不过会怎样:起不来或马上退出 → 先看该容器名的日志(docker logs,第七段 ⑤);报端口被占用 → 同一节点上已占用1025~1027,回第七段按千位段重配 ⭐ 顺序纪律:先 Coordinator + Controller,再 P/D 或 union —— 后面的角色要用COORDINATOR_SERVICE、CONTROLLER_SERVICE去连这两个地址
命令 COORDINATOR_SERVICE="<IP0>" CONTROLLER_SERVICE="<IP1>" JOB_NAME="u0" ROLE="union" POD_IP="<IP0>" CONTAINER_NAME="docker_u0_node0" sh start_docker.sh
含义:拉起 PD 混部的一个 union 实例(官方示例:1 个 union 实例占
<IP0>、<IP1>两个节点) 拆解:ROLE="union"(混部角色:一个容器同时干 Prefill 和 Decode)+JOB_NAME="u0"(实例任务名,同一实例的多个容器必须一样)+POD_IP(这个容器在哪台机器)+CONTAINER_NAME(节点内唯一即可) 用法:第二个节点上把POD_IP="<IP1>"、CONTAINER_NAME="docker_u0_node1",其余不变再执行一次(官方强调「相同实例的多个容器需一起拉起」) 期望:两个节点上的容器都起来 不过会怎样:JOB_NAME写得不一致 → 两个容器被当成两个不同实例,机器再多也组不成一个实例;只起了一台 → 实例不完整,请求进不来
命令 ROLE=kv_store POD_IP="<IP2>" KV_STORE_BACKEND=mooncake KVS_MASTER_SERVICE="<IP2>" KV_CACHE_STORE_PORT=50088 KV_STORE_EVICTION_HIGH_WATERMARK_RATIO=0.9 KV_STORE_EVICTION_RATIO=0.1 DEFAULT_KV_LEASE_TTL=11000 CONTAINER_NAME="docker_kv_store" sh start_docker.sh
含义:拉起 PD 分离才需要的 KV Cache Store —— 官方原文:「若启用 KV Cache Store,先在对应节点启动;不启用时跳过」 拆解:
ROLE=kv_store(存储角色)+KV_STORE_BACKEND=mooncake(后端;官方:不启用或用 PD 混部时该值留空)+KVS_MASTER_SERVICE(KV Cache Store 所在节点 IP)+KV_CACHE_STORE_PORT=50088(官方示例端口)+ 两个水位比例(取值 0~1)+DEFAULT_KV_LEASE_TTL=11000(官方约束:须大于env.json中的ASCEND_CONNECT_TIMEOUT和ASCEND_TRANSFER_TIMEOUT) 用法:只在 PD 分离且启用 KV Cache Store 时执行;混部直接跳过 期望:容器起来;随后 P/D 启动命令里的KV_STORE_BACKEND也要同步改成mooncake、KVS_MASTER_SERVICE填这个节点 IP(官方原文) 不过会怎样:混部场景忘了跳过 → 多起一个用不上的容器;TTL填得比超时值小 → KV 对象提前过期,跨角色传输报错
命令 COORDINATOR_SERVICE="<IP0>" CONTROLLER_SERVICE="<IP1>" KVS_MASTER_SERVICE="" ENABLE_IPC_HOST="" JOB_NAME="p0" ROLE="prefill" POD_IP="<IP0>" CONTAINER_NAME="docker_p0_node0" sh start_docker.sh
含义:拉起 PD 分离的 P 实例(Prefill)—— 官方示例把 P 放在
<IP0>、<IP1>拆解:ROLE="prefill"(只干 Prefill 的容器)+JOB_NAME="p0"(同一 P 实例的多个容器同名)+KVS_MASTER_SERVICE=""(不启用 KV Cache Store 时留空) 用法:D 实例同构,只换三处 ——ROLE="decode"、JOB_NAME="d0"、POD_IP/CONTAINER_NAME换成 D 占用的节点(官方示例 D 占<IP2>~<IP5>) 期望:P、D 两个角色的容器都在运行,日志里能看到各自的启动过程 不过会怎样:P 起来了 D 没起 → 请求侧会一直等到"服务尚未就绪"(回第八段);JOB_NAME撞名 → 实例划分错乱 ⚠️ 和混部对比着看:混部只有union一条启动命令,分离多出两条(P、D)+ 可选一条(kv_store)—— 这就是"跨角色 KV 传输需要"翻译成命令行之后的样子
⭐ 一句话理解 PD 分离的动机(归纳,非官方口径): Prefill 和 Decode 这两个阶段的计算特性差别很大 —— 把它们拆开,各自可以用更适合自己的资源配比。 代价是要在两者之间搬运中间数据(KV),所以"跨角色 KV 传输"是必需的。
⚠️ 数据纪律提示:本课只给出官方对照表与术语, Prefill/Decode 的计算原理与性能收益留到 D4 展开,本课不做超出官方原文的推测。
第七段 · 配置文件与端口规划(本课最实用的一段)
① 配置文件是哪几个
官方原文(部署流程 · 准备模型相关配置文件):
准备
user_config.json与env.json文件。 配置字段的完整说明请参考 《user_config 全量参数说明》。
归纳:
- user_config.json —— 主配置(引擎、部署、各角色的参数)
- env.json —— 环境相关配置
- 两者都是"必准备的"
② 官方提供的"现成配置"
官方原文:
- PD 分离场景:MindIE Motor 已提供常用模型(deepseek_v4_flash、deepseek_v4_pro、glm 5.2 等)的 PD 分离配置示例,用户修改少量配置后可直接使用。
- 对于未提供典型配置的模型,可参考 《MindIE Motor 配置自动生成指导》, 自动生成配置文件
user_config.json与env.json。- PD 混部场景:可参考《配置自动生成指导》,自动生成 PD 混部场景下的配置文件。
归纳出两步走(归纳):
① 先看官方有没有你这个模型的现成配置示例 → 有就改少量配置直接用
② 没有 → 用官方《配置自动生成指导》自动生成
⭐ 这条很实用:别从零手写 user_config.json ——
官方提供了示例和自动生成工具,两条路都比手写靠谱。
③ 端口规划(官方原文,极实用)
官方原文:
调整端口配置:Coordinator、Controller 和 Engine 容器部署在不同节点时可使用默认端口; 同一节点部署多个角色时,推荐显式配置以下端口:
- Coordinator 推理、管理和可观测端口分别使用
1025、1026、1027。- Controller 管理和可观测端口使用
2026、2027。- union / Prefill NodeManager 从
3026起规划;Decode NodeManager 从4026起规划。
整理成表(官方数值,一个字没改):
| 角色 | 端口 | 用途 |
|---|---|---|
| Coordinator | 1025 |
推理端口 |
1026 |
管理端口 | |
1027 |
可观测端口 | |
| Controller | 2026 |
管理端口 |
2027 |
可观测端口 | |
| union / Prefill NodeManager | 从 3026 起 |
节点级 |
| Decode NodeManager | 从 4026 起 |
节点级 |
官方在同一节还给了"端口配置规划说明"表,逐条标了用途和注意事项(原文数值,一个字没改):
| 组件 | 推荐端口 | 官方说明 |
|---|---|---|
| Coordinator 推理 | 1025 |
使用 host 网络,通过 Coordinator 节点 IP 访问 |
| Coordinator 管理 | 1026 |
同节点内须避免被其他角色占用 |
| Coordinator 可观测 | 1027 |
/metrics |
| Controller 管理 | 2026 |
同节点部署时避开 1026 |
| Controller 可观测 | 2027 |
同节点部署时避开 1027 |
| union / Prefill NodeManager | 3026 起 |
按实例和节点规划 |
| Decode NodeManager | 4026 起 |
与 Prefill 区分 |
⭐ 这张表解释了"为什么要显式配"(归纳):1026 与 2026、1027 与 2027 是刻意的"错开设计" ——
官方特意写明"同节点部署时避开对方" —— 也就是说,官方是假设你会把 Controller 和 Coordinator 放在同一台机器上的。
⭐ 从这组数值能看出官方的规划习惯(归纳):
1xxx → Coordinator(调度器)
2xxx → Controller(控制器)
3xxx → union / Prefill NodeManager
4xxx → Decode NodeManager
"每类角色一个千位段" —— 这个规律很有用:你在日志或报错里看到一个端口号,就能猜出是谁的。
而且注意这条规则(官方原文): "部署在不同节点时可使用默认端口;同一节点部署多个角色时,推荐显式配置" —— 因为同节点会端口冲突。
④ 配置样例里的一条重要提醒
官方给出的 PD 分离端口配置示例中,有四个字段被明确标注了 // 请勿修改:
{
"motor_deploy_config": {
"p_instances_num": 1, // 请勿修改
"d_instances_num": 1, // 请勿修改
"single_p_instance_pod_num": 2, // 请勿修改
"single_d_instance_pod_num": 4 // 请勿修改
},
"motor_controller_config": {
"api_config": {
"controller_api_port": 2026,
"observability_api_port": 2027
}
},
"motor_coordinator_config": {
"api_config": {
"coordinator_api_infer_port": 1025,
"coordinator_api_mgmt_port": 1026,
"coordinator_obs_port": 1027
}
},
...
}
⭐⭐ 这是本段最值得留意的地方(归纳):
官方在示例里用 // 请勿修改 标了四个字段 ——
看到这种标注,说明"这几个值不是让你按业务调的,是被别的东西决定的"。
改它们的后果官方不会在示例里解释,但一定会在别处报错。
归纳成一条通用纪律: 配置文件里凡是官方写了"请勿修改 / 不要改"的字段,先照抄; 等能跑通了、也看懂它为什么是那个值了,再考虑要不要动。
⑤ 起服务后怎么验端口(本课"如何做"的收口)
官方给的服务验证只有一条命令 —— 它一次验了三件事:端口通不通、服务就绪没有、推理正常没有。
命令 curl -X POST http://<IP0>:1025/v1/chat/completions -H "Content-Type: application/json" -d '{"model":"<模型名>","messages":[{"role":"user","content":"who are you?"}],"max_tokens":36,"stream":true}'
含义:按官方给的方式向 Coordinator 的推理端口
1025发一次真实请求 —— 把前面所有环节串起来验一遍 拆解:http://<IP0>:1025(Coordinator 部署节点 IP + 推理端口,就是第七段那张表里的 1025)+/v1/chat/completions(OpenAI 风格接口)+"model"(填user_config.json里配的模型名)+"stream": true(流式返回) 用法:把<IP0>换成 Coordinator 部署节点 IP、model换成user_config.json里的模型名称;在任意可访问 Coordinator 的机器上执行(官方原文) 期望:返回流式 JSON —— 官方原话:「若返回流式 JSON,则说明推理正常」 不过会怎样:返回{"detail":"Service is not available"}→ 不是端口不通,是服务还没就绪(官方:「表示服务尚未就绪,可稍后重试」)→ 下一步看日志;连接被拒 / 超时 → 端口或 IP 不对,回上面那张端口规划表 ⭐ 这条命令本身就很值得记:"连不上"和"连上了但没就绪"是两种不同的失败,官方用同一个接口把两者分开了 ⚠️ 官方另注:HTTP 有安全风险,生产环境建议开启 HTTPS(接口与 TLS 配置见官方《业务接口》)
命令 docker logs docker_coordinator
含义:看 Coordinator 容器的日志 —— 官方在服务验证一节里直接点名了它 拆解:
docker logs(看容器标准输出)+ 容器名(就是启动时CONTAINER_NAME填的那个) 用法:docker logs docker_coordinator(输出太长可跟--tail 200) 期望:日志里能看到服务启动过程;配合上面那条 curl,能判断"没就绪"卡在哪一步 不过会怎样:报No such container→ 容器名写错,或那个容器根本没起来(回第六段按角色重新起) ⚠️ 这正是 4.0 路径第 ⑦ 步说的"出证据" —— 日志 + curl 回显,是你能拿给别人看的两样东西。
K8s 形态下换成官方给的日志采集脚本:
命令 bash show_log.sh
含义:官方日志采集脚本,采集并持续跟踪各角色日志(输出目录
log_collect/log/) 拆解:脚本在examples/deployer/下;执行前要先编辑log_collect/log_config.ini,把name_space改成与job_id相同的命名空间 用法:cd examples/deployer→ 改log_collect/log_config.ini→bash show_log.sh期望:各 Pod / Deployment 的日志汇总到log_collect/log/,controller、coordinator、P、D 都能看到 不过会怎样:name_space没改 → 采集不到任何日志(脚本不会提醒你写错了);报找不到脚本 → 当前目录不是examples/deployer
第八段 · 出问题怎么查
先按"角色"定位(本课的核心用法)
L3 的故障,第一步是确定"哪个角色出了问题"(归纳):
① 请求进不来 / 没有响应 → Coordinator(统一入口)?
② 接口不通 / 端口连不上 → EngineServer(RESTful EndPoints)?
③ 集群状态乱 / 不知道谁活着 → Controller(状态管控)?
④ 某个节点上的进程没起 → NodeManager(进程启动 + 健康状态)?
⑤ 部署/停止/伸缩动作失败 → Deployer(K8s 参考脚本)?
⑥ P 和 D 之间传不起来 → PD 分离的 KV 传输与 KV Cache Store?
按"部署形态"排(第二层)
| 形态 | 先查什么 |
|---|---|
| Docker-only | 容器是否都起来了、宿主机挂载的配置目录对不对(官方原文:"Docker 容器 + 宿主机挂载配置") |
| K8s | Pod 状态、Deployer 脚本、配置的 ConfigMap(官方提到准备 CONFIGMAP_PATH) |
排查表
| 现象 | 先查什么 |
|---|---|
| 多个角色挤在一台机器上,端口冲突 | 官方规则:同节点推荐显式配置端口(第七段的规划表) |
| 不知道某个端口是谁的 | 千位段规律:1xxx Coordinator / 2xxx Controller / 3xxx Prefill / 4xxx Decode |
| 配置怎么写 | 先找官方现成示例(deepseek_v4_flash 等),没有再考虑自动生成 |
| 改了配置就起不来 | 检查是不是动了标 // 请勿修改 的字段 |
| PD 分离起不来 | 它比混部多一件事:跨角色 KV 传输 + KV Cache Store |
| 想先跑通不想搞 K8s | 用 Docker-only 那条路(官方说它"同时适用于 PD 混部和 PD 分离") |
⭐ 一个最省时间的判断法
L3 集群问题,先问"有几个角色、都是否活着",再问"它们之间通不通"。
| 阶段 | 查什么 |
|---|---|
| 角色是否都起来了 | 各角色的进程/容器/ Pod 状态;NodeManager 管的就是这件事 |
| 角色之间通不通 | 端口(第七段表)、网络(同节点 vs 跨节点) |
| 通了但结果不对 | 往下走:引擎/模型/参数(回 D2、D4) |
先数人头,再看连线 —— 这两步做完,问题范围就缩到一两个角色了。
常见报错清单(现象文案出自官方原文)
⚠️ 数据纪律:下表每条的现象文案都出自官方原文(docker-only 部署文档与 K8s 部署文档的「服务验证」「运维技巧」两节)。 官方没有给 Motor 单列一份独立的报错码表 —— 清单之外的报错,按现象去官方案例库检索:
https://www.hiascend.com/document/caselibrary。
| 现象(官方原文) | 官方怎么解释 | 先查什么(回哪一段) |
|---|---|---|
{"detail":"Service is not available"} |
docker-only 文档:「表示服务尚未就绪,可稍后重试」;K8s 文档:「多为 P/D 或 Coordinator 尚未完全就绪」 | 不是端口错,是没就绪:先 docker logs docker_coordinator(第七段 ⑤),再确认 P/D 起没起(第六段) |
Pod 是 Running 但业务不通 |
「Pod 状态为 Running 仅表示已成功调度并启动,是否业务就绪仍需结合日志进一步确认」 |
别拿 Running 当通:用 bash show_log.sh 看日志(第七段 ⑤) |
权重加载超过约 10 分钟报 timeout |
「部分 vLLM 版本权重加载超过约 10 分钟可能报 timeout,通常不影响程序运行;以所用镜像/引擎版本说明为准」 |
先别改配置 —— 确认是不是只是加载慢,或权重路径不对(见下一条) |
Pod 拉不到镜像(如 ImagePullBackOff) |
「确保 image_name 在集群内可正常拉取」 |
镜像每个节点都要有(第五段形态一);docker load -i 后一定跟 docker images 确认 |
| 起不来,怀疑权重路径 | 「weight_mount_path 在宿主机上存在」 |
核对 user_config.json 里的 weight_mount_path 与启动脚本里挂载的那个绝对路径是否一致(第五段形态二) |
| 部署失败,想重来 | 「可先按 终止服务 小节的命令卸载,排查并修改配置后重新部署」 |
先卸载、再改配置、再部署 —— 别在脏环境上反复试(命令见下) |
| 直接删掉一个 Pod 却没触发实例重调度 | 「实例重调度能力依赖 MindCluster;P/D 实例含多个 Pod 时,直接删除其中一个 Pod 不会触发实例重调度」 | 这是官方写明的已知行为,不是你配错了 |
| 想测基线性能,数字总偏高 | 「Prefix Cache 默认开启」 | 在 engine_config 里关掉(vLLM 用 "no-enable-prefix-caching": true,SGLang 用 "disable_radix_cache": true) |
K8s 形态上"推倒重来"的那条官方命令(官方《终止服务》一节):
命令 bash delete.sh <namespace>
含义:卸载服务,把这次部署起来的角色全部清掉 拆解:
bash delete.sh(卸载脚本,在examples/deployer/下)+<namespace>(与user_config.json中job_id一致) 用法:cd examples/deployer→bash delete.sh mindie-motor期望:该命名空间下的 Pod / Deployment 被清空(再用kubectl get pods -n <namespace> -o wide看,应该是空的) 不过会怎样:namespace 写错 → 什么也没删掉,你还以为环境干净了;删完要核对配置再重新部署(官方原文顺序就是"卸载 → 排查并修改配置 → 重新部署") ⚠️ 别拿它当排障手段:反复"删了重来"会把现场毁掉 —— 先把日志留下来(第七段 ⑤),再卸载。
第九段 · 小结与下一课
三句话带走
- Motor 的核心设计是"控制面(Controller/Coordinator)与数据面(推理引擎)解耦" ——
所以它能"当前支持 vLLM,也能支持 SGLang"(换引擎只改
engine_type)。 - 五个子组件分工:Coordinator(统一入口、数据流枢纽)、Controller(决策大脑、RAS)、 EngineServer(对外 RESTful 接口 / OpenAI / Metrics)、NodeManager(节点级进程与健康)、 Deployer(K8s 部署与伸缩)。
- 端口按千位段规划:1xxx Coordinator / 2xxx Controller / 3xxx Prefill / 4xxx Decode; 同节点多角色必须显式配端口(官方原文)。
本课在体系里的位置
─── 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
本课留下的"钩子"
| 钩子 | 在哪一课填 |
|---|---|
| PD 分离的动机、KV 传输、KV Cache Store | D4 |
| 弹性伸缩、限流、主备倒换、虚推健康检查 | D5(官方 Motor 运维章节) |
user_config.json 的全量字段 |
官方《user_config 全量参数说明》 |
课后自测(3 题)
- "控制面与数据面解耦"带来了什么实际好处?用一句话说明。
- Coordinator、Controller、EngineServer、NodeManager 各管什么?"curl 端口不通"最先怀疑谁?
- 官方给的端口规划里,1025/2026/3026/4026 分别属于谁?能看出什么规律?
附录 A · 本课引用的官方出处
| 内容 | 出处 |
|---|---|
| 「控制面(Controller/Coordinator)与数据面(推理引擎)解耦」;当前支持的引擎一览(vLLM 推荐 / SGLang POC) | 官方 MindIE《支持的推理引擎》zh/mindie/310/mindiemotor/motordev/docs/zh/user_guide/features/supported_inference_engines.md |
| 五个子组件的官方说明(Controller / Coordinator / Deployer / EngineServer / NodeManager) | 官方《MindIE 是什么》表 1zh/mindie/310/whatismindie/MindIE/26.1.0/zh/what_is_mindie.md |
| 「增强 PD 分离服务的可靠性、可用性和可服务性」 | 同上(关键功能特性 · 服务化部署) |
「不使用 Kubernetes deployer、仅用 Docker 容器 + 宿主机挂载配置」;PD 分离/混部对照表;user_config.json + env.json;官方现成配置示例(deepseek_v4_flash 等);端口规划(1025/1026/1027、2026/2027、3026、4026);示例中含 // 请勿修改 标注的字段 |
官方《docker-only 部署多容器 PD 服务》zh/mindie/310/mindiemotor/motordev/docs/zh/user_guide/deployment/docker/multi_container.md |
| 基于 K8s 的 PD 分离服务部署("完整详细""生产环境配置优化实践") | 官方《基于 K8s 多容器 PD 分离服务部署》.../user_guide/deployment/k8s/pd_disaggregation_deployment.md |
user_config 全量参数说明 |
官方《user_config 全量参数说明》.../user_guide/configuration/config_reference.md |
| 配置自动生成指导 | 官方《MindIE Motor 配置自动生成指导》.../examples/infer_engines/vllm/models/README.md |
⚠️ 数据纪律提示: ① 本课的端口数值、字段名、示例结构均出自官方示例,会随版本变化 —— 实际以官方当前文档为准; ② PD 分离的计算原理与收益本课未展开(留 D4),只给官方对照表; ③ RAS 的英文展开是行业通用缩写,本课标注了它是"常见缩写"而非官方逐字展开; ④ 标注「归纳」的图与解读(组件关系图、千位段规律、三层判断法)非官方口径。
附录 B · 本课出现的名词与配置详解
组件名词表
| 名词 | 官方职责(一句话) | 换成"人话"(归纳) |
|---|---|---|
| Controller | 集群的状态管控器和决策大脑,全局业务状态管控及 RAS 能力决策 | 管状态的 |
| Coordinator | 用户推理请求的统一入口,请求调度、管理与转发,集群数据流枢纽 | 管分发的 |
| Deployer | 基于 Kubernetes 的部署参考脚本,服务启动/停止/弹性伸缩 | 管部署与扩缩的 |
| EngineServer | 节点推理服务入口,统一 RESTful EndPoints(OpenAI 接口、Metrics) | 管对外接口的 |
| NodeManager | 节点级服务管理器,节点级服务进程启动 + 节点级健康状态管理 | 管这台机器上进程死活的 |
配置文件
| 文件 | 说明 |
|---|---|
user_config.json |
主配置(引擎、部署、各角色与端口)。全量字段见官方《user_config 全量参数说明》 |
env.json |
环境相关配置 |
examples 目录 |
官方示例,从镜像拷到宿主机后设置 EXAMPLES_PATH 指向它 |
端口的读法
1025 1026 1027 → Coordinator(推理 / 管理 / 可观测)
2026 2027 → Controller(管理 / 可观测)
3026 … → union / Prefill NodeManager
4026 … → Decode NodeManager
- 规律:每类角色占一个千位段(1xxx / 2xxx / 3xxx / 4xxx)。
- 何时需要显式配:同一节点部署多个角色时(官方原文"推荐显式配置")。
- 不同节点时:可用默认端口。
部署形态
| 形态 | 一句话 |
|---|---|
| K8s 形态 | 用 Kubernetes 编排(对应 Deployer),官方定位"生产环境配置优化实践" |
| Docker-only 形态 | 只用 Docker + 宿主机挂载配置,官方给了端到端流程,PD 混部与 PD 分离都适用 |
两种服务模式
| 模式 | Engine 容器 | 跨角色 KV 传输 |
|---|---|---|
| PD 分离 | Prefill 容器 + Decode 容器 | 需要(按需拉起 KV Cache Store) |
| PD 混部 | union 容器 | 不需要 |
附:D2 与 D3 的分工
| 问题 | 在哪一课 |
|---|---|
| vLLM Ascend 是什么、怎么起单个服务、怎么验证 | D2 |
| Motor 的五个子组件、两种部署形态、PD 分离 vs 混部、配置文件与端口规划 | 本课(D3) |
| PD 分离的动机与做法细节 | D4 |
| 起停、限流、主备倒换、伸缩、调优 | D5 |