
Qwen-7B 和 Qwen-VL 这两个模型一个管纯文本一个管图文放在阿里云上跑起来并不难难的是把显存、并发和账单同时按住。我最近把这两套服务从零搭了一遍中间踩了 CUDA 版本、vLLM 启动参数、多模态图片路径、并发锁这几个坑。下面把完整的部署路径、显存计算过程和参数选择逻辑摊开讲适合手里有一张 24GB 卡、想在云上快速验证 Qwen 系列模型或者准备把多模态能力接进业务系统的朋友。纯文本部分我会用 vLLM 起 OpenAI 兼容服务多模态部分用 FastAPI 包一层两个环境分开避免依赖打架。整套流程不需要你在本地有一台像样的机器租一台云上 GPU 实例按小时算成本验证完就释放试错代价比想象中低。1. 先想清楚两个模型为什么要分开部署1.1 纯文本和多模态是两种完全不同的负载Qwen-7B 的请求链路很短文本进、文本出瓶颈在 token 生成速度和并发序列数。vLLM 这类推理框架能把多个请求的 KV Cache 分页管理连续批处理一开吞吐能翻上去。Qwen-VL 不一样它多了一个视觉编码器每张图要先过 ViT 变成图像 token再和文本 token 拼在一起送进语言模型。图片解码、缩放、编码这几步会吃 CPU 和显存而且单次请求的耗时波动很大一张大图和一张小图的处理时间能差出好几倍。我一开始图省事想把两个模型塞进同一张 24GB 卡里结果 Qwen-7B 刚把 KV Cache 预分配完Qwen-VL 加载视觉权重时直接 OOM。后来改成两个独立实例Qwen-7B 用 vLLM 跑高并发Qwen-VL 用单并发队列跑图文请求稳定性立刻上来了。这个取舍看起来多花了一份实例钱但省掉了大量互相挤显存、互相拖慢的排查时间。小规模验证阶段可以分时切换生产环境建议物理隔离至少也要分两张卡。1.2 显存账先算再买卡别先买卡再试云上 GPU 实例的规格很多24GB 的 A10、16GB 的 T4、32GB 的 V100、48GB 的 A40价格差得不少。买之前先把权重、KV Cache、激活值三块账算清楚比开一台机器上去撞 OOM 高效得多。权重部分好算7B 模型 FP16 大约 14GBINT8 大约 7GBINT4 大约 4GBQwen-VL 因为多了视觉编码器FP16 权重大约 19GB。KV Cache 按公式估算2 × 层数 × 隐藏维度 × 序列长度 × 批大小 × 字节数。Qwen-7B 是 32 层、隐藏维度 4096FP16 下每个 token 的 KV Cache 约 0.5MB8192 token 就是 4GB批大小 8 就是 32GB这还没算激活值。所以 24GB 卡跑 Qwen-7B FP16比较稳的配置是max-model-len4096、并发 8 左右或者max-model-len8192、并发 2 到 4。Qwen-VL 的 19GB 权重加载完只剩 5GB 左右图片编码和生成阶段的激活值一挤24GB 卡就是“刚好能跑、不能并发”的状态。我建议 Qwen-VL 至少上 32GB 卡或者用 INT4 量化把权重压到 6GB 左右再谈并发。下面这张表是我实际选规格时用的对照数值是理论估算加实测修正不是厂商宣传页上的理想值。模型与精度权重占用建议最小显存24GB 卡可行性说明Qwen-7B FP16约 14GB20GB可跑并发受限4096 长度、并发 8 以内比较稳Qwen-7B INT8约 7GB12GB轻松适合 16GB 卡质量损失小Qwen-7B INT4约 4GB8GB很轻松适合高并发或小卡需验证效果Qwen-VL FP16约 19GB28GB紧张只能单并发图片编码峰值容易顶到 24GBQwen-VL INT4约 6GB14GB可尝试量化版需要自己验证多模态效果1.3 裸机、容器还是托管服务三种形态怎么选云上部署一般有三条路直接开 GPU 裸机实例、用容器服务编排、或者用平台提供的模型托管服务。裸机最直接SSH 上去装依赖、起进程适合验证阶段出问题好排查但扩容和迁移要自己写脚本。容器适合生产把 CUDA 运行时、Python 依赖、模型路径全打进镜像换机器时拉起容器就能跑缺点是镜像体积大GPU 驱动和容器运行时需要额外配置。托管服务省运维但自定义参数、私有模型、特殊后处理会受限制成本也通常更高。我的做法是验证期用裸机加 conda把启动命令和参数先跑通稳定后把环境打成 Docker 镜像模型文件放对象存储或共享盘容器启动时挂载进去。这样既保留了调试灵活性又能在扩容时快速复制。这里有个细节模型文件不要直接放容器镜像里一个 Qwen-VL 镜像能干到 20GB 以上推送和拉取都痛苦。把权重放共享存储镜像只装代码和依赖启动时挂载/models是更省事的做法。2. 环境准备驱动、CUDA、Python 依赖一次锁死2.1 实例开通与磁盘规划云上开 GPU 实例时系统盘默认通常只有 40GB 到 60GB装完驱动和 CUDA 就剩不了多少。Qwen-7B 权重约 15GBQwen-VL 约 20GB再加上 Python 环境、日志、临时图片我建议系统盘 100GB 起步另外挂一块 200GB 以上的数据盘专门放模型。数据盘挂到/data模型统一放/data/models后面换实例、做快照、迁移都方便。如果你打算两个模型都放一台机器上分时跑数据盘直接给 300GB省得后面扩容。开通时注意安全组默认只放行 SSH 端口不要一上来就把 8000 端口开到公网。验证阶段用 SSH 隧道或者内网跳板机访问生产环境走负载均衡和内网域名。GPU 驱动选择“自动安装”最省事云厂商的公共镜像一般会带 NVIDIA 驱动开好机nvidia-smi能出卡就行。如果镜像没带驱动手动装也简单但要注意内核头文件和编译工具链后面装 flash-attn 时还会用到。2.2 驱动和 CUDA 版本怎么对齐CUDA 版本这事踩一次坑能记一辈子。nvidia-smi右上角显示的CUDA Version是驱动支持的最高运行时版本不是你当前装的 CUDA 版本。PyTorch 装的是 cu121 还是 cu124决定了它依赖的 CUDA 运行时。vLLM 又对 PyTorch 和 CUDA 有要求版本一错启动时报的错能绕好几圈。我一般先用nvidia-smi看驱动版本再决定装哪个 PyTorch 轮子。驱动版本够新的话直接上 cu121 或 cu124 的 PyTorch兼容性最好。装完以后用三行命令验证nvidia-smi看卡python -c import torch; print(torch.__version__, torch.cuda.is_available())看 PyTorch 能不能识别 GPUpython -c import torch; print(torch.version.cuda)看 PyTorch 编译时的 CUDA 版本。三个都对得上再往下走。如果torch.cuda.is_available()返回 False先别怀疑代码八成是驱动版本低于 PyTorch 要求的 CUDA 运行时或者容器启动时没加--gpus all。2.3 两个 conda 环境别混用Qwen-7B 走 vLLMQwen-VL 走 transformers 加 FastAPI这两套依赖对 transformers、tokenizers、numpy 的版本要求经常打架。我试过在同一个环境里装vLLM 能跑Qwen-VL 加载 tokenizer 时报AttributeError反过来也一样。后来干脆建两个 conda 环境一个叫qwen7b一个叫qwenvl互不干扰。环境切换用conda activate启动脚本里写死解释器路径避免 systemd 拉起时找不到环境。基础依赖我一般锁这几个Python 3.10、PyTorch 2.1 以上、transformers 4.32 以上、accelerate、sentencepiece、tiktoken、pillow、fastapi、uvicorn。Qwen-VL 还要加torchvision和modelscope或huggingface-hub用来下载模型。vLLM 环境单独装vllm版本不要追最新选一个明确支持 Qwen 的稳定版比如 0.4.x 到 0.5.x 之间的版本装完先跑python -c import vllm; print(vllm.__version__)确认。conda create -n qwen7b python3.10 -y conda activate qwen7b pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install vllm0.4.3 pip install fastapi uvicorn openai conda create -n qwenvl python3.10 -y conda activate qwenvl pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install transformers4.32.0 accelerate sentencepiece tiktoken pillow pip install fastapi uvicorn python-multipart注意vLLM 和 Qwen-VL 的 transformers 版本要求可能冲突不要为了省空间共用一个环境。磁盘不够就删 pip 缓存别在依赖上妥协。2.4 模型下载与目录规划模型下载有两条路直接从模型仓库拉或者先把权重下到本地再传到云上对象存储。云上实例直连仓库有时速度不稳定尤其是大文件断一次就得重来。我一般用支持断点续传的下载工具或者用云平台的对象存储做中转。目录规划上/data/models/Qwen-7B-Chat和/data/models/Qwen-VL-Chat分开启动参数里写绝对路径不要用相对路径systemd 和容器里相对路径的当前目录经常不是你预期的位置。下载完检查文件完整性至少看config.json、tokenizer.json、model.safetensors这些关键文件在不在文件大小对不对。Qwen-7B-Chat 的权重是分片的少一个分片启动时才会报错提前用du -sh看一眼总大小15GB 左右算正常。Qwen-VL 还要确认视觉编码器的权重文件在别只下了语言模型部分。# 示例用 modelscope 下载支持断点续传 pip install modelscope python - PY from modelscope import snapshot_download snapshot_download(qwen/Qwen-7B-Chat, cache_dir/data/models) snapshot_download(qwen/Qwen-VL-Chat, cache_dir/data/models) PY # 检查目录 du -sh /data/models/Qwen-7B-Chat /data/models/Qwen-VL-Chat ls /data/models/Qwen-7B-Chat | head3. Qwen-7B 服务化vLLM 起服务与参数调优3.1 显存估算与量化方案Qwen-7B 的 FP16 权重约 14GBvLLM 启动时会按gpu-memory-utilization预分配显存默认 0.9也就是 24GB 卡会拿 21.6GB 左右做权重加 KV Cache。权重占 14GB剩下 7GB 左右给 KV Cache 和激活值。按每 token 0.5MB 算7GB 大约能放 14000 个 token 的 KV但这是所有并发序列共享的。你设max-model-len8192、max-num-seqs8理论需求是 8192×8×0.5MB≈32GB明显超了实际 vLLM 按请求动态分配不会一上来全占满但峰值扛不住就会排队甚至 OOM。所以 24GB 卡上我建议两种组合要么max-model-len4096、max-num-seqs8适合短文本问答要么max-model-len8192、max-num-seqs4适合长文档摘要。如果业务对长上下文要求高、并发也高直接上 INT8 或 INT4 量化把权重降到 7GB 或 4GB腾出空间给 KV Cache。量化会带来一点效果损失但 Qwen-7B 在 INT8 下日常问答基本感知不到差别INT4 需要针对业务做一轮抽样验证尤其是数字、代码、专有名词场景。方案权重24GB 卡建议参数适用场景注意点FP1614GB4096 长度、并发 8短文本、高并发长上下文容易挤爆FP1614GB8192 长度、并发 4长文档、低并发峰值显存要留余量INT87GB8192 长度、并发 8平衡型需确认量化权重可用INT44GB8192 长度、并发 16小卡、高并发效果要抽样验证3.2 vLLM 启动命令逐参数解释vLLM 的好处是开箱即用的 OpenAI 兼容接口启动一条命令就能对外提供/v1/chat/completions。下面这条是我在 24GB 卡上跑 Qwen-7B-Chat 的常用配置参数不是抄来的是反复调出来的。--model写绝对路径--served-model-name给模型起个短名字客户端调用时不用带一长串路径--trust-remote-code必须加Qwen 的 tokenizer 和模型代码需要远程加载--dtype float16明确精度避免自动推断出幺蛾子--max-model-len控制上下文长度--gpu-memory-utilization 0.9留 10% 给系统和激活--max-num-seqs控制并发序列数--host 0.0.0.0让容器外能访问--port 8000按需改。conda activate qwen7b python -m vllm.entrypoints.openai.api_server \ --model /data/models/Qwen-7B-Chat \ --served-model-name qwen-7b \ --trust-remote-code \ --dtype float16 \ --max-model-len 4096 \ --gpu-memory-utilization 0.9 \ --max-num-seqs 8 \ --host 0.0.0.0 \ --port 8000启动日志里重点看三行模型加载耗时、KV Cache 块数、可用最大并发。如果日志提示The model weights are not fully loaded或者直接 OOM先把max-model-len砍半再把max-num-seqs降到 4还不行就换 INT8。另一个常见问题是flash-attn编译失败vLLM 会回退到 eager 模式速度慢一些但不影响跑。想强制不用 FlashAttention加--enforce-eager排查阶段很有用能排除掉一类底层报错。3.3 接口联调与流式输出服务起来后先用 curl 探活再用 Python SDK 调。Qwen-7B-Chat 的对话格式由 tokenizer 的 chat template 处理你只要按 OpenAI 的 messages 传就行。注意temperature、top_p、max_tokens这些参数在 vLLM 里都支持repetition_penalty可以通过extra_body传。流式输出对前端体验提升明显尤其是生成长文本时首 token 时间比整段返回快很多。我一般把max_tokens限制在 512 到 1024避免单个请求占住 KV Cache 太久。curl http://127.0.0.1:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen-7b, messages: [{role: user, content: 用三句话解释什么是向量数据库}], temperature: 0.7, max_tokens: 512, stream: false }from openai import OpenAI client OpenAI(base_urlhttp://127.0.0.1:8000/v1, api_keynot-needed) resp client.chat.completions.create( modelqwen-7b, messages[{role: user, content: 写一个 Python 快速排序}], temperature0.2, max_tokens800, ) print(resp.choices[0].message.content)注意如果服务要跨机器访问别偷懒用api_keynot-needed。vLLM 启动时加--api-key客户端带对应的 key至少能挡住扫描器随手调你的接口。3.4 压测与并发上限不压测就不知道真实容量这是我踩过最多次的坑。用hey或者自己写个异步脚本从并发 1 开始每轮加 2观察三个指标首 token 延迟、每秒生成 token 数、GPU 显存峰值。并发到某个点后延迟会突然抬升说明请求开始排队这个点就是当前配置的实际上限。24GB 卡跑 Qwen-7B FP16、4096 长度我实测并发 8 左右还能保持可接受的首 token 延迟再往上排队明显。压测时把max_tokens设成业务典型值不要用 10 个 token 测出来的假高吞吐。另外注意客户端超时vLLM 生成长文本时单个请求可能跑几十秒网关或 SDK 默认超时经常是 60 秒提前调大。如果发现 GPU 利用率上不去、CPU 先满了检查是不是 tokenizer 或网络层成了瓶颈这种情况加并发没用得换更细的实例或优化客户端。4. Qwen-VL 部署多模态请求链路怎么搭4.1 Qwen-VL 的额外依赖与显存构成Qwen-VL 在 Qwen-7B 的语言模型前面加了一个视觉编码器权重总量到 19GB 左右FP16 下 24GB 卡加载完只剩 5GB 左右。这 5GB 要装图片预处理后的像素张量、视觉编码器的中间激活、语言模型的 KV Cache还有生成时的临时缓存。一张 448×448 的图经过 ViT 会变成几百个图像 token多图请求 token 数直接翻倍。所以 Qwen-VL 的并发不能按纯文本的思路估单卡单并发是最稳的起点。依赖上Qwen-VL 需要transformers、accelerate、tiktoken、torchvision、pillow加载模型时trust_remote_codeTrue不能少。官方示例里图片路径用file://前缀这个细节坑了不少人你传http://或者裸路径tokenizer 可能不认报FileNotFoundError。服务端要先把手里的上传文件落盘到临时目录再用绝对路径拼file://传给模型。临时文件用完就删不然跑几天磁盘就满了。4.2 FastAPI 封装与图片上传Qwen-VL 官方没有像 vLLM 那样现成的 OpenAI 兼容服务自己包一层 FastAPI 最直接。接口设计成POST /v1/vision/chat表单里一个图片文件、一个文本问题。收到请求后把图片读到临时目录调用model.chat返回文本。模型加载放在启动阶段全局只加载一次不要每个请求都from_pretrained那样光加载权重就能把显存打爆。import os import uuid import asyncio import threading from fastapi import FastAPI, UploadFile, File, Form from transformers import AutoModelForCausalLM, AutoTokenizer app FastAPI() lock threading.Lock() model_path /data/models/Qwen-VL-Chat tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( model_path, device_mapcuda:0, trust_remote_codeTrue, ).eval() app.get(/health) def health(): return {status: ok} app.post(/v1/vision/chat) async def vision_chat(image: UploadFile File(...), question: str Form(...)): data await image.read() tmp_path f/tmp/{uuid.uuid4().hex}.jpg with open(tmp_path, wb) as f: f.write(data) def infer(): with lock: query tokenizer.from_list_format([ {image: ffile://{tmp_path}}, {text: question}, ]) response, _ model.chat( tokenizer, queryquery, historyNone, max_new_tokens512, ) return response loop asyncio.get_event_loop() try: answer await loop.run_in_executor(None, infer) finally: if os.path.exists(tmp_path): os.remove(tmp_path) return {answer: answer}启动命令用 uvicorn注意--workers 1多 worker 会加载多份模型24GB 卡直接爆。--timeout-keep-alive可以调大一点图片上传和推理耗时比纯文本长。conda activate qwenvl uvicorn app:app --host 0.0.0.0 --port 8001 --workers 1 --timeout-keep-alive 754.3 并发控制与请求队列Qwen-VL 的model.chat不是线程安全的多个请求同时进来轻则输出错乱重则显存峰值叠加直接 OOM。上面的代码用threading.Lock把推理串行化配合run_in_executor避免阻塞 FastAPI 的事件循环。这是最保守也最稳的做法单卡单并发请求排队等。如果你有多张卡可以起多个进程前面用负载均衡分发但每个进程仍然只跑一个模型实例。排队策略要看业务能不能忍。图文问答通常不是高频秒回场景单并发加队列能撑住小流量。如果排队太长优先考虑加卡或换更大显存的实例而不是把锁去掉。我试过去掉锁跑并发 4Qwen-VL 的输出开始串图A 图的回答跑到 B 请求里排查了半天才定位到模型实例共享状态。这个坑写在这里希望你别再踩。注意FastAPI 默认线程池大小有限run_in_executor不指定 executor 时用的是默认线程池。Qwen-VL 推理时间长队列里堆太多请求会占内存入口处加一个简单的信号量限制排队数量超过就返回 429比无限堆积体验好。4.4 效果验证与输入坑服务起来后先用一张清晰的商品图或文档截图测问题问具体一点比如“图里有哪些文字”“这个物体的颜色是什么”。Qwen-VL 对中文场景支持不错但图片质量影响很大。上传前用 Pillow 做两件事转 RGB 模式处理 EXIF 旋转信息。手机拍的图经常带旋转标记不处理的话模型看到的图是躺着的回答自然不对。超大图先缩到长边 1024 左右既省带宽也省显存Qwen-VL 内部还会再缩放但没必要让服务端先吃一张 4000×3000 的原图。from PIL import Image, ImageOps img Image.open(tmp_path) img ImageOps.exif_transpose(img).convert(RGB) img.thumbnail((1024, 1024)) img.save(tmp_path, formatJPEG, quality90)另一个坑是临时文件权限和路径。容器里/tmp可能是内存盘大图多了会占内存多实例共享宿主机时临时文件名要带随机串避免互相覆盖。生产环境我一般把临时目录指到数据盘加一个定时清理任务删掉超过一小时的残留文件。5. 上线后的稳定性与成本控制5.1 进程守护与容器化裸机上直接用python -m vllm...起服务SSH 一断进程就没了。验证阶段可以用tmux或nohup稳定后必须交给 systemd 或容器。systemd 的优点是简单写一个 unit 文件Restartalways挂了自动拉起来日志走 journald。容器的好处是环境一致换机器时不用重新装依赖。我一般先写 systemd 跑通再打 Docker 镜像做标准化。systemd 单元里注意Environment指定 conda 环境路径ExecStart用绝对路径的解释器WorkingDirectory设到代码目录。Qwen-VL 的 uvicorn 进程要设--workers 1别被默认值坑了。容器启动时加--gpus all挂载模型目录和临时目录端口映射到宿主机内网地址不要直接映射到公网。[Unit] DescriptionQwen-7B vLLM Service Afternetwork.target [Service] Typesimple Userubuntu WorkingDirectory/data/app EnvironmentPATH/opt/conda/envs/qwen7b/bin:/usr/local/bin:/usr/bin ExecStart/opt/conda/envs/qwen7b/bin/python -m vllm.entrypoints.openai.api_server \ --model /data/models/Qwen-7B-Chat \ --served-model-name qwen-7b \ --trust-remote-code \ --dtype float16 \ --max-model-len 4096 \ --gpu-memory-utilization 0.9 \ --max-num-seqs 8 \ --host 0.0.0.0 \ --port 8000 Restartalways RestartSec10 [Install] WantedBymulti-user.target5.2 日志、监控与告警日志分两层看应用日志和 GPU 指标。vLLM 的日志会打印吞吐、排队长度、KV Cache 使用率Qwen-VL 的 uvicorn 日志能看请求耗时和异常堆栈。GPU 层用nvidia-smi dmon或者云监控看利用率、显存、温度。告警至少设三条显存持续超过 90%、服务健康检查连续失败、P99 延迟超过业务阈值。显存告警能提前发现内存泄漏或并发配置过大健康检查失败能赶在用户投诉前处理。日志轮转别忽略。vLLM 跑一天能写不少日志Qwen-VL 的请求日志加上图片临时文件磁盘很容易满。用 logrotate 按天切、保留 7 天或者容器日志驱动限制大小。我遇到过磁盘满导致模型加载失败的情况排查时先看df -h再看日志能省很多时间。5.3 访问控制与网络安全服务端口不要裸奔在公网。验证阶段用 SSH 隧道把 8000 和 8001 映射到本地生产环境走内网负载均衡安全组只放行负载均衡的流量。vLLM 启动时加--api-keyQwen-VL 在 FastAPI 里加一个 Header 校验中间件至少挡住没有 key 的请求。请求体大小也要限制Qwen-VL 的图片上传接口设一个 10MB 上限防止有人传超大文件把内存打满。如果业务方需要从公网访问前面加一层网关做限流和鉴权不要把模型服务直接暴露。限流按 IP 和按 key 都要有纯文本接口按 QPS 限多模态接口按并发数限因为后者单个请求占用资源多。这些措施不复杂但能避免大部分“服务突然不可用”的事故。5.4 账单优化别让验证环境跑成长期成本云上 GPU 实例按小时计费验证完忘了关一个月账单能吓一跳。我的习惯是验证阶段用按量付费跑完立即释放稳定后再考虑包月或节省计划。离线批处理任务可以用抢占式实例成本低很多但要注意它可能被回收任务要能断点续跑。在线服务不要用抢占式除非你做了多副本和快速故障转移。模型量化是最直接的降本手段。Qwen-7B 从 FP16 降到 INT8权重减半同样一张卡能扛更高并发或者换更小的卡。Qwen-VL 如果业务对精度要求不高INT4 能把 24GB 卡从“勉强跑”变成“能跑一点并发”。另外模型文件放对象存储、实例本地盘只做缓存换实例时不用重新下载几十 GB 权重也能省不少时间成本。优化手段效果适用阶段注意点按量实例及时释放避免闲置计费验证期做好快照和镜像方便重建模型量化降显存、提并发稳定期抽样验证业务效果对象存储放权重换机不重下全阶段首次加载仍有网络耗时弹性伸缩闲时缩容生产期冷启动慢保留最小副本日志轮转防磁盘满全阶段别把日志写到系统盘6. 常见问题排查实录6.1 启动阶段CUDA、依赖和模型加载启动阶段的问题最集中也最好定位因为报错通常很直接。CUDA 版本不匹配看torch.cuda.is_available()vLLM 报AttributeError多半是版本和模型代码不兼容先升级 vLLM 或换稳定版flash-attn编译失败看有没有装nvcc和内核头文件临时加--enforce-eager绕过模型加载 OOM 先降max-model-len和max-num-seqs再考虑量化。Qwen-VL 加载报trust_remote_code相关错误检查参数有没有漏以及 transformers 版本是否过低。模型文件缺失是另一个高频问题。下载中断、分片没下全、路径写错都会在加载时暴露。先ls看目录结构再对比官方仓库的文件列表重点看model.safetensors.index.json里列的分片是不是都在。磁盘满也会导致加载失败df -h一眼就能排除。6.2 运行阶段OOM、超时和图片路径运行阶段 OOM 通常发生在请求峰值表现是服务突然挂掉或者日志里出现CUDA out of memory。先看是不是某个请求的max_tokens设得太大再看并发是不是超了。Qwen-7B 的 KV Cache 会随序列长度增长长文本请求一来显存曲线立刻往上抬。把max_tokens限制住或者对超长请求做截断能解决大部分问题。Qwen-VL 的图片路径报错九成是file://前缀没加或者路径不是绝对路径。容器里还要注意宿主机路径和容器内路径的映射上传文件保存到容器内临时目录传给模型时用容器内路径。请求超时先看网关和 SDK 的超时设置再看模型生成时间图片大、问题复杂、max_new_tokens高都会拉长耗时。流式输出对多模态帮助有限但纯文本接口可以开。6.3 并发阶段锁、队列和输出错乱并发阶段最隐蔽的问题是输出错乱。Qwen-VL 的模型实例共享状态不加锁并发调用会出现 A 请求拿到 B 请求的图片描述。排查时先看日志里的请求 ID 和图片路径能不能对上对不上就是并发串了。解决方式就是串行化单卡单并发前面加队列。vLLM 本身对并发处理是安全的不需要额外加锁但要注意max-num-seqs和max-model-len的组合排队太长时客户端会超时。另一个并发坑是 tokenizer 的线程安全。Qwen-VL 的 tokenizer 在多线程下偶尔会出问题和模型一起放在锁里最稳。如果吞吐实在不够加卡多副本每个副本独立进程、独立端口前面用负载均衡分发不要在一个进程里开多线程跑同一个模型实例。现象优先排查处理方式启动报 CUDA 不可用驱动版本、PyTorch CUDA 版本对齐驱动和 cu121/cu124 轮子vLLM 启动 OOMmax-model-len、max-num-seqs降长度、降并发、量化Qwen-VL 报 FileNotFoundError图片路径、file://前缀落盘到绝对路径再传请求超时网关超时、max_tokens调大超时、限制生成长度输出串图模型实例并发加锁串行、多副本分流磁盘满日志、临时图片日志轮转、定时清理端口不通安全组、监听地址检查--host和安全组规则最后再分享一个我在实际部署里养成的习惯每次调整启动参数先把变更记在一张纸上包括改了哪个参数、为什么改、改完的显存峰值和延迟变化。Qwen-7B 和 Qwen-VL 的参数互相影响今天为了压延迟调了max-num-seqs明天可能为了长文本调了max-model-len没有记录的话一周后你根本不记得当前配置是怎么来的。云上部署的灵活性是优势但前提是你知道每一次变更带来了什么。