View a markdown version of this page

Terraform을 사용하여 AI/ML 워크로드용 Amazon EKS 클러스터 설정 - Amazon EKS

이 페이지 개선에 도움 주기

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

Terraform을 사용하여 AI/ML 워크로드용 Amazon EKS 클러스터 설정

작은 정보

향후 예정된 Amazon EKS AI/ML 워크숍에 등록합니다.

이 섹션에서는 Terraform을 사용하여 Amazon EKS에서 훈련 또는 추론 워크로드를 실행하는 데 필요한 인프라를 생성하는 단계를 안내합니다. 이 단계에는 EKS 클러스터 생성, EKS 자율 모드 또는 Karpenter를 사용하는 GPU 지원 노드, Prometheus 및 Grafana를 사용하는 모니터링 스택, 모델 가중치를 위한 Amazon S3 스토리지가 포함됩니다.

이러한 기능이 EKS 클러스터에서 EC2 인스턴스를 프로비저닝하고 자동으로 규모를 조정하는 방법에 대한 자세한 내용은 EKS 자율 모드Karpenter 설명서를 참조하세요.

상위 수준 아키텍처 및 워크플로

<shared id=를 보여주는 상위 수준 아키텍처

다이어그램은 이 섹션의 설정에 대한 전체적인 AWS 아키텍처를 보여줍니다.

사전 조건

중요

EKS 클러스터, GPU 인스턴스, Application Load Balancer, Amazon Managed Service for Prometheus를 포함하여 이 자습서에서 생성하는 리소스에는 요금이 부과됩니다. 지속적으로 요금이 부과되는 것을 방지하려면 완료 후 리소스를 삭제하세요.

도구 버전 확인:

terraform --version aws --version kubectl version --client jq --version

1단계: Terraform 코드 다운로드 및 배포

이 연습에서는 sample-eks-docs AWS Samples GitHub 리포지토리의 Terraform 코드를 사용합니다. 리포지토리를 작업 디렉터리에 복제합니다.

git clone git@github.com:aws-samples/sample-eks-docs.git cd sample-eks-docs/ai-ml/set-up-cluster

리포지토리는 방금 이동한 ai-ml/set-up-cluster/ 디렉터리 아래에 다음과 같은 구조를 가지고 있습니다.

set-up-cluster/
├── scripts/
│   └── cleanup.sh
└── terraform/
    ├── auto-mode/
    └── karpenter/

리포지토리는 두 개의 배포 경로를 제공합니다. 가이드 전체에서 하나만 선택하여 사용하세요.

  • EKS Auto Mode(terraform/auto-mode/) - EKS Auto Mode는 핵심 네트워킹, 스토리지, 로드 밸런싱 추가 기능 외에도 훈련 및 추론 워크로드를 위한 다음 기능을 포함하고 관리합니다. EKS 노드 모니터링 에이전트, 자동 노드 복구, 빠른 컨테이너 풀을 위한 SOCI 스냅샷터, 기본 NodeClass의 GPU 준비 상태. NVIDIA 디바이스 플러그인은 EKS 자율 모드가 GPU 지원 노드에 사용하는 Bottlerocket 가속 AMI에 포함되어 있습니다.

  • 자체 관리형 Karpenter(terraform/karpenter/) - EKS Auto Mode가 없는 EKS 클러스터에서는 Terraform 코드가 훈련 및 추론 워크로드에 필요한 구성 요소를 설치하고 구성합니다. 여기에는 네트워킹 추가 기능(VPC CNI, CoreDNS, kube-proxy), Karpenter, EKS 노드 모니터링 에이전트, NVIDIA 디바이스 플러그인 및 빠른 컨테이너 풀을 위한 SOCI 스냅샷터가 포함됩니다.

중요

EKS Auto Mode 또는 자체 관리형 Karpenter를 선택하고 가이드 전체에서 사용합니다. 중간에 전환하려면 클러스터를 삭제하고 다시 시작해야 합니다.

EKS 클러스터 옵션: EKS 자율 모드와 자체 관리형 Karpenter

두 가지 클러스터 옵션, 즉 NodePool이 있는 EKS 자율 모드 클러스터와 자체 관리형 Karpenter, CoreDNS, VPC CNI, NVIDIA 디바이스 플러그인, EKS Pod Identity 에이전트, 노드 모니터링 에이전트, kube-proxy, NodeClass 및 NodePool이 있는 EKS 표준 클러스터를 나란히 비교
Grafana는 기본 자격 증명을 사용하여 HTTP를 통해 공개적으로 액세스할 수 있음

Grafana ALB Ingressvar.my_cidr의 기본값을 0.0.0.0/0으로 설정합니다. 이로 인해 Grafana는 기본 관리자 자격 증명을 사용하여 일반 HTTP를 통해 퍼블릭 인터넷에 노출됩니다. 자동 스캐너는 몇 분 안에 퍼블릭 로드 밸런서를 검색합니다. 반드시 자체 IP 주소로 var.my_cidr을 재정의하여 액세스를 제한해야 합니다.

