컴퓨팅 및 오토 스케일링 - Amazon EKS

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

컴퓨팅 및 오토 스케일링

GPU 리소스 최적화 및 비용 관리

잘 알려진 레이블을 사용하여 GPU 요구 사항으로 워크로드 예약

다양한 GPU 특성(예: GPU, GPU 메모리)에 민감한 AI/ML 워크로드의 경우 Karpenter관리형 노드 그룹에 사용되는 노드 유형에서 지원하는 알려진 예약 레이블을 사용하여 GPU 요구 사항을 지정하는 것이 좋습니다. 이를 정의하지 않으면 GPU 리소스가 부적절한 인스턴스에서 포드가 예약되어 실패하거나 성능이 저하될 수 있습니다. nodeSelector 또는 노드 선호도를 사용하여 포드가 실행할 노드를 지정하고 포드의 리소스 섹션에서 컴퓨팅 리소스(CPU, 메모리, GPUs 등)를 설정하는 것이 좋습니다.

예제

예를 들어 Karpenter를 사용할 때 GPU 이름 노드 선택기를 사용하는 경우:

apiVersion: v1 kind: Pod metadata: name: gpu-pod-example spec: containers: - name: ml-workload image: <image> resources: limits: nvidia.com/gpu: 1 # Request one NVIDIA GPU nodeSelector: karpenter.k8s.aws/instance-gpu-name: "l40s" # Run on nodes with NVIDIA L40S GPUs

Kubernetes 디바이스 플러그인을 사용하여 GPUs 노출

노드에 GPUs를 노출하려면 Kubernetes 스케줄러가 사용 가능한 GPU가 있는 노드에 포드를 할당할 수 있도록 구성된 NVIDIA GPUs 드라이버를 노드의 운영 체제 및 컨테이너 런타임에 설치해야 합니다. NVIDIA Kubernetes 디바이스 플러그인의 설정 프로세스는 사용 중인 EKS 가속 AMI에 따라 달라집니다.

  • Bottlerocket Accelerated AMI: 이 AMI에는 NVIDIA GPU 드라이버가 포함되어 있으며 NVIDIA Kubernetes 디바이스 플러그인은 사전 설치되어 즉시 사용할 수 있으므로 GPU 지원을 즉시 사용할 수 있습니다. Kubernetes 스케줄러에 GPUs를 노출하는 데 추가 구성이 필요하지 않습니다.

  • AL2023 가속 AMI :이 AMI에는 NVIDIA GPU 드라이버가 포함되어 있지만 NVIDIA Kubernetes 디바이스 플러그인은 사전 설치되어 있지 않습니다. 일반적으로 DaemonSet를 통해 디바이스 플러그인을 별도로 설치하고 구성해야 합니다. eksctl을 사용하여 클러스터를 생성하고 ClusterConfig에서 GPU 인스턴스 유형(예: g5.xlarge)을 지정하는 경우 eksctl는 자동으로 가속화된 AMI를 선택하고 노드 그룹의 각 인스턴스에 NVIDIA Kubernetes 디바이스 플러그인을 설치합니다. 자세한 내용은 eksctl 설명서의 GPU 지원을 참조하세요.

NVIDIA 디바이스 플러그인이 활성 상태이고 GPUs되었는지 확인하려면 다음을 실행합니다.

kubectl describe node | grep nvidia.com/gpu

이 명령은 nvidia.com/gpu 리소스가 노드의 용량과 할당 가능한 리소스에 있는지 확인합니다. 예를 들어 GPU가 하나 있는 노드에는가 표시되어야 합니다nvidia.com/gpu: 1. 자세한 내용은 Kubernetes GPU 예약 안내서를 참조하세요.

ML 용량 블록을 사용하여 P 및 Trainium 인스턴스의 용량 보장

ML용 용량 블록을 사용하면 자주 찾는 GPU 인스턴스, 특히 P 인스턴스(예: p6-b200, p5, p5e, p5en, p4d, p4de) 및 Trainium 인스턴스(예: trn1, trn2)를 예약하여 거의 즉시 또는 향후 날짜에 시작하여 단기 기계 학습(ML) 워크로드를 지원할 수 있습니다. 이러한 예약은 모델 훈련 및 미세 조정과 같은 컴퓨팅 집약적 작업의 용량을 보장하는 데 적합합니다. EC2 용량 블록 요금은 예약 요금과 운영 체제 요금으로 구성됩니다. 요금에 대한 자세한 내용은 ML 요금에 대한 EC2 용량 블록을 참조하세요.

예측 가능한 용량 보장을 위해 Amazon EKS에서 AI/ML 워크로드용 GPUs를 예약하려면 단기 또는 온디맨드 용량 예약(ODCRs)을 위해 ML 용량 블록을 활용하여 범용 용량 보장을 수행하는 것이 좋습니다.

  • ODCRs 사용하면 특정 가용 영역에서 EC2 인스턴스 용량(예: g5 또는 p5와 같은 GPU 인스턴스)을 일정 기간 동안 예약하여 수요가 많은 경우에도 가용성을 보장할 수 있습니다. ODCRs 장기 약정이 없지만 사용 여부에 관계없이 예약 용량에 대한 온디맨드 요금을 지불합니다. EKS에서 ODCRs은 Karpenter관리형 노드 그룹과 같은 노드 유형에서 지원됩니다. Karpenter에서 ODCRs의 우선 순위를 지정하려면 capacityReservationSelectorTerms 필드를 사용하도록 NodeClass를 구성합니다. Karpenter NodePools 설명서를 참조하세요.

  • 용량 블록은 GPU(예: p5, p4d) 또는 Trainium(trn1, trn2) 인스턴스에 대한 특수한 예약 메커니즘으로, 모델 훈련, 미세 조정 또는 실험과 같은 단기 ML 워크로드를 위해 설계되었습니다. 미래 날짜부터 정해진 기간(일반적으로 24시간~182일) 동안 예약된 시간에 대해서만 요금을 지불하고 용량을 예약합니다. 선불이며 용량 요구 사항에 대한 사전 계획이 필요하고 자동 크기 조정을 지원하지 않지만 지연 시간이 짧은 네트워킹을 위해 EC2 UltraClusters 공동 배치됩니다. 예약 기간에 대해서만 요금이 부과됩니다. 자세한 내용은 용량 블록 찾기 및 구매를 참조하거나 ML용 용량 블록으로 관리형 노드 그룹 생성의 지침에 따라 용량 블록으로 관리형 노드 그룹을 설정하여 시작하세요.

AWS Management Console을 통해 용량을 예약하고 ML 용량 블록을 사용하도록 노드를 구성합니다. 워크로드 일정에 따라 예약을 계획하고 스테이징 클러스터에서 테스트합니다. 자세한 내용은 용량 블록 설명서를 참조하세요.

G Amazon EC2 인스턴스에 대한 온디맨드, Amazon EC2 스팟 또는 온디맨드 용량 예약(ODCRs) 고려

G Amazon EC2 인스턴스의 경우 온디맨드, Amazon EC2 스팟 인스턴스 및 온디맨드 용량 예약과 같은 다양한 구매 옵션을 고려합니다. ODCRs 사용하면 특정 가용 영역에서 특정 기간 동안 EC2 인스턴스 용량을 예약하여 수요가 많은 경우에도 가용성을 보장할 수 있습니다. P 및 Trainium 인스턴스에서만 사용할 수 있는 ML 용량 블록과 달리 ODCRs은 G 인스턴스를 비롯한 다양한 인스턴스 유형에 사용할 수 있으므로 추론 또는 그래픽과 같은 다양한 GPU 기능이 필요한 워크로드에 적합합니다. Amazon EC2 스팟 인스턴스를 사용할 때 다양한 인스턴스 유형, 크기 및 가용 영역에 걸쳐 다양할 수 있는 것은 스팟을 더 오래 유지할 수 있는 핵심입니다.

ODCRs 장기 약정이 없지만 사용 여부에 관계없이 예약 용량에 대한 온디맨드 요금을 지불합니다. ODCRs 즉시 사용할 수 있도록 생성하거나 향후 날짜로 예약하여 용량 계획에 유연성을 제공할 수 있습니다. Amazon EKS에서 ODCRs은 Karpenter관리형 노드 그룹과 같은 노드 유형에서 지원됩니다. Karpenter에서 ODCRs의 우선 순위를 지정하려면 capacityReservationSelectorTerms 필드를 사용하도록 NodeClass를 구성합니다. Karpenter NodePools 설명서를 참조하세요. CLI 명령을 포함하여 ODCRs 생성하는 방법에 대한 자세한 내용은 온디맨드 용량 예약 시작하기를 참조하세요.

기타 가속화된 인스턴스 유형 및 크기 고려

Amazon EKS의 ML 워크로드에서 성능과 비용을 모두 최적화하려면 적절한 가속화된 인스턴스와 크기를 선택해야 합니다. 예를 들어 GPU 인스턴스 패밀리마다 GPU 메모리와 같은 성능과 기능이 다릅니다. 가장 경제적인 옵션을 선택하는 데 도움이 되도록 가속 컴퓨팅의 EC2 인스턴스 유형 페이지에서 사용 가능한 GPU 인스턴스를 검토하세요. 여러 인스턴스 유형 및 크기를 평가하여 특정 워크로드 요구 사항에 가장 적합한 인스턴스를 찾습니다. GPUs 수, 메모리 및 네트워크 성능과 같은 요소를 고려합니다. 적절한 GPU 인스턴스 유형과 크기를 신중하게 선택하면 EKS 클러스터에서 리소스 사용률과 비용 효율성을 높일 수 있습니다.

EKS 노드에서 GPU 인스턴스를 사용하는 경우 기본적으로 네kube-system임스페이스에 nvidia-device-plugin-daemonset 포드가 있습니다. 인스턴스에서 GPU를 완전히 활용하는지 여부를 빠르게 파악하려면 다음과 같이 nvidia-smi(nvidia-smi)를 사용할 수 있습니다.

