0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

大模型推理服务的弹性部署与GPU调度方案

马哥Linux运维 来源:马哥Linux运维 2026-03-03 09:29 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

vLLM + K8s:大模型推理服务的弹性部署与GPU调度方案

一、概述

1.1 背景介绍

大模型推理服务在生产环境面临四个核心挑战:

GPU 显存管理:7B 模型 FP16 推理需要约 14GB 显存,70B 模型需要 140GB+,KV Cache 随并发数线性增长,显存碎片化导致实际利用率不足 60%

高并发低延迟:在线服务要求 P99 延迟可控,传统静态批处理在请求长度差异大时效率低下

弹性伸缩:GPU 资源昂贵(A100 单卡约 $2/h),流量波谷时需要快速缩容降本

多模型管理:生产环境通常同时运行多个模型版本,需要灰度发布和流量切分能力

vLLM 是 UC Berkeley 开源的高性能 LLM 推理引擎,通过三项核心技术解决上述问题:

PagedAttention:借鉴操作系统虚拟内存分页思想,将 KV Cache 拆分为固定大小的 Block,按需分配和回收,显存利用率从 ~50% 提升到 ~95%,同等显存下并发吞吐提升 2-4 倍

Continuous Batching:请求完成后立即释放资源并插入新请求,无需等待整个 Batch 完成,相比静态批处理延迟降低 30-50%

Tensor Parallelism:支持单节点多卡张量并行,大模型可跨多张 GPU 分片推理

1.2 推理框架对比

特性 vLLM TGI TensorRT-LLM Ollama llama.cpp
吞吐性能 高(PagedAttention) 中高 最高(深度优化) 低-中
首 Token 延迟 最低
GPU 利用率 95%+ 80-90% 95%+ 60-70% 50-70%
易用性 高(OpenAI 兼容) 低(需编译引擎) 最高
K8s 集成 原生支持 原生支持 需 Triton 封装
量化支持 AWQ/GPTQ/FP8 GPTQ/AWQ FP8/INT8/INT4 GGUF GGUF
多卡并行 Tensor Parallel Tensor Parallel Tensor+Pipeline 不支持 部分支持
适用场景 在线推理服务 在线推理服务 极致性能场景 本地开发测试 边缘/CPU 推理

生产环境推荐 vLLM:性能与易用性平衡最好,OpenAI 兼容 API 降低业务接入成本,K8s 部署成熟。

1.3 GPU 调度基础

K8s 通过 NVIDIA Device Plugin 实现 GPU 资源管理:

资源声明:Pod 通过nvidia.com/gpu: 1请求 GPU,调度器自动分配

GPU Operator:自动管理 NVIDIA Driver、Container Toolkit、Device Plugin 的生命周期

MIG(Multi-Instance GPU):A100/H100 支持将单卡切分为最多 7 个独立实例,每个实例有独立的显存和计算单元

GPU 时间片共享:多个 Pod 分时复用同一张 GPU,适合开发测试环境

1.4 适用场景

在线推理服务:面向用户的实时对话、文本生成,要求低延迟高可用

批量推理:离线数据处理、文档摘要生成,追求高吞吐

多模型服务:同一集群部署多个模型,按业务需求路由

A/B 测试:模型版本灰度发布,基于流量比例切分

1.5 环境要求

组件 版本要求 说明
vLLM 0.6.x 推理引擎,需与 CUDA 版本匹配
CUDA 12.1+ 推荐 12.4,vLLM 0.6.x 官方测试版本
NVIDIA Driver 550+ 需支持 CUDA 12.x
Kubernetes 1.31+ 需启用 DevicePlugin 特性门控
GPU Operator 24.6+ 自动管理 GPU 软件栈
容器运行时 containerd 1.7+ 需配置 nvidia-container-runtime
GPU 硬件 A100/H100/L40S/A10 推荐 Ampere 架构及以上

二、详细步骤

2.1 GPU 节点准备

2.1.1 NVIDIA Driver 安装验证

# 检查 GPU 硬件识别
lspci | grep -i nvidia

# 检查驱动版本(需 550+)
nvidia-smi

# 检查 CUDA 版本
nvcc --version

2.1.2 NVIDIA GPU Operator 部署

GPU Operator 统一管理 Driver、Container Toolkit、Device Plugin,避免手动维护各组件版本兼容性。

# 添加 NVIDIA Helm 仓库
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

# 部署 GPU Operator(如果节点已安装驱动,设置 driver.enabled=false)
helm install gpu-operator nvidia/gpu-operator 
 --namespace gpu-operator 
 --create-namespace 
 --setdriver.enabled=false
 --settoolkit.enabled=true
 --setdevicePlugin.enabled=true
 --setmigManager.enabled=true
 --setdcgmExporter.enabled=true
 --version v24.6.2

# 等待所有组件就绪
kubectl -n gpu-operator get pods -w

2.1.3 GPU 资源验证

# 确认节点已注册 GPU 资源
kubectl describe node  | grep -A 5"Capacity"
# 预期输出:nvidia.com/gpu: 8(以 8 卡节点为例)

# 运行 GPU 测试 Pod
kubectl run gpu-test --rm -it --restart=Never 
 --image=nvidia/cuda:12.4.0-base-ubuntu22.04 
 --limits=nvidia.com/gpu=1 
 -- nvidia-smi

2.1.4 MIG 配置(可选,A100/H100)

# 查看 MIG 支持状态
nvidia-smi mig -lgip

# 启用 MIG 模式(需重启 GPU)
sudo nvidia-smi -i 0 -mig 1

# 创建 MIG 实例(以 A100 80GB 为例,创建 7 个 10GB 实例)
sudo nvidia-smi mig -i 0 -cgi 19,19,19,19,19,19,19 -C

# 在 GPU Operator 中配置 MIG 策略
# 编辑 ConfigMap 选择 mixed 或 single 策略
kubectl -n gpu-operator edit configmap mig-parted-config

2.2 vLLM 单机部署

2.2.1 Docker 快速部署

# 拉取 vLLM 官方镜像
docker pull vllm/vllm-openai:v0.6.6

# 启动推理服务(以 Qwen2.5-7B 为例)
docker run -d 
 --name vllm-server 
 --gpus'"device=0"'
 --shm-size=8g 
 -p 8000:8000 
 -v /data/models:/models 
 vllm/vllm-openai:v0.6.6 
 --model /models/Qwen2.5-7B-Instruct 
 --served-model-name qwen2.5-7b 
 --tensor-parallel-size 1 
 --gpu-memory-utilization 0.90 
 --max-model-len 8192 
 --max-num-seqs 64 
 --enable-prefix-caching 
 --trust-remote-code

2.2.2 关键启动参数说明

参数 默认值 说明
--gpu-memory-utilization 0.9 GPU 显存使用比例,预留 10% 防 OOM
--max-model-len 模型最大值 最大上下文长度,直接影响 KV Cache 显存占用
--max-num-seqs 256 最大并发序列数,受显存限制
--tensor-parallel-size 1 张量并行卡数,需能整除模型 attention heads
--quantization None 量化方式:awq / gptq / fp8
--enable-prefix-caching False 启用前缀缓存,相同 system prompt 复用 KV Cache
--enforce-eager False 禁用 CUDA Graph,调试时使用

2.2.3 量化方案对比

量化方式 显存占用(7B) 吞吐影响 精度损失 适用场景
FP16(无量化) ~14 GB 基准 显存充足时首选
AWQ(INT4) ~4.5 GB -5~10% 极小 显存受限,推荐
GPTQ(INT4) ~4.5 GB -10~15% 已有 GPTQ 模型时
FP8 ~7.5 GB +5~10% 极小 H100/Ada 架构,推荐

2.2.4 OpenAI 兼容 API 验证

# 测试 Chat Completions 接口
curl -s http://localhost:8000/v1/chat/completions 
 -H"Content-Type: application/json"
 -d'{
  "model": "qwen2.5-7b",
  "messages": [{"role": "user", "content": "你好"}],
  "max_tokens": 100,
  "temperature": 0.7
 }'| jq .

# 查看已加载模型
curl -s http://localhost:8000/v1/models | jq .

2.3 Kubernetes 部署

2.3.1 Deployment 配置

# vllm-deployment.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:vllm-qwen25-7b
namespace:llm-serving
labels:
 app:vllm
 model:qwen25-7b
spec:
replicas:2
selector:
 matchLabels:
  app:vllm
  model:qwen25-7b
