共计 3563 个字符,预计需要花费 9 分钟才能阅读完成。
很多开发者在本地玩大模型时,第一反应都是拉起 Ollama 或者 LM Studio。这种开箱即用的工具跑单人交互确实爽,但只要你尝试把它接入团队内部的知识库(RAG)、自动化 Agent 工作流,或者供多名同事同时调用,它就会原形毕露:排队阻塞严重、并发吞吐直接崩塌、没有原生的 Token 限流与密钥管理。
上周我帮团队搭建一套自用推理节点,硬件仅有一台配备单张 RTX 4090(24GB 显存)的工控机。需求很明确:必须扛住 20~30 个 Agent 节点的并发提取,跑 DeepSeek-R1 蒸馏出的 32B 级别模型,且完全兼容 OpenAI 接口规范。这篇文章记录了我放弃 Ollama,改用 vLLM + LiteLLM 组合拳打通高性能推理网关的完整工程实践与踩坑记录。
一、方案选型与显存精打细算
在单张 24GB 显存的卡上跑 32B 模型,FP16(需要 64GB+)和 INT8(需要 34GB+)是绝对塞不下的,唯一可行的路径是 4-bit 权重量化 。我们选用 DeepSeek-R1-Distill-Qwen-32B-AWQ,相比 GPTQ,AWQ 在低比特推理时的逻辑推理与数学能力保留得更扎实。
我们先算一笔死账:
- 模型权重占用 :32B 参数的 4-bit 量化权重约为 18.5 GB。
- CUDA Runtime 与系统保留 :固定占用 0.8 ~ 1.0 GB。
- 剩余可用显存 :24 GB – 19.5 GB ≈ 4.5 GB。
这剩下的 4.5 GB,就是决定并发承载能力的 KV Cache(键值缓存池)。vLLM 相比传统推理框架的核心杀手锏在于 PagedAttention,它把显存当操作系统虚拟内存一样分页管理,几乎消除了内存碎片。在 4.5 GB 的 KV Cache 下,若单请求上下文限制在 4096 tokens,跑满 8~12 个活跃并发是完全没有压力的。
二、vLLM 生产级启动配置与显存避坑
在生产环境里,我强烈建议使用 Docker 部署 vLLM,避免本地宿主机 CUDA/PyTorch 版本混乱。直接上我调优后验证稳定的启动配置:
docker run -d --name vllm-server \
--runtime nvidia --gpus all \
-v /data/models:/root/.cache/huggingface \
-p 8000:8000 \
--ipc=host \
--restart unless-stopped \
vllm/vllm-openai:v0.7.2 \
--model casperhansen/deepseek-r1-distill-qwen-32b-awq \
--quantization awq \
--dtype half \
--gpu-memory-utilization 0.94 \
--max-model-len 4096 \
--enforce-eager \
--disable-log-requests
核心参数踩坑解析:
--gpu-memory-utilization 0.94:默认是 0.90,拉高到 0.94 可以多榨出近 1GB 显存给 KV Cache。千万不要设成 0.98 以上,否则模型启动时构建 CUDA 图或者处理长 Prompt 会瞬间 OOM。--enforce-eager: 非常关键的踩坑点! 默认情况下 vLLM 会启用 CUDA Graph 捕获以提高推理效率,但这会瞬间预占 1~2GB 显存。在极限压榨 24G 显存跑 32B 模型的场景下,必须加--enforce-eager关掉它,用 3% 左右的极微吞吐损失换取绝对不爆显存。--max-model-len 4096:AWQ 默认最大上下文可能支持到 32k,但如果不显式限制,vLLM 会按照最大长度预估显存,直接抛出ValueError: No available memory for the cache blocks报错无法启动。
三、LiteLLM:构建生产级反向代理与流式网关
vLLM 自带的 API Server 足够快,但它太纯粹了:没有请求计费、缺少动态 API Key 管理、没有故障重试,更没有多模型路由策略。我们在前面挡一层 LiteLLM Proxy。
创建 config.yaml 配置文件:
model_list:
- model_name: deepseek-r1-32b
litellm_params:
model: openai/casperhansen/deepseek-r1-distill-qwen-32b-awq
api_base: http://vllm-server:8000/v1
api_key: "none"
stream_timeout: 60
max_retries: 2
# 备用降级方案:本地显卡真扛不住时,静默 fallback 到公网 API
- model_name: deepseek-r1-32b-fallback
litellm_params:
model: deepseek/deepseek-reasoner
api_key: "sk-your-deepseek-api-key"
router_settings:
routing_strategy: "latency-based-routing"
timeout: 30
general_settings:
master_key: "sk-nassky-super-admin-key"
store_model_in_db: false
通过 docker-compose.yml 把网关拉起:
version: '3.8'
services:
litellm:
image: ghcr.io/berriai/litellm:main-v1.59.8
container_name: litellm-proxy
ports:
- "4000:4000"
volumes:
- ./config.yaml:/app/config.yaml
command: ["--config", "/app/config.yaml", "--port", "4000"]
depends_on:
- vllm-server
restart: always
networks:
default:
name: ai-network
现在,你的所有业务端(如 Dify、FastGPT、Cursor 或自己的 Python 脚本)只需要将请求指向 http://<IP>:4000/v1,输入你设定的 Master Key,即可无缝享受高并发大模型网关能力。
四、真实压力测试与流式性能表现
搭建完毕后,我用轻量压测脚本(基于 Python aiohttp 模拟真实场景的长输入、长输出并发调用)进行了实测。测试输入为 1000 tokens 的文档,要求输出约 500 tokens 的结构化推理报告。
| 并发数 (Concurrency) | 首字延迟 (TTFT) | 生成吞吐 (Tokens/s) | 显存状态与异常率 |
|---|---|---|---|
| 1 并发 (单用户) | 380 ms | 34.2 tps | 显存占用稳定,0 报错 |
| 8 并发 (小团队负载) | 620 ms | 168.5 tps (系统级总和) | KV Cache 占用约 65%,流式顺滑 |
| 16 并发 (高负载) | 1.42 s | 210.1 tps (系统级总和) | 触发排队机制,无请求失败 |
| 32 并发 (极限过载) | 3.80 s | 218.0 tps | 部分请求挂起排队,系统未崩溃 |
实测数据表明,在 8~16 这一主流并发区间内,整个系统的吐字速度(单流平均维持在 15~20 tokens/s)完全满足肉眼流式阅读的节奏。最重要的是, 即便在 32 并发的极限下,vLLM 的连续批处理(Continuous Batching)机制只是让请求排队,显存纹丝不动,彻底告别了传统方案频繁出现的 CUDA Out of Memory 崩溃。
五、老鸟避坑指南
在这套环境稳定运行的前几天,我踩过了两个隐蔽的深坑,必须给大家提个醒:
- 客户端取消请求导致的“显卡幽灵计算”:
在很多前端流式交互中,用户经常会点击“停止生成”。如果 LiteLLM 和 vLLM 没有正确处理断开连接信号,后端仍会默默地把剩下的几百个 Token 算完,白白霸占宝贵的计算资源。务必确保 LiteLLM 升级到v1.50.0之后的版本,其默认开启了对下游断开的信号传递,能直接通知 vLLM 中止当前的 Sequence。 - 宿主机系统的 IPC 共享内存溢出 :
Docker 运行 vLLM 时如果不加--ipc=host,默认的容器共享内存只有 64MB。在处理高并发的多张张量数据通信时,PyTorch 的 DataLoader 或多进程通信会直接抛出Bus error (core dumped)猝死。这一条配置在生产部署时千万不能漏。
总结与结语
把大模型从“能跑”推进到“能用且抗造”,往往不需要昂贵的多卡 H100 集群。用好开源界最锋利的刀刃——vLLM 做算力极限制约下的推理加速,LiteLLM 做面向业务层的柔性防护 ,一张残血版消费级 4090 照样能打磨成承载几十人团队主力运转的私有 AI 引擎。如果你也在受制于本地推理的并发瓶颈,不妨抄下这套配置,今天就把它跑起来。
===EOF===