Guida alle best practice per la configurazione dei cluster per l'inferenza in tempo reale su Amazon EKS - Amazon EKS

Aiutaci a migliorare questa pagina

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Per contribuire a questa guida per l'utente, scegli il GitHub link Modifica questa pagina nel riquadro destro di ogni pagina.

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Guida alle best practice per la configurazione dei cluster per l'inferenza in tempo reale su Amazon EKS

Introduzione

Questa guida offre una guida pratica per configurare un cluster Amazon Elastic Kubernetes Service (EKS) ottimizzato per carichi di lavoro di inferenza online in tempo reale, incorporando le migliori pratiche curate da esperti. AWS Utilizza un'autorevole architettura EKS Quickstart, un set curato di driver, tipi di istanze e configurazioni in linea con le migliori pratiche per modelli, acceleratori e scalabilità. AWS Questo approccio consente di aggirare il compito di selezionare le impostazioni del cluster, consentendovi di ottenere rapidamente un cluster funzionale e preconfigurato. Lungo il percorso, distribuiremo esempi di carichi di lavoro per convalidare la configurazione, spiegheremo i concetti chiave dell'architettura (come il disaccoppiamento delle attività legate alla CPU dai calcoli ad uso intensivo della GPU), risponderemo a domande comuni (ad esempio, perché scegliere Bottlerocket AMI rispetto a 023?) AL2 e delineate i passaggi successivi per estendere le funzionalità del cluster.

Progettata specificamente per gli ingegneri di Machine Learning (ML) e Artificial Intelligence (AI), gli amministratori di piattaforma, gli operatori e gli data/AI specialisti che non conoscono l'ecosistema AWS ed EKS, questa guida presuppone la familiarità con Kubernetes ma nessuna esperienza precedente con EKS. È progettata per aiutarti a comprendere i passaggi e i processi necessari per rendere operativi i carichi di lavoro di inferenza online in tempo reale. La guida illustra gli elementi essenziali della creazione di un cluster di inferenza a nodo singolo, tra cui il provisioning di risorse GPU, l'integrazione dello storage per gli artefatti del modello, l'abilitazione dell'accesso sicuro ai servizi e l'esposizione degli endpoint di inferenza. AWS Nel complesso, enfatizza il design resiliente e a bassa latenza per applicazioni rivolte agli utenti come il rilevamento delle frodi, i chatbot in tempo reale e l'analisi del sentiment nei sistemi di feedback dei clienti.

In questa guida, ci concentriamo esclusivamente sulla configurazione di un punto di partenza fondamentale e prescrittivo utilizzando le istanze G5. EC2 Se stai cercando configurazioni di cluster o flussi di lavoro AWS specifici per Inferentia, consulta i nostri workshop su. end-to-end Usa le istanze AWS Inferentia con Amazon EKS for Machine Learning Risorse per iniziare a usare AI/ML Amazon EKS

Prima di iniziare

Prima di iniziare, assicurati di aver eseguito le seguenti attività:

Architettura

L'inferenza online in tempo reale si riferisce al processo di utilizzo di un modello di machine learning addestrato per generare previsioni o output sui flussi di dati in entrata con una latenza minima. Ad esempio, consente il rilevamento delle frodi in tempo reale, la classificazione delle immagini o la generazione di grafici della conoscenza in risposta agli input degli utenti. L'architettura di un sistema di inferenza online in tempo reale offre previsioni di machine learning a bassa latenza nelle applicazioni rivolte agli utenti, disaccoppiando la gestione del traffico web legato alla CPU dai calcoli di intelligenza artificiale a uso intensivo di GPU. Questo processo si trova in genere all'interno di un ecosistema applicativo più ampio e spesso include servizi di backend, frontend, vettoriali e di modello, con particolare attenzione a componenti specializzati per consentire scalabilità indipendente, sviluppo parallelo e resilienza contro i guasti. L'isolamento delle attività di inferenza su hardware GPU dedicato e l'utilizzo di interfacce come e WebSockets garantiscono un'elevata concorrenza, un'elaborazione rapida di modelli come i trasformatori APIs e le interazioni degli utenti tramite il frontend. Tieni presente che, sebbene i database vettoriali e le pipeline Retrieval Augmented Generation (RAG) svolgano spesso un ruolo importante nei sistemi di inferenza in tempo reale, questi componenti non sono trattati in questa guida. Come minimo, un'architettura tipica include spesso:

  • Servizio frontend: funge da interfaccia utente, gestisce la logica lato client, restituisce contenuti dinamici e facilita le interazioni in tempo reale, comunica con il servizio di backend per avviare richieste di inferenza e visualizzare i risultati, spesso avviando richieste al servizio di backend che lo utilizza per gli aggiornamenti in streaming o per lo scambio di dati strutturati. WebSockets APIs Questo servizio in genere non richiede un sistema di bilanciamento del carico dedicato, in quanto può essere ospitato su reti di distribuzione dei contenuti (CDNs) come AWS CloudFront per le risorse statiche o fornito direttamente dai server Web, con scalabilità gestita tramite gruppi di auto-scaling se necessario per contenuti dinamici.

  • Servizio di backend: funge da orchestratore dell'applicazione, gestendo logiche di business come l'autenticazione degli utenti, la convalida dei dati e il coordinamento dei servizi (ad esempio, tramite gli endpoint o per le connessioni persistenti). APIs RESTful WebSockets Comunica con il servizio di inferenza, scala indipendentemente su multi-core CPUs e RAM per gestire un traffico Web elevato senza fare affidamento su GPUs e spesso richiede un sistema di bilanciamento del carico (come Application Load Balancer o Network Load AWS Balancer) per distribuire le richieste in entrata su più istanze, specialmente in scenari ad alta concorrenza. Un controller di ingresso può gestire ulteriormente le regole di accesso e routing esterne per migliorare la sicurezza e la modellazione del traffico.

  • Servizio di inferenza: funge da base per i calcoli di intelligenza artificiale e funziona GPUs con una VRAM sufficiente (ad esempio, 8-12 GB per modelli come DistilBert) per eseguire incorporamenti vettoriali, estrazione della conoscenza e inferenza dei modelli (ad esempio, esposta per richieste in batch o APIs per lo streaming in tempo reale) utilizzando modelli personalizzati o open source. WebSockets Questo isolamento previene i conflitti di dipendenza, consente gli aggiornamenti dei modelli senza tempi di inattività e consente la scalabilità orizzontale con bilanciamento del carico per più richieste simultanee. Per esporre in modo efficace il servizio modello, in genere si basa su un sistema di bilanciamento del carico per distribuire i carichi di lavoro legati alla GPU tra istanze replicate, mentre una risorsa o un controller in ingresso (come ALB Ingress Controller in AWS) gestisce il routing esterno, la terminazione SSL e l'inoltro basato sul percorso per garantire un accesso sicuro ed efficiente senza sovraccaricare le persone GPUs.

Panoramica soluzione

