H200 GPU 服务器部署大语言模型:从容器、显存规划到多机推理

面向 H200 服务器部署大语言模型,覆盖 NVIDIA Container Toolkit、vLLM、张量并行、量化、服务化、监控与故障排查。

技术文章约 20 分钟414 次阅读

#这篇实施文解决什么问题

要在 8×H200 这类服务器上把大语言模型(Large Language Model,LLM)稳定地服务化:权重放哪里、KV Cache 与并发如何权衡、宿主机与容器各装什么、单机多卡怎么启动、多机怎么连、上线前怎么验证。背后最基础的两件事是显存预算和通信带宽——下文几乎所有参数选择(tensor-parallel-size、max-model-len、gpu-memory-utilization)都可以由这两件事推导出来。

H200 的核心规格(以 H200 SXM 公开规格为例):约 141 GB HBM3e 高带宽内存、约 4.8 TB/s 内存带宽、NVLink 4.0 每 GPU 双向 900 GB/s,经 NVSwitch 实现节点内 8 卡全互联。大显存解决"模型装得下",高带宽解决"token 出得快",NVLink 解决"多卡协作不拖后腿"。但真正部署时要管理的显存不是一块,而是四类:模型权重、KV Cache、运行工作区、通信缓冲。

版本说明:模型名称、精度、上下文长度和 vLLM 参数会随版本变化。下面命令是可操作的基线模板,生产使用前应在目标模型、目标驱动(Driver)和目标 CUDA 版本上做兼容性验证。

#一、显存预算:从"每参数字节数"推导

#权重字节数由精度决定

模型权重的理论显存 = 参数量 × 每参数字节数。FP16/BF16 通常约 2 字节,FP8 约 1 字节,INT4 权重约 0.5 字节:

精度每参数字节70B 参数权重说明
FP324约 280 GB训练/调试用,推理一般不选
FP16/BF162约 140 GB数据中心 GPU 推理主力精度
FP81约 70 GB需要模型权重本身是 FP8 或经量化得到
INT4(如 GPTQ/AWQ)约 0.5约 35 GB精度损失明显,通常需要量化校准数据

以 70B 参数模型为例:BF16 权重约 140 GB,H200 单卡 141 GB 显存——权重勉强放得下,但 KV Cache、CUDA graph、通信缓冲完全没有空间。这正是"单卡装得下权重"不等于"单卡能服务"的机制原因;这类模型至少需要张量并行(Tensor Parallelism,TP)为 2,每卡约 70 GB 权重,剩下约 70 GB 才留给 KV Cache 与运行开销。

实际数字还要加上量化元数据(分组 scale、zero-point)和反量化开销,INT4 的实际占用通常略高于 0.5 字节/参数;加载时若框架以更高精度暂存权重,峰值还会更高。所以估算只保证"量级正确",最终以 nvidia-smi 实测为准。

#KV Cache 为什么随序列长度和并发增长

自回归生成时,模型每生成一个 token 都要"回看"之前所有 token 的 Key/Value 注意力向量。与其每次重算,serving 引擎把它们缓存起来,这就是 KV Cache(键值缓存)。它是按"每个激活序列的每个 token"计费的:

单序列每 token 的 KV 占用 = 2(K、V 各一份)× 层数 × KV 头数 × head_dim × 每元素字节数。

以 Llama-3-70B 类模型的公开配置为例(80 层、8 个 KV 头、head_dim=128、GQA、BF16),可以用脚本直接算:

# 按公式估算单序列 KV 占用(十进制字节 → GiB)
python3 - <<'PY'
layers, kv_heads, head_dim, byte = 80, 8, 128, 2
per_token = 2 * layers * kv_heads * head_dim * byte
print(f"per token: {per_token} B = {per_token/1024:.0f} KiB")
for ctx in (4096, 32768, 131072):
    print(f"context {ctx:>7} tok: {per_token*ctx/2**30:8.1f} GiB/seq")
PY

