1. 文档范围
本文记录在一台 Ubuntu 20.04.6、单张 RTX 3090 24GB 服务器上,使用 llama.cpp 部署 Qwen3.5-35B-A3B-Q3_K_M.gguf 多模态模型的可复现流程。
当前已验证的服务地址:http://192.168.10.104:10000/v1
当前服务提供文本和图像输入,OpenAI 兼容接口可直接被支持 OpenAI API 格式的客户端调用。
2. 已验证环境
| 项目 | 值 |
|---|---|
| 操作系统 | Ubuntu 20.04.6 LTS |
| GPU | NVIDIA GeForce RTX 3090 24GB |
| NVIDIA 驱动 | 535.86.05(部署过程中不升级) |
| CUDA Toolkit | 11.4 |
| CPU | Intel Core i9-12900K |
| llama.cpp | b10835 |
| 模型量化 | Q3_K_M |
| 上下文 | 131072 tokens(128K) |
| 并发 | 1 路 |
| API 端口 | TCP 10000 |
| 图像投影 | mmproj-F16.gguf,放在 CUDA0 |
显存选择依据
模型全量放入 GPU,KV cache 使用 q8_0,Flash Attention 开启,--parallel 1。服务启动后实测显存约 20.2GB,剩余约 4.0GB;128K 上下文和单路请求已经通过文本及真实图片请求验证。实际请求的图像尺寸、批量大小和输出长度会改变峰值显存,因此生产环境仍应保留降级方案:优先把上下文改为 96K,再改为 64K;同时保持 --parallel 1。
3. 目录约定
bash
PROJECT=/home/xiaoying/project/qwen_vllm
MODEL_DIR=$PROJECT/models/Qwen3.5-35B-A3B-GGUF
LLAMA_DIR=$PROJECT/llama.cpp-b10835-source
BUILD_DIR=$LLAMA_DIR/build-native
最终模型文件:
text
$MODEL_DIR/Qwen3.5-35B-A3B-Q3_K_M.gguf
$MODEL_DIR/mmproj-F16.gguf
4. 下载模型(国内网络)
优先使用 ModelScope。仓库名以实际发布页面为准;部署时使用的目录名为 Qwen3.5-35B-A3B-GGUF。
bash
cd /home/xiaoying/project/qwen_vllm
python3 -m pip install -U modelscope
modelscope download \
--model Qwen/Qwen3.5-35B-A3B-GGUF \
--include 'Qwen3.5-35B-A3B-Q3_K_M.gguf' 'mmproj-F16.gguf' \
--local_dir models/Qwen3.5-35B-A3B-GGUF
如果 ModelScope 页面更换了仓库命名,使用同一模型发布页下载这两个文件,再放到上述目录。不要跳过哈希校验,也不要用同名但来源不明的文件替换。
备用镜像方式(仅在网络策略允许时):
bash
export HF_ENDPOINT=https://hf-mirror.com
5. 文件校验
部署所用文件的 SHA-256:
text
Qwen3.5-35B-A3B-Q3_K_M.gguf
5607c8fcc8b04ada7d1a1152b9a5b6c1e67e6768232c16f6b03d9719d5ab1b2d
mmproj-F16.gguf
a516ab92e8240da4734d68352bdfba84c16e830ee40010b8fac80d69c77272ff
校验命令:
bash
cd /home/xiaoying/project/qwen_vllm/models/Qwen3.5-35B-A3B-GGUF
sha256sum Qwen3.5-35B-A3B-Q3_K_M.gguf mmproj-F16.gguf
输出不一致时停止部署并重新下载;不要通过改名或强行启动绕过校验。
6. 编译 llama.cpp
编译时使用服务器已有 CUDA 11.4,不修改 NVIDIA 驱动。RTX 3090 属于 Ampere,CUDA 架构参数使用 86。
bash
cd /home/xiaoying/project/qwen_vllm
git clone --depth 1 --branch b10835 https://github.com/ggml-org/llama.cpp.git llama.cpp-b10835-source
cd llama.cpp-b10835-source
cmake -S . -B build-native \
-DGGML_CUDA=ON \
-DCMAKE_CUDA_ARCHITECTURES=86 \
-DCMAKE_BUILD_TYPE=Release
cmake --build build-native --config Release -j$(nproc)
如果服务器没有可用的 GitHub 连接,可在可联网机器下载 b10835 源码压缩包后传入服务器;源码版本必须保持 b10835。编译完成后确认:
bash
/home/xiaoying/project/qwen_vllm/llama.cpp-b10835-source/build-native/bin/llama-server --version
若 CMake 报 CUDA 架构或编译器错误,先确认 nvidia-smi、nvcc --version 和 cmake --version,不要安装或升级驱动来"解决"编译问题。
7. systemd 服务
文件:/etc/systemd/system/qwen35-llama.service
ini
[Unit]
Description=Qwen3.5 35B A3B llama.cpp API server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=xiaoying
Group=xiaoying
WorkingDirectory=/home/xiaoying/project/qwen_vllm/llama.cpp-b10835-source/build-native/bin
Environment=CUDA_VISIBLE_DEVICES=0
Environment=LD_LIBRARY_PATH=/home/xiaoying/project/qwen_vllm/llama.cpp-b10835-source/build-native/bin
ExecStart=/home/xiaoying/project/qwen_vllm/llama.cpp-b10835-source/build-native/bin/llama-server --model /home/xiaoying/project/qwen_vllm/models/Qwen3.5-35B-A3B-GGUF/Qwen3.5-35B-A3B-Q3_K_M.gguf --mmproj /home/xiaoying/project/qwen_vllm/models/Qwen3.5-35B-A3B-GGUF/mmproj-F16.gguf --mmproj-device CUDA0 --alias qwen3.5-35b-a3b --host 0.0.0.0 --port 10000 --ctx-size 131072 --parallel 1 --gpu-layers all --flash-attn on --cache-type-k q8_0 --cache-type-v q8_0 --batch-size 512 --ubatch-size 256 --threads 16 --threads-batch 32 --reasoning off --no-ui --metrics
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target
应用配置并启动:
bash
sudo systemctl daemon-reload
sudo systemctl enable --now qwen35-llama.service
本机当前要求为"关闭开机自动启动"时,使用:
bash
sudo systemctl disable qwen35-llama.service
sudo systemctl start qwen35-llama.service
这会保持当前手动启动的服务运行,但重启机器后不会自动启动。
8. 防火墙与服务管理
bash
sudo ufw allow 10000/tcp
sudo ufw delete allow 8000/tcp # 仅当旧规则仍存在时执行
sudo ufw status numbered
bash
sudo systemctl start qwen35-llama.service
sudo systemctl stop qwen35-llama.service
sudo systemctl restart qwen35-llama.service
systemctl --no-pager --full status qwen35-llama.service
journalctl -u qwen35-llama.service -n 200 --no-pager
9. 健康检查与 OpenAI 兼容接口
bash
curl http://192.168.10.104:10000/health
curl http://192.168.10.104:10000/v1/models
文本请求:
bash
curl http://192.168.10.104:10000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "qwen3.5-35b-a3b",
"messages": [{"role": "user", "content": "用一句话说明你已启动。"}],
"temperature": 0.2,
"max_tokens": 256
}'
图像请求(OpenAI 多模态格式,图片内容使用 data URL):
python3
import base64, json, requests
path = "./demo.jpg"
with open(path, "rb") as f:
data = base64.b64encode(f.read()).decode()
payload = {
"model": "qwen3.5-35b-a3b",
"messages": [{"role": "user", "content": [
{"type": "text", "text": "请描述这张图片。"},
{"type": "image_url", "image_url": {
"url": "data:image/jpeg;base64," + data
}}
]}],
"max_tokens": 256
}
r = requests.post(
"http://192.168.10.104:10000/v1/chat/completions",
json=payload, timeout=180)
r.raise_for_status()
print(r.json())
10. 故障排查
服务无法启动
bash
systemctl status qwen35-llama.service
journalctl -u qwen35-llama.service -b --no-pager
nvidia-smi
ss -lntp | grep 10000
重点确认模型路径、mmproj 路径、CUDA_VISIBLE_DEVICES=0、端口是否被占用,以及当前用户是否能读取模型文件。
OOM 或图片请求失败
- 确认只有一个服务实例占用 GPU:
pgrep -af llama-server。 - 保持
--parallel 1,先把--ctx-size 131072降为98304,仍不足再降到65536。 - 保持 KV cache 为
q8_0;不要在 24GB 卡上盲目增加并发或 batch。 - 图片编码有额外显存峰值,优先缩小图片分辨率并重试。
SIGILL / 非法指令
确认 CPU 与编译目标匹配;可移除过度激进的本机 CPU 优化后重新编译。GPU 侧不要把 RTX 3090 的 CUDA 架构写成 89 或 90。
CMake / CUDA 版本问题
本部署依赖现有驱动 535.86.05 和 CUDA 11.4。不要为了安装新版 Python 包或 CUDA Toolkit 自动升级驱动;先检查 which nvcc、nvcc --version、cmake --version,并确保编译和运行使用同一套 llama.cpp 构建产物。
首字慢
首次启动或首次出现新输入形状时,CUDA kernel/图像编码可能需要预热。保持 --parallel 1、--reasoning off,先发送短文本请求预热;实测稳定后的短文本请求首字约 0.54 秒,生成约 98 token/s,真实图片请求编码约 3.1 秒。
11. 当前验收结果
GET /health返回{"status":"ok"}。/v1/models显示multimodal,上下文为131072。- 文本请求成功。
- 真实图片 Base64 请求成功并返回正确图片描述。
- 端口 10000 已放行,旧端口 8000 规则已删除。
- systemd 服务当前可运行,但已执行
disable,不随系统开机自动启动。