I sistemi di inferenza online in tempo reale richiedono un'architettura resiliente e ad alte prestazioni in grado di fornire una latenza estremamente bassa gestendo al contempo esplosioni di traffico imprevedibili e ad alto volume. Questa panoramica della soluzione spiega come i seguenti AWS componenti interagiscono nel cluster Amazon EKS che creeremo per garantire che il nostro cluster sia in grado di ospitare e gestire modelli di machine learning che forniscono previsioni immediate su dati in tempo reale con un ritardo minimo per gli utenti finali.

  • EC2 Istanze Amazon G5: per attività di inferenza a uso intensivo di GPU, utilizziamo i tipi di istanze G5 g5.xlarge e g5.2xlarge, che dispongono di una singola (1) GPU NVIDIA A10G con 24 GB di memoria ( EC2 ad esempio, 8 miliardi di parametri). FP16 Basati sull'architettura NVIDIA Ampere, GPUs sono alimentati da processori NVIDIA A10G Tensor Core GPUs e AMD EPYC di seconda generazione, supportano 4-8 vCPUs, larghezza di banda di rete fino a 10 Gbps e 250-450 GB di storage NVMe SSD locale, garantendo un rapido spostamento dei dati e potenza di calcolo per modelli complessi, rendendoli ideali per attività di inferenza a bassa latenza e ad alto rendimento. La scelta del tipo di EC2 istanza è specifica dell'applicazione, dipende dal modello (ad esempio, immagine, video, modello di testo) e dai requisiti di latenza e velocità effettiva. Ad esempio, se utilizzi un modello di immagine e/o video, potresti voler utilizzare le EC2 istanze P5 per una latenza ottimale in tempo reale. Ti consigliamo di iniziare con le EC2 istanze G5 in quanto forniscono un buon punto di partenza per iniziare subito a lavorare, quindi di valutare se sono la soluzione giusta per i tuoi carichi di lavoro attraverso test di benchmark delle prestazioni. Per casi più avanzati, prendi in considerazione le istanze G6. EC2

  • Istanze Amazon EC2 M7g: per attività che richiedono un uso intensivo della CPU come la preelaborazione dei dati, la gestione delle richieste API, l'hosting del controller Karpenter, componenti aggiuntivi e altri componenti di sistema, utilizziamo il tipo di istanza M7g m5.xlarge. EC2 Le istanze M7g sono istanze basate su ARM che offrono 4 vCPUs, 16 GB di memoria, una larghezza di banda di rete fino a 12,5 Gbps e sono alimentate da processori Graviton3. AWS La scelta del tipo di EC2 istanza è specifica dell'applicazione e dipende dai requisiti di calcolo, memoria e scalabilità del carico di lavoro. Per carichi di lavoro ottimizzati per il calcolo, potresti prendere in considerazione le istanze C7g, che utilizzano anch'esse processori Graviton3 ma sono ottimizzate per prestazioni di calcolo più elevate rispetto EC2 alle istanze M7g per determinati casi d'uso. In alternativa, le istanze C8g più recenti (se disponibili) offrono prestazioni di elaborazione migliori fino al 30% rispetto alle istanze C7g. EC2 Ti consigliamo di iniziare con le EC2 istanze M7g per la loro efficienza in termini di costi e compatibilità con un'ampia gamma di carichi di lavoro (ad esempio, server di applicazioni, microservizi, server di gioco, archivi dati di medie dimensioni), quindi di valutare se sono la soluzione giusta per i tuoi carichi di lavoro attraverso test di benchmark delle prestazioni.

  • Driver CSI Amazon S3 Mountpoint: per carichi di lavoro su istanze a GPU singola in cui più pod condividono una GPU (ad esempio, più pod pianificati sullo stesso nodo per utilizzare le relative risorse GPU), utilizziamo il driver CSI Mountpoint S3 per ottimizzare l'utilizzo della memoria, essenziale per attività come l'inferenza di modelli di grandi dimensioni in configurazioni sensibili ai costi e a bassa complessità. Espone i bucket Amazon S3 come un file system simile a POSIX disponibile per il cluster Kubernetes, che consente ai pod di inferenza di leggere gli artefatti del modello (ad esempio, i pesi del modello) direttamente nella memoria senza doverli prima scaricare e di inserire i set di dati utilizzando operazioni standard sui file. Inoltre, S3 ha una capacità di storage praticamente illimitata e accelera i carichi di lavoro di inferenza ad alta intensità di dati. La scelta di un driver CSI di archiviazione è specifica dell'applicazione e dipende dai requisiti di velocità e latenza del carico di lavoro. Sebbene il driver CSI FSx for OpenZFS offra una latenza inferiore al millisecondo per volumi persistenti condivisi casuali I/O o completamente conformi a POSIX tra i nodi, consigliamo di iniziare con il driver CSI Mountpoint S3 grazie alla sua scalabilità, ai costi inferiori per set di dati di grandi dimensioni e all'integrazione integrata con lo storage di oggetti gestito da S3 per modelli di inferenza ad alta intensità di lettura (ad esempio, input del modello di streaming), quindi valuta se è la soluzione giusta per il tuo lavoro si carica attraverso test di benchmark delle prestazioni.

  • EKS Pod Identity Agent: per consentire l'accesso ai AWS servizi, utilizziamo EKS Pod Identity Agent, che utilizza un unico servizio principale e facilita le associazioni di ruoli IAM a livello di pod all'interno del cluster Amazon EKS. EKS Pod Identity offre un'alternativa semplificata al tradizionale approccio IAM Roles for Service Accounts (IRSA) utilizzando un unico service principal (pods.eks.amazonaws.com) anziché affidarsi a singoli provider OIDC per ogni cluster, il che semplifica l'assegnazione delle autorizzazioni. Inoltre, consente il riutilizzo dei ruoli su più cluster e supporta funzionalità avanzate come i tag di sessione dei ruoli IAM e i ruoli IAM di Target.

  • EKS Node Monitoring Agent — Per garantire la disponibilità e l'affidabilità continue dei servizi di inferenza, utilizziamo EKS Node Monitoring Agent with Auto Repair, che rileva e sostituisce automaticamente i nodi non integri, riducendo al minimo i tempi di inattività. Monitora continuamente i nodi per rilevare problemi di hardware, kernel, rete e storage utilizzando controlli di integrità avanzati (ad esempio,). KernelReady NetworkingReady Per i nodi GPU, rileva i guasti specifici dell'acceleratore, avvia una corretta correzione isolando i nodi non integri, attendendo 10 minuti per la risoluzione dei problemi transitori della GPU e sostituendo i nodi dopo 30 minuti in caso di guasti persistenti.

  • AMI Bottlerocket: per fornire una base sicura per il nostro cluster EKS, utilizziamo l'AMI Bottlerocket, che include solo i componenti essenziali necessari per eseguire i container e offre tempi di avvio minimi per una rapida scalabilità. La scelta di un nodo AMI è specifica dell'applicazione e dipende dai requisiti di personalizzazione, sicurezza e scalabilità del carico di lavoro. Sebbene l'AMI AL2 023 offra una maggiore flessibilità per le installazioni e le personalizzazioni a livello di host (ad esempio, specificando una directory cache dedicata in una configurazione PV/PVC senza nodi aggiuntivi), consigliamo di iniziare con l'AMI Bottlerocket per il suo ingombro ridotto e l'ottimizzazione integrata per carichi di lavoro containerizzati (ad esempio microservizi, server di inferenza, scalabili APIs), quindi di valutare se è la soluzione giusta per i tuoi carichi di lavoro attraverso test di benchmark delle prestazioni.

  • AWS Load Balancer Controller (LBC): per esporre gli endpoint di inferenza in tempo reale, utilizziamo il Load AWS Balancer Controller, che fornisce e gestisce automaticamente Application Load Balancer () per il traffico e Network Load Balancer (ALBs) per il HTTP/HTTPS TCP/UDP traffico basato sulle risorse Kubernetes Ingress and Service, abilitando l'integrazione di modelli di inferenza con client esterni. NLBs Inoltre, supporta funzionalità come il routing basato sul percorso per distribuire le richieste di inferenza su più pod o nodi, garantendo la scalabilità durante i picchi di traffico e riducendo al minimo la latenza attraverso ottimizzazioni AWS native come il multiplexing delle connessioni e i controlli di integrità.

1. Crea il tuo cluster EKS

In questo passaggio, creiamo un cluster con nodi CPU e un gruppo di nodi gestito utilizzando un modello eksctl AWS CloudFormation basato su tecnologia eksctl ClusterConfig. L'inizializzazione del cluster con soli nodi CPU ci consente di utilizzare Karpenter esclusivamente per gestire nodi che richiedono un uso intensivo di CPU e nodi GPU per un'allocazione ottimizzata delle risorse utilizzando Karpenter, che creeremo nei passaggi successivi. NodePools Per supportare i nostri carichi di lavoro di inferenza in tempo reale, forniamo al cluster l'AMI EKS Bottlerocket, EKS Node Monitoring Agent, EKS Pod Identity Agent, il driver CSI Mountpoint S3, il Load AWS Balancer Controller (LBC) e i driver kube-proxy, vpc-cni e coredns. Le istanze m7g.xlarge verranno utilizzate per le attività del sistema CPU, tra cui l'hosting del controller Karpenter, dei componenti aggiuntivi e di altri componenti di sistema.

Per impostazione predefinita, eksctl creerà un VPC dedicato per il cluster con un blocco CIDR di. 192.168.0.0/16 Il VPC include tre sottoreti pubbliche e tre sottoreti private, ciascuna distribuita su tre diverse zone di disponibilità (o due AZs nella us-east-1 regione), il metodo ideale per implementare i carichi di lavoro Kubernetes. Il modello implementa anche un gateway Internet, che fornisce l'accesso a Internet alle sottoreti pubbliche tramite percorsi predefiniti nelle relative tabelle di routing e un singolo gateway NAT in una delle sottoreti pubbliche, con percorsi predefiniti nelle tabelle di routing delle sottoreti private che indirizzano il traffico in uscita attraverso il gateway NAT per l'accesso a Internet. Per ulteriori informazioni su questa configurazione, consulta Distribuire nodi su sottoreti private.

Controlla le tue credenziali

Verifica se le tue credenziali AWS CLI sono valide e possono autenticarsi con i servizi: AWS

aws sts get-caller-identity

In caso di successo, la CLI restituirà i dettagli sulla tua AWS identità (UserId, Account e Arn).

Verifica la disponibilità delle istanze

I tipi di istanze G5 non sono disponibili in tutte le regioni. Controlla la regione più vicina. Per esempio:

aws ec2 describe-instance-types --instance-types g5.xlarge g5.2xlarge --region us-east-1