预期输出:每 token 约 320 KiB;32K 上下文约 10 GiB/序列;128K 上下文约 40 GiB/序列。同一个公式也解释了为什么 KV 头部数(Grouped-Query Attention,GQA)很关键:如果把 8 个 KV 头换成 64 个注意力头全量缓存,每 token 占用会放大 8 倍。

由此得到两个推论,也就是"随序列长度和并发增长"的机制:

  • 序列越长,单序列占用 KV 越多,呈线性增长;
  • 并发越高,同时存活的序列越多,总 KV 占用线性叠加。

vLLM 用 PagedAttention 管理 KV Cache:显存按固定大小的块(block,默认 16 token)预分配成一个池,每个序列按需领取块。因此 KV 块池的总容量决定并发上限:块池越大、每序列消耗越少,能同时服务的请求就越多。把 max-model-len 设得比业务实际需要更长,等于给每个序列预留更多块,会直接压低并发上限。

#为什么实际显存占用比"权重 + KV Cache"还多

权重和 KV 只是大头,实际峰值占用还包括:

  • CUDA context 与驱动预留:每个 CUDA 进程数百 MB,多卡进程各一份;
  • 激活值(activation):前向计算中间张量,与 batch token 数、hidden size 成正比,长 prefill 时峰值可观;
  • CUDA graph:vLLM 为 decode 阶段捕获执行图,需要预留一段不可再分配的内存;
  • NCCL 通信缓冲:TP 每层 all-reduce 需要收发缓冲区;
  • 分配器碎片与预留段:PyTorch 的 caching allocator 会整段保留显存,碎片无法完全避免。

所以"权重 140 GB + KV 预算 70 GB"这种加法在量级上成立,但永远要给工作区留余量——这也是 gpu-memory-utilization 为什么只能给一个比例(如 0.90)而不能给 1.0(详见 4.3)。

#部署前先回答的六个问题

  1. 模型参数量和权重精度是什么?(决定权重字节数)
  2. 最大输入加最大输出 token 数是多少?(决定单序列 KV 上限与 prefill 长度)
  3. 目标并发、首 token 延迟和 token 吞吐是多少?(决定并发上限与批量策略)
  4. 单机八卡能否容纳权重和 KV Cache?(决定是否需要量化或多机)
  5. 需要 Tensor Parallel、Pipeline Parallel 还是 Data Parallel?(决定跨卡通信压力)
  6. GPU 间是 NVLink/NVSwitch,节点间是 InfiniBand 还是 Ethernet?(决定 TP 能扩到多大、跨节点用哪种并行)

这六个问题的答案共同决定下文的启动参数,不要跳过。

#二、宿主机与容器分层

#为什么宿主机只装驱动、容器装 runtime

GPU 软件栈分两层:内核层是驱动模块(nvidia.ko / nvidia_uvm.ko),由宿主机内核加载,负责访问 /dev/nvidia* 设备节点;用户层是 CUDA runtime、cuBLAS、cuDNN、框架,属于纯用户态文件。内核模块必须与宿主机内核和驱动版本匹配,用户层库则可以被容器完全固定。正确分层因此是:

  • 宿主机只安装 NVIDIA 驱动,并安装 NVIDIA Container Toolkit;
  • CUDA runtime 和框架全部放进经过验证的容器镜像(如 nvidia/cuda、vllm/vllm-openai)。

容器与宿主机共享内核模块,不要在容器里再安装一个与宿主机冲突的内核驱动;容器可移植性来自用户层库的固定版本,宿主机只提供内核接口。这样宿主机 Python、CUDA 库和系统包互不污染,升级框架只换镜像。

#NVIDIA Container Toolkit 配置与验证

前置条件:已装 NVIDIA 驱动(nvidia-smi 能列出 GPU)、已装 Docker、已装 NVIDIA Container Toolkit(nvidia-ctk 命令存在)。

# 确认驱动和 GPU 状态
nvidia-smi
nvidia-smi topo -m

# 配置 Docker 使用 nvidia runtime(写入 /etc/docker/daemon.json 并重启)
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