kubectl exec nvidia-device-plugin-daemonset-xxxxx \ -n kube-system -- nvidia-smi \ --query-gpu=index,power.draw,power.limit,temperature.gpu,utilization.gpu,utilization.memory,memory.free,memory.used \ --format=csv -l 5
  • utilization.memory가 100%에 가까우면 코드(들)가 메모리 경계일 가능성이 높습니다. 즉, GPU(메모리)가 완전히 활용되지만 추가 성능 최적화를 조사해야 한다고 제안할 수 있습니다.

  • utilization.gpu가 100%에 가까우면 반드시 GPU가 완전히 사용된다는 의미는 아닙니다. 더 나은 지표는 대 power.draw의 비율입니다power.limit. 이 비율이 100% 이상이면 코드(들)가 GPU의 컴퓨팅 용량을 완전히 활용하는 것입니다.

  • -l 5 플래그는 5초마다 지표를 출력하라는 메시지를 표시합니다. 단일 GPU 인스턴스 유형의 경우 인덱스 쿼리 플래그가 필요하지 않습니다.

자세한 내용은 AWS 설명서의 GPU 인스턴스를 참조하세요.

시간 조각화, MIG 및 분수 GPU 할당을 사용하여 GPU 리소스 할당 최적화

Kubernetes의 정적 리소스 제한(예: CPU, 메모리, GPU 수)은 특히 추론과 같은 동적 AI/ML 워크로드의 경우 과다 프로비저닝 또는 사용 부족으로 이어질 수 있습니다. 올바른 GPU를 선택하는 것이 중요합니다. 소량 또는 급증하는 워크로드의 경우 시간 분할을 사용하면 컴퓨팅 리소스를 공유하여 여러 워크로드가 단일 GPU를 공유할 수 있으므로 효율성을 개선하고 낭비를 줄일 수 있습니다. GPU 공유는 다양한 옵션을 통해 달성할 수 있습니다.

  • 노드 선택기/노드 선호도를 활용하여 일정에 영향: 프로비저닝된 노드와 포드가 워크로드에 적합한 GPUs에 예약되어 있는지 확인합니다(예: karpenter.k8s.aws/instance-gpu-name: "a100").

  • 시간 조각화: 시간 경과에 따라 GPU의 컴퓨팅 리소스를 공유하도록 워크로드를 예약하므로 물리적 파티셔닝 없이 동시에 실행할 수 있습니다. 이는 컴퓨팅 요구가 가변적인 워크로드에 적합하지만 메모리 격리가 부족할 수 있습니다.

  • 다중 인스턴스 GPU(MIG): MIG를 사용하면 단일 NVIDIA GPU를 격리된 여러 인스턴스로 분할할 수 있으며 NVIDIA Ampere(예: A100 GPU), NVIDIA Hopper(예: H100 GPU) 및 NVIDIA Blackwell(예: Blackwell GPUs) GPUs에서 지원됩니다. 각 MIG 인스턴스는 전용 컴퓨팅 및 메모리 리소스를 수신하여 다중 테넌트 환경 또는 리소스 보장이 필요한 워크로드에서 리소스를 공유할 수 있으므로 시간 분할을 통해 배치 크기가 다른 여러 모델을 제공하는 등의 시나리오를 포함하여 GPU 리소스 사용률을 최적화할 수 있습니다.

  • 분수 GPU 할당: 소프트웨어 기반 예약을 사용하여 GPU 컴퓨팅 또는 메모리의 일부를 워크로드에 할당하여 동적 워크로드에 유연성을 제공합니다. Run:ai 플랫폼의 일부인 NVIDIA KAI 스케줄러를 사용하면 포드가 부분 GPU 리소스를 요청할 수 있습니다.

EKS에서 이러한 기능을 활성화하려면 GPUs를 예약 가능한 리소스로 노출하고 시간 분할 및 MIG를 지원하는 NVIDIA 디바이스 플러그인을 배포할 수 있습니다. 자세한 내용은 Kubernetes의 시간 조각화 GPUs NVIDIA 시간 조각화 및 가속화된 EC2 인스턴스를 사용한 Amazon EKS의 GPU 공유를 참조하세요.

예제

예를 들어 NVIDIA 디바이스 플러그인을 사용하여 시간 조각화를 활성화하려면:

apiVersion: v1 kind: ConfigMap metadata: name: nvidia-device-plugin-config namespace: kube-system data: config.yaml: | version: v1 sharing: timeSlicing: resources: - name: nvidia.com/gpu replicas: 4 # Allow 4 pods to share each GPU

예제

예를 들어 KAI 스케줄러를 분수 GPU 할당에 사용하려면 NVIDIA GPU 연산자와 함께 배포하고 포드 사양에서 분수 GPU 리소스를 지정합니다.

apiVersion: v1 kind: Pod metadata: name: fractional-gpu-pod-example annotations: gpu-fraction: "0.5" # Annotation for 50% GPU labels: runai/queue: "default" # Required queue assignment spec: containers: - name: ml-workload image: nvcr.io/nvidia/pytorch:25.04-py3 resources: limits: nvidia.com/gpu: 1 nodeSelector: nvidia.com/gpu: "true" schedulerName: kai-scheduler

노드 복원력 및 훈련 작업 관리

자동 복구를 통한 노드 상태 확인 구현

여러 노드에서 다중 GPU 모델 훈련과 같이 노드 간 통신이 자주 필요한 Amazon EKS의 분산 훈련 작업의 경우 GPU 또는 EFA 장애와 같은 하드웨어 문제로 인해 훈련 작업이 중단될 수 있습니다. 이러한 중단으로 인해 특히 안정적인 하드웨어에 의존하는 장기 실행 AI/ML 워크로드의 경우 훈련 진행 상황이 손실되고 비용이 증가할 수 있습니다.

GPU 워크로드를 실행하는 EKS 클러스터의 GPU 장애와 같은 하드웨어 장애에 대한 복원력을 추가하려면 EKS 노드 모니터링 에이전트를 자동 복구 또는 Amazon SageMaker HyperPod와 함께 활용하는 것이 좋습니다. 자동 복구가 포함된 EKS 노드 모니터링 에이전트는 노드 상태 모니터링 및 표준 Kubernetes 메커니즘을 사용한 자동 복구와 같은 기능을 제공하지만 SageMaker HyperPod는 심층 상태 확인 및 자동 작업 재개와 같은 대규모 ML 훈련을 위해 특별히 설계된 추가 기능과 대상 복원력을 제공합니다.

  • 노드 자동 복구가 포함된 EKS 노드 모니터링 에이전트는 GPU 또는 네트워킹 장애와 같은 문제를 식별하기 위해 가속화된 하드웨어와 관련된 표준 조건Ready과 같은 표준 조건을 포함하여 로그를 읽고 NodeConditions를 적용하여 노드 상태를 지속적으로 모니터링합니다. 노드가 비정상으로 간주되면 노드 자동 복구는 노드를 코딩하고 새 노드로 교체합니다. 포드 일정 변경 및 작업 재시작은 표준 Kubernetes 메커니즘과 작업 재시작 정책에 따라 달라집니다.

  • SageMaker HyperPod 심층 상태 확인 및 상태 모니터링 에이전트는 GPU 및 Trainium 기반 인스턴스의 상태를 지속적으로 모니터링합니다. 레이블(예: node-health-status)을 사용하여 노드 상태를 관리함으로써 AI/ML 워크로드에 맞게 조정됩니다. 노드가 비정상으로 간주되면 HyperPod는 GPUs. 기본적으로 기본 상태 확인을 통해 EFA의 네트워킹 관련 장애를 감지하고 중단된 훈련 작업에 대한 자동 재개를 지원하므로 마지막 체크포인트에서 작업을 계속할 수 있으므로 대규모 ML 작업의 중단이 최소화됩니다.

EFA를 사용하는 자동 복구 및 SageMaker HyperPod 클러스터가 있는 EKS 노드 모니터링 에이전트의 경우 원격 직접 메모리 액세스(RDMA) 오류 및 패킷 삭제와 같은 EFA별 지표를 모니터링하려면 AWS EFA 드라이버가 설치되어 있는지 확인합니다. 또한 CloudWatch Observability 추가 기능을 배포하거나 Prometheus 및 Grafana와 함께 DCGM Exporter와 같은 도구를 사용하여 EFA, GPU 및 SageMaker HyperPod의 경우 기능과 관련된 특정 지표를 모니터링하는 것이 좋습니다.

중단에 민감한 워크로드에 대해 Karpenter 통합 비활성화

처리, 대규모 AI/ML 예측 작업 또는 훈련과 같은 중단에 민감한 워크로드의 경우 작업 실행 중에 중단이 발생하지 않도록 Karpenter 통합 정책을 조정하는 것이 좋습니다. Karpenter의 통합 기능은 사용률이 낮은 노드를 종료하거나 저렴한 대체 노드로 대체하여 클러스터 비용을 자동으로 최적화합니다. 그러나 워크로드가 GPU를 완전히 활용하는 경우에도 Karpenter는 포드의 요구 사항을 충족하는 적절한 크기의 저렴한 인스턴스 유형을 식별하면 노드를 통합할 수 있으며, 이로 인해 작업이 중단될 수 있습니다.

WhenEmptyOrUnderutilized 통합 정책은 노드를 조기에 종료하여 실행 시간이 길어질 수 있습니다. 예를 들어, 중단으로 인해 포드 다시 예약, 데이터 다시 로드로 인해 작업 재개가 지연될 수 있으며, 이는 장기 실행 배치 추론 작업에 비용이 많이 들 수 있습니다. 이를 완화하기 위해를 consolidationPolicy 로 설정하고 워크로드 급증 중에 노드를 유지하도록 1시간과 같은 consolidateAfter 기간을 WhenEmpty 구성할 수 있습니다. 예:

disruption: consolidationPolicy: WhenEmpty consolidateAfter: 60m

이 접근 방식은 중단 비용이 컴퓨팅 비용 절감을 능가하는 실시간 온라인 추론 데이터 처리 또는 모델 훈련과 같이 급증하는 배치 추론 워크로드 및 기타 중단에 민감한 작업의 포드 시작 지연 시간을 개선합니다. Karpenter NodePool 중단 예산은 Karpenter 중단을 관리하는 또 다른 기능입니다. 예산을 사용하면 특정 시점에 선택한 NodePool에서 특정 수 이하의 노드 노드가 중단되지 않도록 할 수 있습니다. 또한 중단 예산을 사용하여 특정 시간(예: 피크 시간)에 모든 노드가 중단되는 것을 방지할 수 있습니다. 자세한 내용은 Karpenter 통합 설명서를 참조하세요.

ttlSecondsAfterFinished를 사용하여 Kubernetes 작업 자동 정리