export MY_CIDR="$(curl -s https://checkip.amazonaws.com)/32" terraform apply -var "my_cidr=${MY_CIDR}"

소스 IP 허용 목록을 완전한 보호 수단이 아닌 최소한의 보호 수단으로 취급하세요. 또한 처음 로그인한 후 기본 Grafana 관리자 암호를 변경해야 합니다. 더 강력한 보안 태세를 위해 alb.ingress.kubernetes.io/schemeinternal(VPC 또는 연결된 VPN 내에서만 연결 가능)로 변경하고 TLS 인증서를 추가하세요.

클러스터 배포

선택한 경로의 디렉터리로 이동하여 Terraform을 초기화한 후 다음을 적용합니다.

두 변형 모두 기본적으로 us-east-2 리전으로 설정됩니다. 다른 리전에 배포하려면 다음 단계의 terraform apply 명령에 -var "region=region-code"을 추가합니다. 여기서 region-code는 배포하려는 AWS 리전입니다.

Terraform 코드는 대상 리전의 사용 가능한 가용 영역을 모두 사용하지만, Amazon EKS는 use1-az3, usw1-az2cac1-az3 가용 영역에서 컨트롤 플레인 배치를 지원하지 않으므로 해당 영역은 제외됩니다.

EKS Auto Mode
cd terraform/auto-mode terraform init terraform apply

이 명령은 완료까지 몇 분 정도 걸립니다.

Self-managed Karpenter
cd terraform/karpenter terraform init terraform apply

이 명령은 15분 정도 소요됩니다. 이 명령은 추가 기능 및 Karpenter 컨트롤러를 호스팅하는 전용 관리형 노드 그룹이 있는 EKS 클러스터를 생성합니다. Terraform은 스팟 중단 대기열이 활성화되고 NodeRepairStaticCapacity 기능 게이트가 켜진 상태로 Karpenter를 설치합니다. 또한 NVIDIA 디바이스 플러그인, AWS Load Balancer Controller, 모니터링 스택을 설치합니다.

Terraform 출력 검토

적용이 완료되면 Terraform은 다음 출력을 인쇄합니다(값은 구성에 따라 다름).

Apply complete! Resources: 74 added, 0 changed, 0 destroyed.

Outputs:

cluster_name         = "ai-eks-docs"
configure_kubectl    = "aws eks update-kubeconfig --region us-east-2 --name ai-eks-docs --alias ai-eks-docs"
configure_model_bucket = "export MODEL_BUCKET=ai-eks-docs-models-20250612abc1"
model_bucket         = "ai-eks-docs-models-20250612abc1"
node_iam_role_name   = "ai-eks-docs-eks-auto-20250612..."
region               = "us-east-2"

configure_kubectl 출력은 kubectl이 해당 클러스터를 가리키도록 하는 실행 가능한 명령입니다. model_bucket 출력에는 모델 가중치를 위한 S3 버킷 이름이 포함됩니다. node_iam_role_name 출력에는 노드가 사용하는 IAM 역할이 표시됩니다.

kubectl 구성

kubectl이 새 클러스터를 가리키도록 합니다. configure_kubectl 출력은 실행 가능한 명령입니다.

eval "$(terraform output -raw configure_kubectl)"

클러스터 확인

EKS Auto Mode
kubectl get pods --all-namespaces

예상 결과:

NAMESPACE NAME READY STATUS RESTARTS AGE kube-system metrics-server-5db89f9ffd-h4mlr 1/1 Running 0 3m kube-system metrics-server-5db89f9ffd-nd748 1/1 Running 0 3m monitoring kube-prometheus-stack-grafana-ff9b5fd57-dh562 3/3 Running 0 3m monitoring kube-prometheus-stack-kube-state-metrics-5dcbfdf69b-wd6qn 1/1 Running 0 3m monitoring kube-prometheus-stack-operator-548c4f4485-m5wh2 1/1 Running 0 3m monitoring kube-prometheus-stack-prometheus-node-exporter-p2z7l 1/1 Running 0 3m monitoring prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0 3m

EKS 자율 모드에서 VPC CNI, kube-proxy, CoreDNS는 관리형 구성 요소로 실행되며 kube-system에 포드로 표시되지 않습니다.

Self-managed Karpenter
kubectl get pods --all-namespaces

예상 출력에는 Karpenter, CoreDNS, kube-proxy, aws-node(VPC CNI), EKS Pod Identity 에이전트, EKS 노드 모니터링 에이전트, NVIDIA 디바이스 플러그인이 포함됩니다.