# 验证容器能看到 GPU:能输出 nvidia-smi 且 CUDA 版本匹配即可
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi

验证失败的排查顺序:Docker 是否使用 nvidia runtime(docker info 看 Runtimes)→ 宿主机驱动是否正常(nvidia-smi)→ 设备节点是否存在(ls -l /dev/nvidia*)→ 当前用户是否有权访问设备(是否在 docker 组、是否被权限拒绝)。容器里运行 nvidia-smi 看到的是"宿主机驱动 + 镜像内用户层库"的组合结果,镜像的 CUDA 版本以实际模型与 vLLM 镜像要求为准。

#三、模型与缓存目录

#目录规划与令牌安全

模型权重与 Hugging Face 缓存放在宿主机持久目录(本地 NVMe 或高性能并行文件系统),容器以只读方式挂载。好处是:多个容器、多次重启不重复下载;模型文件不进容器层;方便固定 revision 并做校验和。

# 创建只允许服务用户访问的缓存目录(-o/-g 按实际服务用户调整)
sudo install -d -o 1000 -g 1000 -m 0750 /srv/models /srv/hf-cache

# 通过环境变量指定缓存位置,避免写入容器临时层
export HF_HOME=/srv/hf-cache
# 令牌从密钥管理读取(如 Docker/K8s secret),不要写死在命令、历史或镜像里
export HUGGING_FACE_HUB_TOKEN="$(cat /run/secrets/hf_token)"

令牌(token)安全:不要把令牌写进镜像、Shell 历史或 Git;用短时效令牌或密钥管理系统注入。受限网络环境使用内部镜像仓库或离线制品(先在有网环境下载完整目录再离线拷贝),不要为了下载模型关闭 TLS 验证。

#预下载与校验

# 预下载模型;模型仓库与 revision 按实际授权替换,例如授权后的
# meta-llama/Llama-3.1-8B-Instruct(拒绝访问说明未接受 license 或网络受限)
huggingface-cli download meta-llama/Llama-3.1-8B-Instruct \
  --revision main \
  --local-dir /srv/models/llama-3.1-8b-instruct

生产环境固定模型 revision,并记录文件校验和,供回滚与审计:

# 生成模型目录校验和清单(sha256sum 为系统自带命令)
find /srv/models/llama-3.1-8b-instruct -type f -exec sha256sum {} + \
  > /srv/models/llama-3.1-8b-instruct.checksums.txt

#四、用 vLLM 启动单机多卡推理

#先看拓扑,再定 TP

单机八卡的第一步不是直接把 tensor-parallel-size 设成 8,而是先确认八卡拓扑、显存总量和服务端口,避免把跨 PCIe 交换的慢链路当成 NVLink 用:

# 查看八卡、显存和拓扑
nvidia-smi --list-gpus
nvidia-smi topo -m

topo -m 的输出中,节点内的 NVLink 连接会标注为 NV#;NVSwitch 把 8 卡全互联,而 PCIe/C2C 路径带宽远低于 NVLink。TP 应尽量覆盖 NVLink 域内的卡。

#单机多卡启动命令

# 启动单机多卡服务;参数需按模型实际能力调整
docker run --rm --gpus all --ipc=host --shm-size=32g \
  -p 8000:8000 \
  -v /srv/models:/models:ro \
  -v /srv/hf-cache:/root/.cache/huggingface \
  -e HF_TOKEN="$HUGGING_FACE_HUB_TOKEN" \
  vllm/vllm-openai:latest \
  --model /models/llama-3.1-8b-instruct \
  --tensor-parallel-size 8 \
  --dtype bfloat16 \
  --served-model-name llama-3.1-8b \
  --max-model-len 32768 \
  --gpu-memory-utilization 0.90 \
  --host 0.0.0.0 --port 8000

启动后先看日志是否出现 init engine 与 Started server process,再用第五节接口验证。