완료된 작업 객체ttlSecondsAfterFinished를 자동으로 삭제하려면 Amazon EKS에서 Kubernetes 작업을 설정하는 것이 좋습니다. 링거링 작업 객체는 API 서버 메모리와 같은 클러스터 리소스를 사용하며, 대시보드(예: Grafana, Amazon CloudWatch)를 복잡하게 만들어 모니터링을 복잡하게 만듭니다. 예를 들어 TTL을 1시간으로 설정하면 완료 직후 작업이 제거되어 클러스터가 깔끔하게 유지됩니다. 자세한 내용은 완료된 작업에 대한 자동 정리를 참조하세요.

우선 순위가 높은 작업/워크로드를 위한 우선 순위가 낮은 작업 사전 구성

Amazon EKS의 혼합 우선 순위 AI/ML 워크로드의 경우 우선 순위가 낮은 작업 선점으로 구성하여 우선 순위가 높은 작업(예: 실시간 추론)이 리소스를 즉시 수신하도록 할 수 있습니다. 선점 없이 배치 프로세스(예: 배치 추론, 데이터 처리), 비배치 서비스(예: 백그라운드 작업, cron 작업) 또는 CPU/메모리 집약적 작업(예: 웹 서비스)과 같은 우선 순위가 낮은 워크로드는 노드를 점유하여 중요한 포드를 지연시킬 수 있습니다. 우선 순위가 높은 포드에 리소스가 필요할 때 Kubernetes가 우선 순위가 낮은 포드를 제거하여 GPUs, CPUs 또는 메모리가 있는 노드에서 효율적인 리소스 할당을 보장할 수 있습니다. 우선 순위를 할당하고 제거 동작을 제어PriorityClass하려면 KubernetesPodDisruptionBudget를 사용하는 것이 좋습니다.

apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: low-priority value: 100 --- spec: priorityClassName: low-priority

자세한 내용은 Kubernetes Priority and Preemption 설명서를 참조하세요.

애플리케이션 조정 및 성능

Karpenter 또는 정적 노드를 사용한 ML 워크로드의 사용자 지정 컴퓨팅 용량

Amazon EKS의 기계 학습(ML) 워크플로에 대한 비용 효율적이고 응답성이 뛰어난 컴퓨팅 용량을 보장하려면 워크로드의 특성 및 비용 약정에 맞게 노드 프로비저닝 전략을 조정하는 것이 좋습니다. 다음은 Karpenter를 사용한 just-in-time 조정과 예약 용량에 대한 정적 노드 그룹이라는 두 가지 고려 사항입니다.

  • Karpenter와 같은 Just-in-time 데이터 영역 스케일러: 컴퓨팅 요구가 가변적인 동적 ML 워크플로(예: GPU 기반 추론 후 CPU 기반 플로팅)의 경우 Karpenter와 같은 just-in-time 데이터 영역 스케일러를 사용하는 것이 좋습니다.

  • 예측 가능한 워크로드에 정적 노드 그룹 사용: 예측 가능한 정상 상태 ML 워크로드의 경우 또는 예약 인스턴스를 사용할 때 EKS 관리형 노드 그룹은 예약 용량이 완전히 프로비저닝되고 활용되도록 하여 절감 효과를 극대화할 수 있습니다. 이 접근 방식은 RIs 또는 ODCRs을 통해 커밋된 특정 인스턴스 유형에 적합합니다.

예제

인스턴스 생성이 3보다 큰 g Amazon EC2 인스턴스를 시작할 수 있는 다양한 Karpenter NodePool의 예입니다.

apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-inference spec: template: spec: nodeClassRef: group: karpenter.k8s.aws kind: EC2NodeClass name: default requirements: - key: karpenter.sh/capacity-type operator: In values: ["on-demand"] - key: karpenter.k8s.aws/instance-category operator: In values: ["g"] - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["3"] - key: kubernetes.io/arch operator: In values: ["amd64"] taints: - key: nvidia.com/gpu effect: NoSchedule limits: cpu: "1000" memory: "4000Gi" nvidia.com/gpu: "10" *# Limit the total number of GPUs to 10 for the NodePool* disruption: consolidationPolicy: WhenEmpty consolidateAfter: 60m expireAfter: 720h

예제

훈련 워크로드에 정적 노드 그룹을 사용하는 예:

apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: ml-cluster region: us-west-2 managedNodeGroups: - name: gpu-node-group instanceType: p4d.24xlarge minSize: 2 maxSize: 2 desiredCapacity: 2 taints: - key: nvidia.com/gpu effect: NoSchedule

테인트 및 허용치를 사용하여 가속화되지 않은 워크로드가 가속화된 인스턴스에서 예약되지 않도록 방지

GPU 리소스에서 가속화되지 않은 워크로드를 예약하는 것은 컴퓨팅 효율적이지 않으므로 테인트와 허용 오차를 사용하여 가속화되지 않은 워크로드 포드가 부적절한 노드에 예약되지 않도록 하는 것이 좋습니다. 자세한 내용은 Kubernetes 설명서를 참조하세요.

모델 성능에 따라 규모 조정

추론 워크로드의 경우 적절한 휴지 기간과 함께 Kubernetes Event-Driven Autoscaling(KEDA)을 사용하여 추론 요청 또는 토큰 처리량과 같은 모델 성능 지표를 기반으로 확장하는 것이 좋습니다. 정적 조정 정책은 리소스를 과다 또는 과소 프로비저닝하여 비용과 지연 시간에 영향을 미칠 수 있습니다. KEDA 설명서에서 자세히 알아보세요.

고급 GPU 관리를 위한 동적 리소스 할당

동적 리소스 할당(DRA)은 Kubernetes GPU 리소스 관리의 근본적인 발전을 나타냅니다. DRA는 기존 디바이스 플러그인 제한을 넘어 정교한 GPU 공유, 토폴로지 인식 및 노드 간 리소스 조정을 지원합니다. Amazon EKS 버전 1.33에서 사용할 수 있는 DRA는 다음을 제공하여 AI/ML 워크로드의 중요한 문제를 해결합니다.

  • 세분화된 GPU 할당

  • 다중 프로세스 서비스(MPS) 및 다중 인스턴스 GPU(MIG)와 같은 고급 공유 메커니즘

  • NVIDIA GB200 UltraClusters를 포함한 차세대 하드웨어 아키텍처 지원

기존 GPU 할당은 GPUs 불투명한 정수 리소스로 취급하여 사용률이 크게 떨어집니다(프로덕션 클러스터에서 종종 30~40%). 이는 소수 리소스만 필요한 경우에도 워크로드GPUs에 독점적으로 액세스할 수 있기 때문입니다. DRA는 Kubernetes 스케줄러에 하드웨어 특성 및 워크로드 요구 사항에 대한 완전한 가시성을 제공하는 구조화된 선언적 할당을 도입하여이 모델을 변환합니다. 이를 통해 지능형 배치 결정과 효율적인 리소스 공유가 가능합니다.

NVIDIA 디바이스 플러그인 대신 DRA를 사용할 때의 이점

NVIDIA 디바이스 플러그인( 버전 부터 시작0.12.0)은 시간 조각화, MPS 및 MIG를 포함한 GPU 공유 메커니즘을 지원합니다. 그러나 DRA에서 다루는 아키텍처 제한 사항이 있습니다.

NVIDIA 디바이스 플러그인 제한 사항

  • 정적 구성: GPU 공유 구성(시간 분할 복제본 및 MPS 설정)에는를 통해 클러스터 전체에서 사전 구성이 필요합니다ConfigMaps. 따라서 워크로드마다 다른 공유 전략을 제공하기가 어렵습니다.

  • 제한적인 세분화 선택: 디바이스 플러그인은 노드 레이블을 통해 GPU 특성을 노출하지만 워크로드는 일정 결정의 일부로 특정 GPU 구성(메모리 크기 및 컴퓨팅 기능)을 동적으로 요청할 수 없습니다.

  • 노드 간 리소스 조정 없음: 여러 노드에서 분산 GPU 리소스를 관리하거나 NVIDIA GB200과 같은 시스템의 NVLink 도메인과 같은 복잡한 토폴로지 요구 사항을 표현할 수 없습니다.

  • 스케줄러 제약: Kubernetes 스케줄러는 GPU 리소스를 불투명한 정수로 취급하여 토폴로지 인식 결정을 내리거나 복잡한 리소스 종속성을 처리하는 기능을 제한합니다.

  • 구성 복잡성: 서로 다른 공유 전략을 설정하려면 여러 개의 신중ConfigMaps한 노드 레이블 지정이 필요하므로 운영 복잡성이 발생합니다.

DRA를 사용한 솔루션

  • 동적 리소스 선택: DRA를 사용하면 워크로드가를 통해 요청 시 세부 요구 사항(GPU 메모리, 드라이버 버전 및 특정 속성)을 지정할 수 있습니다resourceclaims. 이를 통해 보다 유연한 리소스 매칭이 가능합니다.

  • 토폴로지 인식: DRA는 구조화된 파라미터 및 디바이스 선택기를 통해 노드 간 GPU 통신 및 메모리 일관성 상호 연결과 같은 복잡한 요구 사항을 처리합니다.

  • 노드 간 리소스 관리: IMEX 채널을 computeDomains 통해 GB200과 같은 시스템에 중요한 여러 노드에서 분산 GPU 리소스를 조정할 수 있습니다.

  • 워크로드별 구성: 각각 서로 다른 공유 전략 및 구성을 ResourceClaim 지정하므로 클러스터 전체 설정이 아닌 워크로드별로 세분화된 제어가 가능합니다.

  • 향상된 스케줄러 통합: DRA는 스케줄러에 자세한 디바이스 정보를 제공하고 하드웨어 토폴로지 및 리소스 특성에 따라 보다 지능적인 배치 결정을 가능하게 합니다.

중요: DRA는 NVIDIA 디바이스 플러그인을 완전히 대체하지 않습니다. NVIDIA DRA 드라이버는 디바이스 플러그인과 함께 작동하여 향상된 기능을 제공합니다. 디바이스 플러그인은 기본 GPU 검색 및 관리를 계속 처리하는 반면, DRA는 고급 할당 및 예약 기능을 추가합니다.

DRA에서 지원하는 인스턴스 및 해당 기능

DRA 지원은 다음 표와 같이 Amazon EC2 인스턴스 패밀리 및 GPU 아키텍처에 따라 다릅니다.

인스턴스 패밀리 GPU 유형 시간 조각화 MIG 지원 MPS 지원 IMEX 지원 사용 사례

G5

NVIDIA A10G

아니요

아니요

추론 및 그래픽 워크로드

G6

NVIDIA L4

아니요

아니요

