Skip to content

vLLM Deployment & Optimization

Versiones compatibles: Kubernetes 1.31, 1.32, 1.33
Última actualización: April 9, 2026

vLLM es el motor de inferencia open-source de alto rendimiento más ampliamente adoptado para modelos de lenguaje de gran tamaño (LLMs). En este capítulo, exploraremos las características y la arquitectura más recientes de vLLM, y aprenderemos cómo desplegarlo y optimizarlo a escala de producción en EKS.

Lab Environment Setup

Para seguir los ejemplos de este documento, necesitarás las siguientes herramientas y entorno:

Required Tools and Resources

  • kubectl v1.31 o superior
  • Helm v3.10 o superior
  • Cluster EKS con GPUs NVIDIA (recomendación mínima: instancia g5.2xlarge)
  • Drivers NVIDIA y NVIDIA Device Plugin instalados
  • Al menos 50 GB de espacio en disco

GPU Node Setup

bash
# Install NVIDIA Device Plugin
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.0/nvidia-device-plugin.yml

# Verify GPU nodes
kubectl get nodes "-o=custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu"

Introduction to vLLM

vLLM es un motor de inferencia de LLM con las siguientes características:

Key Features of vLLM

  1. PagedAttention:

    • Tecnología de gestión de memoria que administra eficientemente la KV cache
    • Inspirada en la gestión de memoria virtual de los sistemas operativos
    • Permite procesar hasta 10 veces más solicitudes concurrentes
  2. Continuous Batching:

    • Agrupa solicitudes dinámicamente para maximizar la utilización de GPU
    • Comienza a procesar nuevas solicitudes inmediatamente al llegar
    • Hasta 2 veces de mejora en throughput
  3. Distributed Inference:

    • Soporta modelos a gran escala mediante paralelización de tensores
    • Sharding del modelo en múltiples GPUs
    • Soporta modelos de más de 175B parámetros
  4. Quantization:

    • Soporta varias precisiones, incluidas INT8 y FP16
    • Reduce el uso de memoria y mejora la velocidad de inferencia
    • Hasta 2 veces de mejora en eficiencia de memoria con pérdida mínima de precisión

Supported Models

vLLM soporta los siguientes modelos:

Model FamilySupported ModelsQuantization Options
LLaMA 3 / 3.1 / 3.2 / 3.31B, 3B, 8B, 70B, 405BFP16, BF16, FP8, INT8, INT4, AWQ, GPTQ
DeepSeek V3 / R17B, 67B, 671B (MoE)FP16, BF16, FP8, AWQ, GPTQ
Qwen 2 / 2.5 / QwQ0.5B ~ 72BFP16, BF16, FP8, INT8, AWQ, GPTQ
Mistral / Mixtral7B, 8x7B, 8x22B, Large 2FP16, BF16, FP8, AWQ, GPTQ
Gemma 2 / 32B, 9B, 27BFP16, BF16, INT8
Phi-3 / Phi-43.8B, 7B, 14BFP16, BF16, INT8, AWQ
Command R / R+35B, 104BFP16, BF16
DBRX132B (MoE)FP16, BF16
StarCoder 23B, 7B, 15BFP16, BF16
Vision Models (VLM)LLaVA, Pixtral, Qwen2-VL, InternVLFP16, BF16
  1. PagedAttention: Mecanismo de atención eficiente en memoria que optimiza el uso de memoria al procesar secuencias largas.
  2. Continuous Batching: Agrupa solicitudes dinámicamente para mejorar el throughput.
  3. Distributed Inference: Distribuye modelos entre múltiples GPUs y nodes para manejar modelos a gran escala.
  4. Quantization: Soporta quantization INT8/INT4 para reducir el uso de memoria y mejorar el throughput.
  5. OpenAI Compatible API: Proporciona una interfaz compatible con la API de OpenAI.

Latest vLLM Features (v0.6+)

vLLM evoluciona rápidamente con nuevas capacidades significativas en versiones recientes:

Speculative Decoding

Usa un modelo borrador más pequeño para generar múltiples tokens candidatos, que el modelo más grande verifica en una sola pasada, mejorando la velocidad de inferencia entre 2 y 3 veces:

bash
python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-3.1-70B-Instruct \
  --speculative-model meta-llama/Llama-3.1-8B-Instruct \
  --num-speculative-tokens 5

Prefix Caching

Reutiliza automáticamente la KV cache entre solicitudes que comparten el mismo system prompt o contexto, reduciendo drásticamente el TTFT (Time to First Token):

