Ajudar a melhorar esta página
Para contribuir com este guia de usuário, escolha o link Editar esta página no GitHub, disponível no painel direito de cada página.
Configurar o cluster do Amazon EKS para workloads de IA/ML usando CLIs
dica
Registre-se
Esta seção guia você pelas etapas de criação da infraestrutura necessária para executar workloads de treinamento ou inferência no Amazon EKS por meio de comandos da CLI. As etapas incluem a criação de um cluster do EKS, nós habilitados para GPU com o Modo Automatizado do EKS ou o Karpenter, uma pilha de monitoramento com Prometheus e Grafana, e o armazenamento do Amazon S3 para os pesos do modelo.
Consulte a documentação do Modo Automático do EKS e do Karpenter
Arquitetura e fluxo de trabalho de alto nível
O diagrama ilustra a arquitetura de alto nível da AWS para a configuração desta seção. As etapas numeradas à direita indicam a ordem na qual você realiza a configuração nas etapas abaixo.
Pré-requisitos
-
kubectl>= 1.35. Consulte Configurar o kubectl e o eksctl para obter instruções de configuração. -
AWSCLI >= 2.27. Para obter instruções de configuração, consulte Instalar.
-
Helm >= 3.14. Para obter instruções de configuração, consulte Configurar o Helm.
-
jq. Para obter instruções de configuração, consulte Baixar jq. -
eksctl>= 0.227.0. Para obter instruções de instalação, consulte Installingna documentação do eksctl.
Verifique sua versão do eksctl:
eksctl version
Se você estiver usando uma versão anterior à 0.227.0, siga o eksctl installation guide
Definição de variáveis de ambiente
Mantenha o nome do cluster e a região da AWS a seguir consistentes ao longo de todas estas etapas. Alterá-los pode fazer com que os comandos subsequentes sejam direcionados ao cluster do EKS errado.
export CLUSTER_NAME=ai-eks-docs export AWS_REGION=us-east-2
O uso de todas as AZs disponíveis melhora a tolerância a falhas e aumenta as chances de obtenção de capacidade 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
As zonas de disponibilidade use1-az3, usw1-az2, e cac1-az3 são excluídas porque o Amazon EKS não é compatível com colocação do ambiente de gerenciamento nessas zonasUnsupportedAvailabilityZoneException.
Saída esperada:
us-east-2a,us-east-2b,us-east-2c
As AZs na saída variam por região. Este exemplo mostra as AZs disponíveis para a região us-east-2.
Criar cluster e NodePool de GPU
Esta seção fornece dois caminhos para a criação do cluster do EKS e de nós habilitados para GPU, mostrados no diagrama a seguir. Escolha apenas uma opção ao longo de todo o guia.
-
Modo Automático do EKS: além dos principais complementos de rede, armazenamento e balanceamento de carga, o Modo Automático do EKS inclui e gerencia os seguintes recursos para workloads de treinamento e inferência: agente de monitoramento de nós do EKS, reparo automático de nós, snapshotter SOCI
para extração rápida de contêineres e prontidão de GPU para a NodeClass padrão. O plug-in de dispositivo NVIDIA está incluído na AMI acelerada do Bottlerocket que o Modo Auomático do EKS usa para nós habilitados para GPU. -
Karpenter autogerenciado: em um cluster do EKS sem o Modo Automático do EKS, você é responsável por instalar e configurar os componentes necessários para workloads de treinamento e inferência. Isso inclui os complementos de rede (CNI da VPC, CoreDNS, kube-proxy), o Karpenter, o agente de monitoramento de nós do EKS, o plug-in de dispositivo NVIDIA e o snapshotter SOCI para extração rápida de contêineres.
Opções de cluster do EKS: Modo Automático do EKS e Karpenter autogerenciado
Em cada uma das etapas a seguir, escolha um caminho (Modo Automático do EKS, Karpenter) e siga-o sempre. Depois de concluir as etapas do caminho escolhido, você terá um cluster do EKS com um NodePool de GPU pronto para programar as workloads da GPU.
Etapa 1: Criar um cluster
Comece por criar o cluster do EKS e instalar os componentes de cluster necessários para as workloads da GPU.
Com o Modo Automático do EKS, um único comando eksctl create cluster --enable-auto-modeprovisiona um cluster do EKS pronto para as workloads da GPU.
Com o Karpenter autogerenciado, o comando eksctl create cluster provisiona os principais complementos de rede; depois, etapas adicionais são necessárias para permitir o reparo automático de nós por meio de um controle de atributo do Karpenter, instalar o agente de monitoramento de nós do EKS e instalar o plug-in de dispositivo NVIDIA.
Atenção
Tanto para o Modo Automático do EKS quanto para os caminhos autogerenciados do Karpenter, o reparo automático de nós se comporta da mesma maneira para os nós provisionados por NodePools. O reparo automático de nós no Modo Automático do EKS e no Karpenter é um método de interrupção imperativo que ignora PodDisruptionBudgets, a anotação karpenter.sh/do-not-disrupt e o terminationGracePeriod. O reparo automático do nó espera 10 minutos antes de substituir um nó com a condição AcceleratedHardwareReady definida como False e 30 minutos para outras condições de reparo.
Etapa 2: criar o NodePool dinâmico
Defina um NodePool que provisione dinamicamente as instâncias de GPU da família G com geração acima de 4, usando a capacidade spot com capacidade sob demanda como fallback. Os caminhos Modo Automático do EKS e do Karpenter usam a mesma API NodePool, sendo a única diferença o NodeClass para o qual ele aponta. No Modo Automático do EKS, a NodeClass default incluída já seleciona a AMI correta e configura a extração paralela do SOCl, portanto, o NodePool é o único objeto que você cria. No Karpenter autogerenciado, você também precisa de uma EC2NodeClass personalizada que fixe a AMI e ajuste o SOCI.
Valide que o NodePool foi criado:
kubectl get nodepool gpu-inf
Saída esperada:
NAME NODECLASS NODES READY AGE gpu-inf default 0 True 8s
No caminho autogerenciado do Karpenter, a coluna NODECLASS mostra gpu-inf em vez de default.
Etapa 3: testar com um exemplo de pod
Teste a configuração do NodePool da GPU com um 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
Verifique se o pod foi agendado e concluído com sucesso.
kubectl get pods
Saída esperada:
NAME READY STATUS RESTARTS AGE nvidia-smi 0/1 Completed 0 67s
O STATUS: Concluído significa que o comando nvidia-smi foi executado e encerrado. Verifique os logs do pod para ver a GPU detectada pelo nó.
kubectl logs nvidia-smi
Saída esperada:
+-----------------------------------------------------------------------------------------+ | 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 | +-----------------------------------------+------------------------+----------------------+
A saída mostra o modelo da GPU, a versão do driver, a versão do CUDA e a memória disponível. Neste exemplo, o Karpenter provisionou uma instância G7e que tem uma GPU NVIDIA RTX PRO 6000 Blackwell com 96 GB de memória. 30C é a temperatura atual da GPU e P0 significa que a GPU está em seu mais alto estado de performance (ociosa, mas pronta). O valor 81W/600W mostra o consumo de energia atual em relação à capacidade máxima de energia, e 0MiB/97887MiB mostra a memória atual da GPU usada em relação ao total disponível. Como o Pod acabou de executar o nvidia-smi e sair, nenhuma workload está usando a GPU, então a memória está em 0 e a alimentação está ociosa. A versão do driver de GPU NVIDIA (580.126.09) vem da AMI do Bottlerocket, enquanto a versão do CUDA (13.0) vem da imagem do contêiner. O modelo e a memória da GPU variam de acordo com o tipo de instância selecionado pelo Karpenter. As instâncias G5 têm GPUs NVIDIA A10G (24 GB), as instâncias G6e têm GPUs NVIDIA L40S (48 GB) e as instâncias G7e têm GPUs NVIDIA RTX PRO 6000 (96 GB).
Para entender como o Karpenter e o agendador do Kubernetes se coordenaram para provisionar um nó e posicionar o pod, confira os eventos do ciclo de vida do pod:
kubectl describe po nvidia-smi
Saída esperada:
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
Esses eventos mostram a sequência de agendamento do Pod: o agendamento inicial do Pod falha porque não existe nenhum nó de GPU (FailedScheduling), o Karpenter nomeia uma nova NodeClaim (Nominada), o agendador atribui o Pod quando o nó fica pronto (Agendada) e, em seguida, a imagem do contêiner é extraída e iniciada. O Modo Automático do EKS vem com a extração paralela do SOCI (Seekable OCI) instalada e configurada imediatamente nas instâncias G, P e Trn. Observe que, graças à extração paralela do SOCI, a imagem do contêiner foi extraída do ECR em menos de 2 segundos (1,237 s).
Uma NodeClaim é uma solicitação que o Karpenter cria para provisionar um nó específico. Ela mostra o tipo de instância, o tipo de capacidade, a AZ e se o nó está pronto.
kubectl get nodeclaims
Saída esperada da NodeClaim:
NAME TYPE CAPACITY ZONE NODE READY AGE gpu-inf-xxxxx g7e.2xlarge spot us-east-2a i-0xxxxxxxxxxxx True 2m
O tipo de instância e a AZ podem variar. Qualquer instância da família G com geração > 4 é elegível.
O aviso FailedCreatePodSandBox em kubectl describe pod nvidia-smi é transitório e esperado. A CNI da VPC é inicializada de maneira assíncrona após a união do nó, e o kubelet faz uma nova tentativa automaticamente. Se o pod permanecer em ContainerCreating, verifique os eventos do nó com kubectl describe node <node-name>.
dica
Se nenhum nó aparecer, verifique se há erros de capacidade insuficiente:
kubectl get events | grep InsufficientCapacityError
O Karpenter armazena em cache as ofertas indisponíveis por 3 minutos. Ampliar os tipos de instância e as AZs permitidas no NodePool aumenta as chances de conseguir capacidade.
nota
As instâncias spot iniciadas pelo Karpenter não aparecem no console Solicitações spot do EC2. O Karpenter usa a API CreateFleet do EC2 com o type: instant. As instâncias aparecem no console de instâncias do EC2 com um ciclo de vida spot.
Etapa 4: adicionar capacidade reservada ao NodePool (opcional)
Para usar capacidade reservada primeiro com o fallback spot/sob demanda, crie uma ODCR e conecte-a ao NodeClass e, em seguida, atualize o NodePool dinâmico da etapa 2 para também permitir capacidade reserved. A chamada de API de reserva é a mesma para os dois caminhos; o anexo NodeClass é diferente porque o Modo Automático do EKS e o Karpenter autogerenciado usam tipos diferentes de NodeClass.
Atenção
O comando a seguir gera uma cobrança do tipo de instância reservada até que você a cancele com aws ec2 cancel-capacity-reservation --capacity-reservation-id <id>.
Criar a reserva de capacidade:
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
Se você receber um erro InsufficientInstanceCapacity, mude CR_AZ para uma AZ diferente e tente novamente.
Procure o ID da reserva de capacidade e armazene-o em uma variável shell para as seguintes etapas:
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}"
Depois, aplique as alterações de NodeClass e NodePool para o caminho:
O Karpenter trata reserved como a opção mais econômica e a inicia primeiro. Quando a reserva está cheia, ele recorre a spot ou sob demanda.
Depois de aplicar as alterações, verifique se o Karpenter prioriza a capacidade reservada e depois recorre a spot ou sob demanda. Faça uma implantação com duas réplicas que solicite uma GPU por pod. A ODCR é para uma instância, então, o primeiro pod aciona o Karpenter para iniciar um nó reservado. O segundo pod não cabe no nó reservado e aciona o Karpenter para iniciar outro nó usando capacidade spot ou sob 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
Ao contrário do pod de teste nvidia-smi da etapa 3, que foi executado e encerrado, essa implantação mantém os pods em execução (sleep infinity) para que eles conservem a GPU e não liberem o nó.
Verifique os pods agendados em nós diferentes:
kubectl get pods -l app=gpu-overflow-test -o wide
Saída esperada:
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>
Os dois pods estão operando, cada um em um nó diferente.
Verifique as NodeClaims para ver os tipos de capacidade:
kubectl get nodeclaims
Saída esperada:
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
O nó reservado foi iniciado primeiro, seguido por um nó spot ou sob demanda quando a reserva ficou cheia.
Limpar a implantação de teste:
kubectl delete deployment gpu-overflow-test
Monitoramento
Instale uma pilha de monitoramento que colete métricas de cluster, de nó e de GPU no Amazon Managed Service for Prometheus (AMP) e visualize-as com o Grafana. O chart do Helm kube-prometheus-stack implanta o Prometheus para extrair e gravar remotamente métricas no AMP, além de um Grafana autogerenciado para painéis. O NVIDIA DCGM Exporter adiciona métricas específicas da GPU (utilização, memória, temperatura, alimentação, NVLink e atividade do tensor).
Prometheus, Grafana e o operador usam nós não de GPU por padrão porque os nós de GPU levam o taint nvidia.com/gpu:NoSchedule. O node-exporter e o DCGM Exporter são executados em nós de GPU para poderem extrair métricas de host e GPU de toda a frota.
Se você abriu um novo terminal, defina o nome e a região do cluster:
export CLUSTER_NAME=ai-eks-docs export AWS_REGION=us-east-2
Criar um espaço de trabalho do AMP
Crie um espaço de trabalho do AMP para armazenar as métricas:
aws amp create-workspace \ --alias "amp-ws-${CLUSTER_NAME}" \ --region ${AWS_REGION}
Obtenha o ID do espaço de trabalho:
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}"
Obtenha o endpoint de gravação 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}"
Criar uma política do IAM e associações do Identidade de Pods do EKS
Crie uma política do IAM que permita ao Prometheus gravar métricas remotamente e ao Grafana consultá-las:
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}"
Crie o namespace do monitoramento e as contas de serviço para o Prometheus e o Grafana:
kubectl create namespace monitoring kubectl create serviceaccount amp-iamproxy-ingest-service-account -n monitoring kubectl create serviceaccount grafana-sa -n monitoring
Crie associações do Identidade de Pods do EKS para vincular as contas do serviço à política do 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 se as ambas as associações do Identidade de Pods do EKS foram criadas:
eksctl get podidentityassociation --cluster ${CLUSTER_NAME} --region ${AWS_REGION}
A saída esperada deve incluir amp-iamproxy-ingest-service-account e grafana-sa no namespace monitoring.
Instalar o kube-prometheus-stack
Adicione o repositório do Helm:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update
Esse arquivo de valores omite um nodeSelector para o Prometheus, o Grafana e o operador: o taint nvidia.com/gpu:NoSchedule dos nós de GPU os mantém fora dos nós de GPU, então eles acabam no sistema ou ni pool de uso geral por padrão. O node-exporter usa uma tolerância curinga, então é executado em todos os nós, incluindo os nós de GPU, para coletar métricas em toda a frota.
Crie o arquivo 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 as variáveis foram preenchidas corretamente:
grep -E "url:|region:|tolerations:" /tmp/kube-prometheus-values.yaml
Você deve ver o URL completo do endpoint do AMP (começando com https://aps-workspaces…), a região e a linha tolerations: do node-exporter. Se algum desses valores estiver vazio, exporte novamente as variáveis e recrie o arquivo.
Instale o chart:
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \ --namespace monitoring \ -f /tmp/kube-prometheus-values.yaml
Verifique se os pods estão em execução:
kubectl get pods -n monitoring
Saída esperada:
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
A pilha implanta os seguintes componentes:
-
Prometheus (StatefulSet): extrai métricas e as grava remotamente no AMP
-
Grafana: painéis e visualização, pré-configurado com a fonte de dados do AMP
-
kube-state-metrics: gera métricas sobre estado de objetos do Kubernetes (status de pod, solicitações/limites de recurso, estados de NodeClaim)
-
node-exporter (DaemonSet, um por nó): coleta métricas no nível do host (incluindo CPU, memória, disco e rede)
-
operator: gerencia os recursos personalizados do Prometheus e do Alertmanager
O Alertmanager está desabilitado nessa configuração.
Acessar o Grafana
Abra um terminal separado e use port-forward para acessar o Grafana:
kubectl port-forward svc/kube-prometheus-stack-grafana 3000:80 -n monitoring
Abra o http://localhost:3000admin e a senha do seguinte comando:
kubectl --namespace monitoring get secrets kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo
Para verificar se o pipeline de métricas está funcionando de ponta a ponta:
-
Navegue até Connectionse > Data sources e confirme que o
Amazon-Managed-Prometheusestá listado como a fonte de dados padrão.Valide a fonte de dados do AMP no Grafana
-
Navegue até Drilldown > Metrics e procure a métrica
up. Você deve ver os resultados dos alvos de extração do cluster.Validar a métrica
upno Grafana
Se up mostrar resultados, o pipeline (cluster → Prometheus → AMP → Grafana) estará funcionando.
Implante o DCGM Exporter para coletar métricas da GPU
O kube-prometheus-stack coleta métricas da CPU e da memória no nível do nó, mas não métricas da GPU. O NVIDIA DCGM Exporter adiciona utilização, memória, temperatura, consumo de energia, largura de banda NVLink e atividade do tensor da GPU.
helm repo add gpu-helm-charts https://nvidia.github.io/dcgm-exporter/helm-charts helm repo update
Defina a chave seletora de nó de GPU para o caminho. O Modo Automático do EKS e o Karpenter autogerenciado usam chaves de tag diferentes para o fabricante da GPU.
Crie o arquivo de valores do DCGM Exporter:
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
O campo customMetrics substitui o conjunto de métricas padrão do DCGM Exporter por um conjunto estendido que inclui largura de banda NVLink, atividade de tensor, throughput de PCIe, erros de ECC e o controle de utilização térmico. Para workloads de inferência, essas métricas ajudam a saber se as unidades de computação da GPU são utilizadas totalmente, se a GPU fica ociosa entre as solicitações devido ao baixo tamanho dos lotes, se a transferência de dados entre a CPU e a GPU é um gargalo, se o controle de utilização térmico está causando picos de latência e quanto espaço extra de memória da GPU resta para lotes maiores.
Instale o DCGM Exporter:
helm install dcgm-exporter gpu-helm-charts/dcgm-exporter \ --namespace monitoring \ -f /tmp/dcgm-exporter-values.yaml
As tolerations permitem que o exportador seja executado nos nós de GPU com taint provisionados na etapa 2. O serviceMonitor com o rótulo release: kube-prometheus-stack garante que o Prometheus o descubra e extraia automaticamente.
Verifique o DaemonSet do DCGM Exporter:
kubectl get daemonset dcgm-exporter -n monitoring
Quando um nó de GPU entra em execução, você deve ver um pod pronto. Para validar as métricas do DCGM, navegue até Drilldown > Metrics no Grafana e procure DCGM_.
Validar as métricas do DCGM no Grafana
Para visualizar o painel, navegue até Dashboards > GPU Monitoring > NVIDIA DCGM Exporter Dashboard.
Painel do NVIDIA DCGM Exporter no Grafana
Bucket do S3 de pesos do modelo
Crie um bucket do Amazon S3 para armazenar os pesos do modelo e configure uma associação de Identidade de Pods do EKS para que os pods de workload possam ler e gravar nele.
Se você abriu um novo terminal, defina o nome e a região do cluster:
export CLUSTER_NAME=ai-eks-docs export AWS_REGION=us-east-2
Criar um bucket do S3
Crie o bucket com um sufixo aleatório para evitar colisões de nomes:
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}
Os buckets do S3 criados após janeiro de 2023 têm criptografia do lado do servidor (AES256) e bloqueio de acesso público habilitados por padrão.
Configurar o Identidade de Pods do EKS para acessar o S3
Crie uma ServiceAccount model-storage-sa no namespace default, uma política do IAM tendo como escopo o bucket de modelo e uma associação do Identidade de Pods do EKS que os vincule. Os pods de workload que definirem serviceAccountName: model-storage-sa poderão ler e gravar no bucket.
kubectl create serviceaccount model-storage-sa
Criar a política do 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
Essa política concede os direitos de s3:DeleteObject e s3:PutObject para a etapa de validação. Para pods de inferência de produção que leem apenas os pesos do modelo, remova s3:PutObject e s3:DeleteObject para seguir a regra de privilégio mínimo.
Crie uma associação do Identidade de Pods do EKS. O eksctl cria um perfil do IAM com a política de confiança correta e a vincula à 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 a associação:
eksctl get podidentityassociation --cluster ${CLUSTER_NAME} --region ${AWS_REGION}
A saída deve incluir a associação model-storage-sa no namespace default.
Execute um pod temporário com a imagem da AWS CLI, usando a ServiceAccount model-storage-sa, para confirmar que o Identidade de Pods do EKS está configurado corretamente e o acesso ao S3 funciona:
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
Aguarde a conclusão do pod e verifique os logs:
kubectl wait --for=jsonpath='{.status.phase}'=Succeeded pod/s3-test --timeout=300s kubectl logs s3-test
Saída esperada:
=== 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.txtA identidade do chamador confirma que o pod assumiu o perfil ${CLUSTER_NAME}-model-storage-role por meio do Identidade de Pods do EKS. Os comandos do S3 confirmam o acesso para leitura e gravação.
Limpe o pod de teste:
kubectl delete pod s3-test
Próximas etapas
Com o cluster pronto, você pode prosseguir para Carregar e servir modelo para implantar um grande modelo de linguagem e interagir com o endpoint de inferência.
Limpeza
dica
Se você planejar continuar nas próximas seções deste guia, pule a limpeza completa. Faça a limpeza completa apenas quando terminar.
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
Se você criou uma ODCR, cancele-a primeiro:
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
O cancelamento de uma reserva não encerra as instâncias em execução. Elas continuam aos preços padrão das instâncias sob demanda até serem encerradas. Exclua primeiro a implantação para drenar o nó reservado antes de cancelar.
Procure o ARN da política do 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}"
Procure o ID do espaço de trabalho do 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 a versão do Helm do DCGM Exporter:
helm uninstall dcgm-exporter -n monitoring
Desinstale a versão do Helm do kube-prometheus-stack:
helm uninstall kube-prometheus-stack -n monitoring
Exclua a associação do Identidade de Pods do EKS para a conta do serviço de entrada do Prometheus:
eksctl delete podidentityassociation \ --cluster ${CLUSTER_NAME} \ --namespace monitoring \ --service-account-name amp-iamproxy-ingest-service-account \ --region ${AWS_REGION}
Exclua a associação do Identidade de Pods do EKS para a conta do serviço do Grafana:
eksctl delete podidentityassociation \ --cluster ${CLUSTER_NAME} \ --namespace monitoring \ --service-account-name grafana-sa \ --region ${AWS_REGION}
Exclua a política do IAM usada pelo Prometheus e pelo Grafana:
aws iam delete-policy --policy-arn ${AMP_POLICY_ARN}
Exclua o espaço de trabalho do AMP:
aws amp delete-workspace --workspace-id ${AMP_WORKSPACE_ID} --region ${AWS_REGION}
Exclua o namespace do monitoramento:
kubectl delete namespace monitoring
Procure o nome do bucket do modelo:
MODEL_BUCKET=$(aws s3api list-buckets \ --query "Buckets[?starts_with(Name, '${CLUSTER_NAME}-models-')].Name | [0]" \ --output text) echo "Model bucket: ${MODEL_BUCKET}"
Procure o ARN da política do 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}"
Exclua o bucket do modelo S3 e todos os seus objetos:
aws s3 rb s3://${MODEL_BUCKET} --force
Para excluir a associação do Identidade de Pods do EKS:
eksctl delete podidentityassociation \ --cluster ${CLUSTER_NAME} \ --namespace default \ --service-account-name model-storage-sa \ --region ${AWS_REGION}
Exclua a política do IAM:
aws iam delete-policy --policy-arn ${POLICY_ARN}
Exclua a ServiceAccount do 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