View a markdown version of this page

Configuración del clúster de Amazon EKS para cargas de trabajo de IA y ML con Terraform - Amazon EKS

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.

Configuración del clúster de Amazon EKS para cargas de trabajo de IA y ML con Terraform

sugerencia

Regístrese en los próximos talleres de IA/ML de Amazon EKS.

En esta sección, se explican los pasos necesarios para crear la infraestructura necesaria para ejecutar cargas de trabajo de entrenamiento o inferencia en Amazon EKS con Terraform. Los pasos incluyen la creación de un clúster de EKS, nodos habilitados para GPU con el modo automático de EKS o Karpenter, una pila de supervisión con Prometheus y Grafana y almacenamiento en Amazon S3 para el peso de los modelos.

Consulte la documentación del modo automático de EKS y Karpenter para obtener más información sobre cómo esas características aprovisionan y escalan automáticamente las instancias de EC2 en los clústeres de EKS.

Arquitectura y flujo de trabajo de alto nivel

Arquitectura de alto nivel que muestra <shared id=

En el diagrama se muestra la arquitectura de alto nivel de AWS para la configuración de esta sección.

Requisitos previos

importante

Los recursos que cree en este tutorial, lo que incluye los clústeres de EKS, las instancias de GPU, los equilibradores de carga de aplicación y Amazon Managed Service para Prometheus, conllevan cargos. Elimine los recursos cuando haya terminado para evitar que se sigan cobrando los cargos.

Verifique las versiones de las herramientas:

terraform --version aws --version kubectl version --client jq --version

Paso 1: descarga e implementación del código de Terraform

Este tutorial usa el código de Terraform del repositorio sample-eks-docs de muestras de AWS de GitHub. Clone el repositorio en un directorio de trabajo:

git clone git@github.com:aws-samples/sample-eks-docs.git cd sample-eks-docs/ai-ml/set-up-cluster

El repositorio tiene la siguiente estructura en el directorio ai-ml/set-up-cluster/ al que acaba de cambiar:

set-up-cluster/
├── scripts/
│   └── cleanup.sh
└── terraform/
    ├── auto-mode/
    └── karpenter/

El repositorio proporciona dos rutas de implementación. Elija solo una y utilícela a lo largo de la guía.

  • Modo automático de EKS (terraform/auto-mode/): además de los complementos de redes, almacenamiento y equilibrio de carga principales, el modo automático de EKS incluye y administra las siguientes capacidades para las cargas de trabajo de entrenamiento e inferencia: agente de supervisión de nodos de EKS, reparación automática de nodos, capturador de instantáneas SOCI para extraer contenedores rápidamente y preparación de la GPU para la NodeClass predeterminada. El complemento para dispositivos de NVIDIA se incluye en la AMI acelerada de Bottlerocket que utiliza el modo automático de EKS para los nodos habilitados para GPU.

  • Karpenter autoadministrado (terraform/karpenter/): en un clúster de EKS sin el modo automático de EKS, el código de Terraform instala y configura los componentes necesarios para las cargas de trabajo de entrenamiento e inferencia. Esto incluye complementos de red (CNI de VPC, CoreDNS, kube-proxy), Karpenter, el agente de supervisión de nodos de EKS, el complemento para dispositivos de NVIDIA y el capturador de instantáneas SOCI para extraer contenedores rápidamente.

importante

Elija el modo automático de EKS o Karpenter autoadministrado y utilícelo durante toda la guía. Para cambiar a media transmisión, es necesario eliminar el clúster y volver a comenzar.

Opciones de clústeres de EKS: modo automático de EKS y Karpenter autoadministrado

Comparación en paralelo de las dos opciones de clústeres: un clúster del modo automático de EKS con NodePool y un clúster estándar de EKS con Karpenter, CoreDNS, CNI de VPC, complemento para dispositivos de NVIDIA, agente de Pod Identity de EKS, agente de supervisión de nodos, kube-proxy y NodeClass y NodePool
Grafana es de acceso público a través de HTTP con las credenciales predeterminadas

La Ingress del ALB de Grafana tiene el valor predeterminado de var.my_cidr 0.0.0.0/0, lo que expone Grafana a la Internet pública a través de HTTP simple con credenciales de administrador predeterminadas. Los escáneres automatizados detectan los equilibradores de carga públicos en cuestión de minutos. Debe restringir el acceso mediante la sustitución de var.my_cidr por su propia dirección IP:

