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单实例就跑起来了。你可能会问:“就一个实例,够用吗?” 别急,后面我们会聊多实例和负载均衡。先把单实例玩熟,后面才能玩得转。
公众号:蓝海资料掘金营,微信deep3321