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

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 du début à la fin. 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 permet d'éviter de sélectionner les paramètres du cluster, ce qui vous permet de mettre rapidement en place 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 plutôt que 023 ?) AL2 , 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 pour que les charges de travail d'inférence en ligne en temps réel soient opérationnelles. 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. Dans l'ensemble, il met l'accent 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 feedback client.

Dans ce guide, nous nous concentrons exclusivement sur la mise en place d'un point de départ prescriptif fondamental à 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 AI/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 fait référence au processus d'utilisation d'un modèle d'apprentissage automatique entraîné pour générer des prédictions ou des sorties sur les flux de données entrants avec une latence minimale. Par exemple, il permet de détecter les fraudes en temps réel, de classer des images ou de générer des graphiques 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 d'apprentissage automatique à faible latence dans les applications destinées aux utilisateurs en dissociant la gestion du trafic Web lié au processeur des calculs d'intelligence artificielle gourmands en GPU. Ce processus s'inscrit généralement dans un écosystème d'applications plus vaste et inclut souvent des services de backend, de frontend, de vecteur et de modèle, en mettant l'accent sur des composants spécialisés pour permettre une mise à l'échelle indépendante, un développement parallèle et une résilience en cas de défaillance. 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 RAG (Retrieval Augmented Generation) 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'entrée peut également gérer les règles d'accès et de routage externes pour améliorer la sécurité et la régulation 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 . Cette isolation empêche les conflits de dépendance, permet de mettre à jour les modèles sans interruption de service et permet une mise à l'échelle horizontale avec équilibrage de charge pour plusieurs demandes 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 performante capable de fournir une latence extrêmement faible tout en gérant des pics de trafic imprévisibles et importants. 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 — Pour les tâches d'inférence gourmandes en GPU, nous utilisons les types d' EC2 instances G5 g5.xlarge et g5.2xlarge, qui comportent un (1) seul GPU NVIDIA A10G avec 24 Go de mémoire ( EC2 par exemple, 8 milliards de paramètres chez). FP16 Basés sur l'architecture NVIDIA Ampere, GPUs ils sont alimentés par des processeurs NVIDIA A10G Tensor Core GPUs et AMD EPYC de 2e génération, prennent en charge une bande passante réseau de 4 à 8 VCPUs, jusqu'à 10 Gbit/s et 250 à 450 Go de stockage NVMe SSD local, garantissant un transfert rapide des données et une puissance de calcul pour les modèles complexes, ce qui les rend idéaux pour les tâches d'inférence à faible latence et à haut débit. Le choix d'un type d' EC2 instance est spécifique à l'application, dépend de votre modèle (par exemple, modèle d'image, de vidéo, 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 souhaiterez peut-être utiliser des EC2 instances P5 pour une latence optimale en temps réel. Nous vous recommandons de commencer par les EC2 instances G5, car elles constituent un bon point de départ pour une mise en service rapide, puis d'évaluer si elles sont adaptées à vos charges de travail grâce à des tests de performance. Pour les cas plus avancés, pensez aux EC2 instances G6.

  • Instances Amazon EC2 m7g : pour les tâches gourmandes en ressources processeur telles que le prétraitement des données, le traitement des demandes d'API, l'hébergement du contrôleur Karpenter, les modules complémentaires et d'autres composants du système, nous utilisons le type d'instance m5.xlarge m7g. EC2 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' EC2 instance est spécifique à l'application et dépend des exigences de calcul, de mémoire et d'évolutivité de votre charge de travail. Pour les charges de travail optimisées pour le calcul, vous pouvez envisager les EC2 instances C7g, qui utilisent également des processeurs Graviton3 mais sont optimisées pour des performances de calcul supérieures à celles des instances M7g dans certains cas d'utilisation. Par ailleurs, les nouvelles EC2 instances 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 EC2 instances m7G pour leur rentabilité et leur compatibilité avec un large éventail de charges de travail (par exemple, les serveurs d'applications, les microservices, les serveurs de jeu, les magasins de données de taille moyenne), puis d'évaluer si elles conviennent à vos charges de travail grâce à des tests de performance.

  • Pilote CSI Mountpoint Amazon S3 — Pour les charges de travail sur des instances à GPU unique où plusieurs pods partagent un GPU (par exemple, plusieurs pods programmés sur le même nœud pour utiliser ses ressources GPU), nous utilisons le pilote CSI Mountpoint S3 pour optimiser l'utilisation de la mémoire, ce qui est essentiel pour des tâches telles que l'inférence de grands modèles dans des configurations sensibles aux coûts et peu complexes. Il présente les buckets Amazon S3 comme un système de fichiers de type POSIX disponible pour le cluster Kubernetes, qui permet aux modules d'inférence de lire les artefacts des modèles (par exemple, les poids des modèles) directement en mémoire sans avoir à les télécharger au préalable, et de saisir des ensembles 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 est spécifique à l'application et dépend des exigences de débit et de latence de votre charge de travail. 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 rationalisée à l'approche traditionnelle des rôles IAM pour les comptes de service (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. En outre, il permet de réutiliser les rôles sur 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 Target IAM.

  • Agent de surveillance des nœuds EKS — Pour 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 les temps d'arrêt. 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, initie une correction progressive en bouclant les nœuds défectueux, en attendant 10 minutes que les problèmes de GPU transitoires soient résolus et en remplaçant les nœuds au bout de 30 minutes en cas de défaillance persistante.

  • AMI Bottlerocket — Pour renforcer la sécurité de notre cluster EKS, nous utilisons l'AMI Bottlerocket, qui inclut uniquement 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 est spécifique à l'application et dépend des exigences de personnalisation, de sécurité et d'évolutivité de votre charge de travail. Bien que l'AMI AL2 023 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éez 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 du système CPU, y compris l'hébergement du contrôleur Karpenter, des modules complémentaires et d'autres composants du 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, les 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 la section Déployer des nœuds sur 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é de l'instance

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

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

En cas de succès, 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 ID AMI Bottlerocket pour la 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

En cas de succès, l'AMI Bottlerocket est disponible dans la région que vous avez spécifiée.

Préparez votre environnement

Définissez d'abord les variables d'environnement suivantes dans une nouvelle fenêtre de terminal. Remarque : veillez à remplacer les exemples d'espaces réservés par vos valeurs uniques, 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 des commandes ultérieures pour des raisons de cohérence et pour éviter les répétitions. Assurez-vous d'exécuter les commandes dans l'ordre afin que ces valeurs soient correctement exportées et puissent ê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éation des rôles et des politiques requis

Karpenter a besoin de rôles et de politiques IAM spécifiques (par exemple, le rôle IAM du contrôleur Karpenter, le profil d'instance et les politiques) pour gérer les EC2 instances en tant que nœuds de travail Kubernetes. Il utilise ces rôles pour effectuer des actions telles que le lancement et la résiliation d' EC2 instances, le balisage des ressources et l'interaction avec d'autres AWS services. Créez les rôles et les politiques de 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éez la politique LBC IAM :

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

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 CSI Mountpoint for Mountpoint S3 doit être autorisé pour interagir avec le compartiment Amazon S3 que vous créez 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 au cours de 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, le plug-in VPC CNI. À ce jour, le pilote CSI Mountpoint S3 n'est pas disponible pour EKS Pod Identity, nous avons donc créé un compte IRSA (IAM Roles for Service Account) 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 vérifications de santé pour nous assurer que le cluster est prêt. Lorsque la commande précédente est terminée, visualisez les types d'instances et vérifiez que les nœuds de votre système CPU ont atteint l'Readyétat requis à 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 Pod Identity et la manière dont elles associent 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 nœuds de travail 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. Configuration : 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 le total des ressources sur tous les nœuds du pool, en autorisant 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 d'interruption pour supprimer les nœuds vides au bout de 30 minutes (consolidateAfter: 30m) et fixé une durée de vie maximale des nœuds de 30 jours (expireAfter: 720h) afin d'optimiser les coûts et de préserver l'intégrité des nœuds pour les tâches gourmandes en 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 d'interruption pour supprimer les nœuds vides au bout de 60 minutes (consolidateAfter: 60m) et fixé une durée de vie maximale des nœuds de 30 jours (expireAfter: 720h) afin d'optimiser les coûts et de préserver l'intégrité des nœuds pour les tâches gourmandes en 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 Nvidia Device Plugin 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éployer un module d'échantillonnage

Karpenter agit de manière dynamique : il approvisionne les 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, voir Planifier les charges de travail en fonction des exigences en matière de 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élisation pour la classification des images en temps réel, en commençant par le téléchargement des pondérations des modèles dans 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. Suivez ces étapes pour créer un compte gratuit et configurer la CLI NGC :

  • Ouvrez un compte NGC gratuit et générez une clé API depuis le 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. Entrez votre clé d'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. En cas de succès, vous devriez voir quelque chose comme : 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 le contrôle de version S3 pour le compartiment, afin d'éviter que les suppressions et remplacements accidentels n'entraînent une perte de données immédiate et permanente :

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

Appliquez une règle de cycle de vie au compartiment afin de supprimer les versions d'objets remplacées ou supprimées 14 jours après leur date d'expiration, de supprimer les marqueurs de suppression expirés et de supprimer les téléchargements en plusieurs parties incomplets au bout de 7 jours. Pour en savoir plus, consultez 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 devez 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éation du service de modèles

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 inclut des transformations de prétraitement d'image et expose deux points de terminaison : un point de terminaison GET racine pour la vérification du statut et un point de /predict terminaison POST qui accepte une URL d'image.

Nous diffusons le modèle à l'aide de FastAPI PyTorch, en chargeant les poids depuis Amazon S3 au moment 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, voir Serving ML Models.

Pour créer l’application

Créez un répertoire pour les fichiers de votre application, par exemplemodel-testing, puis remplacez-le par des répertoires 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éez le 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, en précachant le 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éduction de la taille des images des conteneurs.

Dans le même répertoire queapp.py, créez 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 un exemple de requête POST 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 doit être une réponse JSON avec les 5 meilleures prédictions, similaire à celle-ci (les étiquettes et les probabilités réelles peuvent varier légèrement en fonction de la précision de l'image et 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 ».

Transférez 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 implique la création d'un nouveau référentiel ECR pour stocker l'image, l'authentification avec ECR, puis le balisage et le transfert de l'image du conteneur vers notre registre.

Tout d'abord, revenez au répertoire dans lequel vous avez défini vos variables d'environnement au début de ce guide. Par exemple :

cd ..

Créez 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

Marquez l'image :

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

Transférez 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 de configurer le LBC, de monter les pondérations des modèles depuis Amazon S3 sous forme de volume persistant à l'aide du pilote CSI Mountpoint S3, de déployer un module d'application accéléré par GPU, de créer un service et une entrée pour configurer un Application Load Balancer (ALB) et de tester le 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

Marquez le groupe de sécurité de votre 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é du cluster par défaut (qui possède la "kubernetes.io/cluster/<cluster-name>: owned" balise) n'est pas automatiquement étiqueté avec des karpenter.sh/discovery balises. Cette balise est essentielle pour que Karpenter découvre et attache ce groupe de sécurité aux nœuds qu'il approvisionne. 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 pour 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 karpenter.sh/discovery balise 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 Pod Identity lors de la création du cluster, nous les avons définis pour qu'ils correspondent serviceAccount.name à ce qui est défini dans notre configuration de 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 sous forme de fichiers locaux, éliminant ainsi les téléchargements gourmands en ressources vers des espaces de stockage éphémères ou des conteneurs d'initialisation, ce qui est idéal pour les modèles volumineux de plusieurs gigaoctets.

Le PV monte l'intégralité de la racine du compartiment (aucun chemin n'est spécifié dansvolumeAttributes), prend en charge l'accès simultané en lecture seule par plusieurs modules et expose des fichiers tels que les poids du modèle (/models/gpunet-0.pth) à l'intérieur du conteneur à des fins d'inférence. Cela garantit que le « téléchargement » de secours dans notre application (app.py) ne se déclenche pas car le fichier existe via le montage. En découplant 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 sous la forme d'un déploiement Kubernetes, en montant le volume persistant soutenu par S3 pour l'accès au modèle, en appliquant des sélecteurs de nœuds GPU et des tolérances, et en définissant des variables d'environnement pour le chemin du modèle. Ce déploiement définit le chemin du modèle (env var of"/models/gpunet-0.pth"), de sorte que notre application (inapp.py) utilisera ce chemin par défaut. Lorsque le volume du déploiement est monté sur /models (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

Karpenter mettra quelques minutes à provisionner un nœud GPU s'il n'en existe pas déjà un. Vérifiez que le module d'inférence est dans un é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

Attendez quelques minutes pour que l'Application Load Balancer (ALB) termine le provisionnement. 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 requête POST avec un exemple d'URL d'image (par exemple, à partir du jeu de données COCO), en 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 doit être une réponse JSON avec les 5 meilleures prédictions, similaire à celle-ci (les étiquettes et les probabilités réelles peuvent varier légèrement en fonction de la précision de l'image et 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 EC2 instances G5, 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 utiliser pleinement votre cluster, configurez l'agent de surveillance des nœuds EKS avec 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

À la fin, vous devriez voir le résultat de réponse suivant :

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.