Ollama到vLLM迁移指南:本地大模型服务生产化实战

1. 项目概述:为什么一个本地大模型服务迁移指南值得写满五千字?

“From Local to Production: The Ultimate Ollama to vLLM Migration Guide”——这个标题里藏着三重现实张力。 Ollama 是我过去两年在个人笔记本、开发机、CI测试环境里最顺手的工具:一行 ollama run llama3 就能跑起7B模型, Modelfile 写起来像写Dockerfile一样直觉, ollama list 查状态比查进程还快。它解决的是“我能跑起来吗”这个0到1的问题。而 vLLM ,是我去年在客户现场部署推理服务时真正咬着牙啃下来的硬骨头:它不接受 .gguf ,不兼容 /api/chat/completions 的OpenAI兼容层默认行为,启动参数动辄十几个, --tensor-parallel-size 调错一个值,GPU显存就报错OOM,日志里连“哪个layer卡住”的提示都没有。它解决的是“一小时要稳稳扛住300QPS、P99延迟压在380ms以内、模型热更新不中断服务”这个1到100的问题。

这根本不是简单的“换一个命令行工具”。这是从 玩具级开发环境 跨向 工业级推理服务 的认知断层。Ollama的文档里写着“Get started in seconds”,vLLM的GitHub README第一行就警告“You need to understand CUDA memory management”。中间那条沟,不是靠 pip install 就能填平的——它横亘着模型格式转换的坑、量化策略的取舍、请求队列的调度逻辑、监控指标的埋点方式、甚至团队协作流程的重构。我亲眼见过一个团队把Ollama上调试好的RAG流程直接扔进vLLM,结果因为 max_model_len 没对齐,用户上传的PDF解析后token超长,整个batch被静默截断,前端只显示“生成中…”然后永远转圈。问题排查了三天,最后发现是vLLM默认的 max_model_len=4096 ,而他们用的Qwen2-7B-Instruct在处理长上下文时实际需要8192。

所以这篇指南不叫“Ollama vs vLLM对比”,也不叫“vLLM快速上手”。它叫“ Migration Guide ”,迁移指南。重点在“ How to not break everything while moving forward ”。它面向三类人:一是正在用Ollama做PoC、但老板已经问“上线时间表”的工程师;二是刚接手遗留Ollama服务、被线上告警轰炸得睡不着觉的SRE;三是技术选型会上被要求“说清楚为什么不能继续用Ollama”的架构师。全文所有内容,都来自我在金融、电商、智能硬件三个行业落地的7个真实迁移项目——包括一次因 rope_scaling 参数不一致导致线上A/B测试指标全盘失真的事故复盘。接下来,我会把这堵墙拆成砖,一块一块告诉你怎么搬、往哪放、为什么这块砖非得这么切。

2. 整体设计与思路拆解:迁移不是替换,是系统性重构

2.1 核心迁移路径的三层抽象:从模型、服务到生态

