☰
Qwen3.5-35B-A3B本地部署实战:vllm+Docker高效推理方案
2026/10/19 16:28:02 网站建设 项目流程

1. 项目概述与核心需求

1.1 Qwen3.5-35B-A3B 是什么,为什么值得自己部署

先说结论:Qwen3.5-35B-A3B 是一个 35B 总参数量的 MoE(混合专家)架构大语言模型,但每次推理只激活约 3B 参数。这类“大参数、小激活”的模型,在过去两年里基本成了开源界兼顾效果和成本的主流解法。35B 的总参数量保证了模型的知识容量和复杂推理能力,3B 的激活参数量让实际计算量接近一个小模型的水平,也就是说,你用接近 7B~8B 稠密模型的计算成本,跑出了一个能力接近更大稠密模型的效果。

这个标题里信息量其实很大,值得拆开看:

  • Qwen3.5:指模型代际,这一代在指令遵循、Agent 调用、多步推理上有明显增强,属于目前开源阵营里能力靠前的那一批。
  • 35B-A3B:MoE 配置,总计 35B 参数分布在多个专家模块里,单 token 推理只激活 3B 参数对应的专家路径。这个“总参-激活参”比例大概在 11:1,属于典型的“性价比型”架构设计。
  • vllm:目前开源社区使用率最高的 LLM 推理服务框架,核心是 PagedAttention 显存管理和 Continuous Batching,能大幅提升吞吐量。
  • docker 部署:用容器化方式把推理服务跑起来,解决环境依赖、CUDA 版本、多卡协作的迁移问题。

为什么要自己部署,而不是直接调云端 API?我见过不少团队从 API 迁移到自部署,核心原因就三个:一是数据敏感,模型必须跑在自己的机器上;二是长期调用量太大,自部署的边际成本远低于按 token 计费;三是对延迟和并发有精细控制的需求——比如需要动态调整并发数、需要和内部 RAG 系统深度耦合。如果你也有这三类诉求,这篇文章大概率能帮你省下几天的踩坑时间。

1.2 部署方案选型:vllm 为什么是默认选择

如果只是把模型跑起来,其实有很多选择:Hugging Face 的 TGI、TensorRT-LLM、llama.cpp,甚至直接用 transformers 库写个简单的推理脚本。但 vllm 几乎成了中大规模部署的默认选项,原因很实际:

  1. 吞吐量优势:vllm 的 Continuous Batching 允许 GPU 在一个批次内同时处理不同请求的不同阶段(有的在做 prefill,有的在做 decode),显存利用率和工作节奏都比传统静态批处理高很多。我实测过同一个模型、同一个硬件上,vllm 的吞吐量常常是 naive 脚本的 3~5 倍。
  2. MoE 模型支持成熟:Qwen3.5-A3B 这类模型依赖“只激活部分专家”的稀疏计算,vllm 对 MoE 的 Kernel 层优化相对到位,包括专家并行和显存布局优化。
  3. 接口兼容性:vllm 默认提供 OpenAI 兼容的 HTTP API,意味着你现有的 API 调用代码几乎不用改,只换 base_url 就能接上。
  4. 社区活跃:出新模型,vllm 通常几天内就加支持,不用自己造轮子。

所以在部署技术上没有纠结的必要,直接 vllm + Docker 就是这个项目最合理的组合。

2. 硬件需求测算:买卡之前先把账算清楚

2.1 显存占用是怎么算出来的

很多人第一步就问要什么显卡,其实这个可以自己算。

模型权重占用是最直观的部分。35B 参数,如果用 bf16 精度存储,每 10 亿参数大约占 2GB 显存,35B 就是 70GB。如果用 FP8 量化,每 10 亿参数约 1GB,35B 就是 35GB。INT4 量化的话,大约 0.5GB 每 10 亿参数,35B 约 18GB。

但这只是权重,推理时还有一个大头:KV Cache。

KV Cache 是推理过程中缓存的键值对,用来避免重复计算历史 token 的注意力。它的占用可以这样粗算:

