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

昇腾部署第 D3 课:MindIE Motor——五个子组件与集群的骨架

2026-10-05

编制:观澜|日期: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 的故障几乎都是"某个角色没起来"或"角色之间连不上"。 不知道有几个角色,就不知道要去哪台机器上查。


第二段 · 学习目标

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

  1. Motor 的架构原则(官方原话)+ 这条原则带来的好处
  2. 五个子组件各干什么,哪些是"控制面"、哪些是"数据面"
  3. 两种部署形态(K8s / Docker-only)的区别与选择
  4. PD 分离与 PD 混部的官方对照(Engine 容器、KV 传输、启动方式)
  5. 配置文件是哪几个、端口怎么规划

第三段 · 架构原则:控制面与数据面解耦

官方原文(《支持的推理引擎》):

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 写错 → 什么也没删掉,你还以为环境干净了;删完要核对配置再重新部署(官方原文顺序就是"卸载 → 排查并修改配置 → 重新部署") ⚠️ 别拿它当排障手段:反复"删了重来"会把现场毁掉 —— 先把日志留下来(第七段 ⑤),再卸载。


第九段 · 小结与下一课

三句话带走

  1. Motor 的核心设计是"控制面(Controller/Coordinator)与数据面(推理引擎)解耦" —— 所以它能"当前支持 vLLM,也能支持 SGLang"(换引擎只改 engine_type)。
  2. 五个子组件分工:Coordinator(统一入口、数据流枢纽)、Controller(决策大脑、RAS)、 EngineServer(对外 RESTful 接口 / OpenAI / Metrics)、NodeManager(节点级进程与健康)、 Deployer(K8s 部署与伸缩)。
  3. 端口按千位段规划: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 题)

  1. "控制面与数据面解耦"带来了什么实际好处?用一句话说明。
  2. Coordinator、Controller、EngineServer、NodeManager 各管什么?"curl 端口不通"最先怀疑谁?
  3. 官方给的端口规划里,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 是什么》表 1
zh/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
选修 · 昇腾 AI 部署实操 本机读完 0/51
选修 C · 昇腾 AI 部署实操
从零把昇腾 AI 部署学一遍:先看懂分几层,再逐层装起来,最后出问题能自己定位到哪一层。不需要先有卡就能开始。
看完整学习路径(6 站 + 3 门选修) · 进度只存在你这台设备的浏览器里,不用登录
辽ICP备2026019944号-1