Prhub

#47636 [KVOffload][P2P] Well-known default host/port env vars and per-DP-rank control port

原始 PR 作者 liranschour 合并时间 2026-07-15 20:22 文件变更 13 提交数 20 评论 20 代码增减 +413 / -61

执行摘要

为 P2P KV 卸载添加可配置监听地址与 DP 级端口偏移

P2P KV 卸载次级存储层此前使用硬编码的 0.0.0.0:7777 作为控制套接字地址,无法在部署时调整;且数据并行多副本运行时固定端口会冲突。PR 旨在提供与 NIXL 连接器相同水准的已知端口配置体验,同时通过 DP rank 偏移保证端口唯一性。PR body 明确说明 'Give the P2P KV-offload secondary tier the same well-known-port ergonomics the NIXL connector already has, plus per-DP-rank port binding so data-parallel replicas don\'t collide.'

该 PR 值得精读,尤其是环境变量驱动的配置回退模式、DP rank 偏移端口分配、以及 NIXL agent 名称与其身份的解耦设计。Review 中关于默认值安全性的 discussion 也体现了将安全 left-shift 到 API 设计的思路。对于部署大规模 PD 分离 + 数据并行的团队,此 PR 是必需的。

讨论亮点

Review 中核心讨论包括:

  • 安全风险depthfirst-app[bot]orozery 指出默认 0.0.0.0 存在安全风险,ZMQ ROUTER 套接字无认证,建议改为 localhost。作者将默认值改为 localhost,并说明跨主机 P2P 需显式设置。
  • 参数命名与数据结构orozery 建议将 poll()owner 参数改为 peer_id,与已有方法保持一致;同时建议用 NamedTuple 存储 inflight 记录。作者采纳并修改。
  • 通配符处理orozery 建议移除对 0.0.0.0/:: 的特殊处理,因为默认已改为 localhost。作者同意并删除相关代码。
    • 此外,Issue 评论中 nilig 报告了非路由 host 导致 NIXL 连接失败、以及 per-process block hashes 不一致导致 KV 无法传输的问题,这些虽超出本 PR 范围,但促使了默认 host 改为 localhost 和 agent 名称解耦。

实现拆解

  1. 注册环境变量:在 vllm/envs.py 中添加 VLLM_P2P_SIDE_CHANNEL_HOST(默认 localhost)和 VLLM_P2P_SIDE_CHANNEL_PORT(默认 5710),镜像 NIXL 侧通道变量。
  2. 重构 Manager 初始化:在 vllm/v1/kv_offload/tiering/p2p/manager.py 中将 P2PSecondaryTierManager.__init__()host/port 参数默认值改为 None,当未显式配置时从环境变量读取;绑定端口时加上 dp_index = offloading_spec.vllm_config.parallel_config.data_parallel_index 偏移。
  3. 解耦 NIXL agent 名称:将 NIXL agent 名称从复用 host:port 改为基于 uuid4 生成,避免因 host 非路由或唯一性不足导致的 NIXL 连接拒绝。
  4. 限定 poll 作用域:在 vllm/v1/kv_offload/tiering/p2p/data/base.pyDataTransport 抽象类中将 poll() 签名增加 peer_id 可选参数;在 nixl.pyNixlTransport 中实现按 peer 过滤的完成事件查询,防止共享传输层中不同 session 的事件互相消耗。
  5. 测试与文档:新增 TestBindHostPortDefaults 测试类覆盖环境变量默认值、显式覆盖和 DP 偏移;更新测试代理脚本 p2p_connector_proxy.py 支持 DP 轮询和从环境变量读取默认端口;更新文档 docs/features/kv_offloading_usage.md
文件 模块 状态 重要度
vllm/v1/kv_offload/tiering/p2p/manager.py P2P 管理 modified 6.86
vllm/v1/kv_offload/tiering/p2p/data/nixl.py 数据传输 modified 7.76
vllm/v1/kv_offload/tiering/p2p/data/base.py 传输接口 modified 6.22
vllm/envs.py 环境配置 modified 5.8
tests/v1/kv_offload/tiering/p2p/test_manager.py 测试管理 modified 7.27
tests/v1/kv_offload/tiering/p2p/test_data_transport.py 测试传输 modified 5.41
tests/v1/kv_offload/tiering/p2p/p2p_connector_proxy.py 测试代理 modified 6.73

关键符号

P2PSecondaryTierManager.__init__ DataTransport.poll NixlTransport.poll

关键源码片段

vllm/v1/kv_offload/tiering/p2p/manager.py dependency-wiring

核心入口,实现环境变量回退和 DP 端口偏移逻辑。

