新闻详情

新闻详情

首页 / 资讯中心 / 详情

vLLM生产环境混跑对话与向量模型:从踩坑到隔离部署实战

发布时间:2026/9/29 21:13:27来源:尧图网络
vLLM生产环境混跑对话与向量模型:从踩坑到隔离部署实战
最近后台私信里出现得最多的一个问题是vLLM production stack 能不能同时扛住对话模型和向量模型起因也简单团队里既要上 deepseek、qwen 这类对话模型做 Chatbot又要接 qwen3-embedding-0.6b 这种向量模型做 RAG大家第一反应都是“vLLM 反正也支持 embedding那就一个服务全包了呗”。实际动手之后才发现scheduler、显存、镜像版本、路由全都得重新盘一遍——这不是简单地在启动命令后面追加一个模型名就能搞定的事。这篇就是把我在生产环境里的踩坑记录、架构取舍和最终验证过的部署方式写出来。不管你是刚接触 vLLM 的新手还是已经在线上跑对话模型、正准备接向量服务的同学都可以照着后面的命令直接抄作业。1. 不是“多塞一个模型”那么简单两类负载的本质差异1.1 对话模型是“连续剧”自回归推理天然吃状态对话模型也就是我们常说的 LLM推理过程是典型的自回归。一个 prompt 进来之后vLLM 先做 prefill把整段上下文并行算一遍产出的 KV cache 留在显存里之后进入 decode 阶段每生成一个 token 都要基于之前的 KV cache 做一次前向计算然后继续更新 cache。这个特性决定了对话模型对三样东西极度敏感显存容量、内存带宽、推理延迟。KV cache 是动态的prompt 越长、并发越高显存里被 cache 吃掉的量就越大。vLLM 之所以能高性能核心就是 PagedAttention 把 KV cache 分页管理不让碎片浪费显存同时调度器在每一个 step 里挑一批请求一起算最大化 GPU 利用率。生产环境里围绕它调优时--max-model-len、--gpu-memory-utilization、--max-num-seqs几乎人人都会调一遍。1.2 向量模型是“拍快照”一次前向就结束向量模型embedding model完全是另一套逻辑。它不需要生成 token没有 decode 循环也没有 KV cache 的概念。输入一段文本前向计算一次吐出定长向量请求就结束了。qwen3-embedding-0.6b 这类模型尤其典型0.6B 参数量模型不大但是短文本检索场景下同时并发几千个请求都很正常。对话模型请求是“长尾状态”的一个会话可能持续几十秒过程中 GPU 一直被这个请求占着向量模型请求是“短查询突发”的单个请求几毫秒到几十毫秒就结束但量大、来得快。把这两类请求丢进同一个 serving 进程本质是让一个调度器同时处理两种完全不同的资源消耗模式问题在短时间内就会暴露。1.3 混合负载会动摇原来的一切调优基线过去我们把 vLLM 生产栈调优到稳定的状态所有参数都是围绕对话模型定的。比如我给 deepseek 类模型留 80% 显存作为 KV cache因为对话高峰期长上下文的请求很多网关层超时设成 5 分钟因为流式出字慢是可以接受的。一旦向量服务也挤进来情况就变了向量请求数量可能是对话请求的十倍甚至更高它们不占 KV cache但照样占用调度 slot 和 GPU 算力。对话请求在这种混跑环境下会出现首 token 延迟TTFT抖动、排队时间变长而向量请求也会因为对话 prefill 把 GPU 打满而偶尔超时。原来那一套 production stack 的假设前提被打破了必须重新设计。2. 镜像与部署vllm/vllm-openai:v0.27.1 实战记录2.1 镜像选择vllm-openai 与 vllm 的差别很多人第一次看 Docker Hub 会懵vllm/vllm-openai和vllm/vllm到底有什么区别简单说vllm/vllm-openai是带 OpenAI 兼容 API server 的镜像启动后就能直接对外提供/v1/chat/completions、/v1/embeddings这类接口vllm/vllm更接近纯 Python 库镜像适合你自己写调用脚本。生产环境我建议直接用vllm/vllm-openai。原因不是它能少写几行代码而是因为 OpenAI 兼容协议已经成了事实标准chatbox、FastGPT、Dify 这类上层应用接它都不需要额外适配团队里不管开发还是测试都能快速上手。这次部署我锁定的 tag 是v0.27.1拉取命令很简单docker pull vllm/vllm-openai:v0.27.12.2 镜像里不带模型权重模型文件必须自己挂载这一点新手经常误解。镜像里只有 vLLM 引擎、CUDA 依赖和 Python 运行时不包含任何对话模型或者向量模型的权重文件。换句话说你 pull 下来的镜像是同一个但跑 deepseek 还是跑 qwen3-embedding取决于你把哪个模型目录挂进去。我的习惯是把所有模型放在宿主机统一目录然后通过-v挂载给容器/models/ ├── DeepSeek-R1-Distill-Qwen-7B/ │ ├── config.json │ ├── tokenizer.json │ └── model-00001.safetensors └── Qwen3-Embedding-0.6B/ ├── config.json ├── tokenizer.json └── model.safetensors模型下载我用huggingface-cli download国内网络不方便的时候就换modelscope关键是目录结构要完整config.json、tokenizer.json、safetensors 权重这三样缺一不可。缺 tokenizer 的话vLLM 启动时会在加载阶段直接报错。2.3 加载 qwen3-embedding-0.6b 的启动命令向量模型和对话模型的启动命令核心差别在于--task参数。vLLM 支持通过--task embedding让服务以 embedding 模式启动对外暴露/v1/embeddings接口。我这里实际用的命令是这样的docker run -d --name vllm-embed \ --runtime nvidia --gpus all \ -e CUDA_VISIBLE_DEVICES1 \ -v /models:/models \ -p 8001:8001 \ --ipchost \ vllm/vllm-openai:v0.27.1 \ --model /models/Qwen3-Embedding-0.6B \ --task embedding \ --served-model-name qwen3-embed \ --max-model-len 8192 \ --port 8001同时跑对话模型的话我会再起一个容器用--task generate这也是默认值不写也行占用另一张卡docker run -d --name vllm-chat \ --runtime nvidia --gpus all \ -e CUDA_VISIBLE_DEVICES0 \ -v /models:/models \ -p 8000:8000 \ --ipchost \ vllm/vllm-openai:v0.27.1 \ --model /models/DeepSeek-R1-Distill-Qwen-7B \ --served-model-name deepseek-chat \ --max-model-len 32768 \ --gpu-memory-utilization 0.8 \ --trust-remote-code这里有个细节值得强调--served-model-name是给客户端看的模型名可以自定义--model是指向模型文件的实际路径。很多人在 chatbox 或 API 调用时报model not found就是因为只改了--model忘了--served-model-name客户端请求里写的名字和实际暴露的对不上。2.4 版本适配deepseek、qwen3、GLM 系列怎么选 vLLM 镜像关于 GLM 这类新模型到底配哪个 vLLM 版本我的经验是不要看第三方博客只看模型官方仓库的 README 和 vLLM 的 release note。比如模型仓库里写了“inference with vLLM:pip install vllm0.9.0”那你就别用太老的镜像反过来如果 vLLM 新版本改了默认 scheduler 或者 breaking change线上模型不兼容那就得锁回旧版。还有一个通用做法vLLM 启动时加--trust-remote-code因为部分模型的自定义层需要加载远程代码。线上环境不建议对未知模型直接加这个参数但 deepseek、qwen、glm 这类主流模型基本是安全的。镜像 tag 不要用latest生产环境一旦重发、内容不一致很难回溯。锁死v0.27.1这样的精确版本至少能保证“昨天能跑今天 pull 下来也能跑”。3. Scheduler 与资源分配让两类负载在一张卡上和平共处3.1 vLLM Scheduler 到底在干什么vLLM 的调度器是它吞吐量优势的核心。传统推理框架一次只处理一个 batch等这个 batch 里最慢的序列结束才释放 GPUvLLM 则实现了 continuous batching在每个 step 都会重新检查队列里的请求把已经结束的请求踢出去把新请求塞进来尽量让 GPU 一直满负荷运转。调度器维护了几个队列running正在计算的请求、waiting排队等待的请求、swapped被抢占后换出显存的请求。碰到显存不够的时候它会做 preemption把一部分请求的 KV cache 换到 CPU 内存腾出空间给新请求。对话模型长上下文多这一套机制非常关键。--max-num-seqs限制每一步最多同时处理多少个序列--max-num-batched-tokens限制每一步最多批处理多少 token两个参数一起决定了“调度窗口”的大小。3.2 embedding 请求在调度器眼里到底是什么我一开始也以为 embedding 请求不生成 token就不会占用调度资源。实际看了/metrics才明白embedding 请求一样会进入 waiting 队列、分配 sequence slot、参与 batch 组成。区别在于它 prefill 完直接出结果不需要 decode也不会产生 KV cache。这就带来一个隐蔽问题在一个混合了对话和向量模型的 engine 里embedding 请求会占用正常对话请求的调度位置。大量短小的 embedding 请求频繁进出会让调度器花更多精力做 batch 重组对话请求的 TTFT 就会往上飘。我在压测里看到过一个很直观的现象单独跑对话服务时 TTFT P95 是 600ms混跑之后直接到 1.8s原因就是 GPU 时间片被 embedding 抢走了一部分。3.3 三个生产参数max-num-seqs、max-num-batched-tokens、gpu-memory-utilization参数怎么定取决于模型大小和负载类型。下面是基于我实际测试比较稳的一组配置大家可以当起点再调整参数对话服务7B 级向量服务0.6B 级--gpu-memory-utilization0.80 - 0.900.30 - 0.50--max-num-seqs128 - 25632 - 64--max-num-batched-tokens8192 - 163844096--max-model-len32768按需8192对话服务要把大部分显存留给 KV cache因为长上下文场景下 cache 增长很快向量服务完全没有 decode 和 KV cachegpu-memory-utilization太高反而是浪费0.4 左右足够支撑 0.6B 模型的高并发。如果你想在一张卡上同时跑两个服务那对话服务最多留 0.5向量服务留 0.3但我不推荐这么干原因下一节细说。3.4 两张卡分开跑是性价比最高的隔离方式生产环境里最稳的做法就是显存隔离一张卡跑对话另一张卡跑向量。哪怕你的对话模型只用了 40% 显存我也不建议把 embedding 硬塞进来。因为 vLLM 的显存池是按 engine 初始化的同卡混跑时两者各自预留显存看起来互不干扰但在显存紧张的瞬间CUDA OOM 往往会同时拖垮两个服务。最简单的方式就是通过CUDA_VISIBLE_DEVICES指定 GPU 编号效果立竿见影。两张卡如果都是 24G 的 4090 或者 48G 的 L40S7B 对话模型 0.6B 向量模型完全没压力。如果卡资源紧张再考虑同卡混跑但必须做好监控重点盯gpu_cache_usage和gpu_memory_usage两个指标。4. 生产架构一个实例挂两个模型还是拆成两个服务4.1 方案 A单 vLLM 实例多模型听起来很美落地要谨慎vLLM 社区一直在推进单引擎多模型支持理想状态下一条命令把一个对话模型和一个 embedding 模型都加载起来共用端口、共用调度器看起来确实省心。但生产环境里选择这个方案前务必先搞清楚代价多模型共用一个 scheduler、一个显存池意味着一个模型的高峰负载会直接挤压另一个模型的可用资源。对话模型的 prefill 阶段是最吃显存和算力的而 embedding 模型的突发流量恰恰也可能是毫秒级潮涌。两者混在一个调度器里你很难单独为 embedding 设置“最多占用多少比例”的限流。我目前的态度是小规模内网试用可以生产环境不想半夜被 oncall 电话叫醒的话先不要上。4.2 方案 B两个 vLLM 服务 统一网关生产推荐我最终落地的是方案 B对话服务和向量服务各占一个独立容器、独立端口前面加一层网关按请求路径或者model字段做路由。服务容器名端口暴露模型名职责对话服务vllm-chat8000deepseek-chat处理/v1/chat/completionsSSE 流式输出向量服务vllm-embed8001qwen3-embed处理/v1/embeddings批量向量化网关层的核心工作有两个第一把/v1/embeddings请求转发给 8001把/v1/chat/completions转发给 8000第二设置不同的超时策略。对话接口允许最长 5 分钟不返回向量接口如果超过 5 秒还没返回直接 504不要让它拖着连接占资源。这样拆开之后两个服务可以独立扩缩容、独立发布、独立看监控。对话服务高峰要扩到 3 个副本那就只扩对话向量服务对延迟敏感可以放在离存储近的节点上毫无耦合。4.3 能不能换成 Ollama开发环境与生产环境的边界被问最多的问题就是“Ollama 也能跑向量模型为什么不用它”。Ollama 确实把模型管理做得很轻本地开发试模型特别方便。比如ollama pull nomic-embed-text这类向量模型装完后你以为ollama run就能出向量其实不行。Ollama 里要拿向量需要调用它自己的 APIcurl http://localhost:11434/api/embed \ -H Content-Type: application/json \ -d {model:nomic-embed-text,input:hello}如果用ollama run进去那是把模型当作对话来问出来的不是向量的效果。这个 API 本身没问题问题在于 Ollama 面向的是单机开发场景高并发能力、显存分页复用、生产级 metrics 和 vLLM 完全不是一个量级。对比项vLLMOllamaOpenAI 兼容 API原生支持部分支持路径不标准高并发吞吐连续批处理吞吐高单机简单调度并发有限显存分页管理PagedAttention显存利用率高相对简单长文本容易 OOM可观测性/metrics、日志完善弱很多生产部署Docker、K8s 友好适合本地开发结论很明确开发阶段用 Ollama 快速验证模型效果没问题生产环境接 RAG、接对话服务还是 vLLM 更稳。生产环境没有试错的余地一个并发峰值就能把差别打出来。4.4 混合负载对生产栈的实际影响拆成两个服务之后影响范围依然存在只是变得可控了。硬件成本上你要多占一张卡但 0.6B 的向量模型可以跑在单卡上成本不算高。运维成本上多了一个 deployment、多一套日志和监控面板但这些在 K8s 模式下都是可复制的工作。更关键的影响在 SLO 上。拆开之前对话服务的 TTFT 会被向量流量干扰拆开之后两者的 SLO 完全隔离。对话服务可以说“TTFT P95 1s”向量服务可以说“QPS 可扩展”这两个承诺不再互相牵扯。对于要写进 SLA 的团队来说这个架构决策比任何参数调优都重要。5. 核心步骤实录从 Docker 启动到请求验证5.1 准备模型目录与依赖环境先把宿主机目录建好下载模型权重。我以 Qwen3-Embedding-0.6B 为例下载后确认目录里有config.json、tokenizer.json或tokenizer_config.json、权重文件三个核心部分。如果目录不完整vLLM 启动时会报错不要硬着头皮往下走。宿主机需要装好 NVIDIA 驱动和 CUDA 运行环境nvidia-smi能正常输出就行。容器内镜像已经带好 vLLM 依赖你不需要单独在容器里 pip install。5.2 启动对话模型服务这一步假设你要部署 deepseek 系列对话模型命令在 2.3 里已经贴过。启动之后等几十秒到几分钟不等日志里出现Application startup complete就说明服务起来了。这段时间可以做一次健康检查curl http://127.0.0.1:8000/health返回OK就完事了。这里要注意服务起来很慢不代表有问题加载 7B 模型权重到显存本身就需要时间。5.3 启动向量模型服务同样用 2.3 的命令启动端口我用 8001。关键参数一个是--task embedding另一个是--served-model-name qwen3-embed。启动后访问curl http://127.0.0.1:8001/health确认返回OK。如果很不幸报了ValueError: You are using a model with task embedding...之类的问题先检查--task参数是不是写对了。5.4 验证对话接口与 embedding 接口对话接口用 OpenAI 兼容方式验证curl http://127.0.0.1:8000/v1/chat/completions \ -H Content-Type: application/json \ -d {model:deepseek-chat,messages:[{role:user,content:你好}],stream:false}向量接口的验证更简单curl http://127.0.0.1:8001/v1/embeddings \ -H Content-Type: application/json \ -d {model:qwen3-embed,input:你好这是测试文本}返回的 JSON 里会带data[0].embedding这个数组长度就是模型的向量维度。具体是多少以模型config.json为准不用去背参数表。Python 端集成就换成 OpenAI SDKbase_url 指向 8001api_key 随便填一个占位符from openai import OpenAI client OpenAI(base_urlhttp://127.0.0.1:8001/v1, api_keyEMPTY) resp client.embeddings.create( modelqwen3-embed, input[RAG 检索测试文本, 第二条测试文本] ) for item in resp.data: print(item.index, len(item.embedding))5.5 chatbox 接入注意模型名不是目录名很多同学本地用 chatbox 调试遇到最多的坑就是填写的模型名对不上。记住一个原则chatbox 里填的模型名必须等于 vLLM 启动参数里的--served-model-name不是你挂载的目录名也不是模型仓库的原始名字。比如我上面启动命令写的是--served-model-name deepseek-chat那 chatbox 里model字段就填deepseek-chat如果填DeepSeek-R1-Distill-Qwen-7BvLLM 会返回模型不存在的报错。很多同学以为自己模型加载失败了其实只是名字映射问题。6. 常见问题与排查速查表6.1 启动报错、OOM、模型名不匹配我把生产环境里遇到过的典型问题整理成一个表格方便排查症状常见原因处理方法容器启动后立即退出日志无明显报错模型目录挂载路径不对检查-v /models:/models和--model是否一致启动时报CUDA out of memory显存不足或gpu-memory-utilization设置过高调低到 0.5 再试或换更大显存的卡调用时报model not found客户端 model 字段和--served-model-name不一致修改请求里的 model 字段embedding 接口返回 404启动时没有加--task embedding停止容器补上参数重启对话接口返回乱码或错误部分模型需要--trust-remote-code启动参数加上后重启服务起来了但推理速度极慢--max-num-batched-tokens设置过小调大到 8192 以上观察显存余量这里最容易被忽略的是 IPC 配置。vLLM 用共享内存做 tensor 并行和 tokenizer 缓存容器里默认的/dev/shm只有 64MB很容易在加载 tokenizer 或者多进程通信时莫名卡死。生产环境一定要加--ipchost或者显式设置--shm-size这个细节能省掉很多排查时间。6.2 请求 embedding 返回结果不对向量服务返回的 embedding 维度坑不少。有的模型输出维度是 1024有的是 768如果你下游的向量库索引已经按 1024 建好了模型突然换了个 768 维的写入时必然报错。解决办法是切换模型前先读config.json里的hidden_size字段并且把维度校验写进上游服务。另一个容易被忽略的点是 input 类型。OpenAI 的 embeddings 接口允许传字符串或者字符串数组但文本长度超过--max-model-len时会被截断向量也就失去了语义完整性。生产环境建议在网关层加一道长度检查超长文本先做切片再向量化。6.3 与 Ollama 混用时的典型误区很多团队开发环境用 Ollama、生产环境用 vLLM两边混着用容易搞混模型名和服务地址。一个常见误区是本地已经用 Ollama 跑起来一个向量模型就以为生产环境也可以用同样的方式接入。前面说过Ollama 的 embedding 接口路径、请求格式和 OpenAI 协议不一致业务代码如果只依赖 OpenAI SDK直接切到 Ollama 是调不通的。我的建议是所有上层业务代码都只认 OpenAI 兼容接口。开发阶段就算是用 Ollama也在前面套一个兼容适配层生产环境切到 vLLM 时代码一行都不用改只改 base_url。6.4 可观测性从日志到 metrics把服务稳定跑起来只是第一步生产 stack 没有监控就是盲飞。vLLM 自带/metrics端点在容器启动时没有默认暴露需要你在代码里开启 Prometheus 监听。常见的做法是启动一个独立进程去抓http://pod-ip:8000/metrics。重点关注这几个指标vllm:num_requests_running正在处理的请求数、vllm:gpu_cache_usage_percKV cache 占用比、vllm:request_generation_duration_seconds首 token 延迟。如果gpu_cache_usage_perc长期超过 0.9说明显存快见底了随时可能触发 preemption如果num_requests_running频繁饱和说明调度窗口太小该调参或扩容了。写在最后这套双服务架构我实际跑了两个多月最大的体会是vLLM 的能力边界一直在扩展但生产环境的稳定性是靠“主动隔离”换来的不是靠“一个引擎包打天下”。对话模型和向量模型的负载模型差异太大硬塞在一起短期看省了一张卡长期看是给自己埋雷。最后再分享一个小技巧每次升级 vLLM 镜像前先在测试环境跑一遍对话和向量的压测回归重点看 TTFT 和 embedding P95这两项数据不会骗人。版本锁定、显存隔离、模型名规范这三件事做到位你的 vLLM production stack 就基本稳了。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

