Guide de configuration de clusters basé sur les meilleures pratiques pour l’inférence en temps réel sur Amazon EKS - Amazon EKS

Aidez à améliorer cette page

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Pour contribuer à ce guide de l'utilisateur, cliquez sur le GitHub lien Modifier cette page sur qui se trouve dans le volet droit de chaque page.

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Guide de configuration de clusters basé sur les meilleures pratiques pour l’inférence en temps réel sur Amazon EKS

Astuce

Inscrivez-vous aux prochains AI/ML ateliers Amazon EKS.

Introduction

Ce guide propose une procédure pratique pour configurer un cluster Amazon Elastic Kubernetes Service (EKS) optimisé pour les charges de travail d'inférence en ligne en temps réel, en intégrant les meilleures pratiques élaborées par des experts. AWS Il utilise une architecture EKS Quickstart bien ancrée, c'est-à-dire un ensemble de pilotes, de types d'instances et de configurations sélectionnés conformément aux AWS meilleures pratiques en matière de modèles, d'accélérateurs et de scalabilité. Cette approche vous évite d’avoir à sélectionner les paramètres du cluster, ce qui vous permet de disposer rapidement d’un cluster fonctionnel et préconfiguré. En cours de route, nous déploierons des exemples de charges de travail pour valider votre configuration, expliquer les concepts architecturaux clés (tels que le découplage des tâches liées au processeur des calculs gourmands en GPU), répondre aux questions courantes (par exemple, pourquoi choisir Bottlerocket AMI ?) AL2023 , et décrivez les prochaines étapes pour étendre les capacités de votre cluster.

Conçu spécifiquement pour les ingénieurs du Machine Learning (ML) et de l'intelligence artificielle (IA), les administrateurs de plateformes, les opérateurs et les data/AI spécialistes qui découvrent l'écosystème EKS pour la AWS première fois, ce guide suppose une connaissance de Kubernetes mais aucune expérience préalable d'EKS. Il est conçu pour vous aider à comprendre les étapes et les processus nécessaires à la mise en place et au fonctionnement des charges de travail d’inférence en ligne en temps réel. Ce guide explique les éléments essentiels de la création d'un cluster d'inférence à nœud unique, notamment le provisionnement des ressources GPU, l'intégration du stockage pour les artefacts du modèle, la sécurisation de l'accès aux AWS services et la mise en évidence des points de terminaison d'inférence. Tout au long du document, l’accent est mis sur une conception résiliente et à faible latence pour les applications destinées aux utilisateurs, telles que la détection des fraudes, les chatbots en temps réel et l’analyse des sentiments dans les systèmes de commentaires des clients.

Dans ce guide, nous nous concentrons exclusivement sur la mise en place d’un point de départ fondamental et normatif à l’aide d’instances G5 EC2. Si vous recherchez des configurations de clusters ou des end-to-end flux de travail AWS spécifiques à Inferentia, consultez nos Utiliser des instances AWS Inferentia avec Amazon EKS pour le Machine Learning ateliers dans. Ressources pour démarrer avec l’IA/ML sur Amazon EKS

Avant de commencer

Avant de commencer, assurez-vous d’avoir effectué les tâches suivantes :

Architecture

L’inférence en ligne en temps réel désigne le processus consistant à utiliser un modèle de machine learning entraîné pour générer des prédictions ou des résultats sur des flux de données entrants avec une latence minimale. Par exemple, il permet la détection de fraudes en temps réel, la classification d’images ou la génération de graphes de connaissances en réponse aux entrées des utilisateurs. L’architecture d’un système d’inférence en ligne en temps réel fournit des prédictions de machine learning à faible latence dans les applications destinées aux utilisateurs en dissociant le traitement du trafic Web lié au processeur des calculs d’IA gourmands en ressources GPU. Ce processus s’inscrit généralement dans un écosystème applicatif plus large et comprend souvent des services dorsaux, frontaux, vectoriels et de modélisation, en mettant l’accent sur des composants spécialisés afin de permettre une évolutivité indépendante, un développement parallèle et une résilience face aux défaillances. L'isolation des tâches d'inférence sur du matériel GPU dédié et l'exploitation d'interfaces telles que APIs et WebSockets garantissent une simultanéité élevée, un traitement rapide de modèles tels que les transformateurs et des interactions avec les utilisateurs via le frontend. Notez que, bien que les bases de données vectorielles et les pipelines de génération à enrichissement contextuel (RAG) jouent souvent un rôle important dans les systèmes d’inférence en temps réel, ces composants ne sont pas abordés dans ce guide. Au minimum, une architecture typique comprend souvent :

  • Service frontal : Sert d'interface utilisateur, gère la logique côté client, affiche le contenu dynamique et facilite les interactions en temps réel. Il communique avec le service principal pour lancer des demandes d'inférence et afficher les résultats, en adressant souvent des demandes au service principal qui l'utilise WebSockets pour les mises à jour en continu ou pour l'échange de données structurées. APIs Ce service ne nécessite généralement pas d'équilibreur de charge dédié, car il peut être hébergé sur des réseaux de diffusion de contenu (CDNs), comme AWS CloudFront pour les actifs statiques, ou servi directement à partir de serveurs Web, le dimensionnement étant géré via des groupes d'auto-dimensionnement si nécessaire pour le contenu dynamique.

  • Service principal : agit en tant qu'orchestrateur de l'application, gérant la logique métier telle que l'authentification des utilisateurs, la validation des données et la coordination des services (par exemple, via APIs pour les RESTful points de terminaison ou WebSockets pour les connexions persistantes). Il communique avec le service d'inférence, s'adapte indépendamment sur le multicœur CPUs et la RAM pour gérer le trafic Web élevé sans compter sur celui-ci GPUs, et nécessite souvent un équilibreur de charge (tel que Application Load AWS Balancer ou Network Load Balancer) pour répartir les demandes entrantes sur plusieurs instances, en particulier dans les scénarios de concurrence élevée. Un contrôleur d’accès peut également gérer les règles d’accès externe et de routage afin d’améliorer la sécurité et la gestion du trafic.

  • Service d'inférence : sert de base aux calculs d'intelligence artificielle, fonctionnant GPUs avec suffisamment de VRAM (par exemple, 8 à 12 Go pour des modèles tels que Distilbert) pour effectuer des intégrations vectorielles, l'extraction de connaissances et l'inférence de modèles (par exemple, exposée pour les demandes par lots ou APIs pour le streaming en temps réel) à l'aide de modèles personnalisés ou open source WebSockets . Cet isolement empêche les conflits de dépendance, permet la mise à jour des modèles sans durée d’indisponibilité du service et facilite la mise à l’échelle horizontale avec équilibrage de charge pour plusieurs requêtes simultanées. Pour exposer efficacement le modèle de service, celui-ci est généralement installé derrière un équilibreur de charge chargé de répartir les charges de travail liées au GPU entre les instances répliquées, tandis qu'une ressource ou un contrôleur d'entrée (tel qu'un contrôleur d'entrée ALB intégré AWS) gère le routage externe, la terminaison SSL et le transfert basé sur le chemin afin de garantir un accès sécurisé et efficace sans surcharger le personnel. GPUs