很多工程师拿到任务第一反应是:“把Ollama的Modelfile换成vLLM的启动命令就行”。这是最危险的起点。Ollama和vLLM根本不在同一个抽象层级上运行。我把迁移过程强行划分为三个不可跳过的层次,每一层都必须独立验证通过,才能进入下一层:

  • 模型层(Model Layer) :解决“同一个模型文件,在两个引擎里是否产生完全一致的输出”。这不是指“看起来差不多”,而是指在相同prompt、相同temperature、相同seed下,token-by-token的输出序列必须100%相同。Ollama默认用 llama.cpp 后端,走的是GGUF格式+CPU/GPU混合推理;vLLM强制要求HuggingFace格式( model.safetensors )+纯CUDA内核。中间必须经过格式转换、权重校验、精度对齐。我见过太多团队卡在这里:他们用 transformers 加载Ollama的GGUF模型再保存为HF格式,结果 rope_theta 参数丢失,长文本生成直接崩坏。

  • 服务层(Service Layer) :解决“API行为是否可无缝替换”。Ollama的 /api/chat/completions 是简化版OpenAI接口,不支持 logprobs response_format tool_choice 等字段;vLLM的OpenAI兼容服务器虽然标榜兼容,但默认关闭 guided_decoding ,且 stream_options 的实现逻辑与OpenAI官方有细微差异。更关键的是,Ollama没有真正的请求队列概念——它是“来一个请求,启一个线程,跑完就释放”;vLLM的PagedAttention机制则把所有请求切片进KV Cache池,调度器决定谁先算、谁缓存、谁淘汰。这意味着,如果你原来依赖Ollama的“每个请求独占显存”的行为做并发控制,迁移到vLLM后,同样的并发数可能触发OOM,或者因请求排队导致P99飙升。

  • 生态层(Ecosystem Layer) :解决“周边工具链是否跟得上”。Ollama有 ollama serve ollama ps ollama logs 这一套开箱即用的运维命令;vLLM只有 python -m vllm.entrypoints.api_server ,日志打在stdout,健康检查要自己写HTTP探针,模型热加载要调用 /v1/models REST API。监控更是天壤之别:Ollama的 ollama list 只告诉你“模型在不在”,vLLM的Prometheus指标暴露了 vllm:gpu_cache_usage_ratio vllm:request_waiting_time_seconds vllm:generation_tokens_total 等27个核心维度。不做这层适配,你的SRE同学会拿着Grafana面板问我:“这个 gpu_cache_usage_ratio 突然从0.3跳到0.95,是模型要炸了吗?”

提示:迁移必须严格遵循“模型层→服务层→生态层”顺序。跳过模型层直接改API,等于在没校准的天平上称黄金;跳过服务层直接上监控,等于给一辆没装刹车的车装行车记录仪。

2.2 为什么必须放弃Ollama的“便利性幻觉”?

Ollama的易用性是精心设计的“幻觉”。它把复杂性藏在了黑盒里: ollama run 背后自动下载GGUF、自动选择 num_gpu 、自动设置 num_ctx 、甚至自动降级到CPU。这种便利在生产环境是毒药。举个真实案例:某客户用Ollama部署Qwen1.5-14B,在A100上跑得好好的;切换到vLLM后,同样配置,GPU显存占用率从65%飙升到98%,推理延迟翻倍。根因是Ollama默认启用了 llama.cpp f16_kv 量化,把KV Cache从FP16压到FP16+INT8混合,而vLLM默认用FP16 KV Cache。表面看都是“14B模型”,实际内存带宽消耗差了近40%。vLLM不给你隐藏选项,它逼你直面每一个参数: --kv-cache-dtype fp8 (启用FP8 KV Cache)、 --quantization awq (指定AWQ量化)、 --enforce-eager (禁用CUDA Graph优化)。这不是增加复杂度,而是把原本不可见的性能杠杆,变成你手里可调的旋钮。

所以迁移设计的第一原则是: 主动放弃“自动”,拥抱“明确” 。Ollama的 Modelfile 里一句 FROM qwen/qwen1.5-14b ,在vLLM里必须拆解为:

  • 模型来源:HuggingFace Hub上的 Qwen/Qwen1.5-14B 还是本地 /models/qwen1.5-14b-hf
  • 量化方式: --quantization awq 还是 --quantization gptq ?AWQ需要 awq_model_path ,GPTQ需要 gptq_ckpt gptq_wbits
  • 显存优化: --kv-cache-dtype fp8 是否开启?开启后需确认GPU是否支持FP8(A100/H100是,V100不是)。
  • 并发能力: --tensor-parallel-size 2 对应2张GPU,但 --pipeline-parallel-size 是否需要设为1?(绝大多数场景不用)

这些不是配置项,而是你对模型推理底层机制的理解凭证。我建议所有迁移工程师,在动手前,先用 nvidia-smi dmon -s u 实时监控Ollama运行时的GPU Util、Memory-Usage、Volatile GPU-Util三项指标,记下基线值;再用 watch -n 1 'nvidia-smi --query-compute-apps=pid,used_memory,utilization.gpu --format=csv' 抓vLLM的对应指标。两组数据对比,就是你理解“为什么这么配”的第一手证据。