bash
--enable-prefix-caching

Chunked Prefill

Divide el prefill de prompts largos en chunks más pequeños intercalados con pasos de decodificación, reduciendo el impacto de las solicitudes de contexto largo sobre la latencia de otras solicitudes:

bash
--enable-chunked-prefill --max-num-batched-tokens 2048

Dynamic LoRA Adapter Loading

Carga y descarga dinámicamente múltiples adaptadores LoRA en tiempo de ejecución, sirviendo muchos modelos personalizados desde un único modelo base:

bash
--enable-lora --max-loras 4 --max-lora-rank 64
python
# Specify LoRA model in API request
response = client.chat.completions.create(
    model="my-custom-lora-adapter",
    messages=[{"role": "user", "content": "Hello!"}]
)

Structured Output

Soporta generación de salida restringida mediante JSON Schema, patrones regex y CFG (Context-Free Grammar) para generar datos estructurados de forma confiable:

python
from openai import OpenAI
client = OpenAI(base_url="http://vllm-service:8000/v1")

response = client.chat.completions.create(
    model="meta-llama/Llama-3.1-8B-Instruct",
    messages=[{"role": "user", "content": "Return user information as JSON"}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "user_info",
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "age": {"type": "integer"},
                    "email": {"type": "string"}
                },
                "required": ["name", "age", "email"]
            }
        }
    }
)

Tool Calling

Soporta Tool/Function Calling compatible con OpenAI para integrarse con flujos de trabajo de agentes:

python
response = client.chat.completions.create(
    model="meta-llama/Llama-3.1-8B-Instruct",
    messages=[{"role": "user", "content": "What's the weather in Seoul?"}],
    tools=[{
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather for a specified location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string", "description": "City name"}
                },
                "required": ["location"]
            }
        }
    }]
)

FP8 Quantization

Soporta FP8 quantization en GPUs Hopper (H100) y Ada Lovelace (L4, L40S), reduciendo a la mitad el uso de memoria mientras mantiene una precisión casi idéntica:

bash
--quantization fp8 --kv-cache-dtype fp8

Vision-Language Model (VLM) Serving

Soporta modelos multimodales que procesan imágenes y texto simultáneamente:

python
response = client.chat.completions.create(
    model="llava-hf/llava-v1.6-mistral-7b-hf",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "Describe this image"},
            {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}
        ]
    }]
)

System Requirements

Requisitos del sistema para desplegar vLLM en EKS:

  1. Hardware:

    • NVIDIA GPU (arquitectura Volta, Turing, Ampere, Hopper)
    • Memoria GPU mínima: varía según el tamaño del modelo
      • Modelo 7B: mínimo 16 GB de memoria GPU
      • Modelo 13B: mínimo 24 GB de memoria GPU
      • Modelo 70B: mínimo 80 GB de memoria GPU (o distribuida entre múltiples GPUs)
  2. Software:

    • CUDA 12.1 o superior (se recomienda CUDA 12.4 para FP8)
    • Python 3.9 o superior
    • PyTorch 2.4.0 o superior
  3. EKS Node Types:

    • p5.48xlarge: 8x NVIDIA H100 GPU, 80 GB cada una (máximo rendimiento)
    • p4d.24xlarge: 8x NVIDIA A100 GPU, 40 GB u 80 GB cada una
    • g6.12xlarge: 4x NVIDIA L4 GPU, 24 GB cada una (rentable)
    • g5.12xlarge: 4x NVIDIA A10G GPU, 24 GB cada una
    • g6e.12xlarge: 4x NVIDIA L40S GPU, 48 GB cada una
    • trn1.32xlarge: 16x AWS Trainium, 32 GB cada una (silicio de AWS)

EKS Infrastructure Configuration

Storage Configuration

vLLM requiere almacenamiento de alto rendimiento, ya que necesita cargar pesos de modelo grandes:

FSx for Lustre Setup

FSx for Lustre es un sistema de archivos paralelo de alto rendimiento adecuado para cargar rápidamente pesos de modelo grandes:

yaml
apiVersion: fsx.aws.k8s.io/v1beta1
kind: Lustre
metadata:
  name: vllm-models