NAMESPACE NAME READY STATUS RESTARTS AGE kube-system aws-node-bzdcz 2/2 Running 0 5m kube-system aws-node-vkbhb 2/2 Running 0 5m kube-system coredns-7dbb8998cf-9b9wk 1/1 Running 0 5m kube-system coredns-7dbb8998cf-pwtjd 1/1 Running 0 5m kube-system ebs-csi-controller-748f54b69-8h7jv 6/6 Running 0 5m kube-system ebs-csi-controller-748f54b69-v2mv8 6/6 Running 0 5m kube-system eks-node-monitoring-agent-5qw4l 1/1 Running 0 5m kube-system eks-node-monitoring-agent-7lrtm 1/1 Running 0 5m kube-system eks-pod-identity-agent-ddvlv 1/1 Running 0 5m kube-system eks-pod-identity-agent-q4g29 1/1 Running 0 5m kube-system karpenter-898ff78-cndbd 1/1 Running 0 5m kube-system karpenter-898ff78-hfbhn 1/1 Running 0 5m kube-system kube-proxy-gcfnh 1/1 Running 0 5m kube-system kube-proxy-tktcf 1/1 Running 0 5m kube-system metrics-server-5b789db597-cm9qd 1/1 Running 0 5m kube-system metrics-server-5b789db597-w57b6 1/1 Running 0 5m kube-system nvidia-device-plugin-node-feature-discovery-gc-66cb7f5dc-xvftc 1/1 Running 0 5m kube-system nvidia-device-plugin-node-feature-discovery-master-854d6b5hnqtp 1/1 Running 0 5m monitoring kube-prometheus-stack-grafana-6797bcb59f-2zlwg 3/3 Running 0 5m monitoring kube-prometheus-stack-kube-state-metrics-8446bd549c-q6gjc 1/1 Running 0 5m monitoring kube-prometheus-stack-operator-5f499b784-vf5xb 1/1 Running 0 5m monitoring kube-prometheus-stack-prometheus-node-exporter-4c6wq 1/1 Running 0 5m monitoring kube-prometheus-stack-prometheus-node-exporter-l5t25 1/1 Running 0 5m monitoring prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0 5m

NVIDIA 디바이스 플러그인이 설치되어 있는지 확인합니다. 일치하는 amiFamily=al2023 레이블이 있는 GPU NodePool이 프로비저닝될 때까지 제로 디바이스 플러그인 포드는 나타나지 않습니다.

kubectl get daemonset nvidia-device-plugin -n kube-system

예상 결과:

NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE nvidia-device-plugin 0 0 0 0 0 amiFamily=al2023 5m

2단계: 동적 GPU NodePool 생성

GPU NodePool은 옵트인 방식입니다. 기본적으로 terraform apply는 GPU 용량 및 GPU 청구 없이 클러스터 및 모니터링 스택을 생성합니다. GPU 노드를 프로비저닝하려면 nodepools 변수에 전략 이름을 전달합니다.

온디맨드를 폴백으로 사용하는 스팟 용량을 사용하여 4세대보다 상위의 G 패밀리 GPU 인스턴스를 프로비저닝하는 spot-ondemand 전략을 활성화합니다.

terraform apply -var 'nodepools={"spot-ondemand"={}}'

이 명령은 nodepools/spot-ondemand/ 디렉터리의 NodePool 및 NodeClass 템플릿을 적용합니다. 두 경로 모두 동일한 NodePool API를 사용하지만 NodePool이 참조하는 NodeClass에서 차이가 있습니다.

EKS Auto Mode

NodePool은 이미 Bottlerocket 가속 AMI, NVIDIA 드라이버, NVIDIA 디바이스 플러그인, SOCI 병렬 풀을 선택하는 관리형 default NodeClass를 참조합니다. spot-ondemand 전략은 이 경로에서 자체 NodeClass를 제공하지 않습니다.

NodePool을 검증합니다.

kubectl get nodepools,nodeclasses

예상 결과. gpu-inf NodePool은 내장 general-purposesystem NodePool을 조인하며 셋 모두 관리형 default NodeClass를 참조합니다.

NAME NODECLASS NODES READY AGE nodepool.karpenter.sh/general-purpose default 0 True 12m nodepool.karpenter.sh/gpu-inf default 0 True 20s nodepool.karpenter.sh/system default 1 True 12m NAME ROLE READY AGE nodeclass.eks.amazonaws.com/default ai-eks-docs-eks-auto-... True 12m
Self-managed Karpenter

Terraform은 NodePool과 함께 사용자 지정 gpu-inf EC2NodeClass를 적용합니다. EC2NodeClass는 EKS 최적화 AL2023 AMI 별칭을 고정하고, FastImagePull 기능 게이트를 통해 SOCI를 활성화하고, Containered 이미지 캐시를 로컬 NVMe로 이동하도록 instanceStorePolicy: RAID0을 설정합니다.