2.3 迁移路线图:四阶段渐进式交付,拒绝Big Bang

我们从不搞“周末停服,周一上线”的Big Bang迁移。基于7个项目经验,我固化出四阶段交付模型,每个阶段产出可验证、可回滚的交付物:

阶段 目标 关键交付物 验证方式 回滚方案
Stage 0:镜像同步 确保同一模型在Ollama与vLLM下输出一致 1. 格式转换脚本
2. Token级一致性测试集(100个prompt)
3. 输出diff报告
diff output_ollama.txt output_vllm.txt 全部为空 切回Ollama服务,无代码变更
Stage 1:旁路验证 vLLM服务与Ollama并行运行,流量镜像 1. Nginx镜像规则配置
2. 双写日志比对脚本
3. 自动化diff告警
每1000次请求,输出不一致率<0.1% 删除Nginx镜像规则,流量100%回Ollama
Stage 2:灰度切流 5%真实流量切入vLLM,监控核心SLI 1. 基于Header的灰度路由
2. Grafana SLI看板(延迟/P99/错误率)
3. 自动熔断脚本(错误率>5%自动切回)
P99延迟≤Ollama基线×1.2,错误率≤0.5% 执行熔断脚本,或手动修改路由权重
Stage 3:全量接管 100%流量,Ollama仅作灾备 1. vLLM高可用部署(多实例+LoadBalancer)
2. Ollama灾备切换手册
3. 客户端SDK降级逻辑
连续72小时SLI达标,无P1告警 修改DNS或LB指向Ollama集群

这个路线图的价值在于:它把一个“能不能上线”的政治问题,转化成了“第几阶段没过”的工程问题。Stage 0不过,说明模型转换有bug,停在研发侧;Stage 1不过,说明API兼容性有问题,停在测试侧;Stage 2不过,说明性能或稳定性不足,停在SRE侧。每个阶段都有明确出口标准,避免“再调两天就好”的无限循环。

3. 核心细节解析与实操要点:模型层迁移的生死线

3.1 GGUF到HuggingFace格式转换:不只是 convert.py 那么简单

Ollama模型本质是GGUF格式(由 llama.cpp 定义),而vLLM原生只认HuggingFace格式( config.json + model.safetensors )。网上流传的“用 llama.cpp 自带的 convert.py 转一下就行”是最大误区。 convert.py 只负责权重拷贝,它 完全不处理 以下三个致命细节:

  • RoPE参数错位 :GGUF里 rope.freq_base rope.freq_scale 是分开存储的,HF的 config.json 里必须合并为 rope_theta 。Qwen系列模型的 rope_theta=1000000 ,如果转换时漏掉,长文本生成会直接乱码。实测:一个128K上下文的prompt,在Ollama里输出正常,在vLLM里从第8000token开始全是重复句。

  • Tokenizer不兼容 :Ollama的 tokenizer.model 是SentencePiece格式,vLLM要求 tokenizer.json (HuggingFace Tokenizer格式)。直接用 transformers AutoTokenizer.from_pretrained() 加载GGUF目录会失败,报错 OSError: Can't load tokenizer from ... 。必须用 llama.cpp convert_hf_to_gguf.py 反向操作,或用 llama-cpp-python 库的 LlamaTokenizer 导出。

  • 权重精度丢失 :GGUF支持Q4_K_M、Q5_K_S等多种量化, convert.py 默认转成FP16,但vLLM的AWQ/GPTQ量化需要原始FP16权重。如果源模型本身就是Q4_K_M量化版,转出的FP16权重已失真,再喂给AWQ量化器,效果雪上加霜。

