行业资讯
📅 2026/9/8 5:12:07
vLLM KV cache泄漏排查:nvidia-smi看不见的显存盲区与Kvcachescope实战
vLLM 是目前本地部署大语言模型最常用的推理框架之一很多团队在部署 Qwen、DeepSeek、Llama 系列模型时都会优先选它。但有一个非常容易踩、又非常难发现的坑KV cache 泄漏。更麻烦的是传统的显存观测工具根本看不到它。Kvcachescope 这个项目就是专门为解决这个问题出现的。先给你三个结论nvidia-smi 显示的是驱动视角下进程占用的总显存它看不到进程内部 KV cache 池的分配、释放和复用情况所以当 vLLM 出现 KV cache 泄漏时nvidia-smi 很可能是“盲”的。KV cache 泄漏的典型表现不是“某张显卡 OOM”而是服务吞吐逐步下降、延迟持续拉高重启后恢复过一段时间又劣化极其隐蔽。要定位这类问题需要深入 vLLM 进程内部拿指标。Kvcachescope 这类工具的价值就在这——它把 KV cache 的分配与释放情况暴露出来让你能判断当前瓶颈到底是正常的长上下文消耗还是真的在泄漏。这篇文章会从原理、使用场景、环境准备、部署流程、功能验证、接口调用、批量任务、资源占用、常见排错等角度完整展开尽量让你看完能自己跑一遍并且知道怎么验证结果。1. 核心能力速览先给一张总表快速判断这个项目值不值得看。能力项说明项目类型KV cache 可视化与泄漏检测工具来源Show HN 社区项目具体仓库、维护团队以官方页面为准核心功能观察 vLLM 进程内的 KV cache block 分配、释放、缓存命中率辅助定位显存泄漏解决问题nvidia-smi 只能看进程级显存占用无法区分 KV cache 池内部的分配状况显存需求观测组件本身占用很低被观测的 vLLM 实例按模型规模配置依赖环境Linux NVIDIA GPU需要 GPU 驱动、CUDA 环境和 vLLM 推理服务启动方式作为 vLLM 的辅助观测组件启动具体命令需按项目 README 配置接口能力通常以 Web 面板或 CLI 方式提供观测入口具体以项目版本为准批量任务可配合 vLLM 批量推理的压测场景做持续观测适合场景vLLM 长稳运行、批量推理集群、显存优化、缓存命中率调优不适合场景单次短时推理、临时测试小模型、无持续显存压力的场景这个项目本身不会替代 vLLM。它的位置是把 vLLM 推理引擎里“看不见的那部分显存”用可读的方式展示出来。2. 为什么 nvidia-smi 看不到 vLLM 的 KV cache 泄漏这是整个问题最核心的地方。vLLM 为了高性能并不是“用多少显存就临时申请多少”而是在加载模型后根据参数一次性预留一大块显存专门给 KV cache 使用。这块空间由 vLLM 内部的 PagedAttention 机制管理按 block 分配、释放和复用。从操作系统和 NVIDIA 驱动的角度看显存已经被 vLLM 进程吃掉了。nvidia-smi 汇报的进程显存占用是“这个进程总共拿了多少显存”而不是“这些显存当前被哪个模块用着、是否真的在用”。所以会出现这种情况nvidia-smi 看到显存占用一直很稳定没有涨到接近 OOM但 vLLM 服务越来越慢单请求延迟从 200ms 慢慢涨到 700ms并发稍微一高大量请求排队重启 vLLM 进程之后一切恢复几个小时后再次劣化。这种劣化很多并不是“显存总量不足”而是 KV cache 池内部出现了问题已经分配出去的 block 没有被正确回收到空闲池缓存无法命中导致每次请求都重新计算历史 token 的 KV cache。单位请求的显存消耗增加有效处理并发量下降服务变慢。你用 nvidia-smi 盯一整天的显存曲线可能都看不出来问题。如果只关注“显存占用率”一个指标你甚至会觉得服务非常健康。这就是标题里说的“blind”——不是 nvidia-smi 坏了而是它的观测粒度根本不在 KV cache 这一层。vLLM 在显存管理和缓存策略上做得很复杂这本身是设计取舍不是缺陷。但对使用者来说如果缺少进程内观测手段出了缓存泄漏和利用率下降问题就只能靠盲猜。Kvcachescope 想补上的正是这一层观测缺口。3. 适用场景与使用边界适合用这个工具的场景大体有三类。3.1 适合长期运行的 vLLM 服务部署完一个模型开了 24 小时在线 API这种服务最容易积累 KV cache 管理问题。短期重启看不出跑一天两天后性能逐步劣化。Kvcachescope 可以持续观察 KV cache 相关指标判断是否有缓慢泄漏。3.2 批量推理与高并发压测批量任务场景下请求类型多样有的 prompt 很长有的生成文本很长。KV cache 的分配伸缩很频繁一旦 block 回收逻辑触发不及时就会出现问题。批量任务跑得越久问题越明显。3.3 显存利用率和吞吐调优vLLM 的gpu_memory_utilization参数决定了 KV cache 最大可用空间但模型也不一定用满。KV cache 池的利用率需要实际观测。如果你只想把单卡吞吐压到极限这个项目的观测粒度是很有用的。3.4 使用边界与合规提醒KV cache 观测本身不涉及模型权重修改也不涉及用户输入内容的修改但仍然要注意几类问题如果观测面板需要对外访问要限制到内网或加鉴权避免推理服务接口和观测页面暴露在公网。被观测的模型可能是商用模型或有许可证要求的开源模型不要因为做性能观测就忽略模型本身的授权范围。推理服务里如果包含用户数据日志和指标不要记录 prompt 原文避免隐私风险。4. 环境准备与前置条件要跑通整个链路建议按以下清单准备环境。4.1 硬件与系统项目最低建议操作系统Linux 优先Ubuntu 22.04 是常见选择GPUNVIDIA GPU建议显存 8GB 以上驱动需要和 CUDA 版本匹配的 NVIDIA 驱动磁盘模型文件按规模准备7B 模型通常需要 15GB 以上空间内存建议 32GB 以上取决于模型大小Windows 环境也能跑 vLLM但兼容性问题较多。如果你在 Windows 上部署优先考虑 WSL2 或 Docker Desktop 方案不要指望原生 Windows 环境能和 Linux 环境完全一样。从热词来看也有“windows vllm modelscope”这类组合场景说明还是有不少人尝试在 Windows 上配合 ModelScope 做模型下载和 vLLM 部署这时候更建议用 Linux 容器统一环境减少模型路径、显存调用上的差异。4.2 软件依赖依赖作用PythonvLLM 和观测工具的运行环境CUDA Toolkit部分版本需要编译 vLLM 算子不同版本对 CUDA 版本有要求vLLM被观测的推理框架nvidia-ml-pyPython 环境下读取 nvidia-smi 信息4.3 模型准备用 vLLM 部署需要先准备 Hugging Face 格式的模型权重。以 Qwen 系列为例可以提前在 ModelScope 或 Hugging Face 下载权重也可以让 vLLM 启动时自动拉取。为了部署稳定更推荐先把权重下载到本地指定目录再通过--model参数指定路径。这样避免了启动时下载失败、磁盘占满等问题。4.4 检查端口占用vLLM 默认的 API 服务端口是 8000观测工具一般会占用一个独立端口。启动前检查一下sudo lsof -i :8000如果有残留进程占用先终止它避免服务启动失败。5. 安装部署与启动方式本节分两部分先启动 vLLM 推理服务再挂载 Kvcachescope 这类观测组件。vLLM 是整个链路的基础必须确保推理服务健康。5.1 vLLM 推理服务启动vLLM 有 Python API 和 Docker 两种常见启动方式。先看 Python 方式。# 创建虚拟环境建议独立 venv避免污染全局环境 python -m venv vllm-env source vllm-env/bin/activate # 安装 vLLM具体命令以官方文档为准 pip install vllm安装完成后启动 OpenAI 兼容的 API 服务python -m vllm.entrypoints.openai.api_server \ --model /data/models/Qwen/Qwen2.5-7B-Instruct \ --gpu-memory-utilization 0.9 \ --max-model-len 32768 \ --host 127.0.0.1 \ --port 8000说明一下参数--model模型路径或 Hugging Face 模型名。--gpu-memory-utilization允许 vLLM 使用的显存比例0.9 表示最多使用单卡显存的 90%。实际值需要根据你的显卡确定。--max-model-len模型最大上下文长度。设得越大KV cache 预留空间越多单请求能占用的显存越多。--host和--port服务监听地址和端口。如果走 Docker 路线命令模板如下docker run --rm --gpus all \ -p 8000:8000 \ -v /data/models:/data/models \ vllm/vllm-openai:latest \ --model /data/models/Qwen/Qwen2.5-7B-Instruct \ --gpu-memory-utilization 0.9 \ --max-model-len 32768这里-v把宿主机模型目录挂载进容器模型路径以容器内路径为准。5.2 安装并启动 KvcachescopeKvcachescope 是辅助观测工具具体的安装命令需要以项目 README 为准。从项目定位推测它的启动流程大概率是做三件事连接 vLLM 实例、读取 KV cache 状态、在某个端口展示结果。通用启动思路如下# 示例按实际项目 README 调整 git clone https://github.com/Kvcachescope/kvcachescope.git cd kvcachescope pip install -r requirements.txt # 连接到已经在 127.0.0.1:8000 启动的 vLLM 服务 python run.py --vllm-url http://127.0.0.1:8000 --port 8080如果你用的是发行版一键包一般会提供一个start.sh或者start.bat。我建议第一次不要直接双击而是先在终端里运行把输出日志看清楚。终端里如果出现类似 “Connected to vLLM” 的提示说明连接成功如果卡在连接超时优先检查 vLLM 服务是否真的在运行、URL 端口是否写对。5.3 启动后的验证启动完成后可以先做一个最小检查curl http://127.0.0.1:8000/health如果输出OK说明 vLLM 推理服务可用。然后打开浏览器访问 Kvcachescope 的观测页面确认页面上能读取到 vLLM 的基本信息。6. 功能测试与效果验证光能启动还不够关键是验证工具真的能看到“nvidia-smi 看不到的问题”。下面给出一套通用验证流程。6.1 基线记录先记录一张基准表用 nvidia-smi 记录当前 GPU 显存占用。用观测工具记录 KV cache 空闲 block 数量、已分配 block 数量、缓存命中率。记录一个固定请求的响应延迟。这些数据作为“健康状态”的标准。以后任何调优或排错都拿这组数据对比。nvidia-smi --query-gpuindex,memory.used,memory.total --formatcsv6.2 连续请求压力测试连续向 vLLM 发送同一组请求观察指标变化。这里推荐用 Python 脚本做持续压力请求import json import time import requests url http://127.0.0.1:8000/v1/chat/completions headers {Content-Type: application/json} payload { model: Qwen/Qwen2.5-7B-Instruct, messages: [ {role: user, content: 给我写一篇关于城市交通的五百字介绍。} ], max_tokens: 512, temperature: 0.7 } for i in range(50): start time.time() try: resp requests.post(url, headersheaders, jsonpayload, timeout180) cost time.time() - start print(frequest{i} status{resp.status_code} cost{cost:.2f}s) except Exception as exc: print(frequest{i} error{exc}) time.sleep(0.5)跑完这 50 个请求后分别看三个层面的现象第一层进程显存占用。如果 nvidia-smi 显示的占用没有明显变化说明显存总量层面没出问题。第二层KV cache 可用量。如果观测工具显示可用 block 数量持续下降并且没有回升迹象就说明有 block 被分配出去但没有正常释放。这是 KV cache 泄漏最直接的信号。第三层请求延迟。如果第 1 个请求耗时 300ms跑到第 40 个请求时耗时到了 800ms但显存总量、模型参数量都没有变化就要高度怀疑 KV cache 池复用出现了异常。6.3 变长上下文测试KV cache 泄漏与上下文长度关系很大。建议准备三组测试短 prompt短生成验证基础性能。长 prompt短生成观察预填充阶段对 KV cache 的占用。短 prompt长生成观察解码阶段 KV cache 的持续分配。三类请求混合发送更容易暴露缓存回收问题。6.4 判断成功与失败的标准判断观测工具有没有“真正起作用”可以看三个标准能展示 vLLM 进程内 KV cache block 级别的统计数据而不是 GPU 总显存曲线。在持续请求压力下指标有真实变化能看出分配、释放、命中率的波动。当 KV cache 到达上限时指标能反映出缓存淘汰或请求排队而不是等到 OOM 才暴露。如果观测页面只能显示 nvidia-smi 已有的信息那就没有解决原来的盲区问题。6.5 失败排查柯场景下最容易遇到的失败是“能打开观测页面但页面没有数据”。这时候依次检查vLLM 服务是否确实运行在指定端口观测工具和 vLLM 是否在同一台机器如果不在同一台网络策略是否放开vLLM 日志里是否有输出 KV cache 相关的统计信息供观测工具抓取观测工具和 vLLM 版本是否兼容。7. 接口 API 与批量任务vLLM 本身提供了 OpenAI 兼容接口这是最稳定的接入方式。下面的接口功能都基于 vLLM 的标准接口说明如果你的模型通过 vLLM 启动这些接口默认可用。7.1 基础调用示例curl http://127.0.0.1:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: Qwen/Qwen2.5-7B-Instruct, messages: [ {role: user, content: 用一句话介绍 KV cache 是什么} ], max_tokens: 128 }返回结果中会包含usage字段这里可以看到prompt_tokens和completion_tokens。这两个值是判断 KV cache 压力的基础数据。7.2 批量任务设计批量任务场景里不适合用“每个请求都建新连接、每次都不复用 KV cache”的方式。更好的模式是固定一个长上下文作为背景知识在这个上下文基础上多次追加问题观察这些请求是否命中了已缓存的 KV cache。如果命中了虽然prompt_tokens每次都很大但实际耗时应该很低。如果耗时一直很高说明缓存没有生效每次请求都在重复计算前文 KV cache。一个简单的批量脚本模板import json import time import requests from concurrent.futures import ThreadPoolExecutor url http://127.0.0.1:8000/v1/chat/completions headers {Content-Type: application/json} def send(prompt, idx): payload { model: Qwen/Qwen2.5-7B-Instruct, messages: [{role: user, content: prompt}], max_tokens: 256 } start time.time() resp requests.post(url, headersheaders, jsonpayload, timeout300) data resp.json() usage data.get(usage, {}) print(ftask{idx} cost{time.time()-start:.2f}s fprompt_tokens{usage.get(prompt_tokens)} fcompletion_tokens{usage.get(completion_tokens)}) return data prompts [f第 {i} 题请解释城市公共交通系统的优化方向。 for i in range(20)] with ThreadPoolExecutor(max_workers4) as executor: futures [executor.submit(send, prompts[i], i) for i in range(len(prompts))] for f in futures: f.result()批量任务的重点是长时间跑、观察趋势而不是只看单次结果。建议任务结束后对比观测工具里的 KV cache 状态和任务开始前是否一致。如果可用 block 大幅下降且长时间不恢复基本可以认定存在泄漏。7.3 失败重试与日志批量任务建议加三样东西每一条请求的耗时和状态码都记录到本地日志失败的请求单独重试不要混在正常任务里继续发任务结束后的快照式观测数据用来判断是否存在泄漏。日志文件建议至少包含时间戳、请求 ID、状态码、耗时、返回 token 数。这些数据在做性能对比时非常有用。8. 资源占用与性能观察8.1 显存占用怎么观察在 vLLM 场景下显存观察不能只看 nvidia-smi。要给出一套组合观察方式nvidia-smi 负责看“进程总显存”和“GPU 总显存”是否达到边界观测工具Kvcachescope 所属这类工具负责看 KV cache 池内部状态vLLM 日志负责看每次请求的 token 数和耗时。这里提供一个用 Python 读取 nvidia-smi 信息的简单脚本让显存数据和观测页面时间轴对上import time import pynvml pynvml.nvmlInit() handle pynvml.nvmlDeviceGetHandleByIndex(0) print(time_sec,gpu_mem_used_mib,gpu_mem_total_mib) for i in range(60): meminfo pynvml.nvmlDeviceGetMemoryInfo(handle) used_mib meminfo.used / 1024 / 1024 total_mib meminfo.total / 1024 / 1024 print(f{i * 5},{used_mib:.1f},{total_mib:.1f}, flushTrue) time.sleep(5)这个脚本需要先安装依赖pip install nvidia-ml-py把输出结果拿到 Excel 或任何表格工具里画一条曲线就能看出来 nvidia-smi 视角下显存占用是否一直平缓。如果平缓而服务变慢问题基本可以锁定在 KV cache 层。8.2 CPU 推理和 GPU 推理的差异vLLM 主要面向 GPU 推理。CPU 推理一般用 llama.cpp 或其他专用方案。如果你是在一台没有 NVIDIA GPU 的机器上做验证可以先跑 CPU 环境的小模型熟悉 vLLM 的 API 流程但显存和 KV cache 观测的意义会大打折扣。原因很简单CPU 环境下没有 GPU 显存池KV cache 分配在普通内存上nvidia-smi 完全派不上用场。8.3 关键参数对性能的影响几个最重要的参数建议按实际模型调整参数影响--gpu-memory-utilization决定 KV cache 池上限设太大会挤占模型和计算剩余量设太小缓存命中率上不去--max-model-len决定单请求最长可用 KV cache 空间越大单请求风险越高但长上下文适配更好--max-num-seqs决定并发序列数设置过大会增加 KV cache 池的压力--enable-prefix-caching开启前缀缓存优化对批量任务很有帮助但需要 vLLM 版本支持如果你不确定改哪个参数优先控制并发数再调gpu_memory_utilization。这两个参数对 KV cache 池的压力影响最直接。8.4 如何降低 KV cache 压力缩小max-model-len不让请求无限使用长上下文开启前缀缓存或自动前缀缓存机制减少重复计算控制并发请求数避免短时间内大量请求同时创建新的 KV cache block长任务拆成多个小任务减少单个序列对缓存池的长期占用。9. 常见问题与排查方法下面这张表覆盖 vLLM 部署 KV cache 观测过程中最常遇到的问题。问题现象可能原因排查方式解决方案观测页面打不开观测工具端口未启动或端口被占用查看工具启动日志确认监听端口换端口或重启观测服务观测页面打开但没有数据连不上 vLLM 实例或版本不兼容从观测工具配置项检查 vLLM URL核对 URL、检查网络策略、升级或降级 vLLMnvidia-smi has failed because it couldnt communicate with the nvidia driver显卡驱动异常、驱动与 CUDA 版本不匹配、容器没有挂载驱动先执行nvidia-smi看报错检查驱动状态查看内核模块日志重装匹配版本的驱动容器内要挂载/usr/bin/nvidia-smi和/proc/driver/nvidianvidia-smi 显示显存很高但服务性能反而一般显存被预留但 KV cache 利用率低模型并发吞吐受限看观测工具的 KV cache 利用率指标调低gpu_memory_utilization或增大max-num-seqs逻辑按实际观察结果调整服务刚启动快跑几小时后越来越慢疑似 KV cache 泄漏或缓存命中率下降对比启动初期和当前的 KV cache 可用 block 和请求延迟开启前缀缓存、控制并发、必要时重启服务恢复健康状态大批量任务跑到一半开始大量超时请求并发超过 KV cache 池或带宽上限也可能是磁盘或日志阻塞看观测工具的 block 分配曲线和 vLLM 日志降低并发数增加失败重试给批量任务加队列启动 vLLM 时显存不足 OOMgpu-memory-utilization设置过高或模型权重本身占用超过显卡显存查看显卡剩余显存确认模型权重大小调低gpu-memory-utilization换更大显存的卡或使用量化模型观测指标没有 KV cache 相关的值vLLM 版本不支持对应指标或没有开启相关启动参数打开 vLLM 的 metrics 端口检查指标名升级 vLLM更新观测工具或按项目要求的 vLLM 版本对齐缓存命中率很低每次请求都慢前缀缓存未开启或请求前缀不一致检查请求前缀是否相同查看缓存命中指标开启前缀缓存批量任务尽量复用相同前缀遇到nvidia-smi报错时第一反应不要觉得是工具坏了。这个报错在实际部署中出现频率很高核心问题是驱动和 CUDA 环境不匹配。先执行nvidia-smi确认裸机驱动没问题再进 Python 环境执行import torch; print(torch.cuda.is_available())确认 PyTorch 能看到 GPU最后再启动 vLLM。这样能把问题快速分层。10. 最佳实践与使用建议10.1 第一次先小参数测试不要一上来就开gpu_memory_utilization0.98加超大 max-model-len。第一次测试用一个小模型、限制并发、限制 max-tokens把所有流程跑通确认观测面板能正常看到数据再做压力测试。10.2 保留一套最小可运行配置把能稳定运行 vLLM 观测工具的命令写到脚本里包括 Python 虚拟环境、模型路径、端口、观测工具连接地址。遇到环境被破坏时可以快速恢复。10.3 模型和输出分目录管理建议用以下目录结构/data/ models/ # 模型权重 inputs/ # 测试输入 outputs/ # 推理输出 logs/ # vLLM 日志和观测数据这样模型目录、测试素材、运行日志互不干扰重装环境时只需要备份 logs 和 inputs。10.4 批量任务要加日志和失败重试批量任务不要裸跑至少加日志和重试。耗时数据要能和观测面板的 KV cache 数据对齐这样才能定位是哪一类请求触发了缓存压力。10.5 接口服务要限制访问范围vLLM 的服务端口如果暴露到公网很容易被刷流量。建议vLLM 服务只监听内网地址对外开放走 API 网关加鉴权观测工具的 Web 页面更敏感不要暴露到公网。10.6 涉及模型权重和数据合规使用模型前确认模型许可协议。如果推理服务涉及业务数据观测工具内部尽量不要记录用户输入原文只记录 token 数、耗时、命中率这类结构化指标。10.7 上线前做稳定性预演社区或公司内部如果要上线 vLLM 服务建议上线前用批量任务连续压测 3 到 6 个小时观察显存占用长时间是否稳定KV cache 可用量是否出现持续下降高延迟请求占比是否逐步上升。如果三项里有两项异常不要急着上线先调参数或换版本。11. 总结与下一步这个项目最值得尝试的点就是它补上了 vLLM 在 KV cache 层面的观测盲区。nvidia-smi 看显存总量Kvcachescope 看显存内部的 KV cache 状态。前者回答“显存够不够”后者回答“KV cache 池是不是真的在高效工作”。建议你拿到项目后先做以下三个动作第一部署一个小模型 vLLM 服务并确认 API 可用。这一步过不去后面所有观测都无从谈起。第二连接 Kvcachescope确保面板有数据。先不发压力请求就看空闲状态的 KV cache 分配情况。第三连续发一批长上下文请求观察 KV cache 可用量和请求延迟的变化。这是最快复现泄漏问题的路径。最容易踩的坑是版本不匹配vLLM 版本和观测工具版本相差太远或者观测指标名对不上。排错顺序永远是从底向上驱动、CUDA、vLLM、观测工具。后续可以扩展的方向也很多。你可以把这个观测思路接到 Prometheus Grafana 上做成持续监控面板也可以在多卡环境下对比不同模型并行策略下的 KV cache 利用率还可以结合批量任务做一个自动告警一旦 KV cache 可用量连续下降超过阈值就通知运维。如果你正在长期维护 vLLM 服务建议先把这个项目跑起来把 KV cache 的基线数据留好后面调参数、换显卡、升级 vLLM 都能有据可依。