通过vLLM部署本地模型并通过Cloudflare Tunnel发布到公网

这份指南将以我本人解决问题的历程为线索,记录每一步的命令、参数、遇到的问题以及相应的解决方案。


1. 准备工作

在开始之前,请确保您具备以下条件:

  • 硬件:一台拥有至少24GB显存的NVIDIA GPU。
  • 软件
    • 64位的Linux操作系统(例如Ubuntu 20.04+)。
    • Python 3.8+ 环境。
    • pip 包管理器。
  • 账户与域名
    • 一个 Cloudflare 账户。(若不进行接口的公网开放可以没有)
    • 一个您自己的域名,并将其DNS管理设置为使用Cloudflare。(若不进行接口的公网开放可以没有)

2. 部署本地vLLM服务

2.1 安装vLLM

首先,创建一个干净的Python虚拟环境并安装vLLM及其依赖。

1
2
3
4
5
6
7
8
9
# 创建并激活虚拟环境
python3 -m venv vllm_env
source vllm_env/bin/activate

# 升级pip
pip install --upgrade pip

# 安装vllm多模态版本和openai客户端
pip install "vllm[multimodal]" openai

2.2 下载模型文件(离线部署准备)

由于本人的服务器网络环境不佳,我们选择在另一台网络好的电脑上下载模型,再上传到服务器进行离线部署。

1
2
# 在一台Windows电脑上尝试使用git clone
git clone https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct

git clone 命令失败,报错 Failed to connect to huggingface.co port 443 after ... ms: Could not connect to server

  • 原因分析:网络问题,Git无法建立稳定的HTTPS连接。

    • 解决方案:直接从Hugging Face网站手动下载或使用官方库下载。

      1. 手动下载:访问模型页面的 “Files and versions“标(以Qwen2.5-VL-7B-Instruct为例),逐个下载所有文件,并将它们全部放在一个新建的文件夹里。

      2. 使用huggingface-hub库下载:编写一个简单的Python脚本来下载,该方法支持断点续传,更稳定。

        1
        2
        3
        4
        5
        6
        7
        # download_model.py
        from huggingface_hub import snapshot_download
        snapshot_download(
        repo_id="Qwen/Qwen2.5-VL-7B-Instruct",
        local_dir="/path/to/save/models/Qwen2.5-VL-7B-Instruct", # 指定本地保存路径
        local_dir_use_symlinks=False
        )

下载完成后,将包含所有模型文件的 Qwen2.5-VL-7B-Instruct 文件夹完整上传到你的Linux AI服务器上,例如路径为:/home/models/Qwen2.5-VL-7B-Instruct

2.3 启动vLLM服务

在模型文件准备就绪后,我们开始启动vLLM服务。

第1轮:基础启动尝试

  • 初始指令:
1
2
3
4
python -m vllm.entrypoints.openai.api_server \
--model /path/to/models/Qwen2.5-VL-7B-Instruct \ # 这里需要需要修改为你Linux服务器实际存储模型文件的路径
--port 8000 \ # vLLM的本地运行端口,由于我本人在后端运行django,为避免冲突可以修改端口号,例如8001
--trust-remote-code

问题 #1:GPU显存不足 (预检失败)

  • 错误日志: ValueError: Free memory on device (...) is less than desired GPU memory utilization (0.9, ...)
  • 问题分析: vLLM默认尝试在第一块显卡(GPU 0)上预留90%的显存,运行命令 nvidia-smi 发现,GPU 0 上有多个其他进程占用了约5.7GB显存,导致剩余可用显存不足。
  • 解决方案: 我们发现服务器的第二块显卡(GPU 1)完全空闲。因此,我们需要明确告诉vLLM去使用这块空闲的GPU。

第2轮:修正显卡选择

我们通过添加 CUDA_VISIBLE_DEVICES 环境变量来指定GPU,让vLLM在空闲的GPU 1上运行。

  • 修正后的指令:
1
2
3
4
CUDA_VISIBLE_DEVICES=1 python -m vllm.entrypoints.openai.api_server 
--model /path/to/models/Qwen2.5-VL-7B-Instruct
--port 8000
--trust-remote-code

问题 #2:KV缓存空间不足

  • 错误日志: ValueError: ... GiB KV cache is needed, which is larger than the available KV cache memory ...
  • 问题分析: 模型加载成功后,vLLM需要为上下文(KV缓存)分配空间。Qwen模型的理论最大长度(128k tokens)非常大,导致计算出的所需KV缓存空间超过了24GB显卡上加载完模型后剩余的空间。
  • 解决方案: 使用 --max-model-len 参数告诉vLLM,不需要为理论最大长度做准备,只需为一个更实际、更小的长度分配空间即可。

第3轮:解决KV缓存空间问题

我们在命令中加入 --max-model-len 参数,限制上下文长度,使其KV缓存能被装入剩余显存。

  • 修正后的指令:
1
2
3
4
5
CUDA_VISIBLE_DEVICES=1 python -m vllm.entrypoints.openai.api_server 
--model /path/to/models/Qwen2.5-VL-7B-Instruct
--port 8000
--trust-remote-code
--max-model-len 32768

问题 ##3:模型名称不匹配 (404 Not Found)

  • 错误日志: 服务成功启动,但客户端(test_client.py)调用时,服务器返回 Error: The model 'Qwen/Qwen2.5-VL-7B-Instruct' does not exist.
  • 问题分析: 当vLLM从本地路径加载模型时,它默认使用完整的文件路径作为模型名。而客户端请求的是模型的Hugging Face官方名称,两者不匹配。
  • 解决方案: 使用 --served-model-name 参数为本地加载的模型指定一个对外的“别名”,使其与客户端请求的名称一致。同时,既然我们已经在使用一块专用的空闲GPU,我们可以把显存利用率调回到一个较高的值以获得最佳性能。

第5轮:最终成功的命令

成功启动一个稳定、高性能、且能被客户端正确识别的服务。

  • 最终指令:
1
2
3
4
5
6
7
CUDA_VISIBLE_DEVICES=1 python -m vllm.entrypoints.openai.api_server 
---model /path/to/models/Qwen2.5-VL-7B-Instruct
--served-model-name Qwen/Qwen2.5-VL-7B-Instruct
--port 8000
--trust-remote-code
--gpu-memory-utilization 0.95
--max-model-len 32768
  • 若多行命令报错,可直接使用以下单行命令
1
CUDA_VISIBLE_DEVICES=1 python -m vllm.entrypoints.openai.api_server ---model /path/to/models/Qwen2.5-VL-7B-Instruc --served-model-name Qwen/Qwen2.5-VL-7B-Instruct --port 8000 --trust-remote-code --gpu-memory-utilization 0.95 --max-model-len 32768

参数详解:

  • CUDA_VISIBLE_DEVICES=1: 指定程序仅使用编号为1的GPU。
  • --model /path/...: 指定从本地文件夹加载模型,实现离线部署。
  • --served-model-name Qwen/...: 为本地模型设置一个对外服务的别名,以匹配客户端请求。
  • --port 8000: 在8000端口上提供服务。
  • --trust-remote-code: 允许执行模型自带的自定义Python代码,Qwen模型必需。
  • --gpu-memory-utilization 0.95: 设置GPU显存使用率,因为GPU 1是空闲的,可以设置高一些。
  • --max-model-len 32768: 限制最大上下文长度为32k,以解决KV缓存空间不足的问题。

3. 使用Cloudflare Tunnel发布到公网

现在我们有了一个本地运行的服务,接下来将它发布到公网。

3.1 安装cloudflared

添加Cloudflare GPG密钥和软件源

1
2
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloudflare-main.gpg
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared jammy main' | sudo tee /etc/apt/sources.list.d/cloudflared.list

安装 cloudflared:

1
2
sudo apt update
sudo apt install cloudflared

3.2 授权cloudflared

1
cloudflared tunnel login

3.3 创建并配置隧道

  1. 创建隧道:
1
cloudflared tunnel create vllm-service

记下输出的隧道UUID和凭证文件路径。
image
2. 创建并编辑配置文件:

1
2
mkdir -p /home/path/.cloudflared
nano ~/.cloudflared/config.yml

根据你的隧道信息和域名,填入以下内容:

1
2
3
4
5
6
7
tunnel: <你的隧道UUID>
credentials-file: /home/path/.cloudflared/<你的隧道UUID>.json

ingress:
- hostname: qwen-vl.你的域名 # 你的公共访问域名
service: http://localhost:8000 # 指向本地vLLM服务
- service: http_status:404 # 对于其他所有未匹配的请求,返回 404
  1. 创建DNS路由:
1
cloudflared tunnel route dns vllm-service qwen-vl.你的域名

3.4 启动隧道服务

临时测试运行

1
cloudflared tunnel run vllm-service

作为后台服务永久运行

1
2
3
4
5
6
7
8
9
10
11
# 将cloudflared安装为systemd服务
sudo cloudflared service install

# 启动服务
sudo systemctl start cloudflared

# (推荐)设置开机自启
sudo systemctl enable cloudflared

# 检查服务状态
sudo systemctl status cloudflared

4. 如何从公网调用API

现在,您的模型已经成功部署并通过 https://qwen-vl.你的域名 向公网提供服务。任何人都可以通过OPENAI接口进行调用。

1
2
3
4
# --- 需要提供给用户的信息 ---
API_BASE_URL = "https://qwen-vl.你的域名/v1"
MODEL_NAME = "Qwen/Qwen2.5-VL-7B-Instruct"
API_KEY = "not-needed" # API Key可以是任意字符串

测试脚本:https://github.com/EvannZhongg/Blog-Learning.git