Présentation de la solution

Les systèmes d’inférence en ligne en temps réel nécessitent une architecture résiliente et hautement performante, capable d’offrir une latence ultra-faible tout en gérant des pics de trafic imprévisibles et volumineux. Cette présentation de la solution explique comment les AWS composants suivants fonctionnent ensemble dans le cluster Amazon EKS que nous allons créer pour garantir que notre cluster est capable d'héberger et de gérer des modèles d'apprentissage automatique qui fournissent des prévisions immédiates sur les données en temps réel avec un délai minimal pour les utilisateurs finaux.

  • Instances Amazon G5 EC2 — Pour les tâches d’inférence gourmandes en ressources GPU, nous utilisons les types d’instances g5.xlarge et g5.2xlarge G5 EC2, qui disposent d’un (1) GPU NVIDIA A10G avec 24 Go de mémoire (par exemple, 8 milliards de paramètres en FP16). Basés sur l’architecture NVIDIA Ampere, ces GPU sont équipés de GPU NVIDIA A10G Tensor Core et de processeurs AMD EPYC de 2e génération. Ils prennent en charge 4 à 8 vCPU, une bande passante réseau pouvant atteindre 10 Gbit/s et 250 à 450 Go de stockage SSD NVMe local, garantissant ainsi un transfert rapide des données et une puissance de calcul suffisante pour les modèles complexes. Ils sont donc parfaits pour les tâches d’inférence à faible latence et haut débit. Le choix d’un type d’instance EC2 dépend de l’application, de votre modèle (par exemple, modèle d’image, de vidéo ou de texte) et de vos exigences en matière de latence et de débit. Par exemple, si vous utilisez un modèle d’image et/ou de vidéo, vous pouvez utiliser des instances P5 EC2 pour obtenir une latence optimale en temps réel. Nous vous recommandons de commencer avec des instances G5 EC2, car elles constituent un bon point de départ pour être rapidement opérationnel, puis d’évaluer si elles sont adaptées à vos charges de travail à l’aide de tests de performance. Pour les cas plus avancés, pensez aux instances G6 EC2.

  • Instances Amazon EC2 M7g — Pour les tâches gourmandes en ressources CPU telles que le prétraitement des données, le traitement des requêtes API, l’hébergement du contrôleur Karpenter, les modules complémentaires et d’autres composants système, nous utilisons le type d’instance EC2 m5.xlarge M7g. Les instances m7G sont des instances ARM dotées de 4 VCPUs, de 16 Go de mémoire, d'une bande passante réseau allant jusqu'à 12,5 Gbit/s et alimentées par des processeurs Graviton3. AWS Le choix d’un type d’instance EC2 dépend de l’application et des exigences de votre charge de travail en matière de calcul, de mémoire et de capacité de mise à l’échelle. Pour les charges de travail optimisées pour le calcul, vous pouvez envisager les instances EC2 C7g, qui utilisent également des processeurs Graviton3, mais qui sont optimisées pour offrir des performances de calcul supérieures à celles des instances M7g dans certains cas d’utilisation. Par ailleurs, les nouvelles instances EC2 C8g (lorsqu’elles sont disponibles) offrent des performances de calcul jusqu’à 30 % supérieures à celles des instances C7g. Nous vous recommandons de commencer par les instances M7g EC2 pour leur rentabilité et leur compatibilité avec un large éventail de charges de travail (par exemple, serveurs d’applications, microservices, serveurs de jeux, magasins de données de taille moyenne), puis d’évaluer si elles conviennent à vos charges de travail à l’aide de tests de performance.

  • Pilote CSI Amazon S3 Mountpoint : Pour les charges de travail sur des instances à GPU unique où plusieurs pods partagent un GPU (par exemple, plusieurs pods planifiés sur le même nœud pour utiliser ses ressources GPU), nous utilisons le pilote CSI Mountpoint S3 afin d’optimiser l’utilisation de la mémoire, ce qui est essentiel pour des tâches telles que l’inférence de modèles volumineux dans des configurations peu complexes et sensibles aux coûts. Il expose les compartiments Amazon S3 comme un système de fichiers de type POSIX accessible au cluster Kubernetes, ce qui permet aux pods d’inférence de lire les artefacts du modèle (par exemple, les poids du modèle) directement en mémoire sans avoir à les télécharger au préalable, et d’entrer des jeux de données à l’aide d’opérations de fichiers standard. De plus, S3 dispose d’une capacité de stockage pratiquement illimitée et accélère les charges de travail d’inférence gourmandes en données. Le choix d’un pilote CSI de stockage dépend de l’application et des exigences de votre charge de travail en matière de débit et de latence. Bien que le pilote CSI FSx pour OpenZFS offre une latence inférieure à la milliseconde pour les volumes persistants partagés aléatoires I/O ou entièrement compatibles POSIX entre les nœuds, nous vous recommandons de commencer par le pilote CSI Mountpoint S3 en raison de son évolutivité, de ses coûts réduits pour les grands ensembles de données et de son intégration au stockage d'objets géré par S3 pour les modèles d'inférence lourds (par exemple, les entrées du modèle de streaming), puis d'évaluer s'il convient à vos charges de travail grâce à des tests de performance.

  • Agent d'identité EKS Pod — Pour permettre l'accès aux AWS services, nous utilisons l'agent EKS Pod Identity, qui utilise un seul principal de service et facilite les associations de rôles IAM au niveau du pod au sein du cluster Amazon EKS. EKS Pod Identity offre une alternative simplifiée à l’approche traditionnelle IAM Roles for Service Accounts (IRSA) en utilisant un seul principal de service (pods.eks.amazonaws.com) au lieu de s’appuyer sur des fournisseurs OIDC individuels pour chaque cluster, ce qui facilite l’attribution des autorisations. De plus, il permet de réutiliser les rôles dans plusieurs clusters et prend en charge des fonctionnalités avancées telles que les balises de session de rôle IAM et les rôles IAM cibles.

  • Agent de surveillance des nœuds EKS : afin de garantir la disponibilité et la fiabilité continues des services d’inférence, nous utilisons l’agent de surveillance des nœuds EKS avec réparation automatique, qui détecte et remplace automatiquement les nœuds défectueux, minimisant ainsi la durée d’indisponibilité. Il surveille en permanence les nœuds pour détecter les problèmes liés au matériel, au noyau, au réseau et au stockage à l'aide de contrôles de santé améliorés (par exemple KernelReady,, NetworkingReady). Pour les nœuds GPU, il détecte les défaillances spécifiques à l’accélérateur, lance une correction en douceur en isolant les nœuds défaillants, attend 10 minutes que les problèmes GPU transitoires se résolvent et remplace les nœuds après 30 minutes en cas de défaillances persistantes.

  • AMI Bottlerocket : Afin de fournir une base sécurisée à notre cluster EKS, nous utilisons l’AMI Bottlerocket, qui ne comprend que les composants essentiels nécessaires au fonctionnement des conteneurs et offre des temps de démarrage minimaux pour une mise à l’échelle rapide. Le choix d’une AMI de nœud dépend de l’application et des exigences de personnalisation, de sécurité et de capacité de mise à l’échelle de votre charge de travail. Bien que l'AL2023 AMI offre une plus grande flexibilité pour les installations et les personnalisations au niveau de l'hôte (par exemple, en spécifiant un répertoire de cache dédié dans une configuration PV/PVC sans nœuds supplémentaires), nous vous recommandons de commencer par l'AMI Bottlerocket pour son encombrement réduit et son optimisation intégrée pour les charges de travail conteneurisées (par exemple, microservices, serveurs d'inférence, évolutifs APIs), puis d'évaluer si elle convient à vos charges de travail grâce à des tests de performance.

  • AWS Load Balancer Controller (LBC) — Pour exposer les points de terminaison d'inférence en temps réel, nous utilisons le Load Balancer Controller, qui approvisionne et gère automatiquement les équilibreurs de AWS charge d'application () pour le trafic et les équilibreurs de charge réseau (ALBs) pour le HTTP/HTTPS trafic en fonction des ressources Kubernetes Ingress et Service, permettant ainsi l'intégration de modèles d'inférence avec des clients externes. NLBs TCP/UDP En outre, il prend en charge des fonctionnalités telles que le routage basé sur les chemins pour distribuer les demandes d'inférence sur plusieurs pods ou nœuds, garantissant ainsi l'évolutivité lors des pics de trafic et minimisant la latence grâce à des optimisations AWS natives telles que le multiplexage des connexions et les bilans de santé.

