K8s部署vLLM推理服务详细步骤

描述

一、概述

1.1 背景介绍

vLLM在生产环境部署时,服务暴露是关键环节。Kubernetes的Service和Ingress组件负责将内部Pod流量对外暴露,合理的Service类型选择和负载均衡策略直接影响推理服务的可用性、响应速度和资源利用率。

生产环境vLLM推理服务通常以多副本形式部署,通过Ingress Controller接收外部HTTP请求,再通过Service分发到后端vLLM Pod。整个链路的性能瓶颈往往出现在Service和Ingress层:不当的负载均衡策略会导致请求在Pod间分布不均,Session Affinity缺失可能导致同一用户的多次推理请求落到不同Pod,破坏上下文连续性;过长的健康检查间隔会让故障Pod继续接收流量,造成请求失败;TLS配置不当则可能引发SSL握手失败或性能下降。

实际部署中,vLLM推理服务的特点是:GPU资源昂贵,通常2-4个vLLM实例共享一个物理GPU或独占GPU显存;单次推理请求耗时较长(100ms-2s不等,取决于模型大小和输出长度);推理服务对网络延迟敏感,跨Region部署会导致明显的性能下降。这些特性要求Ingress和Service配置必须针对推理场景优化。

1.2 技术特点

Headless Service vs ClusterIP

Headless Service(clusterIP: None)直接返回Pod IP列表,适用于StatefulSet部署或有状态服务。vLLM推理服务通常使用普通Deployment部署,因此推荐使用ClusterIP类型。ClusterIP提供稳定的服务IP,由kube-proxy在节点层面实现负载均衡,适合无状态的推理服务。

Headless Service的唯一优势是客户端可以直接获取Pod IP,适合需要精细控制连接的场景。但实际测试中,在vLLM推理场景下,使用ClusterIP配合Ingress Controller的负载均衡已经足够,Headless Service反而增加了客户端复杂度。

Ingress Controller选型

Nginx Ingress是最成熟的选择,GitHub Star超过18k,社区活跃,文档完善,生产环境经过大量验证。默认配置下,单个Nginx Ingress Controller可以支撑10万+ QPS,足以满足大多数vLLM推理服务的需求。

Traefik 3.0原生支持Service Mesh集成,配置更简洁,动态配置更新无需重启。但在GPU推理场景下,Traefik的开销略高于Nginx(实测约3-5%),需要权衡。

AWS ALB Ingress适合云原生环境,可以无缝集成AWS的Target Group和Auto Scaling。但跨Region部署时,ALB会引入额外的跨可用区延迟,对vLLM这种对延迟敏感的服务不太友好。

生产环境推荐Nginx Ingress或Traefik,具体取决于团队熟悉程度和现有技术栈。

会话保持策略

推理服务通常需要Session Affinity。vLLM的KV Cache会缓存历史对话的中间状态,如果同一用户的多次请求被分发到不同Pod,KV Cache无法复用,推理性能会下降30-50%。

Nginx Ingress支持基于客户端IP的会话保持,通过nginx.ingress.kubernetes.io/affinity和nginx.ingress.kubernetes.io/session-cookie-name参数配置。实测下来,默认的cookie hash策略在多客户端场景下表现良好,负载分布较为均匀。

注意:会话保持时间不宜过长,建议设置为300s-600s。过长的会话保持会导致新Pod无法接收流量,影响滚动升级。

GPU资源调度

K8s 1.29+原生支持GPU资源调度,通过resources.limits.nvidia.com/gpu字段指定GPU需求。vLLM部署时必须设置GPU资源限制,否则可能导致多个Pod争抢同一个GPU,造成OOM或性能剧烈波动。

实际生产环境中,单张A100 80GB通常分配给2-4个vLLM实例,每个实例分配20GB-40GB显存。需要根据模型大小和并发需求调整分配策略:7B模型推荐单GPU部署2-3实例,70B模型推荐单GPU部署1-2实例。

GPU资源调度使用的是软限制,K8s只确保Pod调度到有GPU的节点,但不保证独占。如果节点有多个GPU,可能需要配置Node Affinity确保Pod分布在不同GPU上。

1.3 适用场景

生产环境vLLM推理服务

需要高可用、负载均衡、自动故障转移的推理服务。通过Ingress + Service组合实现流量接入和分发,配合K8s的ReplicaSet实现多副本部署,当某个Pod故障时自动创建新Pod替代。

多副本推理部署

多个vLLM实例并行服务,需要Ingress进行流量分发。场景包括:高QPS业务需要横向扩展、模型并行需要多个实例协同、A/B测试需要同时部署多个模型版本。

外部流量接入

需要暴露给外部用户访问的推理API服务。通过Ingress配置域名、TLS证书、限流策略,实现安全可控的外部访问。

1.4 环境要求

组件 版本要求 说明
Kubernetes 1.29+ 支持GPU资源调度和HPA v2
vLLM 0.6.3+ 2026年最新稳定版
NVIDIA Driver 535+ 支持CUDA 12.2+
CUDA 12.2+ 2026年主流CUDA版本
Nginx Ingress 1.9+ 最新稳定版
Traefik 3.0+ 支持Service Mesh
GPU NVIDIA A100/H100/A800 推荐80GB显存

二、详细步骤

2.1 准备工作

2.1.1 系统检查

部署前需要确认K8s集群GPU节点状态、NVIDIA驱动版本、CUDA工具链是否正常。

 

# 检查K8s集群版本
kubectl version --short
Client Version: v1.30.2
Kustomize Version: v5.4.2
Server Version: v1.30.2

# 检查GPU节点状态
kubectl get nodes -l accelerator=nvidia-tesla-gpu
NAME           STATUS   ROLES    AGE   VERSION
gpu-node-1     Ready    worker   30d   v1.30.2
gpu-node-2     Ready    worker   30d   v1.30.2

# 检查节点GPU资源
kubectl describe node gpu-node-1 | grep -A 5 "nvidia.com/gpu"
nvidia.com/gpu     8     8     8
  Allocated resources:
    (Total limits may be over 100 percent, i.e., overcommitted.)
    Resource           Requests     Limits
    --------           --------     ------
    nvidia.com/gpu     4            4

# 检查NVIDIA驱动版本
kubectl exec -it nvidia-device-plugin-xxxx -n kube-system -- nvidia-smi
NVIDIA-SMI 535.183.01   Driver Version: 535.183.01   CUDA Version: 12.2

# 检查CUDA工具链
docker run --rm --gpus all nvidia/cuda:12.2.0-base-ubuntu22.04 nvidia-smi

 

说明:GPU节点应该标记为accelerator=nvidia-tesla-gpu或其他自定义标签,方便后续通过Node Selector控制Pod调度。nvidia-smi输出显示CUDA 12.2+才能支持vLLM的最新特性。

2.1.2 镜像准备

vLLM官方镜像已经预装CUDA和PyTorch,可以直接使用。生产环境建议基于官方镜像构建自己的业务镜像,将模型文件、推理脚本打包进镜像。

 

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

# 验证GPU支持
docker run --rm --gpus all vllm/vllm-openai:v0.6.3 python -c "import torch; print(torch.cuda.is_available())"
True

# 验证vLLM版本
docker run --rm --gpus all vllm/vllm-openai:v0.6.3 python -c "import vllm; print(vllm.__version__)"
0.6.3

# 推送到私有镜像仓库
docker tag vllm/vllm-openai:v0.6.3 registry.example.com/vllm:v0.6.3
docker push registry.example.com/vllm:v0.6.3

 

说明:如果使用私有模型,建议将模型文件打包进镜像或通过PVC挂载。生产环境推荐使用Model Registry(如MLflow、HuggingFace Hub)统一管理模型版本。

2.1.3 命名空间准备

创建独立的命名空间用于部署vLLM推理服务,便于资源隔离和权限管理。

 

# 创建命名空间
kubectl create namespace vllm-inference

# 设置默认命名空间(可选)
kubectl config set-context --current --namespace=vllm-inference

# 验证命名空间
kubectl get namespace vllm-inference
NAME              STATUS   AGE
vllm-inference    Active   2m

# 创建资源配额(可选)
cat <

 

说明:资源配额防止vLLM服务占用过多GPU资源影响其他业务。根据集群规模调整配额值,确保每个命名空间都有公平的GPU资源。

2.2 核心配置

2.2.1 vLLM Deployment配置

Deployment定义vLLM Pod的副本数、资源限制、环境变量、健康检查等关键配置。

 