spec:
  deploymentType: SCRATCH_2
  storageCapacity: 1200
  subnetIds:
    - subnet-0123456789abcdef0
  securityGroupIds:
    - sg-0123456789abcdef0
  perUnitStorageThroughput: 200
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fsx-lustre-sc
provisioner: fsx.csi.aws.com
parameters:
  fileSystemId: fs-0123456789abcdef0
  mountName: vllm-models
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: vllm-models-pvc
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: fsx-lustre-sc
  resources:
    requests:
      storage: 1200Gi

Downloading Models from S3

Job para almacenar modelos de Hugging Face en S3 y descargarlos a FSx for Lustre:

yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: model-download
spec:
  template:
    spec:
      containers:
      - name: model-download
        image: huggingface/transformers:latest
        command:
        - python
        - -c
        - |
          from huggingface_hub import snapshot_download
          import os

          model_id = "meta-llama/Llama-3.1-70B-Instruct"
          dest_dir = "/models/llama-3.1-70b"

          os.makedirs(dest_dir, exist_ok=True)
          snapshot_download(repo_id=model_id, local_dir=dest_dir, token=os.environ["HF_TOKEN"])
        env:
        - name: HF_TOKEN
          valueFrom:
            secretKeyRef:
              name: huggingface-token
              key: token
        volumeMounts:
        - name: models-volume
          mountPath: /models
      restartPolicy: Never
      volumes:
      - name: models-volume
        persistentVolumeClaim:
          claimName: vllm-models-pvc

vLLM Deployment

Deployment Architecture

El siguiente diagrama muestra dos arquitecturas principales para desplegar vLLM en EKS:

Single Node Deployment

Deployment que ejecuta vLLM en una sola GPU o en múltiples GPUs en un único node:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-inference
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vllm-inference
  template:
    metadata:
      labels:
        app: vllm-inference
    spec:
      containers:
      - name: vllm-server
        image: vllm/vllm-openai:latest
        command:
        - python
        - -m
        - vllm.entrypoints.openai.api_server
        - --model=/models/llama-3.1-70b
        - --tensor-parallel-size=8
        - --gpu-memory-utilization=0.95
        - --max-num-batched-tokens=16384
        - --enable-prefix-caching
        - --enable-chunked-prefill
        - --port=8000
        ports:
        - containerPort: 8000
        resources:
          limits:
            nvidia.com/gpu: 8
        volumeMounts:
        - name: models-volume
          mountPath: /models
        env:
        - name: CUDA_VISIBLE_DEVICES
          value: "0,1,2,3,4,5,6,7"
      volumes:
      - name: models-volume
        persistentVolumeClaim:
          claimName: vllm-models-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: vllm-inference
spec:
  selector:
    app: vllm-inference
  ports:
  - port: 8000
    targetPort: 8000
  type: LoadBalancer

Multi-Node Distributed Deployment

Método para distribuir modelos grandes entre múltiples nodes:

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: vllm-config
data:
  hostfile: |
    vllm-inference-0 slots=8
    vllm-inference-1 slots=8
  run_server.sh: |
    #!/bin/bash

    RANK=$HOSTNAME
    if [[ $HOSTNAME == "vllm-inference-0" ]]; then
      RANK=0
    elif [[ $HOSTNAME == "vllm-inference-1" ]]; then
      RANK=1
    fi

    python -m vllm.entrypoints.openai.api_server \
      --model=/models/llama-3.1-70b \
      --tensor-parallel-size=16 \
      --pipeline-parallel-size=1 \
      --max-num-batched-tokens=8192 \
      --port=8000 \
      --host=0.0.0.0 \
      --master-addr=vllm-inference-0 \
      --master-port=29500 \
      --rank=$RANK
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: vllm-inference
spec:
  serviceName: "vllm-inference"
  replicas: 2
  selector:
    matchLabels:
      app: vllm-inference
  template:
    metadata:
      labels:
        app: vllm-inference
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - vllm-inference
            topologyKey: kubernetes.io/hostname
      containers:
      - name: vllm-server
        image: vllm/vllm-openai:latest
        command:
        - bash
        - /config/run_server.sh
        ports:
        - containerPort: 8000
        - containerPort: 29500
        resources:
          limits:
            nvidia.com/gpu: 8
        volumeMounts:
        - name: models-volume
          mountPath: /models
        - name: config-volume
          mountPath: /config
        env:
        - name: CUDA_VISIBLE_DEVICES
          value: "0,1,2,3,4,5,6,7"
        - name: NCCL_DEBUG
          value: "INFO"
        - name: NCCL_IB_DISABLE
          value: "0"
        - name: NCCL_IB_GID_INDEX
          value: "3"
        - name: NCCL_NET_GDR_LEVEL
          value: "5"
      volumes:
      - name: models-volume
        persistentVolumeClaim:
          claimName: vllm-models-pvc
      - name: config-volume
        configMap:
          name: vllm-config
          defaultMode: 0755