export MY_CIDR="$(curl -s https://checkip.amazonaws.com)/32" terraform apply -var "my_cidr=${MY_CIDR}"

Trate las listas de permitidos de IP de origen como una protección mínima, no como una completa. Cambie también la contraseña de administrador predeterminada de Grafana después del primer inicio de sesión. Para una posición más sólida, cambie alb.ingress.kubernetes.io/scheme a internal (al que solo se pueda acceder desde su VPC o una VPN conectada) y agregue un certificado TLS.

Implementación del clúster

Cambie al directorio de la ruta elegida, inicialice Terraform y aplique lo siguiente:

Ambas variantes están configuradas de forma predeterminada en la región us-east-2. Para implementar en otra región, agregue -var "region=region-code" al comando terraform apply del siguiente paso, donde region-code es la región de AWS en la que desea implementar.

El código de Terraform utiliza todas las zonas de disponibilidad disponibles en la región de destino, excepto use1-az3, usw1-az2 y cac1-az3 porque Amazon EKS no admite la ubicación de planos de control en esas zonas.

EKS Auto Mode
cd terraform/auto-mode terraform init terraform apply

Este comando tarda varios minutos en completarse.

Self-managed Karpenter
cd terraform/karpenter terraform init terraform apply

Este comando tarda alrededor de 15 minutos en completarse. Crea un clúster de EKS con un grupo de nodos administrado dedicado a alojar complementos y el controlador de Karpenter. Terraform instala Karpenter con la cola de interrupción de spot habilitada y las puertas de características NodeRepair y StaticCapacity activadas. También instala el complemento para dispositivos de NVIDIA, el controlador del equilibrador de carga de AWS y la pila de supervisión.

Revisión de las salidas de Terraform

Cuando se completa la aplicación, Terraform imprime las siguientes salidas (los valores varían según la configuración):

Apply complete! Resources: 74 added, 0 changed, 0 destroyed.

Outputs:

cluster_name         = "ai-eks-docs"
configure_kubectl    = "aws eks update-kubeconfig --region us-east-2 --name ai-eks-docs --alias ai-eks-docs"
configure_model_bucket = "export MODEL_BUCKET=ai-eks-docs-models-20250612abc1"
model_bucket         = "ai-eks-docs-models-20250612abc1"
node_iam_role_name   = "ai-eks-docs-eks-auto-20250612..."
region               = "us-east-2"

La salida configure_kubectl es un comando listo para ejecutarse que dirige kubectl al clúster. La salida model_bucket contiene el nombre del bucket de S3 para los pesos de los modelos. La salida node_iam_role_name muestra el rol de IAM que utilizan los nodos.

Configuración de kubectl

Dirija kubectl al nuevo clúster. La salida configure_kubectl es un comando listo para ejecutarse:

eval "$(terraform output -raw configure_kubectl)"

Verificación del clúster

EKS Auto Mode
kubectl get pods --all-namespaces

Resultado previsto:

NAMESPACE NAME READY STATUS RESTARTS AGE kube-system metrics-server-5db89f9ffd-h4mlr 1/1 Running 0 3m kube-system metrics-server-5db89f9ffd-nd748 1/1 Running 0 3m monitoring kube-prometheus-stack-grafana-ff9b5fd57-dh562 3/3 Running 0 3m monitoring kube-prometheus-stack-kube-state-metrics-5dcbfdf69b-wd6qn 1/1 Running 0 3m monitoring kube-prometheus-stack-operator-548c4f4485-m5wh2 1/1 Running 0 3m monitoring kube-prometheus-stack-prometheus-node-exporter-p2z7l 1/1 Running 0 3m monitoring prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0 3m

En el modo automático de EKS, el CNI de VPC, kube-proxy y CoreDNS se ejecutan como componentes administrados y no aparecen como pods en kube-system.

Self-managed Karpenter
kubectl get pods --all-namespaces

La salida esperada incluye Karpenter, CoreDNS, kube-proxy, aws-node (CNI de VPC), el agente de Pod Identity de EKS, el agente de supervisión de nodos de EKS y el complemento para dispositivos de NVIDIA:

NAMESPACE NAME READY STATUS RESTARTS AGE kube-system aws-node-bzdcz 2/2 Running 0 5m kube-system aws-node-vkbhb 2/2 Running 0 5m kube-system coredns-7dbb8998cf-9b9wk 1/1 Running 0 5m kube-system coredns-7dbb8998cf-pwtjd 1/1 Running 0 5m kube-system ebs-csi-controller-748f54b69-8h7jv 6/6 Running 0 5m kube-system ebs-csi-controller-748f54b69-v2mv8 6/6 Running 0 5m kube-system eks-node-monitoring-agent-5qw4l 1/1 Running 0 5m kube-system eks-node-monitoring-agent-7lrtm 1/1 Running 0 5m kube-system eks-pod-identity-agent-ddvlv 1/1 Running 0 5m kube-system eks-pod-identity-agent-q4g29 1/1 Running 0 5m kube-system karpenter-898ff78-cndbd 1/1 Running 0 5m kube-system karpenter-898ff78-hfbhn 1/1 Running 0 5m kube-system kube-proxy-gcfnh 1/1 Running 0 5m kube-system kube-proxy-tktcf 1/1 Running 0 5m kube-system metrics-server-5b789db597-cm9qd 1/1 Running 0 5m kube-system metrics-server-5b789db597-w57b6 1/1 Running 0 5m kube-system nvidia-device-plugin-node-feature-discovery-gc-66cb7f5dc-xvftc 1/1 Running 0 5m kube-system nvidia-device-plugin-node-feature-discovery-master-854d6b5hnqtp 1/1 Running 0 5m monitoring kube-prometheus-stack-grafana-6797bcb59f-2zlwg 3/3 Running 0 5m monitoring kube-prometheus-stack-kube-state-metrics-8446bd549c-q6gjc 1/1 Running 0 5m monitoring kube-prometheus-stack-operator-5f499b784-vf5xb 1/1 Running 0 5m monitoring kube-prometheus-stack-prometheus-node-exporter-4c6wq 1/1 Running 0 5m monitoring kube-prometheus-stack-prometheus-node-exporter-l5t25 1/1 Running 0 5m monitoring prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0 5m

Verifique que el complemento para dispositivos de NVIDIA esté instalado. No aparece ningún pod del complemento para dispositivos hasta que se aprovisione un NodePool de GPU con la etiqueta amiFamily=al2023 correspondiente:

kubectl get daemonset nvidia-device-plugin -n kube-system

Resultado previsto:

NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE nvidia-device-plugin 0 0 0 0 0 amiFamily=al2023 5m

Paso 2: creación de NodePool de GPU dinámica

Los NodePools de GPU están registrados. De forma predeterminada, terraform apply crea el clúster y la pila de supervisión sin capacidad de GPU ni facturación por GPU. Para aprovisionar nodos de GPU, pase la variable nodepools con un nombre de estrategia.

Habilite la estrategia spot-ondemand, que aprovisiona instancias de GPU de la familia G con una generación superior a 4, con capacidad de spot con la opción bajo demanda como alternativa:

terraform apply -var 'nodepools={"spot-ondemand"={}}'

Este comando aplica las plantillas de NodePool y NodeClass del directorio nodepools/spot-ondemand/. Ambas rutas utilizan la misma API de NodePool, pero difieren en la NodeClass a la que hace referencia el NodePool.

EKS Auto Mode

El NodePool hace referencia a la NodeClass default administrada, que ya selecciona la AMI acelerada de Bottlerocket, los controladores de NVIDIA, el complemento para dispositivos de NVIDIA y la extracción en paralelo de SOCI. La estrategia spot-ondemand no envía ninguna NodeClass propia en esta ruta.

Valide el NodePool:

kubectl get nodepools,nodeclasses

Resultado previsto. El NodePool gpu-inf se une a los NodePools general-purpose y system integrados, y los tres hacen referencia a la NodeClass default administrada:

NAME NODECLASS NODES READY AGE nodepool.karpenter.sh/general-purpose default 0 True 12m nodepool.karpenter.sh/gpu-inf default 0 True 20s nodepool.karpenter.sh/system default 1 True 12m NAME ROLE READY AGE nodeclass.eks.amazonaws.com/default ai-eks-docs-eks-auto-... True 12m
Self-managed Karpenter

Terraform aplica una EC2NodeClass gpu-inf personalizada junto con el NodePool. La EC2NodeClass fija el alias de la AMI de AL2023 optimizada para EKS, habilita SOCI a través de la puerta de características FastImagePull y establece instanceStorePolicy: RAID0 para mover la caché de imagen de containerd a un NVMe local.

Valide el NodePool y la EC2NodeClass:

kubectl get nodepools,ec2nodeclasses