我的实操方案(已验证于Qwen1.5-14B、Llama3-8B、Phi-3-mini):

  1. 绝不使用 llama.cpp convert.py 。改用HuggingFace官方 transformers 库的 convert_llama_weights_to_hf.py (位于 transformers/src/transformers/models/llama/convert_llama_weights_to_hf.py ),它专为Llama系模型设计,能正确映射 rope_theta

  2. Tokenizer转换分两步

    # Step 1: 用llama.cpp导出SentencePiece model
    ./llama-cli -m models/qwen1.5-14b.Q4_K_M.gguf -p "test" --dump-llama-key
    # Step 2: 用hf-transfer工具转成tokenizer.json
    pip install hf-transfer
    python -c "
    from transformers import AutoTokenizer
    tk = AutoTokenizer.from_pretrained('Qwen/Qwen1.5-14B', use_fast=False)
    tk.save_pretrained('./qwen1.5-14b-hf-tokenizer')
    "
    
  3. 权重校验必须做 :转换后,用同一prompt跑Ollama和vLLM,提取前10个token的logits(vLLM需加 --enable-prefix-caching --log-requests ),用 numpy.allclose() 比对:

    import numpy as np
    # ollama_logits.shape = (10, 128256) # vocab_size
    # vllm_logits.shape = (10, 128256)
    print(np.allclose(ollama_logits, vllm_logits, atol=1e-3)) # 必须返回True
    

    atol=1e-3 是底线, 1e-2 就说明转换有严重问题。

注意:Qwen2系列模型需额外处理 rope_scaling 。GGUF里是 {"type": "dynamic", "factor": 4.0} ,HF config里必须写成 {"rope_scaling": {"type": "dynamic", "factor": 4.0}} ,漏掉外层 rope_scaling key,vLLM启动直接报错 KeyError: 'rope_scaling'

3.2 量化策略选择:AWQ vs GPTQ vs FP16,没有银弹,只有权衡

vLLM支持三种主流量化:AWQ、GPTQ、FP16。选错一个,轻则吞吐降30%,重则输出错乱。这不是理论选择,是实测数据:

量化方式 启动时间 显存占用(Qwen1.5-14B) 吞吐(tokens/sec) 输出质量(vs FP16) 适用场景
FP16 12s 28.4GB (A100) 142 100% 开发调试、小流量POC
AWQ 48s 14.1GB 218 98.7% 主力生产环境(推荐)
GPTQ 32s 13.8GB 195 97.2% 老旧GPU(无FP16 Tensor Core)

为什么AWQ是首选?
AWQ(Activation-aware Weight Quantization)的核心思想是:不是所有权重同等重要,用激活值(activation)的分布来指导量化——高频出现的权重区间用更高精度(如INT4),低频区间用更低精度(如INT3)。vLLM的AWQ实现做了深度优化:它把量化后的权重与dequantize kernel编译进CUDA,避免运行时解量化开销。实测中,AWQ比GPTQ吞吐高12%,因为GPTQ的 exllama 内核在vLLM里仍有少量CPU-GPU同步等待。

但AWQ有硬门槛

  • 必须用 awq_model_path 指定已AWQ量化的模型(如 Qwen/Qwen1.5-14B-AWQ ),vLLM 不提供在线AWQ量化功能 。你得先用 autoawq 库离线量化:
    pip install autoawq
    python -c "
    from awq import AutoAWQForCausalLM
    from transformers import AutoTokenizer
    model = AutoAWQForCausalLM.from_pretrained('Qwen/Qwen1.5-14B', **{'low_cpu_mem_usage': True})
    tokenizer = AutoTokenizer.from_pretrained('Qwen/Qwen1.5-14B', trust_remote_code=True)
    model.quantize(tokenizer, quant_config={'zero_point': True, 'q_group_size': 128, 'w_bit': 4, 'version': 'GEMM'})
    model.save_quantized('./qwen1.5-14b-awq')
    "
    
  • 量化时 q_group_size=128 是黄金值。小于64,精度损失大;大于256,显存节省不明显。我试过 q_group_size=64 ,PPL(Perplexity)从12.3升到15.7,生成质量肉眼可见下降。

