Amazon EKS에서의 실시간 추론 모범 사례 클러스터 설정 가이드 - Amazon EKS

이 페이지 개선에 도움 주기

이 사용자 가이드에 기여하려면 모든 페이지의 오른쪽 창에 있는 GitHub에서 이 페이지 편집 링크를 선택합니다.

Amazon EKS에서의 실시간 추론 모범 사례 클러스터 설정 가이드

소개

이 가이드는 실시간 온라인 추론 워크로드에 최적화된 Amazon Elastic Kubernetes Service(EKS) 클러스터를 설정하여 AWS 전문가가 큐레이션한 모범 사례를 통합하는 실습을 제공합니다. 모델, 액셀러레이터 및 스케일링에 대한 AWS 모범 사례에 따라 큐레이션된 드라이버, 인스턴스 유형 및 구성 세트인 독자적 EKS Quickstart 아키텍처를 사용합니다. 이 접근 방식을 사용하면 클러스터 설정을 선택하는 작업을 우회하여 사전 구성된 클러스터를 빠르게 설치하고 실행할 수 있습니다. 그 과정에서 샘플 워크로드를 배포하여 설정을 검증하고, 주요 아키텍처 개념(예: GPU 집약적 계산에서 CPU 의존 작업 분리)을 설명하고, 일반적인 질문을 해결하고(예: AL2023에서 Bottlerocket AMI를 선택하는 이유), 클러스터 기능 확장을 위한 다음 단계를 간략하게 설명합니다.

AWS 및 EKS 에코시스템을 처음 사용하는 기계 학습(ML) 및 인공 지능(AI) 엔지니어, 플랫폼 관리자, 운영자와 데이터/AI 전문가에 맞춰 특별히 설계된 이 가이드는 Kubernetes에 익숙하지만 EKS를 사용한 경험은 없다고 가정합니다. 실시간 온라인 추론 워크로드를 시작하고 실행하는 데 필요한 단계와 프로세스를 이해하는 데 도움이 되도록 설계되었습니다. 이 가이드에서는 GPU 리소스 프로비저닝, 모델 아티팩트용 스토리지 통합, 보안 AWS 서비스 액세스 활성화, 추론 엔드포인트 노출 등 단일 노드 추론 클러스터 생성에 필요한 사항을 보여줍니다. 전체적으로 사기 탐지, 실시간 챗봇, 고객 피드백 시스템의 감정 분석과 같이 사용자에게 표시되는 애플리케이션을 위한 지연 시간이 짧고 복원력이 뛰어난 설계를 강조합니다.

이 가이드에서는 G5 EC2 인스턴스를 사용하여 기본적인 권장 시작점을 설정하는 데만 중점을 둡니다. AWS Inferentia 관련 클러스터 구성 또는 종단 간 워크플로를 찾는 경우의 기계 학습을 위해 Amazon EKS와 함께 AWS Inferentia 인스턴스 사용 또는 Amazon EKS에서 AI/ML을 시작하기 위한 리소스의 워크숍을 참조하세요.

시작하기 전 준비 사항

시작하기 전에 다음 태스크를 수행했는지 확인합니다.

아키텍처

실시간 온라인 추론은 훈련된 기계 학습 모델을 사용하여 지연 시간을 최소화하면서 수신 데이터 스트림에 대한 예측 또는 출력을 생성하는 프로세스를 의미합니다. 예를 들어 실시간 사기 탐지, 이미지 분류 또는 사용자 입력에 대한 응답에 따른 지식 그래프 생성이 가능합니다. 실시간 온라인 추론 시스템의 아키텍처는 GPU 집약적 AI 계산에서 CPU 의존 웹 트래픽 처리를 분리하여 사용자에게 표시되는 애플리케이션에서 지연 시간이 짧은 기계 학습 예측을 제공합니다. 이 프로세스는 일반적으로 더 큰 애플리케이션 에코시스템 내에 상주하고, 주로 백엔드, 프론트엔드, 벡터 및 모델 서비스를 포함하며, 실패에 대한 독립적 조정, 병렬 개발 및 복원력을 지원하는 특수 구성 요소에 중점을 둡니다. 전용 GPU 하드웨어에서 추론 작업을 격리하고 API 및 WebSocket과 같은 인터페이스를 활용하면 높은 동시성, 변환기와 같은 모델의 빠른 처리, 프론트엔드를 통한 사용자 상호 작용이 보장됩니다. 벡터 데이터베이스와 검색 증강 생성(RAG) 파이프라인은 실시간 추론 시스템에서 중요한 역할을 하는 경우가 많지만 이러한 구성 요소는 이 가이드에서 다루지 않습니다. 일반적인 아키텍처에는 다음 요소가 최소한으로 포함됩니다.

  • 프론트엔드 서비스: 사용자 대면 인터페이스 역할을 하면서 클라이언트 측 로직을 처리하고, 동적 콘텐츠를 렌더링하고, 실시간 상호 작용을 지원합니다. 백엔드 서비스와 통신하여 추론 요청을 시작하고 결과를 표시하며, 종종 스트리밍 업데이트에 WebSocket을 사용하거나 구조화된 데이터 교환에 API를 사용하는 백엔드 서비스 요청을 시작합니다. 이 서비스는 일반적으로 정적 자산을 위해 AWS CloudFront와 같은 콘텐츠 전송 네트워크(CDN)에서 호스팅되거나 웹 서버에서 직접 제공될 수 있으므로 전용 로드 밸런서가 필요하지 않으며, 동적 콘텐츠에 대해 필요한 경우 Auto Scaling 그룹을 통해 스케일링이 처리됩니다.

  • 백엔드 서비스: 애플리케이션의 오케스트레이터 역할을 하며, 사용자 인증, 데이터 검증 및 서비스 조정과 같은 비즈니스 로직을 관리합니다(예: RESTful 엔드포인트용 API 또는 영구 연결을 위한 WebSocket을 통해). 추론 서비스와 통신하고, GPU에 의존하지 않은 채 높은 웹 트래픽을 처리하기 위해 멀티 코어 CPU 및 RAM에서 독립적으로 조정하고, 특히 동시성이 높은 시나리오에서 여러 인스턴스에 수신 요청을 분산하기 위해 로드 밸런서(예: AWS Application Load Balancer 또는 Network Load Balancer)가 필요한 경우가 많습니다. 수신 컨트롤러는 외부 액세스 및 라우팅 규칙을 추가로 관리하여 보안 및 트래픽 셰이핑을 강화할 수 있습니다.

  • 추론 서비스: 사용자 지정 또는 오픈 소스 모델을 사용하여 벡터 임베딩, 지식 추출 및 모델 추론(예: 배치 요청의 경우 API, 실시간 스트리밍의 경우 WebSocket을 통해 노출됨)을 수행하기에 VRAM이 충분한(예: DistilBERT와 같은 모델의 경우 8~12GB) GPU에서 실행되는 AI 계산의 코어 역할을 합니다. 이 격리는 종속성 충돌을 방지하고, 가동 중지 없이 모델 업데이트를 허용하며, 여러 개의 동시 요청에 대해 로드 밸런싱을 통한 수평적 스케일링을 활성화합니다. 모델 서비스를 효과적으로 노출하기 위해 일반적으로 로드 밸런서 뒤에 배치하여 복제된 인스턴스에서 GPU 의존 워크로드를 분산하는 반면, 수신 리소스 또는 컨트롤러(예: AWS의 ALB Ingress Controller)는 외부 라우팅, SSL 종료 및 경로 기반 전달을 처리하여 개별 GPU에 부담을 주지 않고 안전하면서 효율적인 액세스를 보장합니다.