1. Créer votre cluster EKS

Dans cette étape, nous créons un cluster avec des nœuds CPU et un groupe de nœuds gérés à l'aide d'un modèle eksctl AWS CloudFormation alimenté par un modèle eksctl ClusterConfig. L'initialisation du cluster avec uniquement des nœuds CPU nous permet d'utiliser Karpenter exclusivement pour gérer les nœuds gourmands en CPU et en GPU afin d'optimiser l'allocation des ressources à l'aide de Karpenter NodePools , que nous créerons ultérieurement. Pour prendre en charge nos charges de travail d'inférence en temps réel, nous fournissons au cluster l'AMI EKS Bottlerocket, l'agent de surveillance des nœuds EKS, l'agent EKS Pod Identity, le pilote Mountpoint S3 CSI, le contrôleur Load AWS Balancer (LBC) et les pilotes kube-proxy, vpc-cni et coredns. Les instances m7g.xlarge seront utilisées pour les tâches système du processeur, notamment l’hébergement du contrôleur Karpenter, des modules complémentaires et d’autres composants système.

Par défaut, eksctl créera un VPC dédié pour le cluster avec un bloc CIDR de 192.168.0.0/16. Le VPC comprend trois sous-réseaux publics et trois sous-réseaux privés, chacun étant réparti sur trois zones de disponibilité différentes (ou deux AZs dans la us-east-1 région), ce qui constitue la méthode idéale pour déployer les charges de travail Kubernetes. Le modèle déploie également une passerelle Internet, fournissant un accès Internet aux sous-réseaux publics via des routes par défaut dans leurs tables de routage et une passerelle NAT unique dans l’un des sous-réseaux publics, avec des routes par défaut dans les tables de routage des sous-réseaux privés dirigeant le trafic sortant via la passerelle NAT pour l’accès à Internet. Pour en savoir plus sur cette configuration, consultez Déployer des nœuds dans des sous-réseaux privés.

Vérifiez vos informations d’identification

