vLLM KV Cache 池化:Mooncake + MultiConnector 生产级实践

Mooncake Master + MultiConnector(MooncakeConnectorV1 + AscendStoreConnector)双层 KV Cache 池化架构的完整实践指南:从原理到 GLM-5.2 生产配置,涵盖前缀缓存、SSD 卸载、Decode KV 回写和排障。

一、什么是 KV Cache 池化

1.1 无池化的 PD 分离

请求 → Proxy → Prefill (max_tokens=1) → KV Cache 直传 → Decode (流式)
                     ↓                                    ↓
              计算完就丢弃                           用完就丢弃

每次请求,Prefill 生成的 KV Cache 用一次就扔。下一个相同 System Prompt 的请求,Prefill 要重新计算。

1.2 有池化的 PD 分离

请求 → Proxy → Prefill → KV Cache 写入 Pool → Decode 从 Pool 读取
                     ↓              ↕                ↓
              Mooncake Master (KV Pool)         命中则跳过 Prefill

              AscendStoreConnector (前缀缓存池 + SSD 卸载)

核心变化:KV Cache 不再是一次性的,而是持久化在共享 Pool 中。后续请求如果命中前缀缓存,Prefill 直接跳过。

1.3 池化的三种层次

层次组件功能收益
L1: KV 传输MooncakeConnectorV1P→D 间传输 KV CachePD 分离的基础
L2: 前缀缓存AscendStoreConnector相同前缀只算一次Prefill 延迟降低 30-90%
L3: 全池化+ SSD 卸载 + Decode 回写KV 持久化 + 跨请求复用Prefill 接近 0(命中时)

二、架构全景

2.1 组件关系

┌─────────────────────────────────────────────────────────┐
│                    Mooncake Master                       │
│  - KV block 索引                                        │
│  - 租约管理 (default_kv_lease_ttl)                       │
│  - 驱逐策略 (eviction_high_watermark_ratio)               │
│  - SSD 卸载调度 (enable_offload)                         │
└──────┬──────────────────────────────────┬───────────────┘
       │ 注册 + 心跳                       │ 注册 + 心跳
┌──────▼──────────┐                ┌──────▼──────────┐
│   Prefill 节点   │   RDMA 直传    │   Decode 节点    │
│                  │◄──────────────►│                  │
│ MultiConnector   │   KV blocks   │ MultiConnector   │
│ ├─ Mooncake V1   │               │ ├─ Mooncake V1   │
│ └─ AscendStore   │               │ └─ AscendStore   │
│    (kv_producer) │               │    (kv_consumer) │
└──────────────────┘               └──────────────────┘

2.2 MultiConnector 双层设计

MultiConnector
  ├── MooncakeConnectorV1    ← L1: 跨节点 KV 传输(RDMA)
  │    职责:P→D 发送 KV blocks
  │    端口:kv_port (30000/30100)

  └── AscendStoreConnector   ← L2: 持久化前缀缓存
       职责:前缀缓存管理 / SSD 卸载 / Decode 回写
       后端:mooncake (复用 Master)
       参数:lookup_rpc_port / load_async / consumer_is_to_put
Connectorkv_role做什么
MooncakeConnectorV1kv_producerPrefill 节点:将 KV blocks 编码后通过 RDMA 发给 Decode
MooncakeConnectorV1kv_consumerDecode 节点:接收 KV blocks 并加载到显存
AscendStoreConnectorkv_producerPrefill 节点:将前缀缓存写入 Pool
AscendStoreConnectorkv_consumerDecode 节点:从 Pool 读取前缀缓存(load_async=true 异步)
AscendStoreConnectorkv_both混合模式:同时读和写(PD 共置场景)

为什么需要两个 Connector? MooncakeConnectorV1 只管传输(快,无状态),AscendStoreConnector 管存储(持久化,有索引)。分开设计让传输路径不阻塞、存储路径可独立扩展(加 SSD)。


三、Mooncake Master

3.1 部署