template:
 metadata:
  labels:
   app:vllm
   model:qwen25-7b
  annotations:
   # Prometheus 指标采集
   prometheus.io/scrape:"true"
   prometheus.io/port:"8000"
   prometheus.io/path:"/metrics"
 spec:
  # 调度到 GPU 节点
  nodeSelector:
   nvidia.com/gpu.present:"true"
  # 同一模型的 Pod 尽量分散到不同节点
  affinity:
   podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
     -weight:100
      podAffinityTerm:
       labelSelector:
        matchLabels:
         model:qwen25-7b
       topologyKey:kubernetes.io/hostname
  containers:
   -name:vllm
    image:vllm/vllm-openai:v0.6.6
    args:
     -"--model"
     -"/models/Qwen2.5-7B-Instruct"
     -"--served-model-name"
     -"qwen2.5-7b"
     -"--gpu-memory-utilization"
     -"0.90"
     -"--max-model-len"
     -"8192"
     -"--max-num-seqs"
     -"64"
     -"--enable-prefix-caching"
     -"--trust-remote-code"
    ports:
     -containerPort:8000
      name:http
      protocol:TCP
    resources:
     limits:
      nvidia.com/gpu:"1"   # 请求 1 张 GPU
      memory:"32Gi"
     requests:
      cpu:"4"
      memory:"16Gi"
    # 启动探针:模型加载可能需要数分钟
    startupProbe:
     httpGet:
      path:/health
      port:8000
     initialDelaySeconds:30
     periodSeconds:10
     failureThreshold:30    # 最多等待 5 分钟
    # 就绪探针:确认服务可接收流量
    readinessProbe:
     httpGet:
      path:/health
      port:8000
     periodSeconds:10
     failureThreshold:3
    # 存活探针:检测服务是否卡死
    livenessProbe:
     httpGet:
      path:/health
      port:8000
     periodSeconds:30
     failureThreshold:3
    env:
     -name:VLLM_LOGGING_LEVEL
      value:"INFO"
     # 共享内存,PagedAttention 需要
     -name:NCCL_SHM_DISABLE
      value:"0"
    volumeMounts:
     -name:model-storage
      mountPath:/models
      readOnly:true
     -name:shm
      mountPath:/dev/shm
  volumes:
   -name:model-storage
    persistentVolumeClaim:
     claimName:model-pvc
   -name:shm
    emptyDir:
     medium:Memory
     sizeLimit:8Gi
  # 优雅终止:等待正在处理的请求完成
  terminationGracePeriodSeconds:120

2.3.2 Service + Ingress 暴露

# vllm-service.yaml
apiVersion:v1
kind:Service
metadata:
name:vllm-qwen25-7b
namespace:llm-serving
spec:
selector:
 app:vllm
 model:qwen25-7b
ports:
 -name:http
  port:8000
  targetPort:8000
  protocol:TCP
type:ClusterIP
---
# vllm-ingress.yaml
apiVersion:networking.k8s.io/v1
kind:Ingress
metadata:
name:vllm-ingress
namespace:llm-serving
annotations:
 # 推理请求可能耗时较长,超时设为 5 分钟
 nginx.ingress.kubernetes.io/proxy-read-timeout:"300"
 nginx.ingress.kubernetes.io/proxy-send-timeout:"300"
 # 支持 SSE 流式输出
 nginx.ingress.kubernetes.io/proxy-buffering:"off"
spec:
ingressClassName:nginx
rules:
 -host:llm-api.example.com
  http:
   paths:
    -path:/v1
     pathType:Prefix
     backend:
      service:
       name:vllm-qwen25-7b
       port:
        number:8000

2.3.3 HPA 自动伸缩

# vllm-hpa.yaml
apiVersion:autoscaling/v2
kind:HorizontalPodAutoscaler
metadata:
name:vllm-qwen25-7b-hpa
namespace:llm-serving
spec:
scaleTargetRef:
 apiVersion:apps/v1
 kind:Deployment
 name:vllm-qwen25-7b
minReplicas:1
maxReplicas:8
metrics:
 # 基于 GPU 利用率伸缩(需 DCGM Exporter + Prometheus Adapter)
 -type:Pods
  pods:
   metric:
    name:DCGM_FI_DEV_GPU_UTIL
   target:
    type:AverageValue
    averageValue:"70"   # GPU 利用率超过 70% 触发扩容
behavior:
 scaleUp:
  stabilizationWindowSeconds:60
  policies:
   -type:Pods
    value:2        # 每次最多扩 2 个 Pod
    periodSeconds:120
 scaleDown:
  stabilizationWindowSeconds:300# 缩容冷却 5 分钟,避免频繁波动
  policies:
   -type:Pods
    value:1
    periodSeconds:180

2.3.4 KEDA 事件驱动伸缩

HPA 基于资源指标伸缩存在滞后性,KEDA 支持基于 Prometheus 自定义指标实现更精准的伸缩策略。

# vllm-keda.yaml
apiVersion:keda.sh/v1alpha1
kind:ScaledObject
metadata:
name:vllm-qwen25-7b-scaler
namespace:llm-serving
spec:
scaleTargetRef:
 name:vllm-qwen25-7b
minReplicaCount:1
maxReplicaCount:8
cooldownPeriod:300      # 缩容冷却期 5 分钟
pollingInterval:15      # 每 15 秒检查一次指标
triggers:
 # 基于等待队列长度伸缩
 -type:prometheus
  metadata:
   serverAddress:http://prometheus.monitoring:9090
   metricName:vllm_waiting_requests
   query:|
     avg(vllm:num_requests_waiting{model_name="qwen2.5-7b"})
   threshold:"10"     # 平均等待请求超过 10 个触发扩容
 # 基于 GPU 显存利用率
 -type:prometheus
  metadata:
   serverAddress:http://prometheus.monitoring:9090
   metricName:gpu_memory_util
   query:|
     avg(DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_FREE) by (pod)
   threshold:"0.85"

2.4 多模型服务与路由

2.4.1 多 Deployment 部署

生产环境通常需要同时运行多个模型,每个模型独立 Deployment 管理生命周期:

# 部署结构示意
# llm-serving namespace
# ├── vllm-qwen25-7b  (Deployment, 2 replicas, GPU: A10)
# ├── vllm-qwen25-72b  (Deployment, 1 replica, GPU: A100x4)
# └── vllm-llama3-8b  (Deployment, 2 replicas, GPU: L40S)

2.4.2 Gateway API 路由

# 按路径将请求分发到不同模型
apiVersion:gateway.networking.k8s.io/v1
kind:HTTPRoute
metadata:
name:llm-router
namespace:llm-serving
spec:
parentRefs:
 -name:llm-gateway
rules:
 # /v1/qwen-7b/* -> qwen25-7b 服务
 -matches:
   -headers:
     -name:x-model-id
      value:qwen-7b
  backendRefs:
   -name:vllm-qwen25-7b
    port:8000
 # /v1/qwen-72b/* -> qwen25-72b 服务
 -matches:
   -headers:
     -name:x-model-id
      value:qwen-72b
  backendRefs:
   -name:vllm-qwen25-72b
    port:8000
 # 金丝雀发布:90% 流量到稳定版,10% 到新版
 -matches:
   -headers:
     -name:x-model-id
      value:llama3-8b
  backendRefs:
   -name:vllm-llama3-8b-stable
    port:8000
    weight:90
   -name:vllm-llama3-8b-canary
    port:8000
    weight:10

2.5 Tensor Parallelism 多卡推理

2.5.1 单节点多卡

70B 及以上模型单卡放不下,需要多卡张量并行:

# 72B 模型 4 卡并行部署片段
containers:
-name:vllm
 image:vllm/vllm-openai:v0.6.6
 args:
  -"--model"
  -"/models/Qwen2.5-72B-Instruct"
  -"--tensor-parallel-size"
  -"4"           # 4 卡张量并行
  -"--gpu-memory-utilization"
  -"0.92"
  -"--max-model-len"
  -"4096"
 resources:
  limits:
   nvidia.com/gpu:"4"   # 请求 4 张 GPU
   memory:"128Gi"

2.5.2 GPU 拓扑感知调度

多卡推理时,GPU 间通信带宽直接影响推理延迟。NVLink 带宽(600 GB/s)远高于 PCIe(64 GB/s),调度时应优先选择 NVLink 互联的 GPU:

# 通过节点标签确保调度到 NVLink 节点
nodeSelector:
nvidia.com/gpu.present:"true"
gpu-topology:nvlink      # 自定义标签,标识 NVLink 互联节点
# 配合 GPU Operator 的拓扑感知特性
# 在 ClusterPolicy 中启用:
# topologyManager:
#  policy: single-numa-node    # 确保 GPU 和 CPU 在同一 NUMA 节点

三、示例代码和配置

3.1 完整部署清单

将所有资源整合为一个可直接 apply 的清单:

# 文件路径:deploy/vllm-full-stack.yaml
# 包含:Namespace + PDB + ConfigMap + Deployment + Service + HPA
---
apiVersion:v1
kind:Namespace
metadata:
name:llm-serving
labels:
 monitoring:enabled