Resultado previsto. El par gpu-inf se une al NodePool general-purpose y a la EC2NodeClass que Terraform crea para cargas de trabajo que no son de GPU:

NAME NODECLASS NODES READY AGE nodepool.karpenter.sh/general-purpose general-purpose 1 True 14m nodepool.karpenter.sh/gpu-inf gpu-inf 0 True 25s NAME READY AGE ec2nodeclass.karpenter.k8s.aws/general-purpose True 14m ec2nodeclass.karpenter.k8s.aws/gpu-inf True 25s

Ambas rutas muestran nodos 0 para gpu-inf hasta que se programe una carga de trabajo de GPU. El modo automático de EKS y Karpenter solo lanzan nodos cuando los pods pendientes lo requieren.

Paso 3: prueba con un pod de muestra

Pruebe la configuración de NodePool de GPU con un pod nvidia-smi:

cat << EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: nvidia-smi labels: guide: ai-eks-docs spec: restartPolicy: OnFailure tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" containers: - name: nvidia-smi image: public.ecr.aws/amazonlinux/amazonlinux:2023-minimal command: ["nvidia-smi"] resources: limits: nvidia.com/gpu: 1 EOF

Verifique que el pod se haya programado y completado correctamente:

kubectl get pods nvidia-smi

Resultado previsto:

NAME         READY   STATUS      RESTARTS   AGE
nvidia-smi   0/1     Completed   0          67s

STATUS: Completed significa que el comando nvidia-smi se ejecutó y cerró. Compruebe los registros del pod para ver la GPU detectada por el nodo:

kubectl logs nvidia-smi

Resultado previsto:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.159.03             Driver Version: 580.159.03     CUDA Version: 13.0     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA L4                      On  |   00000000:31:00.0 Off |                    0 |
| N/A   41C    P8             13W /   72W |       0MiB /  23034MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

La salida muestra el modelo de GPU, la versión del controlador, la versión de CUDA y la memoria disponible. En este ejemplo, Karpenter aprovisionó una instancia G6 que tiene una GPU L4 de NVIDIA con 24 GB de memoria. El modelo de GPU y la memoria varía según el tipo de instancia que seleccione Karpenter. Las instancias G5 tienen GPU A10G de NVIDIA (24 GB), las instancias G6 tienen GPU L4 de NVIDIA (24 GB) y las instancias G6e tienen GPU L40S de NVIDIA (48 GB).

Para entender cómo Karpenter y el programador de Kubernetes se coordinaron para aprovisionar un nodo y colocar el pod, compruebe los eventos del ciclo de vida del pod:

kubectl describe pod nvidia-smi

Resultado previsto:

Events:
  Type     Reason            Age   From                   Message
  ----     ------            ----  ----                   -------
  Warning  FailedScheduling  75s   default-scheduler      0/2 nodes are available: 2 node(s) had untolerated taint(s).
  Normal   Nominated         74s   eks-auto-mode/compute  Pod should schedule on: nodeclaim/gpu-inf-z6q75
  Normal   Scheduled         35s   default-scheduler      Successfully assigned default/nvidia-smi to i-0eb897a8302551589
  Normal   Pulling           27s   kubelet                spec.containers{nvidia-smi}: Pulling image "public.ecr.aws/amazonlinux/amazonlinux:2023-minimal"
  Normal   Pulled            22s   kubelet                spec.containers{nvidia-smi}: Successfully pulled image "public.ecr.aws/amazonlinux/amazonlinux:2023-minimal" in 5.625s (5.626s including waiting). Image size: 37440620 bytes.
  Normal   Created           22s   kubelet                spec.containers{nvidia-smi}: Container created
  Normal   Started           21s   kubelet                spec.containers{nvidia-smi}: Container started

Estos eventos muestran la secuencia de programación del pod: el pod inicialmente no se puede programar porque no existen nodos de GPU (FailedScheduling), Karpenter nomina una nueva NodeClaim (Nominated), el programador asigna el pod una vez que el nodo está listo (Scheduled) y, a continuación, se extrae e inicia la imagen del contenedor. El moto automático de EKS tiene la extracción en paralelo de SOCI (Seekable OCI) instalada y configurada de forma predeterminada en las instancias G, P y Trn, y la ruta de Karpenter autoadministrada la configura de forma explícita a través de la puerta de características FastImagePull.

nota

En un clúster de Karpenter autoadministrado, el evento Nominated muestra karpenter/compute en lugar de eks-auto-mode/compute.

