3. vLLM推理引擎部署:从零搭建你的推理服务
聊到K8s多租户推理,vLLM绝对是个绕不开的角色。我最早接触它是在一次大模型推理优化的项目中,当时团队被显存占用和首token延迟折磨得够呛。后来换上vLLM,效果立竿见影。说白了,vLLM就是专门为LLM推理打造的高性能引擎,核心卖点是PagedAttention——一种把KV Cache分页管理的神奇机制。
3.1 vLLM简介:为什么选它?
vLLM是加州大学伯克利分校开源的项目,目标很纯粹:让大模型推理又快又省。它解决了传统推理框架的几个痛点:
- 显存碎片化:传统框架的KV Cache是连续分配的,容易产生碎片。vLLM用分页机制,像操作系统管理内存一样管理显存。
- 批处理效率低:vLLM支持动态batching,请求来了随时加入,不用等当前批次跑完。
- 首token延迟高:通过优化prefill阶段,首token生成速度提升明显。
我在项目中实测过,同样一个13B模型,vLLM的吞吐量比HuggingFace原生的transformers高出5-10倍。嗯,这个数字不夸张,你想想看,生产环境里这就是实打实的成本节省。
核心优势总结:vLLM = PagedAttention + 连续批处理 + 高效显存管理。适合部署在K8s集群中,作为多租户推理的底座。
3.2 Docker镜像构建:从源码到容器
部署vLLM的第一步,是把它的运行环境打包成Docker镜像。我个人习惯用多阶段构建,这样镜像体积小,部署快。
先看Dockerfile怎么写:
# 第一阶段:构建环境
FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04 AS builder
RUN apt-get update && apt-get install -y \
python3 python3-pip git build-essential \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN git clone https://github.com/vllm-project/vllm.git
WORKDIR /app/vllm
RUN pip3 install --upgrade pip && \
pip3 install torch==2.1.0 && \
pip3 install -e .
# 第二阶段:运行环境
FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04
COPY --from=builder /usr/local/lib/python3.10 /usr/local/lib/python3.10
COPY --from=builder /app/vllm /app/vllm
WORKDIR /app/vllm
ENTRYPOINT ["python3", "-m", "vllm.entrypoints.openai.api_server"]
这里有个坑,我曾经踩过:CUDA版本必须和宿主机驱动匹配。你想想看,如果宿主机是CUDA 11.8,你镜像用12.1,容器启动时就会报错。建议先查一下nvidia-smi的输出,确认驱动支持的CUDA版本。
小技巧:构建镜像时加上--network=host参数,能加速pip下载。另外,建议把模型文件挂载到宿主机,而不是打进镜像里,否则镜像体积会大到离谱。
构建命令很简单:
docker build -t vllm-server:latest -f Dockerfile .
构建完成后,可以本地测试一下:
docker run --gpus all -p 8000:8000 \
-v /path/to/models:/models \
vllm-server:latest \
--model /models/llama-2-7b-chat \
--tensor-parallel-size 1
3.3 Deployment与Service配置:让vLLM跑在K8s上
镜像准备好了,接下来就是把它部署到K8s集群里。Deployment负责管理Pod的生命周期,Service负责暴露服务。
先看Deployment的YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-inference
namespace: ai-inference
spec:
replicas: 2
selector:
matchLabels:
app: vllm
template:
metadata:
labels:
app: vllm
spec:
containers:
- name: vllm
image: vllm-server:latest
ports:
- containerPort: 8000
env:
- name: MODEL_NAME
value: "llama-2-7b-chat"
- name: TENSOR_PARALLEL_SIZE
value: "1"
resources:
limits:
nvidia.com/gpu: 1
requests:
nvidia.com/gpu: 1
volumeMounts:
- name: model-storage
mountPath: /models
volumes:
- name: model-storage
persistentVolumeClaim:
claimName: model-pvc
这里要注意几个点:
- resources.limits.nvidia.com/gpu:指定每个Pod需要1张GPU。如果你用多卡推理,可以改成2或4。
- tensor-parallel-size:张量并行度,一般和GPU数量一致。我建议单卡推理时设为1,多卡时再调整。
- 模型挂载:用PVC把模型文件挂载进来,避免每次Pod重启都重新下载。
Service配置相对简单:
apiVersion: v1
kind: Service
metadata:
name: vllm-service
namespace: ai-inference
spec:
selector:
app: vllm
ports:
- protocol: TCP
port: 8000
targetPort: 8000
type: ClusterIP
如果你需要对外暴露服务,可以把type改成NodePort或LoadBalancer。不过在生产环境,我建议用Ingress统一管理入口。
避坑指南:我曾经遇到过Pod启动后一直CrashLoopBackOff,查了半天发现是模型路径写错了。建议在Deployment里加一个initContainer,先检查模型文件是否存在。
3.4 GPU资源调度:NVIDIA GPU Operator
K8s默认是不认识GPU的。你需要安装NVIDIA的设备插件,才能让Pod申请GPU资源。而GPU Operator,说白了就是把这些杂事打包成一个Operator,一键搞定。
安装GPU Operator:
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update
helm install gpu-operator nvidia/gpu-operator \
--namespace gpu-operator \
--create-namespace
安装完成后,它会自动部署:
- NVIDIA驱动(如果宿主机没有)
- Container Toolkit(让容器能访问GPU)
- Device Plugin(让K8s能调度GPU)
- DCGM Exporter(监控GPU指标)
验证是否安装成功:
kubectl get pods -n gpu-operator
kubectl describe node | grep nvidia.com/gpu
如果看到节点上有nvidia.com/gpu资源,说明GPU Operator工作正常。
核心逻辑:GPU Operator把GPU资源抽象成K8s的可调度资源。你只需要在Pod的resources里声明nvidia.com/gpu: 1,K8s就会自动把Pod调度到有GPU的节点上。
下面这张图展示了vLLM推理服务在K8s上的整体架构:
从这张图可以看得很清楚:用户请求先打到Service,Service负载均衡到后端的Pod,每个Pod跑一个vLLM容器,独占一张GPU。GPU Operator在底层负责把GPU资源暴露给K8s调度。
生产建议:如果你有多租户需求,可以结合K8s的Namespace和ResourceQuota来做资源隔离。每个租户一个Namespace,限制GPU使用上限。我在项目中就是这么做的,效果不错。
好了,vLLM的部署流程就这些。从镜像构建到K8s部署,再到GPU资源调度,每一步都有它的门道。你动手试试看,遇到问题随时翻翻这部分内容。
公众号:蓝海资料掘金营,微信deep3321