vLLM MoRIIOConnector 实战指南:基于 MoRI-IO 的高性能 PD 分离 KV Cache 传输
MoRIIOConnector 是 vLLM 中一个面向 PD(Prefill-Decode)分离部署的高性能 KV 缓存连接器,构建于 ROCm 的 MoRI-IO 点对点通信库之上,通过 RDMA 或 xGMI 传输 KV 字节,并叠加轻量 TCP 控制面完成握手、块号交换与完成信号。读完本文,你将掌握 MoRIIOConnector 的安装部署、单机/多机 PD 分离的完整启动流程、全部应用级与传输层配置项的含义,以及 RDMA 环境常见故障的定位方法,并能结合 vLLM 仓库源码理解其 WRITE/READ 两种传输模式的底层工作机制。
一、架构总览:MoRIIOConnector 在 vLLM 中的位置
在 vLLM 的 KV 连接器工厂注册表中,MoRIIOConnector 被映射到实现模块,见 factory 注册表。其核心实现位于 vllm/distributed/kv_transfer/kv_connector/v1/moriio/ 目录:
- moriio_connector.py:连接器主体,实现
KVConnectorBase_V1接口,负责请求级元数据管理、TP rank 对齐、与路由代理的注册/心跳; - moriio_common.py:配置解析(
MoRIIOConfig)、常量、ZMQ 地址编解码; - moriio_engine.py:MoRI IOEngine 封装,
MoRIIOWriter实现 WRITE 模式的逐层写入状态机; - moriio_layout.py:KV 块布局与按层传输几何计算(支持 MLA 与标准 Attention 布局)。
从源码结构看,连接器依赖 mori.io 模块(IOEngine、IOEngineConfig、BackendType 等);若环境未安装 MoRI,导入阶段仅会记录错误日志并置 MoRIIO_enabled = False,因此该连接器只在安装了 MoRI 的 ROCm 环境中可用。配套的单元测试位于 tests/v1/kv_connector/unit/,覆盖路由公平性、TP ACK、unmap 等场景,可作为行为参照。
二、前置条件与安装
MoRI 有两种安装方式:
- Docker(推荐):MoRI 随官方 ROCm vLLM 镜像
vllm/vllm-openai-rocm:nightly一起提供,开箱即用; - 手动安装:
pip install amd_mori
镜像构建细节可参考 Dockerfile.rocm_base,从源码构建 MoRI 的方法见 MoRI 官方仓库(ROCm 的 mori 项目)。
若使用 RDMA 后端,还需要安装与主机内核模块/固件版本匹配的网卡用户态(userspace)库,具体见文末附录:安装 NIC 用户态库。
三、单机基本用法
启动顺序上,建议先启动代理(proxy):producer 与 consumer 实例会持续重试注册,直到代理可达。以下示例以单机 8 卡、Qwen3-235B-A22B-FP8 模型为例。
3.1 Producer(Prefiller)配置
启动一个生成 KV 缓存的 prefiller 实例(GPU 0-3):
# Prefill instance (GPU 0-3)
export VLLM_ROCM_USE_AITER=1
export CUDA_VISIBLE_DEVICES=0,1,2,3
export HIP_VISIBLE_DEVICES=0,1,2,3
vllm serve Qwen/Qwen3-235B-A22B-FP8 \
-tp 4 \
--port 20005 \
--gpu-memory-utilization 0.9 \
--kv-transfer-config '{
"kv_connector": "MoRIIOConnector",
"kv_role": "kv_producer",
"kv_connector_extra_config": {
"proxy_ip": "127.0.0.1",
"proxy_ping_port": "36367",
"http_port": "20005",
"handshake_port": "6301",
"notify_port": "6105"
}
}'
3.2 Consumer(Decoder)配置
启动消费 KV 缓存的 decoder 实例(GPU 4-7):
# Decode instance (GPU 4-7)
export VLLM_ROCM_USE_AITER=1
export CUDA_VISIBLE_DEVICES=4,5,6,7
export HIP_VISIBLE_DEVICES=4,5,6,7
vllm serve Qwen/Qwen3-235B-A22B-FP8 \
-tp 4 \
--port 40005 \
--gpu-memory-utilization 0.9 \
--kv-transfer-config '{
"kv_connector": "MoRIIOConnector",
"kv_role": "kv_consumer",
"kv_connector_extra_config": {
"proxy_ip": "127.0.0.1",
"http_port": "40005",
"proxy_ping_port": "36367",
"handshake_port": "7301",
"notify_port": "7501"
}
}'
注意两侧 kv_role 不同(kv_producer / kv_consumer),且端口错开以避免冲突。
3.3 代理服务
代理位于 producer 与 consumer 之前,负责接收用户请求并路由到对应实例。vllm-router 是推荐的代理,既可手动安装也可用 Docker 容器运行。注意下面命令中的端口 36367 就是各 vLLM 实例配置的 proxy_ping_port。
Docker 方式:
docker run \
--network host \
vllm/vllm-router:nightly \
vllm-router \
--vllm-pd-disaggregation \
--kv-connector moriio \
--vllm-discovery-address "0.0.0.0:36367"
手动安装方式:
pip install vllm-router
vllm-router \
--vllm-pd-disaggregation \
--kv-connector moriio \
--vllm-discovery-address "0.0.0.0:36367"
作为替代,也可以运行 vLLM 自带的参考实现代理(单节点协议参考,非生产路由):
cd <path_to>/vllm
pip install quart aiohttp msgpack
python examples/disaggregated/disaggregated_serving/moriio_toy_proxy_server.py
从 moriio_toy_proxy_server.py 的源码可以看到代理与实例之间的注册协议:每个 vLLM 实例通过 ZMQ ROUTER 向代理的注册端口发送 msgpack 消息,消息需包含 http_address、zmq_address(格式为 host:IP,handshake:PORT,notify:PORT)、dp_size、tp_size、transfer_mode 等字段;代理把两侧实例的 zmq 地址嵌入 request_id(形如 ___prefill_addr_{zmq}___decode_addr_{zmq}_{32位hex}),由连接器在对端请求中解析出对端连接信息——这一编解码逻辑正是 parse_moriio_zmq_address 所实现的,且解析时按第一个冒号切分以兼容 IPv6 地址。该参考代理文件头部的注释也明确:它演示的是单节点下的 DP-rank 绑定契约,生产多 Pod 部署应使用 llm-d-router、vllm-router 或满足同一 kv_transfer_params 契约的路由 sidecar。
四、配置详解
MoRIIOConnector 的配置分为两层:应用级(角色、控制面端口、传输模式)与传输级(RDMA / xGMI 后端及其调优参数),全部通过 --kv-transfer-config.kv_connector_extra_config 传入。源码中这些键在 MoRIIOConfig.from_vllm_config 中被逐一解析。
4.1 应用级配置
传输模式(Mode):MoRI 提供 WRITE 与 READ 两种工作模式。
- WRITE 模式(默认):producer 在每一层计算完成后,主动把该层的 KV 块推送到 consumer 的内存中;
- READ 模式:consumer 在收到"块已就绪"通知后,一次性从 producer 拉取全部 KV 块。
READ 模式通过 --kv-transfer-config.kv_connector_extra_config.read_mode true 开启。源码侧的解析见 get_moriio_mode:仅当值为 "true" 或 "1"(不区分大小写)时进入 READ 模式,否则回落到 WRITE。
WRITE 模式的逐层写入状态机在 MoRIIOWriter 中有明确注释:decoder 先发送目标块分配信息(block allocation),prefiller 在每层的 CUDA event 之后调度一次写入,前向结束后对已调度写入计数做"封层"(seal),所有写入完成后再通知 decoder 并释放 producer 侧块。
控制面配置:MoRI 的 KV 字节流走 RDMA/xGMI,但 producer 与 consumer 之间还需要带外的 TCP 通道来完成握手、块号交换、存活探测与完成信号。以下键均位于 kv_connector_extra_config 之下:
| 配置键 | 含义 |
|---|---|
proxy_ip |
PD 分离代理/路由器的 IP 地址。每个 vLLM 实例用它注册自身并发送心跳,让代理知道向哪里路由请求 |
proxy_ping_port |
proxy_ip 上代理监听实例心跳与注册消息的 TCP 端口,用于探测死掉的 vLLM 实例、保持路由表新鲜 |
http_port |
本 vLLM 实例对外暴露 OpenAI 兼容 API 的 HTTP 端口。代理注册该端口,在选定实例后把用户请求转发过来 |
handshake_port |
prefiller 与 decoder 之间一次性 MoRI 引擎握手使用的 TCP 端口,两侧在此交换 RDMA 引擎描述符,之后才能开始 KV 传输 |
notify_port |
prefiller 与 decoder 之间控制和同步消息的 TCP 端口,两种模式下用途不同(见下) |
notify_port 在两种模式下的具体职责:
- WRITE 模式:
- 块分配:decoder 把自身的块 id 通知给 prefiller,prefiller 据此把计算好的 KV 块推到 decoder 实例的正确位置;
- 完成:所有块传输完成后,prefiller 通知 decoder 可以安全使用这些块。
- READ 模式:
- 完成:decoder 从 prefiller 读走全部块后,通知 prefiller 可以释放其 KV 缓存块。
注意:
notify_port是作为**基地址端口(base port)**使用的。实例内每个 (DP rank, TP rank) 对使用notify_port + offset,偏移量由 rank 计算。请确保从notify_port起始的一段端口范围在主机上空闲。
偏移量计算逻辑见 get_port_offset:offset = dp_rank * tp_size + tp_rank,并在 from_vllm_config 中以 notify_port = base_notify_port + port_offset 的形式生效。此外,host_ip 键可显式指定实例对外通告的 KV 传输 IP,用于 Ray 等框架下 get_ip() 解析到不可路由地址的场景(见 resolve_host_ip);transfer_timeout(默认 30 秒)与 defer_timeout(默认 60 秒)两个超时项分别控制等待传输完成与回收无完成的延迟发送,均可在 kv_connector_extra_config 中覆盖,见 MoRIIOConstants。
值得一提的是,源码中的 _DEPRECATED_ENV_VARS 表明早期版本曾用 VLLM_MORIIO_CONNECTOR_READ_MODE、VLLM_MORIIO_QP_PER_TRANSFER、VLLM_MORIIO_POST_BATCH_SIZE、VLLM_MORIIO_NUM_WORKERS 等环境变量传递这些参数,现已弃用并会被忽略,统一改由 kv_connector_extra_config 传入。
4.2 传输级配置(Transport)
MoRI 有两个传输后端:RDMA 与 xGMI。通过 --kv-transfer-config.kv_connector_extra_config.backend $BACKEND 选择,$BACKEND 取 rdma 或 xgmi。源码校验逻辑(moriio_common.py)对后端名做小写化并严格白名单校验,取值非法会直接抛 ValueError。RDMA 是默认后端,多节点部署应使用 RDMA。
RDMA 后端
qp_per_transfer:每次传输使用的 RDMA 队列对(Queue Pair)数量,默认 1。更多 QP 可让单次传输在多个 QP 上条带化,提高网卡并发度,代价是占用更多 RDMA 资源;post_batch_size:一次ibv_post_send门铃(doorbell)中批处理的 RDMA 工作请求(Work Request)数量,默认 -1,即交给 MoRI 后端默认值。更大的批次能降低每个 WR 的提交开销;num_workers:MoRI 用于提交传输与轮询完成事件(completion)的后台工作线程数,默认 1。
高级用户还可以通过 MORI_IO_QP_MAX_SEND_WR、MORI_IO_QP_MAX_CQE 等环境变量直接配置 MoRI 库本身。这些是 MoRI 库变量,与 vLLM 侧的配置项相互独立,细节见 MoRI 官方仓库。
xGMI 后端
当 prefiller 与 decoder 位于同一物理主机时,可改用 xGMI 后端:传输走 AMD GPU 之间的 xGMI fabric,完全绕开网卡。该后端目前只能通过 MoRI 专属的环境变量配置,参见 MoRI 官方仓库。注意上文的 RDMA 调优参数(qp_per_transfer、post_batch_size、num_workers)在 xGMI 后端下会被忽略(源码注释明确标注 "Knobs for RDMA transfers, ignored if on xgmi backend")。
五、多节点部署(1P1D)
以下示例展示在两节点上运行 1P1D 部署:代理与 prefill 实例部署在同一节点。示例镜像为 vllm/vllm-openai-rocm:nightly,模型为 deepseek-ai/DeepSeek-R1-0528。
5.1 两节点通用设置
# Set on both nodes before running any command
export PREFILL_IP=<node1-ip>
export DECODE_IP=<node2-ip>
5.2 Node 1:代理 + Prefill 实例
先按代理服务一节启动代理,然后启动 prefill 实例:
docker run \
--name moriio-prefill \
--init --network host --ipc host --privileged \
--security-opt seccomp=unconfined \
--ulimit memlock=-1 --ulimit stack=67108864 --shm-size 256G \
--group-add video --group-add render \
--device /dev/kfd --device /dev/dri --device /dev/infiniband \
-e VLLM_ROCM_USE_AITER=1 \
vllm/vllm-openai-rocm:nightly \
deepseek-ai/DeepSeek-R1-0528 \
--port 8100 \
--tensor-parallel-size 8 \
--enable-expert-parallel \
--gpu-memory-utilization 0.8 \
--trust-remote-code \
--kv-transfer-config '{
"kv_connector": "MoRIIOConnector",
"kv_role": "kv_producer",
"kv_connector_extra_config": {
"proxy_ip": "'"${PREFILL_IP}"'",
"proxy_ping_port": "36367",
"http_port": "8100",
"handshake_port": "6301",
"notify_port": "61005"
}
}'
5.3 Node 2:Decode 实例
docker run \
--name moriio-decode \
--init --network host --ipc host --privileged \
--security-opt seccomp=unconfined \
--ulimit memlock=-1 --ulimit stack=67108864 --shm-size 256G \
--group-add video --group-add render \
--device /dev/kfd --device /dev/dri --device /dev/infiniband \
-e VLLM_ROCM_USE_AITER=1 \
vllm/vllm-openai-rocm:nightly \
deepseek-ai/DeepSeek-R1-0528 \
--port 8200 \
--tensor-parallel-size 8 \
--gpu-memory-utilization 0.8 \
--trust-remote-code \
--enable-expert-parallel \
--kv-transfer-config '{
"kv_connector": "MoRIIOConnector",
"kv_role": "kv_consumer",
"kv_connector_extra_config": {
"proxy_ip": "'"${PREFILL_IP}"'",
"proxy_ping_port": "36367",
"http_port": "8200",
"handshake_port": "6301",
"notify_port": "61005"
}
}'
注意容器参数中 --device /dev/infiniband(暴露 RDMA 设备)、--ulimit memlock=-1(RDMA 注册内存需要无限制锁内存)与 --privileged 等选项是 RDMA 路径的必要前提;两侧的 handshake_port 与 notify_port 可以相同,因为它们绑定在不同的主机上。
六、故障排查
availDevices.size() > 0 断言失败
现象:vLLM 启动失败,日志中出现:
libibverbs: Warning: Driver bnxt_re does not support the kernel ABI of 6 (supports 1 to 1) for device /sys/class/infiniband/rdma4
...
ker: /app/mori/src/io/rdma/backend_impl.cpp: mori::io::RdmaManager::RdmaManager(const RdmaBackendConfig, application::RdmaContext *): Assertion `availDevices.size() > 0' failed.
原因与修复:说明环境中安装的 RDMA 用户态库与主机上已安装的内核模块/固件版本不匹配。必须安装与 RDMA 内核模块和固件版本对应的 NIC 用户态库,详见下文附录。
附录:安装 NIC 用户态库
要让 MoRI 跑在 RDMA 上,环境必须安装与内核模块和固件版本匹配的 RDMA 用户态库。官方镜像 vllm/vllm-openai-rocm:nightly 预装了以下网卡与内核模块版本的配套用户态库(详见 Dockerfile.rocm):
- AINIC(AMD Pensando Pollara):版本
1.117.3-hydra,与ioinic-dkms=25.11.1.001测试通过; - Thor2(Broadcom):版本
235.2.86.0,与bnxt-en-dkms=1.10.3.235.2.86.0、bnxt-re-dkms=235.2.86.0测试通过。
如果你的网卡、内核模块或固件不在上述组合之列,请遵循相应厂商的安装说明自行安装配套的用户态库。
小结
MoRIIOConnector 把 PD 分离场景下的 KV 缓存传输拆成了清晰的两个平面:数据面由 MoRI-IO 经 RDMA(跨节点)或 xGMI(同主机)搬运 KV 字节,控制面则用少量 TCP 端口(proxy_ping_port、handshake_port、notify_port)完成实例注册、引擎握手与块级同步。掌握 kv_connector_extra_config 中各键的语义——尤其是 notify_port 的"基地址端口 + rank 偏移"分配规则、WRITE/READ 两种模式在块分配与完成通知上的差异、以及 RDMA 三个调优参数——就能把它部署到从单机 8 卡到跨节点 1P1D 的实际 PD 分离环境中;再配合仓库内 moriio_connector.py 与单元测试 test_moriio_connector.py,可以进一步验证请求路由、TP rank 对齐与传输确认等行为是否符合预期。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00