AI 추론 및 비디오 처리

G6e

NVIDIA L40S

아니요

아니요

훈련, 추론 및 그래픽

P4d/P4de

NVIDIA A100

아니요

대규모 훈련 및 HPC

P5

NVIDIA H100

아니요

파운데이션 모델 훈련

P6

NVIDIA B200

아니요

10억 또는 1조 파라미터 모델, 분산 훈련 및 추론

P6e

NVIDIA GB200

10억 또는 1조 파라미터 모델, 분산 훈련 및 추론

다음은 테이블의 각 기능에 대한 설명입니다.

  • 시간 조각화: 여러 워크로드가 시간 경과에 따라 GPU 컴퓨팅 리소스를 공유할 수 있습니다.

  • 다중 인스턴스 GPU(MIG): 격리된 GPU 인스턴스를 생성하는 하드웨어 수준 파티셔닝입니다.

  • 다중 프로세스 서비스(MPS): 단일 GPU에서 여러 CUDA 프로세스의 동시 실행을 활성화합니다.

  • Internode Memory Exchange(IMEX): GB200 UltraClusters.

추가 리소스

Kubernetes DRA 및 NVIDIA DRA 드라이버에 대한 자세한 내용은 GitHub의 다음 리소스를 참조하세요.

고급 GPU 관리를 위한 동적 리소스 할당 설정

다음 주제에서는 고급 GPU 관리를 위해 동적 리소스 할당(DRA)을 설정하는 방법을 보여줍니다.

사전 조건

Amazon EKS에서 DRA를 구현하기 전에 환경이 다음 요구 사항을 충족하는지 확인합니다.