Mooncake Master 是 KV Pool 的中心调度器,只需要在 Prefill 的 p0 节点上启动一个实例:

mooncake_master \
  --port 50088 \
  --eviction_high_watermark_ratio 0.9 \
  --eviction_ratio 0.1 \
  --default_kv_lease_ttl 11000
参数含义建议值
--portMaster RPC 端口50088
--eviction_high_watermark_ratio触发驱逐的水位(显存占比)0.9(90% 显存开始驱逐)
--eviction_ratio每次驱逐的比例0.1(每次驱逐 10% 的 KV blocks)
--default_kv_lease_ttlKV block 租约时间(ms)11000(11 秒,覆盖最长请求)

3.2 Docker Compose 集成(GLM-5.2 真实配置)

# 仅在 p0 节点启用(profiles: mooncake)
mooncake:
  image: quay.io/ascend/vllm-ascend:v0.23.0rc1
  container_name: mooncake-master
  restart: unless-stopped
  network_mode: host
  profiles:
    - mooncake
  volumes:
    - /data/models/GLM-5.2/config:/workspace/config:ro
  command:
    - mooncake_master
    - --port
    - "50088"
    - --eviction_high_watermark_ratio
    - "0.9"
    - --eviction_ratio
    - "0.1"
    - --default_kv_lease_ttl
    - "11000"
# 启动(只启动 p0 的 mooncake)
COMPOSE_PROFILES=mooncake docker compose up -d mooncake

3.3 SSD 卸载配置

如果节点有 NVMe SSD(如 /mnt/nvme),可以将溢出的 KV blocks 卸载到 SSD:

mooncake_master \
  --port 50088 \
  --eviction_high_watermark_ratio 0.9 \
  --eviction_ratio 0.1 \
  --default_kv_lease_ttl 11000 \
  --enable_offload=true \
  --client_ttl=120

同时需要在 mooncake.json 中声明 SSD 路径:

{
  "local_hostname": "<NODE_IP>",
  "metadata_server": "<MASTER_IP>:50088",
  "protocol": "rdma",
  "device_name": "ibp8s0f0",
  "enable_ssd_offload": true,
  "ssd_offload_path": "/mnt/nvme/mooncake_ssd"
}

注意MOONCAKE_OFFLOAD_TOTAL_SIZE_LIMIT_BYTES 默认 2TB。如果 NVMe 只有 1TB,需要手动调小,否则会超出磁盘容量。


四、mooncake.json 模板

所有 Prefill 和 Decode 节点都需要这个文件,指向同一个 Master:

{
  "local_hostname": "<本节点 IP>",
  "metadata_server": "<p0_IP>:50088",
  "protocol": "rdma",
  "device_name": "ibp8s0f0"
}

Ansible 生成模板

由于每个节点 IP 不同,用 Ansible 模板生成:

# Jinja2 模板
cat > config/mooncake.json.j2 << 'EOF'
{
  "local_hostname": "{{ ansible_host }}",
  "metadata_server": "{{ hostvars[groups['prefills'][0]]['ansible_host'] }}:50088",
  "protocol": "rdma",
  "device_name": "ibp8s0f0"
}
EOF

# Ansible task
- name: 生成 mooncake.json
  template:
    src: config/mooncake.json.j2
    dest: "/data/models/{{ model_name }}/config/mooncake.json"

五、MultiConnector 完整配置

5.1 全局环境变量

所有节点必须设置:

export MOONCAKE_CONFIG_PATH="/workspace/config/mooncake.json"  # 指向 mooncake.json
export PYTHONHASHSEED=0           # MultiConnector 要求统一哈希种子
export HCCL_RDMA_TIMEOUT=17       # RDMA 超时
export ASCEND_CONNECT_TIMEOUT=10000   # 连接超时 (ms)
export ASCEND_TRANSFER_TIMEOUT=10000  # 传输超时 (ms)
变量说明
PYTHONHASHSEED必须 0所有节点一致,否则 KV 哈希查找失败
HCCL_RDMA_TIMEOUT174.096μs × 2^17 ≈ 537ms
ASCEND_CONNECT_TIMEOUT10000≥ 500ms × Decode 卡数
ASCEND_TRANSFER_TIMEOUT10000单向传输,需 > RDMA_TIMEOUT × 7

