告别Ollama并发瓶颈:vLLM+LiteLLM搭建企业级大模型流式网关与显存压榨实录

2次阅读
没有评论

共计 3853 个字符,预计需要花费 10 分钟才能阅读完成。

很多朋友在自己的工作站或内网服务器上跑本地模型时,起手式几乎都是 Ollama run。不得不说,单人玩具阶段 Ollama 确实开箱即用,体验极佳。但上个月我们把内网代码辅助工具和两套 RAG 检索管线挂到一台双卡 RTX 4090 的节点后,痛点接踵而至:只要并发请求超过 4 个,首字延迟(TTFT)直接飙到 8 秒以上,偶尔还会出现请求排队超时被直接掐断的惨状。

Ollama 的底层封装抹平了过多推理参数细节,动态 Batching 和显存分配策略对于多并发、长上下文混合负载来说几乎是个黑盒。为了榨干硬件性能并给团队提供稳定低延迟的 OpenAI 标准接口,我花了三天时间把整套推理栈重构成 vLLM + LiteLLM。本篇文章记录实测数据、核心配置文件以及踩坑排障过程。

一、架构选型与核心痛点拆解

在生产级混合负载下,模型服务的核心指标不是单 Token 生成速度,而是 TTFT(Time To First Token) 和 高并发吞吐量。原生 Ollama 在这方面主要受限在以下三点:

  • PagedAttention 调度不透明: 遇到超长 Prompt(如长代码上下文)时,显存碎片化严重,显存利用率上不去。
  • 没有细粒度的 Continuous Batching 控制: 无法针对并发请求动态插入计算,新请求必须等前面的大请求推理完毕才吐出首字。
  • 网关治理能力薄弱: 缺乏 Token 级限流、鉴权、多模型路由与故障降级机制。

我们采用的落地架构非常清晰:底层由 vLLM 提供极致优化的推理引擎,上层由 LiteLLM 承担反向代理、路由、限流和鉴权,前置通过 Nginx 解决 HTTP/2 与流式传输(SSE)缓冲问题。

二、vLLM 底层部署:参数调优与显存压榨

测试硬件环境为单机双卡 RTX 4090 (24GB x 2 = 48GB VRAM),系统为 Ubuntu 22.04 LTS,CUDA 12.4。我们选用的基底模型是当前通用能力极强的 Qwen/Qwen2.5-14B-Instruct-AWQ 与 DeepSeek-R1-Distill-Qwen-14B。

1. 关键参数详解

启动 vLLM 时切忌直接无脑运行,几个核心参数直接决定了你的显存能否支撑多并发:

  • --gpu-memory-utilization:显存预分配比例。建议设为 0.90 到 0.92,留出 8%-10% 避免 CUDA Context 偶尔突发 OOM。
  • --max-model-len:最大上下文长度。根据实际业务硬截断(如设为 16384),盲目设成 32k/128k 会直接把 KV Cache 吃干抹净。
  • --max-num-seqs:最大并发序列数。这是控制 Continuous Batching 的关键阀门,4090 跑 14B 模型建议压在 32~64 之间。
  • --enforce-eager:开启即时执行模式(跳过 CUDA Graph 捕获)。显存捉襟见肘时开启它能省下几百 MB 静态显存,但吞吐量会有约 5% 的损耗;若显存充足,建议关掉保持默认。

2. 生产级 Docker Compose 编排

以下是我目前正在稳定运行的 docker-compose.yml 核心服务定义:

version: '3.8'

services:
  vllm-qwen14b:
    image: vllm/vllm-openai:v0.6.3.post1
    container_name: vllm-qwen14b
    runtime: nvidia
    environment:
      - CUDA_VISIBLE_DEVICES=0,1
      - HUGGING_FACE_HUB_TOKEN=your_hf_token_here
    ports:
      - "8000:8000"
    volumes:
      - /data/huggingface_cache:/root/.cache/huggingface
      - /dev/shm:/dev/shm
    ipc: host
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    command: >
      --model Qwen/Qwen2.5-14B-Instruct-AWQ
      --tensor-parallel-size 2
      --gpu-memory-utilization 0.90
      --max-model-len 16384
      --max-num-seqs 32
      --trust-remote-code
      --served-model-name qwen2.5-14b
      --disable-log-requests
    restart: unless-stopped

