实战踩坑:基于 vLLM + One-API 搭建自建与聚合的大模型高并发生产级网关

8次阅读
没有评论

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

最近给团队内部搭建一套统一的大模型 API 接入层,目标很明确:白天跑本地私有化部署的开源模型(Qwen2.5-14B/32B),用来处理敏感数据和内部 Agent 批量分析;夜间或流量打满时,自动平滑无缝降级到公网商业模型(如 Claude 3.5 Sonnet 或 DeepSeek API)。原本以为用常见的 FastAPI 简单包一层转发就行,结果一上并发测试,流式响应(SSE)卡死、显存碎片引发 OOM、连接数打满 504 Gateway Timeout 连番轰炸。折腾了两天,最终敲定了 vLLM + One-API 的架构方案,这里把完整的部署编排、关键配置和避坑记录整理出来。

一、架构选型与真实痛点拆解

很多同学在本地或私有服务器跑模型,第一反应是直接用 Ollama。Ollama 在单人本地开发测试确实很香,但在多用户、多 Agent 并发发起长文本请求的场景下,它的吞吐表现和显存调度机制很容易成为瓶颈。我们需要满足以下三个硬指标:

  • 高吞吐并发推理:必须支持 PagedAttention 和连续批处理(Continuous Batching),最大化榨干显卡算力。
  • 协议标准化与统一鉴权:对外必须输出标准的 OpenAI 兼容格式接口,且能精细化控制每个内部业务线 Token 额度。
  • 故障隔离与自动 Fallback:本地显存打满或服务挂掉时,客户端零感知自动切换到备用节点或公有云 API。

最终的落地拓扑图很简单:客户端 /Agent → One-API 网关(负责分流、鉴权、限流、Fallback)→ vLLM 推理集群(负责本地高性能推理)/ 商业模型供应商。

二、vLLM 本地高性能推理节点部署

我们采用双卡 RTX 4090(24G × 2)的机器作为承载节点,模型选用性价比极高的 Qwen/Qwen2.5-32B-Instruct-AWQ。AWQ 4-bit 量化后的显存占用大概在 20GB 左右,正好可以双卡张量并行(TP=2),给长上下文(KV Cache)留出充足的显存空间。

1. 启动容器与关键参数陷阱

直接上生产环境经过验证的 docker-compose.yml 片段:

version: '3.8'

services:
  vllm-qwen:
    image: vllm/vllm-openai:v0.6.3
    container_name: vllm-qwen-32b
    runtime: nvidia
    restart: always
    environment:
      - CUDA_VISIBLE_DEVICES=0,1
      - HUGGINGFACE_HUB_CACHE=/root/.cache/huggingface
    volumes:
      - /data/models/huggingface:/root/.cache/huggingface
    ports:
      - "8000:8000"
    ipc: host
    command:
      - "--model=Qwen/Qwen2.5-32B-Instruct-AWQ"
      - "--tensor-parallel-size=2"
      - "--quantization=awq"
      - "--max-model-len=8192"
      - "--gpu-memory-utilization=0.92"
      - "--enforce-eager"
      - "--trust-remote-code"
      - "--served-model-name=qwen2.5-32b"

2. 这里有三个极为关键的参数陷阱:

  • ipc: host 必须加:多卡张量并行(Tensor Parallelism)底层依赖 NCCL 进行显卡间通信。如果不共享宿主机的 IPC 内存,在高并发时极易直接触发 Watchdog caught collective operation timeout 错误。
  • --gpu-memory-utilization=0.92:默认是 0.9。如果你跑 8K 以上的长上下文,千万别直接拉到 0.98,PyTorch 的激活显存波动会直接导致 CUDA out of memory。留出 8% 显存作为安全缓冲是非常必要的。
  • --enforce-eager:在某些 Ampere / Ada 架构卡上,vLLM 默认开启 CUDA Graphs 会在捕获阶段预先占用显存。如果遇到莫名其妙的初始化显存爆掉,加上这个参数强制走 Eager 模式,能省下近 2G 的静态图显存,代价是首 Token 延迟有微秒级的极轻微增加,但并发容量显著提升。

三、接入 One-API 构建韧性网关

有了 vLLM 暴露的 8000 端口,千万不要直接暴露给应用层使用。我们使用 One-API(或轻量化的 New-API)作为门面网关。

