View a markdown version of this page

Configurar o cluster do Amazon EKS para workloads de IA/ML usando CLIs - Amazon EKS

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 para os próximos workshops do Amazon EKS AI/ML.

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 para obter mais informações sobre como esses recursos provisionam e escalam automaticamente as instâncias do EC2 nos clusters do EKS.

Arquitetura e fluxo de trabalho de alto nível

Arquitetura de alto nível mostrando o cluster do EKS com NodeClass e NodePool do Karpenter, pilha de monitoramento do Grafana e do Prometheus gravando no Amazon Managed Service para Prometheus, um bucket do Amazon S3 para os pesos do modelo e as etapas numeradas do fluxo de trabalho

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 Installing na 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 para atualizar para a versão mais recente.

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 zonas. A criação de um cluster com sub-redes em qualquer uma dessas zonas resulta em um erro de UnsupportedAvailabilityZoneException.

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

Comparação lado a lado das duas opções de cluster: um cluster do Modo Automático do EKS com um NodePool e um cluster padrão do EKS com o Karpenter, o CoreDNS, o CNI da VPC, o plug-in de dispositivo NVIDIA, o agente do Identidade de Pods do EKS, o Agente de Monitoramento de Nós, o kube-proxy, e o NodeClass e o NodePool

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.

EKS Auto Mode

Criar cluster do Modo Automático do EKS

eksctl create cluster \ --name=$CLUSTER_NAME \ --region=$AWS_REGION \ --enable-auto-mode \ --version=1.36 \ --zones=$AZS

Esse comando pode levar alguns minutos para ser concluído. Após a conclusão, o eksctl atualiza automaticamente o arquivo kubeconfig para funcionar com o cluster recém-provisionado. Verifique se o cluster está operacional:

kubectl get pods --all-namespaces

Saída esperada:

NAMESPACE NAME READY STATUS RESTARTS AGE kube-system metrics-server-55cf976ddd-cz2mw 1/1 Running 0 3m kube-system metrics-server-55cf976ddd-wrjvv 1/1 Running 0 3m

No Modo Automático do EKS, a CNI da VPC, o kube-proxy e o CoreDNS são executados como componentes gerenciados e não aparecem como pods no kube-system.

Criar o StorageClass padrão

As workloads de GPU geralmente precisam de armazenamento persistente, por exemplo, para armazenar em cache os pesos dos modelos para que o Kubernetes não os baixe novamente a cada reinicialização do pod. O Modo Automático do EKS inclui um recurso de armazenamento em bloco integrado, portanto, você não precisa instalar o driver CSI do Amazon EBS. Crie um StorageClass que faça referência ao provisionador do Modo Automático:

cat << EOF | kubectl apply -f - apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: gp3 annotations: storageclass.kubernetes.io/is-default-class: "true" provisioner: ebs.csi.eks.amazonaws.com volumeBindingMode: WaitForFirstConsumer parameters: type: gp3 encrypted: "true" EOF

Esse comando cria um StorageClass gp3 e o marca como padrão do cluster com a anotação storageclass.kubernetes.io/is-default-class. Qualquer PersistentVolumeClaim que omita storageClassName usa este StorageClass. A configuração volumeBindingMode: WaitForFirstConsumer atrasa a criação do volume até que o Kubernetes agende um pod para que o volume do EBS seja criado na mesma zona de disponibilidade do pod.

Self-managed Karpenter

Autenticar o Helm em ECR público

O eksctl extrai o chart do Helm do Karpenter do Amazon Public ECR. Faça a autenticação antes de criar o cluster para evitar um erro 403 na etapa de instalação do Helm:

aws ecr-public get-login-password --region us-east-1 \ | helm registry login --username AWS --password-stdin public.ecr.aws

O ECR público é um serviço global hospedado na região us-east-1. Use --region us-east-1 aqui, independentemente da região em que seu cluster do EKS esteja.

Saída esperada: Login Succeeded