# 创建vLLM Deployment
cat < vllm-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-inference
  namespace: vllm-inference
  labels:
    app: vllm-inference
    version: v0.6.3
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: vllm-inference
  template:
    metadata:
      labels:
        app: vllm-inference
        version: v0.6.3
    spec:
      nodeSelector:
        accelerator: nvidia-tesla-gpu
      containers:
      - name: vllm
        image: registry.example.com/vllm:v0.6.3
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8000
          name: http
          protocol: TCP
        env:
        - name: MODEL_NAME
          value: "meta-llama/Llama-2-7b-chat-hf"
        - name: TOKENIZER_MODE
          value: "auto"
        - name: MAX_MODEL_LEN
          value: "4096"
        - name: GPU_MEMORY_UTILIZATION
          value: "0.90"
        - name: DTYPE
          value: "float16"
        - name: SERVED_MODEL_NAME
          value: "llama-2-7b"
        resources:
          requests:
            cpu: "4"
            memory: "16Gi"
            nvidia.com/gpu: "1"
          limits:
            cpu: "8"
            memory: "32Gi"
            nvidia.com/gpu: "1"
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 60
          periodSeconds: 30
          timeoutSeconds: 10
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 15"]
EOF

kubectl apply -f vllm-deployment.yaml

 

说明

replicas: 3:部署3个副本,通过Ingress进行负载均衡

nodeSelector:确保Pod只调度到有GPU的节点

GPU_MEMORY_UTILIZATION: 0.90:GPU显存利用率90%,预留10%用于KV Cache动态增长

MAX_MODEL_LEN: 4096:最大序列长度,根据业务需求调整

livenessProbe.initialDelaySeconds: 60:vLLM加载模型需要较长时间,初始延迟设为60s

lifecycle.preStop:Pod删除前等待15s,确保Ingress不再转发新流量

2.2.2 Service配置

Service类型选择ClusterIP,通过kube-proxy实现负载均衡。配置会话保持避免请求分发到不同Pod。

 

# 创建ClusterIP Service
cat < vllm-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: vllm-service
  namespace: vllm-inference
  labels:
    app: vllm-inference
spec:
type: ClusterIP
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 300
  ports:
  - port: 80
    targetPort: 8000
    protocol: TCP
    name: http
  selector:
    app: vllm-inference
EOF

kubectl apply -f vllm-service.yaml

# 验证Service
kubectl get svc vllm-service -n vllm-inference
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
vllm-service    ClusterIP   10.96.234.12            80/TCP    1m

# 查看Service端点
kubectl get endpoints vllm-service -n vllm-inference
NAME            ENDPOINTS                             AGE
vllm-service    10.244.1.12:8000,10.244.2.18:8000,10.244.3.21:8000   1m

 

说明

type: ClusterIP:使用集群内部IP,通过Ingress暴露

sessionAffinity: ClientIP:基于客户端IP的会话保持

sessionAffinityConfig.clientIP.timeoutSeconds: 300:会话保持300s,避免同一用户的多次请求分发到不同Pod

ports.port: 80:Service监听端口

ports.targetPort: 8000:Pod容器端口

2.2.3 Ingress配置

Nginx Ingress配置路由规则、TLS证书、限流策略。生产环境必须配置TLS,推荐使用Cert Manager自动管理证书。

 

# 创建Nginx Ingress
cat < vllm-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: vllm-ingress
  namespace: vllm-inference
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    nginx.ingress.kubernetes.io/proxy-buffer-size: "32k"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
    nginx.ingress.kubernetes.io/client-body-buffer-size: "10m"
    nginx.ingress.kubernetes.io/limit-connections: "100"
    nginx.ingress.kubernetes.io/limit-rps: "200"
    nginx.ingress.kubernetes.io/limit-burst: "50"
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "route"
    nginx.ingress.kubernetes.io/session-cookie-expires: "3600"
    nginx.ingress.kubernetes.io/session-cookie-max-age: "3600"
    nginx.ingress.kubernetes.io/session-cookie-samesite: "Lax"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - vllm.example.com
    secretName: vllm-tls-cert
  rules:
  - host: vllm.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: vllm-service
            port:
              number: 80
EOF

kubectl apply -f vllm-ingress.yaml

# 验证Ingress
kubectl get ingress vllm-ingress -n vllm-inference
NAME            CLASS   HOSTS               ADDRESS          PORTS     AGE
vllm-ingress    nginx   vllm.example.com    192.168.1.100    80, 443   1m

# 查看Ingress详细配置
kubectl describe ingress vllm-ingress -n vllm-inference

 

说明

ingressClassName: nginx:使用Nginx Ingress Controller

ssl-redirect: "true":强制HTTPS重定向

proxy-connect-timeout: 600、proxy-send-timeout: 600、proxy-read-timeout: 600:推理请求耗时较长,超时时间设置为600s

limit-rps: 200:每秒请求数限制为200,防止过载

limit-burst: 50:突发流量缓冲为50请求

affinity: "cookie":基于Cookie的会话保持

session-cookie-name: "route":Cookie名称为route

session-cookie-expires: "3600":Cookie有效期1小时

cert-manager.io/cluster-issuer:使用Cert Manager自动签发Let's Encrypt证书

2.3 启动和验证

2.3.1 启动服务

按照Deployment → Service → Ingress的顺序依次应用配置,确保依赖关系正确。

 

# 应用配置(按顺序)
kubectl apply -f vllm-deployment.yaml
kubectl apply -f vllm-service.yaml
kubectl apply -f vllm-ingress.yaml

# 查看Pod启动状态
kubectl get pods -n vllm-inference -l app=vllm-inference
NAME                               READY   STATUS    RESTARTS   AGE
vllm-inference-7b8f9c6d6-xj2kp     1/1     Running   0          2m
vllm-inference-7b8f9c6d6-p4r5t     1/1     Running   0          2m
vllm-inference-7b8f9c6d6-z8q9x     1/1     Running   0          2m

# 查看Pod详情
kubectl describe pod vllm-inference-7b8f9c6d6-xj2kp -n vllm-inference

# 查看GPU分配情况
kubectl describe pod vllm-inference-7b8f9c6d6-xj2kp -n vllm-inference | grep -A 3 "Allocated resources"
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests    Limits
  --------           --------    ------
  cpu                4           8
  memory             16Gi        32Gi
  nvidia.com/gpu     1           1

 

说明:Pod状态应该显示Running,Ready状态为1/1。如果Pod处于ContainerCreating或ImagePullBackOff状态,使用kubectl describe pod查看详细错误信息。

2.3.2 功能验证

验证Pod健康状态、Service端点、Ingress路由是否正常工作。

 

# 1. 验证Pod健康检查
kubectl exec -it vllm-inference-7b8f9c6d6-xj2kp -n vllm-inference -- curl -s http://localhost:8000/health
{"status": "ok"}

# 2. 验证Service端点
kubectl get endpoints vllm-service -n vllm-inference
NAME            ENDPOINTS                             AGE
vllm-service    10.244.1.12:8000,10.244.2.18:8000,10.244.3.21:8000   5m

# 3. 从集群内部访问Service
kubectl run test-pod --rm -it --image=curlimages/curl --restart=Never -- curl -s http://vllm-service.vllm-inference/health
{"status": "ok"}

# 4. 验证Ingress路由
curl -k https://vllm.example.com/health
{"status": "ok"}

# 5. 测试推理接口
curl -k https://vllm.example.com/v1/completions 
  -H "Content-Type: application/json" 
  -d '{
    "model": "llama-2-7b",
    "prompt": "Hello, my name is",
    "max_tokens": 10
  }'

# 6. 验证负载均衡
for i in {1..10}; do
  curl -k https://vllm.example.com/health -H "X-Client-ID: test-$i"
done

# 检查Ingress日志
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller -f | grep vllm

 

说明

步骤1验证Pod的/health端点正常响应

步骤2确认Service已正确关联到3个Pod端点

步骤3从集群内部测试Service路由

步骤4从外部测试Ingress路由(需要DNS解析生效)

步骤5测试实际推理功能

步骤6验证负载均衡是否生效

如果Ingress无法访问,检查DNS解析是否正确,Ingress Controller是否正常运行,防火墙规则是否放行80/443端口。

三、示例代码和配置

3.1 完整配置示例

3.1.1 vLLM Deployment完整YAML

生产环境推荐的完整Deployment配置,包含资源限制、健康检查、优雅关闭等关键配置。

 

# 文件路径:vllm-deployment-production.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:vllm-inference
namespace:vllm-inference
labels:
    app:vllm-inference
    version:v0.6.3
    env:production
spec:
replicas:4
revisionHistoryLimit:10
strategy:
    type:RollingUpdate
    rollingUpdate:
      maxSurge:25%
      maxUnavailable:0
selector:
    matchLabels:
      app:vllm-inference
