H200 GPU 服务器部署大语言模型:从容器、显存规划到多机推理
面向 H200 服务器部署大语言模型,覆盖 NVIDIA Container Toolkit、vLLM、张量并行、量化、服务化、监控与故障排查。
#这篇实施文解决什么问题
要在 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 参数权重 | 说明 |
|---|---|---|---|
| FP32 | 4 | 约 280 GB | 训练/调试用,推理一般不选 |
| FP16/BF16 | 2 | 约 140 GB | 数据中心 GPU 推理主力精度 |
| FP8 | 1 | 约 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)。
#部署前先回答的六个问题
- 模型参数量和权重精度是什么?(决定权重字节数)
- 最大输入加最大输出 token 数是多少?(决定单序列 KV 上限与 prefill 长度)
- 目标并发、首 token 延迟和 token 吞吐是多少?(决定并发上限与批量策略)
- 单机八卡能否容纳权重和 KV Cache?(决定是否需要量化或多机)
- 需要 Tensor Parallel、Pipeline Parallel 还是 Data Parallel?(决定跨卡通信压力)
- 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 -mtopo -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)时按顺序排查:
- HCA 名称/端口:
ibdev2netdev、ibstat确认名称与NCCL_IB_HCA一致,端口 State 为 Active; - 容器内可见性:容器是否映射 /dev/infiniband;没有则 NVIDIA 库看不到 HCA;
- 裸链路带宽:用 perftest 对打,验证 IB 本身通且带宽正常:
# 节点 A 起 server
ib_write_bw -d mlx5_0
# 节点 B 起 client 对打(IP 按实际替换)
ib_write_bw -d mlx5_0 192.0.2.10- 节点间路由:用 ping/
ip route确认管理面与数据面互通; - 驱动与 OFED 匹配:
ibstat正常但 NCCL 仍退化时,检查驱动/OFED 版本是否被官方支持; - 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-serverXid 错误出现在内核日志(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 条还没有留言,来说点什么吧。