Vérifiez si vos informations d'identification AWS CLI sont valides et si vous pouvez vous authentifier auprès des AWS services :

aws sts get-caller-identity

En cas de succès, la CLI renverra des informations sur votre AWS identité (UserId, votre compte et votre ARN).

Vérifier la disponibilité des instances

Les types d’instances G5 ne sont pas disponibles dans toutes les régions. Vérifiez votre région la plus proche. Par exemple :

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

Si l’opération réussit, le type d’instance G5 est disponible dans la région que vous avez spécifiée.

L’AMI Bottlerocket n’est pas disponible dans toutes les régions. Vérifiez en récupérant un identifiant AMI Bottlerocket pour votre région la plus proche. Par exemple :

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

Si l’opération réussit, l’AMI Bottlerocket est disponible dans la région que vous avez spécifiée.

Préparez votre environnement

Commencez par définir les variables d’environnement suivantes dans une nouvelle fenêtre de terminal. Remarque : Veillez à remplacer les espaces réservés par vos propres valeurs, notamment le nom du cluster, la région souhaitée, la version de Karpenter et la version de Kubernetes.

Astuce

Certaines variables (telles que ${AWS_REGION} et ${K8S_VERSION}) sont définies au début du bloc, puis référencées dans les commandes suivantes pour des raisons de cohérence et afin d’éviter les répétitions. Veillez à exécuter les commandes dans l’ordre afin que ces valeurs soient correctement exportées et disponibles pour être utilisées dans les définitions suivantes.

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/')"

Créer les rôles et les politiques requis

Karpenter a besoin de rôles et de politiques IAM spécifiques (par exemple, rôle IAM du contrôleur Karpenter, profil d’instance et politiques) pour gérer les instances EC2 en tant que composants master Kubernetes. Il utilise ces rôles pour effectuer des actions telles que le lancement et la résiliation d'instances EC2, le balisage des ressources et l'interaction avec d'autres services. AWS Créez les rôles et politiques Karpenter à l’aide du fichier cloudformation.yaml de 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}"

Le AWS LBC a besoin d'une autorisation pour approvisionner et gérer des équilibreurs de AWS charge, par exemple ALBs pour créer des ressources Ingress ou NLBs pour des services de type similaire. LoadBalancer Nous définirons cette politique d’autorisation lors de la création du cluster. Lors de la création du cluster, nous créerons le compte de service avec eksctl dans le. ClusterConfig Créer la politique 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)"

Lorsque le pilote CSI Mountpoint S3 est installé, ses DaemonSet modules sont configurés pour utiliser un compte de service pour l'exécution. Le pilote Mountpoint pour Mountpoint S3 CSI nécessite une autorisation pour interagir avec le compartiment Amazon S3 que vous créerez plus loin dans ce guide. Nous définirons cette politique d’autorisation lors de la création du cluster. Lors de la création du cluster, nous créerons le compte de service avec eksctl dans le. ClusterConfig Créez la politique IAM 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}/*\"]}]}"

Remarque : Si un rôle portant ce nom existe déjà, donnez-lui un autre nom. Le rôle que nous créons à cette étape est spécifique à votre cluster et à votre compartiment S3.

Créer le cluster

Dans ce modèle, eksctl crée automatiquement un compte de service Kubernetes pour EKS Pod Identity, Node Monitoring Agent, CoreDNS, Kubeproxy et le plug-in VPC CNI. À ce jour, le pilote Mountpoint S3 CSI n’est pas disponible pour EKS Pod Identity. Nous créons donc un rôle IAM pour compte de service (IRSA) et un point de terminaison OIDC. En outre, nous créons un compte de service pour le AWS Load Balancer Controller (LBC). Pour accéder aux nœuds Bottlerocket, eksctl attache automatiquement SSMManaged InstanceCore Amazon pour Bottlerocket afin de permettre des sessions shell sécurisées via SSM.

Dans le même terminal où vous définissez vos variables d’environnement, exécutez le bloc de commande suivant pour créer le 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

Ce processus prend plusieurs minutes. Si vous souhaitez surveiller l'état, consultez la AWS CloudFormationconsole.

2. Vérifiez l’état du nœud et du pod du cluster

Effectuons quelques surveillance de l’état pour nous assurer que le cluster est prêt. Une fois la commande précédente terminée, affichez les types d’instance et vérifiez que vos nœuds système CPU ont atteint l’état Ready souhaité à l’aide de la commande suivante :

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

Le résultat attendu devrait ressembler à ceci :

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

Vérifiez toutes les associations d’identité de pod et la manière dont elles mappent un rôle à un compte de service dans un espace de noms du cluster à l’aide de la commande suivante :

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

La sortie doit indiquer les rôles IAM de Karpenter (« karpenter ») et du AWS LBC (» «). aws-load-balancer-controller

Vérifiez qu' DaemonSets ils sont disponibles :

kubectl get daemonsets -n kube-system

Le résultat attendu devrait ressembler à ceci :

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

Vérifiez que tous les addons sont installés sur le cluster :

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

Le résultat attendu devrait ressembler à ceci :

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. Installez Karpenter

Installez le contrôleur Karpenter sur les composants master de votre processeur (cpu-worker) afin d’optimiser les coûts et de préserver les ressources du GPU. Nous allons l’installer dans l’espace de noms « kube-system » et spécifier le compte de service « karpenter » que nous avons défini lors de la création du cluster. En outre, cette commande configure le nom du cluster et une file d’attente d’interruption d’instance Spot pour les nœuds du processeur. Karpenter utilisera l’IRSA pour assumer ce rôle 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

Le résultat attendu devrait ressembler à ceci :

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

Vérifiez que Karpenter est en cours d’exécution :

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

Le résultat attendu devrait ressembler à ceci :

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

4. Configurer Karpenter NodePools