Criar o cluster do EKS com o Karpenter

Armazene sua versão do Karpenter em uma variável de ambiente para uso posterior. Para as versões mais recentes do Karpenter, consulte Karpenter releases no GitHub.

export KARPENTER_VERSION=1.14.0
exemplo YAML de configuração de cluster
cat << EOF > /tmp/cluster-karpenter.yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: ${CLUSTER_NAME} region: ${AWS_REGION} version: "1.36" tags: karpenter.sh/discovery: ${CLUSTER_NAME} availabilityZones: [$(echo $AZS | sed 's/,/, /g')] autoModeConfig: enabled: false iam: withOIDC: true karpenter: version: "${KARPENTER_VERSION}" withSpotInterruptionQueue: true managedNodeGroups: - name: system instanceTypes: ["m6i.2xlarge", "m5.2xlarge", "m6a.2xlarge", "m5a.2xlarge", "m7i.2xlarge"] desiredCapacity: 2 minSize: 2 maxSize: 3 labels: node-role: system tags: karpenter.sh/discovery: ${CLUSTER_NAME} addons: - name: eks-pod-identity-agent - name: eks-node-monitoring-agent - name: aws-ebs-csi-driver EOF

O grupo de nós gerenciados do system lista vários tipos de instância do mesmo tamanho (8 vCPU, 32 GiB) em vez de um único tipo. Se um tipo estiver temporariamente indisponível em uma zona de disponibilidade, o grupo do Auto Scaling do grupo de nós recorrerá a outro tipo, o que evita falhas de inicialização InsufficientInstanceCapacity. Para um grupo de nós gerenciados sob demanda, todos os tipos de instância listados devem ter a mesma vCPU e memória para que o tamanho do nó seja uniforme.

Crie o cluster com o arquivo de configuração:

eksctl create cluster -f /tmp/cluster-karpenter.yaml

Esse processo leva cerca de 15 minutos. Ele cria um cluster do EKS com um grupo de nós gerenciados dedicado à hospedagem de complementos e do controlador do Karpenter. O Karpenter é instalado com a fila de interrupção de spot habilitada para que ele possa lidar com as recomendações de interrupção e rebalanceamento de spot. A configuração autoModeConfig.enabled: false deixa explícito que esse cluster não usa o Modo Automático do EKS, portanto, os componentes do Karpenter instalados nesse caminho são responsáveis pelo gerenciamento dos nós.

O cluster também tem o Agente de Identidade de Pods do EKS, o agente de monitoramento de nós do EKS e o driver CSI do Amazon EBS instalados como complementos do EKS. O Identidade de Pods do EKS é usado posteriormente neste guia. O agente de monitoramento de nós do EKS é executado em todos os nós e lê os logs do kernel para definir as condições dos nós, como AcceleratedHardwareReady, KernelReady e NetworkingReady, que o reparo automático de nós do Karpenter usa para decidir quando substituir um nó não íntegro.

Verifique se o cluster está operacional:

kubectl get pods --all-namespaces

A saída esperada inclui o Karpenter, o CoreDNS, o kube-proxy, o aws-node (CNI da VPC), o Agente de Identidade de Pods do EKS e o agente de monitoramento de nós do EKS.

NAMESPACE NAME READY STATUS RESTARTS AGE karpenter karpenter-567547464c-s6vkx 1/1 Running 0 3m40s karpenter karpenter-567547464c-x7gmw 1/1 Running 0 3m40s kube-system aws-node-b6gf2 2/2 Running 0 12m kube-system aws-node-lcphh 2/2 Running 0 12m kube-system coredns-7d4dcbf4fb-ccvrr 1/1 Running 0 16m kube-system coredns-7d4dcbf4fb-qbhk2 1/1 Running 0 16m kube-system eks-node-monitoring-agent-h79vm 1/1 Running 0 9m45s kube-system eks-node-monitoring-agent-tf4dw 1/1 Running 0 9m45s kube-system eks-pod-identity-agent-5jbtc 1/1 Running 0 12m kube-system eks-pod-identity-agent-rwcrc 1/1 Running 0 12m kube-system kube-proxy-p4bmq 1/1 Running 0 12m kube-system kube-proxy-v5nwr 1/1 Running 0 12m kube-system metrics-server-5b966ff79c-hr58p 1/1 Running 0 9m22s kube-system metrics-server-5b966ff79c-szs2d 1/1 Running 0 9m22s