클러스터 구성
  • 버전 1.33 이상을 실행하는 Amazon EKS 클러스터

  • Amazon EKS 관리형 노드 그룹(DRA는 현재 AL2023 및 Bottlerocket NVIDIA 최적화 AMIs 있는 관리형 노드 그룹에서만 지원되며 [Karpenter 제외](https://github.com/kubernetes-sigs/karpenter/issues/1231))

  • 적절한 인스턴스 유형이 있는 NVIDIA GPU 지원 작업자 노드

필수 구성 요소
  • NVIDIA 디바이스 플러그인 버전 0.17.1 이상

  • NVIDIA DRA 드라이버 버전 25.3.0 이상

1단계: eksctl을 사용하여 DRA 지원 노드 그룹으로 클러스터 생성

  1. 라는 클러스터 구성 파일을 생성합니다dra-eks-cluster.yaml.

    --- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: dra-eks-cluster region: us-west-2 version: '1.33' managedNodeGroups: - name: gpu-dra-nodes amiFamily: AmazonLinux2023 instanceType: g6.12xlarge desiredCapacity: 2 minSize: 1 maxSize: 3 labels: node-type: "gpu-dra" nvidia.com/gpu.present: "true" taints: - key: nvidia.com/gpu value: "true" effect: NoSchedule
  2. 클러스터를 생성합니다.

    eksctl create cluster -f dra-eks-cluster.yaml

2단계: NVIDIA 디바이스 플러그인 배포

NVIDIA 디바이스 플러그인을 배포하여 기본 GPU 검색을 활성화합니다.

  1. NVIDIA 디바이스 플러그인 Helm 리포지토리를 추가합니다.

    helm repo add nvidia https://nvidia.github.io/k8s-device-plugin helm repo update
  2. 디바이스 플러그인에 대한 사용자 지정 값을 생성합니다.

    cat <<EOF > nvidia-device-plugin-values.yaml gfd: enabled: true nfd: enabled: true tolerations: - key: [nvidia.com/gpu](http://nvidia.com/gpu) operator: Exists effect: NoSchedule EOF
  3. NVIDIA 디바이스 플러그인을 설치합니다.

    helm install nvidia-device-plugin nvidia/nvidia-device-plugin \ --namespace nvidia-device-plugin \ --create-namespace \ --version v0.17.1 \ --values nvidia-device-plugin-values.yaml

3단계: NVIDIA DRA 드라이버 Helm 차트 배포

  1. DRA 드라이버에 대한 dra-driver-values.yaml 값 파일을 생성합니다.

    --- nvidiaDriverRoot: / gpuResourcesEnabledOverride: true resources: gpus: enabled: true computeDomains: enabled: true # Enable for GB200 IMEX support controller: tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule kubeletPlugin: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: "nvidia.com/gpu.present" operator: In values: ["true"] tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
  2. NVIDIA NGC Helm 리포지토리를 추가합니다.

    helm repo add nvidia https://helm.ngc.nvidia.com/nvidia helm repo update
  3. NVIDIA DRA 드라이버를 설치합니다.

    helm install nvidia-dra-driver nvidia/nvidia-dra-driver-gpu \ --version="25.3.0-rc.2" \ --namespace nvidia-dra-driver \ --create-namespace \ --values dra-driver-values.yaml

4단계: DRA 설치 확인

  1. DRA API 리소스를 사용할 수 있는지 확인합니다.

    kubectl api-resources | grep [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1)

    예상 출력은 다음과 같습니다.

    deviceclasses [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) false DeviceClass resourceclaims [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) true ResourceClaim resourceclaimtemplates [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) true ResourceClaimTemplate resourceslices [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) false ResourceSlice
  2. 사용 가능한 디바이스 클래스를 확인합니다.

    kubectl get deviceclasses

    다음은 예상 출력의 예입니다.

    NAME AGE compute-domain-daemon.nvidia.com 4h39m compute-domain-default-channel.nvidia.com 4h39m gpu.nvidia.com 4h39m mig.nvidia.com 4h39m

    새로 생성된 G6 GPU 인스턴스가 DRA가 활성화된 Amazon EKS 클러스터에 조인하면 다음 작업이 발생합니다.

    • NVIDIA DRA 드라이버는 A10G GPU를 자동으로 검색하고 해당 노드resourceslices에 2개를 생성합니다.

    • gpu.nvidia.com 조각은 사양(메모리, 컴퓨팅 기능 등)에 물리적 A10G GPU 디바이스를 등록합니다.

    • A10G는 MIG 파티셔닝을 지원하지 않으므로 compute-domain.nvidia.com 조각은 GPU의 전체 컴퓨팅 컨텍스트를 나타내는 단일 컴퓨팅 도메인을 생성합니다.

    • 그런 resourceslices 다음 Kubernetes API 서버에 게시되어를 통해 GPU 리소스를 예약할 수 있습니다resourceclaims.

      DRA 스케줄러는 이제를 통해 GPU 리소스를 요청하는 포드에이 GPU를 지능적으로 할당하여 기존 디바이스 플러그인 접근 방식에 비해 더 유연한 리소스 관리를 resourceclaimtemplates제공할 수 있습니다. 이는 수동 개입 없이 자동으로 발생합니다. DRA 드라이버가 리소스 검색 및 등록 프로세스를 완료하면 노드를 GPU 워크로드에 사용할 수 있게 됩니다.

      다음 명령을 실행할 때:

      kubectl get resourceslices

      다음은 예상 출력의 예입니다.

      NAME NODE DRIVER POOL AGE ip-100-64-129-47.ec2.internal-compute-domain.nvidia.com-rwsts ip-100-64-129-47.ec2.internal compute-domain.nvidia.com ip-100-64-129-47.ec2.internal 35m ip-100-64-129-47.ec2.internal-gpu.nvidia.com-6kndg ip-100-64-129-47.ec2.internal gpu.nvidia.com ip-100-64-129-47.ec2.internal 35m

계속해서 동적 리소스 할당을 사용하여 간단한 GPU 워크로드 예약로 이동하세요.

동적 리소스 할당을 사용하여 간단한 GPU 워크로드 예약

동적 리소스 할당(DRA)을 사용하여 간단한 GPU 워크로드를 예약하려면 다음 단계를 수행합니다. 계속하기 전에를 따랐는지 확인합니다고급 GPU 관리를 위한 동적 리소스 할당 설정.

  1. basic-gpu-claim-template.yaml이름이 인 파일을 사용하여 GPU 할당에 ResourceClaimTemplate 대한 기본을 생성합니다.

    --- apiVersion: v1 kind: Namespace metadata: name: gpu-test1 --- apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: namespace: gpu-test1 name: single-gpu spec: spec: devices: requests: - name: gpu deviceClassName: gpu.nvidia.com
  2. 템플릿을 적용합니다.

    kubectl apply -f basic-gpu-claim-template.yaml
  3. 상태를 확인합니다.

    kubectl get resourceclaimtemplates -n gpu-test1

    다음은 예 출력입니다.

    NAME AGE single-gpu 9m16s
  4. 이름이 인 파일과 ResourceClaimTemplate 함께를 사용하는 포드를 생성합니다basic-gpu-pod.yaml.

    --- apiVersion: v1 kind: Pod metadata: namespace: gpu-test1 name: gpu-pod labels: app: pod spec: containers: - name: ctr0 image: ubuntu:22.04 command: ["bash", "-c"] args: ["nvidia-smi -L; trap 'exit 0' TERM; sleep 9999 & wait"] resources: claims: - name: gpu0 resourceClaims: - name: gpu0 resourceClaimTemplateName: single-gpu nodeSelector: NodeGroupType: gpu-dra nvidia.com/gpu.present: "true" tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule"
  5. 포드를 적용하고 모니터링합니다.

    kubectl apply -f basic-gpu-pod.yaml
  6. 포드 상태를 확인합니다.

    kubectl get pod -n gpu-test1

    다음은 예상 출력의 예입니다.

    NAME READY STATUS RESTARTS AGE gpu-pod 1/1 Running 0 13m
  7. ResourceClaim 상태를 확인합니다.

    kubectl get resourceclaims -n gpu-test1

    다음은 예상 출력의 예입니다.

    NAME STATE AGE gpu-pod-gpu0-l76cg allocated,reserved 9m6s
  8. 포드 로그를 보고 GPU 정보를 확인합니다.

    kubectl logs gpu-pod -n gpu-test1

    다음은 예상 출력의 예입니다.

    GPU 0: NVIDIA L4 (UUID: GPU-da7c24d7-c7e3-ed3b-418c-bcecc32af7c5)

DRA를 사용한 고급 GPU 최적화 기술을 동적 리소스 할당을 통한 GPU 최적화 기법 보려면를 계속 진행합니다.

동적 리소스 할당을 통한 GPU 최적화 기법

최신 GPU 워크로드는 최적의 사용률과 비용 효율성을 달성하려면 정교한 리소스 관리가 필요합니다. DRA를 사용하면 다양한 사용 사례와 하드웨어 기능을 해결하는 몇 가지 고급 최적화 기술을 사용할 수 있습니다.

이러한 기술은 리소스 사용률을 크게 개선할 수 있습니다. 조직은 GPU 사용률이 기존 할당의 경우 30~40%에서 최적화된 공유 전략의 경우 80~90%로 증가한다고 보고합니다. 기술 선택은 워크로드 특성, 격리 요구 사항 및 하드웨어 기능에 따라 달라집니다.

시간 조각화로 GPU 워크로드 최적화

시간 조각화를 사용하면 여러 워크로드가 동일한 물리적 GPU에서 순차적으로 실행되도록 예약하여 GPU 컴퓨팅 리소스를 공유할 수 있습니다. 산발적인 GPU 사용량이 있는 추론 워크로드에 적합합니다.

다음 단계를 수행합니다.

  1. 라는 파일을 사용하여 시간 조각화를 ResourceClaimTemplate 위한을 정의합니다timeslicing-claim-template.yaml.

    --- apiVersion: v1 kind: Namespace metadata: name: timeslicing-gpu --- apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: timeslicing-gpu-template namespace: timeslicing-gpu spec: spec: devices: requests: - name: shared-gpu deviceClassName: gpu.nvidia.com config: - requests: ["shared-gpu"] opaque: driver: gpu.nvidia.com parameters: apiVersion: resource.nvidia.com/v1beta1 kind: GpuConfig sharing: strategy: TimeSlicing
  2. timeslicing-pod.yaml이름이 인 파일을 사용하여 시간 조각화를 사용하여 포드를 정의합니다.

    --- # Pod 1 - Inference workload apiVersion: v1 kind: Pod metadata: name: inference-pod-1 namespace: timeslicing-gpu labels: app: gpu-inference spec: restartPolicy: Never containers: - name: inference-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import time import os print(f"=== POD 1 STARTING ===") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Simulate inference workload for i in range(20): x = torch.randn(1000, 1000).cuda() y = torch.mm(x, x.t()) print(f"Pod 1 - Iteration {i+1} completed at {time.strftime('%H:%M:%S')}") time.sleep(60) else: print("No GPU available!") time.sleep(5) resources: claims: - name: shared-gpu-claim resourceClaims: - name: shared-gpu-claim resourceClaimTemplateName: timeslicing-gpu-template nodeSelector: NodeGroupType: "gpu-dra" nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule --- # Pod 2 - Training workload apiVersion: v1 kind: Pod metadata: name: training-pod-2 namespace: timeslicing-gpu labels: app: gpu-training spec: restartPolicy: Never containers: - name: training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import time import os print(f"=== POD 2 STARTING ===") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Simulate training workload with heavier compute for i in range(15): x = torch.randn(2000, 2000).cuda() y = torch.mm(x, x.t()) loss = torch.sum(y) print(f"Pod 2 - Training step {i+1}, Loss: {loss.item():.2f} at {time.strftime('%H:%M:%S')}") time.sleep(5) else: print("No GPU available!") time.sleep(60) resources: claims: - name: shared-gpu-claim-2 resourceClaims: - name: shared-gpu-claim-2 resourceClaimTemplateName: timeslicing-gpu-template nodeSelector: NodeGroupType: "gpu-dra" nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
  3. 템플릿과 포드를 적용합니다.

    kubectl apply -f timeslicing-claim-template.yaml kubectl apply -f timeslicing-pod.yaml
  4. 리소스 클레임 모니터링:

    kubectl get resourceclaims -n timeslicing-gpu -w

    다음은 예 출력입니다.

    NAME STATE AGE inference-pod-1-shared-gpu-claim-9p97x allocated,reserved 21s training-pod-2-shared-gpu-claim-2-qghnb pending 21s inference-pod-1-shared-gpu-claim-9p97x pending 105s training-pod-2-shared-gpu-claim-2-qghnb pending 105s inference-pod-1-shared-gpu-claim-9p97x pending 105s training-pod-2-shared-gpu-claim-2-qghnb allocated,reserved 105s inference-pod-1-shared-gpu-claim-9p97x pending 105s

첫 번째 포드(inference-pod-1)

  • 상태: allocated,reserved

  • 의미: DRA가 사용 가능한 GPU를 찾아이 포드용으로 예약했습니다.

  • 포드 상태: 즉시 실행 시작

두 번째 포드(training-pod-2)

  • 상태: pending

  • 의미: DRA가 동일한 GPU에서 시간 분할을 구성할 때까지 대기

  • 포드 상태: 예약 대기 중

  • 상태는에서 pending 로 이동합니다allocated,reserved. running

MPS를 사용하여 GPU 워크로드 최적화

다중 프로세스 서비스(MPS)를 사용하면 시간 조각화보다 더 나은 격리를 통해 단일 GPU에서 여러 CUDA 컨텍스트를 동시에 실행할 수 있습니다.

다음 단계를 수행합니다.

  1. mps-claim-template.yaml이름이 인 파일을 사용하여 MPSResourceClaimTemplate용을 정의합니다.

    --- apiVersion: v1 kind: Namespace metadata: name: mps-gpu --- apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mps-gpu-template namespace: mps-gpu spec: spec: devices: requests: - name: shared-gpu deviceClassName: gpu.nvidia.com config: - requests: ["shared-gpu"] opaque: driver: gpu.nvidia.com parameters: apiVersion: resource.nvidia.com/v1beta1 kind: GpuConfig sharing: strategy: MPS
  2. mps-pod.yaml이름이 인 파일을 사용하여 MPS를 사용하여 포드를 정의합니다.

    --- # Single Pod with Multiple Containers sharing GPU via MPS apiVersion: v1 kind: Pod metadata: name: mps-multi-container-pod namespace: mps-gpu labels: app: mps-demo spec: restartPolicy: Never containers: # Container 1 - Inference workload - name: inference-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import torch.nn as nn import time import os print(f"=== INFERENCE CONTAINER STARTING ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Create inference model model = nn.Sequential( nn.Linear(1000, 500), nn.ReLU(), nn.Linear(500, 100) ).cuda() # Run inference for i in range(1, 999999): with torch.no_grad(): x = torch.randn(128, 1000).cuda() output = model(x) result = torch.sum(output) print(f"Inference Container PID {os.getpid()}: Batch {i}, Result: {result.item():.2f} at {time.strftime('%H:%M:%S')}") time.sleep(2) else: print("No GPU available!") time.sleep(60) resources: claims: - name: shared-gpu-claim request: shared-gpu # Container 2 - Training workload - name: training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import torch.nn as nn import time import os print(f"=== TRAINING CONTAINER STARTING ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Create training model model = nn.Sequential( nn.Linear(2000, 1000), nn.ReLU(), nn.Linear(1000, 500), nn.ReLU(), nn.Linear(500, 10) ).cuda() criterion = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # Run training for epoch in range(1, 999999): x = torch.randn(64, 2000).cuda() target = torch.randn(64, 10).cuda() optimizer.zero_grad() output = model(x) loss = criterion(output, target) loss.backward() optimizer.step() print(f"Training Container PID {os.getpid()}: Epoch {epoch}, Loss: {loss.item():.4f} at {time.strftime('%H:%M:%S')}") time.sleep(3) else: print("No GPU available!") time.sleep(60) resources: claims: - name: shared-gpu-claim request: shared-gpu resourceClaims: - name: shared-gpu-claim resourceClaimTemplateName: mps-gpu-template nodeSelector: NodeGroupType: "gpu-dra" nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
  3. 템플릿을 적용하고 여러 MPS 포드를 생성합니다.

    kubectl apply -f mps-claim-template.yaml kubectl apply -f mps-pod.yaml
  4. 리소스 클레임을 모니터링합니다.

    kubectl get resourceclaims -n mps-gpu -w

    다음은 예 출력입니다.

    NAME STATE AGE mps-multi-container-pod-shared-gpu-claim-2p9kx allocated,reserved 86s

이 구성은 동적 리소스 할당(DRA)을 통해 NVIDIA Multi-Process Service(MPS)를 사용한 실제 GPU 공유를 보여줍니다. GPU를 순차적으로 사용하여 워크로드가 회전하는 시간 조각화와 달리 MPS를 사용하면 두 컨테이너가 동일한 물리적 GPU에서 동시에 실행될 수 있습니다. 핵심 인사이트는 DRA MPS 공유에는 별도의 여러 포드가 아닌 단일 포드 내의 여러 컨테이너가 필요하다는 것입니다. 배포되면 DRA 드라이버는 포드ResourceClaim에 하나를 할당하고 추론 컨테이너와 훈련 컨테이너가 동시에 실행되도록 MPS를 자동으로 구성합니다.

각 컨테이너는 기본 하드웨어에 대한 액세스를 조정하는 MPS 데몬을 사용하여 자체 격리된 GPU 메모리 공간과 컴퓨팅 리소스를 가져옵니다. 다음을 수행하여 작동 중인지 확인할 수 있습니다.

  • 확인 중nvidia-smi, 두 컨테이너가 모두 동일한 GPU 디바이스를 공유하는 M+C(MPS + Compute) 프로세스로 표시됩니다.

  • 두 컨테이너의 로그를 모니터링하여 동시 실행을 입증하는 인터리브 타임스탬프를 표시합니다.

이 접근 방식은 보완 워크로드가 단일 프로세스로 활용도를 떨어뜨리지 않고 비용이 많이 드는 GPU 하드웨어를 효율적으로 공유할 수 있도록 하여 GPU 사용률을 극대화합니다.

Container1: inference-container
root@mps-multi-container-pod:/workspace# nvidia-smi Wed Jul 16 21:09:30 2025 +-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 570.158.01 Driver Version: 570.158.01 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 L4 On | 00000000:35:00.0 Off | 0 | | N/A 48C P0 28W / 72W | 597MiB / 23034MiB | 0% E. Process | | | | N/A | +-----------------------------------------+------------------------+----------------------+ +-----------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=========================================================================================| | 0 N/A N/A 1 M+C python 246MiB | +-----------------------------------------------------------------------------------------+
Container2: training-container
root@mps-multi-container-pod:/workspace# nvidia-smi Wed Jul 16 21:16:00 2025 +-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 570.158.01 Driver Version: 570.158.01 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 L4 On | 00000000:35:00.0 Off | 0 | | N/A 51C P0 28W / 72W | 597MiB / 23034MiB | 0% E. Process | | | | N/A | +-----------------------------------------+------------------------+----------------------+ +-----------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=========================================================================================| | 0 N/A N/A 1 M+C python 314MiB | +-----------------------------------------------------------------------------------------+

다중 인스턴스 GPU를 사용하여 GPU 워크로드 최적화

다중 인스턴스 GPU(MIG)는 하드웨어 수준 파티셔닝을 제공하여 전용 컴퓨팅 및 메모리 리소스를 사용하여 격리된 GPU 인스턴스를 생성합니다.

다양한 프로필에서 동적 MIG 파티셔닝을 사용하려면 NVIDIA GPU 운영자가 필요합니다. NVIDIA GPU 운영자는 MIG Manager를 사용하여 MIG 프로파일을 생성하고 P4D, P4De, P5, P6 등과 같은 GPU 인스턴스를 재부팅하여 구성 변경 사항을 적용합니다. GPU 운영자에는 노드 레이블 변경을 감시하고 적절한 MIG 구성을 자동으로 적용하는 MIG Manager 구성 요소를 통한 포괄적인 MIG 관리 기능이 포함되어 있습니다. MIG 프로필 변경이 요청되면 연산자는 모든 GPU 클라이언트를 정상적으로 종료하고, 새 파티션 지오메트리를 적용하고, 영향을 받는 서비스를 다시 시작합니다. 이 프로세스를 수행하려면 GPU 인스턴스에 대한 노드 재부팅이 필요합니다. 따라서 성공적인 MIG 배포를 위해서는 MIG Manager 구성WITH–0—REBOOT=true에서를 활성화해야 합니다.

Amazon EKS에서 MIG를 사용하려면 NVIDIA DRA 드라이버와 NVIDIA GPU 운영자가 모두 필요합니다. NVIDIA 디바이스 플러그인 및 DCGM Exporter는 NVIDIA GPU Operator의 일부이므로이 외에도 필요하지 않습니다. EKS NVIDIA AMIs는 NVIDIA 드라이버가 사전 설치되어 제공되므로 충돌을 방지하고 인스턴스에 이미 있는 최적화된 드라이버를 활용하기 위해 GPU 운영자의 드라이버 배포를 비활성화했습니다. NVIDIA DRA 드라이버는 MIG 인스턴스에 대한 동적 리소스 할당을 처리하는 반면 GPU 운영자는 전체 GPU 수명 주기를 관리합니다. 여기에는 MIG 구성, 디바이스 플러그인 기능, DCGM을 통한 모니터링 및 노드 기능 검색이 포함됩니다. 이 통합 접근 방식은 하드웨어 수준 격리 및 동적 리소스 할당 기능을 갖춘 엔터프라이즈 GPU 관리를 위한 완전한 솔루션을 제공합니다.

1단계: NVIDIA GPU 운영자 배포
  1. NVIDIA GPU Operator 리포지토리를 추가합니다.

    helm repo add nvidia https://nvidia.github.io/gpu-operator helm repo update
  2. gpu-operator-values.yaml 파일 생성:

    driver: enabled: false mig: strategy: mixed migManager: enabled: true env: - name: WITH_REBOOT value: "true" config: create: true name: custom-mig-parted-configs default: "all-disabled" data: config.yaml: |- version: v1 mig-configs: all-disabled: - devices: all mig-enabled: false # P4D profiles (A100 40GB) p4d-half-balanced: - devices: [0, 1, 2, 3] mig-enabled: true mig-devices: "1g.5gb": 2 "2g.10gb": 1 "3g.20gb": 1 - devices: [4, 5, 6, 7] mig-enabled: false # P4DE profiles (A100 80GB) p4de-half-balanced: - devices: [0, 1, 2, 3] mig-enabled: true mig-devices: "1g.10gb": 2 "2g.20gb": 1 "3g.40gb": 1 - devices: [4, 5, 6, 7] mig-enabled: false devicePlugin: enabled: true config: name: "" create: false default: "" toolkit: enabled: true nfd: enabled: true gfd: enabled: true dcgmExporter: enabled: true serviceMonitor: enabled: true interval: 15s honorLabels: false additionalLabels: release: kube-prometheus-stack nodeStatusExporter: enabled: false operator: defaultRuntime: containerd runtimeClass: nvidia resources: limits: cpu: 500m memory: 350Mi requests: cpu: 200m memory: 100Mi daemonsets: tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" nodeSelector: accelerator: nvidia priorityClassName: system-node-critical
  3. gpu-operator-values.yaml 파일을 사용하여 GPU 연산자를 설치합니다.

    helm install gpu-operator nvidia/gpu-operator \ --namespace gpu-operator \ --create-namespace \ --version v25.3.1 \ --values gpu-operator-values.yaml

    이 차트 Helm은 다음 구성 요소와 여러 MIG 프로파일을 배포합니다.

    • 디바이스 플러그인(GPU 리소스 예약)

    • DCGM Exporter(GPU 지표 및 모니터링)

    • 노드 기능 검색(NFD - 하드웨어 레이블 지정)

    • GPU 특성 검색(GFD - GPU별 레이블 지정)

    • MIG Manager(다중 인스턴스 GPU 파티셔닝)

    • Container Toolkit(GPU 컨테이너 런타임)

    • 운영자 컨트롤러(수명 주기 관리)

  4. 배포 포드를 확인합니다.

    kubectl get pods -n gpu-operator

    다음은 예 출력입니다.

    NAME READY STATUS RESTARTS AGE gpu-feature-discovery-27rdq 1/1 Running 0 3h31m gpu-operator-555774698d-48brn 1/1 Running 0 4h8m nvidia-container-toolkit-daemonset-sxmh9 1/1 Running 1 (3h32m ago) 4h1m nvidia-cuda-validator-qb77g 0/1 Completed 0 3h31m nvidia-dcgm-exporter-cvzd7 1/1 Running 0 3h31m nvidia-device-plugin-daemonset-5ljm5 1/1 Running 0 3h31m nvidia-gpu-operator-node-feature-discovery-gc-67f66fc557-q5wkt 1/1 Running 0 4h8m nvidia-gpu-operator-node-feature-discovery-master-5d8ffddcsl6s6 1/1 Running 0 4h8m nvidia-gpu-operator-node-feature-discovery-worker-6t4w7 1/1 Running 1 (3h32m ago) 4h1m nvidia-gpu-operator-node-feature-discovery-worker-9w7g8 1/1 Running 0 4h8m nvidia-gpu-operator-node-feature-discovery-worker-k5fgs 1/1 Running 0 4h8m nvidia-mig-manager-zvf54 1/1 Running 1 (3h32m ago) 3h35m
  5. MIG 예제를 테스트하기 위해 p4De 관리형 노드 그룹을 사용하여 Amazon EKS 클러스터를 생성합니다.

    apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: dra-eks-cluster region: us-east-1 version: '1.33' managedNodeGroups: # P4DE MIG Node Group with Capacity Block Reservation - name: p4de-mig-nodes amiFamily: AmazonLinux2023 instanceType: p4de.24xlarge # Capacity settings desiredCapacity: 0 minSize: 0 maxSize: 1 # Use specific subnet in us-east-1b for capacity reservation subnets: - us-east-1b # AL2023 NodeConfig for RAID0 local storage only nodeadmConfig: apiVersion: node.eks.aws/v1alpha1 kind: NodeConfig spec: instance: localStorage: strategy: RAID0 # Node labels for MIG configuration labels: nvidia.com/gpu.present: "true" nvidia.com/gpu.product: "A100-SXM4-80GB" nvidia.com/mig.config: "p4de-half-balanced" node-type: "p4de" vpc.amazonaws.com/efa.present: "true" accelerator: "nvidia" # Node taints taints: - key: nvidia.com/gpu value: "true" effect: NoSchedule # EFA support efaEnabled: true # Placement group for high-performance networking placementGroup: groupName: p4de-placement-group strategy: cluster # Capacity Block Reservation (CBR) # Ensure CBR ID matches the subnet AZ with the Nodegroup subnet spot: false capacityReservation: capacityReservationTarget: capacityReservationId: "cr-abcdefghij" # Replace with your capacity reservation ID

    NVIDIA GPU 운영자는 노드에 추가된 레이블을 nvidia.com/mig.config: "p4de-half-balanced" 사용하고 지정된 프로필로 GPU를 분할합니다.

  6. p4de 인스턴스에 로그인합니다.

  7. 다음 명령 실행:

    nvidia-smi -L

    다음 예제 출력이 표시되어야 합니다.

    [root@ip-100-64-173-145 bin]# nvidia-smi -L GPU 0: NVIDIA A100-SXM4-80GB (UUID: GPU-ab52e33c-be48-38f2-119e-b62b9935925a) MIG 3g.40gb Device 0: (UUID: MIG-da972af8-a20a-5f51-849f-bc0439f7970e) MIG 2g.20gb Device 1: (UUID: MIG-7f9768b7-11a6-5de9-a8aa-e9c424400da4) MIG 1g.10gb Device 2: (UUID: MIG-498adad6-6cf7-53af-9d1a-10cfd1fa53b2) MIG 1g.10gb Device 3: (UUID: MIG-3f55ef65-1991-571a-ac50-0dbf50d80c5a) GPU 1: NVIDIA A100-SXM4-80GB (UUID: GPU-0eabeccc-7498-c282-0ac7-d3c09f6af0c8) MIG 3g.40gb Device 0: (UUID: MIG-80543849-ea3b-595b-b162-847568fe6e0e) MIG 2g.20gb Device 1: (UUID: MIG-3af1958f-fac4-59f1-8477-9f8d08c55029) MIG 1g.10gb Device 2: (UUID: MIG-401088d2-716f-527b-a970-b1fc7a4ac6b2) MIG 1g.10gb Device 3: (UUID: MIG-8c56c75e-5141-501c-8f43-8cf22f422569) GPU 2: NVIDIA A100-SXM4-80GB (UUID: GPU-1c7a1289-243f-7872-a35c-1d2d8af22dd0) MIG 3g.40gb Device 0: (UUID: MIG-e9b44486-09fc-591a-b904-0d378caf2276) MIG 2g.20gb Device 1: (UUID: MIG-ded93941-9f64-56a3-a9b1-a129c6edf6e4) MIG 1g.10gb Device 2: (UUID: MIG-6c317d83-a078-5c25-9fa3-c8308b379aa1) MIG 1g.10gb Device 3: (UUID: MIG-2b070d39-d4e9-5b11-bda6-e903372e3d08) GPU 3: NVIDIA A100-SXM4-80GB (UUID: GPU-9a6250e2-5c59-10b7-2da8-b61d8a937233) MIG 3g.40gb Device 0: (UUID: MIG-20e3cd87-7a57-5f1b-82e7-97b14ab1a5aa) MIG 2g.20gb Device 1: (UUID: MIG-04430354-1575-5b42-95f4-bda6901f1ace) MIG 1g.10gb Device 2: (UUID: MIG-d62ec8b6-e097-5e99-a60c-abf8eb906f91) MIG 1g.10gb Device 3: (UUID: MIG-fce20069-2baa-5dd4-988a-cead08348ada) GPU 4: NVIDIA A100-SXM4-80GB (UUID: GPU-5d09daf0-c2eb-75fd-3919-7ad8fafa5f86) GPU 5: NVIDIA A100-SXM4-80GB (UUID: GPU-99194e04-ab2a-b519-4793-81cb2e8e9179) GPU 6: NVIDIA A100-SXM4-80GB (UUID: GPU-c1a1910f-465a-e16f-5af1-c6aafe499cd6) GPU 7: NVIDIA A100-SXM4-80GB (UUID: GPU-c2cfafbc-fd6e-2679-e955-2a9e09377f78)

NVIDIA GPU Operator가 P4DE 인스턴스에 p4de-half-balanced MIG 프로파일을 성공적으로 적용하여 구성된 하드웨어 수준 GPU 파티션을 생성했습니다. 파티셔닝 작동 방식은 다음과 같습니다.

GPU 운영자는 임베디드 MIG 프로파일에서이 구성을 적용했습니다.

p4de-half-balanced: - devices: [0, 1, 2, 3] # First 4 GPUs: MIG enabled mig-enabled: true mig-devices: "1g.10gb": 2 # 2x small instances (10GB each) "2g.20gb": 1 # 1x medium instance (20GB) "3g.40gb": 1 # 1x large instance (40GB) - devices: [4, 5, 6, 7] # Last 4 GPUs: Full GPUs mig-enabled: false

nvidia-smi -L 출력에서 GPU 운영자가 생성한 내용은 다음과 같습니다.

  • MIG 지원 GPUs(0-3): 하드웨어 분할됨

    • GPU 0: NVIDIA A100-SXM4-80GB

      • MIG 3g.40gb 디바이스 0 - 대규모 워크로드(40GB 메모리, 42 SMs)

      • MIG 2g.20gb 디바이스 1 - 중간 워크로드(20GB 메모리, 28 SMs)

      • MIG 1g.10gb 디바이스 2 - 소규모 워크로드(10GB 메모리, 14 SMs)

      • MIG 1g.10gb 디바이스 3 - 소규모 워크로드(10GB 메모리, 14 SMs)

    • GPU 1: NVIDIA A100-SXM4-80GB

      • MIG 3g.40gb 디바이스 0 - 동일한 파티션 레이아웃

      • MIG 2g.20gb 디바이스 1

      • MIG 1g.10gb 디바이스 2

      • MIG 1g.10gb 디바이스 3

    • GPU 2 및 GPU 3 - GPU 0 및 GPU 1과 동일한 패턴

  • 전체 GPUs(4~7): MIG 파티셔닝 없음

    • GPU 4: NVIDIA A100-SXM4-80GB – 전체 80GB GPU

    • GPU 5: NVIDIA A100-SXM4-80GB – 전체 80GB GPU

    • GPU 6: NVIDIA A100-SXM4-80GB – 전체 80GB GPU

    • GPU 7: NVIDIA A100-SXM4-80GB – 전체 80GB GPU

NVIDIA GPU 운영자가 MIG 파티션을 생성하면 NVIDIA DRA 드라이버는 이러한 하드웨어 격리 인스턴스를 자동으로 감지하여 Kubernetes에서 동적 리소스 할당에 사용할 수 있도록 합니다. DRA 드라이버는 특정 프로필(1g.10gb, 2g.20gb, 3g.40gb)이 있는 각 MIG 인스턴스를 검색하고 mig.nvidia.com 디바이스 클래스를 통해 예약 가능한 리소스로 노출합니다.

DRA 드라이버는 MIG 토폴로지를 지속적으로 모니터링하고 모든 GPUs에서 사용 가능한 인스턴스의 인벤토리를 유지합니다. 포드가를 통해 특정 MIG 프로파일을 요청하면 ResourceClaimTemplateDRA 드라이버는 사용 가능한 GPU에서 적절한 MIG 인스턴스를 지능적으로 선택하여 실제 하드웨어 수준 다중 테넌시를 활성화합니다. 이 동적 할당을 사용하면 엄격한 리소스 경계와 성능 보장을 유지하면서 동일한 물리적 GPU에서 여러 격리된 워크로드를 동시에 실행할 수 있습니다.

2단계: MIG 리소스 할당 테스트

이제 DRA가 다양한 워크로드에 MIG 인스턴스를 동적으로 할당하는 방법을 보여주는 몇 가지 예제를 실행해 보겠습니다. resourceclaimtemplates 및 테스트 포드를 배포하여 DRA 드라이버가 사용 가능한 MIG 파티션에 워크로드를 배치하는 방법을 확인하여 여러 컨테이너가 하드웨어 수준 격리로 GPU 리소스를 공유할 수 있도록 합니다.

  1. MIG를 mig-claim-template.yaml 포함하도록 생성resourceclaimtemplates:

    apiVersion: v1 kind: Namespace metadata: name: mig-gpu --- # Template for 3g.40gb MIG instance (Large training) apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mig-large-template namespace: mig-gpu spec: spec: devices: requests: - name: mig-large deviceClassName: mig.nvidia.com selectors: - cel: expression: | device.attributes['gpu.nvidia.com'].profile == '3g.40gb' --- # Template for 2g.20gb MIG instance (Medium training) apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mig-medium-template namespace: mig-gpu spec: spec: devices: requests: - name: mig-medium deviceClassName: mig.nvidia.com selectors: - cel: expression: | device.attributes['gpu.nvidia.com'].profile == '2g.20gb' --- # Template for 1g.10gb MIG instance (Small inference) apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mig-small-template namespace: mig-gpu spec: spec: devices: requests: - name: mig-small deviceClassName: mig.nvidia.com selectors: - cel: expression: | device.attributes['gpu.nvidia.com'].profile == '1g.10gb'
  2. 세 가지 템플릿을 적용합니다.

    kubectl apply -f mig-claim-template.yaml
  3. 다음 명령 실행:

    kubectl get resourceclaimtemplates -n mig-gpu

    다음은 예 출력입니다.

    NAME AGE mig-large-template 71m mig-medium-template 71m mig-small-template 71m
  4. mig-pod.yaml를 생성하여이를 활용할 여러 작업을 예약resourceclaimtemplates합니다.

    --- # ConfigMap containing Python scripts for MIG pods apiVersion: v1 kind: ConfigMap metadata: name: mig-scripts-configmap namespace: mig-gpu data: large-training-script.py: | import torch import torch.nn as nn import torch.optim as optim import time import os print(f"=== LARGE TRAINING POD (3g.40gb) ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Using GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1e9:.1f} GB") # Large model for 3g.40gb instance model = nn.Sequential( nn.Linear(2048, 1024), nn.ReLU(), nn.Linear(1024, 512), nn.ReLU(), nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 10) ).cuda() optimizer = optim.Adam(model.parameters()) criterion = nn.CrossEntropyLoss() print(f"Model parameters: {sum(p.numel() for p in model.parameters())}") # Training loop for epoch in range(100): # Large batch for 3g.40gb x = torch.randn(256, 2048).cuda() y = torch.randint(0, 10, (256,)).cuda() optimizer.zero_grad() output = model(x) loss = criterion(output, y) loss.backward() optimizer.step() if epoch % 10 == 0: print(f"Large Training - Epoch {epoch}, Loss: {loss.item():.4f}, GPU Memory: {torch.cuda.memory_allocated()/1e9:.2f}GB") time.sleep(3) print("Large training completed on 3g.40gb MIG instance") medium-training-script.py: | import torch import torch.nn as nn import torch.optim as optim import time import os print(f"=== MEDIUM TRAINING POD (2g.20gb) ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Using GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1e9:.1f} GB") # Medium model for 2g.20gb instance model = nn.Sequential( nn.Linear(1024, 512), nn.ReLU(), nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 10) ).cuda() optimizer = optim.Adam(model.parameters()) criterion = nn.CrossEntropyLoss() print(f"Model parameters: {sum(p.numel() for p in model.parameters())}") # Training loop for epoch in range(100): # Medium batch for 2g.20gb x = torch.randn(128, 1024).cuda() y = torch.randint(0, 10, (128,)).cuda() optimizer.zero_grad() output = model(x) loss = criterion(output, y) loss.backward() optimizer.step() if epoch % 10 == 0: print(f"Medium Training - Epoch {epoch}, Loss: {loss.item():.4f}, GPU Memory: {torch.cuda.memory_allocated()/1e9:.2f}GB") time.sleep(4) print("Medium training completed on 2g.20gb MIG instance") small-inference-script.py: | import torch import torch.nn as nn import time import os print(f"=== SMALL INFERENCE POD (1g.10gb) ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Using GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1e9:.1f} GB") # Small model for 1g.10gb instance model = nn.Sequential( nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 10) ).cuda() print(f"Model parameters: {sum(p.numel() for p in model.parameters())}") # Inference loop for i in range(200): with torch.no_grad(): # Small batch for 1g.10gb x = torch.randn(32, 512).cuda() output = model(x) prediction = torch.argmax(output, dim=1) if i % 20 == 0: print(f"Small Inference - Batch {i}, Predictions: {prediction[:5].tolist()}, GPU Memory: {torch.cuda.memory_allocated()/1e9:.2f}GB") time.sleep(2) print("Small inference completed on 1g.10gb MIG instance") --- # Pod 1: Large training workload (3g.40gb) apiVersion: v1 kind: Pod metadata: name: mig-large-training-pod namespace: mig-gpu labels: app: mig-large-training workload-type: training spec: restartPolicy: Never containers: - name: large-training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "/scripts/large-training-script.py"] volumeMounts: - name: script-volume mountPath: /scripts readOnly: true resources: claims: - name: mig-large-claim resourceClaims: - name: mig-large-claim resourceClaimTemplateName: mig-large-template nodeSelector: node.kubernetes.io/instance-type: p4de.24xlarge nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule volumes: - name: script-volume configMap: name: mig-scripts-configmap defaultMode: 0755 --- # Pod 2: Medium training workload (2g.20gb) - can run on SAME GPU as Pod 1 apiVersion: v1 kind: Pod metadata: name: mig-medium-training-pod namespace: mig-gpu labels: app: mig-medium-training workload-type: training spec: restartPolicy: Never containers: - name: medium-training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "/scripts/medium-training-script.py"] volumeMounts: - name: script-volume mountPath: /scripts readOnly: true resources: claims: - name: mig-medium-claim resourceClaims: - name: mig-medium-claim resourceClaimTemplateName: mig-medium-template nodeSelector: node.kubernetes.io/instance-type: p4de.24xlarge nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule volumes: - name: script-volume configMap: name: mig-scripts-configmap defaultMode: 0755 --- # Pod 3: Small inference workload (1g.10gb) - can run on SAME GPU as Pod 1 & 2 apiVersion: v1 kind: Pod metadata: name: mig-small-inference-pod namespace: mig-gpu labels: app: mig-small-inference workload-type: inference spec: restartPolicy: Never containers: - name: small-inference-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "/scripts/small-inference-script.py"] volumeMounts: - name: script-volume mountPath: /scripts readOnly: true resources: claims: - name: mig-small-claim resourceClaims: - name: mig-small-claim resourceClaimTemplateName: mig-small-template nodeSelector: node.kubernetes.io/instance-type: p4de.24xlarge nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule volumes: - name: script-volume configMap: name: mig-scripts-configmap defaultMode: 0755
  5. 세 개의 포드를 배포해야 하는이 사양을 적용합니다.

    kubctl apply -f mig-pod.yaml

    이러한 포드는 DRA 드라이버에서 예약해야 합니다.

  6. DRA 드라이버 포드 로그를 확인하면 다음과 비슷한 출력이 표시됩니다.

    I0717 21:50:22.925811 1 driver.go:87] NodePrepareResource is called: number of claims: 1 I0717 21:50:22.932499 1 driver.go:129] Returning newly prepared devices for claim '933e9c72-6fd6-49c5-933c-a896407dc6d1': [&Device{RequestNames:[mig-large],PoolName:ip-100-64-173-145.ec2.internal,DeviceName:gpu-0-mig-9-4-4,CDIDeviceIDs:[k8s.gpu.nvidia.com/device=**gpu-0-mig-9-4-4**],}] I0717 21:50:23.186472 1 driver.go:87] NodePrepareResource is called: number of claims: 1 I0717 21:50:23.191226 1 driver.go:129] Returning newly prepared devices for claim '61e5ddd2-8c2e-4c19-93ae-d317fecb44a4': [&Device{RequestNames:[mig-medium],PoolName:ip-100-64-173-145.ec2.internal,DeviceName:gpu-2-mig-14-0-2,CDIDeviceIDs:[k8s.gpu.nvidia.com/device=**gpu-2-mig-14-0-2**],}] I0717 21:50:23.450024 1 driver.go:87] NodePrepareResource is called: number of claims: 1 I0717 21:50:23.455991 1 driver.go:129] Returning newly prepared devices for claim '1eda9b2c-2ea6-401e-96d0-90e9b3c111b5': [&Device{RequestNames:[mig-small],PoolName:ip-100-64-173-145.ec2.internal,DeviceName:gpu-1-mig-19-2-1,CDIDeviceIDs:[k8s.gpu.nvidia.com/device=**gpu-1-mig-19-2-1**],}]
  7. resourceclaims를 확인하여 포드 상태를 확인합니다.

    kubectl get resourceclaims -n mig-gpu -w

    다음은 예 출력입니다.

    NAME STATE AGE mig-large-training-pod-mig-large-claim-6dpn8 pending 0s mig-large-training-pod-mig-large-claim-6dpn8 pending 0s mig-large-training-pod-mig-large-claim-6dpn8 allocated,reserved 0s mig-medium-training-pod-mig-medium-claim-bk596 pending 0s mig-medium-training-pod-mig-medium-claim-bk596 pending 0s mig-medium-training-pod-mig-medium-claim-bk596 allocated,reserved 0s mig-small-inference-pod-mig-small-claim-d2t58 pending 0s mig-small-inference-pod-mig-small-claim-d2t58 pending 0s mig-small-inference-pod-mig-small-claim-d2t58 allocated,reserved 0s

    보시다시피 모든 포드가 DRA 드라이버에 allocated,reserved 의해 보류 중에서 로 이동했습니다.

  8. 노드nvidia-smi에서를 실행합니다. 세 개의 Python 프로세서가 실행 중인 것을 확인할 수 있습니다.

    root@ip-100-64-173-145 bin]# nvidia-smi +-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 570.158.01 Driver Version: 570.158.01 CUDA Version: 12.8 | |-----------------------------------------+------------------------+----------------------+ | 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 A100-SXM4-80GB On | 00000000:10:1C.0 Off | On | | N/A 63C P0 127W / 400W | 569MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 1 NVIDIA A100-SXM4-80GB On | 00000000:10:1D.0 Off | On | | N/A 56C P0 121W / 400W | 374MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 2 NVIDIA A100-SXM4-80GB On | 00000000:20:1C.0 Off | On | | N/A 63C P0 128W / 400W | 467MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 3 NVIDIA A100-SXM4-80GB On | 00000000:20:1D.0 Off | On | | N/A 57C P0 118W / 400W | 249MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 4 NVIDIA A100-SXM4-80GB On | 00000000:90:1C.0 Off | 0 | | N/A 51C P0 77W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ | 5 NVIDIA A100-SXM4-80GB On | 00000000:90:1D.0 Off | 0 | | N/A 46C P0 69W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ | 6 NVIDIA A100-SXM4-80GB On | 00000000:A0:1C.0 Off | 0 | | N/A 52C P0 74W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ | 7 NVIDIA A100-SXM4-80GB On | 00000000:A0:1D.0 Off | 0 | | N/A 47C P0 72W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ +-----------------------------------------------------------------------------------------+ | MIG devices: | +------------------+----------------------------------+-----------+-----------------------+ | GPU GI CI MIG | Memory-Usage | Vol| Shared | | ID ID Dev | BAR1-Usage | SM Unc| CE ENC DEC OFA JPG | | | | ECC| | |==================+==================================+===========+=======================| | 0 2 0 0 | 428MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 2MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 0 3 0 1 | 71MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 0MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 0 9 0 2 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 0 10 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 1 0 0 | 107MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 0MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 5 0 1 | 71MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 0MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 13 0 2 | 161MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 2MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 14 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 1 0 0 | 107MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 0MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 5 0 1 | 289MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 2MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 13 0 2 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 14 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 1 0 0 | 107MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 0MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 5 0 1 | 71MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 0MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 13 0 2 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 14 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ +-----------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=========================================================================================| **| 0 2 0 64080 C python 312MiB | | 1 13 0 64085 C python 118MiB | | 2 5 0 64073 C python 210MiB |** +-----------------------------------------------------------------------------------------+

