在 Linux 上部署生产级 vLLM:GPU 推理、TLS、限流与监控

vLLM 是开源推理引擎,它把 PagedAttention 从研究论文变成了生产环境中服务开放权重大模型的常用方案。它原生兼容 OpenAI API,可以在单张 GPU 上批量处理数千个请求,支持跨多 GPU 的张量并行,并提供 FP8、AWQ、GPTQ、INT4 等量化方式,而且通常能在新模型发布当天开始运行。

本教程从一台装有 NVIDIA GPU 的干净 Linux 主机开始,最终搭建一个经过加固、带监控、前置 TLS 的 vLLM 服务。多数安装指南止步于 pip install vllm 和一次 curl,那只能得到演示环境,不是服务。完整部署还需要 hardened systemd 单元、支持 SSE 流式输出和 API 密钥限流的 nginx、自动续期的 Let's Encrypt TLS、多租户密钥网关、带真实告警规则的 Prometheus/Grafana,以及有意义的性能调优与实测数据。

本文终端输出和基准测试在 2026 年 5 月使用 vLLM 0.20.1、Ubuntu 24.04 LTS、NVIDIA RTX 4090(24 GB 显存)、驱动 580.95.05 和 CUDA 13.0 采集,指南在 2026 年 11 月重新审核。

最终架构

完成后,服务器会通过 https://vllm.example.com/v1/chat/completions 提供 OpenAI 兼容接口,以 systemd 服务运行并在失败时重启;nginx 负责 TLS 和按密钥限流;Prometheus 指标只在私有端口暴露;Grafana 展示队列深度、KV 缓存压力和首 token 延迟,并配置告警。

客户端 ──HTTPS──► nginx(TLS、限流、认证)──HTTP──► vllm.service(systemd)──CUDA──► GPU
                                                           │
                                                           └──/metrics──► Prometheus ──► Grafana

单 GPU 主机已经可以为小团队或内部工具提供可信的 LLM 端点。需要横向扩展时,可以在 nginx 后增加副本;需要纵向扩展时,可以增加 GPU 并使用张量并行。

硬件与操作系统要求

vLLM 以 GPU 为主。计算能力必须为 7.0 或更高,覆盖 Volta(V100)之后的 NVIDIA 显卡。实际起点通常是 Ampere 消费级显卡,例如 RTX 30 系列。

粗略估算:FP16 每个参数约 2 字节,FP8/INT8 约 1 字节,INT4 约半字节,再为 KV 缓存增加 10% 到 30%;长上下文还要更多。70B FP16 模型仅权重就约 140 GB,无法放入单张消费级 GPU,通常要使用两张 H100 做张量并行,或降到 FP8/INT4。

Ubuntu 24.04 LTS 是主要支持版本,Ubuntu 22.04 LTS 也可用。RHEL 9、Rocky Linux 9、AlmaLinux 9 同样支持,主要区别是软件包管理器由 apt 改为 dnf,以及 NVIDIA 驱动来源不同。磁盘至少准备 200 GB:一个大模型的 Hugging Face 缓存就可能达到 50 GB,并应保留两到三个模型的空间。

安装 NVIDIA 驱动与 CUDA

vLLM 的 PyTorch wheel 会携带 CUDA 运行时,因此主机只需安装 NVIDIA 内核驱动,不必安装完整 CUDA Toolkit。vLLM 0.20.x 对 CUDA 12.4 wheel 的最低驱动为 550,对默认提供的 CUDA 12.8/12.9/13.0 wheel 为 570。驱动过旧时引擎仍可能加载,但首个请求会出现难以理解的 CUBLAS_STATUS_NOT_INITIALIZED。

Ubuntu 无头服务器建议安装驱动元包:

sudo apt update
sudo ubuntu-drivers devices
sudo apt install -y nvidia-driver-570-server
sudo reboot

ubuntu-drivers devices 会列出推荐版本。如果推荐版本比 570 新,就安装它。新装 Ubuntu 24.04 时,570-server 分支是稳定选择;Blackwell 如需更新驱动,可通过 ppa:graphics-drivers/ppa 获取 575 或 580。