NodePool 및 EC2NodeClass를 검증합니다.

kubectl get nodepools,ec2nodeclasses

예상 결과. gpu-inf 쌍은 Terraform이 비GPU 워크로드에 대해 생성하는 general-purpose NodePool 및 EC2NodeClass를 조인합니다.

NAME NODECLASS NODES READY AGE nodepool.karpenter.sh/general-purpose general-purpose 1 True 14m nodepool.karpenter.sh/gpu-inf gpu-inf 0 True 25s NAME READY AGE ec2nodeclass.karpenter.k8s.aws/general-purpose True 14m ec2nodeclass.karpenter.k8s.aws/gpu-inf True 25s

두 경로 모두 GPU 워크로드가 예약될 때까지 gpu-inf0 노드를 표시합니다. EKS Auto Mode 및 Karpenter는 보류 중인 포드에 필요한 경우에만 노드를 시작합니다.

3단계: 샘플 포드로 테스트

nvidia-smi 포드를 사용하여 GPU NodePool 설정을 테스트합니다.

cat << EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: nvidia-smi labels: guide: ai-eks-docs spec: restartPolicy: OnFailure tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" containers: - name: nvidia-smi image: public.ecr.aws/amazonlinux/amazonlinux:2023-minimal command: ["nvidia-smi"] resources: limits: nvidia.com/gpu: 1 EOF

포드가 예약되고 성공적으로 완료되었는지 확인합니다.

kubectl get pods nvidia-smi

예상 결과:

NAME         READY   STATUS      RESTARTS   AGE
nvidia-smi   0/1     Completed   0          67s

STATUS: Completednvidia-smi 명령이 실행되고 종료되었음을 의미합니다. 포드 로그를 확인하여 노드에서 감지된 GPU를 확인합니다.

kubectl logs nvidia-smi

예상 결과:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.159.03             Driver Version: 580.159.03     CUDA Version: 13.0     |
+-----------------------------------------+------------------------+----------------------+
| 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 L4                      On  |   00000000:31:00.0 Off |                    0 |
| N/A   41C    P8             13W /   72W |       0MiB /  23034MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

출력에는 GPU 모델, 드라이버 버전, CUDA 버전, 사용 가능한 메모리가 표시됩니다. 이 예제에서 Karpenter는 메모리가 24GB인 NVIDIA L4 GPU가 있는 G6 인스턴스를 프로비저닝했습니다. GPU 모델 및 메모리는 Karpenter가 선택하는 인스턴스 유형에 따라 달라집니다. G5 인스턴스에는 NVIDIA A10G GPU(24GB)가 있고, G6 인스턴스에는 NVIDIA L4 GPU(24GB)가 있고, G6e 인스턴스에는 NVIDIA L40S GPU(48GB)가 있습니다.

노드를 프로비저닝하고 포드를 배치하기 위해 Karpenter와 Kubernetes 스케줄러가 어떻게 조정되는지 이해하려면 포드의 수명 주기 이벤트를 확인하세요.

kubectl describe pod nvidia-smi

예상 결과:

Events:
  Type     Reason            Age   From                   Message
  ----     ------            ----  ----                   -------
  Warning  FailedScheduling  75s   default-scheduler      0/2 nodes are available: 2 node(s) had untolerated taint(s).
  Normal   Nominated         74s   eks-auto-mode/compute  Pod should schedule on: nodeclaim/gpu-inf-z6q75
  Normal   Scheduled         35s   default-scheduler      Successfully assigned default/nvidia-smi to i-0eb897a8302551589
  Normal   Pulling           27s   kubelet                spec.containers{nvidia-smi}: Pulling image "public.ecr.aws/amazonlinux/amazonlinux:2023-minimal"
  Normal   Pulled            22s   kubelet                spec.containers{nvidia-smi}: Successfully pulled image "public.ecr.aws/amazonlinux/amazonlinux:2023-minimal" in 5.625s (5.626s including waiting). Image size: 37440620 bytes.
  Normal   Created           22s   kubelet                spec.containers{nvidia-smi}: Container created
  Normal   Started           21s   kubelet                spec.containers{nvidia-smi}: Container started

이러한 이벤트는 포드 예약 시퀀스를 보여줍니다. 처음에 포드가 GPU 노드가 없어 예약에 실패하고(FailedScheduling), Karpenter가 새 NodeClaim을 지명하고(Nominated), 노드가 준비되면 스케줄러가 포드를 할당한 다음(Scheduled) 컨테이너 이미지를 가져와 시작합니다. EKS Auto Mode는 기본적으로 G, P 및 Trn 인스턴스에 SOCI(Seekable OCI) 병렬 풀이 설치 및 구성되어 있으며 자체 관리형 Karpenter 경로는 FastImagePull 기능 게이트를 통해 이를 명시적으로 구성합니다.

