3、vLLM部署基础:使用Deployment部署vLLM服务、暴露Service、配置资源限制

好,咱们进入实战环节。这一章我会带你亲手把vLLM服务跑在K8s上。说白了,就是三步走:用Deployment把模型拉起来,用Service把端口露出去,再给Pod拴上资源缰绳。这三件事搞定了,你的大模型推理服务才算真正“上线”。

3.1 为什么非得用Deployment?

你可能会问:我直接 docker run 跑一个vLLM容器不行吗?当然行,但那是玩具。在生产环境里,Pod挂了谁帮你重启?流量大了谁帮你扩容?版本更新了谁帮你滚动升级?

Deployment 就是干这个的。它帮你管理Pod的“生老病死”。我个人习惯是,任何无状态服务上来先套一个Deployment,省心。

核心要点: Deployment 保证你声明的副本数(replicas)始终存活。Pod挂了,它自动拉一个新的起来。你只管写YAML,剩下的交给它。

3.2 编写vLLM的Deployment YAML

先看一个最简版本。假设我们要部署 Qwen2.5-7B-Instruct 这个模型。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-qwen25-7b
  labels:
    app: vllm-qwen25
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vllm-qwen25
  template:
    metadata:
      labels:
        app: vllm-qwen25
    spec:
      containers:
      - name: vllm
        image: vllm/vllm-openai:latest
        command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
        args:
          - "--model"
          - "/models/Qwen2.5-7B-Instruct"
          - "--port"
          - "8000"
        ports:
        - containerPort: 8000
          name: http
        volumeMounts:
        - name: model-storage
          mountPath: /models
      volumes:
      - name: model-storage
        persistentVolumeClaim:
          claimName: model-pvc

嗯,这里要注意几个点:

  • 镜像版本:别用 latest,我在项目中被坑过——某次latest悄悄升级了,结果模型加载方式变了,服务直接崩了。建议固定到具体版本号,比如 v0.6.3
  • 模型加载路径:我习惯把模型文件放在PVC(持久卷)里,这样Pod重启后不用重新下载。如果你用HuggingFace在线拉取,记得给Pod配好网络和认证。
  • 启动参数--port 8000 是vLLM默认的API端口,后面Service要跟这个对上。

3.3 暴露Service:让外界能访问

Deployment把Pod跑起来了,但Pod的IP是动态的。你总不能每次重启都去查IP吧?Service 就是给Pod一个固定的“入口”

我个人最常用的是 ClusterIP 类型,配合Ingress对外暴露。但如果你只是想快速测试,用 NodePort 也行。

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

这段YAML干了什么?

  • 它创建了一个名叫 vllm-qwen25-svc 的Service
  • 监听 8000 端口,流量转发到Pod的 8000 端口
  • 通过 selector 匹配标签 app: vllm-qwen25 的Pod

小技巧: 如果你想让Service只转发到健康的Pod,可以配合Readiness Probe使用。vLLM本身提供了 /health 端点,我建议加上存活探针和就绪探针,这样K8s能自动摘掉不健康的Pod。

3.4 配置资源限制:别让模型把节点吃垮

大模型推理是典型的“算力饥渴”型负载。不加资源限制,一个Pod能把整台机器的显存和CPU吃光,其他服务全得遭殃。

我曾经遇到过一件事:团队里有人部署了一个70B的模型,没配资源限制,结果直接把GPU节点的显存打爆,节点挂了,上面十几个Pod全被驱逐。从那以后,我定了个规矩——所有推理服务必须显式声明资源请求和限制

来看一个带资源限制的完整示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-qwen25-7b
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vllm-qwen25
  template:
    metadata:
      labels:
        app: vllm-qwen25
    spec:
      containers:
      - name: vllm
        image: vllm/vllm-openai:v0.6.3
        command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
        args:
          - "--model"
          - "/models/Qwen2.5-7B-Instruct"
          - "--port"
          - "8000"
          - "--max-model-len"
          - "8192"
          - "--gpu-memory-utilization"
          - "0.9"
        ports:
        - containerPort: 8000
        resources:
          requests:
            memory: "16Gi"
            cpu: "4"
            nvidia.com/gpu: 1
          limits:
            memory: "20Gi"
            cpu: "8"
            nvidia.com/gpu: 1
        volumeMounts:
        - name: dshm
          mountPath: /dev/shm
        - name: model-storage
          mountPath: /models
      volumes:
      - name: dshm
        emptyDir:
          medium: Memory
          sizeLimit: "8Gi"
      - name: model-storage
        persistentVolumeClaim:
          claimName: model-pvc

这里有几个关键配置,我展开说说:

配置项 说明 我的建议
resources.requests Pod启动时保证能拿到的资源 按模型实际需求来,7B模型至少16GB内存+1块GPU
resources.limits Pod最多能用的资源上限 比requests高20%左右,留点余量
nvidia.com/gpu 声明需要GPU 记得提前装好NVIDIA Device Plugin
/dev/shm 共享内存,vLLM内部通信用 建议挂载为Memory类型的emptyDir,大小8GB起步
--gpu-memory-utilization 控制GPU显存利用率 0.9是比较稳妥的值,留10%给其他进程

警告: 千万别把 limits 设得比 requests 大太多。比如你请求1块GPU但限制4块,调度器可能把Pod调度到只有1块GPU的节点上,结果Pod启动后想用4块,直接OOM。requests和limits的GPU数量必须一致。

3.5 本章知识体系图

下面这张图帮你理清Deployment、Service、资源限制三者之间的关系:

vLLM 部署核心三要素 Deployment 管理Pod生命周期 自动重启/滚动更新 声明式副本数管理 Service 固定访问入口 负载均衡到Pod 支持ClusterIP/NodePort 资源限制 CPU/内存/GPU配额 防止资源争抢 requests & limits 暴露 保障 三者缺一不可 没有Deployment,Pod挂了没人管;没有Service,客户端找不到入口;没有资源限制,节点可能被打爆 Pod 1 Pod 2 Pod 3 ... 由Deployment统一管理

3.6 部署与验证

YAML写好了,怎么跑起来?很简单:

# 创建PVC(假设你已经准备好了模型数据)
kubectl apply -f model-pvc.yaml

# 部署vLLM
kubectl apply -f vllm-deployment.yaml

# 暴露Service
kubectl apply -f vllm-service.yaml

# 查看状态
kubectl get pods -l app=vllm-qwen25
kubectl get svc vllm-qwen25-svc

# 测试调用(从集群内)
curl http://vllm-qwen25-svc:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "/models/Qwen2.5-7B-Instruct",
    "messages": [{"role": "user", "content": "你好"}]
  }'

如果返回了正常的JSON响应,恭喜你,vLLM服务已经在K8s上跑起来了。

避坑指南: 我第一次部署时,curl一直超时。查了半天发现是Pod里的vLLM监听的是 127.0.0.1:8000,而不是 0.0.0.0:8000。记得在启动参数里加 --host 0.0.0.0,否则Service转发不过去。

好了,这一章的内容就这些。Deployment、Service、资源限制,这三板斧抡好了,vLLM部署的基础就算打牢了。下一章我们会聊更进阶的话题——如何用HPA做自动扩缩容,让服务能扛住突发流量。


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