솔루션 개요

실시간 온라인 추론 시스템에는 예측할 수 없는 대량의 트래픽 버스트를 처리하는 동시에 매우 짧은 지연 시간을 제공할 수 있도록 복원력이 뛰어난 고성능 아키텍처가 필요합니다. 이 솔루션 개요는 클러스터가 최종 사용자의 지연을 최소화하면서 라이브 데이터에 대한 즉각적인 예측을 제공하는 기계 학습 모델을 호스팅 및 관리할 수 있도록 Amazon EKS 클러스터에서 다음 AWS 구성 요소가 함께 작동하는 방식을 설명합니다.

  • Amazon G5 EC2 인스턴스 - GPU 집약적 추론 작업의 경우 g5.xlarge 및 g5.2xlarge G5 EC2 인스턴스 유형을 사용하며, 24GB 메모리(예: FP16에서 80억 개의 파라미터)가 포함된 단일 NVIDIA A10G GPU가 사용됩니다. NVIDIA Ampere 아키텍처를 기반으로 하는 이러한 GPU는 NVIDIA A10G Tensor Core GPU 및 2세대 AMD EPYC 프로세서로 구동되고, 4~8개의 vCPU, 최대 10Gbps 네트워크 대역폭 및 250~450GB의 로컬 NVMe SSD 스토리지를 지원합니다. 그에 따라 복잡한 모델에서의 빠른 데이터 이동과 컴퓨팅 성능이 보장되어 지연 시간이 짧고 처리량이 많은 추론 작업에 적합합니다. EC2 인스턴스 유형 선택은 애플리케이션별로 다르며 모델(예: 이미지, 비디오, 텍스트 모델)과 지연 시간 및 처리량 요구 사항에 따라 달라집니다. 예를 들어 이미지 및/또는 비디오 모델을 사용하는 경우 실시간에 가까운 최적의 지연 시간을 위해 P5 EC2 인스턴스를 사용하는 것이 좋습니다. 빠르게 시작하고 실행하는 데 있어 좋은 출발점이 되므로 G5 EC2 인스턴스로 시작한 다음 성능 벤치마크 테스트를 통해 워크로드에 적합한지 여부를 평가하는 것이 좋습니다. 고급 사례의 경우 G6 EC2 인스턴스를 고려하세요.

  • Amazon EC2 M7g 인스턴스 - 데이터 사전 처리, API 요청 처리, Karpenter 컨트롤러, 추가 기능 및 기타 시스템 구성 요소 호스팅 등 CPU 집약적 작업의 경우 m5.xlarge M7g EC2 인스턴스 유형을 사용합니다. M7g 인스턴스는 4개의 vCPU, 16GB 메모리, 최대 12.5Gbps 네트워크 대역폭을 갖추고 AWS Graviton3 프로세서로 구동되는 ARM 기반 인스턴스입니다. EC2 인스턴스 유형 선택은 애플리케이션별로 다르며 워크로드의 컴퓨팅, 메모리 및 확장성 요구 사항에 따라 달라집니다. 컴퓨팅 최적화 워크로드의 경우 Graviton3 프로세서도 사용하지만 특정 사용 사례에서 M7g 인스턴스보다 더 높은 컴퓨팅 성능에 최적화된 C7g EC2 인스턴스를 고려하는 것이 좋습니다. 또는 최신 C8g EC2 인스턴스(사용 가능한 경우)는 C7g 인스턴스에 비해 최대 30% 향상된 컴퓨팅 성능을 제공합니다. 비용 효율성과 다양한 워크로드(예: 애플리케이션 서버, 마이크로서비스, 게임 서버, 중간 규모의 데이터 스토어)와의 호환성을 위해 M7g EC2 인스턴스로 시작하고 성능 벤치마크 테스트를 통해 워크로드에 적합한지 여부를 평가하는 것이 좋습니다.

  • Amazon S3 Mountpoint CSI 드라이버 - 여러 개의 포드가 GPU를 공유하는 단일 GPU 인스턴스(예: GPU 리소스를 활용하기 위해 동일한 노드에 여러 포드가 예약됨)의 워크로드의 경우 Mountpoint S3 CSI 드라이버를 사용하여 비용에 민감하고 복잡성이 낮은 설정에서 대규모 모델 추론 등의 작업에 필요한 메모리 사용량을 최적화합니다. Amazon S3 버킷을 Kubernetes 클러스터에 사용할 수 있는 POSIX와 유사한 파일 시스템으로 노출하므로 추론 포드가 모델 아티팩트(예: 모델 가중치)를 먼저 다운로드할 필요 없이 메모리로 직접 읽고 표준 파일 작업을 사용하여 데이터세트를 입력할 수 있습니다. 추가로 S3는 스토리지 용량이 거의 무제한이며 데이터 집약적 추론 워크로드를 가속화합니다. 스토리지 CSI 드라이버 선택은 애플리케이션별로 다르며 워크로드의 처리량 및 지연 시간 요구 사항에 따라 달라집니다. FSx for OpenZFS CSI 드라이버는 노드 간 임의 I/O 또는 완전 POSIX 호환 공유 영구 볼륨에 밀리초 미만의 지연 시간을 제공합니다. 하지만 확장성, 대규모 데이터세트의 비용 절감, 읽기 중심 추론 패턴(예: 스트리밍 모델 입력)을 위한 S3 관리형 객체 스토리지와의 내장 통합으로 인해 Mountpoint S3 CSI 드라이버로 시작한 다음 성능 벤치마크 테스트를 통해 워크로드에 적합한지 평가하는 것이 좋습니다.

  • EKS Pod Identity Agent - AWS 서비스에 대한 액세스를 활성화하기 위해 단일 서비스 보안 주체를 사용하고 Amazon EKS 클러스터 내에서 포드 수준 IAM 역할 연결을 지원하는 EKS Pod Identity Agent를 사용합니다. EKS Pod Identity는 각 클러스터의 개별 OIDC 공급자에 의존하는 대신 단일 서비스 보안 주체(pods.eks.amazonaws.com)를 활용하여 IRSA(서비스 계정 기능에 대한 IAM 역할) 접근 방식에 대한 간소화된 대안을 제공하므로 권한을 더 쉽게 할당할 수 있습니다. 추가로 여러 클러스터에서 역할을 재사용할 수 있으며 IAM 역할 세션 태그대상 IAM 역할과 같은 고급 기능을 지원합니다.

  • EKS 노드 모니터링 에이전트 - 추론 서비스의 지속적인 가용성과 신뢰성을 보장하기 위해 비정상 노드를 자동으로 감지하고 교체하여 가동 중지 시간을 최소화하는 자동 복구 기능이 포함된 EKS 노드 모니터링 에이전트를 사용합니다. 개선된 상태 확인(예: KernelReady, NetworkingReady)을 사용하여 노드에서 하드웨어, 커널, 네트워킹 및 스토리지 문제를 지속적으로 모니터링합니다. GPU 노드의 경우 액셀러레이터별 장애를 감지하고, 비정상 노드를 코드화하고, 일시적인 GPU 문제가 해결될 때까지 10분을 기다린 다음, 30분 후에 영구 장애 노드를 교체하여 정상적인 문제 해결을 시작합니다.

  • Bottlerocket AMI - EKS 클러스터에 보안 강화 기반을 제공하기 위해 Bottlerocket AMI를 사용합니다. 이 AMI에는 컨테이너를 실행하는 데 필요한 필수 구성 요소만 포함되어 있고 빠른 스케일링을 위해 최소한의 부팅 시간을 제공합니다. 노드 AMI 선택은 애플리케이션별로 다르며 워크로드의 사용자 지정, 보안 및 확장성 요구 사항에 따라 달라집니다. AL2023 AMI는 호스트 수준 설치 및 사용자 지정(예: 추가 노드 구성 없이 PV/PVC에 전용 캐시 디렉터리 지정)에 있어 더욱 유연하게 활용할 수 있습니다. 하지만 설치 공간이 작고 컨테이너화된 워크로드(예: 마이크로서비스, 추론 서버, 확장 가능한 API)에 대한 최적화가 내장된 Bottlerocket AMI로 시작한 다음 성능 벤치마크 테스트를 통해 워크로드에 적합한지 평가하는 것이 좋습니다.

  • AWS LBC(Load Balancer Controller) - 실시간 추론 엔드포인트를 노출하기 위해 AWS Load Balancer Controller를 사용합니다. Kubernetes Ingress 및 Service 리소스를 기반으로 HTTP/HTTPS 트래픽용 Application Load Balancer(ALB)와 TCP/UDP 트래픽용 Network Load Balancer(NLB)를 자동으로 프로비저닝 및 관리하여 추론 모델을 외부 클라이언트와 통합할 수 있습니다. 추가로 경로 기반 라우팅과 같은 기능을 지원하여 여러 포드 또는 노드에서 추론 요청을 분산하여 트래픽 급증 시 확장성을 보장하고 연결 멀티플렉싱 및 상태 확인과 같은 AWS 기본 최적화를 통해 지연 시간을 최소화합니다.