单 token 的 KV Cache 大小 = 隐藏层维度 × 层数 × 4(K和V各两个张量)× 字节数

Qwen3.5-35B-A3B 这类模型的架构参数大概是:隐藏层维度 2048 左右、层数 48 左右(实际值以官方 config.json 为准)。粗略算一下:

单 token KV = 2048 × 48 × 4 × 2(bf16)= 786,432 字节 ≈ 0.75MB

如果 max_model_len 设为 32K,同时并发 16 个请求,那峰值 KV Cache 占用大约是:

0.75MB × 32768 × 16 ≈ 384GB

这个数字看起来很大,但 vllm 的 PagedAttention 是按页分配,不是一次性为所有并发请求预分配满额显存,所以实际占用是动态的,但容量规划时仍要按峰值场景预留。

算到这里就很清楚了:

  • 如果跑 bf16,70GB 权重加 KV Cache,单卡 80GB 的卡勉强能跑,但并发和序列长度都很受限。
  • 如果跑 FP8,35GB 权重,单卡 48GB 或 80GB 会比较舒服。
  • 如果跑 INT4(AWQ/GPTQ 量化),18GB 权重,理论上单卡 4090(24GB)也能跑,但要严格控制并发数。

我的建议:如果是正式项目,至少准备一张 48GB 以上的卡,或者两张 24GB 卡组合。如果只是个人学习体验,一张 4090 配合 AWQ 量化也是可行的方案,只是别对并发吞吐抱太高期望。

2.2 不同场景的硬件选型参考

场景推荐配置实际能做什么
个人学习/原型验证单张 4090(24GB)+ AWQ INT4 量化序列长度 8K 以内,并发 1~4,跑通功能
团队内部工具单张 A100 80G 或两张 4090 组合bf16 或 FP8,并发 8~16,序列 32K
生产服务单张 H100 80G 或多卡 A100FP8,并发 32+,长序列,高吞吐

注意多卡组合时,如果卡之间靠 PCIe 互联而非 NVLink,MoE 模型的 all-to-all 通信会比较吃紧,性能可能打折扣。所以能买大显存单卡,就别追求多张小卡,这是 MoE 模型部署的一个特殊点。

提示:MoE 模型的 Expert 分布在多卡上,每次激活 token 可能需要跨卡访问专家,通信开销占比很高。多卡部署时建议优先让每张卡都装得下完整模型权重,或者用 tensor parallelism 配合 expert parallelism 一起开。

3. Docker 环境准备:镜像选型与容器配置

3.1 NVIDIA Container Toolkit 是第一步

搞 Docker 部署大模型,最基础也最容易忽略的一环是让容器里能访问 GPU。光装 Docker 不够,得装 NVIDIA Container Toolkit。

简单说,这套工具的作用是把宿主的 NVIDIA 驱动透传给容器里的 CUDA 运行时。vllm 官方镜像里打包了 CUDA 环境,但容器里的 CUDA 只是用户态的库,真正的内核态驱动还是靠宿主提供的。

安装步骤其实很固定:

# 以 Debian/Ubuntu 系为例 curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt-get update sudo apt-get install -y nvidia-container-toolkit sudo nvidia-ctk runtime configure --runtime=docker sudo systemctl restart docker

装完以后,可以验证一下:

docker run --rm --gpus all nvidia/cuda:12.0-base nvidia-smi

如果能看到显卡信息,说明容器已经能正常访问 GPU 了。这一步没搞定,后面所有部署都白搭。

3.2 vllm 镜像怎么选

vllm 官方在 Docker Hub 上发布了镜像,思路很简单:尽量用官方镜像,别自己从头搭,除非你对版本有特殊要求。

镜像拉取:

docker pull vllm/vllm-openai:latest

或者指定版本号,建议用带版本号的标签而不是 latest,比如:

docker pull vllm/vllm-openai:v0.10.0

为什么建议锁版本?因为 vllm 迭代非常快,不同版本对模型的支持、量化 kernel 的兼容性都不太一样。锁了版本,团队内复现环境才稳定。

