

 **Ayude a mejorar esta página** 

Para contribuir a esta guía del usuario, elija el enlace **Edit this page on GitHub** que se encuentra en el panel derecho de cada página.

# Modelos de carga y servicio en Amazon EKS
<a name="ml-inference-load-serve-model"></a>

**sugerencia**  
 [Regístrese](https://events.eksworkshop.com/workshops/genai/) en los próximos talleres de IA/ML de Amazon EKS.

Los pasos de esta sección implementan un modelo de lenguaje de gran tamaño (LLM) en Amazon EKS, lo sirven con vLLM e interactúan con el punto de conexión de inferencia.

En este tutorial se utilizan las siguientes herramientas:
+  [vLLM:](https://docs.vllm.ai/en/latest/) un motor de inferencia de alto rendimiento optimizado para el servicio de LLM y la administración de la memoria de la GPU.
+  [Run:ai Model Streamer:](https://github.com/run-ai/runai-model-streamer) transmite los pesos de los modelos directamente desde Amazon S3 a la memoria de la GPU, lo que reduce el tiempo de carga de minutos a segundos.
+  [Open WebUI:](https://openwebui.com/) un frontend de chat autoalojado que se conecta a la API compatible con OpenAI del vLLM.

En esta sección se utiliza el [modelo Ministral-3-8B-Instruct-2512](https://huggingface.co/mistralai/Ministral-3-8B-Instruct-2512), pero puede implementar cualquier modelo de IA compatible con vLLM. Para obtener una lista de los modelos admitidos, consulte [Modelos admitidos](https://docs.vllm.ai/en/latest/models/supported_models/#text-generation) en la documentación de vLLM.

**importante**  
Utilice el clúster que ha creado en la sección [Configuración del clúster de Amazon EKS para cargas de trabajo de IA y ML](ml-cluster-setup.md). Las instrucciones de este tutorial funcionan tanto para el modo automático de EKS como para Karpenter autoadministrado.

![Diagrama de arquitectura que muestra el flujo de trabajo de inferencia de LLM con vLLM en Amazon EKS](http://docs.aws.amazon.com/es_es/eks/latest/userguide/images/ml-inference-load-serve-model-arch.png)


En el siguiente diagrama de arquitectura se muestra el flujo integral:

1. Los pesos de los modelos se descargan de Hugging Face en Amazon S3.

1. vLLM transmite el modelo directamente desde S3 a la memoria de la GPU mediante Run:ai Model Streamer.

1. Los usuarios envían solicitudes de inferencia al punto de conexión de vLLM.

Al completar estos pasos, dispondrá de un punto de conexión de inferencia de vLLM que podrá utilizar para interactuar con un modelo de Ministral a través de una aplicación de frontend de chat.

## Requisitos previos
<a name="_prerequisites"></a>

Complete los pasos de la [sección de configuración del clúster](ml-cluster-setup.md).

Si ha abierto un terminal nuevo, configure el nombre y la región del clúster que utilizó en la sección [Configuración del clúster mediante la CLI](ml-cluster-setup-cli.md):

```
export CLUSTER_NAME=ai-eks-docs
export AWS_REGION=us-east-2
```

Busque el grupo de pesos de los modelos que creó en el paso [Bucket de S3 de pesos de los modelos](ml-cluster-setup-cli.md#cluster-setup-cli-model-bucket):

```
MODEL_BUCKET=$(aws s3api list-buckets \
  --query "Buckets[?starts_with(Name, '${CLUSTER_NAME}-models-')].Name | [0]" \
  --output text)
echo "Model bucket: ${MODEL_BUCKET}"
```

## Paso 1: descarga del modelo desde Hugging Face
<a name="_step_1_download_the_model_from_hugging_face"></a>

En este paso, implementará un trabajo de Kubernetes que descarga el modelo desde Hugging Face y lo cargará en el bucket de S3 que creó en la sección de requisitos previos.

Para descargar el modelo, aplique el siguiente manifiesto de trabajo:

### Manifiesto de trabajo de descarga del modelo
<a name="ml-inference-step1-model-download-yaml"></a>

```
cat << EOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
  name: model-download
  namespace: default
  labels:
    guide: ai-eks-docs
spec:
  backoffLimit: 10
  activeDeadlineSeconds: 3600
  ttlSecondsAfterFinished: 86400
  template:
    spec:
      restartPolicy: Never
      serviceAccountName: model-storage-sa
      containers:
      - name: downloader
        image: python:3.11-slim
        command: ["/bin/bash", "-c"]
        args:
        - |
          set -e
          pip install -q huggingface_hub boto3
          echo "Downloading Ministral-3-8B-Instruct-2512 from Hugging Face..."
          python3 -c "from huggingface_hub import snapshot_download; snapshot_download('mistralai/Ministral-3-8B-Instruct-2512', local_dir='/tmp/mistral', allow_patterns=['*.json', '*.txt', '*.md', 'consolidated.safetensors'], ignore_patterns=['model-*.safetensors', 'model.safetensors.index.json'])"
          echo "Uploading to S3 bucket: \${MODEL_BUCKET}"
          python3 << 'PYTHON'
          import boto3
          import os
          from pathlib import Path

          s3 = boto3.client('s3')
          bucket = os.environ.get('MODEL_BUCKET')
          local_dir = Path("/tmp/mistral")

          for file_path in local_dir.rglob("*"):
              if file_path.is_file():
                  if '.cache' in file_path.parts:
                      continue
                  s3_key = f"Ministral-3-8B-Instruct-2512/{file_path.relative_to(local_dir)}"
                  print(f"Uploading {file_path.name}...")
                  s3.upload_file(str(file_path), bucket, s3_key)
          print("Upload complete!")
          PYTHON
        env:
        - name: MODEL_BUCKET
          value: "${MODEL_BUCKET}"
        - name: HF_HUB_DISABLE_XET
          value: "1"
        resources:
          requests:
            memory: "2Gi"
            cpu: "1"
          limits:
            memory: "4Gi"
            cpu: "2"
EOF
```

Espere a que el trabajo finalice. Los pesos de los modelos (consolidated.safetensors) son de aproximadamente 10,4 GB y este paso suele tardar de 3 a 5 minutos.

```
kubectl wait --for=condition=complete job/model-download --timeout=600s
```

Resultado previsto:

```
job.batch/model-download condition met
```

Verifique que los pesos de los modelo se cargaron en S3:

```
aws s3 ls s3://$(kubectl get job model-download -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="MODEL_BUCKET")].value}')/Ministral-3-8B-Instruct-2512/ --recursive
```

Resultado previsto:

```
2026-05-18 10:29:53      20311 Ministral-3-8B-Instruct-2512/README.md
2026-05-18 10:29:53       2361 Ministral-3-8B-Instruct-2512/SYSTEM_PROMPT.txt
2026-05-18 10:29:53       1903 Ministral-3-8B-Instruct-2512/config.json
2026-05-18 10:29:54 10420633176 Ministral-3-8B-Instruct-2512/consolidated.safetensors
2026-05-18 10:29:53        131 Ministral-3-8B-Instruct-2512/generation_config.json
2026-05-18 10:29:53       1185 Ministral-3-8B-Instruct-2512/params.json
2026-05-18 10:29:53        976 Ministral-3-8B-Instruct-2512/processor_config.json
2026-05-18 10:29:53   16753777 Ministral-3-8B-Instruct-2512/tekken.json
2026-05-18 10:29:53   17077402 Ministral-3-8B-Instruct-2512/tokenizer.json
2026-05-18 10:29:53      21168 Ministral-3-8B-Instruct-2512/tokenizer_config.json
```

El archivo consolidated.safetensors contiene los pesos de los modelos (aproximadamente 10,4 GB). El resto de los archivos son archivos de configuración y tokenizadores que vLLM necesita para servir el modelo.

## Paso 2: implementación del contenedor de inferencia
<a name="_step_2_deploy_the_inference_container"></a>

En esta sección, implementará vLLM como una implementación de Kubernetes para servir al modelo que cargó en Amazon S3.

En la sección se utilizan [Contenedores de aprendizaje profundo de AWS](https://github.com/aws/deep-learning-containers/tree/master) (DLC), que son imágenes de Docker preinstaladas con marcos de aprendizaje profundo y optimizadas para el rendimiento en la infraestructura de AWS. Los DLC incluyen parches de seguridad, versiones de marcos validadas y configuraciones optimizadas de controladores de GPU.

Esta implementación usa el siguiente DLC de AWS para [vLLM 0.21.0](https://gallery.ecr.aws/deep-learning-containers/vllm) compatible con SOCI:

```
public.ecr.aws/deep-learning-containers/vllm:0.21.0-gpu-py312-cu130-ubuntu22.04-ec2-v1.0-soci
```

La etiqueta de imagen indica que vLLM 0.21.0 es compatible con GPU, Python 3.12, CUDA 13.0 y Ubuntu 22.04, está optimizada para cargas de trabajo basadas en EC2 y está habilitada para SOCI a fin de acelerar el inicio de los contenedores.

Este manifiesto crea una implementación que ejecuta vLLM en un nodo de la GPU y transmite el modelo directamente desde S3 a la memoria de la GPU mediante Run:ai Model Streamer. El manifiesto también crea un servicio ClusterIP que expone el punto de conexión de vLLM en el puerto 8000 para el acceso dentro del clúster.

Aplique el manifiesto:

### YAML de implementación y servicio de vLLM
<a name="ml-inference-step2-vllm-deployment-yaml"></a>

```
cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-inference-app
  labels:
    guide: ai-eks-docs
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vllm-inference-app
  template:
    metadata:
      labels:
        app: vllm-inference-app
        guide: ai-eks-docs
    spec:
      serviceAccountName: model-storage-sa
      tolerations:
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule
      nodeSelector:
        karpenter.sh/nodepool: gpu-inf
      containers:
      - name: vllm-inference
        image: public.ecr.aws/deep-learning-containers/vllm:0.21.0-gpu-py312-cu130-ubuntu22.04-ec2-v1.0-soci
        ports:
        - containerPort: 8000
        args:
        - "--model=s3://${MODEL_BUCKET}/Ministral-3-8B-Instruct-2512/"
        - "--host=0.0.0.0"
        - "--port=8000"
        - "--tensor-parallel-size=1"
        - "--gpu-memory-utilization=0.9"
        - "--max-model-len=8192"
        - "--max-num-seqs=1"
        - "--load-format=runai_streamer"
        - "--enforce-eager"
        - "--tokenizer_mode=mistral"
        - "--config_format=mistral"
        - "--enable-auto-tool-choice"
        - "--tool-call-parser=mistral"
        resources:
          limits:
            nvidia.com/gpu: 1
          requests:
            memory: "40Gi"
            cpu: "8"
---
apiVersion: v1
kind: Service
metadata:
  name: vllm-inference-svc
  namespace: default
  labels:
    app: vllm-inference-app
spec:
  selector:
    app: vllm-inference-app
  ports:
  - name: http
    port: 8000
    targetPort: 8000
    protocol: TCP
EOF
```

Compruebe que el estado del pod de vLLM sea Listo:

```
kubectl get pod -l app=vllm-inference-app -w
```

Resultado previsto:

```
NAME                                  READY   STATUS    RESTARTS   AGE
vllm-inference-app-65df5fddc8-5kmjm   1/1     Running   0          86s
```

La imagen del contenedor puede tardar unos 2 minutos en extraerse y vLLM puede tardar este tiempo en transmitir los pesos de los modelos desde S3 a la memoria de la GPU. Espere a que el pod muestre `1/1` en la columna LISTO antes de continuar.

La combinación de EKS, SOCI y Run:ai Model Streamer permite un inicio rápido del pod. Para comprobar la hora de inicio de cada etapa, consulte los eventos del pod:

```
kubectl describe pod -l app=vllm-inference-app | grep -A 20 "Events:"
```

Resultado previsto:

```
Events:
  Type     Reason            Age   From                   Message
  ----     ------            ----  ----                   -------
  Warning  FailedScheduling  86s   default-scheduler      0/2 nodes are available: 2 node(s) had untolerated taint(s).
  Normal   Nominated         85s   eks-auto-mode/compute  Pod should schedule on: nodeclaim/gpu-inf-kqkq6
  Normal   Scheduled         55s   default-scheduler      Successfully assigned default/vllm-inference-app-d9d54586d-csmd7 to i-04f8792414384d2d3
  Normal   Pulling           52s   kubelet                Pulling image "public.ecr.aws/deep-learning-containers/vllm:0.21.0-gpu-py312-cu130-ubuntu22.04-ec2-v1.0-soci"
  Normal   Pulled            4s    kubelet                Successfully pulled image "public.ecr.aws/deep-learning-containers/vllm:0.21.0-gpu-py312-cu130-ubuntu22.04-ec2-v1.0-soci" in 48.376s (48.376s including waiting). Image size: 8802823997 bytes.
  Normal   Created           4s    kubelet                Created container vllm-inference
  Normal   Started           4s    kubelet                Started container vllm-inference
```

En este ejemplo, el nodo de la GPU se aprovisionó en 30 segundos y la imagen del contenedor de 8,8 GB se obtuvo en aproximadamente 48 segundos mediante SOCI. La rápida extracción de la imagen reduce los tiempos de inicio en frío en contenedores de inferencia de gran tamaño, lo que permite escalar los pods de GPU de forma dinámica en lugar de aprovisionar en exceso la capacidad de la GPU inactiva.

A continuación, compruebe los registros de vLLM para verificar el tiempo de carga del modelo:

```
kubectl logs $(kubectl get pod -l app=vllm-inference-app -o jsonpath='{.items[0].metadata.name}') | grep -i 'Model loading took'
```

Resultado previsto:

```
INFO 05-18 18:41:49 [gpu_model_runner.py:4959] Model loading took 9.81 GiB memory and 5.023344 seconds
```

El registro confirma que Run:ai Model Streamer cargó los 10,4 GB de pesos de los modelos directamente desde S3 a la memoria de la GPU en aproximadamente 5 segundos, con un consumo de 9,8 GiB de memoria de la GPU.

El tiempo de descarga de la imagen en este ejemplo se utilizó una instancia g6e.4xlarge, que tiene un ancho de banda de la red sostenido de 20 Gbps. Los tiempos de extracción de imágenes y de carga del modelo variarán en función del resto de tipos de instancias en función del ancho de banda de la red disponible.

## Paso 3: ejecución de inferencia
<a name="_step_3_run_inference"></a>

Con la implementación de vLLM en ejecución, valide el punto de conexión de inferencia e implemente un frontend de chat para interactuar con el modelo.

### Ejecución de una prueba de validación del modelo
<a name="_run_a_model_validation_test"></a>

Exponga el punto de conexión de inferencia mediante el reenvío de puertos:

```
kubectl port-forward svc/vllm-inference-svc 8000:8000
```

Abra una nueva ventana de terminal y, a continuación, valide que el contenedor de inferencia esté respondiendo:

```
curl -sI -X GET http://localhost:8000/health
```

Resultado previsto:

```
HTTP/1.1 200 OK
date: Fri, 18 May 2026 00:39:23 GMT
server: uvicorn
content-length: 0
```

## Paso 4: supervisión de vLLM
<a name="ml-inference-load-serve-model-monitoring"></a>

vLLM expone las métricas de Prometheus listas para usar, como la tasa de solicitudes, el rendimiento de los tokens, la latencia integral y el uso de la memoria caché KV de la GPU. En esta sección, utiliza estas métricas con la pila de supervisión que configuró en los pasos de [Configuración del clúster](ml-cluster-setup.md) y las verá en un panel de Grafana aprovisionado previamente.

**importante**  
Debe completar la subsección [Supervisión](ml-cluster-setup-cli.md#cluster-setup-cli-monitoring) de la sección [Configuración del clúster mediante la CLI](ml-cluster-setup-cli.md) antes de continuar. Este paso depende de que se esté instalando kube-prometheus-stack y de que el panel de Grafana de vLLM ya esté aprovisionado en el archivo de valores.

### Aplicación de ServiceMonitor de vLLM
<a name="ml-inference-load-serve-model-monitoring-servicemonitor"></a>

Un ServiceMonitor indica a Prometheus dónde raspar las métricas de vLLM.

```
cat << EOF | kubectl apply -f -
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: vllm-inference-app
  namespace: default
  labels:
    release: kube-prometheus-stack
spec:
  selector:
    matchLabels:
      app: vllm-inference-app
  endpoints:
  - port: http
    path: /metrics
    interval: 15s
EOF
```

Verifique que se haya creado correctamente ServiceMonitor:

```
kubectl get servicemonitor vllm-inference-app
```

Resultado previsto:

```
NAME                  AGE
vllm-inference-app    5s
```

#### Generación de tráfico de inferencia
<a name="ml-inference-load-serve-model-monitoring-traffic"></a>

Para llenar el panel con métricas, genere tráfico de inferencia a partir del punto de conexión de vLLM que ya ha expuesto mediante el reenvío de puertos en el paso de validación.

Detecte el nombre del modelo servido:

```
MODEL_NAME=$(curl -s http://localhost:8000/v1/models | jq -r '.data[0].id')
echo "Using model: $MODEL_NAME"
```

Envíe 50 solicitudes de finalización de chat en paralelo:

```
for i in $(seq 1 50); do
  curl -s -X POST http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d "{\"model\": \"$MODEL_NAME\", \"messages\": [{\"role\": \"user\", \"content\": \"Write a short poem about Kubernetes.\"}], \"max_tokens\": 128}" \
    > /dev/null &
done
wait
```

Mientras el tráfico fluya (o inmediatamente después), compruebe las métricas de rendimiento de los tokens directamente desde el punto de conexión `/metrics` de vLLM:

```
curl -s http://localhost:8000/metrics | grep -E '^vllm:(prompt_tokens_total|generation_tokens_total|avg_generation_throughput_toks_per_s|avg_prompt_throughput_toks_per_s)' | head
```

Las métricas `vllm:prompt_tokens_total` y `vllm:generation_tokens_total` aumentan monótonamente los contadores de los tokens de entrada y salida servidos. Las métricas `vllm:avg_prompt_throughput_toks_per_s` y `vllm:avg_generation_throughput_toks_per_s` son indicadores de rendimiento del promedio acumulativo. Estas mismas métricas alimentan el panel de Grafana que se abre en la siguiente subsección.

### Visualización del panel de control de Grafana de vLLM
<a name="ml-inference-load-serve-model-monitoring-dashboard"></a>

El archivo de valores de kube-prometheus-stack de la sección [Supervisión](ml-cluster-setup-cli.md#cluster-setup-cli-monitoring) ya aprovisiona el [panel de vLLM (gnetId 25.263)](https://grafana.com/grafana/dashboards/25263-vllm-metrics/) de la comunidad en la carpeta **Supervisión de GPU**, de modo que no es necesario importar nada más.

Para acceder a Grafana, inicie un reenvío de puertos al servicio de Grafana:

```
kubectl port-forward svc/kube-prometheus-stack-grafana 3000:80 -n monitoring
```

Abra [http://localhost:3000](http://localhost:3000) en el navegador y vaya a **Paneles de control > Supervisión de GPU > Métricas de vLLM**.

 **Panel de Grafana de vLLM** 

![Panel de Grafana de vLLM que muestra la tasa de solicitudes, el rendimiento del token, la latencia integral y el uso de la memoria caché KV de la GPU](http://docs.aws.amazon.com/es_es/eks/latest/userguide/images/ml-inference-load-serve-model-vllm-monitoring.png)


El panel muestra la tasa de solicitudes, el rendimiento de los tokens de petición y generación, los percentiles de latencia y el uso de la caché KV de la GPU para el punto de conexión de inferencia de vLLM.

## Paso 5: implementación de una aplicación de chat
<a name="_step_5_deploy_chat_application"></a>

En este paso, implementará Open WebUI como frontend de chat para interactuar con el modelo. Open WebUI es una interfaz de IA de código abierto y autoalojada que admite API compatibles con OpenAI y proporciona una interfaz de chat con historial de conversaciones y representación de Markdown. Como vLLM expone una API compatible con OpenAI, Open WebUI se conecta a ella directamente como un backend.

Para implementar la aplicación Open WebUI, aplique el siguiente manifiesto:

### YAML de implementación y servicio de Open WebUI
<a name="ml-inference-step5-open-webui-yaml"></a>

```
cat << 'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: open-webui
  namespace: default
  labels:
    app: open-webui
    guide: ai-eks-docs
spec:
  replicas: 1
  selector:
    matchLabels:
      app: open-webui
  template:
    metadata:
      labels:
        app: open-webui
        guide: ai-eks-docs
    spec:
      containers:
      - name: open-webui
        image: ghcr.io/open-webui/open-webui:v0.9.2
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "500m"
            memory: "500Mi"
          limits:
            cpu: "1000m"
            memory: "1Gi"
        env:
        - name: OPENAI_API_BASE_URLS
          value: "http://vllm-inference-svc:8000/v1"
        - name: OPENAI_API_KEY
          value: "dummy"
        - name: WEBUI_AUTH
          value: "False"
        - name: ENABLE_OLLAMA_API
          value: "False"
        - name: ENABLE_EVALUATION_ARENA_MODELS
          value: "False"
        volumeMounts:
        - name: webui-volume
          mountPath: /app/backend/data
      volumes:
      - name: webui-volume
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: open-webui
  namespace: default
  labels:
    app: open-webui
spec:
  type: ClusterIP
  selector:
    app: open-webui
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
EOF
```

Espere a que el pod de Open WebUI esté listo:

```
kubectl wait --for=condition=ready pod -l app=open-webui --timeout=300s
```

Resultado previsto:

```
pod/open-webui-6cbfc9867f-jf9w9 condition met
```

Para acceder a la aplicación, configure el reenvío de puertos y abra la aplicación en el navegador:

```
kubectl port-forward svc/open-webui 8080:80 &
sleep 5
echo "Open WebUI: http://localhost:8080"
```

Abra [http://localhost:8080](http://localhost:8080) en el navegador.

Aparece la interfaz de chat en la que puede interactuar con el modelo de Ministral.

Cuando termine las pruebas, ejecute `kill %1 %2` para detener los procesos de reenvío de puertos en segundo plano (o ejecute `jobs` para enumerarlos y `kill %<jobspec>` para cada uno de ellos).

![Captura de pantalla de la interfaz de chat de Open WebUI que muestra una conversación con el modelo de Ministral](http://docs.aws.amazon.com/es_es/eks/latest/userguide/images/ml-inference-load-serve-model-chatui.png)


## Limpieza
<a name="_clean_up"></a>

Para eliminar los recursos de carga de trabajo que creó en esta sección, elimine la aplicación Open WebUI, el servidor de inferencias de vLLM y el trabajo de descarga de modelos:

```
kubectl delete deployment open-webui
kubectl delete service open-webui
kubectl delete deployment vllm-inference-app
kubectl delete service vllm-inference-svc
kubectl delete servicemonitor vllm-inference-app
kubectl delete job model-download
```

Para obtener instrucciones sobre cómo eliminar recursos de infraestructura, como el clúster, NodePool y el bucket de S3, consulte [Limpieza de la configuración del clúster](ml-cluster-setup-cli.md#cluster-setup-cli-cleanup).