---
apiVersion: v1
kind: Service
metadata:
  name: vllm-inference
spec:
  selector:
    app: vllm-inference
  ports:
  - port: 8000
    targetPort: 8000
    name: api
  - port: 29500
    targetPort: 29500
    name: nccl
  clusterIP: None
---
apiVersion: v1
kind: Service
metadata:
  name: vllm-inference-lb
spec:
  selector:
    app: vllm-inference
    statefulset.kubernetes.io/pod-name: vllm-inference-0
  ports:
  - port: 8000
    targetPort: 8000
  type: LoadBalancer

Performance Optimization

GPU Memory Optimization

Métodos para optimizar el uso de memoria GPU de vLLM:

  1. GPU Memory Utilization Adjustment:
bash
--gpu-memory-utilization=0.9
  1. Quantization Application:
bash
--quantization awq
  1. Swap Space Utilization:
bash
--swap-space=16

Throughput Optimization

Métodos para optimizar el throughput de vLLM:

  1. Batch Size Adjustment:
bash
--max-num-batched-tokens=8192
  1. KV Cache Optimization:
bash
--block-size=16
  1. Tensor Parallel Processing Adjustment:
bash
--tensor-parallel-size=8

Network Optimization

Métodos para optimizar el rendimiento de red en deployments distribuidos:

  1. EFA (Elastic Fabric Adapter) Utilization:
yaml
resources:
  limits:
    nvidia.com/gpu: 8
    vpc.amazonaws.com/efa: 1
  1. NCCL Settings Optimization:
yaml
env:
- name: NCCL_DEBUG
  value: "INFO"
- name: NCCL_MIN_NCHANNELS
  value: "4"
- name: NCCL_SOCKET_IFNAME
  value: "^lo,docker"
- name: NCCL_ASYNC_ERROR_HANDLING
  value: "1"
  1. Node Placement Optimization:
yaml
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.kubernetes.io/zone
          operator: In
          values:
          - us-west-2a

Monitoring and Logging

Prometheus Metrics

Método para recopilar métricas de Prometheus desde el servidor vLLM:

yaml
apiVersion: v1
kind: Service
metadata:
  name: vllm-metrics
  labels:
    app: vllm-inference
spec:
  selector:
    app: vllm-inference
  ports:
  - port: 8001
    targetPort: 8001
    name: metrics
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: vllm-metrics
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: vllm-inference
  endpoints:
  - port: metrics
    interval: 15s

Log Collection

Método para recopilar logs del servidor vLLM en CloudWatch:

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
  namespace: logging
data:
  fluent.conf: |
    <source>
      @type tail
      path /var/log/containers/vllm-*.log
      pos_file /var/log/fluentd-vllm.log.pos
      tag kubernetes.vllm.*
      read_from_head true
      <parse>
        @type json
        time_format %Y-%m-%dT%H:%M:%S.%NZ
      </parse>
    </source>

    <filter kubernetes.vllm.**>
      @type kubernetes_metadata
      @id filter_kube_metadata
    </filter>

    <match kubernetes.vllm.**>
      @type cloudwatch_logs
      log_group_name /eks/vllm/logs
      log_stream_name_key $.kubernetes.pod_name
      remove_log_stream_name_key true
      auto_create_stream true
      region us-west-2
    </match>

Autoscaling

HPA (Horizontal Pod Autoscaler)

Método para escalar automáticamente servidores vLLM según el volumen de solicitudes:

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: vllm-inference-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: vllm-inference
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Pods
    pods:
      metric:
        name: requests_per_second
      target:
        type: AverageValue
        averageValue: 100

Node Autoscaling with Karpenter

Método para aprovisionar automáticamente GPU nodes:

yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: vllm-gpu
spec:
  template:
    spec:
      requirements:
      - key: node.kubernetes.io/instance-type
        operator: In
        values:
        - p3.16xlarge
        - g5.12xlarge
      - key: karpenter.sh/capacity-type
        operator: In
        values:
        - on-demand
      - key: kubernetes.io/arch
        operator: In
        values:
        - amd64
      - key: vpc.amazonaws.com/efa
        operator: In
        values:
        - "true"
      nodeClassRef:
        name: vllm-gpu-class
  limits:
    nvidia.com/gpu: 32
