3、vLLM单实例部署:创建Deployment、配置Service、暴露端口、验证推理服务

好,咱们正式开始动手了。这一章的目标很明确——在K8s里跑起一个vLLM实例,并且能从集群外部访问它。说白了,就是让大模型在K8s里“活”起来。

我个人习惯,先把最核心的Deployment和Service搞定,再慢慢调优。你想想看,如果连最基本的推理都跑不通,后面那些花里胡哨的配置全是白搭。

3.1 创建Deployment:让Pod稳定运行

Deployment是K8s里管理Pod的“大管家”。它负责保证你想要的Pod数量始终在线,挂了自动重启,更新版本也能平滑滚动。

下面这个YAML,是我在项目中反复打磨过的模板。注意看几个关键点:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-server
  labels:
    app: vllm-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vllm-server
  template:
    metadata:
      labels:
        app: vllm-server
    spec:
      containers:
      - name: vllm
        image: vllm/vllm-openai:latest
        imagePullPolicy: IfNotPresent
        args:
        - "--model"
        - "/models/Qwen2.5-7B-Instruct"
        - "--served-model-name"
        - "qwen2.5"
        - "--port"
        - "8000"
        - "--max-model-len"
        - "8192"
        - "--gpu-memory-utilization"
        - "0.9"
        ports:
        - containerPort: 8000
          name: http
        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

核心参数解读:

  • --served-model-name:这个很关键。客户端调用时用的模型名,跟你实际模型文件名字可以不一样。我习惯用简短的名字,方便测试。
  • --gpu-memory-utilization 0.9:给GPU显存留10%的余量。我曾经设成0.95,结果并发一上来直接OOM,Pod被K8s杀掉重启,那叫一个惨。
  • resources.limits nvidia.com/gpu: 1:明确告诉K8s,这个Pod需要1张GPU。不加这个,调度器不会给你分配GPU节点。

我的小技巧: 如果你用的是HuggingFace上的模型,可以把模型文件挂载到PVC里,避免每次启动都下载。特别是模型文件几十G的时候,下载一次能省半小时。

3.2 配置Service:打通集群内部访问

Deployment跑起来了,Pod也有了IP。但Pod是会漂移的——挂了重启,IP就变了。这时候就需要Service出场了。

Service提供一个稳定的虚拟IP和DNS名称,让集群内的其他服务能找到你的vLLM实例。

apiVersion: v1
kind: Service
metadata:
  name: vllm-service
  labels:
    app: vllm-server
spec:
  type: ClusterIP
  selector:
    app: vllm-server
  ports:
  - protocol: TCP
    port: 8000
    targetPort: 8000
    name: http

这里我用了ClusterIP类型。为什么?因为大多数场景下,推理服务只对集群内部暴露,前端或其他微服务通过Service名调用就行。如果你非要让外部直接访问,后面会讲NodePort和Ingress。

注意: Service的selector必须和Deployment里Pod的labels完全匹配。少一个标签都匹配不上,流量就转发不过去。我踩过这个坑,排查了半天才发现是label写错了。

3.3 暴露端口:让外部也能调用

ClusterIP只能在集群内部访问。如果你想在本地用curl测试,或者让外部客户端调用,需要把端口暴露出来。

有两种常见方式:

方式 优点 缺点 适用场景
NodePort 配置简单,直接暴露节点端口 端口范围有限(30000-32767),安全性差 开发测试环境
LoadBalancer 自带负载均衡,有公网IP 需要云厂商支持,成本高 生产环境

我个人习惯,开发阶段用NodePort快速验证。改一下Service类型就行:

apiVersion: v1
kind: Service
metadata:
  name: vllm-service-nodeport
spec:
  type: NodePort
  selector:
    app: vllm-server
  ports:
  - protocol: TCP
    port: 8000
    targetPort: 8000
    nodePort: 30080
    name: http

这样,你就可以通过任意节点IP:30080访问vLLM了。

3.4 验证推理服务:跑一个请求试试

部署完了,怎么知道它好不好使?别急着上复杂测试,先发一个简单的curl请求看看。

先找到Service的ClusterIP:

kubectl get svc vllm-service

然后,从集群内任意一个Pod里发请求:

curl http://vllm-service:8000/v1/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen2.5",
    "prompt": "你好,请介绍一下你自己",
    "max_tokens": 100,
    "temperature": 0.7
  }'

如果返回了类似这样的JSON,说明服务跑通了:

{
  "id": "cmpl-xxx",
  "object": "text_completion",
  "choices": [
    {
      "text": "你好!我是Qwen2.5,一个由阿里云开发的大语言模型...",
      "index": 0,
      "finish_reason": "length"
    }
  ]
}

验证要点:

  • 响应时间: 首次请求可能会慢一些(模型加载),后续应该稳定在秒级。
  • 显存占用:nvidia-smi看看GPU显存是否被正确分配。如果显存占用为0,说明模型没加载成功。
  • 日志检查: kubectl logs <pod-name> 看看有没有报错。常见错误是模型路径不对或显存不足。

避坑指南: 我曾经遇到一个问题——curl请求返回了空结果,但Pod日志显示正常。后来发现是--served-model-name和请求里的model字段不匹配。记住,这两个名字必须一致。

3.5 本章知识体系

下面这张图,帮你理清vLLM单实例部署的核心流程:

vLLM单实例部署核心流程 1. 创建Deployment 定义Pod模板 指定GPU资源 2. 配置Service 稳定虚拟IP 内部DNS解析 3. 暴露端口 NodePort/LB 外部可访问 4. 验证推理服务 curl请求测试 检查响应与日志 关键点:Deployment保证Pod稳定,Service提供固定入口 端口暴露方式根据环境选择,验证时注意模型名匹配

嗯,到这里,一个能用的vLLM单实例就跑起来了。你可能会问:“就一个实例,够用吗?” 别急,后面我们会聊多实例和负载均衡。先把单实例玩熟,后面才能玩得转。


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