5.2 Prefill 节点 — kv_producer

vllm serve /workspace/GLM-5.2-w8a8 \
  --kv-transfer-config '{
    "kv_connector": "MultiConnector",
    "kv_role": "kv_producer",
    "kv_load_failure_policy": "recompute",
    "kv_connector_extra_config": {
      "connectors": [
        {
          "kv_connector": "MooncakeConnectorV1",
          "kv_role": "kv_producer",
          "kv_port": "30000",
          "kv_connector_extra_config": {
            "prefill": {"dp_size": 4, "tp_size": 8},
            "decode":  {"dp_size": 8, "tp_size": 4}
          }
        },
        {
          "kv_connector": "AscendStoreConnector",
          "kv_role": "kv_producer",
          "kv_connector_extra_config": {
            "lookup_rpc_port": "0",
            "backend": "mooncake"
          }
        }
      ]
    }
  }'

5.3 Decode 节点 — kv_consumer

vllm serve /workspace/GLM-5.2-w8a8 \
  --kv-transfer-config '{
    "kv_connector": "MultiConnector",
    "kv_role": "kv_consumer",
    "kv_load_failure_policy": "recompute",
    "kv_connector_extra_config": {
      "connectors": [
        {
          "kv_connector": "MooncakeConnectorV1",
          "kv_role": "kv_consumer",
          "kv_port": "30100",
          "kv_connector_extra_config": {
            "prefill": {"dp_size": 4, "tp_size": 8},
            "decode":  {"dp_size": 8, "tp_size": 4}
          }
        },
        {
          "kv_connector": "AscendStoreConnector",
          "kv_role": "kv_consumer",
          "kv_connector_extra_config": {
            "lookup_rpc_port": "0",
            "load_async": true,
            "backend": "mooncake"
          }
        }
      ]
    }
  }'

5.4 Prefill vs Decode 的关键差异

参数PrefillDecode原因
kv_rolekv_producerkv_consumerP 生产 KV,D 消费 KV
kv_port3000030100不同端口避免冲突(同组 P/D 共享同一 engine_id 但不同端口)
engine_id同 group 内相同同 group 内相同配对的 P 和 D 用同一 ID 通信
load_async不适用trueD 端异步加载 KV 不阻塞推理
consumer_is_to_put不适用true(可选)D 端也回写 KV(MLA 模型)
dp_size/tp_size自己的拓扑对端的拓扑MooncakeConnectorV1 需要知道对端的 DP/TP 布局来做数据路由

dp_size/tp_size 的坑:Prefill 节点的 MooncakeConnectorV1 中 "decode": {"dp_size": 8, "tp_size": 4} 描述的是 Decode 侧的拓扑,而不是 Prefill 自己。Decode 节点同理,"prefill": {"dp_size": 4, "tp_size": 8} 描述的是 Prefill 侧的拓扑。


六、AscendStoreConnector 进阶

6.1 前缀缓存原理

请求1: "你是一个运维专家,精通 K8s 和 Docker..."  → Prefill → KV 写入 Pool
请求2: "你是一个运维专家,精通 K8s 和 Docker..."  → 命中 Pool → 跳过 Prefill

                                    AscendStoreConnector 返回缓存 KV

AscendStoreConnector 通过哈希匹配前缀,直接返回缓存的 KV blocks。命中时 TTFT 降低 50-90%。

6.2 Decode KV 回写(consumer_is_to_put)

{
  "kv_connector": "AscendStoreConnector",
  "kv_role": "kv_consumer",
  "kv_connector_extra_config": {
    "lookup_rpc_port": "0",
    "backend": "mooncake",
    "consumer_is_to_put": true
  }
}