#参数背后的机制

  • tensor-parallel-size:把单个层的权重矩阵横向切到多张 GPU,每张卡算一部分,再通过 all-reduce 合并结果。因为每个 transformer 层、每个 token 周期都要做至少两次全量 all-reduce(通信量与 hidden size × 序列长度成正比),TP 高度依赖卡间带宽:H200 的 NVLink 4.0 每方向约 450 GB/s,8 卡经 NVSwitch 全互联;而 400G InfiniBand 约 50 GB/s、100GbE 约 12.5 GB/s。这就是"Tensor Parallel 为什么必须放在同一 NVLink/NVSwitch 域内"的机制——换到跨节点,同一通信量会慢一个数量级。

  • dtype=bfloat16:H100/H200 原生高效支持 FP16/BF16/FP8。BF16 与 FP32 同指数范围,训练/推理稳定性好,是数据中心 GPU 的常见选择;最终以模型权重本身的精度和框架支持为准,不要用与权重精度不匹配的 dtype 启动。

  • max-model-len:同时影响两件事——单序列 KV 的上限(见 1.2)和 prefill 阶段激活值规模。设得比业务实际需要长,会减少 KV 块池能支持的并发(每序列预留更多块),甚至直接 OOM;设得比需求短,超长请求会被拒绝。实践中先按"业务最大输入 + 最大输出"定,再压测微调。

  • gpu-memory-utilization:vLLM 把该比例的显存划为总预算,扣除权重后的大头是 KV 块池,剩余给激活与工作区。设成 1.0 会在两个地方失败:CUDA context、CUDA graph、NCCL 缓冲、激活峰值没有预留空间;分配器碎片使实际可用小于理论值。常见起点 0.85–0.93,启动后用 nvidia-smi 观察峰值,OOM 时先降这个值。

  • ipc=host 与 shm-size:vLLM 多进程/NCCL 需要在共享内存中做进程间同步与缓冲;容器默认 /dev/shm 只有 64 MB,容易触发共享内存不足。--ipc=host 让容器复用宿主机 /dev/shm(此时 --shm-size 不生效);不采用 host IPC 时必须显式给足 --shm-size(如 32g)。注意 --ipc=host 削弱容器隔离,按安全策略评估是否可接受。

#continuous batching 如何把并发吃满

vLLM 的调度器在每个 decode step 重新组合批量(iteration-level scheduling / continuous batching),而不是等一个静态批次全部结束:

  • 正在生成的序列每步各前进一个 token,一起组成一个 batch 交给 GPU;
  • 只要 KV 块池还有空块、显存与计算未饱和,新请求随时加入当前轮;
  • 完成的序列立即出队,释放它的 KV 块给新请求。

配合前面的按块分配,内存不再需要为"整条序列的最大长度"预留连续空间,短请求剩余的空间能立刻被下一个请求复用。并发上限最终由 KV 块池(取决于 gpu-memory-utilization、max-model-len)和 GPU 计算能力共同决定:块池空了,调度器拒绝或排队新请求;算力满了,decode 步长变慢、首 token 延迟上升。这也是"并发压不上去"时要先看 KV 块池与 max-model-len、而不是盲目加机器的原因。

#五、OpenAI 兼容接口和基本验证

服务就绪后做三连验证:

# 1) 检查服务是否就绪:200 即通过,vLLM 的 /health 服务可用时返回 OK
curl -f http://127.0.0.1:8000/health

# 2) 查看模型列表:data[0].id 应等于 --served-model-name
curl -s http://127.0.0.1:8000/v1/models | jq

# 3) 发送一条最小请求:返回 choices[0].message.content
curl -s http://127.0.0.1:8000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "llama-3.1-8b",
    "messages": [{"role": "user", "content": "用一句话解释 NUMA。"}],
    "temperature": 0.2,
    "max_tokens": 64
  }' | jq

再验证流式与超长请求行为:

# 流式:应逐行收到 data: {...} 并以 data: [DONE] 结束
curl -N http://127.0.0.1:8000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  --max-time 60 \
  -d '{"model":"llama-3.1-8b","messages":[{"role":"user","content":"数到 10"}],"stream":true,"max_tokens":64}'