Dans cette étape, nous configurons le processeur et le GPU Karpenter NodePools qui s'excluent mutuellement. Le limits champ de la NodePool spécification limite le maximum de ressources totales (par exemple, processeur, mémoire GPUs) que chacun NodePool peut consommer sur tous les nœuds provisionnés, empêchant ainsi le provisionnement de nœuds supplémentaires si ces limites sont dépassées. Tout en NodePools prenant en charge de larges catégories d'instances (par exemplec,,g), la spécification de types d'instances spécifiques, de types de capacité et de limites de ressources vous permet d'estimer plus facilement les coûts de vos charges de travail à la demande. Dans ces derniers NodePools, nous utilisons un ensemble diversifié de types d'instances au sein de la famille d'instances G5. Cela permet à Karpenter de sélectionner automatiquement le type d'instance le plus approprié en fonction des demandes de ressources du pod, optimisant ainsi l'utilisation des ressources tout en respectant les limites totales NodePool du pod. Pour en savoir plus, consultez la section Création NodePools.

Configuration du GPU NodePool

Dans ce cadre NodePool, nous définissons des limites de ressources pour gérer le provisionnement des nœuds dotés de capacités GPU. Ces limites sont conçues pour plafonner les ressources totales sur tous les nœuds du pool, permettant jusqu’à 10 instances au total. Chaque instance peut être g5.xlarge (4 v, CPUs 16 GiB de mémoire, 1 GPU) ou g5.2xlarge (8 v, 32 GiB de mémoireCPUs, 1 GPU), à condition que le total de CPUs v ne dépasse pas 80, que la mémoire totale ne dépasse pas 320 Go et que le total ne dépasse pas 10. GPUs Par exemple, le pool peut provisionner 10 instances g5.2xlarge (80 v, CPUs 320 GiB, 10 GPUs) ou 10 instances g5.xlarge (40 v, CPUs 160 GiB, 10), ou une combinaison telle que 5 g5.xlarge et 5 g5.2xlarge (60 v, 240 GiB, 10 GPUs), garantissant ainsi une flexibilité basée sur les demandes de charge de travail tout en respectant les CPUs contraintes de ressources. GPUs

De plus, nous indiquons l’ID de la variante Nvidia de l’AMI Bottlerocket. Enfin, nous avons défini une politique de perturbation visant à supprimer les nœuds vides après 30 minutes (consolidateAfter: 30m) et fixé une durée de vie maximale de 30 jours (expireAfter: 720h) pour les nœuds afin d’optimiser les coûts et de maintenir l’état des nœuds pour les tâches gourmandes en ressources GPU. Pour en savoir plus, consultez Désactiver la consolidation Karpenter pour les charges de travail sensibles aux interruptions et Utiliser ttlSecondsAfter Finished pour nettoyer automatiquement les tâches Kubernetes.

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

Le résultat attendu devrait ressembler à ceci :

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

Vérifiez que le NodePool est créé et qu'il est sain :

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

Cherchez status.conditions comme ValidationSucceeded: TrueNodeClassReady: True, et confirmez Ready: True qu' NodePool il est sain.

Configuration du processeur NodePool

Dans ce cadre NodePool, nous avons défini des limites pour prendre en charge environ 50 instances, en fonction d'une charge de travail modérée du processeur (par exemple, 100 à 200 pods) et de quotas de AWS vCPU classiques (par exemple, 128 à 1152). Les limites sont calculées en supposant que l'échelle NodePool devrait atteindre 50 instances m7.xlarge : processeur (4 v CPUs par instance × 50 instances = 200 vCPUs) et mémoire (16 GiB par instance × 50 instances = 800 GiB). Ces limites sont conçues pour plafonner les ressources totales sur tous les nœuds du pool, en autorisant jusqu'à 50 instances de 7 g x large (chacune avec 4 V et CPUs 16 Go de mémoire), à condition que le total de v ne dépasse pas 200 et que la mémoire totale CPUs ne dépasse pas 800 Go.

De plus, nous indiquons l’ID de la variante standard de l’AMI Bottlerocket. Enfin, nous avons défini une politique de perturbation visant à supprimer les nœuds vides après 60 minutes (consolidateAfter: 60m) et fixé une durée de vie maximale de 30 jours (expireAfter: 720h) pour les nœuds afin d’optimiser les coûts et de maintenir l’état des nœuds pour les tâches gourmandes en ressources GPU. Pour en savoir plus, consultez Désactiver la consolidation Karpenter pour les charges de travail sensibles aux interruptions et Utiliser ttlSecondsAfter Finished pour nettoyer automatiquement les tâches Kubernetes.

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

Le résultat attendu devrait ressembler à ceci :

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

Vérifiez que le NodePool est créé et qu'il est sain :

kubectl get nodepool cpu-inference-m7gxlarge -o yaml

Cherchez status.conditions comme ValidationSucceeded: TrueNodeClassReady: True, et confirmez Ready: True qu' NodePool il est sain.

5. Déployer un module GPU pour exposer un GPU

Vous avez besoin du plug-in Nvidia Device pour permettre à Kubernetes d’exposer les périphériques GPU au cluster Kubernetes. En général, vous devez déployer le plug-in en tant que tel DaemonSet ; toutefois, l'AMI Bottlerocket préinstalle le plug-in dans le cadre de l'AMI. Cela signifie que lorsque vous utilisez Bottlerocket AMIs, il n'est pas nécessaire de déployer le plugin pour appareil Nvidia. DaemonSet Pour en savoir plus, consultez la section consacrée au plugin Kubernetes Device. GPUs

Déploiement d’un exemple de pod

Karpenter agit de manière dynamique : il provisionne des nœuds GPU lorsqu’une charge de travail (pod) demande des ressources GPU. Pour vérifier que les pods peuvent demander et utiliser GPUs, déployez un pod qui demande la nvidia.com/gpu ressource dans ses limites (par exemple,nvidia.com/gpu: 1). Pour en savoir plus sur ces étiquettes, consultez Planifier des charges de travail avec des exigences GPU à l’aide d’étiquettes bien connues.

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