注意踩坑点:务必给容器挂载 /dev/shm:/dev/shm 或设置 ipc: host。在开启 Tensor Parallel(张量并行,如多卡跨卡切分)时,NCCL 节点间通信极度依赖共享内存,如果 shm 太小,服务会在收到第二个并发请求时直接报 RuntimeError: NCCL error in: ... broken pipe 崩溃。

三、LiteLLM 统一网关与安全路由

vLLM 暴露的只是裸推理接口。我们需要 LiteLLM 来做鉴权、多模型映射、速率限制以及与云端商业模型(如 Claude 3.5 Sonnet)做自动 Fallback。

1. 编写 LiteLLM 配置文件

创建 litellm_config.yaml:

model_list:
  - model_name: local-qwen
    litellm_params:
      model: openai/qwen2.5-14b
      api_base: http://vllm-qwen14b:8000/v1
      api_key: "none"
      tpm: 1000000
      rpm: 120
  - model_name: team-fallback-model
    litellm_params:
      model: deepseek/deepseek-chat
      api_key: os.environ/DEEPSEEK_API_KEY

litellm_settings:
  drop_params: true
  set_verbose: false
  request_timeout: 120

general_settings:
  master_key: sk-MasterKeyForAdminAccessOnly1234
  database_url: "sqlite:////data/litellm.db"

通过 LiteLLM,你可以给团队不同人员分发专属的 API Key,并单独限制 RPM(每分钟请求数)与可用额度,彻底告别单点被脚本小子打满导致全员卡死的窘境。

四、前端反向代理(Nginx)排障:流式断流的隐形杀手

把服务挂在 Nginx 后面做 HTTPS 接入时,我遇到了一个极其隐蔽的问题:客户端(如 NextChat、LobeChat 或 Cursor)接收流式输出时,根本不是打字机效果,而是卡住五六秒,然后一大坨字一次性喷出来。

这是因为 Nginx 默认开启了 proxy_buffering,它把后端 SSE (Server-Sent Events) 的每个 chunk 攒够了一定字节才向客户端推一次。必须在对应 location 块中强制关掉缓冲并开启长连接:

location /v1/ {
    proxy_pass http://127.0.0.1:4000; # LiteLLM 端口
    proxy_http_version 1.1;
    
    # 核心流式参数,必须彻底禁用缓冲
    proxy_set_header Connection "";
    proxy_buffering off;
    proxy_cache off;
    chunked_transfer_encoding on;

    # 避免复杂请求推理超长导致超时掐断
    proxy_read_timeout 600s;
    proxy_send_timeout 600s;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

五、实测压测对比:Ollama vs vLLM

我们用 Python 写了一个简单的并发压测脚本,模拟 16 个用户同时发起包含 2048 Token 输入、要求生成 512 Token 输出的场景。硬件环境均为双卡 4090,模型同为 Qwen2.5-14B-Instruct(AWQ 4-bit 量化)。

指标项目 Ollama (默认并行配置) vLLM (优化配置) 提升幅度
并发量 (Concurrent Users) 16 16 –
平均首字延迟 (TTFT) 6.82 s 1.14 s 快 83.2%
系统总吞吐 (Tokens/s) 142.3 tps 489.6 tps 提升 3.44 倍
高压请求成功率 87.5% (偶发排队超时) 100% 零丢包

从数据可以看出,一旦进入真实多用户并发环境,vLLM 的 PagedAttention 配合 Continuous Batching 优势呈压倒性态势。Ollama 的请求排队机制在并发稍微抬升时,TTFT 就会迅速劣化,而 vLLM 能够平滑地把新序列并入计算流水线。

总结与避坑心得

本地大模型从“个人折腾尝鲜”走向“团队可用设施”,分水岭就在于 显存管理和调度网关。如果你的应用场景只局限于本地终端单人问答,Ollama 依然是体验最顺滑的工具;但凡你的使用场景涉及到以下三项之一:

  1. 团队共享内网模型服务;
  2. 自动化 Agent 任务高频并发调用;
  3. 长文档检索(RAG)引发的显存动态分配波动。

果断切到 vLLM + LiteLLM 组合,把宝贵的 GPU 算力真正压榨出来。部署时牢记:算好

正文完
 0
评论(没有评论)