In caso di successo, il tipo di istanza G5 è disponibile nella regione specificata.

L'AMI Bottlerocket non è disponibile in tutte le regioni. Verifica recuperando un ID AMI Bottlerocket per la regione più vicina. Per esempio:

aws ssm get-parameter --name /aws/service/bottlerocket/aws-k8s-1.33/arm64/latest/image_id \ --region us-east-1 --query "Parameter.Value" --output text

In caso di successo, l'AMI Bottlerocket è disponibile nella regione specificata.

Prepara il tuo ambiente

Innanzitutto, imposta le seguenti variabili di ambiente in una nuova finestra di terminale. Nota: assicurati di sostituire i segnaposto di esempio con i tuoi valori univoci, tra cui il nome del cluster, la regione desiderata, la versione di rilascio di Karpenter e la versione di Kubernetes.

Suggerimento

Alcune variabili (come ${AWS_REGION} e${K8S_VERSION}) vengono definite all'inizio del blocco e quindi referenziate nei comandi successivi per garantire coerenza ed evitare ripetizioni. Assicuratevi di eseguire i comandi in sequenza in modo che questi valori vengano esportati correttamente e siano disponibili per l'uso nelle definizioni successive.

export TEMPOUT="$(mktemp)" export K8S_VERSION=1.33 export KARPENTER_VERSION="1.5.0" export AWS_REGION="us-east-1" export EKS_CLUSTER_NAME="eks-rt-inference-${AWS_REGION}" export S3_BUCKET_NAME="eks-rt-inference-models-${AWS_REGION}-$(date +%s)" export NVIDIA_BOTTLEROCKET_AMI="$(aws ssm get-parameter --name /aws/service/bottlerocket/aws-k8s-${K8S_VERSION}-nvidia/x86_64/latest/image_id --query Parameter.Value --output text)" export STANDARD_BOTTLEROCKET_AMI="$(aws ssm get-parameter --name /aws/service/bottlerocket/aws-k8s-${K8S_VERSION}/arm64/latest/image_id --query Parameter.Value --output text)" export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)" export ALIAS_VERSION="$(aws ssm get-parameter --name "/aws/service/eks/optimized-ami/${K8S_VERSION}/amazon-linux-2023/x86_64/standard/recommended/image_id" --query Parameter.Value | xargs aws ec2 describe-images --query 'Images[0].Name' --image-ids | sed -r 's/^.*(v[[:digit:]]+).*$/\1/')"

Create i ruoli e le politiche richiesti