template:
    metadata:
      labels:
        app:vllm-inference
        version:v0.6.3
        env:production
      annotations:
        prometheus.io/scrape:"true"
        prometheus.io/port:"8000"
        prometheus.io/path:"/metrics"
    spec:
      nodeSelector:
        accelerator:nvidia-tesla-gpu
        gpu-type:a100
      tolerations:
      -key:nvidia.com/gpu
        operator:Exists
        effect:NoSchedule
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          -weight:100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                -key:app
                  operator:In
                  values:
                  -vllm-inference
              topologyKey:kubernetes.io/hostname
      containers:
      -name:vllm
        image:registry.example.com/vllm:v0.6.3
        imagePullPolicy:IfNotPresent
        ports:
        -containerPort:8000
          name:http
          protocol:TCP
        env:
        -name:MODEL_NAME
          value:"meta-llama/Llama-2-7b-chat-hf"
        -name:TOKENIZER_MODE
          value:"auto"
        -name:MAX_MODEL_LEN
          value:"4096"
        -name:GPU_MEMORY_UTILIZATION
          value:"0.95"
        -name:DTYPE
          value:"float16"
        -name:SERVED_MODEL_NAME
          value:"llama-2-7b"
        -name:TENSOR_PARALLEL_SIZE
          value:"1"
        -name:BLOCK_SIZE
          value:"16"
        -name:SWAP_SPACE
          value:"4"
        -name:MAX_NUM_SEQS
          value:"256"
        -name:MAX_NUM_BATCHED_TOKENS
          value:"8192"
        -name:DISABLE_LOG_STATS
          value:"false"
        -name:VLLM_ATTENTION_BACKEND
          value:"flash_attn"
        resources:
          requests:
            cpu:"4"
            memory:"16Gi"
            nvidia.com/gpu:"1"
          limits:
            cpu:"8"
            memory:"32Gi"
            nvidia.com/gpu:"1"
            ephemeral-storage:"50Gi"
        volumeMounts:
        -name:cache
          mountPath:/root/.cache
        -name:shm
          mountPath:/dev/shm
        livenessProbe:
          httpGet:
            path:/health
            port:8000
          initialDelaySeconds:90
          periodSeconds:30
          timeoutSeconds:10
          failureThreshold:3
          successThreshold:1
        readinessProbe:
          httpGet:
            path:/health
            port:8000
          initialDelaySeconds:60
          periodSeconds:10
          timeoutSeconds:5
          failureThreshold:3
          successThreshold:1
        startupProbe:
          httpGet:
            path:/health
            port:8000
          initialDelaySeconds:10
          periodSeconds:10
          timeoutSeconds:5
          failureThreshold:30
        lifecycle:
          preStop:
            exec:
              command:
              -/bin/sh
              --c
              -sleep15&&curl-XPOSThttp://localhost:8000/shutdown||true
      volumes:
      -name:cache
        emptyDir:
          sizeLimit:20Gi
      -name:shm
        emptyDir:
          medium:Memory
          sizeLimit:8Gi
      terminationGracePeriodSeconds:30
      dnsPolicy:ClusterFirst
      restartPolicy:Always

 

参数说明

replicas: 4:生产环境建议至少4个副本,保证高可用

maxSurge: 25%:滚动更新时最多同时增加25%的新Pod

maxUnavailable: 0:滚动更新时零停机,确保服务连续性

podAntiAffinity:Pod反亲和性,尽量分散到不同节点,提高容灾能力

GPU_MEMORY_UTILIZATION: 0.95:GPU显存利用率95%,充分利用显存资源

MAX_NUM_SEQS: 256:最大并发请求数,根据GPU显存和模型大小调整

MAX_NUM_BATCHED_TOKENS: 8192:批量处理token数,影响吞吐量

SWAP_SPACE: 4:KV Cache交换空间4GB,用于处理超长序列

VLLM_ATTENTION_BACKEND: flash_attn:使用Flash Attention加速推理

startupProbe:启动探针,给模型加载更长时间,避免频繁重启

terminationGracePeriodSeconds: 30:优雅终止时间30s,让Pod有足够时间完成正在处理的请求

/dev/shm:共享内存卷,用于PyTorch多进程通信

3.1.2 Service配置YAML(ClusterIP)

ClusterIP Service配置,包含会话保持和端口映射。

 

# 文件路径:vllm-service-clusterip.yaml
apiVersion:v1
kind:Service
metadata:
name:vllm-service
namespace:vllm-inference
labels:
    app:vllm-inference
    env:production
annotations:
    service.beta.kubernetes.io/aws-load-balancer-type:"nlb"
spec:
type:ClusterIP
sessionAffinity:ClientIP
sessionAffinityConfig:
    clientIP:
      timeoutSeconds:600
ports:
-port:80
    targetPort:8000
    protocol:TCP
    name:http
-port:8080
    targetPort:8000
    protocol:TCP
    name:metrics
selector:
    app:vllm-inference

 

参数说明

sessionAffinity: ClientIP:基于客户端IP的会话保持

sessionAffinityConfig.clientIP.timeoutSeconds: 600:会话保持10分钟

ports[0].port: 80:HTTP服务端口

ports[1].port: 8080:Prometheus监控指标端口

3.1.3 Nginx Ingress配置YAML

Nginx Ingress生产环境配置,包含TLS、限流、会话保持等关键配置。

 

# 文件路径:vllm-ingress-nginx.yaml
apiVersion:networking.k8s.io/v1
kind:Ingress
metadata:
name:vllm-ingress
namespace:vllm-inference
labels:
    app:vllm-inference
    env:production
annotations:
    nginx.ingress.kubernetes.io/rewrite-target:/
    nginx.ingress.kubernetes.io/ssl-redirect:"true"
    nginx.ingress.kubernetes.io/force-ssl-redirect:"true"
    nginx.ingress.kubernetes.io/backend-protocol:"HTTP"
    nginx.ingress.kubernetes.io/proxy-body-size:"20m"
    nginx.ingress.kubernetes.io/proxy-buffer-size:"64k"
    nginx.ingress.kubernetes.io/proxy-buffers-number:"8"
    nginx.ingress.kubernetes.io/proxy-connect-timeout:"900"
    nginx.ingress.kubernetes.io/proxy-send-timeout:"900"
    nginx.ingress.kubernetes.io/proxy-read-timeout:"900"
    nginx.ingress.kubernetes.io/proxy-next-upstream:"error timeout http_502 http_503 http_504"
    nginx.ingress.kubernetes.io/proxy-next-upstream-tries:"3"
    nginx.ingress.kubernetes.io/proxy-request-buffering:"off"
    nginx.ingress.kubernetes.io/client-body-buffer-size:"20m"
    nginx.ingress.kubernetes.io/limit-connections:"200"
    nginx.ingress.kubernetes.io/limit-rps:"500"
    nginx.ingress.kubernetes.io/limit-burst:"100"
    nginx.ingress.kubernetes.io/limit-rate-after:"1048576"
    nginx.ingress.kubernetes.io/limit-rate:"1048576"
    nginx.ingress.kubernetes.io/affinity:"cookie"
    nginx.ingress.kubernetes.io/session-cookie-name:"VLLM_ROUTE"
    nginx.ingress.kubernetes.io/session-cookie-expires:"7200"
    nginx.ingress.kubernetes.io/session-cookie-max-age:"7200"
    nginx.ingress.kubernetes.io/session-cookie-samesite:"Lax"
    nginx.ingress.kubernetes.io/session-cookie-path:"/"
    nginx.ingress.kubernetes.io/enable-modsecurity:"false"
    nginx.ingress.kubernetes.io/enable-opentracing:"false"
    nginx.ingress.kubernetes.io/use-forwarded-headers:"true"
    nginx.ingress.kubernetes.io/configuration-snippet:|
      add_header X-Ingress-Host $host always;
      add_header X-Ingress-Path $request_uri always;
      add_header X-Request-ID $request_id always;
    cert-manager.io/cluster-issuer:"letsencrypt-prod"
    cert-manager.io/common-name:"vllm.example.com"
spec:
ingressClassName:nginx
tls:
-hosts:
    -vllm.example.com
    secretName:vllm-tls-cert
rules:
-host:vllm.example.com
    http:
      paths:
      -path:/
        pathType:Prefix
        backend:
          service:
            name:vllm-service
            port:
              number:80
      -path:/metrics
        pathType:Prefix
        backend:
          service:
            name:vllm-service
            port:
              number:8080

 

参数说明

proxy-body-size: 20m:最大请求体20MB,支持长文本推理

proxy-buffer-size: 64k、proxy-buffers-number: 8:增大缓冲区,提高吞吐量

proxy-next-upstream-tries: 3:失败后最多重试3次

limit-rps: 500:每秒请求数限制500,防止过载

limit-rate-after: 1048576、limit-rate: 1048576:响应限速1MB/s

session-cookie-name: VLLM_ROUTE:自定义Cookie名称

session-cookie-max-age: 7200:Cookie有效期2小时

configuration-snippet:自定义响应头,方便追踪请求

3.1.4 Traefik Ingress配置对比

Traefik 3.0原生支持Service Mesh,配置更简洁,动态更新无需重启。

 

# 文件路径:vllm-ingress-traefik.yaml
apiVersion:traefik.containo.us/v1alpha1
kind:IngressRoute
metadata:
name:vllm-ingress
namespace:vllm-inference
spec:
entryPoints:
    -websecure