1. EKS 클러스터 생성

이 단계에서는 AWS CloudFormation 기반 eksctl ClusterConfig 템플릿을 사용하여 CPU 노드와 관리형 노드 그룹이 포함된 클러스터를 생성합니다. CPU 노드만 있는 클러스터를 초기화하면 이후 단계에서 생성하는 Karpenter NodePool을 사용하여 최적화된 리소스 할당에 Karpenter를 단독으로 사용하여 CPU 집약적 및 GPU 노드를 관리할 수 있습니다. 실시간 추론 워크로드를 지원하기 위해 EKS Bottlerocket AMI, EKS 노드 모니터링 에이전트, EKS Pod Identity Agent, Mountpoint S3 CSI 드라이버, AWS LBC(Load Balancer Controller), kube-proxy, vpc-cnicoredns 드라이버를 사용하여 클러스터를 프로비저닝합니다. m7g.xlarge 인스턴스는 Karpenter 컨트롤러, 추가 기능 및 기타 시스템 구성 요소 호스팅을 포함한 CPU 시스템 작업에 사용됩니다.

기본적으로 eksctl은 CIDR 블록이 192.168.0.0/16인 클러스터에 대한 전용 VPC를 생성합니다. VPC에는 퍼블릭 서브넷 3개와 프라이빗 서브넷 3개가 포함되고, 각각 서로 다른 가용 영역 3개(또는 us-east-1 리전의 AZ 2개)에 분산되어 있으므로 Kubernetes 워크로드를 배포하는 데 이상적입니다. 또한 템플릿은 인터넷 게이트웨이를 배포하여 라우팅 테이블의 기본 경로를 통해 퍼블릭 서브넷에 인터넷 액세스를 제공하고 퍼블릭 서브넷 중 하나의 단일 NAT 게이트웨이를 제공하며, 프라이빗 서브넷의 라우팅 테이블의 기본 경로는 인터넷 액세스를 위해 NAT 게이트웨이를 통해 아웃바운드 트래픽을 지시합니다. 이 설정에 대한 자세한 내용은 Deploy Nodes to Private Subnets를 참조하세요.