Habilitar os feature gates do Karpenter

O Modo Automático do EKS habilita o reparo automático de nós e a capacidade estática por padrão. No Karpenter autogerenciado, você deve habilitar explicitamente o feature gate NodeRepair e o gate StaticCapacity para NodePools estáticos, que mantêm uma contagem fixa de nós por meio de spec.replicas em vez de escalar em resposta aos pods pendentes. Ambos os gates são Alpha e estão desativados por padrão no Karpenter. O comando a seguir aplica patches na implantação do Karpenter para habilitar ambos os gates. A atualização do ambiente de implantação aciona a implementação dos pods do Karpenter:

kubectl set env deployment/karpenter -n karpenter \ FEATURE_GATES=NodeRepair=true,StaticCapacity=true

Saída esperada:

deployment.apps/karpenter env updated

Espere que os pods do Karpenter sejam implantados:

kubectl rollout status deployment/karpenter -n karpenter

Instale o plug-in de dispositivo NVIDIA

A AMI do AL2023 otimizada para EKS não inclui o plug-in de dispositivo NVIDIA (ao contrário da AMI do Bottlerocket usada pelo Modo Automático do EKS). Instale-o via Helm para tornar os recursos de GPU utilizáveis com pods no cluster.

helm repo add nvdp https://nvidia.github.io/k8s-device-plugin helm repo update
cat << 'EOF' > /tmp/nvdp-values.yaml mofedEnabled: false nodeSelector: amiFamily: al2023 gfd: enabled: true nfd: worker: tolerations: - operator: "Exists" EOF
helm install nvidia-device-plugin nvdp/nvidia-device-plugin \ --namespace kube-system \ -f /tmp/nvdp-values.yaml
  • mofedEnabled: false: desabilita a verificação da presença do Mellanox OFED (InfiniBand), que a AWS não usa

  • nodeSelector.amiFamily: al2023: limita o escopo do DaemonSet apenas aos nós do AL2023 (o Bottlerocket já tem o plug-in incorporado)

  • gfd.enabled: true: habilita os rótulos do GPU Feature Discovery (nvidia.com/gpu.product, nvidia.com/gpu.memory etc.)

Verifique se o plug-in de dispositivo NVIDIA está instalado. A expectativa é que não haja nenhum pod de plug-in de dispositivo até que um NodePool de GPU com o rótulo correspondente seja provisionado.

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

Saída esperada:

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

Em seguida, crie um NodePool de uso geral. O grupo de nós gerenciados do system executa os complementos e os controladores. Adicione um NodePool do Karpenter de uso geral para que as workloads sem GPU possam ser escaladas sob demanda sem serem alocadas nos nós do system. O NodePool da GPU é criado posteriormente na Etapa 2.

YAML de EC2NodeClass e NodePool para uso geral

cat << EOF | kubectl apply -f - apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass metadata: name: general-purpose spec: role: "eksctl-KarpenterNodeRole-${CLUSTER_NAME}" amiSelectorTerms: - alias: al2023@latest subnetSelectorTerms: - tags: karpenter.sh/discovery: ${CLUSTER_NAME} securityGroupSelectorTerms: - tags: karpenter.sh/discovery: ${CLUSTER_NAME} tags: karpenter.sh/discovery: ${CLUSTER_NAME} --- apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: general-purpose spec: template: spec: nodeClassRef: group: karpenter.k8s.aws kind: EC2NodeClass name: general-purpose requirements: - key: karpenter.sh/capacity-type operator: In values: ["on-demand"] - key: karpenter.k8s.aws/instance-category operator: In values: ["c", "m", "r"] - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["4"] - key: kubernetes.io/arch operator: In values: ["amd64"] limits: cpu: 1000 memory: 1000Gi EOF