routes:
-match:Host(`vllm.example.com`)
    kind:Rule
    services:
    -name:vllm-service
      port:80
      sticky:
        cookie:
          name:VLLM_ROUTE
          secure:true
          httpOnly:true
          sameSite:Lax
      healthCheck:
        path:/health
        interval:10s
        timeout:2s
      responseForwarding:
        flushInterval:1ms
tls:
    certResolver:letsencrypt
    domains:
    -main:vllm.example.com
---
apiVersion:traefik.containo.us/v1alpha1
kind:Middleware
metadata:
name:vllm-rate-limit
namespace:vllm-inference
spec:
rateLimit:
    average:500
    burst:100
    period:1s
---
apiVersion:traefik.containo.us/v1alpha1
kind:Middleware
metadata:
name:vllm-timeout
namespace:vllm-inference
spec:
timeout:
    forwardRequestTimeout:900s
    idleTimeout:900s
---
apiVersion:networking.k8s.io/v1
kind:Ingress
metadata:
name:vllm-ingress-k8s
namespace:vllm-inference
annotations:
    traefik.ingress.kubernetes.io/router.middlewares:vllm-inference-vllm-rate-limit@kubernetescrd,vllm-inference-vllm-timeout@kubernetescrd
spec:
ingressClassName:traefik
tls:
-hosts:
    -vllm.example.com
rules:
-host:vllm.example.com
    http:
      paths:
      -path:/
        pathType:Prefix
        backend:
          service:
            name:vllm-service
            port:
              number:80

 

对比说明

Traefik使用IngressRoute和Middleware,配置更模块化

healthCheck:Traefik原生支持健康检查,无需额外配置

responseForwarding.flushInterval: 1ms:实时转发响应,减少延迟

idleTimeout: 900s:空闲超时时间,适应长连接

Traefik配置变更后自动热加载,无需重启Ingress Controller

实测下来,Traefik在vLLM推理场景下的延迟比Nginx高约3-5%,但配置管理更友好,适合频繁变更的环境。

3.1.5 服务健康检查脚本

Shell脚本用于批量检查vLLM服务的健康状态。

 

#!/bin/bash
# 脚本功能:批量检查vLLM推理服务健康状态
# 文件名:check-vllm-health.sh

NAMESPACE="vllm-inference"
INGRESS_HOST="vllm.example.com"
TIMEOUT=10

echo"=== vLLM服务健康检查 ==="
echo"命名空间: $NAMESPACE"
echo"Ingress主机: $INGRESS_HOST"
echo

# 1. 检查Pod状态
echo"1. 检查Pod状态"
kubectl get pods -n $NAMESPACE -l app=vllm-inference
echo

# 2. 检查Service端点
echo"2. 检查Service端点"
kubectl get endpoints vllm-service -n $NAMESPACE
echo

# 3. 检查Ingress状态
echo"3. 检查Ingress状态"
kubectl get ingress vllm-ingress -n $NAMESPACE
echo

# 4. 检查Pod资源使用
echo"4. 检查Pod资源使用"
kubectl top pods -n $NAMESPACE -l app=vllM-inference
echo

# 5. 测试Pod健康端点
echo"5. 测试Pod健康端点"
PODS=$(kubectl get pods -n $NAMESPACE -l app=vllm-inference -o jsonpath='{.items[*].metadata.name}')
for pod in$PODS; do
    echo -n "Pod $pod: "
    kubectl exec -n $NAMESPACE$pod -- curl -s --connect-timeout $TIMEOUT http://localhost:8000/health
    echo
done
echo

# 6. 测试Service
echo"6. 测试Service"
kubectl run test-pod --rm -it --image=curlimages/curl --restart=Never 
    -- curl -s --connect-timeout $TIMEOUT http://vllm-service.$NAMESPACE/health
echo

# 7. 测试Ingress
echo"7. 测试Ingress"
curl -s --connect-timeout $TIMEOUT -k https://$INGRESS_HOST/health
echo

# 8. 测试推理接口
echo"8. 测试推理接口"
curl -s --connect-timeout $TIMEOUT -k https://$INGRESS_HOST/v1/completions 
    -H "Content-Type: application/json" 
    -d '{
        "model": "llama-2-7b",
        "prompt": "Hello",
        "max_tokens": 5
    }' | jq -r '.choices[0].text'
echo

echo"=== 检查完成 ==="

 

使用方法

 

chmod +x check-vllm-health.sh
./check-vllm-health.sh

 

3.2 实际应用案例

案例一:生产环境vLLM推理服务完整部署流程

某电商公司部署vLLM推理服务,用于商品描述生成和智能客服。实际部署过程中遇到多个问题,最终优化方案如下。

场景描述

模型:Llama-2-7B-Chat,量化为FP16

集群:K8s 1.30,5个GPU节点,每个节点8张A100 80GB

预期QPS:500-800

延迟要求:P95 < 500ms

实现步骤

准备模型镜像

 

# Dockerfile
FROM vllm/vllm-openai:v0.6.3

# 下载模型
RUN python -c "from transformers import AutoTokenizer; AutoTokenizer.from_pretrained('meta-llama/Llama-2-7b-chat-hf')"

# 复制自定义推理脚本
COPY inference.py /app/

# 设置工作目录
WORKDIR /app

 

优化Deployment配置

 

# 生产配置关键参数
env:
-name:MAX_NUM_SEQS
value:"512"# 提高并发
-name:MAX_NUM_BATCHED_TOKENS
value:"16384"# 提高吞吐量
-name:SWAP_SPACE
value:"8"# 增大交换空间
-name:ENABLE_PREFIX_CACHING
value:"true"# 启用前缀缓存

resources:
limits:
    nvidia.com/gpu:"2"# 单Pod占用2个GPU

 

配置Service会话保持

 

sessionAffinity: ClientIP
sessionAffinityConfig:
  clientIP:
    timeoutSeconds: 1800  # 30分钟,保证对话上下文连续

 

Ingress限流配置

 

nginx.ingress.kubernetes.io/limit-rps: "1000"  # 提高限流阈值
nginx.ingress.kubernetes.io/limit-burst: "200"

 

运行结果

 

QPS: 750 (目标达成)
P95延迟: 420ms (目标达成)
GPU利用率: 85-90%
KV Cache命中率: 65%
错误率: 0.02%

 

踩坑记录

坑1:初始设置MAX_NUM_SEQS=128,QPS只能达到300,提高到512后QPS翻倍

坑2:会话保持时间设为1小时,导致新Pod在滚动更新时长时间接收不到流量,改为30分钟后解决

坑3:Ingress限流设为200,高峰期大量请求被拒绝,改为1000后缓解

坑4:单GPU部署3个实例导致OOM,改为单Pod占用2个GPU后稳定

案例二:多副本Ingress负载均衡配置与性能测试

某AI公司部署vLLM推理服务进行A/B测试,同时运行3个不同版本的模型。通过Ingress的路由规则实现流量分配。

场景描述

模型版本:v1.0、v2.0、v3.0(实验版)

每个版本部署2个副本

流量分配:v1.0占60%、v2.0占30%、v3.0占10%

实现步骤

部署3个版本的Deployment

 

# v1.0
kubectl apply -f vllm-v1-deployment.yaml
# v2.0
kubectl apply -f vllm-v2-deployment.yaml
# v3.0
kubectl apply -f vllm-v3-deployment.yaml

 

创建3个Service

 

# v1.0 Service
apiVersion:v1
kind:Service
metadata:
name:vllm-v1-service
namespace:vllm-inference
spec:
selector:
    app:vllm-inference
    version:v1.0
ports:
-port:80
    targetPort:8000
---
# v2.0 Service
apiVersion:v1
kind:Service
metadata:
name:vllm-v2-service
namespace:vllm-inference
spec:
selector:
    app:vllm-inference
    version:v2.0
ports:
-port:80
    targetPort:8000
---
# v3.0 Service
apiVersion:v1
kind:Service
metadata:
name:vllm-v3-service
namespace:vllm-inference
spec:
selector:
    app:vllm-inference
    version:v3.0
ports:
-port:80
    targetPort:8000

 

配置Ingress流量分配

 

apiVersion: networking.k8s.io/v1
kind:Ingress
metadata:
name:vllm-ab-testing
namespace:vllm-inference
annotations:
    nginx.ingress.kubernetes.io/canary:"true"
    nginx.ingress.kubernetes.io/canary-weight:"10"
spec:
ingressClassName:nginx
rules:
-host:vllm.example.com
    http:
      paths:
      -path:/
        pathType:Prefix
        backend:
          service:
            name:vllm-v3-service
            port:
              number:80
---
apiVersion:networking.k8s.io/v1
kind:Ingress
metadata:
name:vllm-main-ingress
namespace:vllm-inference
annotations:
    nginx.ingress.kubernetes.io/canary:"true"
    nginx.ingress.kubernetes.io/canary-weight:"30"
spec:
ingressClassName:nginx
rules:
-host:vllm.example.com
    http:
      paths:
      -path:/
        pathType:Prefix
        backend:
          service:
            name:vllm-v2-service
            port:
              number:80
