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 la CLI
sugerencia
Regístrese
En esta sección, se explican los pasos necesarios para crear la infraestructura necesaria para ejecutar cargas de trabajo de formación o inferencia en Amazon EKS mediante comandos de la CLI. 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
Arquitectura y flujo de trabajo de alto nivel
En el diagrama se muestra la arquitectura de alto nivel de AWS para la configuración de esta sección. Los pasos numerados de la derecha indican el orden en el que se completa la configuración en los pasos siguientes.
Requisitos previos
-
kubectl>= 1.35. Para obtener instrucciones de configuración, consulte Configuración de kubectl y eksctl. -
AWS CLI >= 2.27. Para obtener instrucciones de configuración, consulte Instalación.
-
Helm >= 3.14. Para obtener instrucciones de configuración, consulte Configuración de Helm.
-
jq. Para obtener instrucciones de configuración, consulte Descarga de jq. -
eksctl>= 0.227.0. Para obtener instrucciones de configuración, consulte Instalaciónen la documentación de eksctl.
Verifique la versión de eksctl:
eksctl version
Si tiene una versión anterior a la 0.227.0, siga la guía de instalación de eksctl
Configuración de las variables de entorno
Mantenga la coherencia entre el nombre del clúster y la región de AWS siguientes a lo largo de estos pasos. Si se cambia, es posible que los siguientes comandos se dirijan al clúster de EKS incorrecto.
export CLUSTER_NAME=ai-eks-docs export AWS_REGION=us-east-2
El uso de todas las AZ disponibles mejora la tolerancia a errores y aumenta las posibilidades de obtener la capacidad de GPU:
export AZS=$(aws ec2 describe-availability-zones \ --region ${AWS_REGION} \ --query "AvailabilityZones[?ZoneId!='use1-az3' && ZoneId!='usw1-az2' && ZoneId!='cac1-az3'].ZoneName" \ --output text | tr '\t' ',') echo $AZS
importante
Las zonas de disponibilidad use1-az3, usw1-az2 y cac1-az3 están excluidas porque Amazon EKS no admite la colocación de planos de control en esas zonasUnsupportedAvailabilityZoneException.
Resultado previsto:
us-east-2a,us-east-2b,us-east-2c
Las AZ de la salida variarán según la región. En este ejemplo, se muestran las AZ disponibles para la región us-east-2.
Creación de NodePool de GPU y clúster
En esta sección, se proporcionan dos rutas para crear el clúster de EKS y los nodos habilitados para GPU, como se muestra en el siguiente diagrama. Elija solo una opción a lo largo de la guía.
-
Modo automático de EKS: 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: en un clúster de EKS sin el modo automático de EKS, es su responsabilidad instalar y configurar 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.
Opciones de clústeres de EKS: modo automático de EKS y Karpenter autoadministrado
En cada uno de los pasos siguientes, elija una ruta (modo automático de EKS o Karpenter) y sígala en todo momento. Después de completar los pasos de la ruta elegida, dispondrá de un clúster de EKS con NodePool de GPU listo para programar las cargas de trabajo de la GPU.
Paso 1: crear el clúster
Comience por crear su clúster de EKS e instalar los componentes del clúster necesarios para las cargas de trabajo de GPU.
Con el modo automático de EKS, un solo comando eksctl create cluster --enable-auto-mode proporciona un clúster de EKS listo para las cargas de trabajo de la GPU.
En el caso de Karpenter autoadministrado, el comando eksctl create cluster proporciona los complementos principales de red y, a continuación, es necesario adoptar medidas adicionales para permitir la reparación automática de los nodos mediante una puerta de la característica de Karpenter, instalar el agente de supervisión de nodos de EKS e instalar el complemento para dispositivo de NVIDIA.
aviso
Tanto para las rutas del modo automático de EKS como las de Karpenter autoadministrado, la reparación automática de nodos se comporta de la misma manera en los nodos aprovisionados por NodePools. La reparación automática de nodos en el modo automático de EKS y Karpenter es un método de interrupción forzosa que evita PodDisruptionBudgets, la anotación karpenter.sh/do-not-disrupt y terminationGracePeriod. La reparación automática de nodos espera 10 minutos antes de sustituir un nodo con la condición AcceleratedHardwareReady establecida en False y 30 minutos si se trata de otras condiciones de reparación.
Paso 2: creación de NodePool de GPU dinámica
Defina un NodePool que aprovisione dinámicamente las instancias de GPU de la familia G con una generación superior a 4 mediante la capacidad de spot con un uso bajo demanda como alternativa. Tanto las rutas del modo automático de EKS como las de Karpenter utilizan la misma API de NodePool, con la única diferencia de la NodeClass a la que apunta. En el modo automático de EKS, la NodeClass default agrupada ya selecciona la AMI correcta y configura la extracción paralela de SOCI, por lo que NodePool es el único objeto que se crea. En Karpenter autoadministrado, también se necesita una EC2NodeClass personalizada que fije la AMI y ajuste SOCI.
Valide que se haya creado el NodePool:
kubectl get nodepool gpu-inf
Resultado previsto:
NAME NODECLASS NODES READY AGE gpu-inf default 0 True 8s
En la ruta de Karpenter autoadministrado, la columna NODECLASS muestra gpu-inf en lugar de default.
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: 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 restartPolicy: OnFailure EOF
Compruebe que el pod se haya programado y completado correctamente.
kubectl get pods
Resultado previsto:
NAME READY STATUS RESTARTS AGE nvidia-smi 0/1 Completed 0 67s
Si aparece ESTADO: Completado, significa que el comando nvidia-smi se ejecutó y se cerró. Compruebe los registros del pod para ver la GPU detectada por el nodo.
kubectl logs nvidia-smi
Resultado previsto:
+-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 580.126.09 Driver Version: 580.126.09 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 RTX PRO 6000 Blac... On | 00000000:2B:00.0 Off | 0 | | N/A 30C P0 81W / 600W | 0MiB / 97887MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+
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 G7e que tiene una GPU Blackwell NVIDIA RTX PRO 6000 con 96 GB de memoria. 30 °C es la temperatura actual de la GPU y P0 significa que la GPU se encuentra en su estado de máximo rendimiento (inactiva, pero lista). El valor de 81 W o 600 W muestra el consumo de energía actual en comparación con la capacidad máxima de energía, mientras que 0 MiB o 97 887 MiB muestra el consumo actual de la GPU en comparación con el total disponible. Como el pod acaba de ejecutar nvidia-smi y se ha cerrado, no hay carga de trabajo que utilice la GPU, por lo que la memoria está a 0 y la alimentación está inactiva. La versión del controlador de GPU de NVIDIA (580.126.09) proviene de la AMI de Bottlerocket, mientras que la versión de CUDA (13.0) proviene de la imagen del contenedor. El modelo de GPU y la memoria variarán según el tipo de instancia que seleccione Karpenter. Las instancias G5 tienen GPU NVIDIA A10G (24 GB), las instancias G6e tienen GPU NVIDIA L40S (48 GB) y las instancias G7e tienen GPU NVIDIA RTX PRO 6000 (96 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 po nvidia-smi
Resultado previsto:
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 60s default-scheduler 0/2 nodes are available: 2 node(s) had untolerated taint(s). no new claims to deallocate, preemption: 0/2 nodes are available: 2 Preemption is not helpful for scheduling. Normal Nominated 59s eks-auto-mode/compute Pod should schedule on: nodeclaim/gpu-inf-vxcnj Normal Scheduled 24s default-scheduler Successfully assigned default/nvidia-smi to i-0fb17a09bc4203164 Warning FailedCreatePodSandBox 21s kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "7f85e25b220c8fb245187758dbbbc8efb3d40f3e49e13054404880daf4c3b2f0": plugin type="aws-cni" name="aws-cni" failed (add): add cmd: failed to setup network policy Normal Pulling 7s kubelet spec.containers{nvidia-smi}: Pulling image "public.ecr.aws/amazonlinux/amazonlinux:2023-minimal" Normal Pulled 5s kubelet spec.containers{nvidia-smi}: Successfully pulled image "public.ecr.aws/amazonlinux/amazonlinux:2023-minimal" in 1.237s (1.237s including waiting). Image size: 37442701 bytes. Normal Created 5s kubelet spec.containers{nvidia-smi}: Container created Normal Started 5s kubelet spec.containers{nvidia-smi}: Container started
Estos eventos muestran la secuencia de programación del pod: el pod inicialmente no se programa porque no existen nodos de GPU (FailedScheduling), Karpenter nomina un nuevo NodeClaim (nominado), el programador asigna el pod una vez que el nodo está listo (programado) y, a continuación, se extrae e inicia la imagen del contenedor. El modo automático de EKS viene con la extracción paralela de SOCI (Seekable OCI) instalada y configurada de fábrica en las instancias G, P y Trn. Tenga en cuenta que, debido a la extracción paralela de SOCI, la imagen del contenedor se extrajo de ECR en menos de 2 segundos (1,237 s).
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
Salida esperada de NodeClaim:
NAME TYPE CAPACITY ZONE NODE READY AGE gpu-inf-xxxxx g7e.2xlarge spot us-east-2a i-0xxxxxxxxxxxx True 2m
El tipo de instancia y la AZ variarán. Todas las instancias de la familia G de generación superior a 4 son aptas.
La advertencia FailedCreatePodSandBox en kubectl describe pod nvidia-smi es transitoria y esperada. CNI de VPC se inicializa de forma asíncrona una vez que el nodo se une y el kubelet lo vuelve a intentar automáticamente. Si el pod permanece en ContainerCreating, compruebe los eventos del nodo con kubectl describe node <node-name>.
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 aparecerán 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)
Para utilizar primero la capacidad reservada con la reserva de spot o bajo demanda, cree un ODCR, adjúntelo a su NodeClass y, a continuación, actualice el NodePool dinámico del paso 2 para permitir también la capacidad reserved. La llamada a la API de reserva es la misma para ambas rutas; el adjunto de NodeClass es diferente porque el modo automático de EKS y Karpenter autoadministrado utilizan diferentes tipos de NodeClass.
aviso
El siguiente comando genera un cargo por el tipo de instancia reservada hasta que lo cancele con aws ec2 cancel-capacity-reservation --capacity-reservation-id <id>.
Cree la reserva de capacidad:
CR_AZ="us-east-2a" INSTANCE_TYPE="g6e.4xlarge" aws ec2 create-capacity-reservation \ --instance-type $INSTANCE_TYPE \ --instance-platform Linux/UNIX \ --availability-zone "$CR_AZ" \ --instance-count 1 \ --instance-match-criteria open \ --end-date-type unlimited
Si se produce un error InsufficientInstanceCapacity, cambie CR_AZ a una AZ diferente y vuelva a intentarlo.
Busque el ID de reserva de capacidad y guárdelo en una variable del intérprete de comandos para los siguientes pasos:
CAPACITY_RESERVATION_ID=$(aws ec2 describe-capacity-reservations \ --filters "Name=state,Values=active" "Name=instance-type,Values=${INSTANCE_TYPE}" \ --query 'CapacityReservations[0].CapacityReservationId' \ --output text \ --region ${AWS_REGION}) echo "Capacity reservation ID: ${CAPACITY_RESERVATION_ID}"
A continuación, aplique los cambios de NodeClass y NodePool a la ruta:
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.
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. El ODCR es para 1 instancia, 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 y no lancen 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-59b97944fb-lq56c 1/1 Running 0 2m42s 192.168.186.240 i-057692590480155da <none> <none> gpu-overflow-test-59b97944fb-z4zcx 1/1 Running 0 2m42s 192.168.130.64 i-0521ecd1849fa0578 <none> <none>
Los dos pods se están ejecutando, cada uno en un nodo diferente.
Compruebe NodeClaims para ver los tipos de capacidad:
kubectl get nodeclaims
Resultado previsto:
NAME TYPE CAPACITY ZONE NODE READY AGE gpu-inf-shg5w g6e.xlarge reserved us-east-2a i-0ea91fdeef65b8cb6 True 2m2s gpu-inf-ssnqf g7e.2xlarge spot us-east-2b i-00ccf7ce65cf3f6ca True 112s
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
Instale una pila de supervisión que recopile métricas de clústeres, nodos y GPU en Amazon Managed Service para Prometheus (AMP) y visualícelas con Grafana. El gráfico de Helm kube-prometheus-stack implementa Prometheus para raspar y escribir métricas de forma remota en AMP, además de Grafana autoadministrado para los paneles. El Exportador DCGM de NVIDIA agrega métricas específicas de la GPU (uso, memoria, temperatura, potencia, NVLink y actividad de los tensores).
Prometheus, Grafana y el operador aterrizan en nodos que no son de la GPU de forma predeterminada, ya que los nodos de la GPU incluyen el taint nvidia.com/gpu:NoSchedule. Tanto el exportador de nodos como el Exportador DCGM se ejecutan en nodos de GPU, por lo que podemos raspar las métricas del host y de la GPU de toda la flota.
Si abrió un terminal nuevo, establezca el nombre y la región del clúster:
export CLUSTER_NAME=ai-eks-docs export AWS_REGION=us-east-2
Creación del espacio de trabajo de AMP
Cree un espacio de trabajo de AMP para almacenar las métricas:
aws amp create-workspace \ --alias "amp-ws-${CLUSTER_NAME}" \ --region ${AWS_REGION}
Obtenga el ID de espacio de trabajo:
AMP_WORKSPACE_ID=$(aws amp list-workspaces \ --alias "amp-ws-${CLUSTER_NAME}" \ --query 'workspaces[0].workspaceId' \ --output text \ --region ${AWS_REGION}) echo "AMP Workspace ID: ${AMP_WORKSPACE_ID}"
Obtenga el punto de conexión de escritura remota:
AMP_ENDPOINT=$(aws amp describe-workspace \ --workspace-id ${AMP_WORKSPACE_ID} \ --query 'workspace.prometheusEndpoint' \ --output text \ --region ${AWS_REGION}) echo "AMP Endpoint: ${AMP_ENDPOINT}"
Creación de asociaciones de Pod Identity de EKS y política de IAM
Cree una política de IAM que permita a Prometheus escribir métricas de forma remota y a Grafana consultarlas:
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) AMP_POLICY_ARN=$(aws iam create-policy \ --policy-name "${CLUSTER_NAME}-amp-grafana-policy" \ --policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Sid\": \"AllowAMPReadWrite\", \"Effect\": \"Allow\", \"Action\": [\"aps:ListWorkspaces\", \"aps:DescribeWorkspace\", \"aps:GetMetricMetadata\", \"aps:GetSeries\", \"aps:QueryMetrics\", \"aps:RemoteWrite\", \"aps:GetLabels\"], \"Resource\": \"arn:aws:aps:${AWS_REGION}:${ACCOUNT_ID}:workspace/*\"}, {\"Sid\": \"AllowCloudWatchMetrics\", \"Effect\": \"Allow\", \"Action\": [\"cloudwatch:DescribeAlarmsForMetric\", \"cloudwatch:ListMetrics\", \"cloudwatch:GetMetricData\", \"cloudwatch:GetMetricStatistics\"], \"Resource\": \"*\"}]}" \ --query 'Policy.Arn' \ --output text) echo "AMP Policy ARN: ${AMP_POLICY_ARN}"
Cree el espacio de nombres de supervisión y las cuentas de servicio para Prometheus y Grafana:
kubectl create namespace monitoring kubectl create serviceaccount amp-iamproxy-ingest-service-account -n monitoring kubectl create serviceaccount grafana-sa -n monitoring
Cree asociaciones de Pod Identity de EKS para vincular las cuentas de servicio a la política de IAM:
eksctl create podidentityassociation \ --cluster ${CLUSTER_NAME} \ --namespace monitoring \ --service-account-name amp-iamproxy-ingest-service-account \ --role-name "${CLUSTER_NAME}-amp-ingest-role" \ --permission-policy-arns ${AMP_POLICY_ARN} \ --region ${AWS_REGION} eksctl create podidentityassociation \ --cluster ${CLUSTER_NAME} \ --namespace monitoring \ --service-account-name grafana-sa \ --role-name "${CLUSTER_NAME}-grafana-role" \ --permission-policy-arns ${AMP_POLICY_ARN} \ --region ${AWS_REGION}
Verifique que se hayan creado ambas asociaciones de Pod Identity de EKS:
eksctl get podidentityassociation --cluster ${CLUSTER_NAME} --region ${AWS_REGION}
La salida esperada debe incluir amp-iamproxy-ingest-service-account y grafana-sa en el espacio de nombres monitoring.
Instalación de kube-prometheus-stack
Agregue el repositorio de Helm:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update
Este archivo de valores omite un nodeSelector para Prometheus, Grafana y el operador: el taint nvidia.com/gpu:NoSchedule de los nodos de la GPU los mantiene alejados de los nodos de la GPU, por lo que aterrizan en el sistema o en un grupo de uso general de forma predeterminada. El exportador de nodos utiliza una tolerancia comodín, por lo que se ejecuta en todos los nodos (incluidos los nodos de la GPU) para recopilar las métricas de toda la flota.
Cree el archivo de valores:
cat << EOF > /tmp/kube-prometheus-values.yaml prometheus: serviceAccount: create: false name: amp-iamproxy-ingest-service-account prometheusSpec: serviceAccountName: amp-iamproxy-ingest-service-account remoteWrite: - url: "${AMP_ENDPOINT}api/v1/remote_write" sigv4: region: "${AWS_REGION}" queueConfig: maxSamplesPerSend: 1000 maxShards: 200 capacity: 2500 retention: 5h scrapeInterval: 30s evaluationInterval: 30s podMonitorSelectorNilUsesHelmValues: false serviceMonitorSelectorNilUsesHelmValues: false alertmanager: enabled: false grafana: enabled: true serviceAccount: create: false name: grafana-sa grafana.ini: auth.sigv4: enabled: true sidecar: datasources: defaultDatasourceEnabled: false plugins: - grafana-amazonprometheus-datasource additionalDataSources: - name: Amazon-Managed-Prometheus type: grafana-amazonprometheus-datasource access: proxy url: "${AMP_ENDPOINT}" isDefault: true jsonData: sigV4Auth: true defaultRegion: "${AWS_REGION}" sigV4Region: "${AWS_REGION}" editable: true dashboardProviders: dashboardproviders.yaml: apiVersion: 1 providers: - name: default orgId: 1 folder: 'GPU Monitoring' type: file disableDeletion: false editable: true options: path: /var/lib/grafana/dashboards/default dashboards: default: nvidia-dcgm: gnetId: 25261 revision: 1 datasource: - name: DS_PROMETHEUS value: Amazon-Managed-Prometheus vllm: gnetId: 25263 revision: 1 datasource: - name: DS_PROMETHEUS value: Amazon-Managed-Prometheus prometheus-node-exporter: tolerations: - operator: Exists EOF
Valide que las variables se hayan rellenado correctamente:
grep -E "url:|region:|tolerations:" /tmp/kube-prometheus-values.yaml
Debería ver la URL completa del punto de conexión de AMP (comienza por https://aps-workspaces…), la región y la línea tolerations: del exportador de nodos. Si hay algo vacío, vuelva a exportar las variables y a crear el archivo.
Instale el gráfico:
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \ --namespace monitoring \ -f /tmp/kube-prometheus-values.yaml
Verifique que los pods se estén ejecutando:
kubectl get pods -n monitoring
Resultado previsto:
NAME READY STATUS RESTARTS AGE kube-prometheus-stack-grafana-7c58f54f77-rftrj 3/3 Running 0 4m kube-prometheus-stack-kube-state-metrics-d68dcbc84-5smxq 1/1 Running 0 4m kube-prometheus-stack-operator-5895df479f-ttm47 1/1 Running 0 4m kube-prometheus-stack-prometheus-node-exporter-t9q7s 1/1 Running 0 4m kube-prometheus-stack-prometheus-node-exporter-x6vfb 1/1 Running 0 4m prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0 4m
La pila incluye los siguientes componentes:
-
Prometheus (StatefulSet): raspa métricas y las escribe de forma remota en AMP
-
Grafana: paneles y visualización, preconfigurados con el origen de datos de AMP
-
kube-state-metrics: genera métricas sobre el estado de objetos de Kubernetes (estado del Pod, límites y solicitudes de recursos, estados de NodeClaim)
-
node-exporter (DaemonSet, uno por nodo): recopila métricas de host (CPU, memoria, disco, red)
-
operador: administra los recursos personalizados de Prometheus y Alertmanager
Alertmanager está deshabilitado en esta configuración.
Acceso a Grafana
Abra un terminal y un puerto de reenvío separados para acceder a Grafana:
kubectl port-forward svc/kube-prometheus-stack-grafana 3000:80 -n monitoring
Abra http://localhost:3000admin 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
Para comprobar que la canalización de métricas funciona de principio a fin:
-
Vaya a Conexiones > Orígenes de datos y confirme que
Amazon-Managed-Prometheusaparezca como origen de datos predeterminado.Validación del origen de datos de AMP en Grafana
-
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
upen Grafana
Si up muestra resultados, la canalización (clúster → Prometheus → AMP → Grafana) está funcionando.
Implementación del Exportador DCGM para las métricas de la GPU
kube-prometheus-stack recopila métricas de CPU y memoria de nodo, pero no métricas de GPU. El Exportador DCGM de NVIDIA agrega el uso de GPU, el uso de memoria, la temperatura, el consumo de energía, el ancho de banda de NVLink y la actividad de los tensores.
helm repo add gpu-helm-charts https://nvidia.github.io/dcgm-exporter/helm-charts helm repo update
Establezca la clave del selector de nodos de GPU para la ruta. El modo automático de EKS y Karpenter autoadministrado utilizan diferentes etiquetas según el fabricante de la GPU.
Cree el archivo de valores del Exportador DCGM:
cat << EOF > /tmp/dcgm-exporter-values.yaml resources: requests: memory: "512Mi" cpu: "100m" limits: memory: "1Gi" cpu: "500m" serviceMonitor: enabled: true additionalLabels: release: kube-prometheus-stack nodeSelector: ${GPU_NODE_SELECTOR_KEY}: nvidia tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" customMetrics: | # Clocks DCGM_FI_DEV_SM_CLOCK, gauge, SM clock frequency (in MHz). DCGM_FI_DEV_MEM_CLOCK, gauge, Memory clock frequency (in MHz). # Temperature DCGM_FI_DEV_MEMORY_TEMP, gauge, Memory temperature (in C). DCGM_FI_DEV_GPU_TEMP, gauge, GPU temperature (in C). # Power DCGM_FI_DEV_POWER_USAGE, gauge, Power draw (in W). DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION, counter, Total energy consumption since boot (in mJ). # PCIe DCGM_FI_PROF_PCIE_TX_BYTES, counter, Number of bytes transmitted through PCIe TX (in KB) via NVML. DCGM_FI_PROF_PCIE_RX_BYTES, counter, Number of bytes received through PCIe RX (in KB) via NVML. DCGM_FI_DEV_PCIE_REPLAY_COUNTER, counter, Total number of PCIe retries. # Utilization (the sample period varies depending on the product) DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). DCGM_FI_DEV_MEM_COPY_UTIL, gauge, Memory utilization (in %). DCGM_FI_DEV_ENC_UTIL, gauge, Encoder utilization (in %). DCGM_FI_DEV_DEC_UTIL, gauge, Decoder utilization (in %). # Errors and violations DCGM_FI_DEV_XID_ERRORS, gauge, Value of the last XID error encountered. DCGM_EXP_XID_ERRORS_COUNT, gauge, Value of count of XID errors encountered. DCGM_FI_DEV_POWER_VIOLATION, counter, Throttling duration due to power constraints (in us). DCGM_FI_DEV_THERMAL_VIOLATION, counter, Throttling duration due to thermal constraints (in us). DCGM_FI_DEV_SYNC_BOOST_VIOLATION, counter, Throttling duration due to sync-boost constraints (in us). DCGM_FI_DEV_BOARD_LIMIT_VIOLATION, counter, Throttling duration due to board limit constraints (in us). DCGM_FI_DEV_LOW_UTIL_VIOLATION, counter, Throttling duration due to low utilization (in us). DCGM_FI_DEV_RELIABILITY_VIOLATION, counter, Throttling duration due to reliability constraints (in us). # Memory usage DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). DCGM_FI_DEV_FB_USED, gauge, Framebuffer memory used (in MiB). # Retired pages DCGM_FI_DEV_RETIRED_SBE, counter, Total number of retired pages due to single-bit errors. DCGM_FI_DEV_RETIRED_DBE, counter, Total number of retired pages due to double-bit errors. DCGM_FI_DEV_RETIRED_PENDING, counter, Total number of pages pending retirement. # NVLink DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL, counter, Total number of NVLink bandwidth counters for all lanes. DCGM_FI_PROF_NVLINK_TX_BYTES, counter, The rate of data transmitted over NVLink not including protocol headers in bytes per second. DCGM_FI_PROF_NVLINK_RX_BYTES, counter, The rate of data received over NVLink not including protocol headers in bytes per second. # DCP metrics DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned (in %). DCGM_FI_PROF_SM_OCCUPANCY, gauge, The ratio of number of warps resident on an SM (in %). DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, Ratio of cycles the tensor (HMMA) pipe is active (in %). DCGM_FI_PROF_DRAM_ACTIVE, gauge, Ratio of cycles the device memory interface is active sending or receiving data (in %). DCGM_FI_DEV_CLOCK_THROTTLE_REASONS, gauge, Current clock throttle reasons (bitmask of DCGM_CLOCKS_THROTTLE_REASON_*). DCGM_FI_DEV_GPU_NVLINK_ERRORS, gauge, Identifies a GPU NVLink error type returned by DCGM_FI_DEV_GPU_NVLINK_ERRORS. ## NVLink DCGM_FI_DEV_NVLINK_BANDWIDTH_L0, counter, The number of bytes of active NVLink rx or tx data including both header and payload. ## Remapped rows DCGM_FI_DEV_UNCORRECTABLE_REMAPPED_ROWS, counter, Number of remapped rows for uncorrectable errors. DCGM_FI_DEV_CORRECTABLE_REMAPPED_ROWS, counter, Number of remapped rows for correctable errors. DCGM_FI_DEV_ROW_REMAP_FAILURE, gauge, whether remapping of rows has failed. ## Profiling metrics DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipes are active (in %). DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipes are active (in %). DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipes are active (in %). # ECC DCGM_FI_DEV_ECC_SBE_VOL_TOTAL, counter, Total number of single-bit volatile ECC errors. DCGM_FI_DEV_ECC_DBE_VOL_TOTAL, counter, Total number of double-bit volatile ECC errors. EOF
El campo customMetrics anula el conjunto de métricas predeterminado del Exportador DCGM por uno ampliado que incluye el ancho de banda de NVLink, la actividad de los tensores, el rendimiento de PCIe, los errores de ECC y la limitación térmica. Para las cargas de trabajo de inferencia, esto le permite comprender si las unidades de computación de la GPU se utilizan al máximo, si la GPU está inactiva entre las solicitudes debido al bajo tamaño de los lotes, si la transferencia de datos entre la CPU y la GPU es un cuello de botella, si la limitación térmica provoca picos de latencia y cuánto espacio de memoria queda en la GPU para lotes más grandes.
Instale el Exportador DCGM:
helm install dcgm-exporter gpu-helm-charts/dcgm-exporter \ --namespace monitoring \ -f /tmp/dcgm-exporter-values.yaml
Las tolerations permiten al exportador ejecutarse en los nodos con taint de GPU que aprovisionó en el paso 2. La opción serviceMonitor con la etiqueta release: kube-prometheus-stack garantiza que Prometheus lo detecte y raspe automáticamente.
Verificación del DaemonSet del Exportador DCGM:
kubectl get daemonset dcgm-exporter -n monitoring
Una vez que se ejecute un nodo de la GPU, debería ver un pod listo para usar. 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
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
Bucket de S3 de pesos de los modelos
Cree un bucket de Amazon S3 para almacenar los pesos de los modelos y configure una asociación de Pod Identity de EKS para que los pods de carga de trabajo puedan leer y escribir en él.
Si abrió un terminal nuevo, establezca el nombre y la región del clúster:
export CLUSTER_NAME=ai-eks-docs export AWS_REGION=us-east-2
Crear el bucket de S3
Cree el bucket con un sufijo aleatorio para evitar colisiones de nombres:
BUCKET_SUFFIX=$(head -c 4 /dev/urandom | od -An -tx1 | tr -d ' \n') MODEL_BUCKET="${CLUSTER_NAME}-models-${BUCKET_SUFFIX}" aws s3 mb s3://${MODEL_BUCKET} --region ${AWS_REGION}
Los buckets de S3 creados después de enero de 2023 tienen habilitados de forma predeterminada el cifrado del servidor (AES256) y el bloqueo del acceso público.
Configuración de Pod Identity de EKS para el acceso a S3
Cree una ServiceAccount model-storage-sa en el espacio de nombres default, una política de IAM que se ajuste al bucket de modelos y una asociación de Pod Identity de EKS que las vincule. Los pods de carga de trabajo que establezcan serviceAccountName: model-storage-sa podrán leer y escribir en el bucket.
kubectl create serviceaccount model-storage-sa
Cree la política de IAM:
POLICY_ARN=$(aws iam create-policy \ --policy-name "${CLUSTER_NAME}-model-storage-policy" \ --policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Action\": [\"s3:GetObject\", \"s3:PutObject\", \"s3:ListBucket\", \"s3:DeleteObject\"], \"Resource\": [\"arn:aws:s3:::${MODEL_BUCKET}\", \"arn:aws:s3:::${MODEL_BUCKET}/*\"]}]}" \ --query 'Policy.Arn' \ --output text) echo "Policy ARN: ${POLICY_ARN}"
nota
Esta política concede s3:DeleteObject y s3:PutObject para el paso de validación. Para los pods de inferencia de producción que solo leen los pesos de los modelos, elimine s3:PutObject y s3:DeleteObject siga el privilegio mínimo.
Cree la asociación de Pod Identity de EKS. eksctl crea el rol de IAM con la política de confianza correcta y lo vincula a la ServiceAccount:
eksctl create podidentityassociation \ --cluster ${CLUSTER_NAME} \ --namespace default \ --service-account-name model-storage-sa \ --role-name "${CLUSTER_NAME}-model-storage-role" \ --permission-policy-arns ${POLICY_ARN} \ --region ${AWS_REGION}
Verifique la asociación:
eksctl get podidentityassociation --cluster ${CLUSTER_NAME} --region ${AWS_REGION}
La salida debe incluir la asociación model-storage-sa en el espacio de nombres default.
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-model-storage-role/eks-ai-eks-docs-model-s-..."
}
=== S3 Write Test ===
upload: - to s3://ai-eks-docs-models-01234567/test.txt
=== S3 List Test ===
2026-05-04 12:00:00 19 test.txt
=== S3 Delete Test ===
delete: s3://ai-eks-docs-models-01234567/test.txtLa identidad de la persona que llama confirma que el pod asumió el rol ${CLUSTER_NAME}-model-storage-role 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.
export CLUSTER_NAME=ai-eks-docs export AWS_REGION=us-east-2
kubectl delete pod nvidia-smi --ignore-not-found kubectl delete deployment gpu-overflow-test --ignore-not-found
Si ha creado una ODCR, cancélelo primero:
INSTANCE_TYPE="g6e.4xlarge" CAPACITY_RESERVATION_ID=$(aws ec2 describe-capacity-reservations \ --filters "Name=state,Values=active" "Name=instance-type,Values=${INSTANCE_TYPE}" \ --query 'CapacityReservations[0].CapacityReservationId' \ --output text \ --region ${AWS_REGION}) aws ec2 cancel-capacity-reservation --capacity-reservation-id ${CAPACITY_RESERVATION_ID}
importante
La cancelación de una reserva no termina las instancias en ejecución. Se mantienen con las tasas bajo demanda estándar hasta que se terminen. Elimine antes la implementación para vaciar el nodo reservado antes de cancelarlo.
Busque el ARN de la política de IAM:
AMP_POLICY_ARN=$(aws iam list-policies \ --scope Local \ --query "Policies[?PolicyName=='${CLUSTER_NAME}-amp-grafana-policy'].Arn" \ --output text) echo "AMP Policy ARN: ${AMP_POLICY_ARN}"
Busque el ID del espacio de trabajo de AMP:
AMP_WORKSPACE_ID=$(aws amp list-workspaces \ --alias "amp-ws-${CLUSTER_NAME}" \ --query 'workspaces[0].workspaceId' \ --output text \ --region ${AWS_REGION}) echo "AMP Workspace ID: ${AMP_WORKSPACE_ID}"
Desinstale la versión de Helm del Exportador DCGM:
helm uninstall dcgm-exporter -n monitoring
Desinstale la versión de Helm de kube-prometheus-stack:
helm uninstall kube-prometheus-stack -n monitoring
Elimine la asociación de Pod Identity de EKS para la cuenta de servicio de ingesta de Prometheus:
eksctl delete podidentityassociation \ --cluster ${CLUSTER_NAME} \ --namespace monitoring \ --service-account-name amp-iamproxy-ingest-service-account \ --region ${AWS_REGION}
Elimine la asociación de Pod Identity de EKS para la cuenta de servicio de Grafana:
eksctl delete podidentityassociation \ --cluster ${CLUSTER_NAME} \ --namespace monitoring \ --service-account-name grafana-sa \ --region ${AWS_REGION}
Elimine la política de IAM utilizada por Prometheus y Grafana:
aws iam delete-policy --policy-arn ${AMP_POLICY_ARN}
Elimine el espacio de trabajo de AMP:
aws amp delete-workspace --workspace-id ${AMP_WORKSPACE_ID} --region ${AWS_REGION}
Elimine el espacio de nombres de supervisión:
kubectl delete namespace monitoring
Busque el nombre del bucket del modelo:
MODEL_BUCKET=$(aws s3api list-buckets \ --query "Buckets[?starts_with(Name, '${CLUSTER_NAME}-models-')].Name | [0]" \ --output text) echo "Model bucket: ${MODEL_BUCKET}"
Busque el ARN de la política de IAM:
POLICY_ARN=$(aws iam list-policies \ --scope Local \ --query "Policies[?PolicyName=='${CLUSTER_NAME}-model-storage-policy'].Arn" \ --output text) echo "Policy ARN: ${POLICY_ARN}"
Elimine el bucket del modelo S3 y todos sus objetos:
aws s3 rb s3://${MODEL_BUCKET} --force
Elimine la asociación de Pod Identity de EKS:
eksctl delete podidentityassociation \ --cluster ${CLUSTER_NAME} \ --namespace default \ --service-account-name model-storage-sa \ --region ${AWS_REGION}
Elimine la política de IAM:
aws iam delete-policy --policy-arn ${POLICY_ARN}
Elimine la ServiceAccount de Kubernetes:
kubectl delete serviceaccount model-storage-sa
kubectl delete nodepool gpu-inf --ignore-not-found kubectl delete nodeclass gpu-inf --ignore-not-found kubectl delete ec2nodeclass gpu-inf --ignore-not-found eksctl delete cluster --name=$CLUSTER_NAME --region=$AWS_REGION