Le résultat attendu devrait ressembler à ceci :

pod/gpu-ndivia-smi created

Attendez une minute, puis vérifiez si le Pod a le statut « En attente »ContainerCreating, « En cours », puis « Terminé » :

kubectl get pod gpu-nvidia-smi -w

Vérifiez que le nœud du pod appartient au 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"

Le résultat attendu devrait ressembler à ceci :

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

Consultez les journaux du pod :

kubectl logs gpu-nvidia-smi

Le résultat attendu devrait ressembler à ceci :

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. (Facultatif) Préparer et télécharger les artefacts du modèle pour le déploiement

Au cours de cette étape, vous allez déployer un service de modèle pour la classification d’images en temps réel, en commençant par télécharger les poids du modèle vers un compartiment Amazon S3. À des fins de démonstration, nous utilisons le modèle open source de vision GPUNet-0 de NVIDIA GPUNet, qui prend en charge l'inférence à faible latence sur les images à l'aide de NVIDIA et de TensorRT. GPUs Ce modèle est préentraîné ImageNet, nous permet de classer des objets sur des photos ou des flux vidéo à la volée et est considéré comme un petit modèle avec 11,9 millions de paramètres.

Configuration de votre environnement

Pour télécharger les poids de modèle GPUNet -0 Au cours de cette étape, vous devez accéder au catalogue NGC de NVIDIA et à Docker installé sur votre machine locale. Procédez comme suit pour créer un compte gratuit et configurer la CLI NGC :

  • Inscrivez-vous pour obtenir un compte NGC gratuit et générez une clé API à partir du tableau de bord NGC (icône utilisateur > Configuration > Générer une clé API > Générer une clé personnelle > Catalogue NGC).

  • Téléchargez et installez la CLI NGC (Linux/macOS/Windows) et configurez la CLI en utilisant :ngc config set. Saisissez votre clé API lorsque vous y êtes invité ; définissez org sur nvidia et appuyez sur Entrée pour accepter les valeurs par défaut pour les autres. Si l’opération aboutit, vous devriez voir quelque chose ressemblant à : Successfully saved NGC configuration to /Users/your-username/.ngc/config.

Vérifier les autorisations du compte de service

Avant de commencer, vérifiez les autorisations du compte de service Kubernetes :

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

Lors de la création du cluster, nous avons associé la CSIDriver politique S3 à un rôle IAM et annoté le compte de service (« s3- csi-driver-sa »). Les pods du pilote CSI Mountpoint S3 héritent des autorisations du rôle IAM lorsqu’ils interagissent avec S3. Le résultat attendu devrait ressembler à ceci :

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

Ajouter une tolérance

Le pilote S3 CSI s'exécute en tant que tel DaemonSet sur tous les nœuds. Les pods utilisent le pilote CSI sur ces nœuds pour monter les volumes S3. Pour lui permettre de se programmer sur nos nœuds GPU présentant des anomalies, ajoutez une tolérance aux éléments suivants : 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"}}]'

Le résultat attendu devrait ressembler à ceci :

daemonset.apps/s3-csi-node patched

Charger les poids du modèle dans S3

Au cours de cette étape, vous allez créer un compartiment Amazon S3, télécharger les poids de modèle GPUNet -0 depuis NVIDIA GPU Cloud (NGC) et les charger dans le compartiment. Ces poids seront accessibles par notre application au moment de l’exécution à des fins d’inférence.

Créez votre compartiment Amazon S3 :

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

Activez la gestion des versions S3 pour le compartiment afin d’éviter que des suppressions et des écrasements accidentels n’entraînent une perte immédiate et permanente des données :

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

Appliquez une règle de cycle de vie au compartiment pour supprimer les versions d’objets écrasées ou supprimées 14 jours après leur expiration, supprimer les marqueurs de suppression expirés et supprimer les téléchargements en plusieurs parties incomplets après 7 jours. Pour en savoir plus, consultez les exemples de configurations du cycle de vie S3.

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

Téléchargez les poids du modèle GPUNet -0 sur NGC. Par exemple, sur macOS :

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

Vous devrez peut-être adapter cette commande de téléchargement à votre système d’exploitation. Pour que cette commande fonctionne sur un système Linux, vous devrez probablement créer le répertoire dans le cadre de la commande (par exemple, mkdir ~/downloads).

Le résultat attendu devrait ressembler à ceci :

{ "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]" }

Renommez le fichier de point de contrôle pour qu'il corresponde au nom attendu dans le code de notre application lors des étapes ultérieures (aucune extraction n'est nécessaire, car il s'agit d'un point de contrôle PyTorch *.pth.tar standard contenant le dictionnaire d'état du modèle) :

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

Activez le AWS Common Runtime dans la AWS CLI pour optimiser le débit S3 :

aws configure set s3.preferred_transfer_client crt

Téléchargez les poids du modèle dans votre compartiment S3 :

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

Le résultat attendu devrait ressembler à ceci :

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

Créer le service de modèle

Au cours de cette étape, vous allez configurer une application Web FastAPI pour la classification d'images accélérée par GPU à l'aide du modèle de vision -0. GPUNet L'application télécharge les pondérations des modèles depuis Amazon S3 au moment de l'exécution, extrait l'architecture du modèle depuis le référentiel NVIDIA pour la mise en cache et télécharge les étiquettes de ImageNet classe via HTTP. L’application comprend des transformations de prétraitement d’images et expose deux points de terminaison : un GET racine pour la vérification de l’état et un point de terminaison /predict POST qui accepte une URL d’image.

Nous diffusons le modèle à l'aide de FastAPI PyTorch, en chargeant les poids depuis Amazon S3 lors de l'exécution dans une configuration conteneurisée pour un prototypage rapide et un déploiement Kubernetes. Pour d’autres méthodes telles que le traitement par lots optimisé ou les moteurs à haut débit, consultez la section Utilisation des modèles ML.

Pour créer l’application