---
# PodDisruptionBudget:确保滚动更新和节点维护时至少 1 个 Pod 可用
apiVersion:policy/v1
kind:PodDisruptionBudget
metadata:
name:vllm-qwen25-7b-pdb
namespace:llm-serving
spec:
minAvailable:1
selector:
 matchLabels:
  app:vllm
  model:qwen25-7b
---
# ConfigMap:集中管理 vLLM 启动参数,修改无需重建镜像
apiVersion:v1
kind:ConfigMap
metadata:
name:vllm-config
namespace:llm-serving
data:
MODEL_PATH:"/models/Qwen2.5-7B-Instruct"
SERVED_MODEL_NAME:"qwen2.5-7b"
GPU_MEMORY_UTILIZATION:"0.90"
MAX_MODEL_LEN:"8192"
MAX_NUM_SEQS:"64"
TENSOR_PARALLEL_SIZE:"1"
---
apiVersion:apps/v1
kind:Deployment
metadata:
name:vllm-qwen25-7b
namespace:llm-serving
spec:
replicas:2
strategy:
 type:RollingUpdate
 rollingUpdate:
  maxSurge:1        # 滚动更新时最多多 1 个 Pod
  maxUnavailable:0     # 不允许不可用,确保零停机
selector:
 matchLabels:
  app:vllm
  model:qwen25-7b
template:
 metadata:
  labels:
   app:vllm
   model:qwen25-7b
 spec:
  containers:
   -name:vllm
    image:vllm/vllm-openai:v0.6.6
    command:["python3","-m","vllm.entrypoints.openai.api_server"]
    args:
     -"--model=$(MODEL_PATH)"
     -"--served-model-name=$(SERVED_MODEL_NAME)"
     -"--gpu-memory-utilization=$(GPU_MEMORY_UTILIZATION)"
     -"--max-model-len=$(MAX_MODEL_LEN)"
     -"--max-num-seqs=$(MAX_NUM_SEQS)"
     -"--tensor-parallel-size=$(TENSOR_PARALLEL_SIZE)"
     -"--enable-prefix-caching"
     -"--trust-remote-code"
    envFrom:
     -configMapRef:
       name:vllm-config
    ports:
     -containerPort:8000
    resources:
     limits:
      nvidia.com/gpu:"1"
      memory:"32Gi"
     requests:
      cpu:"4"
      memory:"16Gi"
    startupProbe:
     httpGet:
      path:/health
      port:8000
     initialDelaySeconds:30
     periodSeconds:10
     failureThreshold:30
    readinessProbe:
     httpGet:
      path:/health
      port:8000
     periodSeconds:10
    livenessProbe:
     httpGet:
      path:/health
      port:8000
     periodSeconds:30
    volumeMounts:
     -name:model-storage
      mountPath:/models
      readOnly:true
     -name:shm
      mountPath:/dev/shm
  volumes:
   -name:model-storage
    persistentVolumeClaim:
     claimName:model-pvc
   -name:shm
    emptyDir:
     medium:Memory
     sizeLimit:8Gi
  terminationGracePeriodSeconds:120
---
apiVersion:v1
kind:Service
metadata:
name:vllm-qwen25-7b
namespace:llm-serving
spec:
selector:
 app:vllm
 model:qwen25-7b
ports:
 -port:8000
  targetPort:8000
---
apiVersion:autoscaling/v2
kind:HorizontalPodAutoscaler
metadata:
name:vllm-qwen25-7b-hpa
namespace:llm-serving
spec:
scaleTargetRef:
 apiVersion:apps/v1
 kind:Deployment
 name:vllm-qwen25-7b
minReplicas:1
maxReplicas:8
metrics:
 -type:Pods
  pods:
   metric:
    name:DCGM_FI_DEV_GPU_UTIL
   target:
    type:AverageValue
    averageValue:"70"
behavior:
 scaleDown:
  stabilizationWindowSeconds:300

3.2 GPU 弹性伸缩配置

基于 KEDA + Prometheus 实现精细化伸缩,结合 vLLM 内置指标:

# 文件路径:deploy/vllm-keda-scaler.yaml
# 前置条件:已部署 KEDA、Prometheus、DCGM Exporter
---
apiVersion:keda.sh/v1alpha1
kind:ScaledObject
metadata:
name:vllm-qwen25-7b-keda
namespace:llm-serving
spec:
scaleTargetRef:
 name:vllm-qwen25-7b
minReplicaCount:1       # 最少保留 1 个副本
maxReplicaCount:8       # 最多扩到 8 个副本
cooldownPeriod:300      # 缩容冷却 5 分钟
pollingInterval:15      # 每 15 秒采集一次指标
advanced:
 restoreToOriginalReplicaCount:false
 horizontalPodAutoscalerConfig:
  behavior:
   scaleUp:
    stabilizationWindowSeconds:30
    policies:
     -type:Pods
      value:2
      periodSeconds:60
   scaleDown:
    stabilizationWindowSeconds:300
    policies:
     -type:Pods
      value:1
      periodSeconds:180
triggers:
 # 触发器 1:vLLM 等待队列长度
 -type:prometheus
  metadata:
   serverAddress:http://prometheus.monitoring:9090
   metricName:vllm_pending_requests
   query:|
     sum(vllm:num_requests_waiting{model_name="qwen2.5-7b"})
     /
     count(vllm:num_requests_waiting{model_name="qwen2.5-7b"})
   threshold:"10"     # 平均等待请求 > 10 触发扩容
   activationThreshold:"3" # 等待请求 > 3 才开始评估
 # 触发器 2:请求延迟 P95
 -type:prometheus
  metadata:
   serverAddress:http://prometheus.monitoring:9090
   metricName:vllm_request_latency_p95
   query:|
     histogram_quantile(0.95,
      sum(rate(vllm:request_latency_seconds_bucket{model_name="qwen2.5-7b"}[2m])) by (le)
     )
   threshold:"5"     # P95 延迟超过 5 秒触发扩容
---
# Prometheus 告警规则:GPU 资源异常告警
apiVersion:monitoring.coreos.com/v1
kind:PrometheusRule
metadata:
name:vllm-alerts
namespace:llm-serving
spec:
groups:
 -name:vllm.rules
  rules:
   -alert:VLLMHighQueueDepth
    expr:|
      avg(vllm:num_requests_waiting{model_name="qwen2.5-7b"}) > 20
    for:2m
    labels:
     severity:warning
    annotations:
     summary:"vLLM 请求队列积压"
     description:"模型 qwen2.5-7b 平均等待请求数{{ $value }},持续 2 分钟"
   -alert:VLLMGPUMemoryHigh
    expr:|
      DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE) > 0.95
    for:5m
    labels:
     severity:critical
    annotations:
     summary:"GPU 显存使用率超过 95%"
     description:"节点{{ $labels.node }}GPU{{ $labels.gpu }}显存即将耗尽"

3.3 推理服务压测脚本

#!/bin/bash
# 文件名:benchmark-vllm.sh
# 功能:vLLM 推理服务压测,统计吞吐量和延迟分布
set-euo pipefail

# ========== 配置区 ==========
API_URL="${1//localhost:8000/v1/chat/completions}"
MODEL_NAME="${2:-qwen2.5-7b}"
CONCURRENCY="${3:-16}"      # 并发数
TOTAL_REQUESTS="${4:-200}"    # 总请求数
MAX_TOKENS=256          # 每次生成的最大 token 数
RESULT_DIR="./benchmark_results"

mkdir -p"${RESULT_DIR}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
RESULT_FILE="${RESULT_DIR}/bench_${TIMESTAMP}.csv"
SUMMARY_FILE="${RESULT_DIR}/bench_${TIMESTAMP}_summary.txt"

# 初始化结果文件
echo"request_id,status_code,latency_ms,first_token_ms,tokens_generated">"${RESULT_FILE}"

# ========== 请求函数 ==========
send_request() {
 localreq_id=$1
 localstart_time end_time latency_ms status_code body

 # 构造请求体
  body=$(cat </dev/null) ||true

  end_time=$(date +%s%N)

 # 解析响应
  status_code=$(echo"${response}"| tail -1)
 localresp_body
  resp_body=$(echo"${response}"| sed'$d')

 # 计算延迟(毫秒)
  latency_ms=$(( (end_time - start_time) / 1000000 ))

 # 提取生成的 token 数
 localtokens
  tokens=$(echo"${resp_body}"| jq -r'.usage.completion_tokens // 0'2>/dev/null ||echo"0")

 echo"${req_id},${status_code},${latency_ms},0,${tokens}">>"${RESULT_FILE}"
}

export-f send_request
exportAPI_URL MODEL_NAME MAX_TOKENS RESULT_FILE