Rocky Linux / AlmaLinux 9 先启用 NVIDIA CUDA 仓库,再安装开放内核模块。Hopper 和 Blackwell 必须使用开放模块,Ada 与 Ampere 也可以正常使用:

sudo dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/cuda-rhel9.repo
sudo dnf module install -y nvidia-driver:open-dkms
sudo reboot

重启后确认驱动和 GPU:

nvidia-smi

示例输出:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.95.05              Driver Version: 580.95.05      CUDA Version: 13.0     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 4090        On  |   00000000:C2:00.0 Off |                  Off |
| 60%   31C    P8             33W / 450W |       1MiB /  24564MiB |      0%      Default |
+-----------------------------------------+------------------------+----------------------+

持久模式可以缩短每次冷启动建立 CUDA 上下文的时间。启用方式:

sudo systemctl enable --now nvidia-persistenced

安装 vLLM

vLLM 0.20.x 推荐使用 uv。它会通过 --torch-backend=auto 自动选择与 CUDA 匹配的 PyTorch wheel,安装速度也明显快于 pip。以下流程创建独立系统用户,并把整套环境放在 /opt/vllm,避免污染系统 Python:

sudo useradd -r -m -d /opt/vllm -s /bin/false vllm
sudo apt install -y python3-venv curl ca-certificates
curl -LsSf https://astral.sh/uv/install.sh | sudo -u vllm sh
sudo -u vllm /opt/vllm/.local/bin/uv venv /opt/vllm/.venv --python 3.12 --seed
sudo -u vllm /opt/vllm/.local/bin/uv pip install \
  --python /opt/vllm/.venv/bin/python vllm --torch-backend=auto

安装内容包括匹配 CUDA 的 PyTorch、FlashAttention、xFormers、vLLM kernels 和 OpenAI 兼容服务器。输出末尾示例:

 + torch==2.11.0+cu130
 + torchaudio==2.11.0+cu130
 + torchvision==0.26.0+cu130
 + tokenizers==0.22.2
 + transformers==5.8.0
 + triton==3.6.0
 + vllm==0.20.1
 + xgrammar==0.2.0
real    1m37.994s

确认版本:

/opt/vllm/.venv/bin/vllm --version

应返回:

0.20.1

如果使用 Docker,项目提供 vllm/vllm-openai 镜像,例如 v0.20.1-cu130-ubuntu2404。应固定版本标签,不要使用 latest。需要内核隔离或同机多套 LLM 栈时可选 Docker;单机专用服务器且希望攻击面最小时,裸机安装更合适。

精简系统镜像可能缺少 Triton JIT 所需的 Python 头文件,错误通常是在 /tmp/tmpXXXX/cuda_utils.c 中出现 Python.h: No such file or directory。安装以下软件包即可:

sudo apt install -y python3-dev build-essential

首次运行 vllm serve

先手动启动一次,确认模型加载、API 绑定和 GPU 使用正常。本教程使用 Qwen/Qwen2.5-7B-Instruct,它是开放权重模型,不需要 Hugging Face token,FP16 在 24 GB 显卡上运行良好,并且兼容 vLLM 的工具调用解析器。

预下载权重,避免网络错误与 CUDA 初始化日志混在一起:

sudo -u vllm /opt/vllm/.venv/bin/pip install --quiet huggingface_hub
sudo -u vllm /opt/vllm/.venv/bin/huggingface-cli download Qwen/Qwen2.5-7B-Instruct

受限模型可在命令前加 HF_TOKEN=hf_xxx,或由 vllm 用户执行一次 huggingface-cli login。模型下载到本地后,vLLM 不再需要该 token。

手动测试只绑定到 127.0.0.1:

sudo -u vllm /opt/vllm/.venv/bin/vllm serve Qwen/Qwen2.5-7B-Instruct \
  --host 127.0.0.1 --port 8000 \
  --api-key sk-cfg-demo-key \
  --gpu-memory-utilization 0.90 \
  --max-model-len 16384 \
  --max-num-seqs 128 \
  --served-model-name qwen2.5-7b

快速磁盘首次启动约 30 到 90 秒。vLLM 会加载权重、分配 KV 缓存块、从 batch size 1 到 --max-num-seqs 捕获 CUDA graphs,最后启动 API。关键日志如下:

INFO [core.py:109] Initializing a V1 LLM engine (v0.20.1) with config: model='Qwen/Qwen2.5-7B-Instruct'
INFO [cuda.py:368] Using FLASH_ATTN attention backend out of potential backends: ['FLASH_ATTN', 'FLASHINFER', 'TRITON_ATTN', 'FLEX_ATTENTION'].
INFO [flash_attn.py:646] Using FlashAttention version 2
INFO [gpu_model_runner.py:4879] Model loading took 14.25 GiB memory and 3.654477 seconds
INFO [gpu_worker.py:440] Available KV cache memory: 5.41 GiB
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000

14.25 GiB 是 bfloat16 权重占用的显存,5.41 GiB 是扣除激活和开销后可用的 KV 缓存。把 --gpu-memory-utilization 提高到 0.95 可以再回收一到两 GiB,但长期运行和内存碎片可能触发 OOM。

vLLM 启动日志:FlashAttention 2、模型加载耗时与可用 KV 缓存

查看模型列表:

curl -s http://127.0.0.1:8000/v1/models \
  -H "Authorization: Bearer sk-cfg-demo-key" | python3 -m json.tool

响应中的模型别名与 --served-model-name 一致:

{
  "object": "list",
  "data": [
    {
      "id": "qwen2.5-7b",
      "object": "model",
      "created": 1778106064,
      "owned_by": "vllm",
      "root": "Qwen/Qwen2.5-7B-Instruct",
      "max_model_len": 16384,
      "permission": [
        {
          "allow_sampling": true,
          "allow_logprobs": true,
          "allow_view": true,
          "allow_fine_tuning": false
        }
      ]
    }
  ]
}

发送流式聊天请求,同时验证 SSE 行为:

curl -s -N http://127.0.0.1:8000/v1/chat/completions \
  -H "Authorization: Bearer sk-cfg-demo-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen2.5-7b",
    "stream": true,
    "messages": [{"role": "user", "content": "In one sentence, what is PagedAttention?"}]
  }' | head -20

响应按 data: 分块逐 token 到达:

data: {"id":"chatcmpl-84311d23","object":"chat.completion.chunk","model":"qwen2.5-7b","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
data: {"id":"chatcmpl-84311d23","object":"chat.completion.chunk","model":"qwen2.5-7b","choices":[{"index":0,"delta":{"content":"P"},"finish_reason":null}]}
data: {"id":"chatcmpl-84311d23","object":"chat.completion.chunk","model":"qwen2.5-7b","choices":[{"index":0,"delta":{"content":"aged"},"finish_reason":null}]}
data: {"id":"chatcmpl-84311d23","object":"chat.completion.chunk","model":"qwen2.5-7b","choices":[{"index":0,"delta":{"content":"Attention"},"finish_reason":null}]}
data: {"id":"chatcmpl-84311d23","object":"chat.completion.chunk","model":"qwen2.5-7b","choices":[{"index":0,"delta":{"content":" is"},"finish_reason":null}]}
data: {"id":"chatcmpl-84311d23","object":"chat.completion.chunk","model":"qwen2.5-7b","choices":[{"index":0,"delta":{"content":" an"},"finish_reason":null}]}
data: {"id":"chatcmpl-84311d23","object":"chat.completion.chunk","model":"qwen2.5-7b","choices":[{"index":0,"delta":{"content":" efficient"},"finish_reason":null}]}
...
data: [DONE]

成功后按 Ctrl+C 停止,下一步改为 systemd 服务。

通过 curl 观察 vLLM 流式 Chat Completions 返回

另开终端运行 nvidia-smi,可以看到 VLLM::EngineCore 工作进程。测试中功耗约 78 W、显存使用约 21.4 GiB。

RTX 4090 上 vLLM 推理期间 nvidia-smi 的显存与进程状态
若显存长期接近 24564 MiB,应把 --gpu-memory-utilization 降到 0.85 留出余量;触顶会导致请求抢占并产生延迟尖峰。

用加固的 systemd 服务运行 vLLM

在 tmux 中运行 vllm serve 只适合测试。生产部署需要失败重启、降低权限、隔离文件系统,并从权限严格的文件读取密钥,避免 API key 出现在 ps。

创建环境文件:

sudo install -d -m 0755 -o root -g root /etc/vllm
sudo install -d -m 0755 -o vllm -g vllm /var/log/vllm
sudo tee /etc/vllm/env >/dev/null <<'EOF'
HF_HOME=/opt/vllm/hf-cache
HF_HUB_OFFLINE=1
VLLM_API_KEY=sk-cfg-please-rotate-me
EOF
sudo chown vllm:vllm /etc/vllm/env
sudo chmod 600 /etc/vllm/env

模型预下载后设置 HF_HUB_OFFLINE=1,可以防止每次启动访问 Hugging Face,也能避免上游故障影响启动。

创建 /etc/systemd/system/vllm.service:

[Unit]
Description=vLLM OpenAI-compatible inference server
After=network-online.target nvidia-persistenced.service
Wants=network-online.target

[Service]
Type=simple
User=vllm
Group=vllm
EnvironmentFile=/etc/vllm/env
ExecStart=/opt/vllm/.venv/bin/vllm serve Qwen/Qwen2.5-7B-Instruct \
  --host 127.0.0.1 --port 8000 \
  --api-key ${VLLM_API_KEY} \
  --served-model-name qwen2.5-7b \
  --gpu-memory-utilization 0.90 \
  --max-model-len 16384 \
  --max-num-seqs 128 \
  --enable-prefix-caching

Restart=on-failure
RestartSec=10
TimeoutStartSec=600
TimeoutStopSec=60
LimitNOFILE=1048576

NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/vllm /var/log/vllm
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true

StandardOutput=journal
StandardError=journal
SyslogIdentifier=vllm

[Install]
WantedBy=multi-user.target

TimeoutStartSec=600 给大模型加载和 CUDA 图捕获留出最多十分钟。LimitNOFILE=1048576 提高并发流数量上限。严格文件系统隔离下,ReadWritePaths 是唯一可写区域。

启用服务:

sudo systemctl daemon-reload
sudo systemctl enable --now vllm
sudo systemctl status vllm --no-pager

查看日志:

sudo journalctl -u vllm -f

journald 会自动轮转。如需更长历史,可在 /etc/systemd/journald.conf 中设置 SystemMaxUse=2G 后重新加载。

使用 nginx 反向代理、TLS 与 SSE

安装 nginx 和 certbot:

sudo apt install -y nginx certbot python3-certbot-nginx
sudo systemctl enable --now nginx

Rocky Linux 9 从 EPEL 安装对应的 nginx、certbot、python3-certbot-nginx 包。设置域名和邮箱变量:

DOMAIN=vllm.example.com
EMAIL=admin@example.com

为该域名配置指向公网 IP 的 A 记录。certbot 的 HTTP-01 挑战需要公网能访问 80 端口;NAT 后或需要泛域名证书时,应改用 DNS-01 和相应的 DNS 插件。

开放防火墙:

sudo ufw allow 80,443/tcp
sudo ufw status

创建 /etc/nginx/sites-available/vllm:

limit_req_zone $http_authorization zone=per_key:10m rate=120r/m;

upstream vllm_backend {
    server 127.0.0.1:8000;
    keepalive 64;
}

server {
    listen 80;
    listen [::]:80;
    server_name vllm.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name vllm.example.com;

    # certbot fills these in:
    # ssl_certificate /etc/letsencrypt/live/vllm.example.com/fullchain.pem;
    # ssl_certificate_key /etc/letsencrypt/live/vllm.example.com/privkey.pem;

    client_max_body_size 16M;
    keepalive_timeout 75s;

    location / {
        limit_req zone=per_key burst=40 nodelay;
        limit_req_status 429;

        proxy_pass http://vllm_backend;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";

        # Critical for SSE streaming
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
        chunked_transfer_encoding on;
    }
}

限流区域以 Authorization 头为键,每个 API key 每分钟 120 次、允许突发 40 次;匿名流量共用空键桶。proxy_buffering off 是保证流式响应的关键,否则 nginx 会缓存整个 SSE 响应,最终一次性返回。

启用配置并检查:

sudo ln -s /etc/nginx/sites-available/vllm /etc/nginx/sites-enabled/vllm
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

申请证书:

sudo certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos --redirect -m "$EMAIL"

测试自动续期:

sudo certbot renew --dry-run

验证公网流式请求:

curl -s -N "https://$DOMAIN/v1/chat/completions" \
  -H "Authorization: Bearer sk-cfg-please-rotate-me" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen2.5-7b",
    "stream": true,
    "messages": [{"role": "user", "content": "List three production benefits of PagedAttention."}]
  }'