Decode 节点在生成过程中产生的 KV blocks 也可以回写到 Pool,供后续 Prefill 复用。

适用条件:仅 MLA(Multi-head Latent Attention)模型支持。GLM-5.2 使用 MLA,可以使用。

6.3 PD 混合模式(kv_both)

单节点同时跑 Prefill + Decode(无 PD 分离)时:

vllm serve /model \
  --kv-transfer-config '{
    "kv_connector": "AscendStoreConnector",
    "kv_role": "kv_both",
    "kv_load_failure_policy": "recompute",
    "kv_connector_extra_config": {
      "lookup_rpc_port": "1",
      "backend": "mooncake"
    }
  }'

不需要 MooncakeConnectorV1(因为没有跨节点传输),只需要 AscendStoreConnector 做前缀缓存。


七、关键参数速查

参数位置含义默认建议
kv_load_failure_policyMultiConnector 顶级KV 加载失败策略failrecompute(自动重算)
kv_portMooncakeConnectorV1KV 传输端口P:30000, D:30100
lookup_rpc_portAscendStoreConnector池化查找端口0(自动分配)
load_asyncAscendStoreConnector异步加载falseD 端设 true
consumer_is_to_putAscendStoreConnectorD 端 KV 回写falseMLA 模型设 true
backendAscendStoreConnector存储后端mooncake保持默认
engine_idPD 分离对配对标识同组 P/D 相同

八、监控与排障

8.1 Mooncake Master 状态

# 检查 Master 是否正常
curl -s http://<MASTER_IP>:50088/metrics | grep -E "kv_cache|connected"

# 预期输出:
# mooncake_kv_cache_blocks_total 12345
# mooncake_connected_clients 8

8.2 vLLM 日志验证

# 确认 MultiConnector 初始化成功
grep "connector" /workspace/logs/prefill_rank0.log | head -5

# 预期看到:
# [MultiConnector] Initialized with 2 connectors
# [MooncakeConnectorV1] role=kv_producer, port=30000
# [AscendStoreConnector] role=kv_producer, backend=mooncake

# 检查前缀缓存命中率
grep "hit_rate\|prefix_cache" /workspace/logs/prefill_rank0.log

8.3 常见问题

现象根因排查
Decode 连不上 Prefill网络不通或端口未开放nc -zv $P0_IP 30000
KV 加载失败,日志全是 recomputekv_load_failure_policy 配到了子 connector必须配在 MultiConnector 顶级
节点间 KV 哈希不匹配PYTHONHASHSEED 不一致所有节点 echo $PYTHONHASHSEED 确认都是 0
AscendStore 连不上 MasterMOONCAKE_CONFIG_PATH 未设置或路径错误cat $MOONCAKE_CONFIG_PATH 检查内容
前缀缓存命中率始终为 0前缀不够相似或 Pool 太满检查 eviction_high_watermark_ratio,必要时降低
同节点多 rank 端口冲突lookup_rpc_port 相同每个 rank 用不同端口或设为 0 自动分配
SSD 卸载报空间不足默认 2TB 配额超磁盘实际容量export MOONCAKE_OFFLOAD_TOTAL_SIZE_LIMIT_BYTES=1073741824000 (~1TB)

九、部署检查清单

□ Mooncake Master 在 p0 节点启动(端口 50088)
□ mooncake.json 生成并分发到所有 P + D 节点
□ 所有节点 PYTHONHASHSEED=0
□ MultiConnector 配置在 Prefill 使用 kv_producer
□ MultiConnector 配置在 Decode 使用 kv_consumer
□ kv_port 在 P 和 D 上不同(30000 vs 30100)
□ dp_size/tp_size 填写的是对端拓扑(不是自己)
□ kv_load_failure_policy 配在 MultiConnector 顶级(不是子 connector)
□ SSD 卸载只在有 NVMe 的节点上启用
□ consumer_is_to_put 仅 MLA 模型使用