这里要说一个我自己踩过的坑:官方镜像有个特点,默认的 Python 环境、torch 版本和 CUDA 版本都是配套锁死的。如果直接往容器里 pip 安装额外的包,有可能把依赖弄坏,导致启动报错。所以我建议不要往 base 容器里乱塞东西,如果确实需要额外的 Python 依赖,推荐写一个 Dockerfile 基于官方镜像做二次定制。

4. 模型获取与启动前的关键配置

4.1 权重文件怎么拿

Qwen 系列模型一般从 Hugging Face 或 ModelScope 下载。国内网络条件下,ModelScope 通常更快,海外的话 Hugging Face 更顺。

下载方式上,建议用 huggingface_hub 或 modelscope 的 Python SDK,而不是直接浏览器下载,因为模型文件是分片存储的,用工具下载更可靠。

pip install modelscope modelscope download --model Qwen/Qwen3.5-35B-A3B --local_dir /data/models/Qwen3.5-35B-A3B

下载完成后,检查一下目录里有没有这几个关键文件:

  • config.json:模型架构配置,vllm 启动时会读它
  • tokenizer.json / tokenizer_config.json:分词器文件
  • model-00001-of-0000X.safetensors:分片权重
  • generation_config.json:生成参数默认值

如果只是用 transformers 做张量并行推理,这几个文件缺一不可。vllm 启动时也会依赖同一套文件结构,所以目录别乱动。

4.2 关键启动参数逐个讲清楚

vllm 的启动命令看起来简单,其实参数里学问很多。下面这个是我反复调试后的推荐配置:

docker run --gpus all --ipc=host \ -v /data/models/Qwen3.5-35B-A3B:/models \ -p 8000:8000 \ vllm/vllm-openai:v0.10.0 \ --model /models \ --served-model-name qwen35-a3b \ --tensor-parallel-size 1 \ --max-model-len 32768 \ --gpu-memory-utilization 0.92 \ --enforce-eager \ --dtype bfloat16 \ --port 8000

逐个说明:

  • --ipc=host:这是 Docker 参数,不是 vllm 参数。很多教程不会提,但没有它,vllm 在某些启动场景下会因为共享内存不足报错,尤其是多进程数据加载时。建议固定加上。
  • --model /models:指定权重目录。这里用的是容器内路径,宿主路径通过 -v 挂载进来。
  • --served-model-name:对外暴露的模型名。客户端请求 OpenAI 接口时,模型名不对会直接 404 或报错,所以这个名字要统一好。
  • --tensor-parallel-size:张量并行度。单卡就填 1,多卡就填卡数。需要强调的是,tensor parallel 不是越高越好,卡间通信开销可能抵消计算加速。
  • --max-model-len:最大上下文长度。切得越长,能处理的单请求上下文越宽,但 KV Cache 占用越大,并发数必然下降。32K 是我测过的比较平衡的值。
  • --gpu-memory-utilization:vllm 最多用多少比例的显存。默认 0.9,建议留一点余量给 CUDA context 和其他开销。填 0.92 基本是极限了,再高容易触发 OOM。
  • --enforce-eager:关闭 CUDA Graph 捕获。这个参数很多人会纠结,我的建议是:第一次部署、调试阶段务必加上,因为它能避免很多复杂的底层错误;跑稳定了再摘掉它,让 vllm 开启 CUDA Graph 加速,能明显降低单请求延迟。
  • --dtype bfloat16:默认精度。35B 模型权重默认是 bf16 格式,选这个最稳妥。如果你的权重是 FP8 或 INT4 量化过的,这里要对应的改。

启动后,观察日志。vllm 稳定跑起来会打印类似下面的信息:

INFO: Started server process [xxx] INFO: Uvicorn running on http://0.0.0.0:8000 INFO: (VllmWorker) init engine ... INFO: (VllmWorker) init engine took 15.12 seconds

看到 init engine 就说明模型加载和引擎初始化都成功了。