data: 行应逐条出现。如果全部在末尾同时到达,检查当前启用的站点文件以及 /etc/nginx/conf.d/ 是否覆盖了 proxy_buffering。

使用 LiteLLM 提供多租户 API key

vLLM 的 --api-key 只接受一个共享密钥。第二个团队或应用需要访问时,就需要按密钥统计、预算和无需重启的轮换。LiteLLM 可以作为 vLLM 前的轻量代理,对外提供 OpenAI API,并补充多租户功能。

独立安装 LiteLLM:

sudo useradd -r -m -d /opt/litellm -s /bin/false litellm
sudo -u litellm /opt/vllm/.local/bin/uv venv /opt/litellm/.venv --python 3.12 --seed
sudo -u litellm /opt/vllm/.local/bin/uv pip install \
  --python /opt/litellm/.venv/bin/python "litellm[proxy]"

创建 /etc/litellm/config.yaml:

model_list:
  - model_name: qwen2.5-7b
    litellm_params:
      model: openai/qwen2.5-7b
      api_base: http://127.0.0.1:8000/v1
      api_key: sk-cfg-please-rotate-me

general_settings:
  master_key: sk-litellm-master-rotate-me
  database_url: sqlite:////opt/litellm/litellm.db
  alerting: ["slack"]

litellm_settings:
  drop_params: true
  set_verbose: false
  json_logs: true
  request_timeout: 600

systemd 服务监听 4000 端口,加固方式与 vLLM 服务相同,只需把 ExecStart 改成:

ExecStart=/opt/litellm/.venv/bin/litellm --config /etc/litellm/config.yaml --port 4000 --host 127.0.0.1

将 nginx 的 upstream 指向 127.0.0.1:4000,然后生成团队密钥:

curl -s http://127.0.0.1:4000/key/generate \
  -H "Authorization: Bearer sk-litellm-master-rotate-me" \
  -H "Content-Type: application/json" \
  -d '{
    "models": ["qwen2.5-7b"],
    "max_budget": 10,
    "budget_duration": "30d",
    "metadata": {"team": "platform"}
  }' | python3 -m json.tool

LiteLLM 会记录每个密钥的请求数、token 数和等价成本,并在达到预算 80% 时触发 Slack 告警。

Prometheus、Grafana 与实用告警

vLLM 在 /metrics 暴露请求队列、迭代吞吐、KV 缓存使用率等指标。重点指标:

