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
Importante
Os recursos que você cria neste tutorial, incluindo clusters do EKS, instâncias de GPU, Application Load Balancers e o Amazon Managed Service for Prometheus, incorrem em cobranças. Para evitar cobranças contínuas, exclua os recursos quando terminar.
-
kubectl>= 1.36. 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.
Configurar o balanceamento de carga
As etapas posteriores deste guia expõem o Grafana (e, no passo a passo de inferência, um endpoint do modelo) por meio de um AWS Application Load Balancer (ALB). Configure os pré-requisitos de balanceamento de carga agora para que essas seções possam criar um Ingress sem configuração adicional.
O ALB é alocado nas suas sub-redes de VPC com base em tags de recursos da AWS. As sub-redes públicas usadas para balanceadores de carga voltados para a internet devem ser marcadas como kubernetes.io/role/elb = 1, e as sub-redes privadas usadas para balanceadores de carga internos devem ser marcadas como kubernetes.io/role/internal-elb = 1. Como você criou esse cluster com o eksctl, essas tags já existem no eksctl criado de sub-redes. Se você trouxer sua própria VPC ou sub-redes importadas, marque-as manualmente. Para obter detalhes, consulte Marcar sub-redes para o Modo Automático do EKS.
Criar o IngressClass do balanceador de carga
O restante da configuração difere de acordo com o caminho. O Modo Automático do EKS inclui um controlador do ALB integrado, portanto, você cria um IngressClass por conta própria. O Karpenter autogerenciado requer a instalação do AWS Load Balancer Controller com o Helm, e o chart do Helm cria o IngressClass para você.
Ambos os caminhos agora têm um IngressClass alb. As seções do Grafana e de inferência fazem referência a ingressClassName: alb e definem o esquema do ALB, a lista de permissões de IPs de origem e outros comportamentos por meio de anotações por Ingress.
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. Como os dois caminhos usam AMIs diferentes, as restrições de instância diferem entre eles, conforme observado em cada caminho.
Ao contrário do caminho do Modo automático do EKS, esse NodePool não limita a geração de instâncias, porque a AMI NVIDIA AL2023 otimizada para EKS inclui uma versão de driver compatível com instâncias g7. No momento, a AMI EKS AL2023 usa o driver NVIDIA 580 em todas as instâncias do EC2, exceto g7, que usa o driver NVIDIA 595. Para obter mais informações, consulte AMIs NVIDIA otimizadas para EKS.
Valide se 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.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:35:00.0 Off | 0 | | N/A 42C P8 16W / 72W | 0MiB / 23034MiB | 0% Default | | | | N/A | +-----------------------------------------+------------------------+----------------------+
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 G6 que tem uma GPU NVIDIA L4 com 24 GB de memória. O 42C é a temperatura atual da GPU e o P8 é um estado ocioso de baixo consumo de energia, o que é esperado porque nenhuma workload está usando a GPU. O valor 16W/72W mostra o consumo de energia atual em relação à capacidade máxima de energia, e 0MiB/23034MiB 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.159.03) 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 G6 têm GPUs NVIDIA L4 (24 GB) e as instâncias G6e têm GPUs NVIDIA L40S (48 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).
Um NodeClaim é uma requisiçã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-vxcnj g6.4xlarge spot us-east-2c i-0fb17a09bc4203164 True 51s
O tipo de instância e a Zona de disponibilidade variam. Qualquer instância da família G que satisfaça a restrição de geração do seu NodePool é elegível: maior que 4 para ambos os caminhos, com a família g7 excluída no caminho do Modo automático do EKS.
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 três 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 a capacidade reservada primeiro com o fallback spot/sob demanda, crie uma reserva de capacidade sob demanda (ODCR) e conecte-a ao NodeClass. Em seguida, atualize o NodePool dinâmico da Etapa 2 para também permitir a 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 g6e.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.
O arquivo de valores também configura um Ingress do Grafana para que o gráfico provisione um ALB voltado para a internet para acesso ao navegador usando o IngressClass do alb que você criou em Configurar balanceamento de carga. A anotação alb.ingress.kubernetes.io/inbound-cidrs restringe o balanceador de carga ao seu próprio endereço IP, portanto, configure-o antes da instalação.
O Grafana é acessível publicamente por HTTP com credenciais padrão
O Grafana inclui credenciais de administrador padrão e fornece acesso administrativo total aos seus dados de monitoramento. Um balanceador de carga voltado para a internet disponibiliza essa interface na internet pública por meio de HTTP de texto sem formatação. Os verificadores automatizados descobrem balanceadores de carga públicos em minutos. Você deve restringir o acesso com a anotação alb.ingress.kubernetes.io/inbound-cidrs e tratar a lista de permissões de IPs de origem como uma proteção mínima, não como uma proteção completa. Altere também a senha padrão do administrador do Grafana. Para uma postura mais forte, use um esquema interno (acessível somente de dentro da sua VPC ou em uma VPN conectada) definindo o esquema como internal e adicionando um certificado TLS.
Encontre seu endereço IP público e armazene-o como um CIDR /32. O arquivo de valores lê isso da variável de ambiente MY_CIDR:
export MY_CIDR="$(curl -s https://checkip.amazonaws.com)/32" echo $MY_CIDR
O resultado é semelhante a 203.0.113.4/32. Se sua rede atribuir endereços dinamicamente, seu endereço IP poderá mudar com o tempo, então considere definir um intervalo mais amplo, como 203.0.113.0/24.
Crie o arquivo de valores:
exemplo Arquivo de valores kube-prometheus-stack
cat << EOF > /tmp/kube-prometheus-values.yaml alertmanager: enabled: false prometheus-adapter: enabled: false prometheus: serviceAccount: create: false name: amp-iamproxy-ingest-service-account prometheusSpec: serviceAccountName: amp-iamproxy-ingest-service-account enableRemoteWriteReceiver: true retention: 2h scrapeInterval: 30s evaluationInterval: 30s podMonitorSelectorNilUsesHelmValues: false serviceMonitorSelectorNilUsesHelmValues: false resources: requests: cpu: 500m memory: 1Gi limits: memory: 8Gi remoteWrite: - url: "${AMP_ENDPOINT}api/v1/remote_write" sigv4: region: "${AWS_REGION}" queueConfig: maxSamplesPerSend: 1000 maxShards: 200 capacity: 2500 prometheusOperator: resources: requests: cpu: 100m memory: 128Mi limits: memory: 256Mi kube-state-metrics: resources: requests: cpu: 50m memory: 128Mi limits: memory: 512Mi grafana: enabled: true ingress: enabled: true ingressClassName: alb labels: guide: ai-eks-docs annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip alb.ingress.kubernetes.io/inbound-cidrs: ${MY_CIDR} alb.ingress.kubernetes.io/load-balancer-name: grafana-ai-eks-docs alb.ingress.kubernetes.io/healthcheck-path: /api/health serviceAccount: create: false name: grafana-sa resources: requests: cpu: 100m memory: 256Mi limits: memory: 1Gi 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 vllm-load-analysis: gnetId: 25494 revision: 1 datasource: - name: DS_PROMETHEUS value: Amazon-Managed-Prometheus prometheus-node-exporter: resources: requests: cpu: 50m memory: 64Mi limits: memory: 128Mi tolerations: - operator: Exists EOF
Valide que as variáveis foram preenchidas corretamente:
grep -E "url:|region:" /tmp/kube-prometheus-values.yaml
Você deve ver o URL completo do endpoint do AMP (começando com https://aps-workspaces…) e a sua região. 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
Você acessa o Grafana por meio de um AWS Application Load Balancer (ALB) voltado para a internet. Você não o cria aqui. O gráfico kube-prometheus-stack o provisiona da configuração do grafana.ingress no arquivo de valores usando o IngressClass do alb de Configurar balanceamento de carga. O chart do Helm do Grafana tem o suporte a Ingress integrado, portanto, não é necessário nenhum manifesto separado. O ALB é denominado grafana-ai-eks-docs, carrega o rótulo guide: ai-eks-docs e está restrito ao endereço IP que você definiu em MY_CIDR no momento da instalação.
O caminho de verificação de integridade é necessário para o grupo de destino do ALB do Grafana
A anotação alb.ingress.kubernetes.io/healthcheck-path no arquivo de valores aponta as verificações de integridade do balanceador de carga para o endpoint /api/health do Grafana, que retorna 200. Sem isso, as verificações de integridade do ALB são padronizadas para /, às quais o Grafana responde com um redirecionamento 302 para /login, fazendo com que o grupo de destino indique o endpoint como não íntegro.
Exiba o URL do balanceador de carga. O balanceador de carga é criado de forma assíncrona, então aguarde um ou dois minutos:
echo "http://$(kubectl get ingress kube-prometheus-stack-grafana -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')"
Abra o URL em seu navegador. Faça login com o nome de usuário admin 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:
exemplo 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 randomizado 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. Procure o ID da reserva:
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}) echo "Capacity reservation ID: ${CAPACITY_RESERVATION_ID}"
Cancele a reserva:
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