H20 双机 16 卡 DeepSeek-V4-Flash vLLM 生产部署实践
这篇文章整理的是一个 H20 双机 16 卡推理服务的公开版落地经验。目标模型是 DeepSeek-V4-Flash,服务框架使用 vLLM OpenAI-compatible API,分布式执行使用 Ray,运行形态使用 Docker 容器。为了适合公网发布,文中不会出现原始节点名、IP 地址、模型绝对路径、内部镜像仓库、日志路径和内部业务信息,相关位置统一用占位符表示。
一、为什么不是简单 docker run vllm
单机小模型部署时,一个 vLLM 容器、一个模型路径、一个 API 端口通常就能跑起来。但 DeepSeek-V4-Flash 这类 MoE、FP8、长上下文模型部署到双机 16 卡时,真正的难点在于四层协同:
| 层级 | 关键点 | 失败表现 |
|---|---|---|
| 宿主机层 | GPU、驱动、Docker、NVIDIA Container Toolkit | 容器看不到 GPU、驱动能力不匹配 |
| 网络层 | 管理网、RoCE 数据面、GID、HCA、ARP/RPF | Ray 加入失败、NCCL fallback、跨机慢 |
| 分布式层 | Ray head/worker、placement group、GPU 资源 | worker lost、GPU 资源不足、服务启动卡住 |
| vLLM 层 | TP/PP、KV cache、MoE、FP8、tool parser | OOM、模型加载失败、首 token 延迟异常 |
生产部署的目标不是“启动一次成功”,而是要做到环境可检查、参数可复现、日志可定位、服务可压测、失败可回滚。
二、推荐架构
第一条是业务入口链路。业务方访问入口代理或 vLLM API Server,API 使用 OpenAI-compatible 协议,最常用的是 /v1/chat/completions、/v1/completions、/v1/models 和 /health。生产中建议前面接 Nginx、Envoy 或 HAProxy,用于健康检查、TLS、鉴权、限流和多入口分流。
第二条是 Ray 控制面链路。Ray head 运行在 head 节点,worker 节点加入 Ray 集群。控制面建议绑定管理网,例如 <management-bond>,不要和 RoCE 数据面混用。Ray 看到的 GPU 资源应该是双机共 16 张卡。
第三条是 NCCL 数据面链路。跨机 Tensor Parallel 或 Pipeline Parallel 通信依赖 RoCE/RDMA。容器必须能看到 /dev/infiniband,NCCL 需要绑定正确的 HCA 列表、GID index 和 socket interface。日志中应能看到 Using network IB、GDRDMA 等证据,避免静默退回 TCP/socket。
这里要先区分两种生产形态:如果目标是一个跨双机的单模型副本,vLLM 主流写法是每节点内做 TP、节点间做 PP,例如 2 台 8 卡节点使用 TP=8, PP=2。如果模型单机 8 卡就能放下,也可以部署两个独立 TP=8, PP=1 副本,再由入口代理分流;这属于服务级副本扩展,不是一个跨 16 卡的 vLLM engine。
生产上还要把多节点通信放在隔离网络里。Ray、PyTorch Distributed、NCCL、KV cache 传输都不应该直接暴露到公网;对外只暴露经过鉴权、限流和审计的 API 入口。
三、部署前环境检查
| 检查项 | 目标 |
|---|---|
| OS / Kernel | 双节点版本一致,内核参数可控 |
| GPU | 每台 8 卡可见,nvidia-smi -L 正常 |
| Driver / CUDA capability | 满足目标 vLLM 镜像和模型要求 |
| Docker | 支持 --gpus all,容器内可见 GPU |
| NVIDIA Container Toolkit | 版本一致,runtime 正常 |
/dev/infiniband |
宿主机和容器内均可见 |
| RoCE GID | 目标 GID index 为 RoCE v2 |
| ARP / RPF | 多网卡场景下避免回包路径异常 |
| 模型目录 | 本地可读,权重、tokenizer、config 完整 |
| 镜像能力 | 包含 vLLM、Ray、PyTorch、RDMA 工具 |
容器 GPU 检查可以用类似命令:
docker run --rm \
--gpus all \
--network=host \
--ipc=host \
<cuda-or-vllm-image> nvidia-smi
RDMA 工具建议至少包含:
ibv_devinfo
ibdev2netdev
show_gids
如果官方 vLLM 镜像没有这些工具,可以做一个很薄的派生镜像,只增加 rdma-core、iproute2、curl、net-tools 等诊断工具。
四、容器运行方式
双机推理服务建议使用 host network,减少容器网络对 Ray、NCCL、RoCE 的干扰。典型容器形态如下:
docker run -d \
--name <vllm-ray-container> \
--gpus all \
--network=host \
--ipc=host \
--shm-size=256g \
--ulimit memlock=-1:-1 \
--ulimit stack=67108864 \
--cap-add IPC_LOCK \
--device=/dev/infiniband \
-v /dev/infiniband:/dev/infiniband \
-v <model-path>:<model-path>:ro \
-v <log-dir>:<log-dir> \
-e NCCL_NET=IB \
-e NCCL_SOCKET_IFNAME=<management-bond> \
-e NCCL_IB_GID_INDEX=<gid-index> \
-e NCCL_IB_HCA=<hca-list> \
-e NCCL_ASYNC_ERROR_HANDLING=1 \
-e NCCL_IB_DISABLE=0 \
-e GLOO_SOCKET_IFNAME=<management-bond> \
-e VLLM_HOST_IP=<node-management-ip> \
<vllm-image> sleep infinity
这里有几个容易踩坑的点。
--network=host 不是为了省事,而是为了让 Ray、vLLM、NCCL 在多网卡环境中更可控。--ipc=host 或足够大的 --shm-size 对 PyTorch/vLLM 也很重要。--device=/dev/infiniband 和卷挂载同时配置,是为了让容器内 RDMA 诊断工具和 NCCL 都能看到设备。
五、Ray 启动顺序
推荐顺序是先容器、再 Ray、再 vLLM。
head 节点:启动容器 -> ray start --head
worker 节点:启动容器 -> ray start --address <head-ip>:6379
head 节点:检查 ray status -> 启动 vLLM API Server
Ray 检查重点不是“命令返回成功”,而是资源视图是否正确。应该看到两台节点 active,并且总 GPU 数为 16。若 Ray 资源不足,vLLM 后续 placement group 会失败或一直等待。
六、vLLM 参数基线
DeepSeek-V4-Flash 是 MoE + FP8 + 长上下文模型,不建议一开始就把上下文和并发拉满。一个更稳的首轮基线可以这样设计:
vllm serve <model-path> \
--host 0.0.0.0 \
--port <api-port> \
--served-model-name DeepSeek-V4-Flash \
--trust-remote-code \
--distributed-executor-backend ray \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--dtype auto \
--kv-cache-dtype fp8 \
--max-model-len 65536 \
--gpu-memory-utilization 0.88 \
--max-num-seqs 8 \
--enable-expert-parallel \
--tokenizer-mode deepseek_v4 \
--tool-call-parser deepseek_v4 \
--enable-auto-tool-choice \
--reasoning-parser deepseek_v4
参数解释:
| 参数 | 建议 |
|---|---|
tensor-parallel-size |
对 2 台 8 卡节点,单跨机副本优先设为每节点 GPU 数,即 8 |
pipeline-parallel-size |
对 2 节点跨机副本设为 2,让模型层按 pipeline stage 分布到两台机器 |
max-model-len |
先用 65K,稳定后再扩到 128K/256K |
kv-cache-dtype |
FP8 有利于降低 KV cache 压力 |
gpu-memory-utilization |
先保守,避免碎片和 OOM |
max-num-seqs |
从小并发启动,逐步增加 |
如果使用的是深度定制 DSFlash 镜像,可能还有额外 parser、block size 或 speculative decoding 参数。不建议把这类非通用参数写死;先用 vLLM 主线参数跑通,再按镜像说明做专项 A/B。
TP=16, PP=1 也可以作为实验项,即 Tensor Parallel 横跨两台机器。但这会让大量逐层 all-reduce 穿过跨机 RoCE,对网络、NCCL、GDRDMA 和拓扑一致性要求更高。除非跨机互联已经被验证足够稳定,否则它更适合作为压测对照,而不是首轮生产基线。
如果显存不足,优先按下面顺序收敛:降低 max-num-seqs,再降低 max-model-len,最后再调整 gpu-memory-utilization。不要一开始就把显存利用率拉到极限,否则后续排障很难判断是模型、KV cache、碎片还是通信导致的问题。
七、服务验证
服务启动后,最少做三类验证。
健康检查:
curl -fsS http://<api-host>:<api-port>/health
curl -fsS http://<api-host>:<api-port>/v1/models
功能冒烟:
curl http://<api-host>:<api-port>/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "DeepSeek-V4-Flash",
"messages": [
{"role": "user", "content": "请用一句话介绍你自己。"}
],
"max_tokens": 128,
"temperature": 0
}'
通信证据:
grep -E "Using network IB|GDRDMA|NCCL|Ray" <vllm-log>
如果只看 API 能返回,可能会漏掉 NCCL fallback。跨机推理服务必须确认数据面真的走 RoCE/IB/GDRDMA。
八、生产排障经验
第一,镜像能力要先验收。模型 config 支持 DeepSeek V4 不等于当前镜像一定支持对应 architecture、parser、MoE、FP8 和长上下文。
第二,控制面和数据面要分清。Ray、Gloo、API 健康检查可以走管理网;NCCL 数据面要绑定 RoCE HCA。两者混在一起会导致“能启动但性能不稳定”。
第三,日志要从启动阶段开始保留。TileLang lazy compile、Ray worker lost、NCCL fallback、OOM、KV cache 不足都可能只在特定阶段出现。
第四,长上下文不能只靠 max_position_embeddings 判断。模型配置可能支持极长上下文,但实际可服务长度取决于 KV cache、并发、显存碎片和 batch token 策略。
九、参考资料
- vLLM Parallelism and Scaling:https://docs.vllm.ai/en/stable/serving/parallelism_scaling/
- vLLM Docker Deployment:https://docs.vllm.ai/en/stable/deployment/docker/
- vLLM Security:https://docs.vllm.ai/en/latest/usage/security/
十、总结
H20 双机 16 卡部署 DeepSeek-V4-Flash 的关键,是把 Docker、Ray、vLLM、RoCE/NCCL 和模型参数放在同一个工程闭环里。正确的落地方式不是复制一条启动命令,而是先检查环境,再固化容器和网络参数,然后按小并发到大并发、短上下文到长上下文逐步压测。
当服务能稳定通过健康检查、功能冒烟、NCCL 数据面验证和三组 workload 压测后,才适合作为后续拓扑优化和业务接入的基线。