指标 含义
vllm:num_requests_running 当前并发序列,达到 --max-num-seqs 后饱和
vllm:num_requests_waiting 队列深度,持续大于 0 表示 CPU 或 GPU 已受限
vllm:kv_cache_usage_perc KV 缓存填充率,超过 95% 会开始抢占请求
vllm:time_to_first_token_seconds 首 token 延迟直方图,p95 最接近用户感受
vllm:time_per_output_token_seconds token 间延迟,乘以输出 token 数可估算生成时间
vllm:prompt_tokens_total 提示词 token 累计计数
vllm:generation_tokens_total 输出 token 累计计数
vllm:prefix_cache_hits_total 前缀缓存命中数,命中越高通常 TTFT 越低

指标与 API 共用端口和密钥,不应直接暴露公网。查询示例:

curl -s http://127.0.0.1:8000/metrics \
  -H "Authorization: Bearer $VLLM_API_KEY" | grep '^vllm:' | head

输出示例:

vllm:num_requests_running{engine="0",model_name="qwen2.5-7b"} 0.0
vllm:num_requests_waiting{engine="0",model_name="qwen2.5-7b"} 0.0
vllm:kv_cache_usage_perc{engine="0",model_name="qwen2.5-7b"} 0.0
vllm:prefix_cache_queries_total{engine="0",model_name="qwen2.5-7b"} 39.0
vllm:prefix_cache_hits_total{engine="0",model_name="qwen2.5-7b"} 0.0
vllm:engine_sleep_state{engine="0",model_name="qwen2.5-7b",sleep_state="awake"} 1.0

vLLM <code>/metrics</code> 中的运行请求、KV 缓存和首 token 延迟指标

使用 Docker Compose 运行 Prometheus 和 Grafana:

services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    network_mode: host
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - ./alerts.yml:/etc/prometheus/alerts.yml:ro
      - prom-data:/prometheus
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.retention.time=30d
      - --web.listen-address=127.0.0.1:9090
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    network_mode: host
    environment:
      - GF_SERVER_HTTP_ADDR=127.0.0.1
      - GF_SERVER_HTTP_PORT=3000
      - GF_SECURITY_ADMIN_PASSWORD__FILE=/run/secrets/grafana_admin
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana_admin:/run/secrets/grafana_admin:ro
    restart: unless-stopped

volumes:
  prom-data:
  grafana-data:

Prometheus 配置:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - alerts.yml

scrape_configs:
  - job_name: vllm
    metrics_path: /metrics
    authorization:
      type: Bearer
      credentials: sk-cfg-please-rotate-me
    static_configs:
      - targets: ['127.0.0.1:8000']
        labels:
          service: vllm
          gpu: rtx-4090

  - job_name: node
    static_configs:
      - targets: ['127.0.0.1:9100']

告警规则:

groups:
  - name: vllm
    interval: 30s
    rules:
      - alert: VllmQueueBacklog
        expr: vllm:num_requests_waiting > 50
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "vLLM queue backlog over 50 for 5 minutes on {{ $labels.gpu }}"
          description: "Capacity is the bottleneck. Add a replica or raise --max-num-seqs."

      - alert: VllmKvCachePressure
        expr: vllm:kv_cache_usage_perc > 0.95
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "KV cache > 95% on {{ $labels.gpu }}"
          description: "Concurrent sequences are about to be preempted. Reduce --max-model-len or scale out."

      - alert: VllmTtftHigh
        expr: histogram_quantile(0.95, sum(rate(vllm:time_to_first_token_seconds_bucket[5m])) by (le, gpu)) > 2
        for: 10m
        labels:
          severity: critical
        annotations:
          summary: "p95 TTFT > 2s on {{ $labels.gpu }}"
          description: "User-visible latency degraded. Check GPU saturation, queue depth, and recent traffic spike."

启动监控栈:

cd /opt/observability
echo "$(openssl rand -hex 24)" | sudo tee grafana_admin >/dev/null
sudo chmod 600 grafana_admin
sudo docker compose up -d
sudo docker compose ps

在 Grafana 中添加 http://127.0.0.1:9090 数据源,再导入官方 vLLM 仪表盘 examples/observability/prometheus_grafana。

