我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

Ollama vs vLLM:本地大模型部署选型与高并发推理实践

Ollama vs vLLM:本地大模型部署选型与高并发推理实践 1. 先搞清楚这两个东西到底在解决什么问题1.1 从一行命令到千级并发中间差了什么很多人第一次接触本地大模型部署都是从 Ollama 开始的。装完之后敲一行ollama run qwen2.5模型就跑起来了浏览器里打开个界面就能对话体验确实丝滑。但当你把这个东西丢到生产环境让几十上百个用户同时用的时候问题就来了——请求排队、响应变慢、显存爆掉、进程直接挂掉。这不是 Ollama 的错它的定位本来就是“让个人开发者快速跑起来一个模型”。而 vLLM 从诞生第一天起瞄准的就是“怎么让一块 GPU 同时服务尽可能多的并发请求”。两者的设计目标完全不同所以选型的时候不能只看“哪个装起来方便”得看你到底要拿它干什么。我见过太多团队在这个问题上踩坑开发阶段用 Ollama 跑得好好的上线之后用户一多就崩然后紧急换 vLLM结果发现部署方式、接口格式、模型管理全都不一样又得重新折腾一遍。所以这篇文章的核心目的就是帮你在动手之前就想清楚——你到底该用哪个以及为什么。1.2 用生活化类比理解两者的本质差异打个比方。Ollama 就像你家楼下那家小餐馆老板一个人掌勺你点一个菜他做一个味道不错上菜也快。但如果同时来了二十桌客人老板就忙不过来了只能一桌一桌排队做。vLLM 更像是一个中央厨房它有一套高效的调度系统能把多个订单里的相似工序合并处理同时出餐。它的核心武器叫PagedAttention这个技术后面会详细讲简单说就是让显存的利用率大幅提升从而在同一块显卡上同时处理更多请求。所以选择逻辑很清晰如果你是自己学习、做 Demo、小团队内部用Ollama 足够了如果你要对外提供服务、要扛并发、要控制成本vLLM 是更合适的选择。但实际情况往往比这复杂下面我会从多个维度展开拆解。1.3 本文适合哪些人看这篇文章主要面向三类读者第一类是有一定开发经验、想在本地或服务器上部署大模型但还没决定用哪个工具的工程师第二类是已经在用 Ollama 或 vLLM但遇到了性能瓶颈或者想优化现有方案的开发者第三类是做技术选型的技术负责人需要一份清晰的对比来支撑决策。不管你是哪种我都会尽量把原理讲透、把操作步骤写清楚、把踩过的坑标出来。不会只告诉你“用这个就对了”而是告诉你“为什么用这个、什么情况下不该用这个”。2. 核心架构差异为什么 vLLM 能扛并发而 Ollama 不能2.1 Ollama 的架构简单直接但天花板明显Ollama 底层用的是 llama.cpp 作为推理后端llama.cpp 本身是一个非常优秀的项目它用 C 实现了高效的 CPU 和 GPU 推理支持量化、支持多种模型格式。Ollama 在它上面包了一层提供了模型管理、API 接口、命令行交互等功能。它的请求处理模型是相对简单的来一个请求分配资源跑完释放。虽然新版本也做了一些并发优化比如支持多个请求排队、支持一定的批处理但它的核心调度逻辑并不是为高并发设计的。当并发请求数超过一定阈值时你会明显感觉到响应时间线性增长甚至出现请求超时。具体来说Ollama 默认的并发数OLLAMA_NUM_PARALLEL通常设置为 1 或很小的值。你可以手动调大这个参数但调大之后显存占用会迅速上升因为每个并发请求都需要独立的 KV Cache 空间。而 llama.cpp 的 KV Cache 管理方式比较“朴素”不像 vLLM 那样做了精细的分页管理所以显存浪费比较严重。2.2 vLLM 的杀手锏PagedAttention 到底强在哪vLLM 最核心的技术创新就是PagedAttention。要理解它先得知道传统推理中 KV Cache 是怎么管理的。在 Transformer 模型推理过程中每生成一个 token都需要缓存之前所有 token 的 Key 和 Value 向量这就是 KV Cache。传统做法是给每个请求预分配一块连续的显存空间大小按照最大可能的序列长度来算。问题是实际生成的序列长度往往远小于最大值这就导致了大量的显存浪费——业内通常估计浪费在 60% 到 80% 之间。PagedAttention 的思路借鉴了操作系统的虚拟内存分页机制。它把 KV Cache 切分成固定大小的“页”block不需要连续存储而是通过一张映射表来管理。这样一来显存利用率可以提升到 90% 以上同样一块显卡能同时服务的请求数就大幅增加了。实测数据在相同硬件条件下vLLM 的吞吐量通常是 Ollama 的 5 到 20 倍具体倍数取决于模型大小、序列长度和并发数。这个差距在并发数越高的时候越明显。2.3 连续批处理另一个关键差异除了 PagedAttentionvLLM 还实现了连续批处理Continuous Batching。传统的静态批处理是这样的攒一批请求一起送进模型等这批全部跑完再处理下一批。问题是同一批里有的请求可能只需要生成 10 个 token有的需要生成 500 个那短的就得等长的跑完GPU 利用率很低。连续批处理的做法是不等整批跑完只要有请求完成了就立刻把新的请求塞进去。这样 GPU 几乎一直在满负荷工作不会出现“等尾巴”的情况。这个机制对于在线服务场景特别重要因为用户的请求是随时到来的不可能攒批。Ollama 在这方面的支持相对有限。虽然 llama.cpp 也在逐步引入一些批处理优化但整体调度策略和 vLLM 不在一个层面上。2.4 架构差异带来的实际影响对照对比维度OllamavLLM推理后端llama.cpp自研基于 PyTorchKV Cache 管理连续分配浪费较多PagedAttention利用率 90%批处理方式静态批处理为主连续批处理并发能力个位数到十位数百位数到千位数显存效率一般优秀部署复杂度极低中等模型格式支持GGUF 为主HuggingFace 格式为主量化支持丰富Q4/Q5/Q8 等支持 AWQ/GPTQ/FP8 等适合场景个人开发、小规模生产环境、高并发这张表不是要分出谁好谁坏而是帮你快速定位自己的需求落在哪个区间。接下来我会从实操角度分别讲清楚两者的部署和使用细节。3. Ollama 实操从安装到调优的完整路径3.1 安装环节的坑与技巧Ollama 的安装本身不复杂官网下载安装包双击就行。但国内用户经常会遇到下载慢的问题这个没什么好办法要么耐心等要么找国内的镜像源。Linux 下可以用官方的一键脚本curl -fsSL https://ollama.com/install.sh | sh安装完成后Ollama 默认会把模型存在系统盘的用户目录下。如果你系统盘空间不大这个很快就会撑满。解决办法是设置环境变量OLLAMA_MODELS指向一个大容量磁盘export OLLAMA_MODELS/data/ollama/modelsWindows 下则是在系统环境变量里添加这个变量然后重启 Ollama 服务。这个操作看起来简单但很多人是等到系统盘红了才想起来改那时候迁移模型又得折腾一遍。另一个常见问题是 Ollama 默认开机自启有些用户不想让它一直占着显存。Linux 下可以用systemctl disable ollama关掉自启Windows 下在任务管理器启动项里禁用就行。3.2 模型拉取与量化选择Ollama 的模型库ollama.com/library提供了大量预量化好的模型直接ollama pull就能用。量化的选择很关键它直接决定了显存占用和输出质量之间的平衡。以 7B 模型为例不同量化级别的显存占用大致如下量化级别显存占用7B质量损失推荐场景Q8_0约 8GB极小显存充足追求质量Q5_K_M约 5.5GB很小平衡之选Q4_K_M约 4.5GB较小显存有限日常使用Q4_0约 4GB可感知显存紧张Q3_K_M约 3.5GB明显不推荐除非实在没空间Q2_K约 3GB严重基本不可用我的经验是Q4_K_M 是大多数场景下的甜点级别。它在 7B 模型上只需要 4.5GB 左右的显存输出质量相比 FP16 的损失在日常对话和文本处理任务中几乎感觉不到。如果你用的是 14B 或更大的模型Q4_K_M 同样适用显存占用大约翻倍。3.3 并发参数调优OLLAMA_NUM_PARALLEL 怎么设Ollama 有一个关键环境变量OLLAMA_NUM_PARALLEL控制同时处理多少个请求。默认值通常是 1意味着请求是串行处理的。你可以把它调大比如设为 4export OLLAMA_NUM_PARALLEL4但这里有个陷阱每增加一个并发就需要额外的 KV Cache 空间。如果你的显存本来就紧张调大这个参数会导致 OOM显存溢出。所以调之前先算一下模型本身占多少显存每个并发请求的 KV Cache 占多少加起来不能超过显卡容量。KV Cache 的大小可以用这个公式估算KV Cache 大小 2 × 层数 × 注意力头数 × 头维度 × 序列长度 × 数据类型字节数以 Llama 3 8B 为例32 层32 个注意力头头维度 128序列长度 4096FP16 数据类型2 字节2 × 32 × 32 × 128 × 4096 × 2 约 2GB也就是说每个并发请求大约需要 2GB 的 KV Cache。如果你的显卡是 24GB模型本身占了 5GB那理论上最多支持 9 个并发左右但还要留一些余量给其他开销实际设 4 到 6 比较稳妥。3.4 API 接口与外部集成Ollama 默认在11434端口提供 REST API接口格式兼容 OpenAI 的部分规范所以很多工具可以直接对接。比如用 FastAPI 调用import httpx async def chat(prompt: str): async with httpx.AsyncClient() as client: resp await client.post( http://localhost:11434/api/generate, json{ model: qwen2.5:7b, prompt: prompt, stream: False }, timeout60.0 ) return resp.json()[response]如果你要把它接入 Dify、FastGPT 这类平台通常只需要填上 Ollama 的地址和模型名就行。但要注意这些平台如果有较高的并发需求Ollama 很可能扛不住后面会讲怎么判断和迁移。4. vLLM 实操从零搭建一个能扛并发的推理服务4.1 安装 vLLM 前必须注意的依赖问题vLLM 的安装比 Ollama 复杂得多最大的坑是它会改变你已经安装好的 PyTorch 版本。vLLM 对 PyTorch、CUDA、cuDNN 的版本要求非常严格pip 安装的时候它会自动拉取它需要的版本可能把你环境里原来的 torch 覆盖掉。如果你机器上还有其他项目依赖特定版本的 PyTorch强烈建议用虚拟环境隔离python -m venv vllm-env source vllm-env/bin/activate pip install vllm如果要用特定 CUDA 版本可以指定pip install vllm --extra-index-url https://download.pytorch.org/whl/cu121安装完成后验证一下python -c import vllm; print(vllm.__version__)注意vLLM 目前对 Windows 的原生支持有限官方推荐在 Linux 下运行。Windows 用户可以考虑用 WSL2或者通过 Docker 部署。如果非要在 Windows 下跑可以试试 GPUStack 这类工具来做封装。4.2 启动一个 OpenAI 兼容的推理服务vLLM 最常用的启动方式是通过命令行python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-7B-Instruct \ --host 0.0.0.0 \ --port 8000 \ --tensor-parallel-size 1 \ --max-model-len 4096 \ --gpu-memory-utilization 0.9几个关键参数解释一下--tensor-parallel-size张量并行数如果你有多张显卡设为显卡数量可以分摊模型。--max-model-len最大序列长度设得越大 KV Cache 占用越多根据实际需求设。--gpu-memory-utilizationGPU 显存利用率上限默认 0.9意思是最多用 90% 的显存。留 10% 是给一些临时开销用的不建议设成 1.0。启动之后它就提供了一个和 OpenAI API 兼容的接口可以直接用 OpenAI 的 SDK 调用from openai import OpenAI client OpenAI(base_urlhttp://localhost:8000/v1, api_keydummy) resp client.chat.completions.create( modelQwen/Qwen2.5-7B-Instruct, messages[{role: user, content: 你好}] ) print(resp.choices[0].message.content)4.3 并发压测怎么知道你的服务能扛多少部署完了不代表就完事了你得知道它能扛多少并发。我一般用wrk或者locust来做压测。简单一点可以用 Python 写个脚本import asyncio import aiohttp import time async def send_request(session, i): start time.time() async with session.post( http://localhost:8000/v1/chat/completions, json{ model: Qwen/Qwen2.5-7B-Instruct, messages: [{role: user, content: f测试请求 {i}}], max_tokens: 100 } ) as resp: await resp.json() return time.time() - start async def main(): concurrency 50 async with aiohttp.ClientSession() as session: tasks [send_request(session, i) for i in range(concurrency)] latencies await asyncio.gather(*tasks) print(f并发数: {concurrency}) print(f平均延迟: {sum(latencies)/len(latencies):.2f}s) print(f最大延迟: {max(latencies):.2f}s) asyncio.run(main())逐步增加并发数观察延迟变化。当延迟开始急剧上升的时候说明已经接近瓶颈了。记录下这个拐点就是你这套配置的安全并发上限。4.4 显存不够怎么办量化与多卡方案vLLM 支持多种量化方案最常用的是 AWQ 和 GPTQ。以 AWQ 为例启动时加上量化参数python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-7B-Instruct-AWQ \ --quantization awq \ --max-model-len 4096AWQ 的 4bit 量化可以把 7B 模型的显存占用从约 14GB 降到约 5GB代价是轻微的质量损失。实测下来在大多数对话和文本任务中AWQ 量化的模型和原始模型的输出差异很小。如果单卡实在放不下可以用多卡张量并行--tensor-parallel-size 2这会把模型切分到两张卡上每张卡承担一半的计算。但要注意张量并行会带来卡间通信开销所以两张卡的性能提升不是线性的通常能到 1.6 到 1.8 倍左右。5. 选型决策什么场景该用哪个5.1 一张决策流程图帮你快速判断与其纠结不如按下面的逻辑走一遍你是个人学习/做 Demo 吗是 → Ollama。不是 → 继续。你的日活用户超过 50 人吗没有 → Ollama 可能够用。有 → 继续。你需要同时处理 10 个以上的请求吗不需要 → Ollama。需要 → vLLM。你有运维能力来维护一个 Python 服务吗没有 → 考虑 Ollama 负载均衡。有 → vLLM。你的显卡显存小于 8GB 吗是 → Ollama量化支持更好。不是 → vLLM。这个流程不是绝对的但能帮你快速缩小选择范围。5.2 混合方案Ollama 做开发vLLM 做生产实际项目中我经常推荐一种混合方案开发阶段用 Ollama因为它的模型管理太方便了ollama pull一下就能换模型调试 prompt 也很直观。等到要上线了把模型权重导出用 vLLM 部署生产服务。这样做的好处是开发效率高生产性能好。坏处是两边的模型格式可能不一样Ollama 用的是 GGUFvLLM 用的是 HuggingFace 格式。不过现在很多模型两边都有现成的版本直接下载对应的就行不用自己转换。5.3 成本对比同样并发下谁更省钱假设你需要支持 100 个并发用户平均每个请求生成 200 个 token。用 Ollama 的话根据实测一台 409024GB大概能支持 5 到 8 个并发那你就需要大约 15 台机器。每台机器按 2 万算硬件成本 30 万。用 vLLM 的话同样一台 4090通过 PagedAttention 和连续批处理大概能支持 50 到 80 个并发。那只需要 2 台机器硬件成本 4 万。这个差距是数量级的。当然实际数字会因模型大小、序列长度、SLA 要求等因素而变化但趋势是明确的并发越高vLLM 的成本优势越明显。6. 常见问题与排查技巧实录6.1 Ollama 常见问题速查问题原因解决方法下载模型太慢网络问题设置代理或找国内镜像显存溢出模型太大或并发太高换更小的量化版本降低 NUM_PARALLEL服务启动失败端口被占用改端口或杀掉占用进程输出质量差量化级别太低换 Q4_K_M 或更高开机自启占显存默认配置禁用自启服务API 调用超时并发过高降低并发或换 vLLM6.2 vLLM 常见问题速查问题原因解决方法安装后 torch 版本变了依赖冲突用虚拟环境隔离启动报 CUDA 错误CUDA 版本不匹配检查 CUDA 和 PyTorch 版本显存不够模型太大或 max-model-len 太大用量化或降低序列长度吞吐量不如预期参数没调好调整 gpu-memory-utilization 和批处理参数Windows 下跑不起来原生支持有限用 WSL2 或 Docker多卡通信慢张量并行开销检查 NVLink 或 PCIe 带宽6.3 几个我踩过的坑第一个坑是Ollama 的模型存储路径。我一开始没改结果系统盘被撑爆了后来迁移模型的时候又遇到权限问题折腾了半天。建议装完第一件事就是改OLLAMA_MODELS。第二个坑是vLLM 的版本兼容性。有一次我升级了 vLLM结果它把我环境里的 PyTorch 从 2.1 升到了 2.3导致另一个项目跑不起来了。从那以后我所有 vLLM 相关的环境都用独立的虚拟环境。第三个坑是压测时的并发数设置。我一开始用 100 并发去压一个 7B 模型结果全部超时以为是 vLLM 不行。后来逐步降低到 30 并发发现延迟很稳定。所以压测要从低到高逐步加找到拐点而不是一上来就拉满。6.4 性能调优的几个实用技巧对于 vLLM有几个参数对性能影响很大--max-num-seqs控制同时处理的最大请求数默认 256。如果你的显存有限可以调低。--max-num-batched-tokens控制每批处理的最大 token 数调大可以提高吞吐但增加延迟。--enable-chunked-prefill对于长 prompt 场景开启这个可以避免一个长请求阻塞其他请求。对于 Ollama除了OLLAMA_NUM_PARALLEL还可以设置OLLAMA_MAX_LOADED_MODELS控制同时加载的模型数量。如果你只需要一个模型设为 1 可以节省显存。7. 从 Ollama 迁移到 vLLM 的实操路径7.1 模型格式转换与准备Ollama 用的 GGUF 格式不能直接给 vLLM 用。你需要找到对应的 HuggingFace 格式模型。大多数主流模型在 HuggingFace 上都有官方或社区上传的版本直接下载就行。如果找不到对应的 HF 格式可以用 llama.cpp 提供的转换脚本把 GGUF 转回 HF 格式但这个过程比较麻烦而且可能有精度损失。我的建议是直接用 HF 上的原始模型然后用 vLLM 支持的量化方案重新量化。7.2 接口适配从 Ollama API 到 OpenAI APIOllama 的 API 格式和 OpenAI 不完全一样迁移的时候需要改调用代码。主要差异在请求体和响应体的字段名上。比如 Ollama 用promptOpenAI 用messagesOllama 返回responseOpenAI 返回choices[0].message.content。如果你不想改代码可以在 vLLM 前面加一个适配层把 OpenAI 格式转换成 Ollama 格式。但更推荐直接改成 OpenAI 格式因为这是行业标准以后换其他推理引擎也不用再改。7.3 灰度切换与回滚方案迁移不要一刀切。先把 vLLM 服务部署好用少量流量测试确认稳定后再逐步加大流量。同时保留 Ollama 服务作为备份万一 vLLM 出问题可以快速切回去。具体做法可以是在网关层做流量分配比如 10% 走 vLLM90% 走 Ollama观察一段时间没问题后调整为 50/50最后全部切到 vLLM。这个过程中要重点监控几个指标请求延迟、错误率、GPU 利用率、显存占用。任何一个指标异常都要及时排查。8. 一些关于推理引擎选型的个人体会做了这么多项目我最大的体会是没有最好的工具只有最合适的工具。Ollama 和 vLLM 不是竞争关系它们服务于不同的场景。你完全可以在自己的工具箱里同时保留这两个根据具体需求来选用。另一个体会是不要过早优化。如果你现在只有几个用户用 Ollama 完全没问题没必要为了“以后可能的高并发”提前上 vLLM那样只会增加开发和运维的复杂度。等到真正遇到瓶颈了再迁移成本反而更低。最后分享一个小技巧不管你用哪个引擎都建议在服务前面加一层网关比如 Nginx这样可以方便地做负载均衡、限流、监控和灰度发布。网关层的配置和推理引擎无关换引擎的时候这一层不用动能省不少事。如果你正在纠结选哪个我的建议是先用 Ollama 把东西跑起来验证你的业务逻辑和 prompt 效果。等到性能真的成为瓶颈了再考虑迁移到 vLLM。不要为了技术而技术能解决问题的方案就是好方案。
返回列表