已合并
【Feature】新增mindie-motor_standalone_coordinator部署模式 #910
【Feature】新增mindie-motor_standalone_coordinator部署模式 #910
已合并
灏悦心陈创建于 9月8日
灏悦心陈
灏悦心陈
9月8日

1. 合入背景

Fixes #546

2. 修改内容

本 PR 为 MindIE Motor 引入"独立部署(Standalone)Coordinator"能力:通过新增 controller_integration_enabled 配置项,使 Coordinator 可在不依赖 Controller 控制面的场景下独立运行,并新增一套仅含路由信息的"外部部署器(External Deployer)"实例事件协议,将最小拓扑信息转换为内部 InsEventMsg 后复用既有纳管与调度流程。同时补充了 KV conductor 在未配置时的跳过逻辑、裸机部署的配置与启停脚本示例,以及配套的单元测试和端到端冒烟测试。

3. 资料变更

涉及
MindIE Motor 引入"独立部署(Standalone)Coordinator"能力

4. 接口变更

不涉及

5. 测试结果

7AE05616-40A9-4D0D-8E2C-7C65B0457400.png

用例 C1 文档 SET 1P1D + 模型名自动发现 + readiness
结果 通过
时间 08:47:00

步骤:POST /instances/refresh body=topology_set_1p1d.json(无 model_name)

HTTP 响应 resp_set_1p1d.json
{"status":"success","message":"Instance refresh completed",
"data":{"event_type":"set","instance_count":2,
"timestamp":"2026-09-09T00:47:00.522166+00:00"}}

GET /readiness readiness_after_set_1p1d.json
{"status":"ok","message":"Coordinator is ok","ready":true}

GET /instances instances_after_set_1p1d.json
count=2
id=1 prefill model_name=qwen3-8b job_name=external-prefill-1 endpoint id=0 port=8100
id=3 decode model_name=qwen3-8b job_name=external-decode-3 endpoint id=0 port=8200

Coordinator 日志(node/node-37-110.log / coordinator.log)
08:47:00 Refresh instances: event_type=EventType.SET, count=2, instance_ids=[(1, 'prefill'), (3, 'decode')]
08:47:00 Added instance ID 1 (role: prefill, job_name: external-prefill-1) with 1 endpoints to available pool successfully
08:47:00 Added instance ID 3 (role: decode, job_name: external-decode-3) with 1 endpoints to available pool successfully
08:47:00 HBM DP registered (ZMQ+HTTP): instance=vllm-prefill-1 dp=0 replay=tcp://127.0.0.1:6677
08:47:00 Refresh instances done: E=0, P=1, D=1, U=0

判定:省略 model_name 仍按精简协议解析;模型从引擎发现为 qwen3-8b;P/D 齐后 ready=true。


用例 C2 ADD P2 不启动进程 / DEL P2 不停止进程
结果 通过
时间 08:47:06

ADD 响应 resp_add_p2.json
{"status":"success","data":{"event_type":"add","instance_count":1,
"timestamp":"2026-09-09T00:47:06.115084+00:00"}}

ADD 后实例表 instances_after_add_p2.json count=3
新增 id=2 prefill job_name=external-prefill-2 endpoint id=0 port=8110

DEL 响应 resp_del_p2.json
{"status":"success","data":{"event_type":"del","instance_count":1,
"timestamp":"2026-09-09T00:47:06.318868+00:00"}}

DEL 后实例表 instances_after_del_p2.json count=2(仅 P1+D)

进程核验(当时现场)
p2_pid_before=39516
p2_pid_after_add=39516 same=1
p2_pid_after_del=39516 health=200 still_alive=1

日志
08:47:06 Refresh instances: event_type=EventType.ADD, count=1, instance_ids=[(2, 'prefill')]
08:47:06 Added instance ID 2 ... Refresh instances done: E=0, P=2, D=1, U=0
08:47:06 Refresh instances: event_type=EventType.DEL, count=1, instance_ids=[(2, 'prefill')]
08:47:06 Refresh instances done: E=0, P=1, D=1, U=0

