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上的整体架构:

vLLM推理服务K8s部署架构 用户请求 Service (vllm-service) Deployment (vllm-inference) - Replicas: 2 Pod 1 vLLM容器 GPU: 1 (nvidia.com/gpu) Pod 2 vLLM容器 GPU: 1 (nvidia.com/gpu) NVIDIA GPU Operator (驱动 + Device Plugin + DCGM)

从这张图可以看得很清楚:用户请求先打到Service,Service负载均衡到后端的Pod,每个Pod跑一个vLLM容器,独占一张GPU。GPU Operator在底层负责把GPU资源暴露给K8s调度。

生产建议:如果你有多租户需求,可以结合K8s的Namespace和ResourceQuota来做资源隔离。每个租户一个Namespace,限制GPU使用上限。我在项目中就是这么做的,效果不错。

好了,vLLM的部署流程就这些。从镜像构建到K8s部署,再到GPU资源调度,每一步都有它的门道。你动手试试看,遇到问题随时翻翻这部分内容。


公众号:蓝海资料掘金营,微信deep3321