# 超长请求:输入超过 max-model-len 应返回明确的 4xx 错误,而不是挂死
curl -s -o /dev/null -w "%{http_code}\n" \
  http://127.0.0.1:8000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"llama-3.1-8b","messages":[{"role":"user","content":"repeat: '"$(printf 'x%.0s' {1..40000})"'"}],"max_tokens":64}'

上线前还要验证:并发限制(限流下请求应排队或快速失败,而不是 OOM)、错误码语义、上下文截断、中文输入、长文本和恶意/对抗提示、下游代理超时。不要仅凭一次 curl 成功就判定服务可用。

#六、多机部署与 NCCL/InfiniBand

#通信路径的选择机理

NCCL 按拓扑自动选路,优先级大致是:

  • 节点内:GPU 间 P2P 走 NVLink/NVSwitch(最优);P2P 不可用时退到共享内存(SHM);
  • 节点间:优先 InfiniBand + GPUDirect RDMA(GDR,GPU 直接读写网卡内存,绕过 CPU 拷贝);其次是 RoCE;最后退化为 TCP sockets。

退化到 TCP 意味着每笔跨节点通信都要经过 CPU 与内核协议栈,带宽和延迟都会劣化。所以多机部署第一步不是"调参数",而是确认 IB 链路本身是否健康,再让 NCCL 选路。

#节点检查与 NCCL 环境变量

前置条件:OFED/驱动已装、IB 端口 ACTIVE、容器映射了 /dev/infiniband(Docker 需 --device /dev/infiniband 或等价授权,GPU Operator 可自动处理)。

# 1) 在每个节点检查 HCA、端口和 RDMA 设备
ibdev2netdev   # HCA <-> 网络设备(netdev)映射
ibstat         # 端口状态,应看到 State: Active
ibv_devinfo    # RDMA 设备与能力

# 2) NCCL 调试环境变量;接口和 HCA 按实际拓扑替换(如 ib0 / mlx5_0:1)
export NCCL_DEBUG=INFO
export NCCL_IB_DISABLE=0
export NCCL_SOCKET_IFNAME=ib0
export NCCL_IB_HCA=mlx5_0:1
export NCCL_NET_GDR_LEVEL=2

变量含义:NCCL_DEBUG=INFO 打印选路与建链日志;NCCL_IB_DISABLE=0 允许使用 IB;NCCL_SOCKET_IFNAME 指定 socket 接口(TCP 路径与控制面);NCCL_IB_HCA 指定使用的 IB HCA 与端口;NCCL_NET_GDR_LEVEL=2 允许 GPU 直接访问网络内存(GPUDirect RDMA,需驱动、固件与 OFED 支持)。这些变量要注入启动服务的容器(-e),或在容器内导出。

先用 nccl-tests 验证 AllReduce:

# 8 卡 AllReduce 带宽测试,从 8 B 到 4 G 逐级加压
./build/all_reduce_perf -b 8 -e 4G -f 2 -g 8

配合 NCCL_DEBUG=INFO,从日志确认通信走的是 NVLink/IB 而不是 NET/Socket(TCP);同节点 8 卡 NVLink 的 AllReduce 带宽应显著高于走 TCP 的数值。

#多机 tensor parallel 的网络瓶颈

跨节点 TP 意味着每个 transformer 层的两次 all-reduce 都要跨节点完成。粗估流量:70B 模型(hidden size 约 8192)decode 阶段单步批量 1024 token 时,通信量约 2 × 80 层 × 8192 × 1024 × 2 字节 ≈ 2.7 GB/step(十进制)。若希望每 step 在约 30 ms 内完成,需要约 90 GB/s 持续带宽——单条 400G IB(约 50 GB/s)会明显成为瓶颈,NVLink(每方向约 450 GB/s)则从容。结论:

  • TP 尽量限制在单节点 NVLink 域内;跨节点扩容优先考虑流水线并行(Pipeline Parallelism,PP,只传层边界激活值,通信量小得多)或整模型副本 + 负载均衡(数据并行,Data Parallelism,DP,节点间几乎没有同步流量)。vLLM 也支持 --pipeline-parallel-size,但功能随版本变化,以所用版本文档为准。
  • 如果单节点确实放不下、必须跨节点 TP,网络至少要有高带宽低延迟 IB,并把 NCCL_SOCKET_IFNAME、NCCL_IB_HCA 显式钉住,避免选错网口。