NodeClaim es una solicitud que Karpenter crea para aprovisionar un nodo específico. Muestra el tipo de instancia, el tipo de capacidad, la AZ y si el nodo está listo:

kubectl get nodeclaims

Resultado previsto:

NAME            TYPE        CAPACITY   ZONE         NODE                  READY   AGE
gpu-inf-z6q75   g6.xlarge   spot       us-east-2a   i-0eb897a8302551589   True    5m

El tipo de instancia y la AZ varían. Todas las instancias de la familia G con una generación superior a 4 son aptas.

sugerencia

Si no aparece ningún nodo, compruebe si hay errores de capacidad insuficiente:

kubectl get events | grep InsufficientCapacityError

Karpenter almacena en caché las ofertas no disponibles durante 3 minutos. Ampliar los tipos de instancias y las zonas de disponibilidad permitidos en el NodePool aumenta las posibilidades de conseguir capacidad.

nota

Las instancias de spot lanzadas por Karpenter no aparecen en la consola de solicitudes de spot de EC2. Karpenter utiliza la API CreateFleet de EC2 con type: instant. Las instancias aparecen en la consola de instancias de EC2 con un ciclo de vida de spot.

Paso 4: adición de capacidad reservada al NodePool (opcional)

Si bien el NodePool de GPU del paso 2 aprovisiona instancias bajo demanda o de spot de forma dinámica, algunos casos de uso necesitan una capacidad garantizada. Puede crear una reserva de capacidad bajo demanda (ODCR) para garantizar que la capacidad de la GPU esté disponible cuando se necesite.

Con Terraform, un solo comando crea la ODCR, una NodeClass personalizada que hace referencia a la reserva por etiqueta, y actualiza el NodePool para incluir reserved como tipo de capacidad. Terraform etiqueta la ODCR con nodepool=reserved-spot-ondemand y la NodeClass la selecciona con esa etiqueta.

aviso

El siguiente comando crea una ODCR que se factura inmediatamente y se continúa facturando hasta que la elimine con terraform destroy o el script de limpieza, independientemente de si se están ejecutando nodos en ella o no.

Utilice los valores predeterminados (g6e.4xlarge, 1 instancia, primera AZ del clúster):

terraform apply -var 'nodepools={"reserved-spot-ondemand"={reservation={}}}'

Elija el tipo de instancia, el recuento y la AZ:

terraform apply -var 'nodepools={"reserved-spot-ondemand"={reservation={instance_type="g6e.2xlarge",instance_count=1,az="us-east-2a"}}}'

El objeto reservation admite los siguientes campos:

  • instance_type: el tipo de instancia de GPU que se reservará. Valor predeterminado: g6e.4xlarge.

  • instance_count: el número de instancias que se reservará. Valor predeterminado: 1.

  • az: la zona de disponibilidad de la reserva. Predeterminado: "" (usa la primera AZ del clúster).

importante

Las estrategias spot-ondemand y reserved-spot-ondemand son mutuamente excluyentes. Puede habilitar como máximo uno de los elementos de la variable nodepools. Si anteriormente utilizó spot-ondemand en el paso 2, el comando reserved-spot-ondemand lo reemplaza porque ambos administran el mismo NodePool gpu-inf.

Si se produce el error InsufficientInstanceCapacity, la reserva no se puede gestionar en la AZ especificada. Cancele la operación de Terraform (Ctrl + C) y vuelva a ejecutarla con un valor de az diferente:

terraform apply -var 'nodepools={"reserved-spot-ondemand"={reservation={instance_type="g6e.4xlarge",az="us-east-2b"}}}'

Tras la aplicación, Terraform actualiza el NodePool para incluir reserved, spot y on-demand en los requisitos de tipo de capacidad. Karpenter considera reserved como la opción más rentable y la lanza primero. Una vez que la reserva esté completa, volverá a ser de spot o bajo demanda.

En la ruta del modo automático de EKS, Terraform crea una NodeClass gpu-inf personalizada (ya que la NodeClass default agrupada es de solo lectura) que hace referencia a la ODCR por etiqueta a través de capacityReservationSelectorTerms. En la ruta autoadministrada de Karpenter, Terraform vuelve a aplicar la EC2NodeClass gpu-inf con capacityReservationSelectorTerms agregado y actualiza el NodePool para incluir reserved.

Verifique que se haya creado la ODCR.