# ========== 执行压测 ==========
echo"=========================================="
echo" vLLM 推理服务压测"
echo"=========================================="
echo" 目标地址:${API_URL}"
echo" 模型名称:${MODEL_NAME}"
echo" 并发数: ${CONCURRENCY}"
echo" 总请求: ${TOTAL_REQUESTS}"
echo" 最大Token:${MAX_TOKENS}"
echo"=========================================="
echo""
echo"压测开始..."

BENCH_START=$(date +%s%N)

# 使用 xargs 控制并发
seq 1"${TOTAL_REQUESTS}"| xargs -P"${CONCURRENCY}"-I {} bash -c'send_request {}'

BENCH_END=$(date +%s%N)
TOTAL_TIME_MS=$(( (BENCH_END - BENCH_START) / 1000000 ))

# ========== 统计结果 ==========
{
 echo"=========================================="
 echo" 压测结果汇总"
 echo"=========================================="
 echo" 总耗时:${TOTAL_TIME_MS}ms"
 echo" 总请求:${TOTAL_REQUESTS}"

 # 成功率
 localsuccess_count
  success_count=$(awk -F',''NR>1 && $2==200 {count++} END {print count+0}'"${RESULT_FILE}")
 echo" 成功数:${success_count}"
 echo" 成功率:$(echo "scale=2; ${success_count} * 100 / ${TOTAL_REQUESTS}" | bc)%"

 # 吞吐量
 echo" QPS:  $(echo "scale=2; ${TOTAL_REQUESTS} * 1000 / ${TOTAL_TIME_MS}" | bc)"

 # 延迟统计(仅成功请求)
 echo""
 echo" 延迟分布 (ms):"
  awk -F',''NR>1 && $2==200 {latencies[NR-1]=$3; sum+=$3; count++}
  END {
    if(count==0) {print " 无成功请求"; exit}
    asort(latencies)
    printf " 最小值: %d
", latencies[1]
    printf " P50:  %d
", latencies[int(count*0.5)]
    printf " P90:  %d
", latencies[int(count*0.9)]
    printf " P95:  %d
", latencies[int(count*0.95)]
    printf " P99:  %d
", latencies[int(count*0.99)]
    printf " 最大值: %d
", latencies[count]
    printf " 平均值: %d
", sum/count
  }'"${RESULT_FILE}"

 # Token 吞吐
 localtotal_tokens
  total_tokens=$(awk -F',''NR>1 && $2==200 {sum+=$5} END {print sum+0}'"${RESULT_FILE}")
 echo""
 echo" Token 吞吐:"
 echo" 总生成 Token:${total_tokens}"
 echo" Token/s:   $(echo "scale=2; ${total_tokens} * 1000 / ${TOTAL_TIME_MS}" | bc)"
 echo"=========================================="
} | tee"${SUMMARY_FILE}"

echo""
echo"详细结果:${RESULT_FILE}"
echo"汇总报告:${SUMMARY_FILE}"

使用方式

# 默认参数压测
chmod +x benchmark-vllm.sh
./benchmark-vllm.sh

# 自定义参数:目标地址、模型名、并发数 32、总请求 500
./benchmark-vllm.sh http://llm-api.example.com/v1/chat/completions qwen2.5-7b 32 500

四、最佳实践和注意事项

4.1 最佳实践

4.1.1 显存优化

显存是 LLM 推理的第一瓶颈,优化思路围绕三个方向:减少模型本身占用、控制 KV Cache 开销、选择合适的量化方案。

gpu-memory-utilization 参数调优

# 默认值 0.9,表示 vLLM 预分配 90% 显存用于模型权重 + KV Cache
# 单模型独占 GPU 时,可适当调高
--gpu-memory-utilization 0.92

# 多模型共享 GPU 或需要预留显存给监控进程时,适当调低
--gpu-memory-utilization 0.80

# 查看实际显存分配情况(启动日志会打印)
# INFO: GPU memory: 79.15 GiB total, 71.24 GiB allocated for KV cache

实际调优建议:先用默认值 0.9 启动,观察nvidia-smi中显存占用是否稳定,如果出现 OOM 则降到 0.85,稳定后再逐步上调。

KV Cache 预分配策略

vLLM 启动时会根据gpu-memory-utilization计算可用显存,扣除模型权重后全部预分配给 KV Cache。这意味着:

max-model-len越大,单个请求占用的 KV Cache 越多,可并发的请求数越少

如果业务场景中 90% 的请求上下文长度不超过 4K,没必要把max-model-len设为 32K

# 根据实际业务场景限制最大上下文长度
# 减少 max-model-len 可以显著增加并发容量
--max-model-len 8192

# 查看 KV Cache 可容纳的最大并发数
# 启动日志:Maximum number of running requests: 256

量化方案选择决策树

模型参数量 → 可用显存 → 精度要求 → 选择方案

70B 模型 + 单卡 80G → 必须量化
 ├─ 精度要求高 → AWQ 4bit(推荐,速度快且精度损失小)
 ├─ 追求极致压缩 → GPTQ 4bit(压缩率略高,推理稍慢)
 └─ H100/L40S → FP8(硬件原生支持,精度损失最小)

7B~14B 模型 + 单卡 24G~40G
 ├─ 显存充足 → FP16/BF16(无精度损失)
 └─ 显存紧张 → AWQ 4bit

7B 模型 + 80G 显卡 → FP16,不要量化(显存够用,量化反而增加延迟)

量化方案 显存节省 精度损失 推理速度 适用 GPU
FP16/BF16 基准 基准 所有
AWQ 4bit ~60% 快(有专用 kernel) 所有
GPTQ 4bit ~60% 低~中 中等 所有
FP8 ~50% 极低 H100/L40S/Ada
SmoothQuant ~50% Ampere+

4.1.2 吞吐量优化

max-num-batched-tokens 调优

这个参数决定了一个推理迭代中最多处理多少 token,直接影响批处理效率。

# 默认值等于 max-model-len,大多数场景不需要手动设置
# 如果请求长度差异大(既有 100 token 的短请求,也有 8K 的长请求),可以手动调整
--max-num-batched-tokens 16384

# 配合 max-num-seqs 控制最大并发请求数
# 短文本高并发场景:增大 max-num-seqs
--max-num-seqs 512

# 长文本低并发场景:减小 max-num-seqs,增大 max-num-batched-tokens
--max-num-seqs 32 --max-num-batched-tokens 32768

Continuous Batching 参数

vLLM 默认启用 Continuous Batching,无需额外配置。但有几个参数影响批处理行为:

# 调度策略:默认 "auto",会根据显存情况自动选择
--scheduling-policy auto

# 预取数量:控制 prefill 和 decode 的调度平衡
# 值越大,新请求的首 token 延迟越高,但整体吞吐越好
--num-scheduler-steps 1 # 默认值,低延迟优先
--num-scheduler-steps 10# 高吞吐优先,适合离线批处理

Prefix Caching(前缀缓存)

当多个请求共享相同的系统提示词(System Prompt)时,Prefix Caching 可以避免重复计算,显著提升吞吐。

# 启用自动前缀缓存
--enable-prefix-caching

# 典型场景:所有请求使用相同的 system prompt(如 RAG 场景)
# 第一个请求:计算完整 KV Cache(包括 system prompt 部分)
# 后续请求:复用 system prompt 的 KV Cache,只计算用户输入部分
# 实测效果:相同 system prompt 下,TTFT 降低 30%~60%

适用条件:请求之间有大量重复前缀(系统提示词、Few-shot 示例等),如果每个请求的输入完全不同,开启后反而有轻微开销。

4.1.3 成本控制

GPU 利用率目标

生产环境 GPU 利用率应维持在 70%~85% 之间。低于 70% 说明资源浪费,高于 85% 容易在流量突增时触发排队。

#!/bin/bash
set-euo pipefail
# gpu-utilization-check.sh - 检查 GPU 利用率并输出建议

THRESHOLD_LOW=70
THRESHOLD_HIGH=85

# 获取所有 GPU 的利用率
nvidia-smi --query-gpu=index,utilization.gpu,memory.used,memory.total 
 --format=csv,noheader,nounits |whileIFS=', 'read-r idx util mem_used mem_total;do
 mem_pct=$((mem_used * 100 / mem_total))
if["${util}"-lt"${THRESHOLD_LOW}"];then
 echo"[GPU${idx}] 利用率${util}%(偏低),显存${mem_pct}% - 建议合并负载或缩容"
elif["${util}"-gt"${THRESHOLD_HIGH}"];then
 echo"[GPU${idx}] 利用率${util}%(偏高),显存${mem_pct}% - 建议扩容或限流"
else
 echo"[GPU${idx}] 利用率${util}%(正常),显存${mem_pct}%"
fi
done

Spot/Preemptible 实例使用

离线批量推理任务(数据标注、批量摘要)适合使用 Spot 实例,成本降低 60%~70%

