ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

大模型本地部署实战:用 Ollama 与 vLLM 搭建可复现的推理环境

大模型本地部署实战:用 Ollama 与 vLLM 搭建可复现的推理环境 1. 本地部署大模型到底解决什么问题从显存焦虑到可复现推理环境大模型本地部署这件事很多人第一次尝试时都会卡在同一个地方模型权重下载完了命令也敲了结果要么显存爆掉要么服务起来之后不知道怎么验证它真的在工作。我自己最早在单卡 24G 的机器上跑 7B 模型以为量化之后就能稳结果并发一上来直接 OOM。后来才明白本地部署不是「把模型跑起来」这么简单而是要搭一个可复现的推理环境——同样的硬件、同样的参数今天能跑明天换台机器也能跑出接近的结果。这篇文章面向的是个人开发者和小型团队手上有 1 到 4 张消费级或入门级专业卡想在自己的 GPU 上完成大模型本地部署。核心会对比两条路径一条是 Ollama 快速拉起适合验证和轻量使用另一条是 vLLM 高吞吐推理适合要做服务化、要接 OpenAI 兼容接口的场景。两条路我都会给出可复制的 Docker 启动命令、量化配置以及验证脚本。先说清楚一个概念所谓「可复现」指的是三件事能对得上第一模型版本和量化格式固定不会因为拉取时间不同拿到不同权重第二启动参数写进配置文件或脚本而不是靠记忆敲命令第三有统一的验证请求能测出首 token 延迟和显存占用换环境后能对比。做到这三点你才算真正拥有了一个能长期用的本地推理环境而不是一次性的玩具。Ollama 和 vLLM 的定位差异很大。Ollama 更像「开箱即用的本地模型管理器」它帮你处理了模型下载、量化选择、服务托管一条ollama run就能对话。vLLM 则是「推理引擎」它专注在吞吐和显存效率上通过 PagedAttention 和连续批处理把 GPU 利用率拉高代价是配置项更多、对硬件和驱动版本更敏感。选哪个取决于你是想快速验证还是想把它当成一个长期在线的服务。下面我会按「先 Ollama 快速拉起再 vLLM 高吞吐」的顺序展开中间穿插显存占用和首 token 延迟的实测记录方式。如果你手上只有一张卡或者想先跑通再优化建议从 Ollama 那节开始如果你已经明确要做 API 服务、要接 Codex 这类编码工具可以直接跳到 vLLM 部分但前置的 Docker 和驱动检查还是要看。2. Ollama 快速拉起OLLAMA_KEEP_ALIVE 与模型常驻的坑Ollama 最大的优点是省心但它的默认行为里有一个很容易被忽略的点模型加载后如果一段时间没有请求会被自动卸载。默认的 keep-alive 是 5 分钟也就是说你ollama run之后放着不动5 分钟后模型就从显存里退出了下次请求又要重新加载首 token 延迟会突然变得很高。这个问题在交互式对话里不明显但如果你把它当成一个后台服务就会遇到「明明刚用过怎么又变慢了」的困惑。解决办法是设置OLLAMA_KEEP_ALIVE。把它设成-1模型就会一直常驻显存不会自动卸载。启动方式有两种一种是临时在命令前加环境变量OLLAMA_KEEP_ALIVE-1 ollama serve另一种是写进 systemd 服务或 Docker 的环境变量里让它持久生效。我实测下来如果不加-1一个 7B 的 Q4 量化模型在 5 分钟后卸载再次请求时首 token 延迟会从 200ms 左右跳到 2 秒以上差距非常明显。所以只要你打算把 Ollama 当服务用这一步几乎是必须的。接下来是模型管理。查看本地已经拉取了哪些模型ollama list运行某个模型ollama run qwen2.5:7b这里有个细节Ollama 的模型名后面可以带量化标签比如qwen2.5:7b-instruct-q4_K_M。不同量化格式对显存和效果的影响不一样Q4_K_M 是比较常用的平衡点Q5 更接近原始精度但显存占用更高Q8 基本就是原始精度了。如果你显存紧张优先选 Q4如果显存够Q5 或 Q8 能减少量化带来的质量损失。Ollama 默认会监听11434端口并且自带一个 OpenAI 兼容的接口。也就是说你不需要额外装什么就能用 OpenAI SDK 去调它。验证一下curl http://localhost:11434/v1/models如果返回了模型列表说明服务正常。这一步很关键因为很多人以为 Ollama 只能用它自己的 CLI其实它的/v1接口可以直接被各种 OpenAI 兼容客户端使用。不过 Ollama 也有它的局限。它的批处理能力比较弱并发请求多了之后吞吐上不去而且它对多卡并行的支持不如 vLLM 成熟。所以如果你的场景是「一个人用、偶尔调用」Ollama 足够如果是「多人共用、要跑批量任务」就得考虑 vLLM 了。还有一个实际会踩的坑Docker 里跑 Ollama 时模型文件默认存在容器内容器一删模型就没了。正确做法是把模型目录挂载出来docker run -d --gpus all \ -v ollama:/root/.ollama \ -p 11434:11434 \ -e OLLAMA_KEEP_ALIVE-1 \ --name ollama \ ollama/ollama这样模型权重存在 named volume 里容器重建也不会丢。如果你要指定具体用哪张卡可以加-e CUDA_VISIBLE_DEVICES0。这些参数写进 docker-compose 或者启动脚本就是「可复现」的第一步。3. vLLM 高吞吐推理Docker 启动命令与量化配置当你需要更高的吞吐、更稳定的并发或者要接 Codex 这类编码工具时vLLM 是更合适的选择。它的核心优势是 PagedAttention能把 KV Cache 的显存碎片问题解决掉从而在同样的显存下支持更长的上下文和更多的并发。代价是配置项多启动参数写错一个就可能起不来。先给一个可以直接复制的 Docker 启动命令。假设你有两张卡要跑一个 DeepSeek 系列的模型docker run -d --gpus all \ --shm-size 16g \ -v /data/models:/models \ -p 8000:8000 \ --name vllm-server \ vllm/vllm-openai:latest \ --model deepseek-ai/DeepSeek-V2-Lite-Chat \ --trust-remote-code \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.8 \ --max-model-len 16384 \ --served-model-name deepseek \ --host 0.0.0.0 \ --port 8000这里几个参数值得展开说。--tensor-parallel-size 2表示用两张卡做张量并行如果你只有一张卡就改成 1。--gpu-memory-utilization 0.8是显存利用率上限vLLM 会按这个比例去分配 KV Cache设太高容易 OOM设太低浪费显存0.8 到 0.9 是比较稳的区间。--max-model-len 16384是最大上下文长度这个值直接决定 KV Cache 的显存占用如果你显存紧张把它降到 8192 甚至 4096 能省不少。--served-model-name deepseek是给模型起个别名后面调用时用这个名字就行。--shm-size 16g这个参数容易被忽略。Docker 默认的共享内存只有 64MBvLLM 在多卡通信和批处理时会用到共享内存不够的话会报错或者性能骤降。设成 16g 是比较保险的做法。如果你要用量化模型vLLM 支持 AWQ 和 GPTQ 两种主流量化格式。以 AWQ 为例启动时加--quantization awqdocker run -d --gpus all \ --shm-size 16g \ -p 8000:8000 \ --name vllm-awq \ vllm/vllm-openai:latest \ --model /models/Qwen2.5-7B-Instruct-AWQ \ --quantization awq \ --gpu-memory-utilization 0.85 \ --max-model-len 8192 \ --served-model-name qwen \ --host 0.0.0.0 \ --port 8000AWQ 的好处是 4bit 量化后显存占用大约是 FP16 的四分之一7B 模型大概 5 到 6G 就能跑起来而且质量损失相对可控。GPTQ 类似把--quantization改成gptq即可。注意量化模型需要提前下载好权重放到挂载目录里vLLM 不会自动帮你转换格式。启动之后用docker logs -f vllm-server看日志。正常的话会看到模型加载进度、KV Cache 分配情况最后出现Uvicorn running on http://0.0.0.0:8000。如果卡在加载阶段很久多半是模型太大或者磁盘 IO 慢如果直接报错退出看日志里的 CUDA 或显存相关提示。这里给一个 Codex 的配置片段方便你把它接到本地 vLLM 上。Codex 的config.toml里可以定义自定义 provider[model_providers.local] name Local Model base_url http://localhost:8000/v1 env_key OPENAI_API_KEY wire_api responses [profiles.local] model_provider local model deepseek然后设置环境变量并启动export OPENAI_API_KEYtoken-abc123 codex --profile local注意这里的base_url、env_key、model三件套要和你 vLLM 启动时的--served-model-name对上。vLLM 的 OpenAI 接口默认不校验 API Key所以token-abc123这种占位符也能用但 Codex 要求必须有这个环境变量存在。4. 验证请求与实测记录首 token 延迟和显存占用怎么测服务起来之后最关键的一步是验证它真的在工作并且记录下可对比的性能数据。很多人跳过这一步结果后面出问题时不知道是模型的问题还是配置的问题。我建议至少做三件事发一个最小请求确认接口通、测首 token 延迟、记录显存占用。先确认接口通。用 curl 发一个最简单的 chat 请求curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: deepseek, messages: [{role: user, content: 你好}], max_tokens: 32 }如果返回了 JSON 格式的回复说明服务正常。如果报 404多半是model名字和--served-model-name不一致如果报连接拒绝检查端口映射和--host 0.0.0.0。接下来用 Python 脚本测首 token 延迟。首 token 延迟TTFT是衡量交互体验的核心指标它指的是从发出请求到收到第一个 token 的时间。流式请求下可以这样测import time from openai import OpenAI client OpenAI( base_urlhttp://localhost:8000/v1, api_keytoken-abc123 ) start time.time() first_token_time None token_count 0 response client.chat.completions.create( modeldeepseek, messages[{role: user, content: 用一句话解释什么是张量并行}], max_tokens128, streamTrue ) for chunk in response: if chunk.choices[0].delta.content is not None: if first_token_time is None: first_token_time time.time() - start token_count 1 total_time time.time() - start print(f首 token 延迟: {first_token_time:.3f}s) print(f总耗时: {total_time:.3f}s) print(f生成 token 数: {token_count}) print(f平均每 token: {total_time / token_count:.3f}s)跑几次取平均记录下来。我实测下来7B 模型在单张 4090 上vLLM 的首 token 延迟大概在 100 到 300ms 之间取决于 prompt 长度和是否命中 KV Cache。Ollama 在模型常驻的情况下接近但如果模型被卸载了重新加载会跳到秒级。显存占用怎么记录最简单的是nvidia-sminvidia-smi --query-gpuindex,memory.used,memory.total,utilization.gpu \ --formatcsv -l 1这个命令每秒刷新一次能看到每张卡的显存使用和 GPU 利用率。建议在发请求的同时开着它观察峰值。vLLM 启动后即使没有请求也会预分配一部分显存给 KV Cache所以你会看到显存占用在启动后就上去了这是正常的。把这两组数据记下来格式可以是这样方案模型量化卡数显存占用首 token 延迟吞吐Ollamaqwen2.5:7bQ4_K_M16.2G0.21s低vLLMDeepSeek-V2-LiteFP16228G0.15s高有了这张表你换硬件或者换模型时就有了对比基准。这也是「可复现」的核心——不是每次重新摸索而是有数据可依。5. 常见报错排查401、local proxy failed 与 reading choices本地部署过程中报错信息往往比配置本身更让人头疼。这一节我把几个高频错误和对应的排查思路整理出来都是实际会遇到的。401 Unauthorized。这个错误通常出现在你用 OpenAI SDK 调本地服务时。vLLM 默认不校验 Key但有些客户端会强制要求api_key非空。解决办法是随便填一个比如token-abc123。如果你用的是 Ollama它的/v1接口也不校验同样随便填。但如果你在客户端里配置了env_key指向一个不存在的环境变量就会报 401。检查一下OPENAI_API_KEY是否真的 export 了。local proxy failed。这个错误一般和网络代理有关。有些环境里设置了http_proxy或https_proxy导致请求本地localhost:8000时也被走了代理结果连不上。解决办法是在环境变量里加no_proxylocalhost,127.0.0.1让本地地址绕过代理。如果你在容器里跑也要检查容器的网络模式--network host和默认 bridge 的行为不一样。reading choices 相关报错。这个通常出现在流式解析时客户端期望chunk.choices[0].delta.content存在但某些情况下choices是空数组或者delta里没有content。稳妥的写法是先判断for chunk in response: if chunk.choices and chunk.choices[0].delta.content is not None: content chunk.choices[0].delta.content另外如果 vLLM 返回的格式和 OpenAI 有细微差异也可能导致解析失败。建议先用 curl 确认原始返回再写解析逻辑。OAuth 相关错误。如果你在接 Codex 或类似工具时遇到 OAuth 报错多半是工具本身在尝试走它的云端认证而不是用你配置的本地 provider。检查config.toml里是否正确指定了model_provider和profile以及启动时是否带了--profile local。Codex 的认证逻辑是如果 profile 指向的 provider 有env_key它就读环境变量如果没有才走 OAuth。所以确保env_key对应的变量存在。模型加载 OOM。这个最直接显存不够。排查顺序是先看模型本身多大FP16 的 7B 约 14G13B 约 26G再看--gpu-memory-utilization设了多少最后看--max-model-len是不是太大。如果显存实在不够换量化模型或者降低max-model-len或者减少tensor-parallel-size之外的并行度。端口被占用。Address already in use说明 8000 端口已经有服务了。用lsof -i:8000或netstat -tlnp | grep 8000找到占用进程要么杀掉要么换个端口。Docker 里还要注意容器名冲突docker ps -a看看有没有同名容器没删干净。这些错误看起来杂但归类之后无非是三类认证问题、网络问题、资源问题。遇到报错先归类再去对应的地方查比盲目改配置高效得多。6. 按硬件条件选方案从单卡到多卡的落地建议最后聊聊怎么根据自己的硬件选方案。这部分没有标准答案但有一些经验性的判断标准。如果你只有一张 8G 到 12G 的卡比如 3060 12G 或 4060 Ti 16GOllama 是更现实的选择。跑 7B 的 Q4 量化模型显存占用大概 5 到 6G留出余量给上下文。vLLM 在这个显存下也能跑但 KV Cache 空间有限max-model-len得压到 4096 左右并发能力也上不去。这个阶段的目标是「跑通、能用」不是「高吞吐」。如果你有一张 24G 的卡比如 3090 或 4090选择就多了。Ollama 可以跑 13B 的 Q4 或者 7B 的 Q8vLLM 可以跑 7B 的 FP16 加上不错的并发。这个配置下我建议直接用 vLLM因为它的 OpenAI 兼容接口更规范接各种工具更顺而且吞吐优势明显。首 token 延迟能压到 200ms 以内体验接近在线服务。如果你有两张或更多卡vLLM 的--tensor-parallel-size就能派上用场。两张 24G 卡跑 32B 的量化模型或者两张 48G 卡跑 70B 的量化模型都是可行的。这时候要注意卡间通信NVLink 的机器比 PCIe 的机器效率高不少。另外--shm-size一定要给够多卡通信对共享内存的需求比单卡大。还有一个维度是使用场景。如果只是自己写代码时偶尔问几句Ollama 的 CLI 体验更轻快如果是要给团队提供一个统一的 API 入口或者要接 Codex、Cline 这类工具vLLM 的稳定性和兼容性更好。我自己的做法是开发调试阶段用 Ollama 快速验证 prompt 和模型效果确定要长期用了再切到 vLLM 做服务化。不管选哪个把启动命令、模型版本、量化格式、验证脚本都存进一个仓库里加上一份 README 说明硬件要求和实测数据。这样下次换机器或者同事接手时不用重新踩一遍坑。本地部署的价值不在于「跑起来」那一刻而在于它能稳定、可复现地为你所用。如果你在配置过程中需要快速验证某个模型的效果或者想对比不同模型在相同 prompt 下的表现可以先用在线的模型对话功能做一轮筛选确定方向后再落到本地部署这样能省下不少下载和调试的时间。接入文档里有完整的 OpenAI 兼容接口说明API Keys 页面可以拿到调用凭证长期做编码和 Agent 任务的话 Coding Plan 会更划算。
返回列表