【无标题】双 DGX Spark 部署 DeepSeek V4 Flash 实战
@[TOC]双 DGX Spark 部署 DeepSeek V4 Flash 实战
两台 NVIDIA DGX Spark (GB10),200G RoCE 直连,Docker 部署 DeepSeek V4 Flash 200B MoE 大模型,推理速度 38 tok/s,支持 200K 上下文。
1. 硬件环境
| 项目 | DGX-1 (Head) | DGX-2 (Worker) |
|---|---|---|
| 型号 | DGX Spark | DGX Spark |
| CPU | Grace ARM 20核 | Grace ARM 20核 |
| GPU | GB10 (Blackwell) | GB10 (Blackwell) |
| 统一内存 | 128GB LPDDR5X | 128GB LPDDR5X |
| 存储 | 1TB NVMe | 1TB NVMe |
| 互联 | ConnectX-7 200G RoCE | ConnectX-7 200G RoCE |
| 管理 IP | 192.168.0.21 | 192.168.0.22 |
| 高速 IP | 192.168.10.58 | 192.168.10.59 |
2. 软件栈
| 组件 | 版本/说明 |
|---|---|
| 操作系统 | Ubuntu 24.04 LTS (aarch64) |
| Docker | 原生安装 + nvidia-container-toolkit |
| vLLM | v0.21.1rc1 + SM12x 补丁 (jasl 分支) |
| 自定义镜像 | vllm-node-dsv4:latest (22.7GB, 自构建) |
| CUDA | 12.x (Blackwell SM12.1a) |
| NCCL | IB/RoCE 传输层 |
| Swap | 64GB (防止 OOM) |
为什么用补丁版 vLLM 而不是 pip 安装?
官方 vLLM 0.22.0 有两个致命问题:① mp backend 不支持跨节点 TP(报 local_world_size > visible devices);② Gloo 进程组在 DGX SPARK 上崩溃(Gloo connectFullMesh failed)。打了 SM12x 补丁后,mp backend 真正支持跨节点张量并行。
3. 架构
┌─────────────────────────┐ ┌─────────────────────────┐
│ DGX-1 (Head, rank=0) │ │ DGX-2 (Worker, rank=1) │
│ │ │ │
│ ┌───────────────────┐ │ │ ┌───────────────────┐ │
│ │ vllm-ds4 容器 │ │ │ │ vllm-ds4 容器 │ │
│ │ TP=0, EP=0 │◄─┼──────┼─►│ TP=1, EP=1 │ │
│ │ HTTP API :8000 │ │ RoCE │ │ (headless) │ │
│ └───────────────────┘ │200G │ └───────────────────┘ │
│ │ │ │
│ GB10 128GB 统一内存 │ │ GB10 128GB 统一内存 │
└─────────────────────────┘ └─────────────────────────┘
▲
│ HTTP (OpenAI 兼容 API)
▼
┌─────────┐
│ 客户端 │
└─────────┘
关键设计决策:
- B12X Mxfp4 MoE 内核替代标准 expert-parallel:更快(~38 tok/s),但与 EP 互斥
- MTP 投机解码 x2:模型内置 Multi-Token Prediction 头,无损加速 50%+
- mp backend 替代 Ray:无需启动 Ray 集群,简化运维
- FP8 KV Cache:block-size 256,prefix caching 启用
4. 部署步骤
4.1 前置准备
# 两台节点都要做
sudo fallocate -l 64G /swapfile
sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# 配置 Docker 镜像加速(中国网络环境)
sudo tee /etc/docker/daemon.json << 'EOF'
{"registry-mirrors": ["https://docker.nju.edu.cn"]}
EOF
sudo systemctl restart docker
# 确认 IB 设备
rdma link show # 找到 ACTIVE 的 HCA,通常是 rocep1s0f1
4.2 启动 Worker(必须先启动)
# DGX-2 (192.168.0.22)
docker run -d \
--name vllm-ds4 --gpus all --net=host --ipc=host \
--shm-size=64g --ulimit memlock=-1 --ulimit stack=67108864 \
--device /dev/infiniband:/dev/infiniband \
-v /home/wangdefa/models:/models \
-e NODE_RANK=1 -e HEADLESS=1 -e MASTER_ADDR=192.168.10.58 \
-e NCCL_NET=IB -e NCCL_IB_DISABLE=0 -e NCCL_IB_HCA=rocep1s0f1 \
-e NCCL_SOCKET_IFNAME=enp1s0f1np1 -e NCCL_IB_GID_INDEX=0 \
-e NCCL_CROSS_NIC=0 -e NCCL_CUMEM_ENABLE=0 -e NCCL_IGNORE_CPU_AFFINITY=1 \
-e NCCL_DEBUG=WARN -e VLLM_ALLOW_LONG_MAX_MODEL_LEN=1 \
-e VLLM_SPARSE_INDEXER_MAX_LOGITS_MB=256 -e VLLM_USE_B12X_MOE=1 \
--entrypoint /usr/local/bin/dsv4-vllm-entrypoint \
vllm-node-dsv4:latest \
serve /models/DeepSeek-V4-Flash \
--distributed-executor-backend mp --nnodes 2 --node-rank 1 \
--master-addr 192.168.10.58 --master-port 25000 \
--tensor-parallel-size 2 \
--kv-cache-dtype fp8 --block-size 256 \
--max-model-len 200000 --gpu-memory-utilization 0.75 \
--max-num-seqs 6 --max-num-batched-tokens 4096 \
--enable-prefix-caching --enable-flashinfer-autotune \
--tokenizer-mode deepseek_v4 --reasoning-parser deepseek_v4 \
--reasoning-config '{"reasoning_parser":"deepseek_v4","reasoning_start_str":"","reasoning_end_str":""}' \
--speculative-config '{"method":"mtp","num_speculative_tokens":2}' \
--api-key sk-xxxx --headless --trust-remote-code
4.3 等 5 秒后启动 Head
# DGX-1 (192.168.0.21)
docker run -d \
--name vllm-ds4 --gpus all --net=host --ipc=host \
--shm-size=64g --ulimit memlock=-1 --ulimit stack=67108864 \
--device /dev/infiniband:/dev/infiniband \
-v /home/wangdefa/models:/models \
-e NODE_RANK=0 -e MASTER_ADDR=192.168.10.58 \
-e NCCL_NET=IB -e NCCL_IB_DISABLE=0 -e NCCL_IB_HCA=rocep1s0f1 \
-e NCCL_SOCKET_IFNAME=enp1s0f1np1 -e NCCL_IB_GID_INDEX=0 \
-e NCCL_CROSS_NIC=0 -e NCCL_CUMEM_ENABLE=0 -e NCCL_IGNORE_CPU_AFFINITY=1 \
-e NCCL_DEBUG=WARN -e VLLM_ALLOW_LONG_MAX_MODEL_LEN=1 \
-e VLLM_SPARSE_INDEXER_MAX_LOGITS_MB=256 -e VLLM_USE_B12X_MOE=1 \
--entrypoint /usr/local/bin/dsv4-vllm-entrypoint \
vllm-node-dsv4:latest \
serve /models/DeepSeek-V4-Flash \
--distributed-executor-backend mp --nnodes 2 --node-rank 0 \
--master-addr 192.168.10.58 --master-port 25000 \
--tensor-parallel-size 2 \
--kv-cache-dtype fp8 --block-size 256 \
--max-model-len 200000 --gpu-memory-utilization 0.75 \
--max-num-seqs 6 --max-num-batched-tokens 4096 \
--enable-prefix-caching --enable-flashinfer-autotune \
--tokenizer-mode deepseek_v4 --reasoning-parser deepseek_v4 \
--reasoning-config '{"reasoning_parser":"deepseek_v4","reasoning_start_str":"","reasoning_end_str":""}' \
--speculative-config '{"method":"mtp","num_speculative_tokens":2}' \
--api-key sk-xxxx --trust-remote-code
4.4 等待就绪
首次启动约 8 分钟,阶段分布:
| 时间 | 阶段 |
|---|---|
| 0-15s | NCCL 初始化,rank 分配 |
| 15s-3min | 模型权重加载(46 个 safetensors 分片) |
| 3-5min | DeepGEMM E8M0 启用,B12X MoE 内核选择 |
| 5-7min | TileLang JIT 编译 |
| 7-8min | FlashInfer autotune + CUDA graph 捕获 |
| 8min | Application startup complete |
# 监控日志
docker logs -f vllm-ds4
# 确认就绪
curl http://192.168.0.21:8000/health
5. 性能数据
| 指标 | 数值 |
|---|---|
| 模型参数 | ~200B (MoE, 43层, 4096 hidden size) |
| 模型磁盘占用 | ~155GB (46 分片, 内置 FP8 量化) |
| KV Cache | FP8, ~9 GiB 可用 (200K 上下文) |
| Decode 速度 | ~38 tok/s (B12X + MTP x2) |
| 无 MTP 速度 | ~26 tok/s |
| 加速比 | ~50% |
| 上下文窗口 | 200,000 tokens |
| 最大并发 | 6 sequences |
| 内存占用 | ~96 GiB / 128 GiB (每节点) |
6. API 接入
端点: http://192.168.0.21:8000/v1/chat/completions
模型: DeepSeek-V4-Flash
认证: Bearer <api-key>
curl 示例:
curl http://192.168.0.21:8000/v1/chat/completions \
-H "Authorization: Bearer sk-xxxx" \
-H "Content-Type: application/json" \
-d '{
"model": "DeepSeek-V4-Flash",
"messages": [{"role": "user", "content": "你好"}],
"max_tokens": 1024
}'
Python 示例:
from openai import OpenAI
client = OpenAI(
base_url="http://192.168.0.21:8000/v1",
api_key="sk-xxxx"
)
response = client.chat.completions.create(
model="DeepSeek-V4-Flash",
messages=[{"role": "user", "content": "你好"}]
)
7. 踩坑记录
坑 1:B12X MoE 与 expert-parallel 互斥
B12X Mxfp4 MoE 内核不接受 ep_size=2 的配置,报错 Mxfp4 MoE backend 'B12X' does not support FusedMoEParallelConfig(tp_size=1, ep_size=2)。选择:开 B12X(更快)或 开 EP(省内存),不能同时开。
坑 2:TileLang JIT 编译需大栈空间
DeepSeek V4 的 mHC (multi-head cache) 内核由 TileLang JIT 编译生成。Docker 默认栈空间不够,不设 --ulimit stack=67108864 会导致内核编译静默崩溃。
坑 3:GB10 统一内存的 NCCL 兼容性
GB10 的 CPU+GPU 共享 128GB LPDDR5X,NCCL 的 CUDA managed memory 优化在此架构上不兼容。必须设置:
NCCL_CUMEM_ENABLE=0:回退到显式内存拷贝NCCL_IGNORE_CPU_AFFINITY=1:禁用容器 CPU pinningNCCL_CROSS_NIC=0:单 NIC 拓扑
坑 4:Worker 必须先启动
mp backend 的 init_process_group 等待所有 rank 就绪。如果 Head 先启动而 Worker 未加入,Head 会在 NCCL 超时后(~10 min)崩溃。正确顺序:先 Worker,等 5 秒,再 Head。
坑 5:vLLM 0.21.1rc1 的 master-addr 语法
v0.21.1rc1 使用 --master-addr / --master-port 作为 CLI 参数,而 v0.22.0 使用 MASTER_ADDR / MASTER_PORT 环境变量。用错语法会导致 master_addr 静默回退到 127.0.0.1,跨节点 NCCL 握手失败但无报错。
坑 6:RoCE IB GID Index
直连(无交换机)场景必须用 NCCL_IB_GID_INDEX=0(link-local GID)。用 index 3 会导致两端 GID 类型不一致,报 ibv_modify_qp failed with Invalid argument。
坑 7:Docker Hub 不可达
DGX Spark 在中国网络环境下 Docker Hub 超时。需配置国内镜像加速(如 docker.nju.edu.cn),且镜像必须在白名单内。
坑 8:64GB swap 是硬性要求
DGX Spark 仅 128GB 统一内存,加载 200B 模型时 page cache + 模型权重 + CUDA 编译中间结果轻松超过 121GB。16GB swap 不够(实测 OOM),32GB 理论边界,200B 模型安全值 64GB。
8. 监控与维护
# 查看容器状态
ssh wangdefa@192.168.0.21 'docker ps --filter name=vllm-ds4'
# 查看实时日志
ssh wangdefa@192.168.0.21 'docker logs -f vllm-ds4 --tail 50'
# 重启服务
ssh wangdefa@192.168.0.21 'docker restart vllm-ds4'
# 一键重新部署
cd ~/.hermes/profiles/guijiqichengse/scripts && python3 deploy_dsv4.py
部署日期:2026-06-21 验证通过
硬件:NVIDIA DGX Spark x2
环境:Ubuntu 24.04 LTS, Docker, vLLM v0.21.1rc1 + SM12x patch
更多推荐
所有评论(0)