侧边栏壁纸
  • 累计撰写 238 篇文章
  • 累计创建 245 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

vLLM 私有化部署实战:本地部署、Docker 部署、接口验证

zhanjie.me
2025-09-28 / 0 评论 / 0 点赞 / 1 阅读 / 0 字

如果说 Ollama + Open WebUI 解决的是“先把体验跑起来”,那 vLLM 更像是在回答另一个问题:

当体验已经有了之后,怎么把模型服务做得更像一个正式的推理接口?

这也是为什么很多团队在跑通第一套 AI 体验环境后,下一步都会很自然地进入 vLLM。

原因并不复杂:

  • 它更偏正式推理服务
  • 更强调吞吐和显存利用率
  • 天然更适合做 API 服务
  • 对接现有应用时,OpenAI 兼容接口非常有用

换句话说,Ollama 更适合快速体验,vLLM 更适合往“服务化”方向走。

但在真正开始部署 vLLM 时,很多人又会遇到新的选择题:

  • 应该先本地装,还是直接上 Docker
  • 是用 vllm serve,还是用 Python 模块方式启动
  • 服务启动之后,到底怎么验证它是不是一个真的可用 API

这篇文章就是为了解决这些问题。

我会把 vLLM 的第一阶段实践拆成两条并行路径:

  • 本地部署:更适合理解链路和调试问题
  • Docker 部署:更适合固化环境和快速复现

最后再把两条路径统一收口到同一件事上:

用 OpenAI 兼容接口验证,确认你的 vLLM 服务不是“进程在跑”,而是真的可以被应用调用。

这一篇仍然保持可实操写法:

  • 先讲为什么值得做
  • 再给命令
  • 关键地方补预期输出
  • 说明每一步什么样才算成功

文中的模型路径、镜像版本、CUDA 版本和端口都基于课程环境整理,与你的实际环境不同是正常现象。
重点是部署逻辑和验证思路。

摘要

如果你只想先看结论,可以先记住三点:

  1. vLLM 更适合作为正式推理服务入口,而不是单纯体验工具
  2. 本地部署适合理解依赖链路,Docker 部署适合快速固化环境
  3. 不管用哪条路径,最后都应该用 curl 访问 /v1/models/v1/chat/completions/v1/completions 做接口验证

这一篇最关键的验收点有三个:

  • vllm --version 正常输出版本号
  • 服务启动后,http://localhost:8000/v1/models 能返回模型列表
  • curl 请求推理接口时,能拿到结构化 JSON 响应

系列导航

这是“大模型私有化部署实践”系列的第六篇。当前系列顺序如下:

  1. 本地、Docker、K8S:大模型私有化部署路线怎么选
  2. 大模型推理环境准备实战:GPU、驱动、CUDA、容器运行时
  3. 基于 Ubuntu 24.04 搭建 AI 推理用原生 K8S 集群
  4. 为 K8S 补齐入口与存储:MetalLB、Gateway API、NFS 动态供给
  5. 用 Ollama + Open WebUI 快速搭建本地 AI 体验环境
  6. vLLM 私有化部署实战:本地部署、Docker 部署、接口验证
  7. vLLM 上 K8S:服务部署、对外暴露、监控与验证
  8. SGLang 私有化部署实战:本地部署、Docker 部署、能力体验
  9. SGLang 上 K8S:接入 Open WebUI、服务发布与 GPU 运维
  10. vLLM 和 SGLang 到底怎么选

如果说上一篇解决的是“先做一条可交互体验链路”,这一篇解决的就是:

如何从体验环境走向正式推理服务,把模型暴露成一个更标准、更适合应用对接的 API。

1. 为什么从 Ollama 走到 vLLM 是一条很自然的路径

很多团队第一次接触私有化部署时,都会先用 Ollama 快速把东西跑起来。
这是合理的,因为它上手快、闭环短、容易看到结果。

但当你开始考虑下面这些需求时,vLLM 的优势就会越来越明显:

  • 希望模型服务更像标准 API
  • 希望更关注吞吐和显存效率
  • 希望后续能平滑进入 K8S 服务化
  • 希望用 OpenAI 风格接口对接现有应用

从这个角度看,vLLM 更像是从“体验阶段”走向“推理服务阶段”的第一块踏板。

1.1 vLLM 更适合什么场景

简单说,vLLM 更适合下面这些方向:

  • 单机 GPU 上提供正式推理接口
  • 内网共享一个稳定的模型 API
  • 对接已有应用、脚本或 SDK
  • 为后续 K8S 服务化做准备

1.2 这篇为什么同时讲本地和 Docker 两条路径