判定:add/del 只改 Coordinator 纳管表,不启停 P2 进程。


用例 C3 Coordinator 重启后必须重放 SET,重放后推理恢复
结果 通过
时间 08:55:58 ~ 08:56:16

重启后、重放前 readiness_before_replay.json
{"status":"ok","message":"Coordinator is ok","ready":false}

重放 SET resp_replay_set.json
{"status":"success","data":{"event_type":"set","instance_count":3,
"timestamp":"2026-09-09T00:56:06.200305+00:00"}}

重放后 readiness_after_replay.json
{"status":"ok","message":"Coordinator is ok","ready":true}

推理 infer_after_replay.json
id=cmpl-178891537681151000006c969451
text=" I'm a new user, so I'm not sure what to do. I"
usage prompt_tokens=6 total_tokens=22 completion_tokens=16

coordinator_restart.log
08:56:04 Uvicorn running on http://0.0.0.0:19001
08:56:06 Refresh instances: event_type=EventType.SET, count=3,
instance_ids=[(1, 'prefill'), (2, 'prefill'), (3, 'decode')]
08:56:06 Added instance ID 1/2/3 ... to available pool successfully
08:56:06 HBM DP registered: vllm-prefill-1 dp=0 ; vllm-prefill-2 dp=0
08:56:06 Refresh instances done: E=0, P=2, D=1, U=0
08:56:06 AUDIT event_type=instance_refresh result=success method=POST path=/instances/refresh
08:56:06 "POST /instances/refresh HTTP/1.1" 200 OK
08:56:06 [Readiness] Coordinator is ready. result=ok_standby instances_status=required_met
08:56:16 Worker 0: metaserver port enabled: 19100
08:56:16 scheduled role=decode instance=3 endpoint=0
08:56:16 scheduled role=prefill instance=1 endpoint=0
req_id=178891537681151000006c969451 policy=kv_cache_affinity matched=0 proposed=1-0
HTTPClientPool address=127.0.0.1:8100 以及 127.0.0.1:8200

判定:重启丢内存拓扑,ready=false;外部重放完整 set 后 ready=true,Layerwise 推理恢复(含 metaserver 19100)。


用例 C4 Layerwise 1P1D 推理(concurrent_engine_sync)
结果 通过
时间 08:53:22

SET 响应 resp_set_1p1d_concurrent.json
{"status":"success","data":{"event_type":"set","instance_count":2}}

推理 POST :19000/v1/completions infer_1p1d_concurrent.json
id=cmpl-17889152027183390000b61d2582
model=qwen3-8b
text=" I'm a new user, so I'm not sure what to do. I"
usage prompt_tokens=6 total_tokens=22 completion_tokens=16
P1 /health 仍为 200

日志
08:53:22 Refresh instances done: E=0, P=1, D=1, U=0
08:53:22 CircuitBreaker cleared all: count=1
08:53:22 scheduled role=prefill req_id=17889152027183390000b61d2582
instance=1 endpoint=0 policy=kv_cache_affinity matched=0 proposed=1-0

判定:精简 SET + concurrent 后,Coordinator 作为推理入口打通 1P1D。


用例 C5 文档 SET 2P1D,endpoint.id 全部为 0
结果 通过
时间 08:53:42

SET 响应 resp_set_2p1d_concurrent.json instance_count=3
GET /instances instances_2p1d.json
id=1 prefill endpoint id=0 port=8100
id=2 prefill endpoint id=0 port=8110
id=3 decode endpoint id=0 port=8200

日志
08:53:42 Refresh instances: event_type=EventType.SET, count=3,
instance_ids=[(1, 'prefill'), (2, 'prefill'), (3, 'decode')]
08:53:42 Added instance ID 2 (role: prefill, job_name: external-prefill-2)
08:53:42 HBM DP registered: instance=vllm-prefill-2 dp=0 replay=tcp://127.0.0.1:6677
08:53:42 Refresh instances done: E=0, P=2, D=1, U=0

判定:与昨晚错误 JSON(P2 endpoint.id=1)不同,本次两边都是 id=0、dp=0。