5. 功能验证与接口测试

5.1 用 OpenAI SDK 进行快速验证

服务启动成功只是第一步,必须用一个真实请求验证推理功能正常。vllm 提供的 OpenAI 兼容接口最容易测。

用 curl 直接测:

curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen35-a3b", "messages": [ {"role": "system", "content": "你是一个乐于助人的助手。"}, {"role": "user", "content": "请用一句话解释什么是量子纠缠?"} ], "temperature": 0.7, "max_tokens": 256 }'

不太懂 curl 的,也可以直接写 Python 脚本:

from openai import OpenAI client = OpenAI( base_url="http://localhost:8000/v1", api_key="EMPTY", # vllm 默认不校验 key,随便填 ) resp = client.chat.completions.create( model="qwen35-a3b", messages=[ {"role": "system", "content": "你是资深的技术顾问。"}, {"role": "user", "content": "用比喻解释什么是 MoE 模型?"}, ], temperature=0.7, max_tokens=512, ) print(resp.choices[0].message.content)

如果响应正常,说明推理链路已经通了。

5.2 性能检查不能只看“能跑”

服务能出结果,只能说明功能正确,完全说明不了性能行不行。我建议至少做两层检查。

第一层,看单请求延迟。用上面的 Python 脚本跑个 20 次左右,统计平均首 token 延迟和总延迟。首 token 延迟低于 300ms 基本算健康,总延迟取决于 max_tokens 设多长,但应该在几秒量级。

第二层,看并发吞吐。这里教大家一个没有额外工具的办法,如果机器上有 Python,可以加上 concurrent.futures 写个简单的并发压力脚本:

import time import concurrent.futures from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY") def single_query(idx): start = time.time() resp = client.chat.completions.create( model="qwen35-a3b", messages=[{"role": "user", "content": f"请列出第{idx}个问题的分步解题过程:计算 1234*5678"}], max_tokens=512, ) return time.time() - start with concurrent.futures.ThreadPoolExecutor(max_workers=16) as executor: results = list(executor.map(single_query, range(32))) print(f"平均单请求耗时: {sum(results)/len(results):.2f}s") print(f"总耗时: {sum(results):.2f}s")

这个脚本虽然没有压测工具专业,但能直观看出并发 16 时的服务表现。如果平均耗时明显上升,说明需要调整并发限制或者显存分配。

提示:生产环境做压力测试建议用专业工具,比如带动态并发负载的脚本,或者 grafana + prometheus 监控 vllm 暴露的指标端口。

6. 踩坑实录与调优建议

6.1 常见报错排查速查

部署大模型,报错是常态,关键是能不能快速定位。我把接触到的最高频问题整理成了一张表:

现象可能原因解决方案
启动时报 CUDA out of memorygpu-memory-utilization 设太高,或 max-model-len 太长降低到 0.85,缩短 max-model-len
日志提示 model not found权重目录路径错误或文件不完整检查挂载路径,确认 safetensors 文件齐全
请求时报 model not found / 404served-model-name 和请求中的 model 字段不一致统一模型名
容器内跑 nvidia-smi 提示没有设备NVIDIA Container Toolkit 未正确安装重新配置 runtime,确认 docker info 里能看到 nvidia
启动进程卡在 loading weights 很久权重下载不完整或磁盘 IO 慢检查文件大小,尽量用 SSD
生成内容乱码或语言不对缺少 tokenizer 文件或配置错误检查分词器文件,确认 tokenizer_config.json 存在
并发稍高就 OOMKV Cache 预留不足调低并发上限,缩短 max-model-len,或者启用量化

6.2 几个提升性能的进阶配置

基础跑通以后,如果想再榨一点性能,这几个方向值得研究。

前缀缓存(Prefix Caching)。如果业务场景里有大量的固定系统提示词或固定知识库前缀,开启前缀缓存能显著降低重复计算的延迟。vllm 的启用方式是设置环境变量:

export VLLM_ENABLE_PREFIX_CACHING=1

效果立竿见影:固定 system prompt 的请求,首 token 延迟能下降 40% 以上。

投机解码(Speculative Decoding)。用一个小的草稿模型先预测接下来几个 token,再用大模型验证。对于 A3B 这种激活参数小的模型,投机解码的加速比可能不如大稠密模型那么夸张,但通常也能带来 1.3~2 倍的吞吐提升。配置方式:

--speculative-config '{"model": "/path/to/draft_model", "num_speculative_tokens": 5}'

草稿模型的选择很关键:小模型和主模型分布差距太大,草稿匹配率太低反而拖慢速度。

启用 CUDA Graph。前面提到调试阶段开 --enforce-eager,稳定运行后关掉它,vllm 会默认启用 CUDA Graph。这一步对短 prompt 的延迟优化特别明显,吞吐量大约能提升 20% 左右。

量化选型再考虑一下。如果你用的卡显存紧张,AWQ 或 GPTQ 量化是必须要做的。但量化的精度损失在代码生成、数学推理任务上比较明显。我的经验是:FP8 的损失几乎不可感知,INT4 则要看任务敏感度。如果显存允许,优先用 FP8;只有实在装不下才考虑 INT4。

6.3 MoE 模型部署的特殊注意事项

MoE 模型和稠密模型的部署体验差异,很多人是实际跑起来才发现的。

第一点,显存够但通信瓶颈明显。多卡部署时,MoE 的 token 需要经过 router 分发给不同卡上的专家,all-to-all 通信非常频繁。两张卡之间如果只是 PCIe,性能可能还不如单卡。所以选型时优先考虑 NVLink 互联的卡型,或者干脆单卡大显存跑。

第二点,int8 / fp8 量化对 MoE 效果的影响。MoE 里专家网络对量化噪声的敏感度不一致,有些专家可以压到低 bit,有些不行。好的做法是用 AWQ 这种按激活值感知权重的量化方法,而不是简单的均匀量化。

第三点,路由不均衡问题。某些 token 可能集中涌向同一个专家,导致部分专家过热而其他专家闲置。vllm 有一些负载均衡的调度选项,比如 --worker-cls 配合多卡调度模式,可以让各卡专家负载更均匀,减少通信排队。

第四点,显存碎片问题。MoE 模型的多专家权重段大小不一,经过 weight 加载、量化替换后,显存碎片比稠密模型更严重。vllm 的显存管理对这种场景做了适配,但如果你发现启动后可用显存和预期差距过大,可以尝试打开 vllm 的显存规划日志,看一下具体分配情况。

7. 最终复盘与个人体会

这次把 Qwen3.5-35B-A3B 通过 vllm 和 Docker 完整部署下来,我的整体感受是:这套组合是目前开源 LLM 落地最省心的一条路径,但它对硬件的理解和参数配置的要求,比很多人预想的要高。

我自己踩过最大的坑,是多卡并行时 PCIe 互联的性能损耗。第一次部署时,我在一台双卡机器上开 tensor-parallel-size 2,结果吞吐比单卡还低。排查了很久,才发现问题出在跨卡通信上。后来换了 NVLink 互联的卡,或者干脆改回单卡部署,性能一下子正常了。这个经验让我意识到,MoE 模型的部署不能只盯着“显存够不够”,通信架构同样关键。

部署这件事,看起来就是一条 docker run 命令,但里面牵涉到显存规划、模型加载、并行策略、性能调优多个层面的权衡。一次成功的部署,靠的不是运气,而是对每个参数背后原理的理解。

最后再分享一个小技巧:vllm 启动后,如果要频繁调整参数而不用重新加载模型,可以利用它内置的模型热加载能力,通过 runtime API 更新部分配置。我自己在调试阶段常做法是,先用小 max-model-len 跑通,再加到预期长度,这样遇到 OOM 也能快速隔离是显存问题还是配置问题。如果你也是第一次部署这个模型,建议先控制并发数,跑通流程后再逐步加压,这样排查起来会轻松很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询