4、环境准备:安装vLLM、配置GPU环境、监控工具介绍
在开始定位显存泄漏之前,必须先搭建一个可观测、可复现的实验环境。显存泄漏的排查高度依赖版本一致性、GPU 运行状态的可视化以及进程级显存数据的采集能力。本节将围绕 vLLM 安装、GPU 环境配置和监控工具链三个部分展开,确保后续章节中的每一项诊断操作都有可靠的环境支撑。
4.1 安装 vLLM:版本锁定与依赖隔离
vLLM 对 CUDA、PyTorch 和 Python 版本有严格约束。推荐使用 Python 3.10+ 和 CUDA 12.1 作为基线,并通过虚拟环境隔离依赖。安装时务必锁定版本,避免因依赖升级引入隐性显存行为变化。
# 创建虚拟环境
python3.10 -m venv /opt/vllm-env
source /opt/vllm-env/bin/activate
# 安装 PyTorch(与 CUDA 12.1 匹配)
pip install torch==2.3.0 torchvision==0.18.0 --index-url https://download.pytorch.org/whl/cu121
# 安装 vLLM(锁定版本)
pip install vllm==0.5.4
# 验证安装
python -c "import vllm; print(vllm.__version__)"
4.2 配置 GPU 环境:显存可见性与进程隔离
GPU 环境配置的核心目标是让显存分配行为透明化。需要从三个层面进行配置:驱动层、运行时层、进程层。
- 驱动层:确认 NVIDIA 驱动版本 ≥ 535,使用
nvidia-smi验证 GPU 拓扑和显存总量。 - 运行时层:设置
CUDA_VISIBLE_DEVICES固定单卡,避免多卡间显存分配干扰。 - 进程层:使用
ulimit -v unlimited解除虚拟内存限制,防止 OOM Killer 误杀排查进程。
# 设置 GPU 可见性(单卡排查)
export CUDA_VISIBLE_DEVICES=0
# 启用 CUDA 内存分配日志(关键)
export CUDA_LAUNCH_BLOCKING=1
export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
# 查看 GPU 状态
nvidia-smi --query-gpu=index,name,memory.total,memory.used,memory.free --format=csv
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True 会改变显存分配策略,使 PyTorch 缓存块可扩展。该配置在排查碎片化泄漏时非常有效,但会略微增加显存占用,需在实验前固定该参数。
4.3 监控工具介绍:从全局到进程级
显存泄漏排查需要三层监控数据:全局 GPU 状态、进程级显存分配、PyTorch 缓存内部视图。以下工具组合覆盖全部三个层次。
| 工具 | 监控层次 | 核心能力 | 输出形式 |
|---|---|---|---|
| nvidia-smi | 全局 GPU | 显存总量、已用、进程列表 | CLI 表格 |
| nvitop | 全局 + 进程 | 实时刷新、按显存排序、历史曲线 | 交互式 TUI |
| py-spy | Python 进程 | 采样 Python 调用栈,定位分配点 | 火焰图 / 文本 |
| torch.cuda.memory | PyTorch 缓存 | 缓存块、分段、保留内存明细 | Python API |
| gpu-burn | 压力测试 | 验证 GPU 硬件稳定性 | CLI 日志 |
其中 torch.cuda.memory 是定位泄漏的核心工具,它能展示 PyTorch 缓存分配器的内部状态,区分缓存占用与实际张量占用。以下代码片段展示了如何采集关键指标:
import torch
import vllm
def snapshot_memory():
# 当前缓存分配器状态
allocator_stats = torch.cuda.memory_stats()
print(f"当前分配: {allocator_stats['current_bytes_allocated'] / 1024**2:.1f} MB")
print(f"缓存保留: {allocator_stats['current_bytes_reserved'] / 1024**2:.1f} MB")
print(f"活跃段数: {allocator_stats['num_active_segments']}")
# 显存快照(JSON 格式)
snapshot = torch.cuda.memory_snapshot()
print(f"快照段数: {len(snapshot)}")
# 打印每个段的关键信息
for seg in snapshot[:5]:
print(f" 段大小: {seg['size'] / 1024**2:.1f} MB, "
f"活跃: {seg['active_size'] / 1024**2:.1f} MB, "
f"分配器: {seg['segment_type']}")
# 在 vLLM 推理前后调用
snapshot_memory()
4.4 监控架构与数据流
为了直观理解各监控工具在排查流程中的位置,下图展示了从 GPU 硬件到 Python 进程的完整监控数据流。图中左侧为硬件层,中间为运行时层,右侧为应用层,箭头表示监控数据的采集方向。
上图展示了监控数据从 GPU 硬件到 Python 应用的分层流动路径。在实际排查中,nvidia-smi 负责确认显存总量是否持续增长,memory_snapshot 用于区分缓存保留与真实占用,py-spy 则帮助定位是哪个 Python 调用触发了显存分配。三层数据相互印证,才能准确判断泄漏发生在 vLLM 内部还是外部代码。
4.5 环境验证与基线采集
环境配置完成后,必须执行一次基线验证,记录正常状态下的显存指标,作为后续泄漏判断的参照。建议执行以下步骤:
- 启动一个最小 vLLM 推理任务(加载一个小模型,如
facebook/opt-125m)。 - 连续执行 50 次推理请求,每 10 次记录一次显存快照。
- 确认显存曲线在初始增长后趋于平稳,波动范围不超过 2%。
- 保存基线数据到 JSON 文件,便于后续对比。
# 基线采集脚本示例
import json
import time
import torch
from vllm import LLM, SamplingParams
llm = LLM(model="facebook/opt-125m", gpu_memory_utilization=0.7)
params = SamplingParams(temperature=0.8, max_tokens=64)
baseline = []
for i in range(50):
output = llm.generate("Hello, world!", params)
stats = torch.cuda.memory_stats()
baseline.append({
"step": i,
"allocated_mb": stats["current_bytes_allocated"] / 1024**2,
"reserved_mb": stats["current_bytes_reserved"] / 1024**2,
"active_segments": stats["num_active_segments"]
})
time.sleep(0.5)
with open("baseline_memory.json", "w") as f:
json.dump(baseline, f, indent=2)
print("基线数据已保存至 baseline_memory.json")
reserved_mb 在预热后应保持稳定,active_segments 数量不应持续增长。若连续 20 次请求后 reserved 值单调递增且无回落,则高度怀疑存在显存泄漏,需进入下一章的详细诊断流程。
4.6 常见环境配置误区
在环境准备阶段,以下误区会直接影响后续排查的准确性,需要特别注意:
- 误区一:使用最新版 vLLM。最新版可能引入未稳定的显存管理逻辑,建议锁定稳定版本(如 0.5.x),排查完成后再升级。
- 误区二:忽略 CUDA 缓存清空。PyTorch 的
torch.cuda.empty_cache()只释放缓存块,不改变分配器保留内存,不能作为泄漏判断依据。 - 误区三:多卡环境未隔离。多卡并行时显存分配可能跨设备,导致单卡监控数据失真,排查时必须固定单卡。
- 误区四:缺少基线数据。没有基线就无法判断显存增长是正常预热还是异常泄漏,务必在环境搭建后立即采集。