---
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: vllm-gpu-class
spec:
  subnetSelector:
    karpenter.sh/discovery: vllm-cluster
  securityGroupSelector:
    karpenter.sh/discovery: vllm-cluster
  ttlSecondsAfterEmpty: 30

Security Configuration

Network Policy

Método para restringir el acceso de red a los servidores vLLM:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: vllm-network-policy
spec:
  podSelector:
    matchLabels:
      app: vllm-inference
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: api-gateway
    ports:
    - protocol: TCP
      port: 8000
  - from:
    - podSelector:
        matchLabels:
          app: vllm-inference
    ports:
    - protocol: TCP
      port: 29500
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: vllm-inference
    ports:
    - protocol: TCP
      port: 29500
  - to:
    ports:
    - protocol: TCP
      port: 443

Security Context

Método para configurar el security context del container:

yaml
securityContext:
  runAsUser: 1000
  runAsGroup: 1000
  fsGroup: 1000
  allowPrivilegeEscalation: false
  capabilities:
    drop:
    - ALL

Client Integration

API Gateway

Método para desplegar un API gateway delante de los servidores vLLM:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-gateway
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-gateway
  template:
    metadata:
      labels:
        app: api-gateway
    spec:
      containers:
      - name: api-gateway
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/conf.d
      volumes:
      - name: nginx-config
        configMap:
          name: nginx-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  default.conf: |
    server {
      listen 80;

      location /v1/ {
        proxy_pass http://vllm-inference:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
      }
    }
---
apiVersion: v1
kind: Service
metadata:
  name: api-gateway
spec:
  selector:
    app: api-gateway
  ports:
  - port: 80
    targetPort: 80
  type: LoadBalancer

Client Example

Método para enviar solicitudes al servidor vLLM usando un cliente Python:

python
import requests
import json

url = "http://api-gateway/v1/completions"

payload = {
    "model": "llama-3.1-70b",
    "prompt": "Once upon a time",
    "max_tokens": 100,
    "temperature": 0.7
}

headers = {
    "Content-Type": "application/json"
}

response = requests.post(url, headers=headers, data=json.dumps(payload))

print(response.json())

Best Practices

Resource Management

  1. Consider Memory Overhead:

    • Asigna suficiente memoria CPU además de memoria GPU.
    • Se recomienda asignar aproximadamente el doble del tamaño del modelo en memoria CPU.
  2. CPU Core Allocation:

    • Asigna al menos 4 CPU cores por GPU.
    • Es posible que se necesiten más CPU cores al usar paralelización de tensores.
  3. Node Selection:

    • Selecciona los tipos de node adecuados según el tamaño del modelo.
    • Elige nodes con alto ancho de banda de memoria.

High Availability

  1. Multi-Availability Zone Deployment:

    • Despliega servidores vLLM en múltiples availability zones.
    • Asegura capacidad suficiente en cada availability zone.
  2. Load Balancing:

    • Distribuye solicitudes entre múltiples instancias de servidor vLLM.
    • Configura session affinity para que las solicitudes del mismo usuario se enruten al mismo servidor.
  3. Failure Recovery:

    • Configura health checks para detectar servidores fallidos.
    • Implementa mecanismos de recuperación automática.

Cost Optimization

  1. Utilize Spot Instances:

    • Usa Spot instances para reducir costos.
    • Adecuado para workloads tolerantes a interrupciones.
  2. Model Quantization:

    • Aplica quantization INT8 o INT4 para reducir el uso de memoria.
    • Considera el equilibrio entre precisión y rendimiento.
  3. Autoscaling:

    • Escala automáticamente los servidores según el volumen de solicitudes.
    • Reduce costos escalando los servidores hacia abajo durante períodos de inactividad.

Conclusion

vLLM es el motor de inferencia LLM open-source con desarrollo más activo, y soporta de forma integral características esenciales para producción, incluidas Speculative Decoding, Prefix Caching, carga dinámica de LoRA, Structured Output y Tool Calling. Combinado con una selección adecuada de instancias GPU, almacenamiento de alto rendimiento, optimización de red y auto-scaling en EKS, puedes construir una plataforma de serving de LLM rentable y escalable. Para comparaciones con otros frameworks como SGLang y TGI, consulta el capítulo Inference Frameworks.

References

Quiz

Para comprobar lo que has aprendido en este capítulo, prueba el cuestionario del tema.