자격 증명 확인

AWS CLI 자격 증명이 유효하고 AWS 서비스로 인증할 수 있는지 확인:

aws sts get-caller-identity

성공하면 CLI는 AWS ID에 대한 세부 정보(UserId, 계정 및 Arn)를 반환합니다.

인스턴스 가용성 확인

일부 리전에서는 G5 인스턴스 유형을 사용할 수 없습니다. 가장 가까운 리전을 확인합니다. 예:

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

성공하면 지정한 리전에서 G5 인스턴스 유형을 사용할 수 있습니다.

일부 리전에서는 Bottlerocket AMI를 사용할 수 없습니다. 가장 가까운 리전의 Bottlerocket AMI ID를 검색하여 확인합니다. 예:

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

성공하면 지정한 리전에서 Bottlerocket AMI를 사용할 수 있습니다.

환경 준비

먼저 새 터미널 창에서 다음 환경 변수를 설정합니다. 참고: 샘플 자리 표시자를 클러스터 이름, 원하는 리전, Karpenter 릴리스 버전, Kubernetes 버전 등 고유한 값으로 바꿔야 합니다.

작은 정보

일부 변수(예: ${AWS_REGION}${K8S_VERSION})는 블록 초기에 정의되고, 이후 일관성 유지와 반복 방지를 위해 명령에서 참조됩니다. 이러한 값을 올바르게 내보내고 후속 정의에서 사용할 수 있도록 명령을 순서대로 실행해야 합니다.

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

필요한 역할 및 정책 생성

EC2 인스턴스를 Kubernetes 워커 노드로 관리하려면 Karpenter에 특정 IAM 역할 및 정책(예: Karpenter 컨트롤러 IAM 역할, 인스턴스 프로파일 및 정책)이 필요합니다. 이러한 역할을 사용하여 EC2 인스턴스 시작 및 종료, 리소스 태그 지정, 다른 AWS 서비스와의 상호 작용 등의 작업을 수행합니다. Karpenter의 cloudformation.yaml을 사용하여 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}"

AWS LBC에는 Ingress 리소스용 ALB 또는 LoadBalancer 유형의 서비스용 NLB 생성과 같은 AWS 로드 밸런서를 프로비저닝 및 관리할 수 있는 권한이 필요합니다. 클러스터 생성 중에 이 권한 정책을 지정합니다. 클러스터 생성 도중 ClusterConfig에서 eksctl을 사용하여 서비스 계정을 생성합니다. 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)"