Esse NodePool não tem taints, portanto, qualquer pod que não tolere o taint de GPU pode ser agendado nele.

Criar o StorageClass padrão

As workloads de GPU geralmente precisam de armazenamento persistente, por exemplo, para armazenar em cache os pesos dos modelos para que o Kubernetes não os baixe novamente a cada reinicialização do pod. O driver CSI do Amazon EBS já foi instalado pelo bloco addons: na configuração do cluster no início desta etapa, então você só precisa criar o StorageClass:

cat << EOF | kubectl apply -f - apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: gp3 annotations: storageclass.kubernetes.io/is-default-class: "true" provisioner: ebs.csi.aws.com volumeBindingMode: WaitForFirstConsumer parameters: type: gp3 encrypted: "true" EOF

Esse comando cria um StorageClass gp3 e o marca como padrão do cluster com a anotação storageclass.kubernetes.io/is-default-class. Qualquer PersistentVolumeClaim que omita storageClassName usa este StorageClass. A configuração volumeBindingMode: WaitForFirstConsumer atrasa a criação do volume até que o Kubernetes agende um pod para que o volume do EBS seja criado na mesma zona de disponibilidade do pod.

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ê.

EKS Auto Mode

O Modo Automático do EKS inclui um controlador integrado do ALB para que você não instale nenhum componente adicional. Crie um IngressClass denominado alb que aponte para o controlador integrado. Cada Ingress que você criar posteriormente fará referência a essa classe pelo nome e definirá seu próprio esquema e outras opções por meio de anotações.

cat << EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: IngressClass metadata: name: alb spec: controller: eks.amazonaws.com/alb EOF
Self-managed Karpenter

O AWS Load Balancer Controller não é um complemento gerenciado pelo EKS, portanto, ele não pode ser inserido no bloco addons: de configuração do cluster do eksctl da mesma forma que o driver CSI do EBS. Instale-o com o Helm.

Faça download da política do IAM

O controlador precisa de permissões do IAM para criar e gerenciar ALBs. Baixe o JSON oficial da política do IAM:

curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/install/iam_policy.json

Criar a política do IAM:

aws iam create-policy \ --policy-name AWSLoadBalancerControllerIAMPolicy \ --policy-document file://iam_policy.json

Crie a associação de Identidade de Pods

O cluster já instala o complemento eks-pod-identity-agent na Etapa 1, portanto, a Identidade de Pods do EKS está disponível. Crie uma associação da Identidade de Pods que vincule a política do IAM à conta de serviço do controlador:

eksctl create podidentityassociation \ --cluster ${CLUSTER_NAME} \ --region ${AWS_REGION} \ --namespace kube-system \ --service-account-name aws-load-balancer-controller \ --permission-policy-arns arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):policy/AWSLoadBalancerControllerIAMPolicy

Instalação usando o Helm

Adicione o repositório de charts do Helm para EKS:

helm repo add eks https://aws.github.io/eks-charts helm repo update

Consulte o ID da VPC do seu cluster. Os valores do Helm precisam disso para que o controlador possa descobrir sub-redes:

VPC_ID=$(aws eks describe-cluster \ --name ${CLUSTER_NAME} \ --region ${AWS_REGION} \ --query 'cluster.resourcesVpcConfig.vpcId' \ --output text)

Instale o controlador. O nodeSelector e a tolerância posicionam o controlador no grupo de nós gerenciados do system ao lado da infraestrutura crítica do cluster, em vez de competir por capacidade com nós de uso geral ou com GPUs inicializados pelo Karpenter:

helm install aws-load-balancer-controller eks/aws-load-balancer-controller \ --namespace kube-system \ --version 3.4.2 \ --set clusterName=${CLUSTER_NAME} \ --set region=${AWS_REGION} \ --set vpcId=${VPC_ID} \ --set serviceAccount.create=true \ --set serviceAccount.name=aws-load-balancer-controller \ --set "nodeSelector.node-role=system" \ --set "tolerations[0].key=CriticalAddonsOnly" \ --set "tolerations[0].operator=Exists"

Verifique se o controlador está em execução. Ele deve relatar duas réplicas disponíveis:

kubectl get deployment aws-load-balancer-controller -n kube-system

Verificar o IngressClass

O chart do Helm cria um IngressClass denominado alb como parte da instalação do controlador, apontando para o controlador autogerenciado ingress.k8s.aws/alb. Você não precisa criá-lo manualmente. Confirme se existe:

kubectl get ingressclass alb

Saída esperada:

NAME CONTROLLER PARAMETERS AGE alb ingress.k8s.aws/alb <none> 22s

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 e abaixo de 7, 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.

Importante

O tipo de instância G7 do EC2 requer a versão 595 ou posterior do driver NVIDIA. Atualmente, as AMIs aceleradas otimizadas para EKS incluem a versão 580 do driver NVIDIA, que não é compatível com instâncias G7. O NodePool nesta etapa restringe a geração da instância a menos de 7 para que o Karpenter não selecione uma instância G7. Para usar instâncias G7 com o Amazon EKS, você deve compilar uma AMI personalizada com a versão 595 do driver NVIDIA. Para obter mais informações, consulte Uso de AMIs aceleradas e otimizadas para o EKS em instâncias de GPU.

EKS Auto Mode

No Modo Automático do EKS, a NodeClass default incluída seleciona automaticamente a AMI do Bottlerocket para as instâncias da GPU, o que inclui drivers NVIDIA pré-instalados, o plug-in de dispositivo NVIDIA e a extração paralela do SOCI. Você só precisa aplicar um NodePool que referencie a NodeClass default:

cat << 'EOF' | kubectl apply -f - apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-inf spec: template: metadata: labels: guide: ai-eks-docs spec: nodeClassRef: group: eks.amazonaws.com kind: NodeClass name: default taints: - key: nvidia.com/gpu effect: NoSchedule requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot", "on-demand"] - key: eks.amazonaws.com/instance-category operator: In values: ["g"] - key: eks.amazonaws.com/instance-generation operator: Gt values: ["4"] - key: eks.amazonaws.com/instance-generation operator: Lt values: ["7"] - key: kubernetes.io/arch operator: In values: ["amd64"] limits: cpu: 1000 memory: 5000Gi EOF

Esse NodePool provisiona instâncias de GPU da família G com geração acima de 4 e abaixo de 7 (G5, G6e etc.). O Modo Automático do EKS escolhe um tipo de instância dentro dessas restrições.

Self-managed Karpenter

O Karpenter autogerenciado não inclui uma NodeClass padrão. Primeiro, você cria uma EC2NodeClass que fixa o alias da AMI NVIDIA do AL2023 otimizada para EKS, habilita o SOCI por meio do controle de atributo FastImagePull e configura instanceStorePolicy: RAID0 para mover o cache de imagem containerd para o NVMe local. Depois, você cria o NodePool que o referencia.

Criar a EC2NodeClass

exemplo
cat << EOF | kubectl apply -f - apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass metadata: name: gpu-inf labels: guide: ai-eks-docs spec: role: "eksctl-KarpenterNodeRole-${CLUSTER_NAME}" amiSelectorTerms: - alias: al2023@latest subnetSelectorTerms: - tags: karpenter.sh/discovery: ${CLUSTER_NAME} securityGroupSelectorTerms: - tags: karpenter.sh/discovery: ${CLUSTER_NAME} tags: karpenter.sh/discovery: ${CLUSTER_NAME} instanceStorePolicy: RAID0 userData: | apiVersion: node.eks.aws/v1alpha1 kind: NodeConfig spec: featureGates: FastImagePull: true EOF

