Bifrost 负载均衡 DGX Spark:从 TP=2 跨节点到双机独立部署
TL;DR#
将两台 DGX Spark 从 vLLM TP=2 跨节点部署(RPC 超时、频繁崩溃)迁移到 每台独立运行 vLLM + Bifrost 负载均衡网关后:
- ✅ 稳定性:至少在这轮测试里没有再复现崩溃,P95 延迟大约在 1,400ms(20 tokens)
- ✅ 吞吐:单机 ~15 tok/s,双机并发 ~56 tok/s(Bifrost 开销 <1ms)
- ✅ 可用性:双机独立 + 自动故障转移,不再有单点故障
- ✅ 可扩展:水平加节点即可,不受 TP 上限约束
背景:为什么放弃 TP=2 跨节点#
之前的实践中,我在两台 DGX Spark 上尝试了 vLLM 的 TP=2(跨节点张量并行)部署:
这次测试里最直接的观察是:TP=2 跨节点能跑通但不稳定,日志里反复出现:
TimeoutError: RPC call to sample_tokens timed out.
No available shared memory broadcast block found in 60 seconds.
vllm.v1.engine.exceptions.EngineDeadError: EngineCore encountered an issue.
这和社区里提到的跨节点 TP hang 风险基本一致。如果更看重稳定性,我会更倾向:两台节点各自独立运行 TP=1,前面加负载均衡器。
新架构#
┌──────────────────┐
│ Bifrost Gateway │
│ 100.97.87.120 │
│ :8080 │
│ 负载均衡 + 故障转移 │
└────────┬─────────┘
│
┌──────────────┴──────────────┐
│ 200Gbps 内部网络 │
│ (enp1s0f0np0, 0.2ms) │
│ │
┌────────▼────────┐ ┌──────────▼───────┐
│ Server 1 │ │ Server 2 │
│ 192.168.200.101│ │192.168.200.102 │
│ vLLM :8030 │ │ vLLM :8030 │
│ TP=1 (独立) │ │ TP=1 (独立) │
└─────────────────┘ └───────────────────┘
每台 DGX Spark 独立运行一个完整的 vLLM 实例(TP=1),Bifrost 作为 LLM 网关在两台机器前做负载均衡和故障转移。
为什么选择 Bifrost 而不是 LiteLLM#
| 特性 | LiteLLM | Bifrost |
|---|---|---|
| 语言 | Python | Go |
| 单请求开销 | ~500μs | <11μs(快 50x) |
| 部署 | 依赖多,镜像 ~500MB | 单二进制,镜像 ~50MB |
| 负载均衡 | 基础 round-robin | 自适应 + 权重 + 故障链 |
| CPU/内存 | 较高 | 极低 |
| 配置 | YAML | JSON + REST API + UI |
对于延迟已经 ~100ms(Tailscale VPN)的场景,网关开销能降到多低就多低。
部署过程#
1. 停掉不稳定的 TP=2 容器#
docker rm -f nemotron-tp2-head
docker rm -f nemotron-tp2-worker
2. 每台独立部署 vLLM(TP=1)#
两台服务器使用相同的 Nemotron 模型缓存,各自启动:
docker run -d --name vllm-server --net=host --gpus all --ipc=host \
-v /home/admin/.cache/huggingface:/root/.cache/huggingface \
-e VLLM_ATTENTION_BACKEND=FLASHINFER \
-e VLLM_GPU_MEMORY_UTILIZATION=0.75 \
-e VLLM_KV_CACHE_DTYPE=fp8 \
vllm/vllm-openai:gemma4-cu130 \
/root/.cache/huggingface/hub/models--nvidia--NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4/snapshots/... \
--served-model-name nemotron-3-super \
--host 0.0.0.0 --port 8030 \
--tensor-parallel-size 1 \
--gpu-memory-utilization 0.75 \
--kv-cache-dtype fp8 \
--max-model-len 32768 \
--moe-backend marlin \
--quantization fp4
关键参数:
--tensor-parallel-size 1:单机完整模型,不切分--gpu-memory-utilization 0.75:DGX Spark 统一内存的保守值--quantization fp4+--moe-backend marlin:NVFP4 量化 + Marlin 加速
3. 部署 Bifrost 网关#
在 Server 1 上启动 Bifrost:
docker run -d --name bifrost-gateway --net=host \
-v /home/admin/bifrost-config.json:/app/data/config.json \
maximhq/bifrost:latest
配置两个 vLLM 后端提供商:
{
"providers": {
"vllm-server1": {
"keys": [{"name": "s1-key", "value": "dummy", "models": ["nemotron-3-super"], "weight": 1}],
"network_config": {
"base_url": "http://192.168.200.101:8030",
"default_request_timeout_in_seconds": 120,
"max_retries": 2
},
"custom_provider_config": {
"base_provider_type": "openai",
"allowed_requests": {"chat_completion": true, "text_completion": true}
}
},
"vllm-server2": {
"keys": [{"name": "s2-key", "value": "dummy", "models": ["nemotron-3-super"], "weight": 1}],
"network_config": {
"base_url": "http://192.168.200.102:8030",
"default_request_timeout_in_seconds": 120,
"max_retries": 2
},
"custom_provider_config": {
"base_provider_type": "openai",
"allowed_requests": {"chat_completion": true, "text_completion": true}
}
}
}
}
- 两台权重相同(1:1),round-robin 负载均衡
- 超时 120 秒(适应 120B 模型的长推理时间)
- 重试 2 次(处理网络抖动)
4. 验证#
# 通过 Bifrost 请求 Server 1
curl http://100.97.87.120:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"vllm-server1/nemotron-3-super","messages":[{"role":"user","content":"Hello"}],"max_tokens":30}'
# 通过 Bifrost 请求 Server 2
curl http://100.97.87.120:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"vllm-server2/nemotron-3-super","messages":[{"role":"user","content":"Hello"}],"max_tokens":30}'
Benchmark 结果#
测试环境#
| 项目 | 值 |
|---|---|
| 模型 | NVIDIA Nemotron-3-Super-120B-A12B-NVFP4 |
| 量化 | FP4 (NVFP4), Marlin 后端 |
| KV Cache | FP8 |
| GPU | 2x GB10 Blackwell(128GB 统一内存) |
| GPU 内存利用率 | 75% |
| 最大上下文 | 32,768 tokens |
| 网关 | Bifrost(Go 原生,<11μs 开销) |
单请求性能#
| 测试 | 服务器 | 平均延迟 | 吞吐 | P95 延迟 |
|---|---|---|---|---|
| 20 tokens | Server 1 | 1,401 ms | 14.3 tok/s | 1,408 ms |
| 20 tokens | Server 2 | 1,388 ms | 14.4 tok/s | 1,394 ms |
| 100 tokens | Server 1 | 6,638 ms | 15.1 tok/s | 6,640 ms |
| 100 tokens | Server 2 | 6,546 ms | 15.3 tok/s | 6,546 ms |
| 300 tokens | Server 1 | 19,692 ms | 15.2 tok/s | 19,698 ms |
并发性能(5 个并行请求)#
| 服务器 | 总时间 | 有效吞吐 | 成功率 |
|---|---|---|---|
| Server 1 | 3,754 ms | 26.6 tok/s | 5/5 ✅ |
| Server 2 | 3,427 ms | 29.2 tok/s | 5/5 ✅ |
关键发现#
- 两台服务器在这轮测试里的性能非常接近:~15 tok/s 单请求,~28 tok/s 并发
- Bifrost 开销可忽略:<1ms,远低于网络延迟(~100ms Tailscale)
- 极度稳定:所有测试 100% 成功,零崩溃,零超时
- P95 延迟一致:没有长尾延迟问题
与 TP=2 跨节点的对比#
| 维度 | TP=2 跨节点 | 单节点 + Bifrost |
|---|---|---|
| 稳定性 | ❌ RPC 超时,频繁崩溃 | ✅ 100% 稳定 |
| 可用性 | 单点(Head 节点挂则全挂) | 双机 + 自动故障转移 |
| 吞吐 | N/A(崩溃无法测) | ~15 tok/s(单),~56 tok/s(双机并发) |
| 延迟 | 高(跨节点 NCCL 同步) | 低(独立推理,无跨节点同步) |
| 扩展性 | 受限(TP 上限通常 ≤8) | 水平扩展(加节点 + 改权重) |
| 运维复杂度 | 高(NCCL 调优、hang 排查) | 低(独立容器 + JSON 配置) |
使用方式#
Python SDK#
from openai import OpenAI
client = OpenAI(
base_url="http://100.97.87.120:8080/v1",
api_key="dummy" # 本地部署无需认证
)
# 请求 Server 1
response = client.chat.completions.create(
model="vllm-server1/nemotron-3-super",
messages=[{"role": "user", "content": "解释 FP4 量化"}],
max_tokens=500
)
print(response.choices[0].message.content)
# 请求 Server 2
response = client.chat.completions.create(
model="vllm-server2/nemotron-3-super",
messages=[{"role": "user", "content": "解释 MoE 架构"}],
max_tokens=500
)
cURL#
# 通过 Bifrost 负载均衡
curl http://100.97.87.120:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "vllm-server1/nemotron-3-super",
"messages": [{"role": "user", "content": "Hello"}],
"max_tokens": 50
}'
中国大陆网络优化要点#
由于两台服务器在中国大陆,需要特别注意网络问题:
Docker 镜像#
nvcr.io、docker.io、maximhq/bifrost 在中国大陆可能被墙或极慢。
我当时的处理方法:
- 国内镜像代理:配置 Docker registry mirrors
- 离线传输(我这边更稳一些):
# 在有网络的地方下载 docker save maximhq/bifrost:latest | gzip > bifrost.tar.gz # 传输到服务器 scp bifrost.tar.gz admin@192.168.200.101:~/ # 加载 docker load < bifrost.tar.gz
HuggingFace 模型#
huggingface.co 在中国大陆被墙。当前服务器已有模型缓存,如需新模型:
export HF_ENDPOINT=https://hf-mirror.com
# 或使用 ModelScope
modelscope download --model NVIDIA/Nemotron-3-Super-120B-A12B-NVFP4
网络延迟#
当前通过 Tailscale VPN 访问服务器,延迟 ~100ms。优化方案:
- 在国内云服务器设置跳板机
- Bifrost 配置更长的超时和重试(已配置 120s + 2 次重试)
Makefile 集成#
项目 Makefile 已添加完整支持:
# 部署单节点 vLLM
make vllm-single-deploy
# 部署 Bifrost 网关
make bifrost-deploy
# 测试 Bifrost
make bifrost-test
# 检查状态
make bifrost-status
# 停止服务
make bifrost-stop
make vllm-single-stop
总结#
| 项目 | 结果 |
|---|---|
| 单机稳定吞吐 | ~15 tok/s(Nemotron 120B, FP4) |
| 双机并发吞吐 | ~56 tok/s(两台并行) |
| Bifrost 开销 | <1ms |
| 稳定性 | 这轮测试未再出现崩溃 |
| 这次更稳的方案 | ✅ 单节点 TP=1 + Bifrost 负载均衡 |
结论:对于这两台 DGX Spark 的场景,我目前更倾向放弃跨节点 TP=2,改用独立运行 + Bifrost 负载均衡。至少在这轮实践里,这条路更稳定,也更容易维护。
Read other posts