因为它们解决的问题不一样:

  • 本地部署更适合理解 vLLM 和 PyTorch、CUDA 的关系
  • Docker 部署更适合快速复现和减少宿主机污染

很多人一开始会把这两条路径理解成二选一。
其实更实用的理解是:

本地路径帮助你看清楚问题,Docker 路径帮助你把问题收敛起来。

2. 部署前先确认一件事:第 2 篇的环境链路已经打通

虽然这一篇不再展开讲 GPU、驱动、CUDA 和容器运行时的底层准备,但在真正装 vLLM 前,最好先确认这些基础动作已经通过:

nvidia-smi
nvcc --version
python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.device_count())"

至少应该能看到:

True
1

如果这一步都还没通过,建议先回看第 2 篇,而不是继续往上装 vLLM。

3. 本地部署路径:最适合吃透运行链路

本地部署的目标,不是追求“最省心”,而是追求“最容易看清问题在哪一层”。

它特别适合这几类情况:

  • 你第一次接触 vLLM
  • 你想确认某个模型能不能在当前机器上跑起来
  • 你想搞清楚 vLLM、PyTorch、CUDA 的关系

3.1 准备 Miniconda 和独立 Python 环境

如果你的机器上还没有合适的 Python 隔离环境,建议先准备 Miniconda。

mkdir -p ~/miniconda3
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda3/Miniconda3-latest-Linux-x86_64.sh
bash ~/miniconda3/Miniconda3-latest-Linux-x86_64.sh -b -u -p ~/miniconda3
~/miniconda3/bin/conda init bash
source ~/.bashrc

课程里的典型安装输出会出现:

PREFIX=/root/miniconda3
Unpacking bootstrapper...
Installing base environment...
installation finished.

然后创建 vLLM 专用环境:

conda create --name vllm python=3.10 -y
conda activate vllm
python --version

预期输出类似:

Python 3.10.x

如果你已经有稳定的 Python 3.10 环境,这一步也可以跳过,但独立环境依然更推荐。

3.2 安装 PyTorch 和 vLLM

在课程环境里,使用的是和 CUDA 对应的 PyTorch wheel,再安装 vLLM。

例如:

wget https://download.pytorch.org/whl/cu124/torch-2.6.0%2Bcu124-cp310-cp310-linux_x86_64.whl
pip install torch-2.6.0+cu124-cp310-cp310-linux_x86_64.whl
pip install vllm
vllm --version

预期输出类似:

0.13.0

如果这里 vllm --version 都过不了,就先不要继续启动服务。
优先检查:

  • 当前虚拟环境是不是你刚创建的那个
  • pip install 是否装到了错误的 Python 环境
  • PyTorch wheel 和 CUDA 版本是否兼容

3.3 准备模型目录

本地部署时,你需要一个明确的模型目录。
课程里使用的是类似:

/data/ModelScope/Deepseek-Qwen

你可以先检查目录是否真的存在:

ls /data/ModelScope
ls /data/ModelScope/Deepseek-Qwen

这里最重要的不是目录名长什么样,而是:

  • 路径真实存在
  • 当前用户有权限访问
  • 模型权重已经完整准备好

3.4 用 vllm serve 启动服务

最直接的方式,就是用 vllm serve

vllm serve /data/ModelScope/Deepseek-Qwen \
  --host 0.0.0.0 \
  --port 8000 \
  --served-model-name deepseek \
  --max-model-len 8192 \
  --dtype auto \
  --gpu-memory-utilization 0.8

这里几个最关键的参数分别是:

  • --host 0.0.0.0:允许外部访问
  • --port 8000:服务监听端口
  • --served-model-name deepseek:接口调用时要写的模型名
  • --max-model-len 8192:模型最大上下文长度
  • --gpu-memory-utilization 0.8:显存占用比例控制

正常启动时,日志里通常会持续打印模型加载信息。
如果服务已经稳定起来,终端不会立刻退出,而是持续驻留。

3.5 也可以用 Python 模块方式启动

课程里还给了另一种写法:

python -m vllm.entrypoints.openai.api_server \
  --model /data/ModelScope/Deepseek-Qwen \
  --host 0.0.0.0 \
  --port 8000 \
  --served-model-name deepseek

这两种方式本质目标一致,都是起 OpenAI 兼容 API 服务。
如果你只是为了快速跑通,优先用 vllm serve 就够了。

3.6 用 systemd 把本地服务托管起来

如果你不想每次手动在终端前台启动,也可以做一个简单的 systemd 托管:

[Unit]
Description=vLLM API Server
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/data
ExecStart=/root/miniconda3/envs/vllm/bin/vllm serve /data/ModelScope/Deepseek-Qwen \
  --host 0.0.0.0 \
  --port 8000 \
  --served-model-name deepseek \
  --max-model-len 8192 \
  --dtype auto \
  --gpu-memory-utilization 0.8
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

保存为 /etc/systemd/system/vllm.service 后执行:

systemctl daemon-reload
systemctl enable vllm
systemctl start vllm
systemctl status vllm --no-pager

如果运行正常,状态里至少应该看到:

Active: active (running)

4. 先别急着接应用,先做本地 API 验证

不管你是前台启动还是 systemd 托管,真正重要的都不是“进程在不在”,而是“接口能不能用”。

4.1 先看模型列表接口

先用最基础的接口确认服务起来了:

curl http://localhost:8000/v1/models

如果服务正常,返回通常类似:

{
  "object": "list",
  "data": [
    {
      "id": "deepseek",
      "object": "model",
      "owned_by": "vllm"
    }
  ]
}

只要这里拿不到结果,就先不要继续调用 /v1/chat/completions

4.2 用 completions 接口做一次最小验证

课程里给的是这样的测试方式:

curl http://localhost:8000/v1/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek",
    "prompt": "地球为什么是圆的?",
    "max_tokens": 100
  }'

如果正常,返回会是结构化 JSON,类似:

{
  "id": "cmpl-xxxx",
  "object": "text_completion",
  "model": "deepseek",
  "choices": [
    {
      "index": 0,
      "text": "地球之所以近似球形,主要是因为引力会把物质尽量拉向中心..."
    }
  ]
}

4.3 如果你更习惯聊天接口,也可以直接测 /v1/chat/completions

curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek",
    "messages": [
      {"role": "user", "content": "请用一句话介绍 vLLM 是什么"}
    ],
    "max_tokens": 128
  }'

只要你能拿到结构化回复,说明这条本地服务链路已经基本成立。

5. Docker 部署路径:更适合快速固化和复现环境

当你已经理解了本地路径之后,Docker 通常会成为更实用的一条路。
它最大的价值不是“更高级”,而是“环境更容易固定下来”。

特别适合这些场景:

  • 单机 GPU 服务器长期提供推理接口
  • 团队希望统一部署方式
  • 不想把大量 Python 依赖直接堆在宿主机上

5.1 先确认 Docker 和 GPU 运行时已经打通

按照第 2 篇的逻辑,在真正跑 vLLM 镜像前,先确认 Docker 和 NVIDIA Container Toolkit 已经通了:

docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi

如果这里都还不通,就不要继续拉 vLLM 镜像。

5.2 安装或检查 Docker

课程里使用的是二进制方式安装 Docker,也可以用你自己的标准安装方式。
关键不是安装路径,而是最后能正常工作:

systemctl enable --now docker
docker version
docker info | grep -A5 Runtimes

如果 NVIDIA 运行时配置好了,docker info 里通常会出现类似:

Runtimes: io.containerd.runc.v2 nvidia runc
Default Runtime: runc

5.3 安装并配置 NVIDIA Container Toolkit

如果你前面还没做过,这一步是 Docker 路线的关键:

sudo apt update
sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
cat /etc/docker/daemon.json

课程里的典型输出会出现:

INFO[0000] Config file does not exist; using empty config
INFO[0000] Wrote updated config to /etc/docker/daemon.json
INFO[0000] It is recommended that docker daemon be restarted.

/etc/docker/daemon.json 里通常会包含类似内容:

{
  "runtimes": {
    "nvidia": {
      "args": [],
      "path": "nvidia-container-runtime"
    }
  }
}

6. 用 Docker 启动 vLLM

课程里使用的是 vllm/vllm-openai:latest 镜像。
这类镜像的优点是非常直接:启动后就是 OpenAI 兼容 API 服务。

6.1 先准备模型挂载目录

假设宿主机模型目录是:

/data/ModelScope

先确认目录存在:

ls /data/ModelScope

6.2 启动容器

如果镜像是本地包,也可以先加载:

docker load -i vllm-openai.tar

然后启动服务:

docker run -d \
  --name vllm-server \
  --gpus all \
  --shm-size=1g \
  -p 8000:8000 \
  -v /data/ModelScope:/models \
  vllm/vllm-openai:latest \
  --model /models/Deepseek-Qwen \
  --served-model-name deepseek \
  --max-model-len 8192 \
  --dtype auto