Créez un répertoire pour vos fichiers d’application, tel que model-testing, puis accédez à ce répertoire et ajoutez le code suivant à un nouveau fichier nommé 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))

Créer le fichier Dockerfile

Le Dockerfile suivant crée une image de conteneur pour notre application en utilisant le GPUNet modèle du référentiel NVIDIA Deep Learning Examples for Tensor Cores. GitHub

Nous réduisons la taille de l'image du conteneur en utilisant une PyTorch base réservée à l'exécution, en installant uniquement les packages essentiels avec nettoyage du cache, pré-mise en cache du code du modèle, et en évitant de « modifier » les poids dans l'image du conteneur afin de permettre des extractions et des mises à jour plus rapides. Pour en savoir plus, consultez la section Réduire la taille des images de conteneur.

Dans le même répertoire que app.py, créez le 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"]

Tester l'application

À partir du même répertoire que votre app.py etDockerfile, créez l'image du conteneur pour l'application d'inférence, en ciblant AMD64 l'architecture :

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

Définissez des variables d'environnement pour vos AWS informations d'identification, et éventuellement un jeton de AWS session. Par exemple :

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

Exécutez le conteneur localement, en injectant des AWS informations d'identification en tant que variables d'environnement pour l'accès à S3. Par exemple :

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

Le résultat attendu devrait ressembler à ceci :

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)

Dans une nouvelle fenêtre de terminal, testez le point de terminaison d’inférence en envoyant une requête POST d’exemple avec une URL d’image publique comme paramètre de requête :

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

Le résultat attendu devrait être une réponse JSON avec les 5 meilleures prédictions, similaire à ceci (les étiquettes et probabilités réelles peuvent varier légèrement en fonction de l’image et de la précision du modèle) :

{"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}]}

Quittez l’application en appuyant sur « Ctrl + C ».

Poussez le conteneur vers Amazon ECR

Au cours de cette étape, nous téléchargeons l'image du conteneur pour le service modèle GPUNet -0 sur Amazon Elastic Container Registry (ECR), afin de la rendre disponible pour le déploiement sur Amazon EKS. Ce processus consiste à créer un nouveau référentiel ECR pour stocker l’image, à s’authentifier auprès d’ECR, puis à baliser et à pousser l’image du conteneur vers notre registre.

Tout d’abord, retournez dans le répertoire où vous avez défini vos variables d’environnement au début de ce guide. Par exemple :

cd ..

Créer un référentiel dans Amazon ECR :

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

Connectez-vous à 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

Le résultat attendu devrait ressembler à ceci :

Login Succeeded

Balisez l’image :

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

Poussez l’image vers votre référentiel Amazon ECR :

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

Cette dernière étape prend plusieurs minutes.

7. (Facultatif) Exposez le modèle de service

Au cours de cette étape, vous allez exposer votre service de modèle d'inférence en temps réel en externe sur Amazon EKS à l'aide du AWS Load Balancer Controller (LBC). Cela implique la configuration du LBC, le montage des poids de modèle depuis Amazon S3 en tant que volume persistant à l’aide du pilote Mountpoint S3 CSI, le déploiement d’un pod d’application accéléré par GPU, la création d’un service et d’une entrée pour provisionner un Application Load Balancer (ALB) et le test du point de terminaison.

Vérifiez d'abord l'association Pod Identity pour le AWS LBC, en vous assurant que le compte de service est correctement lié au rôle IAM requis :

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

Le résultat attendu devrait ressembler à ceci :

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

Baliser votre groupe de sécurité de cluster

Le AWS Load Balancer Controller ne prend en charge qu'un seul groupe de sécurité avec la clé de balise karpenter.sh/discovery: "${EKS_CLUSTER_NAME}" pour la sélection du groupe de sécurité par Karpenter. Lors de la création d’un cluster avec eksctl, le groupe de sécurité par défaut du cluster (qui possède la balise "kubernetes.io/cluster/<cluster-name>: owned") n’est pas automatiquement balisé avec des balises karpenter.sh/discovery. Cette balise est essentielle pour que Karpenter puisse détecter et associer ce groupe de sécurité aux nœuds qu’il provisionne. L'attachement de ce groupe de sécurité garantit la compatibilité avec le AWS Load Balancer Controller (LBC), ce qui lui permet de gérer automatiquement les règles de trafic entrant pour les services exposés via Ingress, tels que le service modèle décrit dans ces étapes.

Exportez l’ID VPC de votre cluster :

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

Exportez le groupe de sécurité par défaut de votre 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)"

Ajoutez la balise karpenter.sh/discovery au groupe de sécurité du cluster par défaut. Cela permettra à nos EC2 NodeClass sélecteurs de CPU et de GPU de l'utiliser :

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

Vérifiez que le tag a été ajouté :

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

Parmi les résultats, vous devriez voir ce qui suit avec le tag et le nom de votre cluster. Par exemple :

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

Configuration du AWS Load Balancer Controller (LBC)

Le AWS LBC est essentiel pour gérer le trafic entrant vers les charges de AI/ML travail sur Amazon EKS, en garantissant l'accès aux points de terminaison d'inférence ou aux pipelines de traitement des données. En s'intégrant aux équilibreurs de charge d' AWS application (ALB) et aux équilibreurs de charge réseau (NLB), le LBC achemine dynamiquement le trafic vers des applications conteneurisées, telles que celles qui exécutent de grands modèles linguistiques, des modèles de vision par ordinateur ou des services d'inférence en temps réel. Comme nous avons déjà créé le compte de service et l’association d’identité de pod lors de la création du cluster, nous définissons le paramètre serviceAccount.name pour qu’il corresponde à ce qui est défini dans la configuration de notre cluster (aws-load-balancer-controller).

Ajoutez le référentiel de cartes eks-charts Helm AWS appartenant à eks-charts :

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

Actualisez vos référentiels Helm locaux avec les graphiques les plus récents :

helm repo update eks