GPTQ的唯一优势是启动快 。如果你的部署流程要求“模型秒级热加载”,GPTQ是唯一选择。但它有个隐藏坑: gptq_ckpt 必须是 exllama 格式,不是HuggingFace原生格式。用 optimum 库导出的GPTQ模型,vLLM会报错 ValueError: Unsupported GPTQ checkpoint format 。必须用 exllamav2 export.py

git clone https://github.com/turboderp/exllamav2
cd exllamav2
python export.py --model_dir /path/to/hf/model --out_dir /path/to/gptq/ckpt --q_bits 4

3.3 KV Cache优化: --kv-cache-dtype fp8 不是开关,是手术刀

vLLM的PagedAttention机制把KV Cache从连续内存块,切成固定大小的Page(默认16个token一组)。 --kv-cache-dtype 参数控制每个Page里KV值的存储精度。Ollama用 llama.cpp f16_kv ,本质是FP16;vLLM默认也是FP16,但 --kv-cache-dtype fp8 能把它压到FP8。

FP8不是免费午餐 。A100/H100支持 E4M3_FNUZ 格式的FP8,但V100/T4不支持。强行开启,vLLM启动报错 CUDA error: no kernel image is available for execution on the device

实测数据(Qwen1.5-14B, A100 80GB):

kv-cache-dtype 显存占用 吞吐 P99延迟 输出质量(BLEU)
fp16 28.4GB 142 t/s 420ms 100%
fp8 19.2GB 189 t/s 385ms 99.3%

提升明显,但BLEU微降0.7%。对于客服对话、代码补全等场景,0.7% BLEU损失可接受;但对于法律文书生成,我们宁可多花9GB显存,也要保FP16。

更关键的是 --block-size 配合 。FP8 KV Cache必须配 --block-size 16 (默认值)。如果设成 32 ,Page内存对齐失效,显存反而涨到21GB,吞吐跌到172t/s。这是因为FP8 Page的内存布局要求严格16-token对齐, block-size=32 会导致一半Page浪费。

实操心得:在 nvidia-smi dmon -s u 监控下,先跑FP16基线,记下 MEM 列数值;再开FP8,观察 MEM 是否稳定下降。如果MEM波动剧烈(±2GB),说明Page分配不稳定,立刻检查 --block-size 是否为16。

4. 实操过程与核心环节实现:服务层迁移的完整流水线

4.1 启动命令的逐参数解剖:从 ollama run vllm 的17个必填项