참고

자체 관리형 Karpenter 클러스터에서 Nominated 이벤트는 eks-auto-mode/compute 대신 karpenter/compute를 표시합니다.

NodeClaim은 Karpenter가 특정 노드를 프로비저닝하기 위해 생성하는 요청입니다. 이 요청은 인스턴스 유형, 용량 유형, AZ, 노드 준비 여부를 보여줍니다.

kubectl get nodeclaims

예상 결과:

NAME            TYPE        CAPACITY   ZONE         NODE                  READY   AGE
gpu-inf-z6q75   g6.xlarge   spot       us-east-2a   i-0eb897a8302551589   True    5m

인스턴스 유형 및 AZ는 다양합니다. 4세대보다 상위의 G 패밀리 인스턴스는 모두 사용할 수 있습니다.

작은 정보

노드가 표시되지 않으면 용량 부족 오류를 확인합니다.

kubectl get events | grep InsufficientCapacityError

Karpenter는 사용할 수 없는 오퍼링을 3분 동안 캐싱합니다. NodePool에서 허용되는 인스턴스 유형과 AZ를 확장하면 랜딩 용량이 늘어납니다.

참고

Karpenter가 시작한 스팟 인스턴스는 EC2 스팟 요청 콘솔에 표시되지 않습니다. Karpenter는 type: instant와 함께 EC2 CreateFleet API를 사용합니다. 인스턴스는 spot 수명 주기와 함께 EC2 인스턴스 콘솔에 표시됩니다.

4단계: NodePool에 예약 용량 추가(선택 사항)

2단계의 GPU NodePool은 스팟 또는 온디맨드 인스턴스를 동적으로 프로비저닝하지만 일부 사용 사례에서는 보장된 용량이 필요합니다. 필요할 때 GPU 용량을 사용할 수 있도록 온디맨드 용량 예약(ODCR)을 생성할 수 있습니다.

Terraform을 사용하면 단일 명령으로 태그별로 예약을 참조하는 사용자 지정 NodeClass인 ODCR을 생성하고, reserved를 용량 유형으로 포함하도록 NodePool을 업데이트합니다. Terraform은 ODCR에 nodepool=reserved-spot-ondemand 태그를 지정하고 NodeClass는 해당 태그로 ODCR을 선택합니다.

주의

다음 명령은 ODCR을 생성합니다. ODCR은 노드 실행 여부에 관계없이 즉시 요금 청구를 시작하고 terraform destroy 또는 정리 스크립트를 사용하여 폐기할 때까지 청구를 계속합니다.

기본값을 사용합니다(g6e.4xlarge, 인스턴스 1개, 첫 번째 클러스터 AZ).

terraform apply -var 'nodepools={"reserved-spot-ondemand"={reservation={}}}'

인스턴스 유형, 개수 및 AZ를 선택합니다.

terraform apply -var 'nodepools={"reserved-spot-ondemand"={reservation={instance_type="g6e.2xlarge",instance_count=1,az="us-east-2a"}}}'

reservation 객체는 다음 필드를 지원합니다.

  • instance_type - 예약할 GPU 인스턴스 유형입니다. 기본값: g6e.4xlarge.

  • instance_count - 예약할 인스턴스 수입니다. 기본값: 1.

  • az - 예약의 가용 영역입니다. 기본값: ""(첫 번째 클러스터 AZ 사용).

중요

spot-ondemandreserved-spot-ondemand 전략은 함께 사용할 수 없습니다. nodepools 변수에서 최대 1개를 활성화할 수 있습니다. 이전에 2단계에서 spot-ondemand를 사용한 경우, 둘 다 동일한 gpu-inf NodePool을 관리하기 때문에 reserved-spot-ondemand 명령이 이를 대체합니다.

InsufficientInstanceCapacity 오류가 발생하면 지정된 AZ에서 예약을 이행할 수 없습니다. Terraform 작업을 취소(Ctrl+C)한 다음 다른 az 값으로 다시 실행합니다.

terraform apply -var 'nodepools={"reserved-spot-ondemand"={reservation={instance_type="g6e.4xlarge",az="us-east-2b"}}}'

적용 후 Terraform은 용량 유형 요구 사항에 reserved, spot, on-demand를 포함하도록 NodePool을 업데이트합니다. Karpenter는 reserved를 가장 비용 효율적인 옵션으로 취급하고 먼저 시작합니다. 예약이 가득 차면 스팟 또는 온디맨드로 대체합니다.

EKS Auto Mode 경로에서 Terraform은 capacityReservationSelectorTerms을 통해 태그별로 ODCR을 참조하는 사용자 지정 gpu-inf NodeClass(번들 default NodeClass가 읽기 전용이기 때문)를 생성합니다. 자체 관리형 Karpenter 경로에서 Terraform은 capacityReservationSelectorTerms가 추가된 gpu-inf EC2NodeClass를 다시 적용하고 reserved를 포함하도록 NodePool을 업데이트합니다.