Mountpoint S3 CSI 드라이버가 설치되면 실행에 서비스 계정을 사용하도록 DaemonSet 포드가 구성됩니다. Mountpoint for Mountpoint S3 CSI 드라이버에는 이 가이드의 뒷부분에서 생성한 Amazon S3 버킷과 상호 작용할 수 있는 권한이 필요합니다. 클러스터 생성 중에 이 권한 정책을 지정합니다. 클러스터 생성 도중 ClusterConfig에서 eksctl을 사용하여 서비스 계정을 생성합니다. S3 IAM 정책 생성:

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}/*\"]}]}"

참고: 이 이름으로 된 역할이 이미 있는 경우 역할에 다른 이름을 지정합니다. 이 단계에서 생성하는 역할은 클러스터와 S3 버킷에 따라 다릅니다.

클러스터 생성

이 템플릿에서 eksctl은 EKS Pod Identity, 노드 모니터링 에이전트, CoreDNS, Kubeproxy, VPC CNI 플러그인에 대한 Kubernetes 서비스 계정을 자동으로 생성합니다. 현재 Mountpoint S3 CSI 드라이버는 EKS Pod Identity에서 사용할 수 없으므로 IRSA(서비스 계정에 대한 IAM 역할)와 OIDC 엔드포인트를 생성합니다. 또한 AWS LBC(Load Balancer Controller)에 대한 서비스 계정을 생성합니다. Bottlerocket 노드에 액세스하기 위해 eksctl은 Bottlerocket용 AmazonSSMManagedInstanceCore를 자동으로 연결하여 SSM을 통한 보안 쉘 세션을 허용합니다.

환경 변수를 설정했던 동일한 터미널에서 다음 명령 블록을 실행하여 클러스터를 생성합니다.

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

이 프로세스는 완료하는 데 몇 분 정도 걸립니다. 상태를 모니터링하려면 AWS CloudFormation 콘솔을 참조하세요.

2. 클러스터 노드 및 포드 상태 확인

몇 가지 상태 확인을 수행하여 클러스터가 준비되었는지 확인합니다. 이전 명령이 완료되면 다음 명령으로 인스턴스 유형을 보고 CPU 시스템 노드가 Ready 상태가 되었는지 확인합니다.

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

예상되는 출력은 다음과 같아야 합니다.

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

다음 명령을 사용하여 모든 Pod Identity 연결과 역할을 클러스터의 네임스페이스에 있는 서비스 계정에 매핑하는 방법을 확인합니다.

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

출력에는 Karpenter에 대한 IAM 역할("karpenter") 및 AWS LBC에 대한 IAM 역할("aws-load-balancer-controller")이 표시되어야 합니다.

DaemonSet를 사용할 수 있는지 확인:

kubectl get daemonsets -n kube-system

예상되는 출력은 다음과 같아야 합니다.

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

클러스터에 모든 추가 기능이 설치되어 있는지 확인:

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

예상되는 출력은 다음과 같아야 합니다.

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. Karpenter 설치

CPU 워커 노드(cpu-worker)에 Karpenter 컨트롤러를 설치하여 비용을 최적화하고 GPU 리소스를 절약합니다. "kube-system" 네임스페이스에 설치하고 클러스터 생성 중에 정의한 "karpenter" 서비스 계정을 지정합니다. 추가로 이 명령을 사용하면 CPU 노드에 대한 클러스터 이름과 스팟 인스턴스 중단 대기열을 구성합니다. Karpenter는 IRSA를 사용하여 이 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

예상되는 출력은 다음과 같아야 합니다.

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

Karpenter가 실행 중인지 확인:

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

예상되는 출력은 다음과 같아야 합니다.

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

4. Karpenter NodePool 설정

이 단계에서는 상호 배타적인 CPU 및 GPU Karpenter NodePool을 구성합니다. NodePool 사양의 limits 필드는 각 NodePool이 프로비저닝된 모든 노드에서 사용할 수 있는 최대 총 리소스(예: CPU, 메모리, GPU)를 제한하고, 이러한 제한을 초과하면 추가 노드 프로비저닝이 방지됩니다. NodePool은 광범위한 인스턴스 범주(예: c, g)를 지원하지만 특정 인스턴스 유형, 용량 유형 및 리소스 제한을 지정하면 온디맨드 워크로드의 비용을 보다 쉽게 추정할 수 있습니다. 이러한 NodePool에서는 G5 인스턴스 패밀리 내에서 다양한 인스턴스 유형 세트를 사용합니다. 이를 통해 Karpenter는 포드 리소스 요청에 따라 가장 적합한 인스턴스 유형을 자동으로 선택하고, NodePool의 총 제한을 준수하면서 리소스 사용률을 최적화할 수 있습니다. 자세한 내용은 Creating NodePools를 참조하세요.

GPU NodePool 설정

이 NodePool에서는 리소스 제한을 설정하여 GPU 기능이 있는 노드의 프로비저닝을 관리합니다. 이러한 제한은 풀의 모든 노드에서 총 리소스를 제한하도록 설계되어 총 10개의 인스턴스를 허용합니다. 각 인스턴스는 총 vCPU가 80개를 초과하지 않고, 총 메모리가 320GiB를 초과하지 않으며, 총 GPU가 10개를 초과하지 않는 한 g5.xlarge(vCPU 4개, 16GiB 메모리, GPU 1개) 또는 g5.2xlarge(vCPU 8개, 32GiB 메모리, GPU 1개)일 수 있습니다. 예를 들어, 풀은 10개의 g5.2xlarge 인스턴스(vCPU 80개, 320GiB, GPU 10개), 10개의 g5.xlarge 인스턴스(vCPU 40개, 160GiB, GPU 10개) 또는 5개의 g5.xlarge 및 5개의 g5.2xlarge(vCPU 60개, 240GiB, GPU 10개) 조합을 프로비저닝하여 리소스 제약 조건을 준수하면서 워크로드 수요를 기반으로 유연하게 스케일링할 수 있습니다.

추가로 Bottlerocket AMI의 Nvidia 버전 ID를 지정합니다. 마지막으로 중단 정책을 설정하여 30분 후에 빈 노드를 제거(consolidateAfter: 30m)하고 최대 노드 수명을 30일로 설정(expireAfter: 720h)하여 GPU 집약적 작업의 비용을 최적화하고 노드 상태를 유지합니다. 자세한 내용은 Disable Karpenter Consolidation for interruption sensitive workloadsUse ttlSecondsAfterFinished to Auto Clean-Up Kubernetes Jobs를 참조하세요.

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

예상되는 출력은 다음과 같아야 합니다.

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

NodePool이 생성되고 정상 상태인지 확인:

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

ValidationSucceeded: True, NodeClassReady: TrueReady: True 등의 status.conditions를 찾아 NodePool이 정상인지 확인합니다.

CPU NodePool 설정

이 NodePool에서 중간 CPU 워크로드(예: 100~200개 포드) 및 일반 AWS vCPU 할당량(예: 128~1152)에 따라 약 50개의 인스턴스를 지원하도록 제한을 설정합니다. NodePool이 최대 50개의 m7.xlarge 인스턴스로 스케일 업된다고 가정하고 계산됩니다. CPU(인스턴스당 vCPU 4개 × 인스턴스 50개 = vCPU 200개) 및 메모리(인스턴스당 16GiB × 인스턴스 50개 = 800GiB). 이러한 제한은 풀의 모든 노드에서 총 리소스를 제한하도록 설계되어 총 vCPU가 200개를 초과하지 않고 총 메모리가 800GiB를 초과하지 않는 한 최대 50개의 m7g.xlarge 인스턴스(각각 vCPU 4개와 16GiB 메모리 포함)가 허용됩니다.

추가로 Bottlerocket AMI의 표준 버전 ID를 지정합니다. 마지막으로 중단 정책을 설정하여 60분 후에 빈 노드를 제거(consolidateAfter: 60m)하고 최대 노드 수명을 30일로 설정(expireAfter: 720h)하여 GPU 집약적 작업의 비용을 최적화하고 노드 상태를 유지합니다. 자세한 내용은 Disable Karpenter Consolidation for interruption sensitive workloadsUse ttlSecondsAfterFinished to Auto Clean-Up Kubernetes Jobs를 참조하세요.

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

예상되는 출력은 다음과 같아야 합니다.

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

NodePool이 생성되고 정상 상태인지 확인:

kubectl get nodepool cpu-inference-m7gxlarge -o yaml

ValidationSucceeded: True, NodeClassReady: TrueReady: True 등의 status.conditions를 찾아 NodePool이 정상인지 확인합니다.

5. GPU 포드를 배포하여 GPU 노출

Kubernetes가 GPU 디바이스를 Kubernetes 클러스터에 노출하려면 Nvidia 디바이스 플러그인이 필요합니다. 일반적으로 플러그인을 DaemonSet로 배포해야 하지만 Bottlerocket AMI는 플러그인을 AMI의 일부로 사전 설치합니다. 즉, Bottlerocket AMI를 사용할 때 Nvidia 디바이스 플러그인 DaemonSet를 배포할 필요가 없습니다. 자세한 내용은 Kubernetes Device Plugin to expose GPUs를 참조하세요.

샘플 포드 배포

Karpenter는 동적으로 작동하며, 워크로드(포드)가 GPU 리소스를 요청할 때 GPU 노드를 프로비저닝합니다. 포드가 GPU를 요청하고 사용할 수 있는지 확인하려면 해당 한도(예: nvidia.com/gpu: 1)에서 nvidia.com/gpu 리소스를 요청하는 포드를 배포합니다. 이러한 레이블에 대한 자세한 내용은 Schedule workloads with GPU requirements using Well-Known labels를 참조하세요.

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

예상되는 출력은 다음과 같아야 합니다.

pod/gpu-ndivia-smi created

1분 후 포드가 "Pending", "ContainerCreating", "Running", "Completed" 상태인지 확인합니다.

kubectl get pod gpu-nvidia-smi -w

포드의 노드가 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"

예상되는 출력은 다음과 같아야 합니다.

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

포드의 로그 확인:

kubectl logs gpu-nvidia-smi

예상되는 출력은 다음과 같아야 합니다.

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. (선택 사항) 모델 아티팩트 배포 준비 및 업로드

이 단계에서는 모델 가중치를 Amazon S3 버킷에 업로드하는 것부터 시작하여 실시간 이미지 분류를 위한 모델 서비스를 배포합니다. 시연을 위해 NVIDIA GPU 및 TensorRT를 사용하는 이미지에 대해 지연 시간이 짧은 추론을 지원하는 NVIDIA GPUNet의 오픈 소스 GPU GPUNet-0 비전 모델을 사용합니다. 이 모델은 ImageNet에서 사전 훈련되었고, 사진 또는 비디오 스트림의 객체를 즉시 분류할 수 있으며, 1,190만 개의 파라미터가 포함된 작은 모델로 간주됩니다.

환경을 설정합니다

GPUNet-0 모델 가중치를 다운로드하려면 이 단계에서 로컬 시스템에 설치된 NVIDIA의 NGC 카탈로그 및 Docker에 액세스해야 합니다. 단계에 따라 무료 계정 설정 및 NGC CLI 구성:

  • 무료 NGC 계정에 가입하고 NGC 대시보드(사용자 아이콘 > Setup > Generate API Key > Generate Personal Key > NGC Catalog)에서 API 키를 생성합니다.

  • NGC CLI(Linux/macOS/Windows)를 다운로드 및 설치하고 ngc config set를 사용하여 CLI를 구성합니다. 메시지가 표시되면 API 키를 입력하고, org를 nvidia로 설정하고 Enter 키를 눌러 다른 사람의 기본값을 수락합니다. 성공하면 다음과 같은 내용이 표시됩니다. Successfully saved NGC configuration to /Users/your-username/.ngc/config.

서비스 계정 권한 확인

시작하기 전에 Kubernetes 서비스 계정 권한을 확인하세요.

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

클러스터 생성 도중 S3CSIDriverPolicy를 IAM 역할에 연결하고 서비스 계정("s3-csi-driver-sa")에 주석을 달았습니다. Mountpoint S3 CSI 드라이버 포드는 S3와 상호 작용할 때 IAM 역할의 권한을 상속합니다. 예상되는 출력은 다음과 같아야 합니다.

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

허용치 추가

S3 CSI 드라이버는 모든 노드에서 DaemonSet로 실행됩니다. 포드는 이러한 노드에서 CSI 드라이버를 사용하여 S3 볼륨을 탑재합니다. 테인트가 있는 GPU 노드에 예약할 수 있도록 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"}}]'

예상되는 출력은 다음과 같아야 합니다.

daemonset.apps/s3-csi-node patched

S3에 모델 가중치 업로드

이 단계에서는 Amazon S3 버킷을 생성하고, NGC(NVIDIA GPU Cloud)에서 GPUNet-0 모델 가중치를 다운로드하고, 버킷에 업로드합니다. 이 가중치는 추론을 위해 런타임 시 애플리케이션에서 액세스합니다.

Amazon S3 버킷 생성:

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

실수로 인한 삭제 및 덮어쓰기로 인해 즉각적이면서 영구적으로 데이터가 손실되지 않도록 버킷에서 S3 버전 관리를 활성화합니다.

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

버킷에 수명 주기 규칙을 적용하여 최신 상태가 아닌 지 14일이 지난 덮어쓰거나 삭제된 객체 버전을 제거하고, 만료된 삭제 마커를 제거하고, 7일이 지나면 불완전한 멀티파트 업로드를 제거합니다. 자세한 내용은 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}}]}'

NGC에서 GPUNet-0 모델 가중치를 다운로드합니다. 예를 들어, macOS에서는 다음을 수행합니다.

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

운영 체제에 따라 다운로드 명령을 조정해야 할 수 있습니다. 이 명령이 Linux 시스템에서 작동하려면 명령의 일부분으로 디렉터리를 생성해야 할 수 있습니다(예: mkdir ~/downloads).

예상되는 출력은 다음과 같아야 합니다.

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

후속 단계에서 애플리케이션 코드에서 예상되는 이름과 일치하도록 체크포인트 파일의 이름을 변경합니다(모델 상태 사전이 포함된 표준 PyTorch *.pth.tar 체크포인트이므로 추출은 필요하지 않음).

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

AWS CLI에서 AWS 공통 런타임을 활성화하여 S3 처리량 최적화:

aws configure set s3.preferred_transfer_client crt

S3 버킷에 모델 가중치 업로드:

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

예상되는 출력은 다음과 같아야 합니다.

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

모델 서비스 생성

이 단계에서는 GPUNet-0 비전 모델을 사용하여 GPU 가속 이미지 분류를 위한 FastAPI 웹 애플리케이션을 설정합니다. 애플리케이션은 런타임 시 Amazon S3에서 모델 가중치를 다운로드하고, 캐싱을 위해 NVIDIA의 리포지토리에서 모델 아키텍처를 가져오고, HTTP를 통해 ImageNet 클래스 레이블을 다운로드합니다. 애플리케이션에는 이미지 사전 처리 변환이 포함되고 상태 확인을 위한 루트 GET과 이미지 URL을 수락하는 POST /predict 엔드포인트, 이상 두 개의 엔드포인트를 노출합니다.

PyTorch에서 FastAPI를 사용하여 모델을 제공하고, 빠른 프로토타이핑 및 Kubernetes 배포를 위해 컨테이너화된 설정으로 런타임 시 Amazon S3에서 가중치를 로드합니다. 최적화된 배치 또는 처리량이 높은 엔진 등 다른 방법은 Serving ML Models를 참조하세요.

애플리케이션 생성

model-testing과 같이 애플리케이션 파일의 디렉터리를 생성하고, 디렉터리를 변경하고 다음 코드를 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))

Dockerfile을 생성합니다

다음 Dockerfile은 Tensor 코어용 NVIDIA Deep Learning 예시 GitHub 리포지토리에 있는 GPUNet 모델을 활용하여 애플리케이션용 컨테이너 이미지를 생성합니다.

런타임 전용 PyTorch 베이스를 사용하고, 캐시 정리가 포함된 필수 패키지만 설치하고, 모델 코드를 사전 캐싱하고, 더 빠른 풀 및 업데이트를 위해 컨테이너 이미지에 "베이킹" 가중치를 배제함으로써 컨테이너 이미지 크기를 줄입니다. 자세한 내용은 Reducing Container Image Sizes를 참조하세요.

app.py와 동일한 디렉터리에서 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"]

애플리케이션 테스트

app.pyDockerfile과 동일한 디렉터리에서 AMD64 아키텍처를 대상으로 하는 추론 애플리케이션의 컨테이너 이미지 빌드:

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

AWS 자격 증명에 대한 환경 변수를 설정하고, 선택적으로 AWS 세션 토큰을 설정합니다. 예:

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

로컬에서 컨테이너를 실행하여 AWS 자격 증명을 S3 액세스의 환경 변수로 주입합니다. 예:

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

예상되는 출력은 다음과 같아야 합니다.

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)

새 터미널 창에서 퍼블릭 이미지 URL을 쿼리 파라미터로 사용하여 샘플 POST 요청을 전송하면서 추론 엔드포인트를 테스트합니다.

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

예상되는 출력은 이와 유사한 상위 5개 예측이 포함된 JSON 응답이어야 합니다(실제 레이블 및 확률은 이미지 및 모델 정밀도에 따라 약간 다를 수 있음).

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

"Ctrl + C"를 사용하여 애플리케이션을 종료합니다.

Amazon ECR로 컨테이너 푸시

이 단계에서는 Amazon EKS에서 배포할 수 있도록 GPUNet-0 모델 서비스의 컨테이너 이미지를 Amazon ECR(Elastic Container Registry)에 업로드합니다. 이 프로세스에는 이미지를 저장할 새 ECR 리포지토리 생성, ECR 인증, 컨테이너 이미지 태그 지정 및 레지스트리로의 푸시 과정이 포함됩니다.

먼저 이 가이드의 시작 부분에서 환경 변수를 설정한 디렉터리로 돌아갑니다. 예:

cd ..

Amazon ECR에서 리포지토리 생성:

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

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

예상되는 출력은 다음과 같아야 합니다.

Login Succeeded

이미지 태그 지정:

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

이미지를 Amazon ECR 리포지토리에 푸시:

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

이 마지막 단계가 완료되는 데 몇 분 정도 걸릴 수 있습니다.

7. (선택 사항) 모델 서비스 노출

이 단계에서는 AWS LBC(Load Balancer Controller)를 사용하여 Amazon EKS 외부에 실시간 추론 모델 서비스를 노출합니다. 여기에는 LBC 설정, Mountpoint S3 CSI 드라이버를 사용하여 영구 볼륨으로 Amazon S3의 모델 가중치 탑재, GPU 가속 애플리케이션 포드 배포, ALB(Application Load Balancer)를 프로비저닝하기 위한 서비스 및 수신 생성, 엔드포인트 테스트가 포함됩니다.

먼저 AWS LBC에 대한 Pod Identity 연결을 확인하여 서비스 계정이 필요한 IAM 역할에 올바르게 연결되어 있는지 확인합니다.

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

예상되는 출력은 다음과 같아야 합니다.

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

클러스터 보안 그룹 태그 지정

AWS Load Balancer Controller는 Karpenter의 보안 그룹 선택에 있어 태그 키 karpenter.sh/discovery: "${EKS_CLUSTER_NAME}"이 있는 단일 보안 그룹만 지원합니다. eksctl을 사용하여 클러스터를 생성할 때 기본 클러스터 보안 그룹("kubernetes.io/cluster/<cluster-name>: owned" 태그 있음)에는 karpenter.sh/discovery 태그가 자동으로 지정되지 않습니다. 이 태그는 Karpenter가 이 보안 그룹을 검색하고 프로비저닝하는 노드에 연결하는 데 있어 필수적입니다. 이 보안 그룹을 연결하면 AWS LBC(Load Balancer Controller)와의 호환성이 보장되어 이러한 단계의 모델 서비스와 같이 Ingress를 통해 노출된 서비스의 인바운드 트래픽 규칙을 자동으로 관리할 수 있습니다.

클러스터의 VPC ID 내보내기:

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

클러스터의 기본 보안 그룹 내보내기:

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

기본 클러스터 보안 그룹에 karpenter.sh/discovery 태그를 추가합니다. 이렇게 하면 CPU 및 GPU EC2NodeClass 선택기에서 다음을 사용할 수 있습니다.

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

태그가 추가되었는지 확인:

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

결과 중에서 태그 및 클러스터 이름과 함께 다음이 표시될 것입니다. 예:

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

AWS LBC(Load Balancer Controller) 설정

AWS LBC는 Amazon EKS에서 AI/ML 워크로드로의 수신 트래픽을 관리하는 데 필수적으로, 추론 엔드포인트 또는 데이터 처리 파이프라인에 대한 액세스를 보장합니다. LBC는 AWS ALB(Application Load Balancer) 및 NLB(Network Load Balancer)와 통합하여 대규모 언어 모델, 컴퓨터 비전 모델 또는 실시간 추론 서비스를 실행하는 애플리케이션과 같이 컨테이너화된 애플리케이션으로 트래픽을 동적으로 라우팅합니다. 클러스터 생성 도중 서비스 계정과 Pod Identity 연결을 이미 생성했으므로 클러스터 구성에 정의된 것(aws-load-balancer-controller)과 일치하도록 serviceAccount.name을 설정합니다.

AWS 소유 eks-charts 헬름 차트 리포지토리 추가:

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

최신 차트로 로컬 Helm 리포지토리 새로 고치기:

helm repo update eks

Helm을 사용하여 AWS LBC를 배포하고, EKS 클러스터 이름을 지정하고 사전 생성된 서비스 계정을 참조합니다.

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

예상되는 출력은 다음과 같아야 합니다.

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!

영구 볼륨에 모델 탑재

이 단계에서는 Mountpoint for Amazon S3 CSI 드라이버 기반의 PV(PersistentVolume)를 사용하여 Amazon S3 버킷에서 모델 가중치를 탑재합니다. 이를 통해 Kubernetes 포드가 S3 객체에 로컬 파일로 액세스하여 리소스 집약적 다운로드를 임시 포드 스토리지 또는 초기 컨테이너로 제거할 수 있으며, 이는 대용량 멀티기가바이트 모델 가중치에 적합합니다.

PV는 전체 버킷 루트(volumeAttributes에 지정된 경로 없음)를 탑재하고, 여러 포드의 동시 읽기 전용 액세스를 지원하고, 추론에 대해 컨테이너 내부에 모델 가중치(/models/gpunet-0.pth)와 같은 파일을 노출합니다. 파일이 탑재를 통해 존재하기 때문에 이를 통해 애플리케이션(app.py)의 폴백 "다운로드"가 트리거되지 않습니다. 컨테이너 이미지에서 모델을 분리하면 이미지 재빌드 없이 공유 액세스 및 독립적 모델 버전 업데이트가 가능합니다.

PV(PersistentVolume) 생성

PV(PersistentVolume) 리소스를 생성하여 모델 가중치가 포함된 S3 버킷을 탑재하면 런타임 시 파일을 다운로드하지 않고도 여러 포드에 대한 읽기 전용 액세스를 활성화할 수 있습니다.

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

PVC(PersistentVolumeClaim) 생성

PV에 바인딩할 PVC(PersistentVolumeClaim)를 생성하고, 탑재된 S3 모델 데이터에 대한 읽기 전용 액세스를 요청합니다.

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

애플리케이션 배포

추론 애플리케이션을 Kubernetes 배포로 배포하여 모델 액세스를 위해 S3 기반 영구 볼륨을 탑재하고, GPU 노드 선택기 및 허용치를 적용하고, 모델 경로에 대한 환경 변수를 설정합니다. 이 배포는 모델 경로("/models/gpunet-0.pth"의 환경 변수)를 설정하므로 (app.py의) 애플리케이션은 기본적으로 이 경로를 사용합니다. 배포의 볼륨이 /models(읽기 전용)에서 탑재된 상태에서 파일이 이미 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

아직 GPU 노드를 사용할 수 없는 경우 Karpenter가 GPU 노드를 프로비저닝하는 데 몇 분 정도 걸립니다. 추론 포드가 “Running” 상태인지 확인:

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

예상되는 출력은 다음과 같아야 합니다.

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

Ingress 및 로드 밸런서를 사용하여 서비스 노출

ClusterIP Service를 생성하여 EKS 클러스터 내에서 내부적으로 추론 배포를 노출하고, 애플리케이션의 포트를 대상으로 합니다.

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

Ingress 리소스를 생성하여 AWS LBC를 통해 인터넷 경계 ALB(Application Load Balancer) 프로비저닝하고, 외부 트래픽을 추론 서비스로 라우팅합니다.

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

ALB(Application Load Balancer)가 프로비저닝을 완료할 때까지 몇 분 정도 기다립니다. Ingress 리소스 상태를 모니터링하여 ALB가 프로비저닝되었는지 확인:

kubectl get ingress gpunet-model-ingress

예상 출력은 다음과 같아야 합니다(ADDRESS 필드가 채워짐).

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

후속 테스트에 사용할 Ingress 상태에서 ALB 호스트 이름 추출 및 내보내기:

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

모델 서비스 테스트

(예를 들어 COCO 데이터세트에서) 샘플 이미지 URL로 POST 요청을 전송하여 노출된 추론 엔드포인트를 검증하고 실시간 예측을 시뮬레이션합니다.

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

예상되는 출력은 이와 유사한 상위 5개 예측이 포함된 JSON 응답이어야 합니다(실제 레이블 및 확률은 이미지 및 모델 정밀도에 따라 약간 다를 수 있음).

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

새 POST 요청에서 다른 이미지를 계속 테스트할 수도 있습니다. 예:

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

결론

이 가이드에서는 GPU 가속 실시간 추론 워크로드에 최적화된 Amazon EKS 클러스터를 설정합니다. G5 EC2 인스턴스로 클러스터를 프로비저닝하고, Mountpoint S3 CSI 드라이버, EKS Pod Identity Agent, EKS 노드 모니터링 에이전트, Bottlerocket AMI, AWS LBC(Load Balancer Controller)Karpenter를 설치하여 CPU 및 GPU NodePool을 관리했습니다. NVIDIA 디바이스 플러그인을 사용하여 GPU 예약을 활성화하고 모델 액세스를 위해 PersistentVolume 및 PersistentVolumeClaim을 사용하여 S3를 구성했습니다. 샘플 GPU 포드를 배포하고, Amazon S3에서 NVIDIA GPUNet-0 모델에 대한 모델 액세스를 설정하고, 포드 초기화를 활성화하고, Application Load Balancer를 통해 추론 서비스를 노출하여 설정을 검증했습니다. 클러스터를 완전히 활용하려면 자동 복구를 사용하여 EKS 노드 모니터링 에이전트를 구성합니다. 응답 시간 최적화를 위해 GPU 성능, 지연 시간 및 처리량 평가 등의 벤치마크 테스트를 실시해야 합니다. 자세한 내용은 Using Monitoring and Observability Tools for your AI/ML Workloads를 참조하세요.

정리

향후 요금이 발생하지 않도록 연결된 CloudFormation 스택을 수동으로 삭제하고 VPC 네트워크를 포함한 이 가이드를 통해 생성된 모든 리소스를 삭제해야 합니다.

eksctl에서 --wait 플래그를 사용하여 CloudFormation 스택 삭제:

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

완료되면 다음 응답 출력이 표시됩니다.

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

Amazon S3 콘솔을 사용하여이 가이드에서 생성된 Amazon S3 버킷을 삭제합니다.