在线服务不建议使用 Spot,被回收时会导致请求中断

混合策略:On-Demand 实例承载基线流量,Spot 实例承载弹性流量

缩容策略(冷却期设计)

# HPA 缩容配置 - 避免频繁缩扩容导致的抖动
apiVersion:autoscaling/v2
kind:HorizontalPodAutoscaler
spec:
behavior:
 scaleDown:
  stabilizationWindowSeconds:600# 缩容冷却期 10 分钟
  policies:
  -type:Pods
   value:1    # 每次最多缩减 1 个 Pod
   periodSeconds:120# 每 2 分钟最多执行一次缩容
 scaleUp:
  stabilizationWindowSeconds:30 # 扩容冷却期 30 秒(快速响应)
  policies:
  -type:Pods
   value:2    # 每次最多扩 2 个 Pod
   periodSeconds:60

多模型共享 GPU

方案 隔离性 配置复杂度 适用场景
NVIDIA MIG 硬件级隔离 高(需预先分区) A100/H100,固定负载
时间片共享 无隔离 开发测试环境
MPS 进程级 多个小模型共享
多容器共享 无隔离 不推荐生产使用

# MIG 分区示例(A100 80G 分为 2 个 40G 实例)
sudo nvidia-smi mig -cgi 9,9 -C
# 查看 MIG 实例
nvidia-smi mig -lgi

4.1.4 高可用设计

多副本 + PodDisruptionBudget

# PDB 配置 - 确保滚动更新和节点维护时至少有可用副本
apiVersion:policy/v1
kind:PodDisruptionBudget
metadata:
name:vllm-pdb
namespace:llm-serving
spec:
minAvailable:1    # 至少保持 1 个 Pod 可用
selector:
 matchLabels:
  app:vllm-inference
---
# 部署时使用反亲和性,将副本分散到不同节点
# 在 Deployment 的 spec.template.spec 中添加
affinity:
podAntiAffinity:
 preferredDuringSchedulingIgnoredDuringExecution:
 -weight:100
  podAffinityTerm:
   labelSelector:
    matchExpressions:
    -key:app
     operator:In
     values:["vllm-inference"]
   topologyKey:kubernetes.io/hostname

健康检查策略

vLLM 内置/health端点,但默认的健康检查配置往往不够合理,模型加载阶段容易被误杀。

# 针对 LLM 推理服务的健康检查配置
containers:
-name:vllm
livenessProbe:
 httpGet:
  path:/health
  port:8000
 initialDelaySeconds:300 # 大模型加载需要 3~8 分钟,给足启动时间
 periodSeconds:30
 timeoutSeconds:10
 failureThreshold:3   # 连续 3 次失败才重启
readinessProbe:
 httpGet:
  path:/health
  port:8000
 initialDelaySeconds:60
 periodSeconds:10
 timeoutSeconds:5
 failureThreshold:3
startupProbe:       # 启动探针:模型加载完成前不触发存活检查
 httpGet:
  path:/health
  port:8000
 initialDelaySeconds:30
 periodSeconds:10
 failureThreshold:60   # 最多等待 10 分钟(10s × 60)

优雅关闭(Drain 在途请求)

# Pod 关闭时给足时间处理在途请求
spec:
terminationGracePeriodSeconds:120# 给 2 分钟完成在途请求
containers:
-name:vllm
 lifecycle:
  preStop:
   exec:
    command:
    -/bin/sh
    --c
    # 先从 Service 摘除(停止接收新请求),等待在途请求完成
    -"sleep 5 && kill -SIGTERM 1"

配合 Service 端的配置,确保流量切换平滑:

# 验证优雅关闭是否生效
# 在滚动更新期间观察是否有 502/503 错误
kubectl rollout restart deployment/vllm-inference -n llm-serving
# 同时监控错误率
curl -s http://llm-api.example.com/health

模型预加载(避免冷启动)

冷启动是 LLM 推理服务的痛点,70B 模型从磁盘加载到 GPU 需要 3~8 分钟。缓解方案:

使用emptyDir+initContainer预拉取模型到本地 SSD

保持最小副本数 >= 1,避免缩容到 0

使用 PVC 持久化模型文件,避免每次 Pod 重建都重新下载

考虑使用 Tensorizer 格式加速模型加载(加载速度提升 2~5 倍)

4.2 注意事项

4.2.1 配置注意事项

警告:OOM Killer 是 vLLM 服务最常见的致命问题

vLLM 启动时会预分配大量显存和内存,如果 K8s 资源限制设置不当,极易触发 OOM Killer。

共享内存不足:vLLM 使用 PyTorch,依赖/dev/shm进行进程间通信。Docker 默认/dev/shm只有 64MB,Tensor Parallel 场景下必须增大:

# 在 Pod spec 中挂载足够的共享内存
volumes:
-name:shm
emptyDir:
 medium:Memory
 sizeLimit:16Gi # 建议设为显存的 20%
containers:
-volumeMounts:
-name:shm
 mountPath:/dev/shm

内存资源限制:resources.limits.memory必须大于模型权重大小 + KV Cache 的 CPU 部分。7B FP16 模型至少需要 20Gi 内存,70B 模型至少 80Gi

模型下载超时:从 HuggingFace 下载大模型容易超时,建议预先下载到 PV 或使用国内镜像源

# 使用 modelscope 镜像加速下载
exportVLLM_USE_MODELSCOPE=True
# 或者手动下载后挂载
huggingface-cli download Qwen/Qwen2.5-7B-Instruct --local-dir /models/qwen2.5-7b

4.2.2 常见错误

错误现象 原因分析 解决方案
CUDA out of memory 显存不足以加载模型 + KV Cache 降低gpu-memory-utilization(如 0.85);减小max-model-len;使用量化模型
Model not found /OSError: Can't load tokenizer 模型路径错误或文件不完整 检查挂载路径;用ls -la /models/确认文件完整;检查config.json是否存在
tensor parallel size does not match TP 数与可用 GPU 数不一致 确认--tensor-parallel-size等于nvidia.com/gpu的 limits 值
Health check timeout → Pod 反复重启 startupProbe 超时时间不够 增大failureThreshold × periodSeconds,70B 模型建议至少 600 秒
torch.cuda.CudaError: invalid device ordinal GPU 设备号不连续或 NVIDIA 驱动异常 检查nvidia-smi;重启nvidia-device-plugin;确认节点 GPU 状态正常
KV cache is too small max-model-len 过大导致 KV Cache 分配失败 减小max-model-len;增大gpu-memory-utilization;使用更大显存的 GPU
Connection refused on port 8000 模型仍在加载中,服务未就绪 等待 readinessProbe 通过;检查启动日志确认加载进度

4.2.3 兼容性问题

vLLM 版本与模型格式

vLLM 版本 支持的模型格式 注意事项
v0.4.x HF、AWQ、GPTQ 不支持 FP8,Qwen2 需要 v0.4.3+
v0.5.x HF、AWQ、GPTQ、FP8、GGUF 新增 FP8 支持,需要 H100/L40S
v0.6.x 同上 + Marlin 格式 改进了 AWQ/GPTQ 推理速度

CUDA 版本匹配

vLLM 官方镜像基于 CUDA 12.1 或 12.4 构建

宿主机驱动版本必须 >= 镜像中 CUDA 版本的最低驱动要求

CUDA 12.1 → 驱动 >= 530.30,CUDA 12.4 → 驱动 >= 550.54

# 检查驱动兼容性
nvidia-smi # 查看 Driver Version 和 CUDA Version
# 如果驱动版本过低,vLLM 容器会报 CUDA initialization error

不同 GPU 架构支持

GPU 架构 代表型号 vLLM 支持 特殊说明
Ampere A100, A10, A30 完整支持 推荐生产使用
Hopper H100, H200 完整支持 支持 FP8,性能最优
Ada Lovelace L40S, RTX 4090 完整支持 支持 FP8
Turing T4, RTX 2080 基础支持 不支持 BF16,需用 FP16
Volta V100 有限支持 不支持 BF16 和 FP8,部分模型不兼容

五、故障排查和监控

5.1 故障排查

5.1.1 日志查看

vLLM 日志级别控制

# 启动时设置日志级别(默认 INFO)
--log-level debug  # 排查问题时使用,会输出详细的调度和推理信息
--log-level warning # 生产环境推荐,减少日志量

# 环境变量方式设置
VLLM_LOGGING_LEVEL=DEBUG

K8s Pod 日志查看

# 查看当前日志(实时跟踪)
kubectl logs -f deployment/vllm-inference -n llm-serving

# 查看上一次崩溃的日志(Pod 重启后原日志会丢失)
kubectl logs deployment/vllm-inference -n llm-serving --previous

# 查看最近 500 行日志
kubectl logs deployment/vllm-inference -n llm-serving --tail=500