2026营口电气检测机构排名 TOP5 CMA 资质机构提供防爆设备检测+防爆安全检测 联系方式推荐 2026/9/29 21:51:10

2026营口电气检测机构排名 TOP5 CMA 资质机构提供防爆设备检测+防爆安全检测 联系方式推荐

营口本地电气防爆检测机构数量众多,化工园区、油库加油站、矿山厂区、制药企业及危化品仓储场所的业主们,面对防爆电气安全排查与生产验收任务时,常感眼花缭乱。大量无资质机构出具的检测报告无法通过应急管理部门核查,令人头疼不…

阅读更多 →
想开发一款业务软件,到底该找谁?外包、自研、开源底座怎么选 2026/9/29 21:51:09

想开发一款业务软件,到底该找谁?外包、自研、开源底座怎么选

引言当企业业务跑起来之后,很多负责人都会冒出一个想法:我需要一套专属软件,可能是商城、订货系统、客户管理、渠道分销或者私域运营平台。紧接着第一个难题就来了:到底找谁做?很多人第一反应:找软件外包公…

阅读更多 →
SEED-XDS560V2仿真器实操避坑指南:从驱动安装到CCS连接调试全解析 2026/9/29 21:51:09

SEED-XDS560V2仿真器实操避坑指南:从驱动安装到CCS连接调试全解析

