

 **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
<a name="ml-realtime-inference-cluster"></a>

**Suggerimento**  
 [Registrati](https://aws-experience.com/emea/smb/events/series/get-hands-on-with-amazon-eks?trk=4a9b4147-2490-4c63-bc9f-f8a84b122c8c&sc_channel=el) ai prossimi AI/ML workshop Amazon EKS.

## Introduzione
<a name="_introduction"></a>

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](inferentia-support.md) [Risorse per iniziare a utilizzare IA/ML su Amazon EKS](ml-resources.md)

## Prima di iniziare
<a name="_before_you_begin"></a>

Prima di iniziare, esegui le seguenti attività:
+  [Configura il tuo ambiente per Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/setting-up.html) 
+  [Installa la versione più recente di eksctl](https://eksctl.io/installation/) 
+  [Installa Helm](https://helm.sh/docs/intro/install/) 
+  [(Opzionale) Installa Docker](https://docs.docker.com/get-started/) 
+  [(Opzionale) Installa la CLI NGC](https://org.ngc.nvidia.com/setup/installers/cli) 

## Architecture
<a name="_architecture"></a>

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
<a name="_solution_overview"></a>

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](https://aws.amazon.com/ec2/instance-types/g5/): 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](https://aws.amazon.com/ec2/instance-types/p5/) per una latenza ottimale in tempo reale. Ti consigliamo di iniziare con le [istanze G5 EC2](https://aws.amazon.com/ec2/instance-types/g5/) 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](https://aws.amazon.com/ec2/instance-types/g6/).
+  [Istanze Amazon EC2 M7g](https://aws.amazon.com/ec2/instance-types/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](https://aws.amazon.com/ec2/instance-types/c7g/), 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](https://aws.amazon.com/ec2/instance-types/c8g/) 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](https://docs.aws.amazon.com/eks/latest/userguide/s3-csi.html): 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](https://docs.aws.amazon.com/eks/latest/userguide/fsx-openzfs-csi.html) 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](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html): 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)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) 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](https://docs.aws.amazon.com/eks/latest/userguide/pod-id-abac.html) e [Target IAM roles](https://docs.aws.amazon.com/eks/latest/userguide/pod-id-assign-target-role.html).
+  [Agente di monitoraggio dei nodi di EKS](https://docs.aws.amazon.com/eks/latest/userguide/node-health.html): 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](https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami-bottlerocket.html): 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](https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html) 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)](https://docs.aws.amazon.com/eks/latest/userguide/lbc-helm.html): 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
<a name="_1_create_your_eks_cluster"></a>

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](https://eksctl.io/usage/creating-and-managing-clusters/) 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 [https://docs.aws.amazon.com/eks/latest/userguide/managing-kube-proxy.html](https://docs.aws.amazon.com/eks/latest/userguide/managing-kube-proxy.html) Le istanze m7g.xlarge verranno utilizzate per le attività del sistema CPU, tra cui hosting del controller Karpenter, componenti aggiuntivi e altri componenti di sistema.

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](https://docs.aws.amazon.com/eks/latest/best-practices/subnets.html#_deploy_nodes_to_private_subnets).

### Verifica le credenziali
<a name="_check_your_credentials"></a>

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
<a name="_check_instance_availability"></a>

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
<a name="_prepare_your_environment"></a>

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](https://github.com/kubernetes-sigs/karpenter/releases) e la [versione di Kubernetes](https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions.html).

**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
<a name="_create_required_roles_and_policies"></a>

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](https://raw.githubusercontent.com/aws/karpenter-provider-aws/v1.5.0/website/content/en/preview/getting-started/getting-started-with-karpenter/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 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
<a name="_create_the_cluster"></a>

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](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html). 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](https://console.aws.amazon.com/cloudformation)

## 2. Verifica dello stato del nodo del cluster e del pod
<a name="_2_verify_cluster_node_and_pod_health"></a>

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
<a name="_3_install_karpenter"></a>

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
<a name="_4_setup_karpenter_nodepools"></a>

[In questo passaggio, configuriamo CPU e GPU Karpenter che si escludono a vicenda. NodePools](https://karpenter.sh/docs/concepts/nodepools/) Il `limits` campo nella NodePool specifica 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](https://karpenter.sh/docs/concepts/nodepools/#instance-types) [capacità e [limiti](https://karpenter.sh/docs/concepts/nodepools/#speclimits) di risorse specifici consente di](https://karpenter.sh/docs/concepts/nodepools/#capacity-type) 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](https://docs.aws.amazon.com/eks/latest/best-practices/karpenter.html#_creating_nodepools)

### Configura la GPU NodePool
<a name="_setup_the_gpu_nodepool"></a>

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 e rispettando i vincoli delle risorse. CPUs GPUs

Inoltre, specifichiamo l’ID della variante Nvidia dell’AMI Bottlerocket. Infine, abbiamo stabilito una [policy di interruzione](https://karpenter.sh/docs/concepts/disruption/#nodepool-disruption-budgets) 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](https://docs.aws.amazon.com/eks/latest/best-practices/aiml-compute.html#_disable_karpenter_consolidation_for_interruption_sensitive_workloads) e [Use ttlSecondsAfter Finished to Auto Clean-Up Kubernetes Jobs](https://docs.aws.amazon.com/eks/latest/best-practices/aiml-compute.html#_use_ttlsecondsafterfinished_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 piace`ValidationSucceeded: True`» e `Ready: True` verifica che NodePool sia sano. `NodeClassReady: True`

### Configura la CPU NodePool
<a name="_setup_the_cpu_nodepool"></a>

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 interruzione](https://karpenter.sh/docs/concepts/disruption/#nodepool-disruption-budgets) 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 Use Finished to Auto Clean-Up Kubernetes](https://docs.aws.amazon.com/eks/latest/best-practices/aiml-compute.html#_disable_karpenter_consolidation_for_interruption_sensitive_workloads) Jobs. ttlSecondsAfter](https://docs.aws.amazon.com/eks/latest/best-practices/aiml-compute.html#_use_ttlsecondsafterfinished_to_auto_clean_up_kubernetes_jobs)

```
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 piace`ValidationSucceeded: True`» e `Ready: True` verifica che NodePool sia sano. `NodeClassReady: True`

## 5. Implementa un Pod GPU per esporre una GPU
<a name="_5_deploy_a_gpu_pod_to_expose_a_gpu"></a>

È 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](https://docs.aws.amazon.com/eks/latest/best-practices/aiml-compute.html#_use_kubernetes_device_plugin_for_exposing_gpus) Device Plugin to expose. GPUs

### Implementa un pod di esempio
<a name="_deploy_a_sample_pod"></a>

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](https://docs.aws.amazon.com/eks/latest/best-practices/aiml-compute.html#_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-nvidia-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
<a name="_6_optional_prepare_and_upload_model_artifacts_for_deployment"></a>

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](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/dle/models/gpunet_0_pyt_ckpt) di NVIDIA [GPUNet](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/dle/resources/gpunet_pyt), che supporta l'inferenza a bassa latenza sulle immagini utilizzando NVIDIA e TensorRT. GPUs Questo modello è preconfigurato [ImageNet](https://www.image-net.org/), 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.

### Configurare l'ambiente
<a name="_set_up_your_environment"></a>

[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.](https://docs.docker.com/get-started/) Segui questa procedura per configurare un account gratuito e la CLI NGC:
+  [Crea un account NGC gratuito](https://ngc.nvidia.com/signup) e genera una chiave API dalla dashboard NGC (Icona utente > Configurazione > Genera chiave API > Genera chiave personale > Catalogo NGC).
+  [Scarica e installa NGC CLI](https://org.ngc.nvidia.com/setup/installers/cli) Linux/macOS/Windows () e configura la CLI utilizzando:. `ngc config set` Inserisci la tua chiave API quando richiesto, poi imposta org su `nvidia` e 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
<a name="_verify_service_account_permissions"></a>

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
<a name="_add_a_toleration"></a>

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
<a name="_upload_model_weights_to_s3"></a>

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](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Versioning.html) 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](https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-configuration-examples.html).

```
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](https://aws.amazon.com/blogs/storage/improving-amazon-s3-throughput-for-the-aws-cli-and-boto3-with-the-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 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
<a name="_create_the_model_service"></a>

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](https://docs.aws.amazon.com/eks/latest/best-practices/aiml-performance.html#_serving_ml_models).

#### Creazione dell’applicazione
<a name="_create_the_application"></a>

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
<a name="_create_the_dockerfile"></a>

[Il seguente Dockerfile crea un'immagine del contenitore per la nostra applicazione utilizzando il modello del repository NVIDIA Deep Learning Examples for Tensor Cores GPUNet .](https://github.com/NVIDIA/DeepLearningExamples) 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 [Reducing Container Image Sizes](https://docs.aws.amazon.com/eks/latest/best-practices/aiml-performance.html#_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
<a name="_test_the_application"></a>

Dalla stessa directory della tua `app.py` e`Dockerfile`, 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
<a name="_push_the_container_to_amazon_ecr"></a>

In questa fase, carichiamo l'immagine del contenitore per il servizio modello GPUNet -0 [su Amazon Elastic Container Registry (ECR)](https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html), 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
<a name="_7_optional_expose_the_model_service"></a>

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
<a name="_tag_your_cluster_security_group"></a>

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)
<a name="setup_the_shared_aws_load_balancer_controller_lbc"></a>

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
<a name="_mount_the_model_in_a_persistent_volume"></a>

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
<a name="_create_the_persistentvolume_pv"></a>

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)
<a name="_create_the_persistentvolumeclaim_pvc"></a>

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
<a name="_deploy_the_application"></a>

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
<a name="_expose_the_service_with_ingress_and_load_balancer"></a>

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
<a name="_test_the_model_service"></a>

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
<a name="_conclusion"></a>

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](https://aws.amazon.com/ec2/instance-types/g5/), installato il [driver CSI Mountpoint S3](https://docs.aws.amazon.com/eks/latest/userguide/s3-csi.html), EKS [Pod Identity Agent, EKS Node Monitoring Agent](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html)[, Bottlerocket](https://docs.aws.amazon.com/eks/latest/userguide/node-health.html)[AMI,](https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami-bottlerocket.html) Load [AWS Balancer Controller (](https://docs.aws.amazon.com/eks/latest/userguide/lbc-helm.html)LBC) e Karpenter per gestire CPU e GPU.](https://karpenter.sh/) 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](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/dle/models/gpunet_0_pyt_ckpt) Amazon S3, abilitando l'inizializzazione del pod ed [esponendo il servizio](https://aws.amazon.com/s3/) di inferenza tramite Application Load Balancer. Per utilizzare appieno il cluster, configura [l’agente di monitoraggio dei nodi di EKS](https://docs.aws.amazon.com/eks/latest/userguide/node-health.html) 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 ](https://docs.aws.amazon.com/eks/latest/best-practices/aiml-observability.html#_using_monitoring_and_observability_tools_for_your_aiml_workloads)

## Eliminazione
<a name="_clean_up"></a>

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](https://console.aws.amazon.com/s3/home).