vLLM + K8s:玩转大模型推理,弹性部署与GPU调度实战指南
摘要 本文介绍了基于vLLM和Kubernetes的大模型推理服务弹性部署方案。vLLM通过PagedAttention、Continuous Batching和Tensor Parallelism三项核心技术,显著提升GPU利用率至95%以上,降低延迟30-50%。文章详细对比了主流推理框架特性,推荐vLLM作为生产环境首选方案,并提供了完整的部署流程:包括GPU节点准备、NVIDIA Oper
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/nvidiahelm repo update# 部署 GPU Operator(如果节点已安装驱动,设置 driver.enabled=false)helm install gpu-operator nvidia/gpu-operator \ --namespace gpu-operator \ --create-namespace \ --set driver.enabled=false \ --set toolkit.enabled=true \ --set devicePlugin.enabled=true \ --set migManager.enabled=true \ --set dcgmExporter.enabled=true \ --version v24.6.2# 等待所有组件就绪kubectl -n gpu-operator get pods -w
2.1.3 GPU 资源验证
# 确认节点已注册 GPU 资源kubectl describe node <gpu-node> | grep -A 5 "Capacity"# 预期输出:nvidia.com/gpu: 8(以 8 卡节点为例)# 运行 GPU 测试 Podkubectl 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.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:vllm-qwen25-7bnamespace:llm-servinglabels: app:vllm model:qwen25-7bspec:replicas:2selector: matchLabels: app:vllm model:qwen25-7btemplate: 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.yamlapiVersion:v1kind:Servicemetadata:name:vllm-qwen25-7bnamespace:llm-servingspec:selector: app:vllm model:qwen25-7bports: -name:http port:8000 targetPort:8000 protocol:TCPtype:ClusterIP---# vllm-ingress.yamlapiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:vllm-ingressnamespace:llm-servingannotations: # 推理请求可能耗时较长,超时设为 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:nginxrules: -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.yamlapiVersion:autoscaling/v2kind:HorizontalPodAutoscalermetadata:name:vllm-qwen25-7b-hpanamespace:llm-servingspec:scaleTargetRef: apiVersion:apps/v1 kind:Deployment name:vllm-qwen25-7bminReplicas:1maxReplicas:8metrics: # 基于 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.yamlapiVersion:keda.sh/v1alpha1kind:ScaledObjectmetadata:name:vllm-qwen25-7b-scalernamespace:llm-servingspec:scaleTargetRef: name:vllm-qwen25-7bminReplicaCount:1maxReplicaCount:8cooldownPeriod: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/v1kind:HTTPRoutemetadata:name:llm-routernamespace:llm-servingspec:parentRefs: -name:llm-gatewayrules: # /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:v1kind:Namespacemetadata:name:llm-servinglabels: monitoring:enabled---# PodDisruptionBudget:确保滚动更新和节点维护时至少 1 个 Pod 可用apiVersion:policy/v1kind:PodDisruptionBudgetmetadata:name:vllm-qwen25-7b-pdbnamespace:llm-servingspec:minAvailable:1selector: matchLabels: app:vllm model:qwen25-7b---# ConfigMap:集中管理 vLLM 启动参数,修改无需重建镜像apiVersion:v1kind:ConfigMapmetadata:name:vllm-confignamespace:llm-servingdata: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/v1kind:Deploymentmetadata:name:vllm-qwen25-7bnamespace:llm-servingspec:replicas:2strategy: type:RollingUpdate rollingUpdate: maxSurge:1 # 滚动更新时最多多 1 个 Pod maxUnavailable:0 # 不允许不可用,确保零停机selector: matchLabels: app:vllm model:qwen25-7btemplate: 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:v1kind:Servicemetadata:name:vllm-qwen25-7bnamespace:llm-servingspec:selector: app:vllm model:qwen25-7bports: -port:8000 targetPort:8000---apiVersion:autoscaling/v2kind:HorizontalPodAutoscalermetadata:name:vllm-qwen25-7b-hpanamespace:llm-servingspec:scaleTargetRef: apiVersion:apps/v1 kind:Deployment name:vllm-qwen25-7bminReplicas:1maxReplicas:8metrics: -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/v1alpha1kind:ScaledObjectmetadata:name:vllm-qwen25-7b-kedanamespace:llm-servingspec:scaleTargetRef: name:vllm-qwen25-7bminReplicaCount: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:180triggers: # 触发器 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/v1kind:PrometheusRulemetadata:name:vllm-alertsnamespace:llm-servingspec: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:-http://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() { local req_id=$1 local start_time end_time latency_ms status_code body # 构造请求体 body=$(cat <<REQEOF{"model": "${MODEL_NAME}","messages": [ {"role": "system", "content": "你是一个技术助手。"}, {"role": "user", "content": "请用 200 字简要介绍 Kubernetes 的核心架构设计理念。请求编号:${req_id}"} ],"max_tokens": ${MAX_TOKENS},"temperature": 0.7}REQEOF ) start_time=$(date +%s%N) # 发送请求并捕获响应 local response response=$(curl -s -w "\n%{http_code}" \ -X POST "${API_URL}" \ -H "Content-Type: application/json" \ -d "${body}" \ --max-time 120 2>/dev/null) || true end_time=$(date +%s%N) # 解析响应 status_code=$(echo"${response}" | tail -1) local resp_body resp_body=$(echo"${response}" | sed '$d') # 计算延迟(毫秒) latency_ms=$(( (end_time - start_time) / 1000000 )) # 提取生成的 token 数 local tokens 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_requestexport API_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}" # 成功率 local success_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\n", latencies[1] printf " P50: %d\n", latencies[int(count*0.5)] printf " P90: %d\n", latencies[int(count*0.9)] printf " P95: %d\n", latencies[int(count*0.95)] printf " P99: %d\n", latencies[int(count*0.99)] printf " 最大值: %d\n", latencies[count] printf " 平均值: %d\n", sum/count }'"${RESULT_FILE}" # Token 吞吐 local total_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 4bit7B 模型 + 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/bashset -euo pipefail# gpu-utilization-check.sh - 检查 GPU 利用率并输出建议THRESHOLD_LOW=70THRESHOLD_HIGH=85# 获取所有 GPU 的利用率nvidia-smi --query-gpu=index,utilization.gpu,memory.used,memory.total \ --format=csv,noheader,nounits | while IFS=', '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}%"fidone
Spot/Preemptible 实例使用:
- 离线批量推理任务(数据标注、批量摘要)适合使用 Spot 实例,成本降低 60%~70%
- 在线服务不建议使用 Spot,被回收时会导致请求中断
- 混合策略:On-Demand 实例承载基线流量,Spot 实例承载弹性流量
缩容策略(冷却期设计):
# HPA 缩容配置 - 避免频繁缩扩容导致的抖动apiVersion:autoscaling/v2kind:HorizontalPodAutoscalerspec: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/v1kind:PodDisruptionBudgetmetadata:name:vllm-pdbnamespace:llm-servingspec: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:vllmlivenessProbe: 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:3startupProbe: # 启动探针:模型加载完成前不触发存活检查 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:shmemptyDir: 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 镜像加速下载export VLLM_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 widekubectl describe pod <pod-name> -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 <pod> -- ls -la /models/
问题四:请求超时(504 Gateway Timeout)
# 诊断:确认超时发生在哪一层# 1. vLLM 本身是否响应kubectl exec -it <pod> -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/bashset -euo pipefail# gpu-diagnose.sh - GPU 状态全面诊断脚本echo"===== 驱动和 CUDA 版本 ====="nvidia-smi --query-gpu=driver_version --format=csv,noheader | head -1nvidia-smi --query-gpu=name,pci.bus_id,compute_mode --format=csvecho""echo"===== 显存和利用率 ====="nvidia-smi --query-gpu=index,name,utilization.gpu,utilization.memory,memory.used,memory.total,temperature.gpu,power.draw \ --format=csvecho""echo"===== GPU 进程占用 ====="nvidia-smi --query-compute-apps=pid,process_name,used_gpu_memory --format=csvecho""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/v1kind:ServiceMonitormetadata:name:vllm-metricsnamespace:llm-servinglabels: release:prometheus # 匹配 Prometheus Operator 的 serviceMonitorSelectorspec:selector: matchLabels: app:vllm-inferenceendpoints:-port:http path:/metrics interval:15s # 采集间隔
DCGM Exporter(GPU 硬件指标):
vLLM 自身指标不包含 GPU 温度、功耗、ECC 错误等硬件信息,需要 DCGM Exporter 补充:
# 通过 Helm 部署 DCGM Exporterhelm repo add gpu-helm-charts https://nvidia.github.io/dcgm-exporter/helm-chartshelm install dcgm-exporter gpu-helm-charts/dcgm-exporter \ --namespace monitoring \ --set serviceMonitor.enabled=true
Grafana 告警规则:
# PrometheusRule - vLLM 推理服务告警apiVersion:monitoring.coreos.com/v1kind:PrometheusRulemetadata:name:vllm-alertsnamespace:llm-servingspec: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:v1kind:PersistentVolumeClaimmetadata:name:model-storagenamespace:llm-servingspec:accessModes:-ReadOnlyMany # 多个 Pod 只读共享,避免写冲突storageClassName:nfs # NFS 适合多节点共享;高性能场景用 Lustre 或本地 SSDresources: requests: storage:200Gi # 根据模型大小预留,70B FP16 约 140Gi
模型仓库镜像策略:
#!/bin/bashset -euo pipefail# sync-model.sh - 模型文件同步脚本(从源仓库同步到本地存储)MODEL_NAME="${1:?用法: $0 <模型名> [源地址]}"SOURCE="${2:-https://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# 校验关键文件完整性for f in config.json tokenizer.json; doif [ ! -f "${LOCAL_DIR}/${f}" ]; then echo"[错误] 缺少关键文件: ${f}" exit 1fidone# 创建备份(保留上一个版本)if [ -d "${BACKUP_DIR}" ]; then rm -rf "${BACKUP_DIR}.old" mv "${BACKUP_DIR}""${BACKUP_DIR}.old"ficp -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/bashset -euo pipefail# restore-vllm-service.sh - vLLM 服务恢复流程NAMESPACE="llm-serving"DEPLOYMENT="vllm-inference"echo"===== 第一步:确认故障状态 ====="kubectl get pods -n "${NAMESPACE}" -l app="${DEPLOYMENT}" -o widekubectl get events -n "${NAMESPACE}" --sort-by='.lastTimestamp' | tail -20echo""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/gpuecho""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=600secho""echo"===== 第五步:验证服务可用性 ====="# 获取 Service ClusterIPSVC_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\n" \"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 分钟),需要配合模型缓存和预热策略
- 适用场景:低频调用的内部工具、开发测试环境
如何系统的学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
一直在更新,更多的大模型学习和面试资料已经上传带到CSDN的官方了,有需要的朋友可以扫描下方二维码免费领取【保证100%免费】👇👇

01.大模型风口已至:月薪30K+的AI岗正在批量诞生

2025年大模型应用呈现爆发式增长,根据工信部最新数据:
国内大模型相关岗位缺口达47万
初级工程师平均薪资28K(数据来源:BOSS直聘报告)
70%企业存在"能用模型不会调优"的痛点
真实案例:某二本机械专业学员,通过4个月系统学习,成功拿到某AI医疗公司大模型优化岗offer,薪资直接翻3倍!
02.大模型 AI 学习和面试资料
1️⃣ 提示词工程:把ChatGPT从玩具变成生产工具
2️⃣ RAG系统:让大模型精准输出行业知识
3️⃣ 智能体开发:用AutoGPT打造24小时数字员工
📦熬了三个大夜整理的《AI进化工具包》送你:
✔️ 大厂内部LLM落地手册(含58个真实案例)
✔️ 提示词设计模板库(覆盖12大应用场景)
✔️ 私藏学习路径图(0基础到项目实战仅需90天)






第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

更多推荐

所有评论(0)