GB200 P6e 인스턴스를 사용하여 IMEX로 GPU 워크로드 최적화

IMEX(Internode Memory Exchange)를 사용하면 NVIDIA GB200 UltraClusters.

다음 단계를 수행합니다.

  1. imex-compute-domain.yaml이름이 인 파일을 사용하여 다중 노드 훈련을 ComputeDomain 위한를 정의합니다.

    apiVersion: resource.nvidia.com/v1beta1 kind: ComputeDomain metadata: name: distributed-training-domain namespace: default spec: numNodes: 2 channel: resourceClaimTemplate: name: imex-channel-template
  2. 이름이 인 파일을 사용하여 IMEX 채널을 사용하여 포드를 정의합니다imex-pod.yaml.

    apiVersion: v1 kind: Pod metadata: name: imex-distributed-training namespace: default labels: app: imex-training spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: nvidia.com/gpu.clique operator: Exists containers: - name: distributed-training image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["bash", "-c"] args: - | echo "=== IMEX Channel Verification ===" ls -la /dev/nvidia-caps-imex-channels/ echo "" echo "=== GPU Information ===" nvidia-smi echo "" echo "=== NCCL Test (if available) ===" python -c " import torch import torch.distributed as dist import os print(f'CUDA available: {torch.cuda.is_available()}') print(f'CUDA device count: {torch.cuda.device_count()}') if torch.cuda.is_available(): for i in range(torch.cuda.device_count()): print(f'GPU {i}: {torch.cuda.get_device_name(i)}') # Check for IMEX environment variables imex_vars = [k for k in os.environ.keys() if 'IMEX' in k or 'NVLINK' in k] if imex_vars: print('IMEX Environment Variables:') for var in imex_vars: print(f' {var}={os.environ[var]}') print('IMEX channel verification completed') " # Keep container running for inspection sleep 3600 resources: claims: - name: imex-channel-0 - name: imex-channel-1 resourceClaims: - name: imex-channel-0 resourceClaimTemplateName: imex-channel-template - name: imex-channel-1 resourceClaimTemplateName: imex-channel-template tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
    참고

    여기에는 P6e GB200 인스턴스가 필요합니다.

  3. ComputeDomain 및 템플릿을 적용하여 IMEX를 배포합니다.

    kubectl apply -f imex-claim-template.yaml kubectl apply -f imex-compute-domain.yaml kubectl apply -f imex-pod.yaml
  4. ComputeDomain 상태를 확인합니다.

    kubectl get computedomain distributed-training-domain
  5. IMEX 데몬 배포를 모니터링합니다.

    kubectl get pods -n nvidia-dra-driver -l [resource.nvidia.com/computeDomain](http://resource.nvidia.com/computeDomain)
  6. 포드에서 IMEX 채널을 확인합니다.

    kubectl exec imex-distributed-training -- ls -la /dev/nvidia-caps-imex-channels/
  7. 포드 로그 보기:

    kubectl logs imex-distributed-training

    다음은 예상 출력의 예입니다.

    === IMEX Channel Verification === total 0 drwxr-xr-x. 2 root root 80 Jul 8 10:45 . drwxr-xr-x. 6 root root 380 Jul 8 10:45 .. crw-rw-rw-. 1 root root 241, 0 Jul 8 10:45 channel0 crw-rw-rw-. 1 root root 241, 1 Jul 8 10:45 channel1

자세한 내용은 GitHub의 NVIDIA 예제를 참조하세요.