用例 C6 P1 KV 亲和:热请求 matched=640,查找 key 为 endpoint.id=0
结果 通过
时间 08:53:54 ~ 08:53:56

请求 affinity_request.json 约 666 prompt tokens,block=128,期望命中 5 块=640。

冷请求 p1_warm.json
id=chatcmpl-17889152340817130000091faa74 usage prompt_tokens=666

热请求 p1_hot.json
id=chatcmpl-178891523688943700001fe22583 usage prompt_tokens=666 completion_tokens=8

日志
08:53:54 scheduled ... instance=1 endpoint=0 policy=load_balance matched=None
req_id=17889152340817130000091faa74 committed=666.0
08:53:56 scheduled ... instance=1 endpoint=0 policy=kv_cache_affinity matched=640
req_id=178891523688943700001fe22583 committed=26.0 proposed=1-0

判定:文档 id=0 与引擎 DP0、调度 DP["0"] 对齐。昨晚 id=1 → DP["1"] → matched=0 的问题本次未复现。


用例 C7 P2 独有前缀亲和:订 5578 后 matched=384,endpoint=0
结果 通过
时间 08:55:35 ~ 08:55:37

手工订 conductor(SET 协议带不了 per-instance ZMQ 口)
POST :19333/register resp_p2_register.json {"status":"ok"}
instance_id=vllm-prefill-2 dp_rank=0 npu=tcp://127.0.0.1:5578 replay=6678

独有前缀 p2_unique_hot.json
id=chatcmpl-17889153379236990000289e62a6
usage prompt_tokens=424 total_tokens=432

日志
08:55:35 scheduled instance=2 endpoint=0 policy=load_balance matched=None committed=424.0
req_id=178891533516689100006adc96b5
08:55:37 scheduled instance=2 endpoint=0 policy=kv_cache_affinity matched=384
req_id=17889153379236990000289e62a6 proposed=2-0

判定:查找 key 仍是 "0"(不是 "1"),P2 自己的事件能计分。

附:自动注册时 P1/P2 都订 replay=tcp://127.0.0.1:6677(5577+id,id 都是 0)。
P2 真实口是 5578。这是同机多 P 的 conductor 端口偏移限制,不是 SET id 写错。


用例 C8 liu意见2:省略 model_name 的精简 SET
结果 通过
时间 09:41:16

liu_omit_model.json
{"status":"success","data":{"event_type":"set","instance_count":2}} http=200

liu_instances.json
id=1 prefill model_name=qwen3-8b job_name=external-prefill-1 endpoint id=0:8100
id=3 decode model_name=qwen3-8b job_name=external-decode-3 endpoint id=0:8200


用例 C9 liu意见3:拒绝 decode_colocation,放行 concurrent_engine_sync
结果 通过
时间 09:41:17

liu_reject_colocation.out HTTP 400
External Deployer dispatch_capabilities must be one of:
prefill_handoff_decode, concurrent_engine_sync
input_value='decode_colocation'

liu_allow_concurrent.out HTTP 200
{"status":"success","data":{"event_type":"set","instance_count":2}}


用例 C10 liu意见1:无配置 / 无 Controller 段时不按有 Controller 处理
结果 通过
时间 09:42:27 ~ 09:43:08

运行时探测 has_motor_controller_config()(liu_c1/check_has_ctrl.py)
missing_file => False
unset USER_CONFIG_PATH => False
配置存在但无 motor_controller_config => False
配置含 motor_controller_config => True

无配置启动日志 liu_c1/missing_cfg.log
Configuration file does not exist, using default configuration
全程无 127.0.0.1:1026 / ControllerConfig / report_alarm
(本机 1025 已被占用,随后 Aborting,属端口冲突,不是告警误打)

独立端口 19120/19121 启动 liu_c1/standalone_only.log
无 127.0.0.1:1026、无 report_alarm

6. CheckList

PR提交人对以下CheckList自检项进行全量自检,自检通过或不涉及,均修改 [ ] 为 [x]

[ ] 代码注释完备

[ ] 正确记录维测日志

[ ] 是否有UT用例