# 多副本场景,查看所有 Pod 日志
kubectl logs -l app=vllm-inference -n llm-serving --tail=100

# 查看 Pod 事件(排查调度和资源问题)
kubectl describe pod -l app=vllm-inference -n llm-serving | grep -A 20"Events:"

5.1.2 常见问题排查

问题一:GPU OOM(显存溢出)

# 诊断:确认显存使用情况
nvidia-smi --query-gpu=index,memory.used,memory.total,memory.free --format=csv

# 检查 vLLM 启动日志中的显存分配信息
kubectl logs deployment/vllm-inference -n llm-serving | grep -i"gpu memory|kv cache|OOM"

解决方案:

降低--gpu-memory-utilization到 0.85

减小--max-model-len(如从 32768 降到 8192)

减小--max-num-seqs(限制并发请求数)

换用量化模型(FP16 → AWQ 4bit)

问题二:推理延迟突增

# 诊断:检查 GPU 利用率是否打满
nvidia-smi dmon -s u -d 1 # 每秒采样一次 GPU 利用率

# 检查是否有大量排队请求
curl -s http://localhost:8000/metrics | grep"vllm:num_requests_waiting"

# 检查是否触发了 swap(KV Cache 被换出到 CPU)
kubectl logs deployment/vllm-inference -n llm-serving | grep -i"swap|preempt"

解决方案:

排队请求多 → 扩容副本数或增大--max-num-seqs

出现 swap/preempt → 减小--max-model-len或增加 GPU 显存

单个请求输入过长 → 业务侧限制输入长度

问题三:Pod 启动失败(模型加载阶段)

# 诊断:查看 Pod 状态和事件
kubectl get pods -n llm-serving -o wide
kubectl describe pod  -n llm-serving

# 常见事件及含义
# FailedScheduling → GPU 资源不足,没有可用节点
# OOMKilled → 内存限制过低,模型加载时被杀
# CrashLoopBackOff → 启动失败后反复重启

解决方案:

FailedScheduling→ 检查集群 GPU 资源:kubectl describe nodes | grep -A 5 "nvidia.com/gpu"

OOMKilled→ 增大resources.limits.memory,7B 模型至少 20Gi,70B 至少 80Gi

模型文件不完整 → 进入 Pod 检查:kubectl exec -it -- ls -la /models/

问题四:请求超时(504 Gateway Timeout)

# 诊断:确认超时发生在哪一层
# 1. vLLM 本身是否响应
kubectlexec-it  -n llm-serving -- curl -s -o /dev/null -w"%{http_code} %{time_total}s"
 http://localhost:8000/health

# 2. 检查 Ingress/Gateway 超时配置
kubectl get ingress -n llm-serving -o yaml | grep -i timeout

# 3. 检查是否有慢请求阻塞
curl -s http://localhost:8000/metrics | grep"vllm:request_duration_seconds"

解决方案:

Ingress 超时 → 增大proxy-read-timeout(LLM 生成长文本需要 60s+)

vLLM 响应慢 → 检查 GPU 负载,考虑扩容

单个请求生成 token 过多 → 设置--max-tokens限制输出长度

5.1.3 nvidia-smi 与 GPU 诊断

#!/bin/bash
set-euo pipefail
# gpu-diagnose.sh - GPU 状态全面诊断脚本

echo"===== 驱动和 CUDA 版本 ====="
nvidia-smi --query-gpu=driver_version --format=csv,noheader | head -1
nvidia-smi --query-gpu=name,pci.bus_id,compute_mode --format=csv

echo""
echo"===== 显存和利用率 ====="
nvidia-smi --query-gpu=index,name,utilization.gpu,utilization.memory,memory.used,memory.total,temperature.gpu,power.draw 
 --format=csv

echo""
echo"===== GPU 进程占用 ====="
nvidia-smi --query-compute-apps=pid,process_name,used_gpu_memory --format=csv

echo""
echo"===== ECC 错误检查(硬件故障排查)====="
nvidia-smi --query-gpu=index,ecc.errors.corrected.volatile.total,ecc.errors.uncorrected.volatile.total 
 --format=csv 2>/dev/null ||echo"ECC 不可用(消费级 GPU 不支持)"

echo""
echo"===== NVLink 状态(多卡通信)====="
nvidia-smi nvlink -s 2>/dev/null ||echo"NVLink 不可用"

echo""
echo"===== PCIe 带宽 ====="
nvidia-smi --query-gpu=index,pcie.link.gen.current,pcie.link.width.current --format=csv

5.2 性能监控

5.2.1 关键指标

LLM 推理服务的监控指标与传统 Web 服务有本质区别,需要关注 GPU 维度和 token 维度的指标。

# 快速查看 vLLM 暴露的 Prometheus 指标
curl -s http://localhost:8000/metrics | grep"^vllm:"| sort

# 关键指标一览
# vllm:num_requests_running  - 正在处理的请求数
# vllm:num_requests_waiting  - 排队等待的请求数
# vllm:gpu_cache_usage_perc  - KV Cache 使用率
# vllm:avg_generation_throughput_toks_per_s - 生成吞吐量(tokens/s)
# vllm:request_duration_seconds - 请求耗时分布

5.2.2 监控指标说明

指标名称 正常范围 告警阈值 说明
GPU 利用率 40%~85% > 90% 持续 5 分钟 过高说明需要扩容
显存使用率 70%~92% > 95% 接近上限会触发 OOM
KV Cache 使用率 < 80% > 90% 持续 3 分钟 过高会导致请求排队或被抢占
排队请求数 0~5 > 20 持续 2 分钟 排队过多说明吞吐不足
TTFT(首 token 延迟) < 500ms(7B) > 2s 用户体感最直接的指标
生成速度 30~80 tokens/s(7B) < 15 tokens/s 低于阈值说明 GPU 负载过重
P99 请求延迟 < 30s > 60s 长尾延迟影响用户体验
GPU 温度 < 80°C > 85°C 过热会触发降频,影响性能
错误率 < 0.1% > 1% 包括 OOM、超时等各类错误

5.2.3 Prometheus + DCGM Exporter + Grafana 监控体系

vLLM 自带 Prometheus 指标

vLLM 默认在/metrics端点暴露 Prometheus 格式指标,无需额外配置。在 K8s 中通过 ServiceMonitor 采集:

apiVersion:monitoring.coreos.com/v1
kind:ServiceMonitor
metadata:
name:vllm-metrics
namespace:llm-serving
labels:
 release:prometheus # 匹配 Prometheus Operator 的 serviceMonitorSelector
spec:
selector:
 matchLabels:
  app:vllm-inference
endpoints:
-port:http
 path:/metrics
 interval:15s    # 采集间隔

DCGM Exporter(GPU 硬件指标)

vLLM 自身指标不包含 GPU 温度、功耗、ECC 错误等硬件信息,需要 DCGM Exporter 补充:

# 通过 Helm 部署 DCGM Exporter
helm repo add gpu-helm-charts https://nvidia.github.io/dcgm-exporter/helm-charts
helm install dcgm-exporter gpu-helm-charts/dcgm-exporter 
 --namespace monitoring 
 --setserviceMonitor.enabled=true

Grafana 告警规则

# PrometheusRule - vLLM 推理服务告警
apiVersion:monitoring.coreos.com/v1
kind:PrometheusRule
metadata:
name:vllm-alerts
namespace:llm-serving
spec:
groups:
-name:vllm.rules
 rules:
 # KV Cache 使用率过高
 -alert:VLLMKVCacheHigh
  expr:vllm:gpu_cache_usage_perc>0.9
  for:3m
  labels:
   severity:warning
  annotations:
   summary:"vLLM KV Cache 使用率超过 90%"
   description:"Pod{{ $labels.pod }}KV Cache 使用率{{ $value | humanizePercentage }},可能导致请求排队"

 # 排队请求过多
 -alert:VLLMRequestQueueHigh
  expr:vllm:num_requests_waiting>20
  for:2m
  labels:
   severity:critical
  annotations:
   summary:"vLLM 排队请求数过多"
   description:"当前排队{{ $value }}个请求,建议扩容"

 # GPU 温度过高(来自 DCGM Exporter)
 -alert:GPUTemperatureHigh
  expr:DCGM_FI_DEV_GPU_TEMP>85
  for:5m
  labels:
   severity:warning
  annotations:
   summary:"GPU 温度过高"
   description:"GPU{{ $labels.gpu }}温度{{ $value }}°C,可能触发降频"

5.3 备份与恢复

5.3.1 模型文件管理

模型文件是 LLM 推理服务的核心资产,丢失意味着需要重新下载(70B 模型 140GB+,耗时数小时)。

PV 持久化方案