1. One-API 容器化配置

使用 SQLite 在并发请求超过 30 QPS 时很容易锁库,生产必须直接上 PostgreSQL 或 MySQL:

  one-api:
    image: calciumion/new-api:latest
    container_name: api-gateway
    restart: always
    ports:
      - "3000:3000"
    environment:
      - SQL_DSN=postgres://api_user:secret_pass@postgres:5432/api_gateway?sslmode=disable
      - REDIS_CONN_STRING=redis://:redis_pass@redis:6379/0
      - MEMORY_CACHE_ENABLED=true
      - GLOBAL_WEB_RATE_LIMIT=1000
    depends_on:
      - postgres
      - redis

2. 渠道与路由策略实操配置

部署完成后进入 Web 控制台,在【渠道】模块做两组关键配置:

  • 本地高优先级渠道 :类型选 自定义渠道 (OpenAI 协议),Base URL 填 http://vllm-qwen:8000,模型填 qwen2.5-32b,重试次数设为 1,渠道优先级设为 10。
  • 云端降级备用渠道:类型选商业模型服务商(或 SiliconFlow / DeepSeek 官方),同样映射模型重定向别名为 qwen2.5-32b,渠道优先级设为 5。

这样设置后,网关在收到请求时,会优先派发到本地 vLLM 集群。一旦本地并发超载返回 503/429,或者显卡死锁断联,One-API 会在秒级内自动触发重试机制,将请求自动降级切到云端 API,上游调用的 Agent 不会出现任何中断异常。

四、反向代理与流式长连接踩坑排障

在 One-API 外层通常还会挂一层 Nginx 处理 HTTPS 和域名映射,这里是导致前端流式打印“吐字卡顿、憋大字”的重灾区。

默认配置下,Nginx 会对上游 HTTP 响应启用 Proxy Buffering,导致大模型生成的 SSE(Server-Sent Events)数据包被 Nginx 暂存,达到 4KB 或缓冲区满才一次性刷给客户端,体验极差。

server {
    listen 443 ssl http2;
    server_name api.nassky.top;

    # SSL 证书配置略...

    location /v1/chat/completions {
        proxy_pass http://127.0.0.1:3000;
        
        # 关掉缓冲区,保证 SSE 能够逐字流式推送
        proxy_buffering off;
        proxy_cache off;
        
        # 保持长连接
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        # 必须调大超时时间!长文本生成可能耗时数分钟
        proxy_connect_timeout 300s;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
        
        # 禁用分块传输编码的额外开销处理
        chunked_transfer_encoding on;
    }
}

五、压力测试与实测表现

我们使用 locust 对直接请求 vLLM 与经过 One-API 代理网关进行并发吞吐对比。输入 Prompt 长度约为 1200 Tokens,期望生成 512 Tokens:

指标项 裸跑 Ollama (单实例) vLLM (TP=2) One-API + vLLM 架构
并发用户数 (Users) 10 50 50
首字延迟 (TTFT) 1.42s 0.38s 0.41s
生成吞吐 (Tokens/s) 48.2 386.5 381.2
请求失败率 (Failure Rate) 14.2% (排队超时) 0.0% 0.0%

实测数据表明,在引入 One-API 网关后,首字延迟仅仅增加了不到 30 毫秒,吞吐量损耗在 1.5% 以内,但换来了完整的鉴权 Token 管理、日志审计以及至关重要的宕机自动 Fallback 能力。

总结与避坑心得

在本地大模型与生产业务深度绑定的过程中,不要迷信任何全家桶封装方案。vLLM 负责把硬件吞吐压榨到极限,One-API 负责把接口标准化与路由韧性做好,各司其职才是最稳妥的架构解法。

最后提炼三条排障铁律:

  1. 双卡以上部署 vLLM 一定要确认共享内存(shm-size 或 ipc: host),否则多卡同步必暴毙。
  2. Nginx 代理层必须显式声明 proxy_buffering off;,否则流式输出体验全毁。
  3. 给本地大模型配置合理的 Max Context Len,不要盲目拉满到模型理论上限(例如 128K),显存的 KV Cache 空间直接决定了你能支撑的并发请求数。
正文完
 0
评论(没有评论)