instanceStorePolicy: RAID0 reúne discos NVMe locais em uma matriz RAID-0. O alias da AMI al2023@latest é resolvido para a AMI do AL2023 otimizada para EKS. Quando o Karpenter inicia um tipo de instância de GPU, ele seleciona automaticamente a variante acelerada AL2023_x86_64_NVIDIA, que inclui o driver NVIDIA pré-instalado.

O controle de atributo FastImagePull habilita o modo de extração paralela do snapshotter SOCI, que baixa e descompacta as camadas de imagem simultaneamente. Isso corresponde ao comportamento do Modo Automático do EKS nas famílias de instâncias G, P e Trn.

Para obter mais opções de ajuste do SOCI (downloads simultâneos por imagem, tamanho do bloco etc.), consulte o esquema do Karpenter com SOCI.

Valide a EC2NodeClass:

kubectl get ec2nodeclass gpu-inf

Saída esperada: READY True. Se False, execute kubectl describe ec2nodeclass gpu-inf e verifique se há condições de tags de sub-rede ou grupo de segurança faltando.

Criar o NodePool da GPU

exemplo
cat << EOF | kubectl apply -f - apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-inf spec: template: metadata: labels: guide: ai-eks-docs amiFamily: al2023 spec: nodeClassRef: group: karpenter.k8s.aws kind: EC2NodeClass name: gpu-inf taints: - key: nvidia.com/gpu effect: NoSchedule requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot", "on-demand"] - key: karpenter.k8s.aws/instance-category operator: In values: ["g"] - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["4"] - key: karpenter.k8s.aws/instance-generation operator: Lt values: ["7"] - key: kubernetes.io/arch operator: In values: ["amd64"] limits: cpu: 1000 memory: 5000Gi EOF

O rótulo amiFamily: al2023 do modelo de nó é o que o DaemonSet do plug-in de dispositivo NVIDIA usa para selecionar esses nós. O Karpenter escolhe um tipo de instância dentro dessas restrições. A taint nvidia.com/gpu:NoSchedule garante que somente pods qualificados para GPU sejam programados nesses nós.

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 AZ podem variar. Qualquer instância da família G com uma geração maior que 4 e menor que 7 é 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 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:

EKS Auto Mode

No Modo Automático do EKS, o NodeClass default empacotado é somente leitura, então, crie uma NodeClass personalizada que referencie a reserva e atualize o NodePool para apontar para a NodeClass e adicione a capacidade reserved à lista capacity-type.

exemplo YAML da NodeClass
NODE_ROLE=$(kubectl get nodeclass default -o jsonpath='{.spec.role}') cat << EOF | kubectl apply -f - apiVersion: eks.amazonaws.com/v1 kind: NodeClass metadata: name: gpu-inf labels: guide: ai-eks-docs spec: role: "$NODE_ROLE" subnetSelectorTerms: - tags: alpha.eksctl.io/cluster-name: "$CLUSTER_NAME" kubernetes.io/role/internal-elb: "1" securityGroupSelectorTerms: - tags: aws:eks:cluster-name: "$CLUSTER_NAME" capacityReservationSelectorTerms: - id: "$CAPACITY_RESERVATION_ID" EOF

A tag kubernetes.io/role/internal-elb: "1" garante que os nós sejam iniciados somente em sub-redes privadas.

Atualize o NodePool para usar a NodeClass baseada em ODCR e inclua reserved como tipo de capacidade:

exemplo YAML do NodePool atualizado
cat << EOF | kubectl apply -f - apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-inf spec: template: metadata: labels: guide: ai-eks-docs spec: nodeClassRef: group: eks.amazonaws.com kind: NodeClass name: gpu-inf taints: - key: nvidia.com/gpu effect: NoSchedule requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot", "on-demand", "reserved"] - key: eks.amazonaws.com/instance-category operator: In values: ["g"] - key: eks.amazonaws.com/instance-generation operator: Gt values: ["4"] - key: eks.amazonaws.com/instance-generation operator: Lt values: ["7"] - key: kubernetes.io/arch operator: In values: ["amd64"] limits: cpu: 1000 memory: 5000Gi EOF
Self-managed Karpenter

Para o Karpenter autogerenciado, reaplique a EC2NodeClass criada na Etapa 2 com os capacityReservationSelectorTerms adicionados. O nome e a forma do campo correspondem à NodeClass do Modo Automático do EKS mostrado na outra guia.

exemplo YAML da EC2NodeClass atualizada
cat << EOF | kubectl apply -f - apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass metadata: name: gpu-inf labels: guide: ai-eks-docs spec: role: "eksctl-KarpenterNodeRole-${CLUSTER_NAME}" amiSelectorTerms: - alias: al2023@latest subnetSelectorTerms: - tags: karpenter.sh/discovery: ${CLUSTER_NAME} securityGroupSelectorTerms: - tags: karpenter.sh/discovery: ${CLUSTER_NAME} tags: karpenter.sh/discovery: ${CLUSTER_NAME} instanceStorePolicy: RAID0 capacityReservationSelectorTerms: - id: "$CAPACITY_RESERVATION_ID" userData: | apiVersion: node.eks.aws/v1alpha1 kind: NodeConfig spec: featureGates: FastImagePull: true EOF

A única alteração na etapa 2 é o novo campo capacityReservationSelectorTerms. Todos os outros campos permanecem os mesmos.

Atualize o NodePool para incluir reserved como tipo de capacidade:

exemplo YAML do NodePool atualizado
cat << EOF | kubectl apply -f - apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-inf spec: template: metadata: labels: guide: ai-eks-docs amiFamily: al2023 spec: nodeClassRef: group: karpenter.k8s.aws kind: EC2NodeClass name: gpu-inf taints: - key: nvidia.com/gpu effect: NoSchedule requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot", "on-demand", "reserved"] - key: karpenter.k8s.aws/instance-category operator: In values: ["g"] - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["4"] - key: karpenter.k8s.aws/instance-generation operator: Lt values: ["7"] - key: kubernetes.io/arch operator: In values: ["amd64"] limits: cpu: 1000 memory: 5000Gi EOF

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:

  1. Navegue até Connectionse > Data sources e confirme que o Amazon-Managed-Prometheus está listado como a fonte de dados padrão.

    Valide a fonte de dados do AMP no Grafana

    Página Connections do Grafana mostrando Amazon-Managed-Prometheus listado como a fonte de dados padrão
  2. 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 up no Grafana

    A página Drilldown Metrics do Grafana mostrando a métrica up com barras de status verdes indicando os alvos de extração ativos

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.

EKS Auto Mode
GPU_NODE_SELECTOR_KEY="eks.amazonaws.com/instance-gpu-manufacturer"
Self-managed Karpenter
GPU_NODE_SELECTOR_KEY="karpenter.k8s.aws/instance-gpu-manufacturer"

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

Página Drilldown Metrics do Grafana filtrada por DCGM_ mostrando métricas de GPU incluindo DCGM_FI_DEV_ECC_SBE_VOL_TOTAL, DCGM_FI_DEV_ENC_UTIL, DCGM_FI_DEV_FB_FREE e DCGM_FI_DEV_FB_USEDCGM_FI_DEV_FB_USED

Para visualizar o painel, navegue até Dashboards > GPU Monitoring > NVIDIA DCGM Exporter Dashboard.

Painel do NVIDIA DCGM Exporter no Grafana

Painel do NVIDIA DCGM Exporter do Grafana mostrando os painéis Grafana GPU Utilization, GPU Avg Temp, GPU Framebuffer Mem Used e GPU Power Total

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.txt

A 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