# 使用 PVC 持久化模型文件,Pod 重建时无需重新下载
apiVersion:v1
kind:PersistentVolumeClaim
metadata:
name:model-storage
namespace:llm-serving
spec:
accessModes:
-ReadOnlyMany     # 多个 Pod 只读共享,避免写冲突
storageClassName:nfs # NFS 适合多节点共享;高性能场景用 Lustre 或本地 SSD
resources:
 requests:
  storage:200Gi   # 根据模型大小预留,70B FP16 约 140Gi

模型仓库镜像策略

#!/bin/bash
set-euo pipefail
# sync-model.sh - 模型文件同步脚本(从源仓库同步到本地存储)

MODEL_NAME="${1:?用法: $0 <模型名> [源地址]}"
SOURCE="${2//modelscope.cn/models/${MODEL_NAME}}"
LOCAL_DIR="/data/models/${MODEL_NAME}"
BACKUP_DIR="/data/models-backup/${MODEL_NAME}"

echo"[$(date '+%Y-%m-%d %H:%M:%S')] 开始同步模型:${MODEL_NAME}"

# 下载到本地目录
mkdir -p"${LOCAL_DIR}"
ifcommand-v modelscope &>/dev/null;then
 modelscope download --model"${MODEL_NAME}"--local_dir"${LOCAL_DIR}"
else
 huggingface-cli download"${MODEL_NAME}"--local-dir"${LOCAL_DIR}"
fi

# 校验关键文件完整性
forfinconfig.json tokenizer.json;do
if[ ! -f"${LOCAL_DIR}/${f}"];then
 echo"[错误] 缺少关键文件:${f}"
 exit1
fi
done

# 创建备份(保留上一个版本)
if[ -d"${BACKUP_DIR}"];then
 rm -rf"${BACKUP_DIR}.old"
 mv"${BACKUP_DIR}""${BACKUP_DIR}.old"
fi
cp -al"${LOCAL_DIR}""${BACKUP_DIR}"# 硬链接拷贝,节省空间

echo"[$(date '+%Y-%m-%d %H:%M:%S')] 同步完成,模型路径:${LOCAL_DIR}"
echo"文件大小:$(du -sh "${LOCAL_DIR}" | cut -f1)"

5.3.2 服务恢复流程

当 vLLM 推理服务出现不可恢复故障时,按以下流程恢复:

#!/bin/bash
set-euo pipefail
# restore-vllm-service.sh - vLLM 服务恢复流程

NAMESPACE="llm-serving"
DEPLOYMENT="vllm-inference"

echo"===== 第一步:确认故障状态 ====="
kubectl get pods -n"${NAMESPACE}"-l app="${DEPLOYMENT}"-o wide
kubectl get events -n"${NAMESPACE}"--sort-by='.lastTimestamp'| tail -20

echo""
echo"===== 第二步:检查 GPU 节点状态 ====="
kubectl get nodes -l nvidia.com/gpu.present=true-o custom-columns=
NAME:.metadata.name,STATUS:.status.conditions[-1].type,GPU:.status.allocatable.nvidia\.com/gpu

echo""
echo"===== 第三步:检查模型文件完整性 ====="
# 在任意 GPU 节点上检查 PV 挂载
PV_NODE=$(kubectl get pv -o jsonpath='{.items[0].spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[0].values[0]}'2>/dev/null ||echo"N/A")
echo"模型存储节点:${PV_NODE}"

echo""
echo"===== 第四步:重建服务 ====="
# 先删除异常 Pod,让 Deployment 控制器重建
kubectl delete pods -n"${NAMESPACE}"-l app="${DEPLOYMENT}"--grace-period=30

# 等待新 Pod 就绪
echo"等待 Pod 就绪..."
kubectl rollout status deployment/"${DEPLOYMENT}"-n"${NAMESPACE}"--timeout=600s

echo""
echo"===== 第五步:验证服务可用性 ====="
# 获取 Service ClusterIP
SVC_IP=$(kubectl get svc"${DEPLOYMENT}"-n"${NAMESPACE}"-o jsonpath='{.spec.clusterIP}')
kubectl run curl-test --rm -i --restart=Never --image=curlimages/curl -- 
 curl -s -o /dev/null -w"HTTP %{http_code}, 耗时 %{time_total}s
"
"http://${SVC_IP}:8000/health"

echo""
echo"===== 恢复完成 ====="
kubectl get pods -n"${NAMESPACE}"-l app="${DEPLOYMENT}"

六、总结

6.1 技术要点回顾

引擎选型:vLLM 凭借 PagedAttention 和 Continuous Batching 机制,在吞吐量上显著优于原生 HuggingFace Transformers 推理,是当前生产环境 LLM 推理的首选引擎

部署架构:K8s + GPU Operator + vLLM 容器化部署是标准方案,通过 Deployment 管理副本、Service 暴露接口、HPA 实现弹性伸缩

显存管理:gpu-memory-utilization、max-model-len、max-num-seqs三个参数构成显存分配的核心调优三角,需要根据模型大小和业务场景综合调整

量化策略:AWQ 4bit 是通用性最好的量化方案,FP8 在 Hopper 架构上精度损失最小,选择量化方案前先评估显存是否真的不够用

高可用保障:PDB + 反亲和性 + 合理的健康检查配置 + 优雅关闭,四层防护确保服务稳定性

监控体系:vLLM 原生指标 + DCGM Exporter + Prometheus + Grafana 构成完整的可观测性方案,重点关注 KV Cache 使用率和排队请求数

6.2 进阶方向

Speculative Decoding(推测解码):使用小模型(Draft Model)预测多个 token,大模型一次性验证,在不损失精度的前提下提升生成速度 1.5~2 倍。vLLM 已支持--speculative-model参数

适用场景:对延迟敏感的在线服务

限制:需要额外显存加载 Draft Model

LoRA 动态加载:vLLM 支持在运行时动态加载/卸载 LoRA 适配器,无需为每个微调模型部署独立实例

启动参数:--enable-lora --max-loras 4 --max-lora-rank 64

请求时指定:"model": "base-model:lora-adapter-name"

适用场景:多租户场景,每个客户有独立的微调模型

多模态模型推理:vLLM 已支持 LLaVA、Qwen-VL 等视觉语言模型的推理,可处理图文混合输入

注意:图片预处理会占用额外 CPU 和内存,需要调整资源配置

Serverless GPU:基于 Knative + GPU 的按需推理方案,空闲时缩容到 0,请求到来时自动扩容

挑战:冷启动时间长(模型加载 3~8 分钟),需要配合模型缓存和预热策略

适用场景:低频调用的内部工具、开发测试环境

6.3 参考资料

vLLM 官方文档- 最权威的参数说明和部署指南

vLLM GitHub 仓库- 源码、Issue 和 Release Notes

NVIDIA GPU Operator 文档- K8s GPU 管理

DCGM Exporter- GPU 监控指标采集

PagedAttention 论文- vLLM 核心算法原理

HuggingFace Model Hub- 模型下载和文档

附录

A. 命令速查表

# ===== vLLM 服务管理 =====
# 启动 vLLM(Docker 方式)
docker run --gpus all -v /models:/models -p 8000:8000 vllm/vllm-openai:latest 
 --model /models/qwen2.5-7b --served-model-name qwen2.5-7b

# 健康检查
curl http://localhost:8000/health

# 查看已加载的模型
curl http://localhost:8000/v1/models

# 查看 Prometheus 指标
curl http://localhost:8000/metrics

# ===== K8s 运维 =====
# 查看 Pod 状态
kubectl get pods -n llm-serving -l app=vllm-inference -o wide

# 查看 Pod 日志
kubectl logs -f deployment/vllm-inference -n llm-serving

# 滚动重启
kubectl rollout restart deployment/vllm-inference -n llm-serving

# 查看 HPA 状态
kubectl get hpa -n llm-serving

# 手动扩缩容
kubectl scale deployment/vllm-inference -n llm-serving --replicas=3

# ===== GPU 诊断 =====
# 查看 GPU 状态
nvidia-smi

# 持续监控 GPU(每秒刷新)
nvidia-smi dmon -s u -d 1

# 查看 GPU 进程
nvidia-smi --query-compute-apps=pid,process_name,used_gpu_memory --format=csv

# 检查 GPU 拓扑(多卡 NVLink 连接)
nvidia-smi topo -m

# ===== 性能测试 =====
# 简单功能验证
curl http://localhost:8000/v1/chat/completions 
 -H"Content-Type: application/json"
 -d'{"model":"qwen2.5-7b","messages":[{"role":"user","content":"hello"}],"max_tokens":50}'

# 并发压测(需安装 hey)
hey -n 100 -c 10 -m POST -H"Content-Type: application/json"
 -d'{"model":"qwen2.5-7b","messages":[{"role":"user","content":"写一首五言绝句"}],"max_tokens":100}'
 http://localhost:8000/v1/chat/completions

