3. vLLM架构与部署:vLLM推理引擎简介、模型加载与推理流程、K8s上部署vLLM
好,咱们进入第三章。这一章我打算聊聊vLLM这个推理引擎本身。说实话,我第一次接触vLLM的时候,心里还犯嘀咕:市面上推理框架那么多,它凭什么脱颖而出?后来在生产环境里跑了几轮,我才真正体会到它的设计精妙之处。尤其是那个PagedAttention,简直是显存管理的“神来之笔”。
3.1 vLLM推理引擎简介
vLLM,全称是Virtual Large Language Model,一个专门为LLM推理优化的高性能引擎。它由加州大学伯克利分校的研究团队开源,核心卖点就两个:快和省。
快,体现在它的推理吞吐量。相比Hugging Face Transformers的原生实现,vLLM在相同硬件上能提升2-4倍的吞吐量。省,则体现在显存利用上。它通过PagedAttention技术,把显存利用率从传统的40%-50%直接拉到了95%以上。
我个人习惯把vLLM比作“显存界的拼多多”——它把原本需要一整块连续显存才能装下的KV Cache,拆成一个个小“页面”,按需分配。你想想看,这就像以前你租房必须整租一套三居室,现在可以按房间租,甚至按床位租,利用率自然上去了。
核心特性一览:
- PagedAttention:显存管理革命,解决KV Cache碎片化问题
- 连续批处理(Continuous Batching):动态合并请求,不浪费算力
- 量化支持:原生支持GPTQ、AWQ、FP8等量化格式
- 多模态扩展:支持LLaVA等视觉语言模型
- OpenAI兼容API:一行代码替换原有推理服务
我在项目中遇到过最头疼的事,就是模型推理时的显存爆炸。一个70B的模型,用FP16加载,光参数就占140GB显存。再加上KV Cache,A100 80G的卡根本扛不住。后来换成vLLM,配合量化+动态批处理,同样的卡能同时服务4个并发请求,而且延迟还降了30%。
3.2 模型加载与推理流程
vLLM的推理流程,说白了就三步:加载 → 调度 → 推理。但每一步都有讲究。
3.2.1 模型加载
vLLM支持从Hugging Face Hub直接拉模型,也支持本地路径。加载时,它会自动检测模型架构,选择合适的层实现。比如LLaMA系列、Mistral系列、Falcon系列,它都有对应的优化版本。
# 最简单的加载方式
from vllm import LLM, SamplingParams
# 加载模型,自动选择最优实现
llm = LLM(model="meta-llama/Llama-2-7b-chat-hf",
tensor_parallel_size=2, # 使用2张GPU
dtype="float16", # 半精度加载
max_model_len=4096) # 最大上下文长度
这里有个坑,我曾经踩过。就是max_model_len这个参数。如果你设得太小,长文本请求会被截断;设得太大,显存预分配会过多。我建议根据实际业务场景来定,一般对话场景设2048或4096就够用,文档摘要场景可能需要8192以上。
3.2.2 推理调度
vLLM的调度器是它的灵魂。它维护了一个“等待队列”和一个“运行队列”。新请求进来,先进等待队列。调度器会定期检查,如果当前GPU还有空闲显存,就从等待队列里拉请求进来,和正在运行的请求“拼车”。
这就是连续批处理的核心思想——不等待批处理完成,而是动态插入新请求。传统批处理是“凑够一车才发车”,vLLM是“随时有人随时上车,到站就下”。
我的经验:调度器的max_num_seqs参数控制最大并发数。设得太小,吞吐上不去;设得太大,单个请求的延迟会变高。我一般从16开始调,观察GPU利用率和P99延迟,找到平衡点。
3.2.3 推理执行
推理执行阶段,vLLM会调用CUDA kernel进行前向计算。这里有个关键优化——算子融合。vLLM把多个小算子合并成一个大算子,减少Kernel Launch的开销。比如把Attention中的QKV投影合并成一个矩阵乘法,能提升10%-15%的吞吐。
# 设置采样参数
sampling_params = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=512,
stop=["</s>", "<|im_end|>"]
)
# 执行推理
outputs = llm.generate(["请介绍一下vLLM的架构"], sampling_params)
# 输出结果
for output in outputs:
print(output.outputs[0].text)
3.3 K8s上部署vLLM
好了,模型在单机上跑通了。但生产环境里,我们不可能手动管理GPU资源。这时候Kubernetes就派上用场了。我一般用vLLM官方Docker镜像 + K8s Deployment + HPA这套组合拳。
3.3.1 构建vLLM镜像
vLLM官方提供了预构建镜像,但生产环境我建议自己构建,方便集成监控和自定义配置。
# Dockerfile
FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04
RUN apt-get update && apt-get install -y python3 python3-pip
# 安装vLLM
RUN pip3 install vllm
# 复制启动脚本
COPY start.sh /start.sh
RUN chmod +x /start.sh
EXPOSE 8000
ENTRYPOINT ["/start.sh"]
start.sh里我一般会设置环境变量,比如模型路径、GPU数量、并发数等。
#!/bin/bash
python3 -m vllm.entrypoints.openai.api_server \
--model /models/llama-2-7b \
--tensor-parallel-size $TP_SIZE \
--max-num-seqs $MAX_NUM_SEQS \
--port 8000
3.3.2 K8s Deployment配置
部署到K8s时,有几个关键点要注意:GPU资源声明、节点亲和性、健康检查。
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-server
spec:
replicas: 1
selector:
matchLabels:
app: vllm
template:
metadata:
labels:
app: vllm
spec:
containers:
- name: vllm
image: myregistry/vllm:latest
ports:
- containerPort: 8000
env:
- name: TP_SIZE
value: "2"
- name: MAX_NUM_SEQS
value: "32"
resources:
limits:
nvidia.com/gpu: 2 # 申请2张GPU
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
注意:GPU资源一定要用limits而不是requests。因为GPU是独占资源,不能超分。另外,tensor-parallel-size必须和nvidia.com/gpu的数量一致,否则vLLM会报错。
3.3.3 暴露服务
部署好Pod后,用Service暴露出来。我一般用ClusterIP,前面再挂一个Ingress做路由和限流。
apiVersion: v1
kind: Service
metadata:
name: vllm-service
spec:
selector:
app: vllm
ports:
- port: 8000
targetPort: 8000
type: ClusterIP
3.3.4 结合HPA自动扩缩
vLLM的HPA配置比较特殊。因为GPU资源不能像CPU那样弹性伸缩,所以HPA的指标不能是CPU利用率。我一般用自定义指标,比如“排队请求数”或“GPU显存利用率”。
具体做法是:在vLLM容器里暴露/metrics端点,用Prometheus采集,然后通过Prometheus Adapter把指标暴露给HPA。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vllm-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm-server
minReplicas: 1
maxReplicas: 5
metrics:
- type: Pods
pods:
metric:
name: vllm_queue_size
target:
type: AverageValue
averageValue: 10 # 排队请求超过10个就扩容
我曾经踩过一个坑:HPA扩容太快,导致GPU资源争抢,反而拖慢了整体吞吐。后来我加了--horizontal-pod-autoscaler-downscale-stabilization参数,让缩容慢一点,避免频繁抖动。
3.4 知识体系总览
下面这张图,是我梳理的vLLM在K8s上部署的完整知识体系。你可以把它当作一个“地图”,后续遇到问题就知道该往哪个方向排查。
这张图把vLLM的核心架构、推理流程和K8s部署串在了一起。你从左上角开始看:vLLM引擎提供三大核心能力,推理流程分三步走,K8s部署则关注镜像、配置和扩缩容。最下面是我总结的避坑点,都是真金白银换来的教训。
好了,这一章的内容就到这里。vLLM的部署其实不难,难的是把每个环节的参数调优到最佳状态。下一章我会专门讲HPA的配置细节,包括如何用自定义指标实现精准扩缩容。