Karpenter necessita di ruoli e policy IAM specifici (ad esempio, il ruolo IAM del controller Karpenter, il profilo dell'istanza e le politiche) per gestire le EC2 istanze come nodi di lavoro Kubernetes. Utilizza questi ruoli per eseguire azioni come l'avvio e la chiusura delle EC2 istanze, l'etichettatura delle risorse e l'interazione con altri servizi. AWS Crea i ruoli e le politiche di Karpenter utilizzando cloudformation.yaml di Karpenter:

curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/v${KARPENTER_VERSION}/website/content/en/preview/getting-started/getting-started-with-karpenter/cloudformation.yaml > "${TEMPOUT}" \ && aws cloudformation deploy \ --stack-name "Karpenter-${EKS_CLUSTER_NAME}" \ --template-file "${TEMPOUT}" \ --capabilities CAPABILITY_NAMED_IAM \ --parameter-overrides "ClusterName=${EKS_CLUSTER_NAME}"

L' AWS LBC necessita dell'autorizzazione per fornire e gestire i sistemi di bilanciamento del AWS carico, ad esempio per la creazione di risorse per Ingress o per servizi di tipo simile. ALBs NLBs LoadBalancer Specificheremo questa politica di autorizzazione durante la creazione del cluster. Durante la creazione del cluster, creeremo l'account di servizio con eksctl in. ClusterConfig Crea la policy LBC IAM:

aws iam create-policy \ --policy-name AWSLoadBalancerControllerIAMPolicy \ --policy-document "$(curl -fsSL https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.13.0/docs/install/iam_policy.json)"

Quando il driver CSI Mountpoint S3 è installato, i relativi DaemonSet pod sono configurati per utilizzare un account di servizio per l'esecuzione. Il driver CSI Mountpoint for Mountpoint S3 richiede l'autorizzazione per interagire con il bucket Amazon S3 creato più avanti in questa guida. Specificheremo questa politica di autorizzazione durante la creazione del cluster. Durante la creazione del cluster, creeremo l'account di servizio con eksctl in. ClusterConfig Crea la policy IAM di S3:

aws iam create-policy \ --policy-name S3CSIDriverPolicy \ --policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Action\": [\"s3:GetObject\", \"s3:PutObject\", \"s3:AbortMultipartUpload\", \"s3:DeleteObject\", \"s3:ListBucket\"], \"Resource\": [\"arn:aws:s3:::${S3_BUCKET_NAME}\", \"arn:aws:s3:::${S3_BUCKET_NAME}/*\"]}]}"

Nota: se esiste già un ruolo con questo nome, assegna al ruolo un nome diverso. Il ruolo che creiamo in questo passaggio è specifico per il tuo cluster e il tuo bucket S3.

Creazione del cluster

In questo modello, eksctl crea automaticamente un account di servizio Kubernetes per EKS Pod Identity, Node Monitoring Agent, CoredNS, Kubeproxy, il plugin VPC CNI. Ad oggi, il driver CSI Mountpoint S3 non è disponibile per EKS Pod Identity, quindi creiamo un IAM Roles for Service Account (IRSA) e un endpoint OIDC. Inoltre, creiamo un account di servizio per il AWS Load Balancer Controller (LBC). Per l'accesso ai nodi Bottlerocket, eksctl collega automaticamente Amazon SSMManaged InstanceCore for Bottlerocket per consentire sessioni shell sicure tramite SSM.

Nello stesso terminale in cui imposti le variabili di ambiente, esegui il seguente blocco di comandi per creare il cluster:

eksctl create cluster -f - <<EOF --- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: ${EKS_CLUSTER_NAME} region: ${AWS_REGION} version: "${K8S_VERSION}" tags: karpenter.sh/discovery: ${EKS_CLUSTER_NAME} # Add more tags if needed for billing iam: # Creates an OIDC endpoint and IRSA service account for the Mountpoint S3 CSI Driver # Uses the S3 CSI Driver policy for permissions withOIDC: true podIdentityAssociations: # Creates the pod identity association and service account # Uses the Karpenter controller IAM policy for permissions - namespace: "kube-system" serviceAccountName: karpenter roleName: ${EKS_CLUSTER_NAME}-karpenter permissionPolicyARNs: - arn:aws:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy-${EKS_CLUSTER_NAME} # Creates the pod identity association and service account # Uses the AWS LBC policy for permissions - namespace: kube-system serviceAccountName: aws-load-balancer-controller createServiceAccount: true roleName: AmazonEKSLoadBalancerControllerRole permissionPolicyARNs: - arn:aws:iam::${AWS_ACCOUNT_ID}:policy/AWSLoadBalancerControllerIAMPolicy iamIdentityMappings: - arn: "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${EKS_CLUSTER_NAME}" username: system:node:{{EC2PrivateDNSName}} groups: - system:bootstrappers - system:nodes managedNodeGroups: # Creates 2 CPU nodes for lightweight system tasks - name: ${EKS_CLUSTER_NAME}-m7-cpu instanceType: m7g.xlarge amiFamily: Bottlerocket desiredCapacity: 2 minSize: 1 maxSize: 10 labels: role: cpu-worker # Enable automatic Pod Identity associations for VPC CNI Driver, coreDNS, kube-proxy addonsConfig: autoApplyPodIdentityAssociations: true addons: # Installs the S3 CSI Driver addon and creates IAM role # Uses the S3 CSI Driver policy for IRSA permissions - name: aws-mountpoint-s3-csi-driver attachPolicyARNs: - "arn:aws:iam::${AWS_ACCOUNT_ID}:policy/S3CSIDriverPolicy" - name: eks-pod-identity-agent - name: eks-node-monitoring-agent - name: coredns - name: kube-proxy - name: vpc-cni EOF

Il processo richiede alcuni minuti. Se desideri monitorare lo stato, consulta la AWS CloudFormationconsole.

2. Verifica dello stato del nodo del cluster e del pod

Eseguiamo alcuni controlli di integrità per assicurarci che il cluster sia pronto. Al termine del comando precedente, visualizza i tipi di istanza e verifica che i nodi del sistema CPU abbiano raggiunto Ready lo stato con il comando seguente:

kubectl get nodes -L node.kubernetes.io/instance-type

L'output previsto dovrebbe essere simile al seguente:

NAME                             STATUS   ROLES    AGE     VERSION               INSTANCE-TYPE
ip-192-168-35-103.ec2.internal   Ready    <none>   12m     v1.33.0-eks-802817d   m7g.xlarge
ip-192-168-7-15.ec2.internal     Ready    <none>   12m     v1.33.0-eks-802817d   m7g.xlarge

Verifica tutte le associazioni di identità Pod e il modo in cui mappano un ruolo a un account di servizio in uno spazio dei nomi nel cluster con il seguente comando:

eksctl get podidentityassociation --cluster ${EKS_CLUSTER_NAME} --region ${AWS_REGION}

L'output dovrebbe mostrare i ruoli IAM per Karpenter («karpenter») e LBC (» «). AWS aws-load-balancer-controller

Verifica che siano disponibili: DaemonSets

kubectl get daemonsets -n kube-system

L'output previsto dovrebbe essere simile al seguente:

NAME                           DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR          AGE
aws-node                       3       3       3     3          3         <none>                 12m
dcgm-server                    0       0       0     0          0         kubernetes.io/os=linux 12m
eks-node-monitoring-agent      3       3       3     3          3         kubernetes.io/os=linux 12m
eks-pod-identity-agent         3       3       3     3          3         <none>                 12m
kube-proxy                     3       3       3     3          3         <none>                 12m
s3-csi-node                    2       2       2     2          2         kubernetes.io/os=linux 12m

Verifica che tutti i componenti aggiuntivi siano installati nel cluster:

eksctl get addons --cluster ${EKS_CLUSTER_NAME} --region ${AWS_REGION}

L'output previsto dovrebbe essere simile al seguente:

NAME                           VERSION              STATUS    ISSUES    IAMROLE                                           UPDATE AVAILABLE    CONFIGURATION VALUES    POD IDENTITY ASSOCIATION ROLES
aws-mountpoint-s3-csi-driver   v1.15.0-eksbuild.1   ACTIVE    0    arn:aws:iam::143095308808:role/eksctl-eks-rt-inference-us-east-1-addon-aws-m-Role1-RAUjk4sJnc0L
coredns                        v1.12.1-eksbuild.2   ACTIVE    0
eks-node-monitoring-agent      v1.3.0-eksbuild.2    ACTIVE    0
eks-pod-identity-agent         v1.3.7-eksbuild.2    ACTIVE    0
kube-proxy                     v1.33.0-eksbuild.2   ACTIVE    0
metrics-server                 v0.7.2-eksbuild.3    ACTIVE    0
vpc-cni                        v1.19.5-eksbuild.1   ACTIVE    0

3. Installa Karpenter

Installa il controller Karpenter sui nodi di lavoro della CPU (cpu-worker) per ottimizzare i costi e conservare le risorse della GPU. Lo installeremo nel namespace «kube-system» e specificheremo l'account di servizio «karpenter» che abbiamo definito durante la creazione del cluster. Inoltre, questo comando configura il nome del cluster e una coda di interruzione delle istanze Spot per i nodi della CPU. Karpenter utilizzerà IRSA per assumere questo ruolo IAM.

# Logout of helm registry before pulling from public ECR helm registry logout public.ecr.aws # Install Karpenter helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version "${KARPENTER_VERSION}" --namespace "kube-system" --create-namespace \ --set "settings.clusterName=${EKS_CLUSTER_NAME}" \ --set "settings.interruptionQueue=${EKS_CLUSTER_NAME}" \ --set controller.resources.requests.cpu=1 \ --set controller.resources.requests.memory=1Gi \ --set controller.resources.limits.cpu=1 \ --set controller.resources.limits.memory=1Gi \ --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"="arn:aws:iam::${AWS_ACCOUNT_ID}:role/${EKS_CLUSTER_NAME}-karpenter" \ --wait

L'output previsto dovrebbe essere simile al seguente:

Release "karpenter" does not exist. Installing it now.
Pulled: public.ecr.aws/karpenter/karpenter:1.5.0
Digest: sha256:9a155c7831fbff070669e58500f68d7ccdcf3f7c808dcb4c21d3885aa20c0a1c
NAME: karpenter
LAST DEPLOYED: Thu Jun 19 09:57:06 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None

Verifica che Karpenter sia in esecuzione:

kubectl get pods -n kube-system -l app.kubernetes.io/name=karpenter

L'output previsto dovrebbe essere simile al seguente:

NAME                       READY   STATUS    RESTARTS   AGE
karpenter-555895dc-865bc   1/1     Running   0          5m58s
karpenter-555895dc-j7tk9   1/1     Running   0          5m58s

4. Configurazione Karpenter NodePools

In questo passaggio, configuriamo CPU e GPU Karpenter che si escludono a vicenda. NodePools Il limits campo nelle NodePool specifiche limita le risorse totali massime (ad esempio, CPU, memoria GPUs) che ciascuna di esse NodePool può consumare su tutti i nodi assegnati, impedendo il provisioning di nodi aggiuntivi se questi limiti vengono superati. Sebbene NodePools supporti ampie categorie di istanze (ad esempio,g)c, la specificazione di tipi di istanze, tipi di capacità e limiti di risorse specifici consente di stimare più facilmente i costi per i carichi di lavoro on-demand. In questi casi NodePools, utilizziamo una serie diversificata di tipi di istanze all'interno della famiglia di istanze G5. Ciò consente a Karpenter di selezionare automaticamente il tipo di istanza più appropriato in base alle richieste di risorse dei pod, ottimizzando l'utilizzo delle risorse e rispettando i limiti totali. NodePool Per ulteriori informazioni, consulta Creazione. NodePools

Configura la GPU NodePool

In questo caso NodePool, stabiliamo dei limiti di risorse per gestire il provisioning di nodi con funzionalità GPU. Questi limiti sono progettati per limitare le risorse totali su tutti i nodi del pool, consentendo un massimo di 10 istanze in totale. Ogni istanza può essere g5.xlarge (4 v, CPUs 16 GiB di memoria, 1 GPU) o g5.2xlarge (8 vCPUs, 32 GiB di memoria, 1 GPU), purché la CPUs v totale non superi 80, la memoria totale non superi 320 GiB e il totale non superi 10. GPUs Ad esempio, il pool può fornire 10 istanze g5.2xlarge (80 v, CPUs 320 GiB, 10) o 10 istanze g5.xlarge (40 v, 160 GiB, 10 GPUs) o un mix come 5 g5.xlarge e 5 g5.2xlarge (60 vCPUs, 240 GiB, GPUs 10), garantendo la flessibilità in base alle richieste del carico di lavoro e rispettando i vincoli delle risorse. CPUs GPUs

Inoltre, specifichiamo l'ID della variante Nvidia dell'AMI Bottlerocket. Infine, abbiamo stabilito una politica di interruzione per rimuovere i nodi vuoti dopo 30 minuti (consolidateAfter: 30m) e abbiamo impostato una durata massima dei nodi di 30 giorni (expireAfter: 720h) per ottimizzare i costi e mantenere lo stato dei nodi per le attività che richiedono un uso intensivo della GPU. Per ulteriori informazioni, consulta Disabilitare il consolidamento di Karpenter per carichi di lavoro sensibili alle interruzioni e Utilizzare Finished to Auto Clean-Up Kubernetes Jobs. ttlSecondsAfter

cat <<EOF | envsubst | kubectl apply -f - apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-a10g-inference-g5 spec: template: metadata: labels: role: gpu-worker gpu-type: nvidia-a10g spec: requirements: - key: node.kubernetes.io/instance-type operator: In values: ["g5.xlarge", "g5.2xlarge"] - key: "karpenter.sh/capacity-type" operator: In values: ["on-demand"] taints: - key: nvidia.com/gpu value: "true" effect: NoSchedule nodeClassRef: name: gpu-a10g-inference-ec2 group: karpenter.k8s.aws kind: EC2NodeClass expireAfter: 720h limits: cpu: "80" memory: "320Gi" nvidia.com/gpu: "10" disruption: consolidationPolicy: WhenEmpty consolidateAfter: 30m --- apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass metadata: name: gpu-a10g-inference-ec2 spec: amiFamily: Bottlerocket amiSelectorTerms: - id: ${NVIDIA_BOTTLEROCKET_AMI} role: "KarpenterNodeRole-${EKS_CLUSTER_NAME}" subnetSelectorTerms: - tags: karpenter.sh/discovery: "${EKS_CLUSTER_NAME}" securityGroupSelectorTerms: - tags: karpenter.sh/discovery: "${EKS_CLUSTER_NAME}" tags: nvidia.com/gpu: "true" EOF

L'output previsto dovrebbe essere simile al seguente:

nodepool.karpenter.sh/gpu-a10g-inference-g5 created
ec2nodeclass.karpenter.k8s.aws/gpu-a10g-inference-ec2 created

Verifica che NodePool sia stato creato e integro:

kubectl get nodepool gpu-a10g-inference-g5 -o yaml

Cerca status.conditions «mi piaceValidationSucceeded: True» e Ready: True verifica che NodePool sia sano. NodeClassReady: True

Configura la CPU NodePool

In questo caso NodePool, abbiamo fissato dei limiti per supportare circa 50 istanze, allineandoci a un carico di lavoro della CPU moderato (ad esempio, 100-200 pod) e alle quote vCPU AWS tipiche (ad esempio 128-1152). I limiti vengono calcolati presupponendo che si NodePool debba scalare fino a 50 istanze m7.xlarge: CPU (4 v CPUs per istanza × 50 istanze = 200 v) CPUs e memoria (16 GiB per istanza × 50 istanze = 800 GiB). Questi limiti sono progettati per limitare le risorse totali su tutti i nodi del pool, consentendo fino a 50 istanze da m7g.xlarge (ciascuna con 4 v e CPUs 16 GiB di memoria), purché la v totale non superi 200 e la memoria totale CPUs non superi 800 GiB.

Inoltre, specifichiamo l'ID della variante standard dell'AMI Bottlerocket. Infine, abbiamo stabilito una politica di interruzione per rimuovere i nodi vuoti dopo 60 minuti (consolidateAfter: 60m) e abbiamo impostato una durata massima dei nodi di 30 giorni (expireAfter: 720h) per ottimizzare i costi e mantenere lo stato dei nodi per le attività che richiedono un uso intensivo della GPU. Per ulteriori informazioni, consulta Disabilitare il consolidamento di Karpenter per carichi di lavoro sensibili alle interruzioni e Utilizzare Finished to Auto Clean-Up Kubernetes Jobs. ttlSecondsAfter

cat <<EOF | envsubst | kubectl apply -f - apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: cpu-inference-m7gxlarge spec: template: metadata: labels: role: cpu-worker spec: requirements: - key: node.kubernetes.io/instance-type operator: In values: ["m7g.xlarge"] - key: karpenter.sh/capacity-type operator: In values: ["on-demand"] taints: - key: role value: cpu-intensive effect: NoSchedule nodeClassRef: name: cpu-inference-m7gxlarge-ec2 group: karpenter.k8s.aws kind: EC2NodeClass expireAfter: 720h limits: cpu: "200" memory: "800Gi" disruption: consolidationPolicy: WhenEmpty consolidateAfter: 60m --- apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass metadata: name: cpu-inference-m7gxlarge-ec2 spec: amiFamily: Bottlerocket amiSelectorTerms: - id: ${STANDARD_BOTTLEROCKET_AMI} role: "KarpenterNodeRole-${EKS_CLUSTER_NAME}" subnetSelectorTerms: - tags: karpenter.sh/discovery: "${EKS_CLUSTER_NAME}" securityGroupSelectorTerms: - tags: karpenter.sh/discovery: "${EKS_CLUSTER_NAME}" EOF

L'output previsto dovrebbe essere simile al seguente:

nodepool.karpenter.sh/cpu-inference-m7gxlarge created
ec2nodeclass.karpenter.k8s.aws/cpu-inference-m7gxlarge-ec2 created

Verifica che NodePool sia stato creato e integro:

kubectl get nodepool cpu-inference-m7gxlarge -o yaml

Cerca status.conditions «mi piaceValidationSucceeded: True» e Ready: True verifica che NodePool sia sano. NodeClassReady: True

5. Implementa un GPU Pod per esporre una GPU

È necessario il Nvidia Device Plugin per consentire a Kubernetes di esporre i dispositivi GPU al cluster Kubernetes. In genere, è necessario distribuire il plug-in come un file DaemonSet; tuttavia, l'AMI Bottlerocket preinstalla il plug-in come parte dell'AMI. Ciò significa che quando si utilizza Bottlerocket AMIs, non è necessario implementare il plug-in del dispositivo Nvidia. DaemonSet Per saperne di più, consulta Kubernetes Device Plugin to expose. GPUs

Distribuisci un pod di esempio

Karpenter agisce in modo dinamico: effettua il provisioning dei nodi GPU quando un carico di lavoro (pod) richiede risorse GPU. Per verificare che i pod siano in grado di richiedere e utilizzare GPUs, implementa un pod che richieda la nvidia.com/gpu risorsa nei suoi limiti (ad esempio,). nvidia.com/gpu: 1 Per ulteriori informazioni su queste etichette, consulta Pianificare i carichi di lavoro con i requisiti della GPU utilizzando le etichette Known.

cat <<EOF | envsubst | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: gpu-nvidia-smi spec: restartPolicy: OnFailure tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" nodeSelector: role: gpu-worker # Matches GPU NodePool's label containers: - name: cuda-container image: nvidia/cuda:12.9.1-base-ubuntu20.04 command: ["nvidia-smi"] resources: limits: nvidia.com/gpu: 1 requests: nvidia.com/gpu: 1 EOF

L'output previsto dovrebbe essere simile al seguente:

pod/gpu-ndivia-smi created

Aspetta un minuto e controlla se lo stato del Pod è «In sospeso»ContainerCreating, «In esecuzione» e quindi «Completato»:

kubectl get pod gpu-nvidia-smi -w

Verifica che il nodo del pod appartenga alla GPU: NodePool

kubectl get node $(kubectl get pod gpu-nvidia-smi -o jsonpath='{.spec.nodeName}') -o custom-columns="Name:.metadata.name,Nodepool:.metadata.labels.karpenter\.sh/nodepool"

L'output previsto dovrebbe essere simile al seguente:

Name                             Nodepool
ip-192-168-83-245.ec2.internal   gpu-a10g-inference-g5

Controlla i log del pod:

kubectl logs gpu-nvidia-smi

L'output previsto dovrebbe essere simile al seguente:

Thu Jul 17 04:31:33 2025 +---------------------------------------------------------------------------------------+ | NVIDIA-SMI 570.148.08 Driver Version: 570.148.08 CUDA Version: 12.9 | |-----------------------------------------+----------------------+----------------------+ | 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 A10G On | 00000000:00:1E.0 Off | 0 | | 0% 30C P8 9W / 300W | 0MiB / 23028MiB | 0% Default | | | | N/A | +---------------------------------------------------------------------------------------+ +---------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=======================================================================================| | No running processes found | +---------------------------------------------------------------------------------------+

6. (Facoltativo) Preparazione e caricamento degli artefatti del modello per la distribuzione

In questa fase, implementerai un servizio modello per la classificazione delle immagini in tempo reale, iniziando con il caricamento dei pesi dei modelli in un bucket Amazon S3. A titolo dimostrativo, stiamo utilizzando il modello di visione open source GPUNet-0 di NVIDIA GPUNet, che supporta l'inferenza a bassa latenza sulle immagini utilizzando NVIDIA e TensorRT. GPUs Questo modello è preconfigurato ImageNet, ci consente di classificare al volo gli oggetti nelle foto o nei flussi video ed è considerato un modello piccolo con 11,9 milioni di parametri.

Configurazione dell'ambiente

Per scaricare i pesi del modello GPUNet -0 In questo passaggio, è necessario accedere al catalogo NGC di NVIDIA e al Docker installato sul computer locale. Segui questi passaggi per configurare un account gratuito e configurare la CLI NGC:

  • Crea un account NGC gratuito e genera una chiave API dalla dashboard NGC (Icona utente > Configurazione > Genera chiave API > Genera chiave personale > NGC Catalog).

  • Scarica e installa NGC CLI Linux/macOS/Windows () e configura la CLI utilizzando:. ngc config set Inserisci la tua chiave API quando richiesto; imposta org su nvidia e premi Invio per accettare le impostazioni predefinite per gli altri. In caso di successo, dovresti vedere qualcosa del tipo:. Successfully saved NGC configuration to /Users/your-username/.ngc/config

Verifica le autorizzazioni dell'account di servizio

Prima di iniziare, controlla le autorizzazioni dell'account del servizio Kubernetes:

kubectl get serviceaccount s3-csi-driver-sa -n kube-system -o yaml

Durante la creazione del cluster, abbiamo collegato la CSIDriver policy S3 a un ruolo IAM e annotato l'account del servizio («s3-»). csi-driver-sa I driver pod Mountpoint S3 CSI ereditano le autorizzazioni del ruolo IAM quando interagiscono con S3. L'output previsto dovrebbe essere simile al seguente:

apiVersion: v1 kind: ServiceAccount metadata: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::143095308808:role/eksctl-eks-rt-inference-us-east-1-addon-aws-m-Role1-fpXXjRYdKN8r creationTimestamp: "2025-07-17T03:55:29Z" labels: app.kubernetes.io/component: csi-driver app.kubernetes.io/instance: aws-mountpoint-s3-csi-driver app.kubernetes.io/managed-by: EKS app.kubernetes.io/name: aws-mountpoint-s3-csi-driver name: s3-csi-driver-sa namespace: kube-system resourceVersion: "2278" uid: 50b36272-6716-4c68-bdc3-c4054df1177c

Aggiungi una tolleranza

Il driver S3 CSI funziona come se fosse DaemonSet su tutti i nodi. I pod utilizzano il driver CSI su quei nodi per montare i volumi S3. Per consentirgli di programmarsi sui nostri nodi GPU che presentano difetti, aggiungi una tolleranza a: DaemonSet

kubectl patch daemonset s3-csi-node -n kube-system --type='json' -p='[{"op": "add", "path": "/spec/template/spec/tolerations/-", "value": {"key": "nvidia.com/gpu", "operator": "Exists", "effect": "NoSchedule"}}]'

L'output previsto dovrebbe essere simile al seguente:

daemonset.apps/s3-csi-node patched

Carica i pesi dei modelli su S3

In questo passaggio, creerai un bucket Amazon S3, scaricherai i pesi del modello GPUNet -0 da NVIDIA GPU Cloud (NGC) e li caricherai nel bucket. La nostra applicazione accederà a questi pesi in fase di esecuzione per scopi di inferenza.

Crea il tuo bucket Amazon S3:

aws s3 mb s3://${S3_BUCKET_NAME} --region ${AWS_REGION}

Abilita il controllo delle versioni S3 per il bucket, per evitare che eliminazioni e sovrascritture accidentali causino una perdita di dati immediata e permanente:

aws s3api put-bucket-versioning --bucket ${S3_BUCKET_NAME} --versioning-configuration Status=Enabled

Applica una regola del ciclo di vita al bucket per rimuovere le versioni degli oggetti sovrascritte o eliminate 14 giorni dopo che sono diventate non correnti, rimuovere i marcatori di eliminazione scaduti e rimuovere i caricamenti incompleti in più parti dopo 7 giorni. Per ulteriori informazioni, consulta Esempi di configurazioni del ciclo di vita di S3.

aws s3api put-bucket-lifecycle-configuration --bucket $S3_BUCKET_NAME --lifecycle-configuration '{"Rules":[{"ID":"LifecycleRule","Status":"Enabled","Filter":{},"Expiration":{"ExpiredObjectDeleteMarker":true},"NoncurrentVersionExpiration":{"NoncurrentDays":14},"AbortIncompleteMultipartUpload":{"DaysAfterInitiation":7}}]}'

Scarica i pesi del modello GPUNet -0 da NGC. Ad esempio, su macOS:

ngc registry model download-version nvidia/dle/gpunet_0_pyt_ckpt:21.12.0_amp --dest ~/downloads
Nota

Potrebbe essere necessario modificare questo comando di download per il sistema operativo in uso. Affinché questo comando funzioni su un sistema Linux, probabilmente è necessario creare la directory come parte del comando (ad esempio,mkdir ~/downloads).

L'output previsto dovrebbe essere simile al seguente:

{ "download_end": "2025-07-18 08:22:39", "download_start": "2025-07-18 08:22:33", "download_time": "6s", "files_downloaded": 1, "local_path": "/Users/your-username/downloads/gpunet_0_pyt_ckpt_v21.12.0_amp", "size_downloaded": "181.85 MB", "status": "Completed", "transfer_id": "gpunet_0_pyt_ckpt[version=21.12.0_amp]" }

Rinomina il file del checkpoint in modo che corrisponda alla denominazione prevista nel codice dell'applicazione nei passaggi successivi (non è necessaria alcuna estrazione, poiché si tratta di un checkpoint PyTorch *.pth.tar standard contenente il dizionario dello stato del modello):

mv ~/downloads/gpunet_0_pyt_ckpt_v21.12.0_amp/0.65ms.pth.tar gpunet-0.pth

Abilita AWS Common Runtime nella AWS CLI per ottimizzare il throughput di S3:

aws configure set s3.preferred_transfer_client crt

Carica i pesi del modello nel tuo bucket S3:

aws s3 cp gpunet-0.pth s3://${S3_BUCKET_NAME}/gpunet-0.pth

L'output previsto dovrebbe essere simile al seguente:

upload: ./gpunet-0.pth to s3://eks-rt-inference-models-us-east-1-1752722786/gpunet-0.pth

Crea il Model Service

In questo passaggio, configurerai un'applicazione web FastAPI per la classificazione delle immagini con accelerazione GPU utilizzando il modello di visione -0. GPUNet L'applicazione scarica i pesi dei modelli da Amazon S3 in fase di esecuzione, recupera l'architettura del modello dal repository NVIDIA per la memorizzazione nella cache e scarica le etichette delle classi tramite HTTP. ImageNet L'applicazione include la preelaborazione, la trasformazione e l'esposizione di due endpoint: un GET root per il controllo dello stato e un endpoint POST che accetta l'URL di un'immagine. /predict

Utilizziamo il modello utilizzando FastAPI con PyTorch caricamento dei pesi da Amazon S3 in fase di esecuzione in una configurazione containerizzata per la prototipazione rapida e l'implementazione di Kubernetes. Per altri metodi come il batching ottimizzato o i motori ad alto rendimento, consulta Serving ML Models.

Creazione dell'applicazione

Create una directory per i file dell'applicazionemodel-testing, ad esempio, quindi modificate le directory al suo interno e aggiungete il codice seguente a un nuovo file denominato: app.py

import os import torch import json import requests from fastapi import FastAPI, HTTPException from PIL import Image from io import BytesIO, StringIO import torchvision.transforms as transforms from torch.nn.functional import softmax import warnings from contextlib import redirect_stdout, redirect_stderr import argparse import boto3 app = FastAPI() # Suppress specific warnings from the model code (quantization is optional and unused here) warnings.simplefilter("ignore", UserWarning) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # Load model code from cache (if present) # Use backed cache directory torch.hub.set_dir('/cache/torch/hub') # Allowlist for secure deserialization (handles potential issues in older checkpoints) torch.serialization.add_safe_globals([argparse.Namespace]) # Load the model architecture only on container startup (changed to pretrained=False) # Precision (FP32 for full accuracy, could be 'fp16' for speed on Ampere+ GPUs) with redirect_stdout(StringIO()), redirect_stderr(StringIO()): gpunet = torch.hub.load('NVIDIA/DeepLearningExamples:torchhub', 'nvidia_gpunet', pretrained=False, model_type='GPUNet-0', model_math='fp32') # Download weights from S3 if not present, then load them model_path = os.getenv('MODEL_PATH', '/cache/torch/hub/checkpoints/gpunet-0.pth') os.makedirs(os.path.dirname(model_path), exist_ok=True) # Ensure checkpoints dir exists if not os.path.exists(model_path): s3 = boto3.client('s3') s3.download_file(os.getenv('S3_BUCKET_NAME'), 'gpunet-0.pth', model_path) checkpoint = torch.load(model_path, map_location=device, weights_only=True) gpunet.load_state_dict(checkpoint['state_dict']) # Move to GPU/CPU gpunet.to(device) gpunet.eval() # Preprocessing preprocess = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ]) # Load ImageNet labels labels_url = "https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json" response = requests.get(labels_url) json_data = json.loads(response.text) labels = [json_data[str(i)][1].replace('_', ' ') for i in range(1000)] # Required, FastAPI root @app.get("/") async def hello(): return {"status": "hello"} # Serve model requests @app.post("/predict") async def predict(image_url: str): try: response = requests.get(image_url) response.raise_for_status() img = Image.open(BytesIO(response.content)).convert("RGB") input_tensor = preprocess(img).unsqueeze(0).to(device) with torch.no_grad(): output = gpunet(input_tensor) probs = softmax(output, dim=1)[0] top5_idx = probs.topk(5).indices.cpu().numpy() top5_probs = probs.topk(5).values.cpu().numpy() results = [{ "label": labels[idx], "probability": float(prob) } for idx, prob in zip(top5_idx, top5_probs)] return {"predictions": results} except Exception as e: raise HTTPException(status_code=400, detail=str(e))

Crea il Dockerfile

Il seguente Dockerfile crea un'immagine del contenitore per la nostra applicazione utilizzando il GPUNet modello del repository NVIDIA Deep Learning Examples for Tensor Cores. GitHub

Riduciamo le dimensioni dell'immagine del contenitore utilizzando una PyTorch base di sola esecuzione, installando solo pacchetti essenziali con pulizia della cache, prememorizzazione del codice del modello ed evitando di «caricare» pesi nell'immagine del contenitore per consentire operazioni di estrazione e aggiornamento più rapide. Per ulteriori informazioni, consulta Riduzione delle dimensioni delle immagini dei contenitori.

Nella stessa directory in cuiapp.py, creaDockerfile:

FROM pytorch/pytorch:2.4.0-cuda12.4-cudnn9-runtime # Install required system packages required for git cloning RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* # Install application dependencies RUN pip install --no-cache-dir fastapi uvicorn requests pillow boto3 timm==0.5.4 # Pre-cache the GPUNet code from Torch Hub (without weights) # Clone the repository containing the GPUNet code RUN mkdir -p /cache/torch/hub && \ cd /cache/torch/hub && \ git clone --branch torchhub --depth 1 https://github.com/NVIDIA/DeepLearningExamples NVIDIA_DeepLearningExamples_torchhub COPY app.py /app/app.py WORKDIR /app CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"]

Eseguire il test dell'applicazione

Dalla stessa directory della tua app.py eDockerfile, crea l'immagine del contenitore per l'applicazione di inferenza, mirando AMD64 all'architettura:

docker build --platform linux/amd64 -t gpunet-inference-app .

Imposta le variabili di ambiente per le tue AWS credenziali e, facoltativamente, un token di sessione. AWS Per esempio:

export AWS_REGION="us-east-1" export AWS_ACCESS_KEY_ID=ABCEXAMPLESCUJFEIELSMUHHAZ export AWS_SECRET_ACCESS_KEY=123EXAMPLEMZREoQXr8XkiicsOgWDQ5TpUsq0/Z

Esegui il contenitore localmente, inserendo AWS le credenziali come variabili di ambiente per l'accesso a S3. Per esempio:

docker run --platform linux/amd64 -p 8080:80 \ -e S3_BUCKET_NAME=${S3_BUCKET_NAME} \ -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \ -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \ -e AWS_DEFAULT_REGION=${AWS_REGION} \ gpunet-inference-app

L'output previsto dovrebbe essere simile al seguente:

INFO:     Started server process [1]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)