Déployez le AWS LBC à l'aide de Helm, en spécifiant le nom du cluster EKS et en faisant référence au compte de service pré-créé :

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

Le résultat attendu devrait ressembler à ceci :

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!

Monter le modèle dans un volume persistant

Au cours de cette étape, vous allez monter les pondérations des modèles à partir de votre compartiment Amazon S3 à l'aide d'un PersistentVolume (PV) soutenu par le pilote CSI Mountpoint for Amazon S3. Cela permet aux pods Kubernetes d’accéder aux objets S3 comme s’il s’agissait de fichiers locaux, éliminant ainsi les téléchargements gourmands en ressources vers le stockage éphémère des pods ou les conteneurs d’initialisation, ce qui est idéal pour les modèles volumineux de plusieurs gigaoctets.

Le PV monte l’ensemble de la racine du bucket (aucun chemin spécifié dans volumeAttributes), prend en charge l’accès en lecture seule simultané par plusieurs pods et expose les fichiers tels que les poids du modèle (/models/gpunet-0.pth) à l’intérieur du conteneur pour l’inférence. Cela garantit que la fonction de secours « téléchargement » de notre application (app.py) ne se déclenche pas, car le fichier existe via le montage. En dissociant le modèle de l’image du conteneur, cela permet un accès partagé et des mises à jour indépendantes des versions du modèle sans avoir à reconstruire l’image.

Créez le PersistentVolume (PV)

Créez une ressource PersistentVolume (PV) pour monter le compartiment S3 contenant les poids de votre modèle, en permettant un accès en lecture seule à plusieurs pods sans télécharger de fichiers lors de l'exécution :

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

Créez le PersistentVolumeClaim (PVC)

Créez un PersistentVolumeClaim (PVC) à lier au PV, en demandant un accès en lecture seule aux données du modèle S3 monté :

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

Déployer l'application

Déployez l’application d’inférence en tant que déploiement Kubernetes, en montant le volume persistant pris en charge par S3 pour l’accès au modèle, en appliquant les sélecteurs et les tolérances de nœuds GPU, et en définissant les variables d’environnement pour le chemin d’accès au modèle. Ce déploiement définit le chemin d’accès au modèle (variable d’environnement "/models/gpunet-0.pth"), de sorte que notre application (dans app.py) utilisera ce chemin par défaut. Avec le volume de déploiement monté à /models (en lecture seule), le téléchargement du modèle ne se déclenchera pas si le fichier est déjà présent via le 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

Il faudra quelques minutes à Karpenter pour provisionner un nœud GPU si aucun n’est déjà disponible. Vérifiez que le pod d’inférence est dans l’état « En cours d’exécution » :

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

Le résultat attendu devrait ressembler à ceci :

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

Exposez le service avec Ingress et Load Balancer

Créez un service ClusterIP pour exposer le déploiement d’inférence en interne au sein du cluster EKS, en ciblant le port de l’application :

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

Créez une ressource Ingress pour approvisionner un Application Load Balancer (ALB) connecté à Internet via le LBC, en acheminant le trafic externe vers AWS le service d'inférence :

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

Donnez à Application Load Balancer (ALB) la fin du provisionnement en l’espace de quelques minutes. Surveillez l’état des ressources d’entrée pour confirmer que l’ALB a été provisionné :

kubectl get ingress gpunet-model-ingress

La sortie attendue devrait ressembler à ceci (avec le champ ADDRESS rempli) :

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

Extrayez et exportez le nom d’hôte ALB à partir de l’état d’entrée pour l’utiliser lors de tests ultérieurs :

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

Testez le Model Service

Validez le point de terminaison d’inférence exposé en envoyant une demande POST avec une URL d’image échantillon (par exemple, provenant de du jeu de données COCO), simulant une prédiction en temps réel :

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

Le résultat attendu devrait être une réponse JSON avec les 5 meilleures prédictions, similaire à ceci (les étiquettes et probabilités réelles peuvent varier légèrement en fonction de l’image et de la précision du modèle) :

{"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}]}

Vous pouvez éventuellement continuer à tester d’autres images dans une nouvelle requête POST. Par exemple :

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

Conclusion

Dans ce guide, vous allez configurer un cluster Amazon EKS optimisé pour les charges de travail d’inférence en temps réel accélérées par GPU. Vous avez provisionné un cluster avec des instances G5 EC2, installé le pilote CSI Mountpoint S3, l'agent EKS Pod Identity, l'agent de surveillance des nœuds EKS, l'AMI Bottlerocket, le AWS Load Balancer Controller (LBC) et Karpenter pour gérer le processeur et le GPU. NodePools Vous avez utilisé le plug-in NVIDIA Device pour activer la planification du GPU et vous avez configuré S3 avec PersistentVolume et PersistentVolumeClaim pour l'accès aux modèles. Vous avez validé la configuration en déployant un exemple de pod GPU, en configurant l'accès au modèle NVIDIA GPUNet-0 sur Amazon S3, en activant l'initialisation du pod et en exposant le service d'inférence via Application Load Balancer. Pour exploiter pleinement votre cluster, configurez EKS Node Monitoring Agent avec la fonction de réparation automatique. Veillez à effectuer des tests de référence, notamment des évaluations des performances, de la latence et du débit du GPU afin d’optimiser les temps de réponse. Pour en savoir plus, consultez la section Utilisation des outils de surveillance et d'observabilité pour vos charges AI/ML de travail.

Nettoyage

Pour éviter d'encourir de futurs frais, vous devez supprimer manuellement la CloudFormation pile associée pour supprimer toutes les ressources créées au cours de ce guide, y compris le réseau VPC.

Supprimez la CloudFormation pile en utilisant le --wait drapeau avec eksctl :

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

Une fois l’opération terminée, vous devriez voir s’afficher la réponse suivante :

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

Supprimez le compartiment Amazon S3 créé au cours de ce guide à l’aide de la console Amazon S3.