Ollama的 ollama run qwen1.5:14b 背后,其实隐含了23个参数决策。vLLM把这些决策全部摊开,要求你显式声明。以下是生产环境必须明确的17个核心参数,附带我的取值逻辑和血泪教训:

  1. --model /models/qwen1.5-14b-hf 绝对路径 。vLLM不支持HuggingFace Hub别名(如 Qwen/Qwen1.5-14B ),必须本地化。原因:Hub拉取不可控,网络抖动会导致服务启动失败。我们用Ansible预同步模型到 /models/

  2. --dtype half :强制FP16。不要用 auto auto 在某些驱动版本下会误判为BF16,导致A100上启动失败。

  3. --tensor-parallel-size 2 :GPU数量。必须与物理GPU数一致。设成 1 而机器有2卡,第二卡闲置;设成 3 而只有2卡,启动直接OOM。

  4. --pipeline-parallel-size 1 99%场景设为1 。Pipeline Parallel用于超大模型(>70B),把模型层切到不同GPU。Qwen1.5-14B用Tensor Parallel就够了,设成>1反而增加通信开销。

  5. --max-model-len 32768 必须与模型原生context长度一致 。Qwen1.5-14B原生支持128K,但vLLM默认 max_model_len=4096 。设小了,长文本被截断;设大了,KV Cache预分配内存爆炸。计算公式: max_model_len = min(模型原生长度, 业务最长输入×1.5) 。我们业务最长输入是24K tokens,所以设32768。

  6. --max-num-seqs 256 :最大并发请求数。不是QPS!这是vLLM内部调度队列长度。Ollama无此概念,它动态创建线程。设太小(如64),高并发时请求排队,P99飙升;设太大(如1024),内存碎片化严重。我们的经验值: max-num-seqs = (GPU显存GB × 1000) ÷ 模型单请求平均显存MB 。Qwen1.5-14B单请求约120MB,2×A100=160GB,所以 160000÷120≈1333 ,但我们保守设256,留足余量。

  7. --max-num-batched-tokens 4096 最关键的吞吐参数 。它控制一个batch最多包含多少tokens。Ollama无batch概念。设太小(如1024),batch太碎,GPU利用率低;设太大(如8192),长请求会阻塞短请求。我们的调优方法:用 locust 压测,找P99延迟拐点。Qwen1.5-14B在4096时P99=385ms,到8192时升到490ms,所以定4096。

  8. --enforce-eager 开发期必开,生产期必关 。开启后禁用CUDA Graph,每次推理都重编译kernel,方便调试;关闭后启用Graph,启动慢3秒,但吞吐高22%。生产环境必须关。

  9. --kv-cache-dtype fp8 :如前所述,A100/H100必开。

  10. --quantization awq :量化方式,与模型路径匹配。

  11. --awq-ckpt /models/qwen1.5-14b-awq :AWQ模型路径,必须与 --quantization 一致。

  12. --trust-remote-code :Qwen/Phi等模型需此参数加载自定义 modeling_*.py

  13. --disable-log-requests 生产环境必关 。日志打满磁盘。我们用 --log-level warning 降级。

  14. --port 8000 :API端口,与Ollama的 /api/chat/completions 对齐。

  15. --host 0.0.0.0 :绑定所有网卡,Ollama默认如此。

  16. --api-key sk-xxx :OpenAI兼容API密钥,Ollama无此概念,但vLLM需要。我们用Vault动态注入。

  17. --enable-prefix-caching 长上下文场景必开 。启用前缀缓存,相同system prompt的多次请求,只计算一次KV Cache。实测在RAG场景下,P99降低35%。

最终生产启动命令(精简版):

python -m vllm.entrypoints.api_server \
  --model /models/qwen1.5-14b-hf \
  --dtype half \
  --tensor-parallel-size 2 \
  --max-model-len 32768 \
  --max-num-seqs 256 \
  --max-num-batched-tokens 4096 \
  --kv-cache-dtype fp8 \
  --quantization awq \
  --awq-ckpt /models/qwen1.5-14b-awq \
  --trust-remote-code \
  --port 8000 \
  --host 0.0.0.0 \
  --api-key $(VAULT_TOKEN) \
  --enable-prefix-caching \
  --disable-log-requests

4.2 OpenAI兼容层的深度适配:让客户端零修改

Ollama的 /api/chat/completions 是极简实现,vLLM的兼容层虽标榜OpenAI,但有5处必须手工patch的差异:

  1. stream_options 缺失 :Ollama忽略此字段,vLLM默认不返回 usage 字段。必须加 --enable-chunked-prefill --return-tokens-as-token-ids ,并在API响应里手动注入 usage

    # vLLM源码 patch: vllm/entrypoints/openai/serving_chat.py
    # 在ChatCompletionResponse构造后,插入:
    response.usage = UsageInfo(
        prompt_tokens=len(prompt_token_ids),
        completion_tokens=len(output_token_ids),
        total_tokens=len(prompt_token_ids) + len(output_token_ids)
    )
    
  2. response_format 不支持JSON Schema :Ollama不支持,vLLM默认只支持 {"type": "json_object"} 。要支持Schema,需启用 --guided-decoding-backend lm-format-enforcer ,并安装 lm-format-enforcer 包。

  3. tool_choice 行为不一致 :Ollama的 "tool_choice": "none" 表示禁用tool call,vLLM需设 "tool_choice": "none" + "tools": [] 才生效。否则仍会尝试调用。

  4. logprobs 字段位置错乱 :Ollama的 logprobs choices[0].logprobs 下,vLLM在 choices[0].logprobs.content 下。必须用Nginx或API网关做字段重写。

  5. Health Check端点不兼容 :Ollama用 GET /api/tags ,vLLM用 GET /health 。我们用Envoy做统一健康检查路由:

    # envoy.yaml
    http_filters:
      - name: envoy.filters.http.lua
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
          default_source_code: |
            function envoy_on_request(request_handle)
              if request_handle:headers():get(":path") == "/api/tags" then
                request_handle:headers():replace(":path", "/health")
              end
            end
    