[ ] 若涉及多线程场景,考虑了并发场景,不存在死锁问题

likedislike
Pull Request已成功合入, 合并人@wangyang
(感谢 灏悦心陈 的贡献)
灏悦心陈灏悦心陈
9月8日 关联了issue:[Feature-Request|需求反馈]: 添加standalone-coordinator部署方式
atomgit-bot
atomgit-bot
9月8日 评论:

变更摘要

本 PR 为 MindIE Motor 引入「独立部署(Standalone)Coordinator」能力:Coordinator 可在不依赖 Controller 控制面的场景下独立运行,通过新增一套仅含路由信息的「外部部署器(External Deployer)」实例事件协议(ExternalInsEventMsg),将最小拓扑信息转换为内部 InsEventMsg 后复用既有纳管与调度流程;同时补充 KV conductor 未配置时的跳过逻辑、Controller 配置惰性加载与告警上报跳过、原生引擎模型名解析,以及配套单元测试和端到端冒烟测试。

主要改动

  • 新增外部部署器事件协议(motor/common/resources/http_msg_spec.py):新增 ExternalEndpoint、ExternalInstance、ExternalInsEventMsg,仅携带 id/role/endpoints(host:port)等路由信息;to_internal() 将其转换为内部 InsEventMsg(含 job_name、parallel_config、端点按 IP 归组等),并校验事件类型(仅支持 set/add/del)、engine_type(仅 vllm)、实例/端点去重及端口范围。

  • 管理面接入独立事件解析(motor/coordinator/api_server/management_server.py):新增 _is_standalone_instance_refresh() 区分外部部署器与 Controller 两种负载形状,_parse_instance_event() 据此选择 InsEventMsg 或 ExternalInsEventMsg 解析;省略 model_name 时由 _resolve_external_model_name() 通过 NativeEngineApiClient.query_model_ids() 查询原生引擎 /v1/models(经 asyncio.to_thread 卸载阻塞 I/O)。

  • Controller 解耦与告警跳过(motor/coordinator/api_client/controller_api_client.py、motor/config/config_utils.py):ControllerConfig/CoordinatorConfig 改为惰性加载;新增 has_motor_controller_config() 判断用户配置是否包含 motor_controller_config,report_alarms() 在独立部署模式下跳过告警上报且不再加载 Controller 配置。

  • KV conductor 空配置跳过(motor/coordinator/api_client/conductor_api_client.py):register_kv_instance() 与 unregister_kv_instance() 在 conductor_service 为空时跳过 KV conductor 实例注册/注销流程。

  • 测试覆盖(tests/):新增 tests/e2e/test_standalone_coordinator_smoke.py 端到端冒烟测试(环境变量开启,覆盖 SET/ADD/DEL 成员事件、推理连通与重启回放),并补充外部拓扑协议校验、独立 Coordinator 配置加载、report_alarms 跳过及 KV 注册相关单元测试。

likedislike
不准确?
atomgit-bot
atomgit-bot
9月8日 评论:

代码审查

✅ 未发现问题

likedislike
不准确?
renxiaolong12
9月8日 评论:

🤖 AIDD 文档质量检测报告

PR #910 的文档检测已全部完成。

📊 任务统计:共 3 个任务(✅ 3 完成 / ❌ 0 失败 / ⊘ 0 取消)
🐛 问题统计:共 1 个问题(致命 0 / 严重 0 / 一般 1 / 提示 0)

🔗 查看完整报告:点击进入平台报告
🔗 查看 PR 检测问题:点击查看问题列表

评论由 AIDD 自动生成,多次推送会增量更新本评论。

likedislike
Xxiangjie10成员
9月8日 添加了label:pr-audit-failed
此处折叠了55条消息 查看更多
灏悦心陈灏悦心陈
9月9日 修改了pull request 的描述
灏悦心陈灏悦心陈
9月9日 修改了pull request 的描述
灏悦心陈灏悦心陈
9月9日 修改了pull request 的描述
灏悦心陈灏悦心陈
9月9日 修改了pull request 的描述
wangyangwangyang成员
9月9日 合入了pull request