ODCR이 생성되었는지 확인합니다.

aws ec2 describe-capacity-reservations \ --filters "Name=state,Values=active" "Name=tag:nodepool,Values=reserved-spot-ondemand" \ --query 'CapacityReservations[0].{Id:CapacityReservationId,State:State,InstanceType:InstanceType,AvailableCount:AvailableInstanceCount}' \ --output table \ --region $(terraform output -raw region)

NodeClass가 ODCR을 참조하는지 확인합니다.

EKS Auto Mode
kubectl get nodeclasses gpu-inf -o yaml | grep 'id: cr-.*'

예상 결과:

id: cr-xxxxxxxxxxxxxxxxx
Self-managed Karpenter
kubectl get ec2nodeclasses gpu-inf -o yaml | grep 'id: cr-.*'

예상 결과:

id: cr-xxxxxxxxxxxxxxxxx

NodePool이 준비되었는지 확인합니다.

kubectl get nodepools gpu-inf

예상 결과:

NAME      NODECLASS   NODES   READY   AGE
gpu-inf   gpu-inf     0       True    30s

변경 사항을 적용한 후 Karpenter가 예약 용량의 우선 순위를 지정하고 스팟 또는 온디맨드로 대체하는지 확인합니다. 포드당 GPU 1개를 요청하는 2-복제본 배포를 배포합니다. ODCR은 인스턴스 1개(GPU 1개)용이므로 첫 번째 포드는 Karpenter가 예약 노드를 시작하도록 트리거합니다. 두 번째 포드는 예약된 노드에 맞을 수 없으며 Karpenter가 스팟 또는 온디맨드 용량에서 다른 노드를 시작하도록 트리거합니다.

cat << 'EOF' | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: gpu-overflow-test labels: guide: ai-eks-docs spec: replicas: 2 selector: matchLabels: app: gpu-overflow-test template: metadata: labels: app: gpu-overflow-test guide: ai-eks-docs spec: tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule containers: - name: nvidia-smi image: public.ecr.aws/amazonlinux/amazonlinux:2023-minimal command: ["sh", "-c", "nvidia-smi && sleep infinity"] resources: limits: nvidia.com/gpu: 1 EOF

실행하고 종료한 3단계의 nvidia-smi 테스트 포드와 달리 이 배포는 포드를 계속 실행(sleep infinity)하므로 GPU가 유지되고 노드가 통합되지 않습니다.

다른 노드에 예약된 포드를 확인합니다.

kubectl get pods -l app=gpu-overflow-test -o wide

예상 결과:

NAME                                 READY   STATUS    RESTARTS   AGE     IP            NODE                  NOMINATED NODE   READINESS GATES
gpu-overflow-test-55d55ff5b9-dvg52   1/1     Running   0          4m42s   10.0.75.210   i-08741a36089ff2088   <none>           <none>
gpu-overflow-test-55d55ff5b9-hw4m9   1/1     Running   0          4m43s   10.0.82.49    i-0f50cdbacb2017202   <none>           <none>

NodeClaim에서 용량 유형을 확인합니다.

kubectl get nodeclaims

예상 결과:

NAME            TYPE          CAPACITY   ZONE         NODE                  READY   AGE
gpu-inf-vw99m   g6e.4xlarge   reserved   us-east-2c   i-0f50cdbacb2017202   True    6m
gpu-inf-s65s6   g6.xlarge     spot       us-east-2b   i-08741a36089ff2088   True    5m59s

예약 노드가 먼저 시작된 후 예약이 가득 차면 스팟 또는 온디맨드 노드가 시작됩니다.

테스트 배포 정리:

kubectl delete deployment gpu-overflow-test

모니터링

Terraform은 1단계의 terraform apply 동안 이미 전체 모니터링 스택을 프로비저닝했습니다. 이 스택에는 Amazon Managed Service for Prometheus(AMP) 워크스페이스, Prometheus 원격 쓰기 및 Grafana 쿼리 액세스를 위한 IAM 정책 및 EKS Pod Identity 연결, kube-prometheus-stack 헬름 차트(Prometheus, Grafana, kube-state-metrics, node-exporter), GPU 지표를 위한 NVIDIA DCGM Exporter가 포함됩니다.

이 섹션에서는 배포된 모니터링 구성 요소의 확인을 다룹니다.

모니터링 포드 확인

모든 모니터링 포드가 준비될 때까지 기다립니다.

kubectl wait --for=condition=Ready pod --all -n monitoring --timeout=300s kubectl get pods -n monitoring

예상 결과:

NAME                                                       READY   STATUS    RESTARTS   AGE
kube-prometheus-stack-grafana-7c58f54f77-rftrj             3/3     Running   0          5m
kube-prometheus-stack-kube-state-metrics-d68dcbc84-5smxq   1/1     Running   0          5m
kube-prometheus-stack-operator-5895df479f-ttm47            1/1     Running   0          5m
kube-prometheus-stack-prometheus-node-exporter-t9q7s       1/1     Running   0          5m
kube-prometheus-stack-prometheus-node-exporter-x6vfb       1/1     Running   0          5m
prometheus-kube-prometheus-stack-prometheus-0              2/2     Running   0          5m

Grafana에 액세스

Grafana는 var.my_cidr에서 설정한 CIDR로 제한된 인터넷 경계 AWS Application Load Balancer(ALB)를 통해 노출됩니다. 로드 밸런서 URL을 출력합니다(ALB가 프로비저닝하는 데 1~2분 소요).

echo "http://$(kubectl get ingress kube-prometheus-stack-grafana -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')"

브라우저에서 URL을 엽니다. 다음 명령에서 사용자 이름 admin과 암호를 사용하여 로그인합니다.

kubectl --namespace monitoring get secrets kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo

지표 파이프라인 확인

지표 파이프라인이 종단 간 작동하는지 확인하려면:

  1. 연결 > 데이터 소스로 이동하여 Amazon-Managed-Prometheus가 기본 데이터 소스로 나열되었는지 확인합니다.

    Grafana에서 AMP 데이터 소스 검증

    기본 데이터 소스로 나열된 Amazon-Managed-Prometheus를 보여주는 Grafana 연결 페이지
  2. 드릴다운 > 지표로 이동하여 up 지표를 검색합니다. 클러스터의 스크레이프 대상의 결과가 표시되어야 합니다.

    Grafana에서 up 지표 검증

    활성 스크레이프 대상을 나타내는 녹색 상태 표시줄이 있는 위쪽 지표를 보여주는 Grafana 드릴다운 지표 페이지

up에 결과가 표시되면 파이프라인(클러스터 → Prometheus → AMP → Grafana)이 작동하는 것입니다.

DCGM GPU 지표 검증

DCGM Exporter DaemonSet는 GPU 노드에서 실행되며 GPU 사용률, 메모리, 온도, 전력 소비, NVLink 대역폭 및 텐서 활동 지표를 보고합니다.

DCGM Exporter DaemonSet를 확인합니다.

kubectl get daemonset dcgm-exporter -n monitoring

GPU 노드가 실행되면(2단계 또는 4단계) 하나 이상의 준비된 포드가 표시됩니다. DCGM 지표를 검증하려면 Grafana에서 드릴다운 > 지표로 이동하여 DCGM_을 검색합니다.

Grafana에서 DCGM 지표 검증

DCGM_FI_DEV_ECC_SBE_VOL_TOTAL, DCGM_FI_DEV_ENC_UTIL, DCGM_FI_DEV_FB_FREE, DCGM_FI_DEV_FB_USED를 포함한 GPU 지표를 보여주는 DCGM_으로 필터링된 Grafana 드릴다운 지표 페이지

대시보드를 보려면 대시보드 > GPU 모니터링 > NVIDIA DCGM Exporter Dashboard로 이동합니다.

Grafana의 NVIDIA DCGM Exporter Dashboard

GPU 사용률, GPU 평균 온도, 사용된 GPU Framebuffer Mem, GPU Power Total 패널을 보여주는 Grafana NVIDIA DCGM Exporter Dashboard

모델 가중치 S3 버킷

Terraform은 이미 모델 가중치를 저장하기 위한 Amazon S3 버킷, default 네임스페이스의 model-storage-sa ServiceAccount, 버킷으로 범위가 지정된 IAM 정책, 그리고 이들을 연결하는 EKS Pod Identity 연결을 생성했습니다. serviceAccountName: model-storage-sa를 설정하는 워크로드 포드는 버킷에서 읽고 쓸 수 있습니다.

버킷 확인

Terraform 출력에서 버킷 이름을 검색합니다.

MODEL_BUCKET=$(terraform output -raw model_bucket) echo ${MODEL_BUCKET}

버킷이 존재하는지 확인합니다.

aws s3api head-bucket --bucket ${MODEL_BUCKET}

예상 결과:

{
    "BucketArn": "arn:aws:s3:::ai-eks-docs-models-20250612abc1",
    "BucketRegion": "us-east-2",
    "AccessPointAlias": false
}

model-storage-sa ServiceAccount를 사용하여 AWS CLI 이미지로 일회성 포드를 실행하여 EKS Pod Identity가 연결되어 있고 S3 액세스가 작동하는지 확인합니다.