In una nuova finestra di terminale, testate l'endpoint di inferenza inviando un esempio di richiesta POST con un URL di immagine pubblica come parametro di query:

curl -X POST "http://localhost:8080/predict?image_url=http://images.cocodataset.org/test-stuff2017/000000024309.jpg"

L'output previsto dovrebbe essere una risposta JSON con le prime 5 previsioni, simile a questa (le etichette e le probabilità effettive possono variare leggermente in base alla precisione dell'immagine e del modello):

{"predictions":[{"label":"desk","probability":0.28885871171951294},{"label":"laptop","probability":0.24679335951805115},{"label":"notebook","probability":0.08539070934057236},{"label":"library","probability":0.030645888298749924},{"label":"monitor","probability":0.02989606373012066}]}

Esci dall'applicazione usando «Ctrl + C».

Invia il contenitore ad Amazon ECR

In questa fase, carichiamo l'immagine del contenitore per il servizio modello GPUNet -0 su Amazon Elastic Container Registry (ECR), rendendola disponibile per la distribuzione su Amazon EKS. Questo processo prevede la creazione di un nuovo repository ECR per archiviare l'immagine, l'autenticazione con ECR, quindi l'etichettatura e l'invio dell'immagine del contenitore nel nostro registro.

Innanzitutto, torna alla directory in cui hai impostato le variabili di ambiente all'inizio di questa guida. Per esempio:

cd ..

Crea un repository in Amazon ECR:

aws ecr create-repository --repository-name gpunet-inference-app --region ${AWS_REGION}

Accedi ad Amazon ECR:

aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com

L'output previsto dovrebbe essere simile al seguente:

Login Succeeded

Etichetta l'immagine:

docker tag gpunet-inference-app:latest ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/gpunet-inference-app:latest

Invia l'immagine al tuo repository Amazon ECR:

docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/gpunet-inference-app:latest

Il completamento di quest'ultimo passaggio richiede diversi minuti.

7. (Facoltativo) Esporre il Model Service

In questa fase, esporrai il tuo servizio di modello di inferenza in tempo reale esternamente su Amazon EKS utilizzando il Load AWS Balancer Controller (LBC). Ciò comporta la configurazione dell'LBC, il montaggio dei pesi del modello da Amazon S3 come volume persistente utilizzando il driver CSI Mountpoint S3, la distribuzione di un pod applicativo accelerato da GPU, la creazione di un servizio e un ingresso per fornire un Application Load Balancer (ALB) e il test dell'endpoint.

Innanzitutto, verifica l'associazione Pod Identity per AWS LBC, confermando che l'account del servizio sia collegato correttamente al ruolo IAM richiesto:

eksctl get podidentityassociation --cluster ${EKS_CLUSTER_NAME} --namespace kube-system --service-account-name aws-load-balancer-controller

