Contribuisci 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
Suggerimento
Registrati
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, consentendo di ottenere rapidamente un cluster funzionale e preconfigurato. Lungo il percorso, distribuiremo esempi di carichi di lavoro per convalidare la configurazione, spiegheremo i concetti architettonici chiave (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?) AL2023 e delineeremo 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. Infatti, ha l’obiettivo di aiutare 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 AWS servizi e l'esposizione degli endpoint di inferenza. Nel complesso, si concentra sul 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 di EC2. Se stai cercando configurazioni di cluster o flussi di lavoro specifici per AWS Inferenza, consulta i nostri workshop su. end-to-end Usa le istanze AWS Inferentia con Amazon EKS for Machine Learning Risorse per iniziare a utilizzare IA/ML su Amazon EKS
Prima di iniziare
Prima di iniziare, esegui le seguenti attività:
Architecture
L’inferenza online in tempo reale si riferisce al processo di utilizzo di un modello di machine learning addestrato per generare previsioni oppure 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 di 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 ai 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 APIs modelli come i trasformatori e le interazioni degli utenti tramite il frontend. Tieni presente che, sebbene svolgano spesso un ruolo importante nei sistemi di inferenza in tempo reale, i database vettoriali e le pipeline di generazione potenziata da recupero dati (RAG) non sono trattati in questa guida. Un’architettura tipica spesso include almeno:
-
Servizio frontend: funge da interfaccia utente, gestisce la logica lato client, rende 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 WebSockets per gli aggiornamenti in streaming o APIs per lo scambio di dati strutturati. 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 l’accesso esterno e le regole di routing per migliorare la sicurezza e la modellazione del traffico.
-
Servizio di inferenza: funge da base per i calcoli di intelligenza artificiale, in esecuzione GPUs con una VRAM sufficiente (ad esempio, 8-12 GB per modelli come DistilBert) per eseguire incorporamenti vettoriali, estrazione della conoscenza e inferenza del modello (ad esempio, esposto APIs per richieste in batch o WebSockets per lo streaming in tempo reale) utilizzando modelli personalizzati o open source. Questo isolamento previene i conflitti di dipendenza, consente di aggiornare i modelli senza tempo di inattività e agevola il dimensionamento 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.
-
Istanze Amazon G5 EC2
: per attività di inferenza a uso intensivo di GPU, utilizziamo i tipi di istanze G5 EC2 g5.xlarge e g5.2xlarge, che dispongono di una (1) singola GPU NVIDIA A10G con 24 GB di memoria (ad esempio, 8 miliardi di parametri in FP16). Queste GPU, basate sull’architettura NVIDIA Ampere, sono alimentate da GPU NVIDIA A10G Tensor Core e processori AMD EPYC di seconda generazione, supportano 4-8 vCPU, larghezza di banda della rete fino a 10 Gbps e 250-450 GB di archiviazione SSD NVMe locale, garantendo un rapido spostamento dei dati e potenza di calcolo per modelli complessi. Per questo motivo, sono ideali per attività di inferenza a bassa latenza e ad alto throughput. La scelta di un tipo di istanza EC2 è specifica dell’applicazione, dipende dal modello (ad esempio, immagine, video, modello di testo) e dai requisiti di latenza e throughput effettivo. Ad esempio, se utilizzi un modello di immagine e/o video, potresti voler utilizzare le istanze EC2 P5 per una latenza ottimale in tempo reale. Ti consigliamo di iniziare con le istanze G5 EC2 in quanto forniscono un buon punto di partenza per iniziare subito a funzionare, per poi 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 pre-elaborazione dei dati, la gestione delle richieste API, l’hosting del controller Karpenter, componenti aggiuntivi e altri componenti di sistema, utilizziamo il tipo di istanza EC2 M7g m5.xlarge. 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 ed è alimentata da processori Graviton3. AWS La scelta di un tipo di istanza EC2 è 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 EC2 , che utilizzano anch’esse processori Graviton3 ma sono ottimizzate per prestazioni di calcolo più elevate rispetto alle istanze M7g per determinati casi d’uso. In alternativa, le istanze C8g EC2 più recenti (se disponibili) offrono prestazioni di elaborazione migliori fino al 30% rispetto alle istanze C7g. Consigliamo di iniziare con le istanze M7g EC2 per la loro efficienza in termini di costi e la compatibilità con un’ampia gamma di carichi di lavoro (ad esempio, server di applicazioni, microservizi, server di gioco, archivi dati di medie dimensioni) per poi valutare se sono la soluzione giusta per i propri carichi di lavoro attraverso test di benchmark delle prestazioni. -
Driver CSI Mountpoint di Amazon S3: 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 di S3 per ottimizzare l’utilizzo della memoria, essenziale per attività come l’inferenza del modello di grandi dimensioni in configurazioni sensibili ai costi e a bassa complessità. Infatti, 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, il peso) 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 archiviazione 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 throughput e dalla 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. Pod Identity di EKS offre un’alternativa semplificata al tradizionale approccio IAM Roles for Service Accounts (IRSA) utilizzando un unico principale del servizio (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 IAM role session tags e Target IAM roles.
-
Agente di monitoraggio dei nodi di EKS: per garantire la disponibilità e l’affidabilità continue dei servizi di inferenza, utilizziamo l’agente di monitoraggio dei nodi di EKS con la riparazione automatica, che rileva e sostituisce automaticamente i nodi non integri riducendo al minimo il tempo di inattività. Monitora continuamente i nodi per problemi hardware, kernel, di rete e di storage utilizzando controlli di integrità avanzati (ad esempio,). KernelReady NetworkingReady Per i nodi GPU, rileva i guasti specifici dell’acceleratore e avvia una correzione adeguata 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 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 scalabilità rapida. La scelta di un nodo AMI è specifica dell’applicazione e dipende dai requisiti di personalizzazione, sicurezza e scalabilità del carico di lavoro. Sebbene l'AL2023 AMI 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, scalabile 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. Creazione di un cluster EKS
In questa fase, creiamo un cluster con nodi CPU e un gruppo di nodi gestito utilizzando un modello eksctl basato su tecnologia. AWS CloudFormation ClusterConfig
Per impostazione predefinita, eksctl creerà un VPC dedicato per il cluster con un intervallo 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 Deploy Nodes to Private Subnets.
Verifica le 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 sono disponibili in tutte le regioni. Controlla la regione più vicina. 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 tramite il recupero di un ID AMI Bottlerocket per la regione più vicina. 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 un 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
Suggerimento
Alcune variabili (come ${AWS_REGION} e ${K8S_VERSION}) vengono definite all’inizio del blocco e utilizzate nei comandi successivi per garantire coerenza ed evitare ripetizioni. Assicurati di eseguire i comandi in sequenza in modo che questi valori vengano esportati correttamente e siano disponibili per l’utilizzo 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/')"
Crea i ruoli e le policy richiesti
Karpenter necessita di ruoli e policy IAM specifici (ad esempio, il ruolo IAM del controller Karpenter, il profilo dell’istanza e le policy) per gestire le istanze EC2 come nodi worker Kubernetes. Utilizza questi ruoli per eseguire azioni come l'avvio e la chiusura delle istanze EC2, l'etichettatura delle risorse e l'interazione con altri servizi. AWS Crea i ruoli e le policy di Karpenter utilizzando cloudformation.yaml
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 sistemi di AWS bilanciamento del carico, ad esempio per la creazione ALBs di risorse per Ingress o per servizi di questo tipo. NLBs LoadBalancer Specificheremo questa policy 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 LBC:
aws iam create-policy \ --policy-name AWSLoadBalancerControllerIAMPolicy \ --policy-document "$(curl -fsSL https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.14.1/docs/install/iam_policy.json)"
Quando il driver CSI Mountpoint S3 è installato, i suoi DaemonSet pod sono configurati per utilizzare un account di servizio per l'esecuzione. Il driver CSI Mountpoint per Mountpoint S3 richiede l’autorizzazione per interagire con il bucket Amazon S3 che creerai più avanti in questa guida. Specificheremo questa policy 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 Pod Identity EKS, l’agente di monitoraggio dei nodi, CoreDNS, Kubeproxy, il plug-in CNI del VPC. Ad oggi, il driver CSI Mountpoint di S3 non è disponibile per Pod Identity EKS, quindi creiamo un IRSA (IAM Roles for Service Account) 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 console. AWS CloudFormation
2. Verifica dello stato del nodo del cluster e del pod
Eseguiamo alcuni controlli dell’integrità per assicurarci che il cluster sia pronto. Quando il comando precedente viene completato, visualizza i tipi di istanze e verifica che tutti i nodi del sistema CPU abbiano raggiunto lo stato Ready 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 Pod Identity e il modo in cui associano un ruolo a un account di servizio in un namespace nel cluster con il comando seguente:
eksctl get podidentityassociation --cluster ${EKS_CLUSTER_NAME} --region ${AWS_REGION}
L'output dovrebbe mostrare i ruoli IAM per Karpenter («karpenter») e AWS LBC (» «). 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 worker 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. NodePoolslimits 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 a cui è stato assegnato il provisioning, impedendo il provisioning aggiuntivo dei nodi in caso di superamento di questi limiti. Sebbene NodePools supporti ampie categorie di istanze (ad esempio,g)c, la specificazione di tipi di istanze, tipi di
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 una combinazione 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 rispettando i vincoli delle risorse. CPUs GPUs
Inoltre, specifichiamo l’ID della variante Nvidia dell’AMI Bottlerocket. Infine, abbiamo stabilito una policy di interruzioneconsolidateAfter: 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 Use ttlSecondsAfter Finished to Auto Clean-Up Kubernetes Jobs.
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 sia stato creato e funzionante: NodePool
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 moderato della CPU (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 policy di interruzioneconsolidateAfter: 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 Use 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 sia stato creato e funzionante: NodePool
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 Pod GPU per esporre una GPU
È necessario il plug-in per dispositivi di Nvidia 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 ulteriori informazioni, consulta Kubernetes Device Plugin to expose. GPUs
Implementa 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 Schedule workloads with GPU requirements using Well-Known labels.
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 il Pod ha lo stato «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) Prepara e carica gli artefatti del modello per l’implementazione
In questa fase, implementerai il servizio di un 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
Configurare l'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.
-
Crea un account NGC gratuito
e genera una chiave API dalla dashboard NGC (Icona utente > Configurazione > Genera chiave API > Genera chiave personale > Catalogo NGC). -
Scarica e installa NGC CLI
Linux/macOS/Windows () e configura la CLI utilizzando:. ngc config setInserisci la tua chiave API quando richiesto, poi imposta org sunvidiae premi Invio per accettare i valori predefiniti per gli altri. Se l’operazione viene eseguita correttamente, dovresti vedere qualcosa come: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 pod del driver CSI Mountpoint di S3 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 un su tutti i nodi. DaemonSet I pod utilizzano il driver CSI su quei nodi per montare i volumi S3. Per consentirne la programmazione 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 runtime per scopi di inferenza.
Crea un 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 eliminare le versioni degli oggetti sovrascritte o eliminate 14 giorni dopo che sono diventate non attuali e per rimuovere i marcatori di eliminazione scaduti e i caricamenti incompleti in più parti dopo 7 giorni. Per ulteriori informazioni, consulta Examples of S3 Lifecycle configurations.
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 -0 da NGC. GPUNet Ad esempio, su macOS:
ngc registry model download-version nvidia/dle/gpunet_0_pyt_ckpt:21.12.0_amp --dest ~/downloads
Nota
Potresti dover 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
aws configure set s3.preferred_transfer_client crt
Carica i pesi del modello nel 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 servizio del modello
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 dell’integrità e un endpoint /predict POST che accetta l’URL di un’immagine.
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 throughput, consulta Serving ML Models.
Creazione dell’applicazione
Crea una directory per i file dell’applicazione, ad esempio model-testing, quindi modifica le directory al suo interno e aggiungi 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 un Dockerfile
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 Reducing Container Image Sizes.
Nella stessa directory di app.py, crea il Dockerfile:
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, mirata all'architettura: AMD64
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 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. 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 del terminale, verifica 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 container 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 e poi l’etichettatura e l’invio dell’immagine del container nel nostro registro.
Innanzitutto, torna alla directory in cui hai impostato le variabili di ambiente all’inizio di questa guida. 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
Tagga 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
Per il completamento di questa fase finale possono essere necessari alcuni minuti.
7. (Facoltativo) Esponi il servizio del modello
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). Questo comporta la configurazione dell’LBC, il montaggio dei pesi del modello da Amazon S3 come volume persistente utilizzando il driver CSI Mountpoint di S3, la distribuzione di un pod applicativo accelerato da GPU, la creazione di un servizio e un ingresso per il provisioning di 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
Assegna tag a un 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. Durante la creazione di un cluster con eksctl, il gruppo di sicurezza del cluster predefinito (che ha il tag "kubernetes.io/cluster/<cluster-name>: owned") non viene automaticamente etichettato con tag karpenter.sh/discovery. Questo tag è essenziale per permettere a Karpenter di 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.
Individua l’ID VPC del 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 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 tag karpenter.sh/discovery al gruppo di sicurezza del cluster predefinito. Ciò consentirà ai nostri selettori di CPU e EC2 NodeClass 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. 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, impostiamo serviceAccount.name in modo che corrisponda a quanto definito nella nostra configurazione del cluster (aws-load-balancer-controller).
AWS Aggiungi l'archivio di grafici Helm di proprietà di eks-charts:
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 creato in precedenza:
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!
Monta 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. Questo consente ai pod Kubernetes di accedere agli oggetti S3 come file locali, eliminando i download a uso intensivo di risorse verso container di archiviazione temporanei su pod o container init, ideali per modelli di pesi di grandi dimensioni da più gigabyte.
Il PV monta l’intera root del bucket (nessun percorso specificato in volumeAttributes), supporta l’accesso simultaneo in sola lettura da più pod ed espone file come il peso dei modelli (/models/gpunet-0.pth) all’interno del container 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 container, viene consentito 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, montando il volume persistente supportato da S3 per l’accesso al modello, applicando i selettori e le tolleranze dei nodi GPU e impostando le variabili di ambiente per il percorso del modello. Questa implementazione imposta il percorso del modello (var amb di "/models/gpunet-0.pth"), quindi la nostra applicazione (in app.py) utilizzerà questo percorso per impostazione predefinita. Con il montaggio del volume di implementazione 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 eseguire il provisioning di un nodo GPU se non è già disponibile. Verifica che il pod di inferenza sia nello stato "Running":
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 bilanciatore del carico
Crea un servizio ClusterIP per esporre l’implementazione dell’inferenza internamente nel 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
Application Load Balancer (ALB) completerà il provisioning nell’arco di alcuni minuti. Monitora lo stato delle risorse Ingress per confermare che sia stato eseguito il provisioning dell’ALB:
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 dell’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 servizio del modello
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. 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 istanze G5 EC2
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 momento del completamento, dovresti vedere il seguente messaggio 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