vLLM 跨节点执行器默认走 Ray:

# 节点 A(head)
ray start --head --port=6379
# 节点 B(worker),IP 换成 head 节点实际地址
ray start --address=192.0.2.10:6379

# 在 head 节点启动跨节点 TP 服务;--tensor-parallel-size 为跨节点总卡数,
# 较新版本可加 --distributed-executor-backend ray 显式指定执行器
docker run --rm --gpus all --ipc=host --shm-size=32g \
  -p 8000:8000 \
  -v /srv/models:/models:ro \
  -v /srv/hf-cache:/root/.cache/huggingface \
  -e NCCL_DEBUG=INFO -e NCCL_IB_DISABLE=0 -e NCCL_SOCKET_IFNAME=ib0 -e NCCL_IB_HCA=mlx5_0:1 \
  vllm/vllm-openai:latest \
  --model /models/llama-3.1-8b-instruct \
  --tensor-parallel-size 16 \
  --dtype bfloat16 \
  --max-model-len 32768 \
  --gpu-memory-utilization 0.90 \
  --host 0.0.0.0 --port 8000

#NCCL 退化到 TCP 的排障顺序

日志出现 NET/Socket(TCP)时按顺序排查:

  1. HCA 名称/端口:ibdev2netdev、ibstat 确认名称与 NCCL_IB_HCA 一致,端口 State 为 Active;
  2. 容器内可见性:容器是否映射 /dev/infiniband;没有则 NVIDIA 库看不到 HCA;
  3. 裸链路带宽:用 perftest 对打,验证 IB 本身通且带宽正常:
# 节点 A 起 server
ib_write_bw -d mlx5_0
# 节点 B 起 client 对打(IP 按实际替换)
ib_write_bw -d mlx5_0 192.0.2.10
  1. 节点间路由:用 ping/ip route 确认管理面与数据面互通;
  2. 驱动与 OFED 匹配:ibstat 正常但 NCCL 仍退化时,检查驱动/OFED 版本是否被官方支持;
  3. NUMA 绑定:GPU 与 HCA 跨 NUMA 时 GDR 可能不可用,用 nvidia-smi topo -m 看亲和关系,必要时调整 PCIe/NUMA 布局或降低 NCCL_NET_GDR_LEVEL。

先用 perftest 和 nccl-tests 定位是"链路坏"还是"NCCL 选路错",再看 vLLM 日志确认建链细节。

#七、生产服务化

#1. systemd 或 Kubernetes 管理

单机可用 systemd 或 Docker Compose 管理重启、日志和资源;集群环境建议用 Kubernetes 调度 GPU,配合 NVIDIA device plugin 或 NVIDIA GPU Operator,让 Pod 能申请 nvidia.com/gpu 资源。部署清单应声明 GPU 资源、readinessProbe、livenessProbe、优雅退出和滚动更新策略:

# Deployment 中 Pod 模板关键字段节选
spec:
  template:
    spec:
      containers:
        - name: llm-server
          resources:
            limits:
              nvidia.com/gpu: 8
          ports:
            - containerPort: 8000
          livenessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 120   # 必须大于模型冷启动时间
            periodSeconds: 10
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 30
            periodSeconds: 10

要点:readiness 要在模型加载完成、/health 返回成功后才把 Pod 置为可用;模型冷启动可能持续数分钟,liveness 的 initialDelaySeconds 要大于冷启动时间,或用 startupProbe 覆盖加载期;滚动更新要避免同一时刻拉起两份 8 卡副本把 GPU 打爆(用最大不可用数或调度策略控制)。