L'output previsto dovrebbe essere simile al seguente:

ASSOCIATION ARN                                                    NAMESPACE    SERVICE ACCOUNT NAME        IAM ROLE ARN    OWNER ARN
arn:aws:eks:us-east-1:143095308808:podidentityassociation/eks-rt-inference-us-east-1/a-buavluu2wp1jropya    kube-system     aws-load-balancer-controller    arn:aws:iam::143095308808:role/AmazonEKSLoadBalancerControllerRole

Etichetta il tuo gruppo di sicurezza del cluster

Il AWS Load Balancer Controller supporta solo un singolo gruppo di sicurezza con la chiave del tag karpenter.sh/discovery: "${EKS_CLUSTER_NAME}" per la selezione del gruppo di sicurezza di Karpenter. Quando si crea un cluster con eksctl, il gruppo di sicurezza del cluster predefinito (che ha il "kubernetes.io/cluster/<cluster-name>: owned" tag) non viene automaticamente etichettato con tag. karpenter.sh/discovery Questo tag è essenziale per Karpenter per scoprire e collegare questo gruppo di sicurezza ai nodi che fornisce. Il collegamento di questo gruppo di sicurezza garantisce la compatibilità con il AWS Load Balancer Controller (LBC), consentendogli di gestire automaticamente le regole del traffico in entrata per i servizi esposti tramite Ingress, come il servizio modello in questi passaggi.

Esporta l'ID VPC per il tuo cluster:

CLUSTER_VPC_ID="$(aws eks describe-cluster --name ${EKS_CLUSTER_NAME} --query cluster.resourcesVpcConfig.vpcId --output text)"

Esporta il gruppo di sicurezza predefinito per il tuo cluster:

CLUSTER_SG_ID="$(aws ec2 describe-security-groups --filters Name=vpc-id,Values=$CLUSTER_VPC_ID Name=tag-key,Values=kubernetes.io/cluster/${EKS_CLUSTER_NAME} --query 'SecurityGroups[].[GroupId]' --output text)"

Aggiungi il karpenter.sh/discovery tag al gruppo di sicurezza del cluster predefinito. Ciò consentirà ai nostri EC2 NodeClass selettori di CPU e GPU di utilizzarlo:

aws ec2 create-tags --resources ${CLUSTER_SG_ID} --tags Key=karpenter.sh/discovery,Value=${EKS_CLUSTER_NAME}

Verifica che il tag sia stato aggiunto:

aws ec2 describe-security-groups --group-ids ${CLUSTER_SG_ID} --query "SecurityGroups[].Tags"

Tra i risultati, dovresti vedere quanto segue con il tag e il nome del cluster. Per esempio:

{ "Key": "karpenter.sh/discovery", "Value": "eks-rt-inference-us-east-1" }

Configurazione del AWS Load Balancer Controller (LBC)

L' AWS LBC è essenziale per gestire il traffico in ingresso ai AI/ML carichi di lavoro su Amazon EKS, garantendo l'accesso agli endpoint di inferenza o alle pipeline di elaborazione dei dati. Integrandosi con AWS Application Load Balancers (ALB) e Network Load Balancers (NLB), LBC indirizza dinamicamente il traffico verso applicazioni containerizzate, come quelle che eseguono modelli linguistici di grandi dimensioni, modelli di visione artificiale o servizi di inferenza in tempo reale. Poiché abbiamo già creato l'account del servizio e la Pod Identity Association durante la creazione del cluster, li abbiamo impostati in modo che corrispondano a quanto definito nella nostra configurazione del cluster (). serviceAccount.name aws-load-balancer-controller

Aggiungi il repository AWS di grafici Helm eks-charts di proprietà:

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

Aggiorna i tuoi repository Helm locali con i grafici più recenti:

helm repo update eks

Implementa AWS LBC utilizzando Helm, specificando il nome del cluster EKS e facendo riferimento all'account di servizio precreato:

helm install aws-load-balancer-controller eks/aws-load-balancer-controller \ -n kube-system \ --set clusterName=${EKS_CLUSTER_NAME} \ --set serviceAccount.create=false \ --set serviceAccount.name=aws-load-balancer-controller

L'output previsto dovrebbe essere simile al seguente:

NAME: aws-load-balancer-controller
LAST DEPLOYED: Wed Jul 9 15:03:31 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
AWS Load Balancer controller installed!

Montate il modello in un volume persistente

