Qwen3.8-27B-AWQ-INT4 双 RTX A4000 部署教程
Qwen3.8-27B-AWQ-INT4 双 RTX A4000 部署教程
Docker + vLLM + 多 GPU + 长上下文 + 多模态 + OpenAI API + Cherry Studio
本教程完整记录在 2 × NVIDIA RTX A4000 16GB 服务器上,通过 Docker + vLLM 部署 Qwen3.8-27B-AWQ-INT4 的全过程。
最终实现:
- 2 张 A4000 共同运行一个 27B 模型
- Tensor Parallel = 2
- 最大上下文 112K
max-num-seqs = 4- 支持文本和图片
- 提供 OpenAI 兼容 API
- 可供 Cherry Studio、curl、OpenAI SDK 等客户端访问
1. 先理解整套部署架构
本次系统分成 5 层:
Cherry Studio / curl / OpenAI SDK
│
│ HTTP / OpenAI API
▼
vLLM API Server
│
▼
Qwen3.8-27B-AWQ-INT4
│
Tensor Parallel = 2
┌──────┴──────┐
▼ ▼
RTX A4000 RTX A4000
16GB 16GB
再往下看运行环境:
NVIDIA GPU
↓
NVIDIA Driver
↓
NVIDIA Container Runtime
↓
Docker
↓
vLLM
↓
Qwen3.8
↓
OpenAI API
↓
Cherry Studio
最重要的一点:
Docker、vLLM、模型、客户端不是一回事。
2. Docker、vLLM、Ollama、ONNX 分别是什么
2.1 Docker:负责运行环境
Docker 不负责模型推理。
它主要负责把这些依赖封装起来:
Python
PyTorch
CUDA 运行库
vLLM
其他依赖
例如:
# 下载已经预装好 vLLM 的 Docker 镜像
docker pull vllm/vllm-openai:latest
可以简单理解:
Docker = 运行环境
vLLM = 推理引擎
Qwen = 被加载的模型
2.2 vLLM:负责高性能大模型服务端推理
vLLM 特别适合:
- NVIDIA GPU 服务器
- 多 GPU
- 高并发
- 长上下文
- OpenAI API
- 多用户访问
- KV Cache 管理
- Continuous Batching
vLLM 的基本工作流:
客户端发请求
↓
vLLM 接收
↓
调度请求
↓
管理 KV Cache
↓
调用 GPU
↓
返回 OpenAI 兼容结果
Continuous Batching
vLLM 可以动态调度不同时间到达的请求:
请求 A 先到
请求 B 后到
请求 C 再到
↓
vLLM 动态加入/移出 batch
↓
尽量保持 GPU 忙碌
因此它非常适合服务器 API 场景。
2.3 Ollama:更适合个人本地快速运行
Ollama 更强调:
安装简单
模型管理方便
命令简单
适合本地快速体验
典型命令:
# 下载模型
ollama pull <模型名>
# 运行模型
ollama run <模型名>
更适合:
Mac
Windows
Linux 桌面
个人电脑
单用户
快速测试模型
2.4 ONNX / ONNX Runtime
ONNX 是一种模型交换格式。
典型流程:
PyTorch / TensorFlow
↓
导出
↓
ONNX
↓
ONNX Runtime
↓
CPU / CUDA / TensorRT
ONNX Runtime 更偏向:
把模型嵌入自己的软件
例如:
- C++
- C#
- Python
- 工业软件
- 桌面软件
- 边缘端
- 跨平台应用
可以简单记:
Ollama
→ 本地快速运行模型
vLLM
→ GPU 服务器提供大模型 API
ONNX Runtime
→ 把模型嵌入自己的应用程序
3. 本次服务器和模型信息
3.1 GPU
服务器:
GPU 0:NVIDIA RTX A4000 16GB
GPU 1:NVIDIA RTX A4000 16GB
总显存约:
32GB
查看 GPU:
# 查看 GPU 型号、显存、温度、利用率和进程
nvidia-smi
实时监控:
# 每 1 秒刷新一次 GPU 状态
watch -n 1 nvidia-smi
3.2 模型
本次准备了:
/data/qwen/models/Qwen3.8-27B-AWQ-BF16-INT4
/data/qwen/models/Qwen3.8-27B-AWQ-INT4
最终使用:
/data/qwen/models/Qwen3.8-27B-AWQ-INT4
原因:
模型权重占用相对更小
↓
留给 KV Cache 的空间更多
↓
更适合长上下文和多模态
4. 理解模型名称:27B、AWQ、INT4
模型名:
Qwen3.8-27B-AWQ-INT4
可拆成:
Qwen3.8
├── 27B
├── AWQ
└── INT4
4.1 27B
B = Billion
所以:
27B ≈ 270 亿参数
INT4 不会让 27B 变成 7B,参数规模仍是 27B。
4.2 AWQ
AWQ 是一种权重量化方案。
高精度权重
↓
AWQ 量化
↓
低 bit 权重
↓
降低显存和存储压力
4.3 INT4
INT4 表示模型权重主要采用 4 bit 表示。
粗略对比:
BF16 → 16 bit
INT8 → 8 bit
INT4 → 4 bit
本次选择 INT4 的核心目的:
减少权重显存
↓
给 KV Cache 留更多空间
5. GPU、驱动、CUDA、Docker Runtime 的关系
完整关系:
NVIDIA GPU
↓
NVIDIA Driver
↓
CUDA Runtime
↓
NVIDIA Container Toolkit
↓
Docker
↓
PyTorch / vLLM
NVIDIA Driver
# 能正常显示 GPU,说明宿主机驱动基本正常
nvidia-smi
CUDA
vLLM 和 PyTorch 通过 CUDA 使用 NVIDIA GPU。
注意:
nvidia-smi 里的 CUDA Version
更接近“当前驱动支持的最高 CUDA Runtime 版本”,不等于系统一定安装了同版本 CUDA Toolkit。
NVIDIA Container Toolkit
它负责把宿主机 GPU 暴露给 Docker 容器。
6. 配置 NVIDIA Container Toolkit
# 查看 nvidia-ctk 是否已经安装
which nvidia-ctk
# 查看版本
nvidia-ctk --version
配置 Docker:
# 把 NVIDIA Runtime 注册到 Docker
sudo nvidia-ctk runtime configure --runtime=docker
# 重新加载 Docker 配置
# 本机还有其他重要容器,因此优先 reload
sudo systemctl reload docker
检查:
# 确认 Docker 已经识别 nvidia runtime
docker info | grep -i runtime
7. 检查 Docker 是否能使用 GPU
本机标准的:
--gpus all
曾出现兼容问题,因此最终使用:
--runtime=nvidia
-e NVIDIA_VISIBLE_DEVICES=all
测试:
# --rm:
# 容器退出后自动删除,只适合一次性测试
#
# --runtime=nvidia:
# 使用 NVIDIA Container Runtime
#
# NVIDIA_VISIBLE_DEVICES=all:
# 让容器看到所有 NVIDIA GPU
docker run --rm \
--runtime=nvidia \
-e NVIDIA_VISIBLE_DEVICES=all \
nvidia/cuda:12.8.0-base-ubuntu22.04 \
nvidia-smi
容器里能看到两张 A4000,就说明 Docker → GPU 链路正常。
8. 磁盘检查与目录准备
查看磁盘:
# 查看根分区和 /data 使用情况
df -h
查看 Docker 占用:
# 查看 Docker 镜像、容器、volume、build cache 占用
docker system df
清理未使用 Build Cache:
# -a:清理全部未使用 build cache
# -f:不再确认
docker builder prune -af
不要在有重要数据库 volume 的服务器上盲目执行:
docker system prune -a --volumes
准备目录:
# 模型目录
mkdir -p /data/qwen/models
# vLLM / Hugging Face 缓存
mkdir -p /data/qwen/vllm-cache
# 临时目录
mkdir -p /data/qwen/tmp
9. 从 Hugging Face 下载 Qwen3.8 模型
在使用 vLLM 启动 Qwen3.8 之前,首先要把模型文件准备好。
本教程采用的方式是:
Hugging Face
↓
hf download
↓
/data/qwen/models/
↓
Docker 只读挂载
↓
vLLM 加载本地模型
这样做的优点是模型文件位置固定,Docker 容器删除或重建以后也不需要重新下载模型。
9.1 Hugging Face 和 hf 命令分别是什么
Hugging Face 是常用的模型、数据集和机器学习资源托管平台。
模型仓库通常写成:
用户名或组织名/模型名
例如本次使用:
cyankiwi/Qwen3.8-27B-AWQ-INT4
其中:
cyankiwi
→ Hugging Face 用户 / 组织
Qwen3.8-27B-AWQ-INT4
→ 模型仓库名称
hf 是 Hugging Face Hub 官方命令行工具,可以完成:
登录
下载模型
上传文件
查看缓存
管理仓库
9.2 安装 Hugging Face CLI
hf 命令由 Python 包 huggingface_hub 提供。
建议在专门用于 Qwen 部署的 Conda 环境中安装:
# 进入自己的 Qwen Python / Conda 环境
# 如果已经进入目标环境,可以跳过这一步
conda activate /data/conda_envs/qwen
# 安装或升级 huggingface_hub
# -U:upgrade,升级到较新的版本
python -m pip install -U huggingface_hub
安装后检查:
# 查看 hf 命令位于哪里
which hf
# 查看 hf CLI 帮助,能正常输出说明安装成功
hf --help
# 查看 huggingface_hub Python 包版本
python -m pip show huggingface_hub
为什么不再推荐 huggingface-cli
旧教程经常使用:
huggingface-cli download ...
较新的 Hugging Face Hub 已经统一推荐:
hf download ...
如果终端提示:
huggingface-cli is deprecated
直接改用 hf 即可。
9.3 是否需要登录 Hugging Face
如果模型仓库是公开的,很多情况下可以直接下载。
如果仓库需要授权、访问受限,或者匿名下载受到限制,则需要登录。
交互式登录:
# 登录 Hugging Face
# 执行后终端会提示输入 Access Token
hf auth login
检查当前账号:
# 查看当前登录的 Hugging Face 用户
hf auth whoami
也可以使用环境变量:
# 把 Hugging Face Token 保存到当前 Shell
# 不要把真实 Token 提交到 Git 或公开教程
export HF_TOKEN='你的_HuggingFace_Access_Token'
Hugging Face Token 和后面 vLLM 的 --api-key 不是同一个东西:
HF_TOKEN
→ 用来访问 Hugging Face 下载模型
VLLM_API_KEY
→ 用来保护自己部署的 vLLM API
9.4 创建模型存储目录
本次不把模型下载到系统根分区,而是统一放到容量更大的 /data:
# 创建模型根目录
# -p:父目录不存在时一起创建;目录已经存在也不会报错
mkdir -p /data/qwen/models
推荐目录结构:
/data/qwen/
├── models/
│ ├── Qwen3.8-27B-AWQ-INT4/
│ └── Qwen3.8-27B-AWQ-BF16-INT4/
├── vllm-cache/
└── tmp/
9.5 下载本次最终使用的 AWQ-INT4 模型
本次最终部署的是:
cyankiwi/Qwen3.8-27B-AWQ-INT4
推荐命令:
# hf download:
# 下载 Hugging Face 仓库中的模型文件
#
# cyankiwi/Qwen3.8-27B-AWQ-INT4:
# Hugging Face 仓库 ID
#
# --local-dir:
# 指定模型最终保存到哪个本地目录
hf download \
cyankiwi/Qwen3.8-27B-AWQ-INT4 \
--local-dir /data/qwen/models/Qwen3.8-27B-AWQ-INT4
一行版:
hf download cyankiwi/Qwen3.8-27B-AWQ-INT4 --local-dir /data/qwen/models/Qwen3.8-27B-AWQ-INT4
下载完成以后,宿主机模型路径就是:
/data/qwen/models/Qwen3.8-27B-AWQ-INT4
9.6 下载另一个 AWQ-BF16-INT4 版本
本次还准备过:
cyankiwi/Qwen3.8-27B-AWQ-BF16-INT4
下载:
# 下载另一个显存压力更大的量化版本
hf download \
cyankiwi/Qwen3.8-27B-AWQ-BF16-INT4 \
--local-dir /data/qwen/models/Qwen3.8-27B-AWQ-BF16-INT4
一行版:
hf download cyankiwi/Qwen3.8-27B-AWQ-BF16-INT4 --local-dir /data/qwen/models/Qwen3.8-27B-AWQ-BF16-INT4
两者都属于 27B 模型,但本次最终选择 AWQ-INT4,因为它能给 KV Cache 留出更多显存。
9.7 下载中断后怎么办
下载几十 GB 的模型时,中途断网并不罕见。
一般情况下,重新执行同一条:
hf download cyankiwi/Qwen3.8-27B-AWQ-INT4 --local-dir /data/qwen/models/Qwen3.8-27B-AWQ-INT4
Hugging Face Hub 会重新检查已有文件,并继续处理缺失或未完成的内容。
因此不需要因为一次网络中断就删除整个模型目录重新下载。
9.8 下载完成后检查模型文件
先看目录:
# 查看模型目录中的文件
ls -lh /data/qwen/models/Qwen3.8-27B-AWQ-INT4
查看总大小:
# -s:只显示总大小
# -h:使用 GB / MB 等人类可读单位
# du:查看目录占用空间
du -sh /data/qwen/models/Qwen3.8-27B-AWQ-INT4
通常会看到一些重要文件:
config.json
生成配置相关 JSON
Tokenizer 相关文件
*.safetensors
模型索引文件
Processor / 多模态相关配置
具体文件名取决于仓库内容。
9.9 检查 config.json
config.json 是模型最重要的配置文件之一。
vLLM 会从中读取:
模型架构
隐藏层配置
最大上下文配置
量化信息
部分多模态配置
查看前 40 行:
# 查看 config.json 前 40 行
head -n 40 /data/qwen/models/Qwen3.8-27B-AWQ-INT4/config.json
搜索架构:
# -n:显示匹配行号
# 搜索 architectures 字段
grep -n '"architectures"' /data/qwen/models/Qwen3.8-27B-AWQ-INT4/config.json
如果想查最大位置 / 上下文相关配置,也可以:
# 搜索常见上下文长度字段;不同模型字段名称可能不同
grep -n -E 'max_position|max_seq|rope' /data/qwen/models/Qwen3.8-27B-AWQ-INT4/config.json
9.10 为什么本教程选择“先下载到本地”
vLLM 也可以在某些情况下直接使用 Hugging Face 仓库 ID,让运行时自动下载模型。
本次没有采用这种方式,而是:
先 hf download
↓
固定模型目录
↓
Docker 只读挂载
↓
vLLM 加载本地路径
原因:
模型位置明确
容器删除后模型仍然存在
重新创建容器不需要重新下载
方便比较不同量化版本
方便检查模型大小和完整性
减少 Docker 层和模型文件混在一起
Docker 中使用:
# 宿主机 /data/qwen/models
# 映射成容器里的 /models
# :ro 表示 read-only,只读
-v /data/qwen/models:/models:ro
所以宿主机:
/data/qwen/models/Qwen3.8-27B-AWQ-INT4
在容器中就变成:
/models/Qwen3.8-27B-AWQ-INT4
这也解释了最终 docker run 为什么写:
/models/Qwen3.8-27B-AWQ-INT4
而不是宿主机完整路径。
9.11 Hugging Face 下载常见错误
hf: command not found
说明当前 Python 环境没有安装 CLI,或者 PATH 没找到它:
# 在当前 Python 环境安装 / 升级
python -m pip install -U huggingface_hub
# 再检查
which hf
hf --help
huggingface-cli is deprecated
不要继续使用旧命令:
huggingface-cli download
改成:
hf download
Hugging Face 无法连接
先测试网络:
# -I:只获取 HTTP 响应头,不下载完整网页
curl -I https://huggingface.co
如果服务器必须通过代理联网,可在当前 Shell 临时设置:
# HTTP 代理;端口根据自己的代理实际配置填写
export http_proxy=http://127.0.0.1:代理端口
# HTTPS 代理
export https_proxy=http://127.0.0.1:代理端口
再执行:
hf download cyankiwi/Qwen3.8-27B-AWQ-INT4 --local-dir /data/qwen/models/Qwen3.8-27B-AWQ-INT4
如果代理只运行在另一台电脑上,需要保证服务器能真正访问那个代理地址,不能把服务器自己的 127.0.0.1 和另一台电脑的 127.0.0.1 混淆。
10. 拉取 vLLM 镜像
# 下载 vLLM OpenAI-Compatible Server 镜像
docker pull vllm/vllm-openai:latest
确认:
# 查看镜像
docker images | grep vllm
11. API Key、OpenSSL 和 Bearer
11.1 为什么需要 API Key
服务如果开放到:
http://服务器IP:8000
就应该加鉴权,避免任意用户直接调用。
vLLM 使用:
--api-key <KEY>
开启 API Key 验证。
11.2 OpenSSL 是什么
OpenSSL 是一个常用的密码学和 TLS 工具集,常见能力包括:
生成随机数
计算哈希
生成密钥
处理证书
TLS / HTTPS
本次部署只用到了它的:
安全随机数生成
命令:
# openssl:
# 调用 OpenSSL
#
# rand:
# 生成密码学安全随机数据
#
# -hex:
# 以十六进制字符串输出
#
# 32:
# 生成 32 字节随机数据
# 32 字节 = 256 bit
openssl rand -hex 32
由于:
1 字节 = 2 个十六进制字符
所以会得到:
64 个十六进制字符
这里的 OpenSSL:
只是用来生成一个难以猜测的 API Key,并不是在给模型本身加密。
11.3 用环境变量保存 API Key
# 把 API Key 保存到当前 Shell 的环境变量
export VLLM_API_KEY='你的API_KEY'
检查长度:
# 只查看长度,不直接打印 Key
echo ${#VLLM_API_KEY}
注意:
export
通常只对当前 Shell 和它启动的子进程生效。
重新 SSH 或新开终端后,可能需要重新设置。
11.4 Bearer 是什么
请求中:
-H "Authorization: Bearer $VLLM_API_KEY"
可以拆成:
Authorization
→ HTTP 身份认证请求头
Bearer
→ 使用 Token 作为凭证的认证格式
$VLLM_API_KEY
→ 真正的 API Key
例如:
Authorization: Bearer abc123
其中 Bearer 不是 API Key 的一部分。
12. 最终部署命令
12.1 教学版
docker run -d \
\
# 容器名称
--name qwen38-int4 \
\
# Docker / 服务器重启后自动恢复
--restart unless-stopped \
\
# 使用 NVIDIA Container Runtime
--runtime=nvidia \
\
# 让容器看到全部 NVIDIA GPU
-e NVIDIA_VISIBLE_DEVICES=all \
\
# vLLM 临时目录
-e TMPDIR=/tmp/vllm \
\
# 使用宿主机 IPC;
# 多 GPU / PyTorch multiprocessing / NCCL 更友好
--ipc=host \
\
# 宿主机 8000 → 容器 8000
-p 8000:8000 \
\
# 模型目录只读挂载
-v /data/qwen/models:/models:ro \
\
# 缓存放到 /data,避免继续占根分区
-v /data/qwen/vllm-cache:/root/.cache \
\
# 临时目录放到 /data
-v /data/qwen/tmp:/tmp/vllm \
\
# vLLM 官方 OpenAI Server 镜像
vllm/vllm-openai:latest \
\
# 实际加载的模型路径
/models/Qwen3.8-27B-AWQ-INT4 \
\
# API 对外暴露的模型 ID
--served-model-name qwen38 \
\
# 两张 GPU 共同运行同一个模型
--tensor-parallel-size 2 \
\
# vLLM 规划使用约 96% GPU 显存
--gpu-memory-utilization 0.96 \
\
# 单条 sequence 最大上下文
# 114688 = 112 × 1024 = 112K
--max-model-len 114688 \
\
# 最大活跃 sequence 数
--max-num-seqs 4 \
\
# 强制 eager mode
# 更偏稳定和兼容,可能牺牲部分速度
--enforce-eager \
\
# 解析 Qwen reasoning / thinking
--reasoning-parser qwen3 \
\
# 开启 API Key 鉴权
--api-key "$VLLM_API_KEY"
12.2 一行版
docker run -d --name qwen38-int4 --restart unless-stopped --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=all -e TMPDIR=/tmp/vllm --ipc=host -p 8000:8000 -v /data/qwen/models:/models:ro -v /data/qwen/vllm-cache:/root/.cache -v /data/qwen/tmp:/tmp/vllm vllm/vllm-openai:latest /models/Qwen3.8-27B-AWQ-INT4 --served-model-name qwen38 --tensor-parallel-size 2 --gpu-memory-utilization 0.96 --max-model-len 114688 --max-num-seqs 4 --enforce-eager --reasoning-parser qwen3 --api-key "$VLLM_API_KEY"
为了支持图片,最终命令中没有加入
--language-model-only。
13. vLLM 核心参数
--served-model-name qwen38
这是 API 对外显示的模型 ID。
请求中:
{
"model": "qwen38"
}
必须匹配。
--tensor-parallel-size 2
一个模型
↓
拆到 2 张 GPU
↓
两张卡协同完成推理
--gpu-memory-utilization 0.96
表示 vLLM 在规划显存时,目标使用比例约 96%。
它会综合考虑:
模型权重
KV Cache
CUDA Runtime
Vision 模块
工作空间
--max-model-len 114688
控制单条 sequence 的最大上下文。
这里:
114688 = 112K
上下文包含:
System Prompt
历史对话
当前输入
图片视觉 token
输出 token
--max-num-seqs 4
最多允许 4 条活跃 sequence 参与调度。
不代表 4 条请求都可以同时各自占满 112K,因为它们共享有限 KV Cache。
--enforce-eager
强制 eager mode。
优点:
兼容性较好
显存行为更直观
缺点:
可能比 CUDA Graph 路径慢
为什么没有 --language-model-only
如果加入:
--language-model-only
会:
关闭视觉模块
↓
不能传图片
↓
节省显存
本次需要多模态,所以没有加。
14. 启动后检查
查看容器:
# 查看 qwen 容器
docker ps -a | grep qwen
查看日志:
# 实时日志
docker logs -f qwen38-int4
最近 100 行:
# 查看最近 100 行
docker logs --tail 100 qwen38-int4
查看状态:
# 查看是否 running、是否反复重启、退出码
docker inspect qwen38-int4 \
--format 'Status={{.State.Status}} Restarting={{.State.Restarting}} RestartCount={{.RestartCount}} ExitCode={{.State.ExitCode}}'
查看真实启动参数:
# 查看当前容器实际使用的全部 vLLM 参数
docker inspect qwen38-int4 --format '{{json .Args}}'
查看 GPU:
# 每秒刷新一次
watch -n 1 nvidia-smi
15. 查询真正的模型 ID
不要猜模型名。
# 查询 vLLM 当前注册的模型 ID
curl http://127.0.0.1:8000/v1/models \
-H "Authorization: Bearer $VLLM_API_KEY"
返回中:
{
"data": [
{
"id": "qwen38"
}
]
}
这里的 id 才是 API 请求应使用的模型名。
16. curl 纯文本测试
# time:
# 统计整个请求耗时
#
# curl:
# 命令行 HTTP 客户端
#
# -H:
# 添加 HTTP Header
#
# -d:
# 发送 JSON 请求体
time curl http://127.0.0.1:8000/v1/chat/completions \
-H "Authorization: Bearer $VLLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model":"qwen38",
"messages":[
{
"role":"user",
"content":"1+1等于多少?只回答结果"
}
],
"max_tokens":20
}'
max_tokens 只控制本次最多生成多少 token,不等于 max-model-len。
17. curl 图片多模态测试
17.1 Base64 是什么
JSON 是文本格式,不能直接把 JPG 二进制原样放进去。
所以常见做法:
JPG / PNG
↓
Base64
↓
文本字符串
↓
放进 JSON
17.2 转换图片
假设:
/data/qwen/test.jpg
执行:
# base64:
# 把二进制图片编码成 Base64 文本
#
# -w0:
# 不自动换行
#
# IMG=:
# 把结果保存到 Shell 变量 IMG
IMG=$(base64 -w0 /data/qwen/test.jpg)
17.3 发送图片
curl http://127.0.0.1:8000/v1/chat/completions \
-H "Authorization: Bearer $VLLM_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"model\": \"qwen38\",
\"messages\": [
{
\"role\": \"user\",
\"content\": [
{
\"type\": \"text\",
\"text\": \"请描述这张图片中的内容\"
},
{
\"type\": \"image_url\",
\"image_url\": {
\"url\": \"data:image/jpeg;base64,$IMG\"
}
}
]
}
],
\"max_tokens\": 512
}"
本次实际测试已经成功识别图片。
18. Cherry Studio 接入
Base URL:
http://服务器IP:8000/v1
例如:
http://192.168.10.102:8000/v1
配置:
API 地址:
http://服务器IP:8000/v1
API Key:
与 --api-key 一致
模型:
使用 /v1/models 返回的 id
如果 curl 图片正常但 Cherry Studio 不能上传图片,优先检查客户端是否识别该模型的 Vision 能力。
19. 上下文、KV Cache 和并发
19.1 上下文
上下文就是模型当前需要保留和处理的 token 总体。
包括:
系统提示词
历史聊天
当前输入
代码
图片视觉 token
输出
19.2 KV Cache
Transformer Attention 会产生:
K = Key
V = Value
为了避免每生成一个 token 都重新计算全部历史,模型会把历史 K/V 缓存在 GPU 中。
这就是:
KV Cache
可以把显存粗略理解为:
GPU 显存
├── 模型权重
├── CUDA / vLLM Runtime
├── Vision 模块
├── 工作空间
└── KV Cache
19.3 为什么长上下文更吃显存
token 越多
↓
需要保存的 K/V 越多
↓
KV Cache 越大
↓
显存越紧张
19.4 为什么并发也吃 KV Cache
每条活跃 sequence 都有自己的 K/V 状态。
所以:
长上下文
+
高并发
会同时竞争显存。
20. 为什么 256K、128K 失败,而 112K 成功
256K
262144 tokens
当时日志显示:
所需 KV Cache ≈ 8.09 GiB
可用 KV Cache ≈ 3.7 GiB
所以失败。
128K
131072 tokens
日志显示:
所需 KV Cache ≈ 4.09 GiB
可用 KV Cache ≈ 3.7 GiB
估计最大长度 ≈ 118384 tokens
所以:
131072 > 118384
仍然失败。
112K
114688 tokens
而:
114688 < 118384
因此成功启动。
21. 为什么图片模式更慢
纯文本:
Text
↓
Tokenizer
↓
LLM
↓
Output
图片:
Image
↓
Decode / Resize / Processor
↓
Vision Encoder
↓
视觉特征
↓
LLM
↓
Reasoning
↓
Output
图片比纯文本多了一整段视觉编码,所以更慢是正常现象。
另外:
--reasoning-parser qwen3
意味着模型可能先生成 reasoning token,再生成最终答案。
如果客户端没有及时显示 reasoning,体感上会像“转了很久”。
22. 常见错误
Unauthorized
表示 API Key 不匹配。
# 查看当前 Shell 是否存在 API Key
echo ${#VLLM_API_KEY}
重新设置:
export VLLM_API_KEY='你的API_KEY'
model does not exist
表示鉴权已经通过,但模型 ID 不对。
# 查询真实模型 ID
curl http://127.0.0.1:8000/v1/models \
-H "Authorization: Bearer $VLLM_API_KEY"
KV Cache 不足
典型:
KV cache is needed
larger than available KV cache memory
优先降低:
--max-model-len
Connection reset by peer
检查:
# 看容器状态
docker ps -a | grep qwen
# 看错误日志
docker logs --tail 100 qwen38-int4
图片不能上传
# 查看真实启动参数
docker inspect qwen38-int4 --format '{{json .Args}}'
确认没有:
--language-model-only
然后先用 curl 验证图片后端,再排查 Cherry Studio。
23. 常用 Docker 运维命令
# 查看容器
docker ps -a | grep qwen
# 停止,不删除
docker stop qwen38-int4
# 启动已有容器
docker start qwen38-int4
# 重启
docker restart qwen38-int4
# 强制停止并删除容器
# 不会删除 /data/qwen/models 中的模型文件
docker rm -f qwen38-int4
只有修改这些启动参数时,才需要重新 docker run:
模型路径
端口
上下文
并发
Tensor Parallel
Volume
API Key
served-model-name
24. vLLM、Ollama、ONNX 的选择建议
| 项目 | vLLM | Ollama | ONNX Runtime |
|---|---|---|---|
| 主要定位 | 大模型服务器 | 本地模型运行 | 通用模型执行 |
| 上手难度 | 中高 | 低 | 中高 |
| 多 GPU LLM | 强 | 封装更多 | 取决于实现 |
| 高并发 | 强 | 一般 | 取决于应用 |
| OpenAI API | 核心能力 | 支持兼容接口 | 一般自己实现 |
| 模型管理 | 自己管理 | 很方便 | 自己管理 |
| C++ 集成 | 非主要用途 | 非主要用途 | 很适合 |
| 长上下文调参 | 灵活 | 封装更多 | 取决于实现 |
| 适合本次 | 是 | 不优先 | 不优先 |
可以直接记:
个人快速跑模型
→ Ollama
GPU 服务器模型服务
→ vLLM
软件内嵌模型
→ ONNX Runtime
25. 后续性能优化方向
模型稳定运行以后,再研究性能。
重要指标:
TTFT = Time To First Token
首 token 延迟
TPS = Tokens Per Second
每秒生成 token 数
建议分别对比:
上下文:
32K
64K
112K
并发:
1
2
4
一次只改一个变量。
后续还可以研究:
FP8 KV Cache
Prefix Cache
CUDA Graph
移除 --enforce-eager
Nginx / Caddy
HTTPS
监控
Benchmark
26. 本次最终配置
模型:
Qwen3.8-27B-AWQ-INT4
GPU:
2 × RTX A4000 16GB
推理框架:
vLLM
部署方式:
Docker
Tensor Parallel:
2
GPU Memory Utilization:
0.96
最大上下文:
114688 tokens
= 112K
最大活跃 sequence:
4
多模态:
开启
Reasoning Parser:
qwen3
Eager:
开启
API:
OpenAI-Compatible API
端口:
8000
最终链路:
2 × RTX A4000
↓
NVIDIA Container Runtime
↓
Docker
↓
vLLM
↓
Qwen3.8-27B-AWQ-INT4
↓
112K Context
+ 4 Active Sequences
+ Vision
↓
OpenAI-Compatible API
↓
Cherry Studio / curl / SDK
27. 最后应掌握的核心术语
Docker
→ 容器运行环境
vLLM
→ 大模型服务端推理框架
Ollama
→ 本地模型管理与运行工具
ONNX Runtime
→ 通用模型执行运行时
27B
→ 参数规模
AWQ
→ 权重量化方案
INT4
→ 4-bit 权重量化
Tensor Parallel
→ 多 GPU 共同运行一个模型
KV Cache
→ 保存历史 Attention K/V 的显存缓存
max-model-len
→ 单条 sequence 最大上下文
max-num-seqs
→ 最大活跃 sequence 数
served-model-name
→ API 对外暴露的模型 ID
OpenSSL
→ 本次用于生成安全随机 API Key
Bearer
→ HTTP Token 鉴权格式
Base64
→ 把图片二进制编码成文本以便放进 JSON
Cherry Studio
→ 客户端,真正推理发生在服务器 vLLM