- experiments README 新增「运维一键部署」章节:6 步完整流程 (免密/文件分发/仓库 clone/拉镜像/启动/验证)+ 停止重启 + 文件来源 - docs/KIMI_K3_DEPLOY.md 附录 B 新增 B.2 运维准备,编号顺延 B.3-B.6 - 修复 scp 分发命令(先建父目录)
Kimi-K3 PD 分离部署(MoonCake RDMA)实验
RTX 6000D(sm_120,8 节点 64 卡)上 Kimi-K3 的 Prefill/Decode 分离部署,KV 传输使用 MoonCake RDMA(计算网 mlx5_0~3,4 链路 RoCE)。
背景与动机
PD 分离部署(PD Disaggregation)将 prefill(预填充,计算密集型)与 decode(逐 token 生成,访存密集型)拆分到不同节点组,是长上下文下提升吞吐/利用率的行业标准做法。本实验聚焦 KV 传输后端的选型验证与稳定部署。
为什么不用 NIXL
实测 NIXL(UCX 后端)在 RTX 6000D(无 GDR/GDAKI)上 VRAM 单次传输上限约 20MB(24/32/64MB 均报 Input/output error → NIXL_ERR_REMOTE_DISCONNECT),且 sglang NIXL 后端不做按字节分块(一次传输整个请求全部 KV),连续负载下必断。根因:UCX 依赖 GDAKI(GPU Direct Async)接口,当前未启用(缺 NVreg_RegistryDwords="PeerMappingOverride=1;")。详见飞书 wiki「Kimi-K3 部署手册」PD 章节。
为什么 MoonCake RDMA 可行
- MoonCake 走 libibverbs 直连(传统 GDR + dmabuf 注册),不依赖 UCX/GDAKI
- 实测跨节点 GPU 显存传输 1GB 无压力,连续 10×64MB 全部成功
- 必须用 0.3.12.post1(含 dmabuf 注册修复 #2035;镜像自带的 0.3.11.post1 无修复)
关键坑(务必遵守)
- 不能设
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:Trueexpandable_segments 分配的 GPU 段,mooncake RDMA 注册报Bad address [14](GitHub kvcache-ai/Mooncake #2511)。移除后 KV 注册 2376 条失败 → 0 失败。 - P 组先启动,D 组后启动(prefill 需先注册 bootstrap)。
- 容器内必须升级 mooncake 到 0.3.12.post1(wheel 在
/data/flashkda_deploy/wheels/,profile 的 BOOTSTRAP 里已含 pip install)。 - NCCL/GLOO 走计算网(
bond1+NCCL_IB_HCA=mlx5_0..3),不要走管理网 bond0。
架构
┌───────────────────┐
client ──31000──▶ │ router (MiniLB) │ 174.1.60.5
└───────┬───────────┘
┌───────▼───────────┐
P 组 (prefill) │ 174.1.60.1~4 │ TP32×EP32,4 节点 32 卡
└── prefill 计算 │ 计算完产出 KV │
└───────┬───────────┘
┌───────▼───────────┐
MoonCake RDMA│ mlx5_0~3 (RoCE) │ 4 链路并行
└───────┬───────────┘
┌───────▼───────────┐
D 组 (decode) │ 174.1.60.5~8 │ TP32×EP32,4 节点 32 卡
└── decode 生成 │ 接收 KV 逐 token │
└───────────────────┘
mc-master (174.1.60.1:50051) MoonCake 元数据服务
运维一键部署(从干净环境到跑通)
以下是运维同学按顺序执行即可完成整套 PD 部署的完整步骤。前 4 步是"一次性准备"(新机器/新环境才需要;已部署过的集群可跳过,直接看第 5 步)。
步骤 1:准备 8 节点环境(一次性)
前置:8 台节点(174.1.60.1~8)已安装 NVIDIA 驱动 + CUDA + Docker,GPU 空闲(nvidia-smi 全 0 MiB)。
# 在任意一台(建议 .5)确认能免密 ssh 到全部 8 节点
for i in 1 2 3 4 5 6 7 8; do ssh 174.1.60.$i hostname; done
# 若某台需密码,先配免密(把公钥分发过去)
ssh-copy-id root@174.1.60.$i # 对每台执行
步骤 2:分发部署文件到 8 节点(一次性)
每台节点需要 3 个文件(patch 脚本 + flashkda wheel + mooncake wheel),放到指定路径:
# 在 .5 上执行(假设文件已存在于 .5 的对应位置;若没有,先从模型团队获取)
FILES="/tmp/patch_k3_sm120.py
/tmp/flash_kda-0.0.1-cp312-cp312-linux_x86_64.whl
/data/flashkda_deploy/wheels/mooncake_transfer_engine_cuda13-0.3.12.post1-cp312-cp312-manylinux_2_28_x86_64.whl"
for h in 1 2 3 4 5 6 7 8; do
ssh 174.1.60.$h "mkdir -p /tmp /data/flashkda_deploy/wheels" # 确保父目录存在
for f in $FILES; do
scp -q $f 174.1.60.$h:/$f
done
echo " .$h 文件就位"
done
# 验证:每台应显示 3/3
for h in 1 2 3 4 5 6 7 8; do
echo -n " .$h: "
ssh 174.1.60.$h "ls /tmp/patch_k3_sm120.py /tmp/flash_kda-0.0.1-cp312-cp312-linux_x86_64.whl /data/flashkda_deploy/wheels/*.whl 2>/dev/null | wc -l"
done
步骤 3:部署 sskj 仓库到 .5 和 .1(一次性)
deploy_pd.sh 依赖 sskj 仓库:.5 上用于编排,.1 上用于执行 P 组 deploy 层命令。
# .5 上已有仓库则跳过;没有则 clone(在 .5 上执行)
git clone https://git.meta-stone.net/qqtang/sskj.git /data/yy/sskj
# .1 上也必须 clone(deploy_pd.sh 会在 .1 执行 python -m sskj.deploy)
ssh 174.1.60.1 "mkdir -p /data/yy && git clone https://git.meta-stone.net/qqtang/sskj.git /data/yy/sskj"
# 保持两边同步最新
cd /data/yy/sskj && git pull origin main
ssh 174.1.60.1 "cd /data/yy/sskj && git pull origin main"
步骤 4:拉取镜像(一次性)
for h in 1 2 3 4 5 6 7 8; do
ssh 174.1.60.$h "docker pull lmsysorg/sglang:kimi-k3" &
done; wait
步骤 5:一键启动(每次部署执行)
cd /data/yy/sskj/experiments/pro6000/kimi3_pro6000_pd_rdma
bash deploy_pd.sh start
# 预计 12~18 分钟(模型加载 + CUDA graph + PD warmup)
步骤 6:验证
# 看状态
bash deploy_pd.sh status
# 端到端推理(应有正常文本返回)
curl -s http://174.1.60.5:31000/generate -H "Content-Type: application/json" -d '{"text":"Hello","sampling_params":{"max_new_tokens":16}}'
停止 / 重启
bash deploy_pd.sh stop # router → D 组 → P 组 → mc-master
bash deploy_pd.sh restart
部署文件来源
patch_k3_sm120.py、flash_kda-0.0.1-*.whl:模型团队产物(见docs/KIMI_K3_DEPLOY.md附录 A)mooncake_transfer_engine_cuda13-0.3.12.post1-*.whl:/data/flashkda_deploy/wheels/- 若文件缺失,找模型团队提供或重新构建/下载
使用
# 一键启动(mc-master → P 组 → D 组 → router)
bash deploy_pd.sh start
# 查看状态
bash deploy_pd.sh status
# 停止 / 重启
bash deploy_pd.sh stop
bash deploy_pd.sh restart
# 端到端验证(router 入口,自动双发 P/D)
curl -s http://174.1.60.5:31000/generate -H "Content-Type: application/json" \
-d '{"text":"Hello","sampling_params":{"max_new_tokens":16}}'
实测验证(2026-08-11)
| 测试 | 输入 tokens | 输出 tokens | 结果 |
|---|---|---|---|
| warmup | 6 | 16 | ✅ 200 OK |
| 中等 | 1680 | 512 | ✅ e2e 31.4s |
| 长输入 | 5280 | 256 | ✅ e2e 13.0s |
- P 组 prefill throughput ~112 tok/s(5280 token 输入),D 组 decode ~26 tok/s
- 两端 GPU 同步工作,PD 分离链路完整跑通
- 对比 NIXL:MoonCake RDMA 处理长 KV 传输稳定,无 20MB 上限问题
文件
| 文件 | 说明 |
|---|---|
deploy_pd.sh |
PD 编排脚本(mc-master + P + D + router 全生命周期) |
config.env |
实验配置 |
deploy/profiles/pro6000/kimi3_pro6000_pd_prefill.env |
P 组(prefill)部署 profile |
deploy/profiles/pro6000/kimi3_pro6000_pd_decode.env |
D 组(decode)部署 profile |
参考
- 飞书 wiki「Kimi-K3 部署手册」PD 分离章节(七~十一章)
- GitHub kvcache-ai/Mooncake: #2511(expandable_segments 注册失败)、#2035(dmabuf 修复)、#351(Bad address)