In questa fase, monterai i pesi dei modelli dal tuo bucket Amazon S3 utilizzando PersistentVolume un (PV) supportato dal driver CSI Mountpoint for Amazon S3. Ciò consente ai pod Kubernetes di accedere agli oggetti S3 come file locali, eliminando i download ad uso intensivo di risorse verso contenitori di storage temporanei o contenitori init, ideali per modelli di grandi dimensioni da più gigabyte.

Il PV monta l'intera radice del bucket (nessun percorso specificato involumeAttributes), supporta l'accesso simultaneo in sola lettura da più pod ed espone file come il model weights (/models/gpunet-0.pth) all'interno del contenitore per l'inferenza. Ciò garantisce che il fallback «download» nella nostra applicazione (app.py) non si attivi perché il file esiste tramite il mount. Disaccoppiando il modello dall'immagine del contenitore, ciò consente l'accesso condiviso e gli aggiornamenti indipendenti delle versioni del modello senza ricostruire l'immagine.

Crea il (PV) PersistentVolume

Crea una risorsa PersistentVolume (PV) per montare il bucket S3 contenente i pesi del modello, abilitando l'accesso in sola lettura a più pod senza scaricare file in fase di esecuzione:

cat <<EOF | envsubst | kubectl apply -f - apiVersion: v1 kind: PersistentVolume metadata: name: s3-model-pv spec: capacity: storage: 5Gi # Ignored by the driver; can be any value accessModes: - ReadOnlyMany # Read only persistentVolumeReclaimPolicy: Retain storageClassName: "" # Required for static provisioning claimRef: namespace: default # Adjust if you prefer a different namespace name: s3-model-pvc mountOptions: - allow-other # Enables multi-user access (useful for non-root pods) - region ${AWS_REGION} # Optional, include if your bucket is in a different region than the cluster csi: driver: s3.csi.aws.com volumeHandle: gpunet-model-volume # Must be unique across all PVs volumeAttributes: bucketName: ${S3_BUCKET_NAME} EOF

Crea il PersistentVolumeClaim (PVC)

Create un PersistentVolumeClaim (PVC) da collegare al PV, richiedendo l'accesso in sola lettura ai dati del modello S3 montato:

cat <<EOF | envsubst | kubectl apply -f - apiVersion: v1 kind: PersistentVolumeClaim metadata: name: s3-model-pvc spec: accessModes: - ReadOnlyMany storageClassName: "" # Required for static provisioning resources: requests: storage: 5Gi # Ignored, match PV capacity volumeName: s3-model-pv # Bind to the PV created above EOF

Distribuzione dell'applicazione

Implementa l'applicazione di inferenza come implementazione Kubernetes, monta il volume persistente supportato da S3 per l'accesso al modello, applica i selettori e le tolleranze dei nodi GPU e imposta le variabili di ambiente per il percorso del modello. Questa distribuzione imposta il percorso del modello (env var of"/models/gpunet-0.pth"), quindi la nostra applicazione (in) utilizzerà questo percorso per impostazione predefinita. app.py Con il montaggio del volume di Deployment su /models (sola lettura), il download del modello non si attiva se il file è già presente tramite il PVC.

cat <<EOF | envsubst | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: gpunet-inference-app spec: replicas: 1 selector: matchLabels: app: gpunet-inference-app template: metadata: labels: app: gpunet-inference-app spec: tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" nodeSelector: role: gpu-worker containers: - name: inference image: ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/gpunet-inference-app:latest ports: - containerPort: 80 env: - name: MODEL_PATH value: "/models/gpunet-0.pth" resources: limits: nvidia.com/gpu: 1 requests: nvidia.com/gpu: 1 volumeMounts: - name: model-volume mountPath: /models readOnly: true volumes: - name: model-volume persistentVolumeClaim: claimName: s3-model-pvc EOF

Karpenter impiegherà alcuni minuti per effettuare il provisioning di un nodo GPU, se non è già disponibile. Verifica che il pod di inferenza sia in uno stato «In esecuzione»:

kubectl get pods -l app=gpunet-inference-app

L'output previsto dovrebbe essere simile al seguente:

NAME                               READY   STATUS    RESTARTS   AGE
gpunet-inference-app-5d4b6c7f8-abcde        1/1     Running   0          2m

Esponi il servizio con Ingress e Load Balancer

Crea un servizio ClusterIP per esporre la distribuzione dell'inferenza internamente all'interno del cluster EKS, mirando alla porta dell'applicazione:

cat <<EOF | envsubst | kubectl apply -f - apiVersion: v1 kind: Service metadata: name: gpunet-model-service spec: type: ClusterIP ports: - port: 80 targetPort: 80 selector: app: gpunet-inference-app EOF

Crea una risorsa Ingress per fornire un Application Load Balancer (ALB) con accesso a Internet tramite AWS LBC, indirizzando il traffico esterno al servizio di inferenza:

cat <<EOF | envsubst | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: gpunet-model-ingress annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip spec: ingressClassName: alb rules: - http: paths: - path: / pathType: Prefix backend: service: name: gpunet-model-service port: number: 80 EOF

Attendi qualche minuto affinché Application Load Balancer (ALB) completi il provisioning. Monitora lo stato delle risorse Ingress per confermare che l'ALB sia stato fornito:

kubectl get ingress gpunet-model-ingress

L'output previsto dovrebbe essere simile al seguente (con il campo ADDRESS compilato):

NAME                   CLASS   HOSTS   ADDRESS                                         PORTS   AGE
gpunet-model-ingress   alb     *       k8s-default-gpunetmo-183de3f819-516310036.us-east-1.elb.amazonaws.com   80      6m58s

Estrai ed esporta il nome host ALB dallo stato Ingress per utilizzarlo nei test successivi:

export ALB_HOSTNAME=$(kubectl get ingress gpunet-model-ingress -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')

Prova il Model Service

Convalida l'endpoint di inferenza esposto inviando una richiesta POST con un URL di immagine di esempio (ad esempio, dal set di dati COCO), simulando la previsione in tempo reale:

curl -X POST "http://${ALB_HOSTNAME}/predict?image_url=http://images.cocodataset.org/test-stuff2017/000000024309.jpg"

L'output previsto dovrebbe essere una risposta JSON con le prime 5 previsioni, simile a questa (le etichette e le probabilità effettive possono variare leggermente in base alla precisione dell'immagine e del modello):

{"predictions":[{"label":"desk","probability":0.2888975441455841},{"label":"laptop","probability":0.2464350312948227},{"label":"notebook","probability":0.08554483205080032},{"label":"library","probability":0.030612602829933167},{"label":"monitor","probability":0.029896672815084457}]}

Facoltativamente, puoi continuare a testare altre immagini in una nuova richiesta POST. Per esempio:

http://images.cocodataset.org/test-stuff2017/000000024309.jpg
http://images.cocodataset.org/test-stuff2017/000000028117.jpg
http://images.cocodataset.org/test-stuff2017/000000006149.jpg
http://images.cocodataset.org/test-stuff2017/000000004954.jpg

Conclusioni

In questa guida, configurerai un cluster Amazon EKS ottimizzato per carichi di lavoro di inferenza in tempo reale accelerati da GPU. Hai effettuato il provisioning di un cluster con EC2 istanze G5, installato il driver CSI Mountpoint S3, EKS Pod Identity Agent, EKS Node Monitoring Agent, Bottlerocket AMI,AWSLoad Balancer Controller (LBC) e Karpenter per gestire CPU e GPU. NodePools Hai utilizzato il NVIDIA Device Plugin per abilitare la pianificazione delle GPU e configurato S3 con un e per l'accesso al modello. PersistentVolume PersistentVolumeClaim Hai convalidato la configurazione distribuendo un pod GPU di esempio, configurando l'accesso al modello NVIDIA GPUNet-0 su Amazon S3, abilitando l'inizializzazione del pod ed esponendo il servizio di inferenza tramite Application Load Balancer. Per utilizzare appieno il cluster, configura EKS Node Monitoring Agent con riparazione automatica. Assicurati di condurre test di benchmark, tra cui valutazioni delle prestazioni, della latenza e del throughput della GPU per ottimizzare i tempi di risposta. Per ulteriori informazioni, consulta Utilizzo degli strumenti di monitoraggio e osservabilità per i carichi di lavoro. AI/ML

Eliminazione

Per evitare di incorrere in addebiti futuri, è necessario eliminare manualmente lo CloudFormation stack associato per eliminare tutte le risorse create durante questa guida, inclusa la rete VPC.

Elimina lo CloudFormation stack usando il flag con eksctl: --wait

eksctl delete cluster --region ${AWS_REGION} --name ${EKS_CLUSTER_NAME} --wait

Al termine, dovresti vedere il seguente output di risposta:

2025-07-29 13:03:55 [✔]  all cluster resources were deleted

Elimina il bucket Amazon S3 creato durante questa guida utilizzando la console Amazon S3.