---
apiVersion:networking.k8s.io/v1
kind:Ingress
metadata:
name:vllm-stable-ingress
namespace:vllm-inference
spec:
ingressClassName:nginx
rules:
-host:vllm.example.com
    http:
      paths:
      -path:/
        pathType:Prefix
        backend:
          service:
            name:vllm-v1-service
            port:
              number:80

 

性能测试脚本

 

#!/bin/bash
# 性能测试脚本
HOST="https://vllm.example.com"
CONCURRENT=50
REQUESTS=10000

echo"开始压力测试..."
echo"并发数: $CONCURRENT"
echo"总请求数: $REQUESTS"

ab -n $REQUESTS -c $CONCURRENT 
   -T "application/json" 
   -p payload.json 
   -H "Accept: application/json" 
   $HOST/v1/completions

 

测试结果

 

Concurrency Level:      50
Time taken for tests:   20.3s
Requests per second:    492.6 [#/sec] (mean)
Time per request:       101.5ms [mean] (mean)
Time per request:       2.0ms [mean] (mean, across all concurrent requests)
Transfer rate:          123.4Kbytes/sec received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        5   10   2.1     10      15
Processing:    85   90   5.3     89     150
Waiting:       80   85   4.8     84     140
Total:         90  100   5.9     99     165

Percentage of the requests served within a certain time (ms)
  50%     99
  66%    101
  75%    103
  80%    105
  90%    110
  95%    118
  98%    130
  99%    140
 100%    165 (longest request)

 

负载分布验证

 

# 查看各版本请求分布
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller | grep "vllm" | grep "v1.0" | wc -l
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller | grep "vllm" | grep "v2.0" | wc -l
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller | grep "vllm" | grep "v3.0" | wc -l

# 预期结果
# v1.0: 6000请求 (60%)
# v2.0: 3000请求 (30%)
# v3.0: 1000请求 (10%)

 

优化建议

如果v3.0性能优于v1.0,逐步调整流量分配:v3.0 → 50%、v2.0 → 30%、v1.0 → 20%

使用Canary Deployment自动化流量分配,通过Prometheus监控各版本的错误率和延迟

生产环境建议使用Feature Flag(如LaunchDarkly)进行灰度,避免配置多个Ingress

四、最佳实践和注意事项

4.1 最佳实践

4.1.1 性能优化

连接复用

启用HTTP/2和Keep-Alive连接复用,减少TCP握手开销。Nginx Ingress默认支持HTTP/2,只需在Service层面启用。

 

# Service配置
apiVersion:v1
kind:Service
metadata:
name:vllm-service
annotations:
    service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout:"3600"
spec:
type:ClusterIP
sessionAffinity:ClientIP

 

实测效果:HTTP/2连接复用后,延迟降低15-20%,CPU占用率降低10%。

请求超时优化

vLLM推理请求耗时较长,需要合理设置超时时间。根据模型大小和输出长度调整:

 

# 7B模型,输出512 tokens:建议300s超时
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"

# 70B模型,输出2048 tokens:建议900s超时
nginx.ingress.kubernetes.io/proxy-read-timeout: "900"

 

注意:超时时间过短会导致长推理请求失败,过长时间会占用连接池资源。建议根据实际P99延迟设置超时时间为P99的1.5倍。

GPU显存优化

通过调整GPU_MEMORY_UTILIZATION和SWAP_SPACE参数,平衡吞吐量和显存占用。

 

env:
-name:GPU_MEMORY_UTILIZATION
value:"0.90"# 90%显存利用率
-name:SWAP_SPACE
value:"4"     # 4GB交换空间
-name:MAX_MODEL_LEN
value:"4096"# 最大序列长度

 

优化策略:

高QPS场景:GPU_MEMORY_UTILIZATION=0.95、SWAP_SPACE=8,最大化吞吐量

低延迟场景:GPU_MEMORY_UTILIZATION=0.85、SWAP_SPACE=2,减少KV Cache交换

长文本场景:MAX_MODEL_LEN=8192,SWAP_SPACE=16,支持超长序列

批量处理优化

通过MAX_NUM_BATCHED_TOKENS和MAX_NUM_SEQS参数优化批量处理性能。

 

env:
- name: MAX_NUM_BATCHED_TOKENS
  value: "16384"  # 批量token数
- name: MAX_NUM_SEQS
  value: "512"    # 最大并发请求数

 

实测数据:

MAX_NUM_BATCHED_TOKENS QPS P95延迟 GPU利用率
4096 300 350ms 60%
8192 450 380ms 75%
16384 600 420ms 85%
32768 620 480ms 90%

建议:根据GPU显存大小和模型选择合适的批量大小,超过16384后边际收益递减。

4.1.2 安全加固

TLS配置

生产环境必须启用TLS,推荐使用TLS 1.3。

 

# Cert Manager自动签发证书
annotations:
cert-manager.io/cluster-issuer:"letsencrypt-prod"
spec:
ingressClassName:nginx
tls:
-hosts:
    -vllm.example.com
    secretName:vllm-tls-cert

 

强制TLS 1.3(修改Nginx Ingress ConfigMap):

 

apiVersion: v1
kind:ConfigMap
metadata:
name:nginx-configuration
namespace:ingress-nginx
data:
ssl-protocols:"TLSv1.3"
ssl-ciphers:"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384"

 

认证配置

使用API Key或JWT Token进行身份认证,防止未授权访问。

 

# Ingress配置API Key认证
annotations:
nginx.ingress.kubernetes.io/auth-type:"basic"
nginx.ingress.kubernetes.io/auth-secret:"vllm-basic-auth"
nginx.ingress.kubernetes.io/auth-realm:"vLLM Inference API"

# 或者使用JWT Token认证
annotations:
nginx.ingress.kubernetes.io/auth-url:"https://auth.example.com/validate"
nginx.ingress.kubernetes.io/auth-response-headers:"Authorization"

 

创建Basic Auth Secret:

 

htpasswd -c auth admin
kubectl create secret generic vllm-basic-auth --from-file=auth -n vllm-inference

 

限流配置

防止过载和DDoS攻击,设置合理的限流阈值。

 

annotations:
  # 连接数限制
nginx.ingress.kubernetes.io/limit-connections:"200"
# 每秒请求数限制
nginx.ingress.kubernetes.io/limit-rps:"500"
# 突发流量缓冲
nginx.ingress.kubernetes.io/limit-burst:"100"
# 响应限速
nginx.ingress.kubernetes.io/limit-rate:"1048576"
nginx.ingress.kubernetes.io/limit-rate-after:"1048576"

 

实际案例:某生产环境设置limit-rps=100,导致高峰期大量429错误,调整为500后错误率从15%降至2%。

4.1.3 高可用配置

多副本部署

生产环境建议至少4个副本,避免单点故障。

 

spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0

 

高可用策略:

核心推理服务:4-8个副本

A/B测试服务:每个版本2-4个副本

临时推理任务:2个副本即可

Pod反亲和性

确保Pod分布在不同节点,提高容灾能力。

 

affinity:
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    -weight:100
      podAffinityTerm:
        labelSelector:
          matchExpressions:
          -key:app
            operator:In
            values:
            -vllm-inference
        topologyKey:kubernetes.io/hostname

 

实际效果:单节点故障时,服务可用性从70%提升至90%。

故障转移

配置健康检查和自动重启,确保故障Pod快速恢复。

 

livenessProbe:
  httpGet:
    path:/health
    port:8000
initialDelaySeconds:90
periodSeconds:30
timeoutSeconds:10
failureThreshold:3
readinessProbe:
httpGet:
    path:/health
    port:8000
initialDelaySeconds:60
periodSeconds:10
timeoutSeconds:5
failureThreshold:3

 

故障转移时间计算:

 

故障检测时间 = initialDelaySeconds + periodSeconds * failureThreshold
= 60s + 10s * 3 = 90s

Pod重启时间 = 镜像拉取 + 模型加载
= 30s + 60s = 90s

总故障转移时间 = 180s (3分钟)

 

优化建议:使用StatefulSet + Volume快照,故障转移时间可缩短至60s。

4.2 注意事项

4.2.1 配置注意事项

 警告:GPU资源配置错误会导致OOM或性能剧烈波动,务必仔细核对配置参数。

注意事项一:GPU显存分配

避免单个Pod占用过多显存导致OOM。实测A100 80GB分配方案:

模型大小 单GPU Pod数 显存分配 KV Cache
7B (FP16) 3-4 20GB-27GB 4GB-8GB
13B (FP16) 2 40GB 8GB-12GB
70B (FP16) 1 72GB 6GB

踩坑记录:某项目尝试单GPU部署2个70B模型,每个分配40GB显存,结果KV Cache不足导致频繁OOM。

注意事项二:会话保持时间

会话保持时间过长会导致新Pod无法接收流量,影响滚动升级。

 

# 不推荐
nginx.ingress.kubernetes.io/session-cookie-expires: "86400"  # 24小时

# 推荐
nginx.ingress.kubernetes.io/session-cookie-expires: "7200"   # 2小时

 

实际案例:某生产环境设置会话保持24小时,滚动升级时新Pod1小时后才接收流量,导致升级时间过长。

注意事项三:健康检查间隔

健康检查间隔过长会导致故障Pod继续接收流量。

 

# 不推荐
livenessProbe:
periodSeconds:120# 太长
failureThreshold:5

# 推荐
livenessProbe:
periodSeconds:30   # 合理
failureThreshold:3

 

故障检测时间对比:

不推荐:120s * 5 = 600s (10分钟)

推荐:30s * 3 = 90s (1.5分钟)

4.2.2 常见错误

错误现象 原因分析 解决方案
Pod状态为ImagePullBackOff 镜像拉取失败,可能是镜像名称错误或私有仓库认证失败 检查镜像名称,配置ImagePullSecret
Pod状态为CrashLoopBackOff vLLM启动失败,可能是显存不足或模型加载错误 查看Pod日志,检查GPU显存分配
Ingress返回502 后端Pod未就绪或健康检查失败 检查Pod Ready状态,验证健康检查端点
Ingress返回504 请求超时,推理耗时过长 增加proxy-read-timeout配置
请求负载不均 会话保持时间过长或轮询算法失效 缩短会话保持时间,检查负载均衡算法
GPU利用率低 批量大小设置过小或并发请求数不足 增加MAX_NUM_BATCHED_TOKENS和MAX_NUM_SEQS
KV Cache命中率低 会话保持失效或请求过于分散 启用前缀缓存,优化会话保持策略
推理延迟波动大 资源争抢或GPU调度问题 配置Pod反亲和性,确保Pod独占GPU

4.2.3 兼容性问题

版本兼容

不同K8s版本对GPU资源调度支持不同:

K8s版本 GPU调度支持 HPA支持 建议配置
< 1.23 需要Device Plugin HPA v1 不推荐
1.23-1.28 Device Plugin HPA v2 需配置nvidia-device-plugin
1.29+ 原生支持 HPA v2 推荐

平台兼容

不同云平台的Ingress Controller差异:

平台 Ingress Controller 特点
AWS AWS ALB Ingress 自动集成ELB,支持Target Group
GKE GKE Ingress 原生集成Cloud Load Balancing
Azure Azure Application Gateway Ingress 支持WAF
自建 Nginx Ingress 灵活可控,社区活跃

组件依赖

组件版本依赖关系:

 

vLLM 0.6.3 → CUDA 12.2+ → NVIDIA Driver 535+
              → PyTorch 2.1+
              → Python 3.9+

 

常见问题:CUDA 12.2无法在NVIDIA Driver 525上运行,必须升级到535+。

五、故障排查和监控

5.1 故障排查

5.1.1 日志查看

 

# 查看Pod日志
kubectl logs -f vllm-inference-7b8f9c6d6-xj2kp -n vllm-inference

# 查看Ingress Controller日志
kubectl logs -f deployment/ingress-nginx-controller -n ingress-nginx

# 查看Service相关事件
kubectl describe svc vllm-service -n vllm-inference

# 查看Pod事件
kubectl describe pod vllm-inference-7b8f9c6d6-xj2kp -n vllm-inference

# 查看Ingress Controller日志中的vLLM请求
kubectl logs deployment/ingress-nginx-controller -n ingress-nginx | grep vllm

# 使用ElasticSearch/Kibana查询日志(如果有)
kubectl port-forward svc/kibana 5601:5601 -n logging
# 浏览器访问 http://localhost:5601

 

关键日志关键词

OOMKilled:GPU显存不足

CrashLoopBackOff:vLLM启动失败

502 Bad Gateway:后端Pod未就绪

504 Gateway Timeout:请求超时

429 Too Many Requests:限流触发

5.1.2 常见问题排查

问题一:Pod启动失败,状态为CrashLoopBackOff

 

# 诊断命令
kubectl describe pod vllm-inference-xxxx -n vllm-inference
kubectl logs vllm-inference-xxxx -n vllm-inference --previous

# 常见原因
1. GPU显存不足:检查nvidia.com/gpu资源限制
2. 模型加载失败:检查模型路径和权限
3. CUDA版本不匹配:检查NVIDIA Driver版本

 

解决方案

检查GPU显存分配,减少单Pod显存使用

验证模型文件完整性,使用md5sum校验

升级NVIDIA Driver到535+

问题二:Ingress返回502错误

 

# 诊断命令
kubectl get endpoints vllm-service -n vllm-inference
kubectl get pods -n vllm-inference -l app=vllm-inference
kubectl logs deployment/ingress-nginx-controller -n ingress-nginx | grep "502"

# 检查Pod健康状态
kubectl exec -it vllm-inference-xxxx -n vllm-inference -- curl -s http://localhost:8000/health

 

解决方案

确认Pod Ready状态为1/1

验证健康检查端点是否正常响应

检查Service端点是否正确关联Pod

增加健康检查超时时间

问题三:请求负载不均

 

# 诊断命令
# 查看各Pod请求数
for pod in $(kubectl get pods -n vllm-inference -l app=vllm-inference -o jsonpath='{.items[*].metadata.name}'); do
    echo "Pod: $pod"
    kubectl logs $pod -n vllm-inference | grep "completion_requests" | wc -l
done

# 检查Ingress负载均衡算法
kubectl exec -it deployment/ingress-nginx-controller -n ingress-nginx -- cat /etc/nginx/nginx.conf | grep -A 10 "upstream vllm"

 

解决方案

缩短会话保持时间

检查负载均衡算法,使用least_conn或ip_hash

配置Pod反亲和性,确保Pod分布均匀

问题四:Ingress返回504超时

 

# 诊断命令
kubectl get ingress vllm-ingress -n vllm-inference -o yaml | grep timeout
kubectl logs deployment/ingress-nginx-controller -n ingress-nginx | grep "upstream timed out"

# 测试推理接口耗时
time curl -k https://vllm.example.com/v1/completions 
    -H "Content-Type: application/json" 
    -d '{"model": "llama-2-7b", "prompt": "Hello", "max_tokens": 512}'

 

解决方案

增加proxy-read-timeout配置到900s

检查vLLM推理耗时,优化模型和参数

减少输出tokens数量

5.1.3 调试模式

 

# 开启vLLM调试日志
kubectl set env deployment/vllm-inference VLLM_LOGGING_LEVEL=DEBUG -n vllm-inference

# 开启Nginx Ingress调试模式
kubectl set env deployment/ingress-nginx-controller error-log-level=debug -n ingress-nginx

# 查看调试信息
kubectl logs -f vllm-inference-xxxx -n vllm-inference
kubectl logs -f deployment/ingress-nginx-controller -n ingress-nginx

# 使用kubectl port-forward本地调试
kubectl port-forward svc/vllm-service 8000:80 -n vllm-inference
curl http://localhost:8000/health

 

5.2 性能监控

5.2.1 关键指标监控

 

# 使用Prometheus监控指标
kubectl port-forward svc/prometheus 9090:9090 -n monitoring
# 浏览器访问 http://localhost:9090

# GPU利用率查询
sum(rate(nvidia_gpu_utilization_gpu{namespace="vllm-inference"}[5m])) by (pod)

# QPS查询
sum(rate(vllm_request_count_total{namespace="vllm-inference"}[5m]))

# 延迟查询
histogram_quantile(0.95, rate(vllm_request_duration_seconds_bucket{namespace="vllm-inference"}[5m]))

# 错误率查询
sum(rate(vllm_request_errors_total{namespace="vllm-inference"}[5m])) / sum(rate(vllm_request_count_total{namespace="vllm-inference"}[5m]))

# KV Cache命中率查询
rate(vllm_cache_hits_total{namespace="vllm-inference"}[5m]) / rate(vllm_cache_requests_total{namespace="vllm-inference"}[5m])

 

5.2.2 监控指标说明

指标名称 正常范围 告警阈值 说明
QPS 100-800 >1000或<50 过高可能导致过载,过低说明资源浪费
P95延迟 200-500ms >1s 延迟过高影响用户体验
错误率 <1% >5% 错误率过高说明服务异常
GPU利用率 70-90% <50%或>95% 利用率过低浪费资源,过高可能导致OOM
KV Cache命中率 50-70% <30% 命中率低说明会话保持失效
内存使用 <80% >90% 内存不足可能导致OOM
CPU使用 30-70% >90% CPU过高说明计算密集
连接数 100-500 >1000 连接数过高说明连接泄漏

5.2.3 Prometheus监控规则示例

 

# 文件路径:vllm-prometheus-rules.yaml
apiVersion:monitoring.coreos.com/v1
kind:PrometheusRule
metadata:
name:vllm-alerts
namespace:vllm-inference
labels:
    app:vllm-inference
spec:
groups:
-name:vllm.rules
    rules:
    # QPS告警
    -alert:VLLMHighQPS
      expr:sum(rate(vllm_request_count_total{namespace="vllm-inference"}[5m]))>1000
      for:5m
      labels:
        severity:warning
      annotations:
        summary:"vLLM推理服务QPS过高"
        description:"vLLM服务 {{ $labels.pod }} 的QPS为 {{ $value }},超过阈值1000"

    # 延迟告警
    -alert:VLLMHighLatency
      expr:histogram_quantile(0.95,rate(vllm_request_duration_seconds_bucket{namespace="vllm-inference"}[5m]))>1
      for:5m
      labels:
        severity:warning
      annotations:
        summary:"vLLM推理服务延迟过高"
        description:"vLLM服务 {{ $labels.pod }} 的P95延迟为 {{ $value }}s,超过阈值1s"

    # 错误率告警
    -alert:VLLMHighErrorRate
      expr:sum(rate(vllm_request_errors_total{namespace="vllm-inference"}[5m]))/sum(rate(vllm_request_count_total{namespace="vllm-inference"}[5m]))>0.05
      for:5m
      labels:
        severity:critical
      annotations:
        summary:"vLLM推理服务错误率过高"
        description:"vLLM服务 {{ $labels.pod }} 的错误率为 {{ $value | humanizePercentage }},超过阈值5%"

    # GPU利用率告警
    -alert:VLLMGPUUtilizationHigh
      expr:nvidia_gpu_utilization_gpu{namespace="vllm-inference"}>95
      for:10m
      labels:
        severity:warning
      annotations:
        summary:"vLLM推理服务GPU利用率过高"
        description:"vLLM服务 {{ $labels.pod }} 的GPU利用率为 {{ $value }}%,超过阈值95%"

    # KV Cache命中率告警
    -alert:VLLMCacheHitRateLow
      expr:rate(vllm_cache_hits_total{namespace="vllm-inference"}[5m])/rate(vllm_cache_requests_total{namespace="vllm-inference"}[5m])<0.3
      for:10m
      labels:
        severity:warning
      annotations:
        summary:"vLLM推理服务KV Cache命中率过低"
        description:"vLLM服务 {{ $labels.pod }} 的KV Cache命中率为 {{ $value | humanizePercentage }},低于阈值30%"

    # Pod异常告警
    -alert:VLLMPodNotReady
      expr:kube_pod_status_ready{namespace="vllm-inference",condition="true"}==0
      for:5m
      labels:
        severity:critical
      annotations:
        summary:"vLLM推理服务Pod未就绪"
        description:"vLLM服务 {{ $labels.pod }} 未就绪超过5分钟"

 

5.3 备份与恢复

5.3.1 备份策略

 

#!/bin/bash
# 备份脚本示例
# 文件名:backup-vllm.sh

BACKUP_DIR="/backup/vllm"
NAMESPACE="vllm-inference"
DATE=$(date +%Y%m%d_%H%M%S)

echo"开始备份vLLM推理服务配置..."
echo"备份目录: $BACKUP_DIR/$DATE"

# 创建备份目录
mkdir -p $BACKUP_DIR/$DATE

# 备份Deployment配置
kubectl get deployment vllm-inference -n $NAMESPACE -o yaml > $BACKUP_DIR/$DATE/vllm-deployment.yaml

# 备份Service配置
kubectl get svc vllm-service -n $NAMESPACE -o yaml > $BACKUP_DIR/$DATE/vllm-service.yaml

# 备份Ingress配置
kubectl get ingress vllm-ingress -n $NAMESPACE -o yaml > $BACKUP_DIR/$DATE/vllm-ingress.yaml

# 备份ConfigMap和Secret
kubectl get configmap -n $NAMESPACE -o yaml > $BACKUP_DIR/$DATE/configmaps.yaml
kubectl get secret -n $NAMESPACE -o yaml > $BACKUP_DIR/$DATE/secrets.yaml

# 备份PVC(如果使用持久化存储)
kubectl get pvc -n $NAMESPACE -o yaml > $BACKUP_DIR/$DATE/pvcs.yaml

# 备份Prometheus监控规则
kubectl get prometheusrule -n $NAMESPACE -o yaml > $BACKUP_DIR/$DATE/prometheus-rules.yaml

# 压缩备份
tar -czf $BACKUP_DIR/vllm-backup-$DATE.tar.gz -C $BACKUP_DIR$DATE

# 删除旧备份(保留最近7天)
find $BACKUP_DIR -name "vllm-backup-*.tar.gz" -mtime +7 -delete

echo"备份完成: $BACKUP_DIR/vllm-backup-$DATE.tar.gz"

 

备份策略

配置备份:每天备份一次YAML配置

数据备份:如果使用PVC存储模型,每小时备份一次

监控备份:Prometheus规则变更后立即备份

保留策略:配置保留30天,数据保留7天

5.3.2 恢复流程

 

#!/bin/bash
# 恢复脚本示例
# 文件名:restore-vllm.sh

BACKUP_FILE=$1
NAMESPACE="vllm-inference"

if [ -z "$BACKUP_FILE" ]; then
    echo"用法: $0 "
    exit 1
fi

echo"开始恢复vLLM推理服务配置..."
echo"备份文件: $BACKUP_FILE"

# 1. 停止现有服务
echo"1. 停止现有服务"
kubectl scale deployment vllm-inference -n $NAMESPACE --replicas=0

# 2. 等待Pod停止
echo"2. 等待Pod停止"
kubectl wait --for=delete pod -l app=vllm-inference -n $NAMESPACE --timeout=300s

# 3. 解压备份文件
echo"3. 解压备份文件"
TEMP_DIR=$(mktemp -d)
tar -xzf $BACKUP_FILE -C $TEMP_DIR

# 4. 恢复ConfigMap和Secret
echo"4. 恢复ConfigMap和Secret"
BACKUP_DATE=$(ls $TEMP_DIR | head -1)
kubectl apply -f $TEMP_DIR/$BACKUP_DATE/configmaps.yaml
kubectl apply -f $TEMP_DIR/$BACKUP_DATE/secrets.yaml

# 5. 恢复Service和Ingress
echo"5. 恢复Service和Ingress"
kubectl apply -f $TEMP_DIR/$BACKUP_DATE/vllm-service.yaml
kubectl apply -f $TEMP_DIR/$BACKUP_DATE/vllm-ingress.yaml

# 6. 恢复Prometheus监控规则
echo"6. 恢复Prometheus监控规则"
kubectl apply -f $TEMP_DIR/$BACKUP_DATE/prometheus-rules.yaml

# 7. 恢复PVC(如果需要)
echo"7. 恢复PVC"
kubectl apply -f $TEMP_DIR/$BACKUP_DATE/pvcs.yaml

# 8. 恢复Deployment
echo"8. 恢复Deployment"
kubectl apply -f $TEMP_DIR/$BACKUP_DATE/vllm-deployment.yaml

# 9. 等待Pod就绪
echo"9. 等待Pod就绪"
kubectl rollout status deployment/vllm-inference -n $NAMESPACE --timeout=600s

# 10. 验证服务
echo"10. 验证服务"
kubectl get pods -n $NAMESPACE -l app=vllm-inference
kubectl get svc vllm-service -n $NAMESPACE
kubectl get ingress vllm-ingress -n $NAMESPACE

# 清理临时文件
rm -rf $TEMP_DIR

echo"恢复完成"

 

恢复流程

停止现有服务(可选,保留原配置用于回滚)

解压备份文件

按顺序恢复配置:ConfigMap → Secret → Service → Ingress → PrometheusRule → PVC → Deployment

等待Pod就绪

验证服务可用性

监控服务状态,确认恢复成功

六、总结

6.1 技术要点回顾

 Service类型选择:vLLM推理服务推荐使用ClusterIP类型,配合Ingress Controller实现对外暴露。Headless Service适用于StatefulSet部署,普通Deployment使用ClusterIP更合适。

 会话保持策略:推理服务必须配置Session Affinity,避免同一用户的请求分发到不同Pod导致KV Cache失效。Nginx Ingress支持基于Cookie和IP的会话保持,推荐使用Cookie方式。

 健康检查配置:vLLM加载模型耗时较长,livenessProbe.initialDelaySeconds建议设为90s,readinessProbe.initialDelaySeconds设为60s。startupProbe可以给模型加载更多时间,避免频繁重启。

 GPU资源调度:K8s 1.29+原生支持GPU资源调度,通过nvidia.com/gpu资源限制确保Pod独占GPU。A100 80GB推荐分配:7B模型3-4实例、13B模型2实例、70B模型1实例。

 Ingress超时配置:推理请求耗时较长,proxy-read-timeout建议设置为900s(70B模型)或300s(7B模型)。超时时间过短会导致长推理请求失败。

 负载均衡优化:通过MAX_NUM_BATCHED_TOKENS和MAX_NUM_SEQS参数优化批量处理性能。实测16384批量大小下QPS可达到600,P95延迟420ms。

6.2 进阶学习方向

自动扩缩容(HPA)

基于QPS和GPU利用率自动调整Pod副本数,实现弹性伸缩。

 

apiVersion: autoscaling/v2
kind:HorizontalPodAutoscaler
metadata:
name:vllm-hpa
namespace:vllm-inference
spec:
scaleTargetRef:
    apiVersion:apps/v1
    kind:Deployment
    name:vllm-inference
minReplicas:2
maxReplicas:10
metrics:
-type:Resource
    resource:
      name:cpu
      target:
        type:Utilization
        averageUtilization:70

 

学习资源:K8s HPA文档

实践建议:结合Prometheus自定义指标,基于QPS和延迟进行扩缩容

模型并行(Tensor Parallel)

大模型(70B+)单GPU显存不足时,使用Tensor Parallel将模型切分到多个GPU。

 

env:
- name: TENSOR_PARALLEL_SIZE
  value: "4"  # 使用4个GPU并行
resources:
  limits:
    nvidia.com/gpu: "4"

 

学习资源:vLLM文档 - Tensor Parallel

实践建议:70B模型推荐使用2-4个GPU,通信开销与性能需要平衡

Service Mesh集成

使用Istio或Linkerd实现流量管理和可观测性。

 

apiVersion: networking.istio.io/v1beta1
kind:VirtualService
metadata:
name:vllm-virtualservice
namespace:vllm-inference
spec:
hosts:
-vllm.example.com
gateways:
-vllm-gateway
http:
-match:
    -uri:
        prefix:/
    route:
    -destination:
        host:vllm-service
        subset:v1
      weight:90
    -destination:
        host:vllm-service
        subset:v2
      weight:10

 

学习资源:Istio文档

实践建议:Service Mesh增加约5-10%的延迟,需要权衡可观测性和性能

6.3 参考资料

vLLM官方文档 - vLLM部署和配置指南

Kubernetes官方文档 - Service - K8s Service详解

Nginx Ingress Controller文档 - Nginx Ingress配置参考

Traefik 3.0文档 - Traefik Ingress配置指南

Cert Manager文档 - TLS证书自动管理

Prometheus文档 - 监控和告警配置

K8s GPU Operator - GPU资源调度方案

vLLM GitHub仓库 - vLLM源码和Issue

附录

A. 命令速查表

 

# Pod管理
kubectl get pods -n vllm-inference -l app=vllm-inference              # 查看Pod列表
kubectl describe pod  -n vllm-inference                   # 查看Pod详情
kubectl logs -f  -n vllm-inference                       # 查看Pod日志
kubectl logs  -n vllm-inference --previous               # 查看上一次运行的日志
kubectl exec -it  -n vllm-inference -- /bin/bash         # 进入Pod Shell
kubectl delete pod  -n vllm-inference                     # 删除Pod
kubectl scale deployment vllm-inference -n vllm-inference --replicas=5  # 扩缩容

# Service管理
kubectl get svc -n vllm-inference                                   # 查看Service列表
kubectl describe svc vllm-service -n vllm-inference                 # 查看Service详情
kubectl get endpoints vllm-service -n vllm-inference               # 查看Service端点
kubectl port-forward svc/vllm-service 8000:80 -n vllm-inference     # 端口转发

# Ingress管理
kubectl get ingress -n vllm-inference                              # 查看Ingress列表
kubectl describe ingress vllm-ingress -n vllm-inference            # 查看Ingress详情
kubectl get ingress vllm-ingress -n vllm-inference -o yaml         # 查看Ingress配置

# 资源管理
kubectl top pods -n vllm-inference                                 # 查看Pod资源使用
kubectl describe node                                   # 查看节点资源
kubectl get events -n vllm-inference --sort-by=.metadata.creationTimestamp  # 查看事件

# 配置管理
kubectl get configmap -n vllm-inference                             # 查看ConfigMap
kubectl get secret -n vllm-inference                                # 查看Secret
kubectl get cm -n vllm-inference -o yaml                            # 导出ConfigMap

# 监控和日志
kubectl logs -f deployment/ingress-nginx-controller -n ingress-nginx | grep vllm  # 查看Ingress日志
kubectl get prometheusrule -n vllm-inference                         # 查看Prometheus规则

# 故障排查
kubectl get pods -n vllm-inference --field-selector=status.phase!=Running  # 查看异常Pod
kubectl describe pod  -n vllm-inference | grep -A 20 Events  # 查看Pod事件
kubectl get events --sort-by=.lastTimestamp                         # 查看集群事件

 

B. 配置参数详解

vLLM环境变量

参数 默认值 说明 推荐值
MODEL_NAME - 模型名称 meta-llama/Llama-2-7b-chat-hf
MAX_MODEL_LEN 4096 最大序列长度 4096-8192
GPU_MEMORY_UTILIZATION 0.90 GPU显存利用率 0.90-0.95
DTYPE auto 数据类型 float16
MAX_NUM_SEQS 256 最大并发请求数 256-512
MAX_NUM_BATCHED_TOKENS 8192 批量token数 8192-16384
SWAP_SPACE 4 KV Cache交换空间(GB) 4-16
TENSOR_PARALLEL_SIZE 1 Tensor并行GPU数 1-4
BLOCK_SIZE 16 KV Cache块大小 16
ENABLE_PREFIX_CACHING false 启用前缀缓存 true

Nginx Ingress Annotations

Annotation 说明 推荐值
nginx.ingress.kubernetes.io/rewrite-target 路径重写 /
nginx.ingress.kubernetes.io/ssl-redirect 强制HTTPS true
nginx.ingress.kubernetes.io/proxy-read-timeout 读取超时 900
nginx.ingress.kubernetes.io/limit-rps 每秒请求数限制 500
nginx.ingress.kubernetes.io/limit-burst 突发流量缓冲 100
nginx.ingress.kubernetes.io/affinity 会话保持类型 cookie
nginx.ingress.kubernetes.io/session-cookie-name Cookie名称 VLLM_ROUTE
nginx.ingress.kubernetes.io/session-cookie-expires Cookie有效期(秒) 7200

Service配置参数

参数 说明 推荐值
type Service类型 ClusterIP
sessionAffinity 会话保持 ClientIP
sessionAffinityConfig.clientIP.timeoutSeconds 会话保持时间 600

健康检查参数

参数 说明 推荐值
livenessProbe.initialDelaySeconds 存活探针初始延迟 90
livenessProbe.periodSeconds 存活探针检查间隔 30
livenessProbe.failureThreshold 存活探针失败阈值 3
readinessProbe.initialDelaySeconds 就绪探针初始延迟 60
readinessProbe.periodSeconds 就绪探针检查间隔 10
readinessProbe.failureThreshold 就绪探针失败阈值 3
startupProbe.initialDelaySeconds 启动探针初始延迟 10
startupProbe.periodSeconds 启动探针检查间隔 10
startupProbe.failureThreshold 启动探针失败阈值 30

C. 术语表

术语 英文 解释
ClusterIP Cluster IP K8s默认Service类型,提供集群内部稳定的虚拟IP
Headless Service Headless Service 不分配ClusterIP的Service,直接返回Pod IP列表
Session Affinity 会话保持 将同一客户端的请求分发到同一个后端Pod
Ingress Controller 入口控制器 实现K8s Ingress规范的控制器,如Nginx、Traefik
KV Cache KV缓存 Key-Value缓存,用于存储注意力机制的中间状态
Tensor Parallel 张量并行 将模型参数切分到多个GPU,支持大模型推理
P95延迟 95th Percentile Latency 95%的请求响应时间,衡量服务质量
QPS Queries Per Second 每秒查询数,衡量服务吞吐量
HPA Horizontal Pod Autoscaler K8s水平Pod自动扩缩容
OOM Out Of Memory 内存不足,容器被终止
Rolling Update 滚动更新 逐步替换旧Pod为新Pod的更新策略
Canary Deployment 金丝雀部署 先部署小流量测试,逐步增加新版本流量
Liveness Probe 存活探针 检测Pod是否存活,失败则重启Pod
Readiness Probe 就绪探针 检测Pod是否就绪,失败则不转发流量
Startup Probe 启动探针 检测Pod是否启动成功,成功后才启动其他探针
Pod Anti-Affinity Pod反亲和性 调度策略,避免Pod调度到同一节点
Node Selector 节点选择器 将Pod调度到指定标签的节点
Taints and Tolerations 污点和容忍度 节点污点阻止Pod调度,容忍度允许Pod调度到污点节点
Prometheus Rule Prometheus规则 定义告警和记录规则的K8s资源
Cert Manager 证书管理器 自动签发和更新TLS证书的K8s插件
GPU Memory Utilization GPU显存利用率 GPU显存的使用比例
Batch Size 批次大小 一次推理处理的请求数量
Prefix Caching 前缀缓存 缓存推理过程中的前缀Token,加速重复推理

 

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

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分