先用最简单的大白话告诉你:SEED-XDS560V2 是 TI DSP 开发中最常见的一类仿真器,负责把 PC 上的 Code Composer Studio(CCS)和板子上的 DSP 芯片连起来,让你能烧程序、看变量、设断点、抓波形。很多新手拿到手的第一反应…

阅读更多 →
我宁愿熬夜三天,也不肯花九十块上云 2026/9/29 21:51:09

我宁愿熬夜三天,也不肯花九十块上云

关于那点放不下的自尊心,和它悄悄吃掉的钱去年有个单子,周五要交。我的主力机正在跑另一个项目的东西,腾不出来。同事随口说,你扔云上呗,一会就完。我没扔。我说,不急,我等它跑完这个再弄。于是…

阅读更多 →
【回眸】AI 电商盲盒怎么营收?从营销创新到运营提效 2026/9/29 21:51:09

【回眸】AI 电商盲盒怎么营收?从营销创新到运营提效

做盲盒生意的商家最近普遍面临一个痛点:流量成本越来越高,但用户的复购意愿却在下降。传统的“随机抽取”模式已经难以满足年轻消费者对于新鲜感和个性化体验的追求。很多开发者在尝试引入 AI 技术时,往往陷入两个误区:要么过度追…

阅读更多 →
CNware虚拟化平台方案拆解:从PPT到落地的避坑指南 2026/9/29 21:50:55

CNware虚拟化平台方案拆解:从PPT到落地的避坑指南

简介:这份PPT资源面向企业IT架构师、云计算运维人员及虚拟化方案选型者,系统梳理了CNware虚拟化平台的整体解决方案,可帮助读者理解企业级云操作系统从底层引擎到上层服务管理的完整技术脉络。资源包内仅含1个pptx文件,大小约1.32…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