共计 3586 个字符,预计需要花费 9 分钟才能阅读完成。
最近团队内部需要把多套业务系统接入本地开源大模型,既要保证 OpenAI 兼容接口,又要在单张 24G 显存(RTX 4090)上扛住 30+ 并发的流式对话。如果直接用原生 Transformers 写个 FastAPI 跑推理,实测并发只要超过 4 个,首字延迟(TTFT)就会从 300ms 飙升到 6 秒以上,显存更是瞬间 OOM 崩掉。在踩平了几个深坑后,我最终敲定了 vLLM + LiteLLM 的分层架构:vLLM 负责底层的 KV Cache 分页管理与批处理吞吐,LiteLLM 充当轻量级 API Gateway 负责路由分发、降级容灾与并发排队。整套方案跑下来,吞吐量相比原生提升了将近 8 倍,现将整套部署实战与参数精调心得整理出来。
一、为什么选 vLLM + LiteLLM 架构组合?
在私有化部署场景下,开发者常犯的一个错误是“一揽子全包”——让推理引擎既负责模型前向计算,又负责鉴权、限流、日志重试和负载均衡。一旦并发冲高,很容易导致推理调度被 Python 的 I/O 阻塞。我们把职责彻底拆开:
- vLLM(算力层):依靠核心的
PagedAttention机制,显存碎片率从传统方式的 60%+ 骤降到 4% 以下。配合 Continuous Batching(动态连续批处理),新请求无需等待上一个完整序列结束即可动态插队运算。 - LiteLLM Proxy(网关层):提供与 OpenAI 完全一致的
/v1/chat/completions接口,具备极轻量的异步反向代理、密钥管理、自动 Fallback 以及精准的流式数据转发能力。业务端代码无需改动一行,改个base_url就能无缝切换。
二、Docker Compose 生产级底座编排
建议直接使用官方针对 CUDA 优化过的 Docker 镜像。以下是我验证过的 docker-compose.yml 生产级配置,针对 Qwen2.5-7B-Instruct 等主流尺寸模型做了参数固化:
version: '3.8'
services:
vllm-engine:
image: vllm/vllm-openai:latest
container_name: vllm-engine
runtime: nvidia
restart: unless-stopped
environment:
- CUDA_VISIBLE_DEVICES=0
- NCCL_IGNORE_DISABLED_P2P=1
volumes:
- /root/.cache/huggingface:/root/.cache/huggingface
- /data/models:/models
ports:
- "8000:8000"
ipc: host
command: >
--model /models/Qwen2.5-7B-Instruct
--served-model-name qwen-local
--trust-remote-code
--max-model-len 8192
--gpu-memory-utilization 0.90
--max-num-seqs 64
--enforce-eager
--dtype bfloat16
litellm-proxy:
image: ghcr.io/berriai/litellm:main-latest
container_name: litellm-proxy
restart: unless-stopped
ports:
- "4000:4000"
volumes:
- ./litellm-config.yaml:/app/config.yaml
command: ["--config", "/app/config.yaml", "--port", "4000"]
depends_on:
- vllm-engine
这里有两个我亲自踩坑得出的关键启动参数:
ipc: host:必须配置。PyTorch 的多进程共享内存(Shared Memory)在 Docker 默认的 64MB 限制下,高并发排队时极易触发死锁或直接 SIGBUS 崩溃退出。--gpu-memory-utilization 0.90:不要贪心设为 1.0。PyTorch 动态显存分配和 CUDA 运行上下文需要预留约 1~1.5GB 的缓冲空间,设为 0.90 是 24G 显卡的最稳分界线。
三、LiteLLM 路由与容灾降级配置
在同级目录下编写 litellm-config.yaml。在实际业务中,当本地显卡满载或者意外宕机时,接口必须有自动降级策略(Fallback 到云端商业 API),不能让前端直接报错:
model_list:
- model_name: prod-chat-model
litellm_params:
model: openai/qwen-local
api_base: http://vllm-engine:8000/v1
api_key: "EMPTY"
rpm: 120
timeout: 30
- model_name: prod-chat-model-fallback
litellm_params:
model: deepseek/deepseek-chat
api_key: "os.environ/DEEPSEEK_API_KEY"
router_settings:
routing_strategy: latency-based-routing
fallbacks:
- prod-chat-model: ["prod-chat-model-fallback"]
allowed_fails: 2
cooldown_time: 30
general_settings:
master_key: "sk-nassky-local-token-9988"
前端或者业务中台统一请求 http://<IP>:4000/v1,模型名称传 prod-chat-model。当本地 vLLM 响应时间过长或连续失败 2 次,LiteLLM 会毫秒级无缝将流量切到 DeepSeek API,待本地服务恢复冷却时间后自动切回,运维体验极度丝滑。
四、踩坑排障:客户端中途断连导致显存“幽灵泄露”
这是在前端使用 Server-Sent Events(SSE)消费流式输出时最隐蔽的坑:用户在生成未完成时突然点击了“停止生成”或关闭网页,底层推理还在死命往前算。
在原生封装的 FastAPI 代理中,HTTP 管道关闭后,后台协程如果没有绑定监听 request.is_disconnected(),PyTorch 依然会把整个 Token 序列生成完。并发稍高时,显存中就会充斥着这种“无人认领”的幽灵计算,严重阻塞新请求。
排查和解决办法:
- vLLM 原生已经支持 Client Disconnect 终止推理,但在前置反向代理(如 Nginx、Caddy 或自定义中转网关)时,必须确保 禁用响应缓冲(Buffering)。
- 如果中间走了一层 Nginx,必须在配置中追加:
proxy_buffering off; proxy_cache off; proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding on; - 这样前端一旦断开 TCP 连接,EOF 信号能第一时间无阻碍透传到 LiteLLM 和 vLLM,框架会立刻终止当前的
SequenceGroup,回收 KV Cache。
五、实测压测数据对比
在单卡 RTX 4090(24GB VRAM)+ AMD Ryzen 7950X 环境下,使用 Locust 对 Qwen2.5-7B-Instruct(输入 Prompt 长度约 512 tokens,生成约 256 tokens)进行阶梯压测,对比直接使用原生 HuggingFace+FastAPI 封装与本架构方案的表现:
| 指标项目 | 原生 HuggingFace + FastAPI | vLLM + LiteLLM Proxy |
|---|---|---|
| 并发吞吐量 (Tokens/s) | 约 38.2 tokens/s (并发 =8 时开始排队) | 286.4 tokens/s (提升近 7.5 倍) |
| 首字延迟 TTFT (并发 =16) | 4.82 秒 | 0.41 秒 |
| 显存碎片率 | ~35% (易触发 OOM) | < 5% (稳定受控) |
| 高并发下稳定性 | 并发 20 时连接频繁超时 | 并发 32 时平稳排队,未见 502/504 错误 |
总结与避坑心得
用消费级硬件榨取大模型生产力,核心是 千万不要让高成本的 GPU 计算为网络 I/O 买单。将网络分流、SSE 协议管理、鉴权容灾交由轻量级的 LiteLLM 网关接管,让 vLLM 专注于显存分配和矩阵计算,是目前个人极客和中小团队最兼顾成本与吞吐的最优解。
如果你的场景以长文本(16k+ 上下文)为主,还可以进一步开启 vLLM 的 Chunked Prefill 特性(增加参数 --enable-chunked-prefill),把超长 Prompt 分片计算,避免超长请求一次性霸占计算单元导致其他短请求首字延迟崩溃。按这套模板落地,你的单卡推理节点基本可以稳定支撑起一个几十人团队的日常高频调用了。
===END===