真正有效的性能调优

vLLM 有约四十个命令行参数,生产环境通常只需少数几个:

参数 默认值 作用
--gpu-memory-utilization 0.90 vLLM 启动时占用显存比例,OOM 时降低,想增加 KV 缓存可提到 0.95
--max-model-len 模型最大值 上下文窗口,降低可释放更多 KV 块给并发
--max-num-seqs 256 最大并发序列,提高吞吐但可能恶化 p99 TTFT
--max-num-batched-tokens 自动 每轮调度 token 预算,提高吞吐或降低首包延迟
--enable-prefix-caching 开 复用相同前缀的 KV 缓存
--enable-chunked-prefill 开 交错预填充和解码,让高负载下 TTFT 更稳定
--kv-cache-dtype 自动 设为 fp8 可把 KV 缓存内存减半
--quantization 无 单 GPU 运行 70B 可用 AWQ 或 FP8
--dtype 自动 Ampere 及以上建议 bfloat16
--tensor-parallel-size 1 将模型切分到同机多张 GPU
--enforce-eager 关 关闭 CUDA 图捕获,只用于调试,会损失 10% 到 30% 吞吐

吞吐与延迟通过批调度互相制约:--max-num-batched-tokens 提高吞吐但会让新请求等待更久;--max-num-seqs 提高并发但每请求分到的 GPU 更少。调优顺序应是先保证 KV 缓存空间,再调并发,最后调批 token 数以优化 TTFT。

单张 RTX 4090 的基准结果

测试使用 vllm bench serve 回放 ShareGPT 风格提示词:

/opt/vllm/.venv/bin/vllm bench serve \
  --backend openai-chat \
  --base-url http://127.0.0.1:8000 \
  --endpoint /v1/chat/completions \
  --model qwen2.5-7b \
  --tokenizer Qwen/Qwen2.5-7B-Instruct \
  --dataset-name sharegpt \
  --dataset-path /tmp/sharegpt.json \
  --num-prompts 128 \
  --max-concurrency 32 \
  --request-rate 20 \
  --api-key sk-cfg-please-rotate-me

并发 128 时,总吞吐超过 5,000 token/s,中位 TTFT 为 138 ms,中位 token 间延迟为 22 ms。

RTX 4090 上 vLLM bench serve 的总吞吐、TTFT 和 ITL

并发从 1 增加到 128 时,总吞吐从每秒 178 token 提升到超过 5,000 token,约为 30 倍;单请求吞吐则从每秒 61 token 降到 17 token。RTX 4090 在该模型和上下文下大约在 32 到 128 并发之间饱和,继续提高并发只会进入队列。

一次 c8 测试的 p99 TTFT 出现 5.3 秒尖峰,是长生成收尾时到达的单个异常请求;中位 TTFT 52 ms、p99 ITL 27 ms 更能代表稳定状态。只有单用户且无法批处理时,vLLM 无法充分利用 GPU,可考虑较小显卡上的 llama.cpp 或多个模型副本。

多 GPU 张量并行

70B FP16 模型权重约 140 GB。vLLM 可通过 --tensor-parallel-size N 把模型拆分到同机多张 GPU,只要求模型 hidden size 能被 N 整除,常见架构在 N 不超过 8 时都满足。

/opt/vllm/.venv/bin/vllm serve meta-llama/Llama-3.1-70B-Instruct \
  --tensor-parallel-size 2 \
  --gpu-memory-utilization 0.92 \
  --max-model-len 8192 \
  --quantization fp8

两张 H100 80GB 可舒适运行 70B FP8;两张 L40S 48GB 需要 INT4 或 AWQ。跨主机部署时,流水线并行通过 --pipeline-parallel-size 按节点切分,张量并行负责节点内切分。通常约定 tensor-parallel=每节点 GPU 数、pipeline-parallel=节点数。多主机还需要 Ray 和高速网络。

常见故障