实操心得:用 curl -v 同时调Ollama和vLLM,把响应体存为 ollama_resp.json vllm_resp.json ,用 jq -S . ollama_resp.json > ollama_fmt.json 标准化后, diff ollama_fmt.json vllm_fmt.json 。所有差异点,就是你必须patch的地方。我们累计patch了12处,其中7处已提PR给vLLM主干。

4.3 生产级部署:从单机 python -m 到K8s Operator

Ollama的 ollama serve 是单机进程,vLLM必须走向云原生。我们的K8s部署栈如下:

  • 基础镜像 :基于 nvcr.io/nvidia/pytorch:23.10-py3 ,预装CUDA 12.2、PyTorch 2.1、vLLM 0.4.2。避免每次启动pip install耗时。

  • 模型存储 :用 hostPath 挂载NFS共享存储 /models/ 。所有Pod读同一份模型,避免镜像臃肿。NFS权限设为 755 ,vLLM进程以 uid=1001 运行。

  • 资源限制

    resources:
      limits:
        nvidia.com/gpu: 2
        memory: 64Gi
        cpu: "8"
      requests:
        nvidia.com/gpu: 2
        memory: 64Gi
        cpu: "8"
    

    关键点 memory request必须≥GPU显存×1.2。A100 80GB显存,所以request 64Gi(≈80GB×0.8)。否则K8s调度器可能把Pod塞到内存不足的节点。

  • 就绪探针(Readiness Probe)

    readinessProbe:
      httpGet:
        path: /health
        port: 8000
      initialDelaySeconds: 60
      periodSeconds: 30
      timeoutSeconds: 5
      failureThreshold: 3
    

    initialDelaySeconds: 60 是重点。vLLM加载14B AWQ模型需45秒,加15秒缓冲。设太小,Pod反复重启。

  • 滚动更新策略

    strategy:
      type: RollingUpdate
      rollingUpdate:
        maxSurge: 1
        maxUnavailable: 0
    

    maxUnavailable: 0 确保更新时至少1个Pod在线,避免服务中断。

  • 日志采集 :用Fluent Bit收集 stdout ,过滤 INFO 级别, error 关键字日志发到ES。特别采集 vLLM 特有的 ENGINE 日志,如 Engine step took X ms

  • 监控指标 :Prometheus抓取 http://vllm-service:8000/metrics ,核心告警规则:

    • vllm:gpu_cache_usage_ratio{job="vllm"} > 0.95 (KV Cache即将爆满)
    • vllm:request_waiting_time_seconds{job="vllm"} > 5 (请求排队超5秒)
    • vllm:generation_tokens_total{job="vllm"} < 100 (吞吐骤降,可能模型卡死)

5. 常见问题与排查技巧实录:那些让我凌晨三点爬起来的Bug

5.1 模型层典型问题:Token不一致的10种死法

问题1:RoPE参数错位,长文本输出乱码

  • 现象 :Ollama输出正常,vLLM在8000token后开始重复、乱码。
  • 排查 vllm 启动加 --log-level debug ,看日志是否有 RoPE scaling factor 相关warning。
  • 根因 :GGUF转HF时, rope_theta 没写入 config.json
  • 修复 :手动编辑 config.json ,添加 "rope_theta": 1000000 (Qwen)或 "rope_theta": 10000 (Llama)。

问题2:Tokenizer不一致,中文分词错误

  • 现象 :Ollama里“人工智能”分1个token,vLLM分3个。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值