aws ec2 describe-capacity-reservations \ --filters "Name=state,Values=active" "Name=tag:nodepool,Values=reserved-spot-ondemand" \ --query 'CapacityReservations[0].{Id:CapacityReservationId,State:State,InstanceType:InstanceType,AvailableCount:AvailableInstanceCount}' \ --output table \ --region $(terraform output -raw region)

Verifique que la NodeClass haga referencia a la ODCR:

EKS Auto Mode
kubectl get nodeclasses gpu-inf -o yaml | grep 'id: cr-.*'

Resultado previsto:

id: cr-xxxxxxxxxxxxxxxxx
Self-managed Karpenter
kubectl get ec2nodeclasses gpu-inf -o yaml | grep 'id: cr-.*'

Resultado previsto:

id: cr-xxxxxxxxxxxxxxxxx

Verifique que el NodePool esté listo:

kubectl get nodepools gpu-inf

Resultado previsto:

NAME      NODECLASS   NODES   READY   AGE
gpu-inf   gpu-inf     0       True    30s

Tras aplicar los cambios, valide que Karpenter priorice la capacidad reservada y recurra a las opciones de spot o bajo demanda. Implemente una implementación de 2 réplicas que solicite 1 GPU por pod. La ODCR es para 1 instancia (1 GPU), por lo que el primer pod activa Karpenter para lanzar un nodo reservado. El segundo pod no cabe en el nodo reservado y hace que Karpenter lance otro nodo desde la capacidad de spot o bajo demanda.

cat << 'EOF' | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: gpu-overflow-test labels: guide: ai-eks-docs spec: replicas: 2 selector: matchLabels: app: gpu-overflow-test template: metadata: labels: app: gpu-overflow-test guide: ai-eks-docs spec: tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule containers: - name: nvidia-smi image: public.ecr.aws/amazonlinux/amazonlinux:2023-minimal command: ["sh", "-c", "nvidia-smi && sleep infinity"] resources: limits: nvidia.com/gpu: 1 EOF

A diferencia del pod de prueba nvidia-smi del paso 3 que se ejecutó y cerró, esta implementación mantiene los pods en ejecución (sleep infinity) para que mantengan la GPU e impidan que se consolide el nodo.

Verifique los pods programados en los distintos nodos:

kubectl get pods -l app=gpu-overflow-test -o wide

Resultado previsto:

NAME                                 READY   STATUS    RESTARTS   AGE     IP            NODE                  NOMINATED NODE   READINESS GATES
gpu-overflow-test-55d55ff5b9-dvg52   1/1     Running   0          4m42s   10.0.75.210   i-08741a36089ff2088   <none>           <none>
gpu-overflow-test-55d55ff5b9-hw4m9   1/1     Running   0          4m43s   10.0.82.49    i-0f50cdbacb2017202   <none>           <none>

Compruebe NodeClaims para ver los tipos de capacidad:

kubectl get nodeclaims

Resultado previsto:

NAME            TYPE          CAPACITY   ZONE         NODE                  READY   AGE
gpu-inf-vw99m   g6e.4xlarge   reserved   us-east-2c   i-0f50cdbacb2017202   True    6m
gpu-inf-s65s6   g6.xlarge     spot       us-east-2b   i-08741a36089ff2088   True    5m59s

El nodo reservado se lanzó primero, seguido de un nodo de spot o bajo demanda una vez que se completó la reserva.

Elimine la implementación de prueba:

kubectl delete deployment gpu-overflow-test

Supervisión

Terraform ya aprovisionó toda la pila de supervisión durante terraform apply en el paso 1. La pila incluye un espacio de trabajo de Amazon Managed Service para Prometheus (AMP), políticas de IAM y asociaciones de Pod Identity de EKS para la escritura remota de Prometheus y el acceso a consultas de Grafana, el gráfico de Helm kube-prometheus-stack (Prometheus, Grafana, kube-state-metrics, node-exporter) y el exportador DCGM de NVIDIA para métricas de GPU.

En esta sección, se trata la verificación de los componentes de supervisión implementados.

Verificación de los pods de supervisión

Espere a que estén listos todos los pods de supervisión:

kubectl wait --for=condition=Ready pod --all -n monitoring --timeout=300s kubectl get pods -n monitoring

Resultado previsto:

NAME                                                       READY   STATUS    RESTARTS   AGE
kube-prometheus-stack-grafana-7c58f54f77-rftrj             3/3     Running   0          5m
kube-prometheus-stack-kube-state-metrics-d68dcbc84-5smxq   1/1     Running   0          5m
kube-prometheus-stack-operator-5895df479f-ttm47            1/1     Running   0          5m
kube-prometheus-stack-prometheus-node-exporter-t9q7s       1/1     Running   0          5m
kube-prometheus-stack-prometheus-node-exporter-x6vfb       1/1     Running   0          5m
prometheus-kube-prometheus-stack-prometheus-0              2/2     Running   0          5m

Acceso a Grafana

Grafana se expone a través de un equilibrador de carga de aplicación (ALB) de AWS orientado a Internet, restringido al CIDR que haya establecido en var.my_cidr. Imprima la URL del equilibrador de carga (espere uno o dos minutos para que el ALB se aprovisione):

echo "http://$(kubectl get ingress kube-prometheus-stack-grafana -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')"

Abra la dirección URL en el navegador. Inicie sesión con el nombre de usuario admin y la contraseña con el siguiente comando:

kubectl --namespace monitoring get secrets kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo

Verificación de la canalización de métricas

Para comprobar que la canalización de métricas funciona de principio a fin:

  1. Vaya a Conexiones > Orígenes de datos y confirme que Amazon-Managed-Prometheus aparezca como origen de datos predeterminado.

    Validación del origen de datos de AMP en Grafana

    Página de conexiones de Grafana que muestra Amazon-Managed-Prometheus como origen de datos predeterminado
  2. Vaya a Desglose > Métricas y busque la métrica up. Debería ver los resultados de los objetivos de raspado del clúster.

    Validación de la métrica up en Grafana

    Página de métricas de desglose de Grafana que muestra la métrica ascendente con barras de estado verdes que indican los objetivos de raspado activos

Si up muestra resultados, la canalización (clúster → Prometheus → AMP → Grafana) está funcionando.

Validación de métricas de GPU de DCGM

El DaemonSet del exportador DCGM se ejecuta en nodos de GPU e informa sobre el uso de la GPU, la memoria, la temperatura, el consumo de energía, el ancho de banda de NVLink y las métricas de actividad del tensor.

Verificación del DaemonSet del Exportador DCGM:

kubectl get daemonset dcgm-exporter -n monitoring

Una vez que el nodo de GPU esté en ejecución (desde el paso 2 o el paso 4), debería ver uno o más pods listos. Para validar las métricas de DCGM, vaya a Desglose > Métricas en Grafana y busque DCGM_.

Validación de las métricas de DCGM en Grafana

Página de métricas desglosadas de Grafana filtrada por DCGM_ que muestra las métricas de GPU, como DCGM_FI_DEV_ECC_SBE_VOL_TOTAL, DCGM_FI_DEV_ENC_UTIL, DCGM_FI_DEV_FB_FREE y DCGM_FI_DEV_FB_USED

Para ver el panel, vaya a Paneles > Supervisión de GPU > Panel del Exportador DCGM de NVIDIA.

Panel del Exportador DCGM de NVIDIA en Grafana

Panel del Exportador DCGM de NVIDIA de Grafana que muestra los paneles Uso de la GPU, Temperatura media de la GPU, Memoria de framebuffer de GPU usado y Potencia total de la GPU

Bucket de S3 de pesos de los modelos

Terraform ya creó un bucket de Amazon S3 para almacenar los pesos del modelo, una ServiceAccount model-storage-sa en el espacio de nombres default, una política de IAM basada en el bucket y una asociación de Pod Identity de EKS que las vincula. Los pods de carga de trabajo que establezcan serviceAccountName: model-storage-sa podrán leer desde el bucket y escribir en él.

Verificación del bucket

Recupere el nombre del bucket de las salidas de Terraform:

MODEL_BUCKET=$(terraform output -raw model_bucket) echo ${MODEL_BUCKET}

Verifique que el bucket exista:

aws s3api head-bucket --bucket ${MODEL_BUCKET}

Resultado previsto:

{
    "BucketArn": "arn:aws:s3:::ai-eks-docs-models-20250612abc1",
    "BucketRegion": "us-east-2",
    "AccessPointAlias": false
}

Ejecute un pod único con la imagen de la AWS CLI, mediante la ServiceAccount model-storage-sa, para confirmar que Pod Identity de EKS esté conectado y que el acceso a S3 funcione:

cat << EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: s3-test labels: guide: ai-eks-docs spec: serviceAccountName: model-storage-sa containers: - name: aws-cli image: public.ecr.aws/aws-cli/aws-cli:2.27.0 command: - sh - -c - | echo "=== Caller Identity ===" aws sts get-caller-identity echo "" echo "=== S3 Write Test ===" echo "pod identity works" | aws s3 cp - s3://${MODEL_BUCKET}/test.txt echo "" echo "=== S3 List Test ===" aws s3 ls s3://${MODEL_BUCKET}/ echo "" echo "=== S3 Delete Test ===" aws s3 rm s3://${MODEL_BUCKET}/test.txt restartPolicy: Never EOF

Espere a que el pod se complete y compruebe los registros:

kubectl wait --for=jsonpath='{.status.phase}'=Succeeded pod/s3-test --timeout=300s kubectl logs s3-test

Resultado previsto:

=== Caller Identity ===
{
    "UserId": "AROA...:eks-ai-eks-docs-model-s-...",
    "Account": "123456789012",
    "Arn": "arn:aws:sts::123456789012:assumed-role/ai-eks-docs-models-.../eks-ai-eks-docs-..."
}

=== S3 Write Test ===
upload: - to s3://ai-eks-docs-models-20250612abc1/test.txt

=== S3 List Test ===
2026-07-15 12:00:00         19 test.txt

=== S3 Delete Test ===
delete: s3://ai-eks-docs-models-20250612abc1/test.txt

La identidad del intermediario confirma que el pod asumió el rol de almacenamiento del modelo a través de Pod Identity de EKS. Los comandos de S3 confirman el acceso de lectura y escritura.

Elimine el pod de prueba:

kubectl delete pod s3-test

Siguientes pasos

Con el clúster listo, puede pasar al modelo de carga y servicio para implementar un modelo de lenguaje de gran tamaño e interactuar con el punto de conexión de inferencia.

Eliminación

sugerencia

Si planea continuar con las siguientes secciones de esta guía, omita la limpieza completa. Ejecútela solo cuando haya terminado.

Elimine las cargas de trabajo de prueba para que ningún pod contenga nodos de GPU:

kubectl delete pod nvidia-smi --ignore-not-found kubectl delete deployment gpu-overflow-test --ignore-not-found

Si solo quiere liberar la ODCR y recurrir a la capacidad de spot y bajo demanda, vuelva a cambiar la variable nodepools a la estrategia spot-ondemand:

terraform apply -var 'nodepools={"spot-ondemand"={}}'

Esto quita reserved de los requisitos de tipo de capacidad de NodePool, elimina la ODCR y deja el clúster, la pila de supervisión y el bucket de S3 en su lugar.

importante

La cancelación de una reserva no termina las instancias que ya se ejecutan en ella. Esas instancias continúan en ejecución según las tarifas bajo demanda estándar hasta que se terminan. Elimine antes las cargas de trabajo de GPU, como se muestra anteriormente, para que el nodo reservado se vacíe antes de que se libere la reserva.

Vacíe los nodos administrados por Karpenter antes de eliminarlos, de modo que ningún ciclo de vida de nodo aplicado bloquee la eliminación. Elimine todos los PodDisruptionBudgets que puedan impedir un vaciado y, a continuación, elimine las NodeClaims:

kubectl delete pdb -A --all --ignore-not-found kubectl delete nodeclaim --all --wait=true --timeout=900s

A continuación, elimine todo lo que creó Terraform, incluido el clúster de EKS, la VPC, la pila de supervisión, los NodePools, las NodeClasses, el bucket del modelo de S3 y cualquier ODCR:

terraform destroy
aviso

El bucket de S3 de pesos del modelo se crea con force_destroy = true, por lo que terraform destroy elimina el bucket junto con los pesos del modelo que haya cargado en él. Copie antes todo lo que desee conservar en otra ubicación.

nota

El repositorio también envía un auxiliar scripts/cleanup.sh que ejecuta los pasos de vaciado y eliminación anteriores y, a continuación, limpia los volúmenes de EBS huérfanos etiquetados con el nombre del clúster. Ejecútelo desde el directorio terraform/<mode>/ desde el que hizo la aplicación y pase --auto-approve para omitir la petición de confirmación de Terraform.

Confirme que no queda ninguna reserva de capacidad activa para el clúster:

aws ec2 describe-capacity-reservations \ --filters "Name=state,Values=active" "Name=tag:nodepool,Values=reserved-spot-ondemand" \ --query 'CapacityReservations[].CapacityReservationId' \ --output text

Un resultado vacío significa que no hay ninguna reserva activa y no se aplican más cargos.