现象 可能原因 处理方式
首次请求出现 Python.h: No such file or directory Triton JIT 缺少 Python 头文件 apt install python3-dev build-essential
启动时 CUDA out of memory KV 缓存超出可用显存 降低 --gpu-memory-utilization、减小 --max-model-len 或设置 --kv-cache-dtype fp8
CUDA error: no kernel image is available wheel 不包含当前 GPU 计算能力的 kernel 使用匹配的镜像,或带 TORCH_CUDA_ARCH_LIST 从源码构建
huggingface-cli 下载返回 HTTP 401 受限模型缺少 token 以 vllm 用户登录,或在 /etc/vllm/env 设置 HF_TOKEN
流式响应一次性返回 代理缓冲开启 nginx 配置 proxy_buffering off,检查 /etc/nginx/conf.d/
多 GPU 启动时 NCCL 卡住 NCCL 版本不一致、网卡绑定或 P2P 问题 设置 NCCL_DEBUG=INFO、固定 NCCL_SOCKET_IFNAME,统一 NCCL 版本
首次请求超过 30 秒 冷引擎加载权重并捕获 CUDA 图 加入负载均衡前先用请求预热,并提高启动探针超时
Hugging Face 下载很慢 匿名限流 即使模型公开也应设置 HF_TOKEN
RuntimeError: Engine core initialization failed 启动阶段异常的通用包装 在日志中继续向上查看真正的错误

vLLM 与其他推理引擎

引擎 最适合 取舍
vLLM NVIDIA 多租户生产、模型覆盖广、新模型支持快 冷启动较重,ROCm 支持落后 CUDA
HuggingFace TGI HF 原生环境、Inferentia/Gaudi 新模型支持慢于 vLLM,Rust+Python 双栈
TensorRT-LLM + Triton 全 NVIDIA、追求极致延迟 每个模型都需编译,运维复杂
SGLang Agent、结构化 JSON、共享前缀的 RAG 社区较小,新模型支持较少
llama.cpp / llama-server 单流、CPU 回退、Apple Silicon 持续批处理能力弱,不适合大量并发
Ollama 开发机、Mac、内部演示 底层是 llama.cpp,生产参数少
LMDeploy Qwen、InternLM、昇腾 西方社区较小,文档偏中文

SGLang 的 RadixAttention 会自动复用共享前缀的 KV 缓存,在 agent 和 RAG 负载上很有吸引力。对于共享 4 KB 系统提示词和 8 KB 固定文档的 RAG,SGLang 报告同硬件可获得 2 到 5 倍吞吐提升。普通聊天请求很少共享长前缀时,vLLM 仍然是稳妥选择。

后续方向

至此,单节点已具备 OpenAI 兼容 HTTPS 服务、失败重启、按 key 限流、指标和三条告警规则。后续可扩展为 nginx 后的多副本、基于 Ray 的多节点 70B 服务、使用 llm-d 的解耦预填充/解码,或 Kubernetes 部署。Kubernetes 需要 NVIDIA device plugin、GPU 节点池、模型缓存 PVC 和 KV 缓存感知路由,复杂度足以单独成篇。

模型选择可参考 Ollama 模型速查表;它覆盖 vLLM 可服务的同类模型,并按显存和质量说明取舍,可映射到 --max-num-seqs 与 --gpu-memory-utilization 调优。新参数和支持模型应以 vLLM 官方文档为准。

Avatar photo

关于 木子

Email: muzi@vip.rockylinux.cn 微信:VebinLee QQ: 2306867585
Founder of the Rocky Linux Chinese community, MVP、VMware vExpert、TVP, advocate for cloud native technologies, with over ten years of experience in site reliability engineering (SRE) and the DevOps field. Passionate about Cloud Computing、Microservices、CI&CD、DevOps、Kubernetes, currently dedicated to promoting and implementing Rocky Linux in Chinese-speaking regions.
用一杯咖啡支持我们,我们的每一篇[文档]都经过实际操作和精心打磨,而不是简单地从网上复制粘贴。期间投入了大量心血,只为能够真正帮助到您。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