这里最关键的参数有:

  • --gpus all:把宿主机 GPU 给容器
  • --shm-size=1g:给共享内存更大的空间
  • -p 8000:8000:把容器内 API 暴露到宿主机
  • -v /data/ModelScope:/models:让容器能访问宿主机模型目录
  • --model /models/Deepseek-Qwen:指定容器内模型路径

6.3 查看容器运行状态

docker ps
docker logs -f vllm-server

docker ps 正常情况下至少能看到:

CONTAINER ID   IMAGE                   COMMAND                  STATUS          PORTS
xxxxxxxxxxxx   vllm/vllm-openai:latest "python3 -m vllm...."   Up 20 seconds   0.0.0.0:8000->8000/tcp

只要容器启动后马上退出,就优先看日志,不要先猜模型或框架有问题。

7. 用接口验证 Docker 版 vLLM

本地版和 Docker 版在验证方法上,其实应该保持一致。
无论底层怎么部署,最后都应该回到“API 到底能不能用”。

7.1 先看 /v1/models

curl http://localhost:8000/v1/models

如果正常,返回类似:

{
  "object": "list",
  "data": [
    {
      "id": "deepseek",
      "object": "model",
      "owned_by": "vllm"
    }
  ]
}

7.2 再测 /v1/chat/completions

curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek",
    "messages": [
      {"role": "user", "content": "请解释一下为什么地球近似球形"}
    ],
    "max_tokens": 128
  }'

7.3 如果镜像配置了 API Key

有些部署方式会带上 --api-key 参数。
如果你启动容器时配置了 API Key,请记得调用时也带上:

curl http://localhost:8000/v1/models \
  -H "Authorization: Bearer your-api-key"

8. 本地部署和 Docker 部署,到底该先用哪个

这其实是很多团队最关心的现实问题。

我的建议是:

  • 第一次理解 vLLM:优先本地部署
  • 第一次给别人复现:优先 Docker 部署
  • 计划长期提供服务:Docker 往往更稳

8.1 本地部署更适合什么

本地部署更适合:

  • 学习链路
  • 排查环境问题
  • 理解 PyTorch、CUDA、vLLM 的关系

8.2 Docker 部署更适合什么

Docker 部署更适合:

  • 固化环境
  • 快速迁移
  • 做统一的服务交付方式

如果用一句话概括:

本地部署帮你把问题看清楚,Docker 部署帮你把问题收敛起来。

9. 最常见的几类问题,基本都卡在这几个地方

到了 vLLM 这一步,最常见的问题通常不再是“命令不会写”,而是下面这些。

9.1 服务进程在,但模型接口没起来

典型表现:

  • 进程在跑
  • curl http://localhost:8000/v1/models 没返回

这通常说明模型还没加载完成,或者服务其实卡在启动阶段。
优先看前台日志或 docker logs

9.2 本地环境能装,接口一调用就报错

典型表现:

  • vllm --version 正常
  • 但服务启动报 CUDA 或模型相关错误

这类问题往往还是回到:

  • PyTorch / CUDA 兼容性
  • 模型路径
  • 显存是否够用

9.3 Docker 容器能起,但容器里没 GPU

典型表现:

  • docker ps 有容器
  • 但推理速度异常慢或日志提示 GPU 不可用

这时要先回头检查:

  • --gpus all 有没有加
  • NVIDIA Container Toolkit 是否配置好了
  • docker run --rm --gpus all nvidia/cuda ... nvidia-smi 是否正常

9.4 模型名和接口调用里的 model 对不上

这类问题也非常常见。

例如你启动时用了:

--served-model-name deepseek

那调用接口时就要写:

"model": "deepseek"

而不是模型目录名,更不是随便起一个别名。

10. 结语:vLLM 是从体验环境走向正式推理服务的第一步

跑完这一篇之后,你会明显感觉到一件事:
和 Ollama 相比,vLLM 已经更像一个“面向应用的推理服务”了。

它的重点不再只是“我能不能跟模型聊起来”,而是:

  • 这个服务能不能稳定暴露成 API
  • 其他应用能不能按标准方式调用它
  • 后面能不能平滑放进容器或集群里

这也是为什么它非常适合作为系列里的下一步。

从系列节奏上看,这一篇解决的是:

  • 单机层面把正式推理服务跑通
  • 把本地和 Docker 两条路径吃透
  • 用 OpenAI 兼容接口把服务验证清楚

而再往下一步,问题就不再是“单机怎么跑”,而是“这套服务怎么放进 K8S,变成团队共享的正式能力”。

下一篇文章,我们就继续沿着这条线往前走:
把 vLLM 放进 K8S,接上 PVC、Service、Gateway 和监控链路,真正把它做成一个可对外使用的集群推理服务。

0

评论区