# vllm/v1/kv_offload/tiering/p2p/manager.py ( 关键变更 )import uuid
import vllm.envs as envsclass P2PSecondaryTierManager(SecondaryTierManager):
​
    def __init__(
        self,
        offloading_spec: OffloadingSpec,
        primary_kv_view: memoryview,
        tier_type: str = "p2p",
        host: str | None = None, # 默认 None,从环境变量获取
        port: int | None = None, # 默认 None,从环境变量获取
        backends: list[str] | None = None,
        num_threads: int = 4,
        **kwargs: Any,
    ) -> None:
        super().__init__(offloading_spec, primary_kv_view, tier_type)
​
        # 如果未显式传入 host / port,则从环境变量读取
        if host is None:
            host = envs.VLLM_P2P_SIDE_CHANNEL_HOST # 默认 "localhost"
        if port is None:
            port = envs.VLLM_P2P_SIDE_CHANNEL_PORT # 默认 5710
​
        # 为每个数据并行副本偏移端口,避免同主机端口冲突
        dp_index = offloading_spec.vllm_config.parallel_config.data_parallel_index
        port = int(port) + dp_index # base_port + rank
​
        # 用于 ZMQ 控制的本地标识,peer 通过此地址回连
        self._local_id = f"{host}:{port}"
​
        # NIXL agent 名称使用 uuid,与 host:port 解耦,确保全局唯一
        self._nixl_agent_name = str(uuid.uuid4())
vllm/v1/kv_offload/tiering/p2p/data/nixl.py core-logic

NIXL 数据传输层实现;增加 _Inflight NamedTuple 和按 peer 过滤的 poll()。

# vllm/v1/kv_offload/tiering/p2p/data/nixl.py ( 关键变更 )from typing import NamedTupleclass _Inflight(NamedTuple):
    """记录已提交但尚未完成的传输及其所属 peer ID。
    ``peer_id`` 使得共享传输层中的 poll() 可以按 session 范围查询,
    避免不同 session 的事件互相消费。
    """
    peer_id: str # 调用 write_blocks 时传入的 peer 身份
    handle: object # NIXL 传输句柄class NixlTransport(DataTransport):
​
    def __init__(self, agent_name: str, view: memoryview, ...):
        self._agent_name = agent_name
        # 将 inflight 字典的值从原始句柄改为 _Inflight 元组
        self._inflight: dict[int, _Inflight] = {}
​
    def poll(self, peer_id: str | None = None) -> PollResult:
        """轮询并清理完成的传输。        Args:
            peer_id: 如果提供,仅返回该 peer 的已完成传输 ID;
                     None 表示轮询所有 peer(用于关闭时 drain)。        Returns:
            PollResult 包含已完成和失败的传输 ID 列表。
        """
        # 内部遍历 _inflight,根据 peer_id 过滤,
        # 并调用 NIXL API 检查传输状态。
        # 实现与具体 NIXL 库交互,此处省略。
        # 关键是只移除与 peer_id 匹配的项,其他保留。
        return PollResult(done=list(done_ids), failed=list(failed_ids))

评论区精华

默认 host 安全性 安全

depthfirst-app[bot] 和 orozery 指出默认 0.0.0.0 存在安全风险,ZMQ ROUTER 无认证,建议改为 localhost

结论:作者将默认值改为 localhost · 已解决

poll 参数命名与数据结构 设计

orozery 建议将 poll() 的 owner 参数改为 peer_id,使用 NamedTuple

结论:作者接受并修改 · 已解决

通配符 host 处理 设计

orozery 建议移除对 0.0.0.0/:: 的特殊处理代码,因默认已改为 localhost

结论:作者同意并删除相关代码 · 已解决

风险与影响

  • 默认 host 从 0.0.0.0 改为 localhost:跨主机 P2P 要求用户显式设置 VLLM_P2P_SIDE_CHANNEL_HOST,否则只能单机通信,可能对隐式依赖 0.0.0.0 的部署造成 break。
  • 端口偏移dp_index 叠加可能导致端口号超出可用范围,但当前偏移值较小(通常 DP < 16),风险可控。
  • NIXL agent 名称改为 uuid:依赖 agent 名称做寻址或过滤的逻辑需确保使用新 id,现有代码已适配。
  • poll(peer_id) 语义变更:共享传输层中调用者必须使用正确的 peer_id,否则可能收到错误的事件集,需确保所有 session 调用处已更新。
  • 用户影响:为 P2P KV offload 引入标准环境变量配置方式,部署更灵活;多 DP 副本不再端口冲突;DP=1 场景行为基本不变(默认 localhost:5710)。
  • 系统影响:无性能影响;NIXL agent 名称改为 uuid 增加了唯一性保证;poll 范围限定提升了传输层的正确性。
  • 团队影响:统一了 P2P 与 NIXL 连接器之间的环境变量命名风格,降低了运维复杂度。
默认值变更可能 break 跨主机通信 端口偏移可能超出范围 poll 语义要求调用方更新

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论