cat << EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: s3-test labels: guide: ai-eks-docs spec: serviceAccountName: model-storage-sa containers: - name: aws-cli image: public.ecr.aws/aws-cli/aws-cli:2.27.0 command: - sh - -c - | echo "=== Caller Identity ===" aws sts get-caller-identity echo "" echo "=== S3 Write Test ===" echo "pod identity works" | aws s3 cp - s3://${MODEL_BUCKET}/test.txt echo "" echo "=== S3 List Test ===" aws s3 ls s3://${MODEL_BUCKET}/ echo "" echo "=== S3 Delete Test ===" aws s3 rm s3://${MODEL_BUCKET}/test.txt restartPolicy: Never EOF

포드가 완료될 때까지 기다렸다가 로그를 확인합니다.

kubectl wait --for=jsonpath='{.status.phase}'=Succeeded pod/s3-test --timeout=300s kubectl logs s3-test

예상 결과:

=== Caller Identity ===
{
    "UserId": "AROA...:eks-ai-eks-docs-model-s-...",
    "Account": "123456789012",
    "Arn": "arn:aws:sts::123456789012:assumed-role/ai-eks-docs-models-.../eks-ai-eks-docs-..."
}

=== S3 Write Test ===
upload: - to s3://ai-eks-docs-models-20250612abc1/test.txt

=== S3 List Test ===
2026-07-15 12:00:00         19 test.txt

=== S3 Delete Test ===
delete: s3://ai-eks-docs-models-20250612abc1/test.txt

호출자 자격 증명은 포드가 EKS Pod Identity를 통해 모델 스토리지 역할을 수임했음을 확인합니다. S3 명령은 읽기 및 쓰기 액세스를 확인합니다.

테스트 포드를 정리합니다.

kubectl delete pod s3-test

다음 단계

클러스터가 준비되면 모델 로드 및 제공으로 이동하여 대규모 언어 모델을 배포하고 추론 엔드포인트와 상호 작용할 수 있습니다.

정리

작은 정보

이 가이드의 다음 섹션을 계속 진행하려면 전체 정리를 건너뛰세요. 완료한 경우에만 정리를 실행합니다.

GPU 노드를 보유한 포드가 없도록 테스트 워크로드를 삭제합니다.

kubectl delete pod nvidia-smi --ignore-not-found kubectl delete deployment gpu-overflow-test --ignore-not-found

ODCR만 릴리스하고 스팟 및 온디맨드 용량으로 대체하려면 nodepools 변수를 spot-ondemand 전략으로 다시 전환합니다.

terraform apply -var 'nodepools={"spot-ondemand"={}}'

이렇게 하면 NodePool 용량 유형 요구 사항에서 reserved가 제거되고 ODCR이 삭제되며 클러스터, 모니터링 스택 및 S3 버킷은 그대로 유지됩니다.

중요

예약을 취소해도 이미 실행 중인 인스턴스는 종료되지 않습니다. 이러한 인스턴스는 종료될 때까지 표준 온디맨드 요금으로 계속 실행됩니다. 예약이 해제되기 전에 예약된 노드가 드레이닝되도록 위와 같이 GPU 워크로드를 먼저 삭제합니다.

삭제하기 전에 Karpenter 관리형 노드를 드레이닝합니다. 이렇게 하면 진행 중인 노드 수명 주기가 삭제를 차단하지 않습니다. 드레이닝을 방해할 수 있는 PodDisruptionBudget을 모두 삭제한 다음 NodeClaim을 삭제합니다.

kubectl delete pdb -A --all --ignore-not-found kubectl delete nodeclaim --all --wait=true --timeout=900s

그런 다음 EKS 클러스터, VPC, 모니터링 스택, NodePool 및 NodeClass, S3 모델 버킷, ODCR을 포함하여 Terraform이 생성한 모든 항목을 삭제합니다.

terraform destroy
주의

모델 가중치 S3 버킷은 force_destroy = true를 사용하여 생성되므로 terraform destroy는 버킷에 업로드한 모델 가중치와 함께 버킷을 삭제합니다. 보관하려는 모든 항목을 먼저 다른 위치에 복사합니다.

참고

또한 리포지토리는 위의 드레이닝 및 삭제 단계를 실행한 다음 클러스터 이름으로 태그가 지정된 분리된 EBS 볼륨을 모두 스윕하는 scripts/cleanup.sh 헬퍼를 제공합니다. 적용을 실행한 terraform/<mode>/ 디렉터리 내에서 이 헬퍼를 실행하고 --auto-approve를 전달하여 Terraform 확인 프롬프트를 건너뜁니다.

클러스터에 활성 용량 예약이 남아 있지 않은지 확인합니다.

aws ec2 describe-capacity-reservations \ --filters "Name=state,Values=active" "Name=tag:nodepool,Values=reserved-spot-ondemand" \ --query 'CapacityReservations[].CapacityReservationId' \ --output text

결과가 비어 있으면 활성화된 예약이 없으며 추가 요금이 부과되지 않습니다.