B. 配置参数详解

参数 默认值 说明 调优建议
--model 必填 模型路径或 HuggingFace 模型 ID 生产环境使用本地路径
--tensor-parallel-size 1 张量并行数(多卡推理) 等于使用的 GPU 数量
--gpu-memory-utilization 0.9 GPU 显存使用比例 单模型独占可调到 0.92
--max-model-len 模型默认值 最大上下文长度 按业务实际需求设置,越小并发越高
--max-num-seqs 256 最大并发请求数 短文本场景可增大到 512
--max-num-batched-tokens max-model-len 单次迭代最大 token 数 一般不需要手动设置
--dtype auto 模型精度 auto 会根据模型配置自动选择
--quantization None 量化方式 awq / gptq / fp8 / squeezellm
--enable-prefix-caching False 启用前缀缓存 相同 system prompt 场景建议开启
--enable-lora False 启用 LoRA 支持 多租户微调场景使用
--max-loras 1 最大同时加载的 LoRA 数 根据显存和租户数设置
--served-model-name 模型路径 API 中暴露的模型名称 建议设置简短易记的名称
--host 0.0.0.0 监听地址 容器环境保持默认
--port 8000 监听端口 容器环境保持默认
--log-level info 日志级别 生产用 warning,排查用 debug
--disable-log-requests False 禁用请求日志 高并发生产环境建议开启以减少 IO
--num-scheduler-steps 1 调度器步数 离线批处理可设为 10 提升吞吐
--swap-space 4 CPU swap 空间(GiB) 显存紧张时可增大,但会影响性能
--seed 0 随机种子 需要可复现结果时设置固定值

C. 术语表

术语 英文 解释
张量并行 Tensor Parallelism (TP) 将模型权重按张量维度切分到多张 GPU 上,每张卡持有部分权重,协同完成推理
流水线并行 Pipeline Parallelism (PP) 将模型按层切分到多张 GPU 上,数据在 GPU 之间流水线式传递
KV Cache Key-Value Cache 推理时缓存已计算的 Key 和 Value 张量,避免重复计算,是自回归生成的核心优化
PagedAttention PagedAttention vLLM 的核心算法,借鉴操作系统虚拟内存分页思想管理 KV Cache,消除显存碎片
Continuous Batching Continuous Batching 连续批处理,请求完成后立即释放资源并插入新请求,而非等待整个 batch 完成
Prefill Prefill 推理的第一阶段,处理完整输入序列并生成 KV Cache,计算密集型
Decode Decode 推理的第二阶段,逐 token 生成输出,访存密集型
TTFT Time To First Token 首 token 延迟,从发送请求到收到第一个生成 token 的时间
TPOT Time Per Output Token 每个输出 token 的生成时间,决定了流式输出的速度
推测解码 Speculative Decoding 用小模型快速预测多个 token,大模型一次性验证,加速生成过程
LoRA Low-Rank Adaptation 低秩适配,一种参数高效的模型微调方法,只训练少量额外参数
量化 Quantization 将模型权重从高精度(FP16)转换为低精度(INT4/INT8/FP8),减少显存占用
AWQ Activation-aware Weight Quantization 基于激活感知的权重量化方法,在 4bit 量化中精度损失较小
GPTQ GPTQ 基于近似二阶信息的训练后量化方法,压缩率高
MIG Multi-Instance GPU NVIDIA 多实例 GPU 技术,将一张 GPU 硬件级分割为多个独立实例
DCGM Data Center GPU Manager NVIDIA 数据中心 GPU 管理工具,提供监控、诊断和策略管理

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • gpu
    gpu
    +关注

    关注

    28

    文章

    5259

    浏览量

    136039
  • 开源
    +关注

    关注

    3

    文章

    4325

    浏览量

    46427
  • 大模型
    +关注

    关注

    2

    文章

    3750

    浏览量

    5268

原文标题:vLLM + K8s:大模型推理服务的弹性部署与GPU调度方案

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    无法在GPU上运行ONNX模型的Benchmark_app怎么解决?

    在 CPU 和 GPU 上运行OpenVINO™ 2023.0 Benchmark_app推断的 ONNX 模型。 在 CPU 上推理成功,但在 GPU 上失败。
    发表于 03-06 08:02

    【产品场景】弹性裸金属服务服务于市场的技术概要分析

    摘要: 弹性裸金属服务服务于市场的技术概要分析混合云和第三方虚拟化软件部署伴随着公有云的高速发展,混合云打通客户线下专有云和线上公有云资源的需求日趋强烈。Open stack和VMw
    发表于 05-18 22:26

    借助Docker弹性部署自己的服务

    用Docker弹性部署自己的服务
    发表于 04-19 10:15

    用tflite接口调用tensorflow模型进行推理

    摘要本文为系列博客tensorflow模型部署系列的一部分,用于实现通用模型部署。本文主要实现用tflite接口调用tensorflow模型
    发表于 12-22 06:51

    基于Docker的云资源弹性调度策略

    存储方法进行改进;然后,建立了一个基于节点综合负载的资源调度优化模型;最后,将Ceph集群和Docker容器的特点相结合,利用Docker Swarm实现了既考虑数据存储、又考虑集群负载的应用容器部署算法和应用在线迁移算法。实验
    发表于 03-29 10:11 0次下载
    基于Docker的云资源<b class='flag-5'>弹性</b><b class='flag-5'>调度</b>策略

    使用MIG和Kubernetes部署Triton推理服务

      NVIDIA Triton 推理服务器是一款开源人工智能模型服务软件,可简化在生产中大规模部署经过培训的人工智能
    的头像 发表于 04-07 09:54 4684次阅读

    如何实现高效的部署医疗影像推理

    变长,GPU 使用效率不高。医疗影像推理的另一个需要考虑的问题是如何实现高效的部署。我们往往需要部署多个医疗影像 AI 应用,那么如何去调度
    的头像 发表于 04-09 08:18 2452次阅读

    基于NVIDIA Triton的AI模型高效部署实践

    NVIDIA Triton 推理服务器(以前称为 TensorRT 推理服务器)是一款开源软件,可简化深度学习模型在生产环境中的
    的头像 发表于 06-28 15:49 3081次阅读

    腾讯云TI平台利用NVIDIA Triton推理服务器构造不同AI应用场景需求

    腾讯云 TI 平台 TI-ONE 利用 NVIDIA Triton 推理服务器构造高性能推理服务部署平台,使用户能够非常便捷地部署包括 TN
    的头像 发表于 09-05 15:33 3379次阅读

    NVIDIA GPU加速潞晨科技Colossal-AI大模型开发进程

    通过 NVIDIA GPU 加速平台,Colossal-AI 实现了通过高效多维并行、异构内存管理、大规模优化库、自适应任务调度等方式,更高效快速部署 AI 大模型训练与
    的头像 发表于 10-19 09:39 2445次阅读

    如何开启Stable Diffusion WebUI模型推理部署

    如何开启Stable Diffusion WebUI模型推理部署
    的头像 发表于 12-11 20:13 1412次阅读
    如何开启Stable Diffusion WebUI<b class='flag-5'>模型</b><b class='flag-5'>推理</b><b class='flag-5'>部署</b>

    摩尔线程宣布成功部署DeepSeek蒸馏模型推理服务

    近日,摩尔线程智能科技(北京)有限责任公司在其官方渠道发布了一则重要消息,宣布公司已经成功实现了对DeepSeek蒸馏模型推理服务部署。这一技术突破,标志着摩尔线程在人工智能领域迈出
    的头像 发表于 02-06 13:49 1432次阅读

    基于RAKsmart云服务器的AI大模型实时推理方案设计

    面对高并发请求、严格的响应延迟要求及波动的业务负载,传统本地化部署的算力瓶颈愈发显著。RAKsmart云服务器凭借其弹性计算资源池、分布式网络架构与全栈AI加速能力,为AI大模型实时
    的头像 发表于 05-13 10:33 711次阅读

    积算科技上线赤兔推理引擎服务,创新解锁FP8大模型算力

    模型轻量化部署方案。用户通过远程算力平台预置的模型镜像与AI工具,仅需50%的GPU算力即可解锁大模型
    的头像 发表于 07-30 21:44 1027次阅读

    神州鲲泰携手趋境科技推出大模型推理智能算力调度解决方案

    近日,华为中国合作伙伴大会现场,神州鲲泰与趋境科技正式签订生态合作协议,并联合推出面向企业级大模型推理场景的智能算力调度解决方案
    的头像 发表于 04-17 15:12 346次阅读
    神州鲲泰携手趋境科技推出大<b class='flag-5'>模型</b><b class='flag-5'>推理</b>智能算力<b class='flag-5'>调度</b>解决<b class='flag-5'>方案</b>