vLLM on Kubernetes 生产部署与 KEDA 弹性伸缩实战
完整讲解 vLLM 0.21 在 Kubernetes 上的生产级部署方案,包括多 GPU Tensor Parallel 配置、Readiness/Liveness 探针设置、KEDA 基于 Prometheus 队列深度自动弹性伸缩,以及 FP8 精度与 Prefix Caching 生产调优。
vLLM 已成为 2026 年最主流的开源 LLM 推理引擎,0.21 版本新增 KV Offload 与 Hybrid Memory Allocator、Speculative Decoding 对 Reasoning 模型的支持,以及默认开启 Prefix Caching。生产环境部署 vLLM 的难点不在于单机启动,而在于如何在 Kubernetes 上做好健康探针、弹性伸缩与资源隔离。本文以 H100 节点为例,提供完整的生产部署配方。
vLLM 0.21 核心特性速览
| 特性 | 说明 |
|---|---|
| KV Cache Offload | 将 KV Cache 卸载到 CPU 内存,支持超长上下文 |
| Prefix Caching(默认开启) | 多轮对话显存复用,降低 TTFT 最高 60% |
| Speculative Decoding | 对 Reasoning 模型支持思考预算约束 |
| TOKENSPEED_MLA 后端 | Blackwell GPU 专用加速后端 |
| FP8 默认路径 | Hopper/Blackwell 原生 FP8,几乎无质量损失 |
系统依赖
- NVIDIA Driver ≥ 525
- CUDA ≥ 12.1
- NVIDIA Container Toolkit ≥ 1.14
- Kubernetes ≥ 1.27,含 NVIDIA GPU Operator
- KEDA v2.x(弹性伸缩)
- Prometheus + Prometheus Adapter(指标驱动)
基础 Deployment 配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-qwen3-32b
namespace: llm-serving
labels:
app: vllm
model: qwen3-32b
spec:
replicas: 1
selector:
matchLabels:
app: vllm
model: qwen3-32b
template:
metadata:
labels:
app: vllm
model: qwen3-32b
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
spec:
# GPU 节点专属污点容忍
tolerations:
- key: dedicated
operator: Equal
value: gpu
effect: NoSchedule
nodeSelector:
node-role: gpu-inference
# 终止等待时间(等当前请求处理完毕)
terminationGracePeriodSeconds: 120
containers:
- name: vllm
image: vllm/vllm-openai:v0.21.0
ports:
- containerPort: 8080
name: http
resources:
# 请求值等于限制值,防止 GPU 超售
requests:
nvidia.com/gpu: "4"
cpu: "16"
memory: "64Gi"
limits:
nvidia.com/gpu: "4"
cpu: "16"
memory: "64Gi"
args:
- --model=/models/qwen3-32b-instruct
- --host=0.0.0.0
- --port=8080
- --tensor-parallel-size=4
- --dtype=fp8 # H100 原生 FP8
- --max-model-len=32768
- --gpu-memory-utilization=0.90
- --enable-prefix-caching # 默认已开启,显式声明更清晰
- --max-num-seqs=256
- --served-model-name=qwen3-32b
env:
- name: VLLM_WORKER_MULTIPROC_METHOD
value: spawn
- name: CUDA_VISIBLE_DEVICES
value: "0,1,2,3"
# 开启 KV Offload(CPU 内存 >= 128GB 时推荐)
- name: VLLM_CPU_KVCACHE_SPACE
value: "40" # 40GB CPU 内存用于 KV Offload
# 健康探针:启动探针(模型加载可能需要数分钟)
startupProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 15
failureThreshold: 40 # 最多等待 60 + 40*15 = 660 秒
successThreshold: 1
# 就绪探针:确认服务可接受请求
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
# 存活探针:检测服务是否挂死
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 120
periodSeconds: 30
failureThreshold: 5
volumeMounts:
- name: models
mountPath: /models
- name: dshm
mountPath: /dev/shm
volumes:
- name: models
nfs:
server: 192.168.1.100
path: /data/models
# Tensor Parallel 需要大共享内存
- name: dshm
emptyDir:
medium: Memory
sizeLimit: 32Gi
Service 与 Ingress
apiVersion: v1
kind: Service
metadata:
name: vllm-qwen3-32b
namespace: llm-serving
spec:
selector:
app: vllm
model: qwen3-32b
ports:
- port: 80
targetPort: 8080
name: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vllm-ingress
namespace: llm-serving
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
ingressClassName: nginx
rules:
- host: llm.internal.company.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: vllm-qwen3-32b
port:
number: 80
Prometheus 指标采集
vLLM 内置 /metrics 端点暴露 Prometheus 指标,核心指标包括:
| 指标 | 含义 |
|---|---|
vllm:num_requests_waiting |
等待中的请求数(队列深度) |
vllm:num_requests_running |
正在处理的请求数 |
vllm:gpu_cache_usage_perc |
KV Cache GPU 使用率 |
vllm:cpu_cache_usage_perc |
KV Cache CPU Offload 使用率 |
vllm:avg_generation_throughput_toks_per_s |
平均生成吞吐量(tok/s) |
vllm:time_to_first_token_seconds |
首 Token 延迟分布 |
# Prometheus ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: vllm-metrics
namespace: monitoring
spec:
namespaceSelector:
matchNames:
- llm-serving
selector:
matchLabels:
app: vllm
endpoints:
- port: http
path: /metrics
interval: 15s
KEDA 弹性伸缩
基于 vllm:num_requests_waiting(队列深度)驱动 HPA,实现按需弹性扩缩:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: vllm-scaledobject
namespace: llm-serving
spec:
scaleTargetRef:
name: vllm-qwen3-32b
pollingInterval: 15 # 每 15 秒轮询一次
cooldownPeriod: 120 # 缩容冷却 120 秒(防止频繁缩容)
minReplicaCount: 1 # 至少保留 1 个副本
maxReplicaCount: 4 # 最多扩到 4 个副本(按 GPU 节点数量设置)
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus-operated.monitoring.svc:9090
metricName: vllm_requests_waiting
# 当队列深度超过 10 时开始扩容
threshold: "10"
query: |
sum(vllm:num_requests_waiting{namespace="llm-serving", model="qwen3-32b"})
扩容验证
# 观察 ScaledObject 状态
kubectl get scaledobject vllm-scaledobject -n llm-serving
kubectl describe scaledobject vllm-scaledobject -n llm-serving
# 模拟高负载(使用 vegeta 压测)
echo "POST http://llm.internal.company.com/v1/chat/completions" | \
vegeta attack -rate=50/s -duration=60s \
-header="Content-Type: application/json" \
-body='{"model":"qwen3-32b","messages":[{"role":"user","content":"解释量子纠缠"}]}' | \
vegeta report
# 观察 Pod 副本数变化
watch kubectl get pods -n llm-serving
生产调优建议
GPU 显存利用率
# --gpu-memory-utilization 根据模型大小调整:
# Qwen3-7B on 40GB A100:0.85~0.90
# Qwen3-32B on 4×H100 80GB(TP=4):0.88~0.92
# Llama4 Scout 17B on 2×H100:0.90
并发与批处理
# 高并发场景(在线 API 服务)
--max-num-seqs=512
--max-num-batched-tokens=32768
# 低并发但高吞吐(离线批处理)
--max-num-seqs=64
--max-num-batched-tokens=131072
--chunked-prefill-enabled
多模型实例隔离
不同模型部署在不同 Namespace,通过 ResourceQuota 限制 GPU 配额:
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-inference-quota
namespace: team-a
spec:
hard:
requests.nvidia.com/gpu: "4"
limits.nvidia.com/gpu: "4"
常见问题
OOM Killed
# 降低 --gpu-memory-utilization 或 --max-num-seqs
# 检查是否有其他进程占用 GPU 显存
nvidia-smi -q -d MEMORY
首 Token 延迟高(TTFT > 500ms)
# 检查 Prefix Cache 命中率
curl http://<vllm-svc>/metrics | grep prefix_cache_hit_rate
# 若命中率低,检查 System Prompt 是否一致(相同 System Prompt 才能命中缓存)
# 同时检查 --max-model-len 设置是否过大,压缩了可用 KV Cache 空间
Pod 重启循环(Liveness 探针失败)
kubectl describe pod <vllm-pod>
# 若是模型加载时间过长导致 Liveness 超时,增大 initialDelaySeconds
# 或改用 startupProbe 替代 livenessProbe 在启动阶段的判断
小结
vLLM on Kubernetes 的生产核心三件套:合理的健康探针防止流量打到未就绪实例 + KEDA 基于队列深度弹性扩缩 + Prefix Caching 降低长上下文 TTFT。GPU 资源 requests 等于 limits 是防止 OOM 的基础原则,不要设置 requests < limits。多副本场景下注意模型加载时间(大模型从 NFS 加载可能需要 3-5 分钟),startupProbe 的 failureThreshold 要足够大。
