首页
/ vLLM MoRIIOConnector 实战指南:基于 MoRI-IO 的高性能 PD 分离 KV Cache 传输

vLLM MoRIIOConnector 实战指南:基于 MoRI-IO 的高性能 PD 分离 KV Cache 传输

2026-09-04 14:55:27作者:咎竹峻Karen

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 模块(IOEngineIOEngineConfigBackendType 等);若环境未安装 MoRI,导入阶段仅会记录错误日志并置 MoRIIO_enabled = False,因此该连接器只在安装了 MoRI 的 ROCm 环境中可用。配套的单元测试位于 tests/v1/kv_connector/unit/,覆盖路由公平性、TP ACK、unmap 等场景,可作为行为参照。

二、前置条件与安装

MoRI 有两种安装方式:

  1. Docker(推荐):MoRI 随官方 ROCm vLLM 镜像 vllm/vllm-openai-rocm:nightly 一起提供,开箱即用;
  2. 手动安装
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_addresszmq_address(格式为 host:IP,handshake:PORT,notify:PORT)、dp_sizetp_sizetransfer_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_offsetoffset = 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_MODEVLLM_MORIIO_QP_PER_TRANSFERVLLM_MORIIO_POST_BATCH_SIZEVLLM_MORIIO_NUM_WORKERS 等环境变量传递这些参数,现已弃用并会被忽略,统一改由 kv_connector_extra_config 传入。

4.2 传输级配置(Transport)

MoRI 有两个传输后端:RDMAxGMI。通过 --kv-transfer-config.kv_connector_extra_config.backend $BACKEND 选择,$BACKENDrdmaxgmi。源码校验逻辑(moriio_common.py)对后端名做小写化并严格白名单校验,取值非法会直接抛 ValueErrorRDMA 是默认后端,多节点部署应使用 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_WRMORI_IO_QP_MAX_CQE 等环境变量直接配置 MoRI 库本身。这些是 MoRI 库变量,与 vLLM 侧的配置项相互独立,细节见 MoRI 官方仓库。

xGMI 后端

当 prefiller 与 decoder 位于同一物理主机时,可改用 xGMI 后端:传输走 AMD GPU 之间的 xGMI fabric,完全绕开网卡。该后端目前只能通过 MoRI 专属的环境变量配置,参见 MoRI 官方仓库。注意上文的 RDMA 调优参数(qp_per_transferpost_batch_sizenum_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_portnotify_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.0bnxt-re-dkms=235.2.86.0 测试通过。

如果你的网卡、内核模块或固件不在上述组合之列,请遵循相应厂商的安装说明自行安装配套的用户态库。

小结

MoRIIOConnector 把 PD 分离场景下的 KV 缓存传输拆成了清晰的两个平面:数据面由 MoRI-IO 经 RDMA(跨节点)或 xGMI(同主机)搬运 KV 字节,控制面则用少量 TCP 端口(proxy_ping_porthandshake_portnotify_port)完成实例注册、引擎握手与块级同步。掌握 kv_connector_extra_config 中各键的语义——尤其是 notify_port 的"基地址端口 + rank 偏移"分配规则、WRITE/READ 两种模式在块分配与完成通知上的差异、以及 RDMA 三个调优参数——就能把它部署到从单机 8 卡到跨节点 1P1D 的实际 PD 分离环境中;再配合仓库内 moriio_connector.py 与单元测试 test_moriio_connector.py,可以进一步验证请求路由、TP rank 对齐与传输确认等行为是否符合预期。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384