#2. 监控和容量保护

至少监控:GPU 利用率、显存、温度、功耗、Xid 错误、请求队列、首 token 延迟、生成 token 吞吐、错误率和 OOM 次数。

# 持续采集关键 GPU 指标(pucm = power/utilization/clock/memory)
nvidia-smi dmon -s pucm -d 5

# 一次性查询关键字段,便于接入采集器
nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total,temperature.gpu,power.draw --format=csv,noheader

# 查看服务容器日志
docker logs --tail=200 -f llm-server

Xid 错误出现在内核日志(dmesg | grep -i xid 或 nvidia-smi -q),属于驱动/硬件级故障,应接入告警。容量保护:限制最大并发(vLLM 可用 --max-num-seqs,以版本为准)与最大请求 token(max-model-len 兜底),避免一个长上下文请求耗尽整个 KV Cache 拖垮所有在线请求;超限请求应返回明确错误而不是排队到超时。

#3. 常见故障排查顺序

  • 启动即 OOM:降低 max-model-len、并发和 gpu-memory-utilization;确认模型精度、tensor-parallel-size 与卡数匹配(权重 × 卡数能否放下且有余量)。
  • 多卡通信慢:检查 nvidia-smi topo -m、NCCL 日志(是否退化为 NET/Socket)、HCA、NUMA 亲和和 RDMA 计数器。
  • 请求超时:区分耗时发生在模型加载、首 token、生成阶段还是网络/代理层——分别看启动日志、流式首包时间和网关超时设置。
  • 输出异常:确认 tokenizer、chat template、模型 revision 和服务参数匹配(model 名、dtype、max-model-len)。
  • GPU 不可见:宿主机 nvidia-smi → 容器 --gpus all → NVIDIA Container Toolkit 配置 → 设备节点与用户权限,逐层排除。

#4. 容量基线与故障演练

上线前固定模型 revision、容器 digest 和测试集,分别测短、中、长上下文,记录首 token 延迟、生成吞吐、显存峰值和错误率:

# 粗粒度基线:同一请求体循环 N 次统计分位耗时;
# 正式压测使用 vLLM 仓库 benchmarks 下的基准脚本或 k6/Vegeta 等工具
for i in $(seq 1 100); do
  curl -s -o /dev/null -w "%{time_total}\n" \
    http://127.0.0.1:8000/v1/chat/completions \
    -H 'Content-Type: application/json' \
    -d '{"model":"llama-3.1-8b","messages":[{"role":"user","content":"1+1=?"}],"max_tokens":16}'
done | sort -n | awk '{a[NR]=$1; s+=$1} END {printf "n=%d avg=%.3fs p50=%.3fs p95=%.3fs p99=%.3fs\n", NR, s/NR, a[int(NR*0.50)], a[int(NR*0.95)], a[int(NR*0.99)]}'

验收口径:readiness 必须在模型加载完成后才成功;超出上下文或并发上限应返回明确错误;OOM 时优先降低上下文、并发或显存比例,而不是加机器。演练至少包含:固定数据集压测、长上下文压测、故障重启(kill 容器后自动拉起)、模型回滚(切回上一 revision 并核对校验和);把驱动、容器 digest、模型 revision、配置和硬件拓扑记录进发布单。

#边界:什么时候不该照抄这套配置

  • 单卡能容纳权重与 KV 的小模型:不要加 TP,直接用单卡,省掉通信开销;
  • 节点间只有以太网、没有高速低延迟网络:不要跨节点 TP,优先多机副本 + 负载均衡;
  • 对首 token 延迟极致敏感、或需要更强量化/编译优化的业务:vLLM 只是基线,再评估其他 serving 引擎与量化方案;
  • 任何参数在换模型、换驱动、换 vLLM 版本后,都必须重新走一遍"六问 → 启动 → 验证 → 压测"。

相关阅读

Related

留言讨论

0 条

还没有留言,来说点什么吧。

H200 GPU 服务器部署大语言模型:从容器、显存规划到多机推理 · LXH·BLOG