기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.
컴퓨팅 및 오토 스케일링
GPU 리소스 최적화 및 비용 관리
잘 알려진 레이블을 사용하여 GPU 요구 사항으로 워크로드 예약
다양한 GPU 특성(예: GPU, GPU 메모리)에 민감한 AI/ML 워크로드의 경우 Karpenter
예제
예를 들어 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 지원을참조하세요.
대신 EKS 가속 AMIs 및 NVIDIA GPU 운영자
NVIDIA 디바이스 플러그인이 활성 상태이고 GPUs가 올바르게 노출되었는지 확인하려면 다음을 실행합니다.
kubectl describe node | grep nvidia.com/gpu
이 명령은 nvidia.com/gpu 리소스가 노드의 용량과 할당 가능한 리소스에 있는지 확인합니다. 예를 들어 GPU가 하나 있는 노드에는가 표시되어야 합니다nvidia.com/gpu: 1. 자세한 내용은 Kubernetes GPU 예약 안내서
다양한 EC2 인스턴스 유형 사용
Kubernetes 데이터 영역 섹션에 설명된 대로 가능한 한 다양한 EC2 인스턴스 유형을 사용하는 것이 Amazon EKS의 확장성을 위한 중요한 모범 사례입니다. 이 권장 사항은 가속화된 하드웨어(예: GPUs 있는 인스턴스에도 적용됩니다. 하나의 인스턴스 유형만 사용하는 클러스터를 생성하고 리전 용량을 초과하여 노드 수를 조정하려고 하면 사용 가능한 인스턴스가 없음을 나타내는 용량 부족 오류(ICE)가 발생할 수 있습니다. 임의로 다각화하기 전에 AI/ML 워크로드의 고유한 특성을 이해하는 것이 중요합니다. EC2 인스턴스 유형 탐색기
가속 컴퓨팅 인스턴스는 단기, 중기 및 안정 상태 워크로드에 맞게 다양한 구매 모델로 제공됩니다. 예약을 피하려는 단기적이고 유연한 내결함성 워크로드의 경우 스팟 인스턴스를 살펴보세요. 용량 블록, 온디맨드 인스턴스 및 절감형 플랜을 사용하면 중장기 워크로드 기간에 가속화된 컴퓨팅 인스턴스를 프로비저닝할 수 있습니다. 원하는 구매 옵션에서 필요한 용량에 성공적으로 액세스할 가능성을 높이려면 다양한 인스턴스 유형 및 가용 영역 목록을 사용하는 것이 좋습니다. 또는 특정 구매 모델에 대해 ICEs가 발생하는 경우 다른 모델을 사용하여 다시 시도하세요.
예제 다음 예제에서는 Karpenter NodePool이 3세대(예: p3)보다 큰 G 및 P 인스턴스를 프로비저닝하도록 활성화하는 방법을 보여줍니다. 자세한 내용은 EKS 확장성 모범 사례 섹션을 참조하세요.
- key: karpenter.k8s.aws/instance-category operator: In values: ["g", "p"] # Diversifies across G-series and P-series - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["3"] # Selects instance generations greater than 3
GPUs용 스팟 인스턴스 사용에 대한 자세한 내용은 아래의 “Karpenter를 사용하여 GPUs용 Amazon EC2 스팟 인스턴스 사용 고려”를 참조하세요.
Karpenter에서 GPUs용 Amazon EC2 스팟 인스턴스 사용 고려
Amazon EC2 스팟 인스턴스를 사용하면 AWS 클라우드에서 미사용 EC2 용량을 활용할 수 있으며 온디맨드 요금에 비해 최대 90% 할인된 가격으로 사용할 수 있습니다. Amazon EC2 EC2 스팟 인스턴스를 중단할 수 있습니다. 자세한 내용은 Amazon EC2 사용 설명서의 스팟 인스턴스를 참조하세요. Amazon EC2 스팟은 내결함성, 상태 비저장 및 유연한(시간 및 인스턴스 유형) 워크로드에 적합한 선택일 수 있습니다. 스팟 인스턴스 사용 시기에 대한 자세한 내용은 EC2 스팟 인스턴스 모범 사례를 참조하세요. 스팟 친화적인 경우 AI/ML 워크로드에 스팟 인스턴스를 사용할 수도 있습니다.
사용 사례
스팟 친화적 워크로드는 빅 데이터, 컨테이너화된 워크로드, CI/CD, 상태 비저장 웹 서버, 고성능 컴퓨팅(HPC) 및 렌더링 워크로드일 수 있습니다. 스팟 인스턴스는 인스턴스 노드 간에 유연하지 않거나, 상태 저장, 내결함성 또는 긴밀하게 결합된 워크로드(예: 컴퓨팅을 위해 서로 크게 의존하는 병렬 프로세스가 있는 워크로드로, 컴퓨팅 유체 역학 또는 복잡한 상호 종속성이 있는 분산 데이터베이스와 같은 MPI 기반 고성능 컴퓨팅 애플리케이션과 같은 일정한 노드 간 통신이 필요함)에는 적합하지 않습니다. 다음은 권장되는 특정 사용 사례입니다(특정 순서 없음).
-
실시간 온라인 추론: 워크로드가 스팟 친화적인 한 실시간 추론 워크로드의 비용 최적화 조정에 스팟 인스턴스를 사용합니다. 즉, 추론 시간은 2분 미만이고, 애플리케이션은 중단에 대한 내결함성이 있으며, 다양한 인스턴스 유형에서 실행될 수 있습니다. 잠재적인 스팟 중단을 처리하기 위해 애플리케이션 수준의 내결함성을 구현하면서 인스턴스 다양성(예: 여러 인스턴스 유형 및 가용 영역) 또는 예약을 통해 고가용성을 보장합니다.
-
하이퍼 파라미터 튜닝: 특히 단기 실험의 경우 중단이 큰 손실 없이 허용될 수 있으므로 스팟 인스턴스를 사용하여 탐색 튜닝 작업을 기회적으로 실행합니다.
-
데이터 증강: 스팟 인스턴스를 사용하여 중단될 경우 체크포인트에서 다시 시작할 수 있는 데이터 사전 처리 및 증강 작업을 수행하여 스팟의 변수 가용성에 이상적입니다.
-
모델 미세 조정: 강력한 체크포인트 메커니즘을 사용하여 스팟 인스턴스를 미세 조정하여 마지막으로 저장된 상태에서 재개하여 인스턴스 중단의 영향을 최소화합니다.
-
배치 추론: 스팟 인스턴스를 사용하여 작업을 일시 중지하고 재개할 수 있는 non-real-time 방식으로 대량의 오프라인 추론 요청을 처리하여 스팟의 비용 절감과 가장 잘 일치하고 재시도 또는 다각화를 통해 잠재적 중단을 처리합니다.
-
기회 훈련 하위 집합: 중단이 허용되고 인스턴스 유형 또는 리전 간 다각화와 같은 효율성 최적화를 적용할 수 있는 한계 또는 실험 훈련 워크로드(예: 파라미터가 1천만 개 미만인 소형 모델)에 스팟 인스턴스를 사용합니다. 단, 잠재적 중단으로 인한 프로덕션 규모 훈련에는 권장되지 않습니다.
고려 사항
Amazon EKS에서 가속화된 워크로드에 스팟 인스턴스를 사용하려면 다음과 같은 여러 가지 주요 고려 사항이 있습니다(특정 순서 없음).
-
Karpenter를 사용하여 고급 통합이 활성화된 스팟 인스턴스를 관리합니다. Karpenter NodePool에서 karpenter.sh/capacity-type "스팟"으로 지정하면 Karpenter는 추가 구성 없이 기본적으로 스팟 인스턴스를 프로비저닝합니다. 그러나 활용도가 낮은 스팟 노드를 저렴한 스팟 대안으로 대체하는 고급 Spot-to-Spot 통합을 활성화하려면 Karpenter 컨트롤러 인수에서 --feature-gates SpotToSpotConsolidation=true를 설정하거나 FEATURE_GATES 환경 변수를 통해 SpotToSpotConsolidation 기능 게이트
를 활성화해야 합니다. Karpenter는 price-capacity-optimized 할당 전략을 사용하여 EC2 인스턴스를 프로비저닝합니다. NodePool 요구 사항 및 포드 제약 조건에 따라 Karpenter 빈은 예약할 수 없는 포드를 압축하고 다양한 인스턴스 유형 세트를 Amazon EC2 플릿 API로 전송합니다. EC2 인스턴스 유형 탐색 기 도구를 사용하여 특정 컴퓨팅 요구 사항에 맞는 인스턴스 유형 목록을 생성할 수 있습니다. -
워크로드가 상태 비저장, 내결함성 및 유연성을 갖추었는지 확인합니다. 워크로드는 상태 비저장, 내결함성, 인스턴스/GPU 크기 측면에서 유연해야 합니다. 이를 통해 스팟 중단 후 원활하게 재개할 수 있으며 인스턴스 유연성을 통해 잠재적으로 스팟을 더 오래 유지할 수 있습니다. 스팟 중단 이벤트를 포착하기 위해 AWS SQS 대기열의 이름으로 settings.interruptionQueue Helm 값을 구성하여 Karpenter에서 스팟 중단 처리를
활성화합니다.interruptionQueue 예를 들어 Helm을 통해 설치할 때는 --set "settings.interruptionQueue=${CLUSTER_NAME}"을 사용합니다. 예를 보려면 Karpenter 시작하기 안내서를 참조하세요. Karpenter는 스팟 중단 이벤트를 발견하면 중단 이벤트 전에 노드(들)를 자동으로 차단, 테인트, 드레이닝 및 종료하여 포드의 종료 유예 기간을 극대화합니다. 동시에 Karpenter는 가능한 한 빨리 준비할 수 있도록 새 노드를 즉시 시작합니다. -
인스턴스 유형 선택을 지나치게 제한하지 마세요. 인스턴스 유형을 최대한 제한해서는 안 됩니다. 인스턴스 유형을 제한하지 않으면 더 낮은 비용으로 스팟 인스턴스 중단 빈도를 낮추면서 대규모로 스팟 용량을 확보할 가능성이 더 높습니다. 예를 들어 특정 유형(예: g5.xlarge)으로 제한하지 마세요. karpenter.k8s.aws/instance-category 및 karpenter.k8s.aws/instance-generation과 같은 키를 사용하여 다양한 인스턴스 범주 및 세대 집합을 지정하는 것이 좋습니다. Karpenter를 사용하면 여러 인스턴스 유형 및 가용 영역(AZs. 또한 AI/ML 워크로드에 특정 또는 제한된 수의 액셀러레이터가 필요하지만 리전 간에 유연한 경우 스팟 배치 점수를 사용하여 시작 전에 워크로드를 배포할 최적의 리전을 동적으로 식별할 수 있습니다.
-
더 많은 수의 유사한 EC2 인스턴스 패밀리를 포함하도록 NodePool 요구 사항을 확장합니다. 모든 스팟 인스턴스 풀은 특정 가용 영역(AZ)의 특정 인스턴스 유형에 대한 미사용 EC2 인스턴스 용량으로 구성됩니다. Karpenter는 새 노드를 프로비저닝하려고 할 때 NodePool의 요구 사항과 일치하는 인스턴스 유형을 선택합니다. 호환되는 인스턴스 유형이 AZ에서 스팟 용량을 가지고 있지 않으면 프로비저닝이 실패합니다. 이 문제를 방지하려면 GPU 메모리 또는 Ray Tracing과 같은 하드웨어 요구 사항을 고려하면서 크기 및 가용 영역(AZs)를 허용하세요. 인스턴스는 유형이 다를 수 있으므로 워크로드가 각 유형에서 실행될 수 있고 얻는 성능이 요구 사항을 충족하는지 확인해야 합니다.
-
리전의 모든 가용 영역을 활용합니다. 가용 용량은 가용 영역(AZ)에 따라 다르며, 특정 인스턴스 유형은 한 AZ에서는 사용할 수 없지만 다른 AZ에서는 풍부할 수 있습니다. 인스턴스 유형과 가용 영역의 각 고유한 조합은 별도의 스팟 용량 풀을 구성합니다. Karpenter NodePool 요구 사항 내 리전의 모든 AZs에서 용량을 요청하면 한 번에 더 많은 풀을 효과적으로 검색할 수 있습니다. 이렇게 하면 스팟 용량 풀 수가 최대화되어 스팟 용량을 획득할 확률이 높아집니다. 이를 위해 NodePool 구성에서 topology.kubernetes.io/zone 키를 완전히 생략하여 Karpenter가 리전에서 사용 가능한 모든 AZs 중에서 선택할 수 있도록 하거나 연산자를 사용하여에 AZs를 명시적으로 나열하고 값(예: us-west-2a)을 제공합니다.
-
스팟 배치 점수(SPS)를 사용하여 스팟 인스턴스를 사용하여 필요한 용량에 성공적으로 액세스할 가능성을 파악할 수 있습니다. 스팟 배치 점수(SPS)는 스팟 요청이 성공할 가능성을 평가하는 데 도움이 되는 점수를 제공하는 도구입니다. SPS를 사용하는 경우 먼저 스팟 인스턴스에 대한 컴퓨팅 요구 사항을 지정한 다음 Amazon EC2는 스팟 요청이 성공할 가능성이 있는 상위 10개 리전 또는 가용 영역(AZs)을 반환합니다. 리전 및 가용 영역은 1~10의 척도로 점수가 매겨집니다. 10점은 스팟 요청이 성공할 가능성이 높지만 성공이 보장되지 않음을 나타냅니다. 1점은 스팟 요청이 성공할 가능성이 전혀 없음을 나타냅니다. 다른 리전 또는 가용 영역에 대해 동일한 점수가 반환될 수 있습니다. 자세한 내용은 AWS에서 스팟 배치 점수 추적기 대시보드 구축 지침을 참조하세요. 스팟 용량이 항상 변동함에 따라 SPS는 워크로드 제약 조건(예: 유연성, 성능, 크기 등)에 가장 적합한 인스턴스 유형, AZs 및 리전 조합을 식별하는 데 도움이 됩니다. AI/ML 워크로드에 특정 또는 제한된 수의 액셀러레이터가 필요하지만 리전 간에 유연한 경우 스팟 배치 점수를 사용하여 시작 전에 워크로드를 배포할 최적의 리전을 동적으로 식별할 수 있습니다. 스팟 용량을 확보할 가능성을 자동으로 파악할 수 있도록 SPS 트래커 대시보드 구축에 대한 지침을 제공합니다. 이 솔루션은 다양한 설정(예: GPUs)에 대한 YAML 구성을 사용하여 시간 경과에 따른 SPS 점수를 모니터링하고, CloudWatch에 지표를 저장하고, 구성을 비교하기 위한 대시보드를 제공합니다. 워크로드당 대시보드를 정의하여 vCPU, 메모리 및 GPU 요구 사항을 평가하여 다른 AWS 리전 사용을 고려하는 등 EKS 클러스터에 대한 최적의 설정을 보장합니다. 자세한 내용은 스팟 배치 점수 작동 방식을 참조하세요.
-
스팟 중단을 원활하게 처리하고 테스트합니다. 종료 기간이 2분보다 긴 포드의 경우 해당 포드가 다시 예약되기 전에 이전 노드가 중단되어 워크로드 가용성에 영향을 미칠 수 있습니다. 애플리케이션을 설계할 때 2분 스팟 중단 공지를 고려하세요. 장기 실행 애플리케이션에서 체크포인트 구현(예: 중단 후 재개하기 위해 Amazon S3)와 같은 영구 스토리지에 진행 상황 저장 Pod 사양에서 terminationGracePeriodSeconds(기본값은 30초)를 확장하여 정상적인 종료를 위해 더 많은 시간을 허용합니다. 정리와 같은 정상적인 종료 활동을 위해 애플리케이션 내에서 preStop 수명 주기 후크 및/또는 SIGTERM 신호를 사용하여 중단을 처리합니다. 상태 저장, 및 연결 종료. 규모 조정 시간이 중요하고 애플리케이션이 트래픽을 처리할 준비가 되는 데 2분 이상 걸리는 실시간 워크로드의 경우 스토리지 및 애플리케이션 조정 및 성능 모범 사례를 검토하여 컨테이너 시작 및 ML 모델 로드 시간을 최적화하는 것이 좋습니다. 대체 노드를 테스트하려면 AWS Fault Injection Service
(FIS)를 사용하여 스팟 중단을 시뮬레이션합니다.
이러한 핵심 스팟 모범 사례 외에도 Amazon EKS에서 GPU 워크로드를 관리할 때 이러한 요소를 고려하세요. CPU 기반 워크로드와 달리 GPU 워크로드는 GPU 기능 및 사용 가능한 GPU 메모리와 같은 하드웨어 세부 정보에 특히 민감합니다. GPU 워크로드는 CPU에 비해 사용 가능한 옵션이 적어 사용할 수 있는 인스턴스 유형에 따라 제한될 수 CPUs. 첫 번째 단계로 워크로드가 인스턴스에 유연한지 평가합니다. 워크로드에서 사용할 수 있는 인스턴스 유형 수를 모르는 경우 개별적으로 테스트하여 호환성과 기능을 확인합니다. 다각화가 워크로드를 계속 작동하도록 하고 성능 영향(예: 처리량 또는 완료 시간)을 이해하면서 최대한 유연하게 다각화할 수 있는 방법을 식별합니다. 워크로드 다각화의 일환으로 다음 사항을 고려하세요.
-
CUDA 및 프레임워크 호환성을 검토합니다. GPU 워크로드는 특정 하드웨어, GPU 유형(예: p3의 V100과 p4의 A100)에 최적화되거나 TensorFlow와 같은 라이브러리의 특정 CUDA 버전에 맞게 작성될 수 있으므로 워크로드의 호환성을 검토해야 합니다. 이러한 호환성은 런타임 오류, 충돌, GPU 가속화 장애(예: PyTorch 또는 TensorFlow와 같은 프레임워크와 일치하지 않는 CUDA 버전이 실행을 방해할 수 있음) 또는 FP16/INT8 정밀도와 같은 하드웨어 기능을 활용하는 기능을 방지하는 데 매우 중요합니다.
-
GPU 메모리. DCGM Exporter
와 같은 도구를 사용하여 모델의 메모리 요구 사항을 평가하고 런타임 중에 모델의 메모리 사용량을 프로파일링하고 karpenter.k8s.aws/instance-gpu-memory와 같은 잘 알려진 레이블에서 인스턴스 유형에 필요한 최소 GPU 메모리를 설정해야 합니다. GPU VRAM은 인스턴스 유형마다 다르며(예: NVIDIA T4는 16GB, A10G는 24GB, V100은 16-32GB), ML 모델(예: 대규모 언어 모델)은 사용 가능한 메모리를 초과하여 out-of-memory(OOM) 오류 또는 충돌을 일으킬 수 있습니다. EKS의 스팟 인스턴스의 경우 다각화를 제한할 수 있습니다. 예를 들어 모델이 맞지 않으면 하위 VRAM 유형을 포함할 수 없으므로 용량 풀에 대한 액세스가 제한되고 중단 위험이 증가할 수 있습니다. 단일 GPU, 단일 노드 추론(예: GPU 리소스를 활용하기 위해 동일한 노드에 예약된 여러 포드)의 경우 스팟 구성에 충분한 VRAM이 있는 인스턴스 유형만 포함할 수 있으므로 다각화가 제한될 수 있습니다. -
부동 소수점 정밀도 및 성능. 모든 Nvidia GPU 아키텍처의 부동 소수점 정밀도가 동일한 것은 아닙니다(예: FP16/INT8). 워크로드에 필요한 코어 유형(CUDA/Tensor/RT) 성능과 부동 소수점 정밀도를 평가합니다. 가격이 저렴하고 성능이 낮은 GPU에서 실행하는 것이 더 낫다는 의미는 아니므로, 다각화의 영향을 이해하려면 특정 기간 내에 완료된 작업 측면에서 성능을 평가하는 것이 좋습니다.
시나리오: 실시간 추론 워크로드의 다각화
스팟 인스턴스의 실시간 온라인 추론 워크로드의 경우 호환되는 GPU 인스턴스 패밀리 및 세대를 다각화하도록 Karpenter NodePool을 구성할 수 있습니다. 이 접근 방식은 GPU 기능, 메모리 및 아키텍처에 대한 제약을 통해 성능을 유지하면서 여러 스팟 풀에서 끌어와 고가용성을 보장합니다. 인스턴스 용량이 제한될 때 대안을 사용하여 중단을 최소화하고 추론 지연 시간을 최적화할 수 있습니다. 이 예제 NodePool 상태는 GPU 메모리가 20GB 이상인 3보다 큰 g 및 p 시리즈 인스턴스를 사용합니다.
예제
apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-inference-spot spec: template: metadata: labels: role: gpu-spot-worker spec: requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot"] # Use Spot Instances - key: karpenter.k8s.aws/instance-category operator: In values: ["g", "p"] # Diversifies across G-series and P-series - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["3"] # Selects instance generations greater than 3 - key: kubernetes.io/arch operator: In values: ["amd64"] # Specifies AMD64 architecture, compatible with NVIDIA GPUs - key: karpenter.k8s.aws/instance-gpu-memory operator: Gt values: ["20480"] # Ensures more than 20GB (20480 MiB) total GPU memory taints: - key: nvidia.com/gpu effect: NoSchedule nodeClassRef: name: gpu-inference-ec2 group: karpenter.k8s.aws kind: EC2NodeClass expireAfter: 720h limits: cpu: 100 memory: 100Gi disruption: consolidationPolicy: WhenEmptyOrUnderutilized consolidateAfter: 5m # Enables consolidation of underutilized nodes after 5 minutes
장기 실행 훈련 작업에 대한 체크포인트 구현
체크포인트는 프로세스 상태를 주기적으로 저장하여 중단 시 마지막으로 저장된 지점에서 재개할 수 있도록 하는 내결함성 기법입니다. 기계 학습에서는 일반적으로 장기 실행 작업이 모델 가중치와 옵티마이저 상태를 저장하여 하드웨어 문제 또는 스팟 인스턴스 중단과 같은 장애 후 훈련을 재개할 수 있는 훈련과 관련이 있습니다.
체크포인트를 사용하여 훈련 중에 기계 학습(ML) 모델의 상태를 저장합니다. 체크포인트는 모델의 스냅샷이며 ML 프레임워크의 콜백 함수로 구성할 수 있습니다. 저장된 체크포인트를 사용하여 마지막으로 저장된 체크포인트에서 훈련 작업을 다시 시작할 수 있습니다. 체크포인트를 사용하면 훈련 작업 또는 인스턴스가 예기치 않게 중단되어 모델 스냅샷을 훈련 중에 저장할 수 있습니다. 이렇게 하면 향후 체크포인트에서 모델 훈련을 재개할 수 있습니다. 노드 복원력 시스템을 구현하는 것 외에도 하드웨어 장애 또는 Amazon EC2 스팟 인스턴스 중단으로 인한 중단을 포함하여 중단의 영향을 완화하기 위해 체크포인트를 구현하는 것이 좋습니다.
체크포인트를 사용하지 않으면 중단으로 인해 컴퓨팅 시간이 낭비되고 진행 상황이 손실되어 장기 실행 훈련 작업에 비용이 많이 들 수 있습니다. 체크포인트를 사용하면 작업이 주기적으로 상태(예: 모델 가중치 및 최적화 프로그램 상태)를 저장하고 중단 후 마지막 체크포인트(마지막으로 처리된 배치)에서 재개할 수 있습니다. 체크포인트를 구현하려면 대규모 배치로 데이터를 처리하고 훈련 작업이 진행되는 동안 Mountpoint for Amazon S3 CSI 드라이버를 통해 Amazon S3 버킷과 같은 영구 스토리지에 중간 결과를 저장하도록 애플리케이션을 설계합니다. Amazon S3
사용 사례
체크포인트는 특정 시나리오에서 내결함성과 성능 오버헤드의 균형을 맞추는 데 특히 유용합니다. 다음과 같은 경우 체크포인트를 사용하는 것이 좋습니다.
-
작업 기간이 몇 시간을 초과함: 장기 실행 훈련 작업(예: 소규모 모델의 경우 >1~2시간 또는 수십억 개의 파라미터가 있는 대규모 파운데이션 모델의 경우 일/주)의 경우 중단으로 인한 진행 상황 손실에 비용이 많이 듭니다. 작업이 짧을수록 I/O 오버헤드가 정당화되지 않을 수 있습니다.
-
스팟 인스턴스 또는 하드웨어 장애의 경우: EC2 스팟(2분 알림) 또는 하드웨어 장애(예: GPU 메모리 오류)와 같이 중단되기 쉬운 환경에서 체크포인트를 사용하면 빠르게 재개할 수 있으므로 내결함성 워크로드에서 비용을 절감할 수 있습니다.
-
대규모 분산 훈련: 수백/수만 개의 액셀러레이터(예: >100 GPUs)가 있는 설정의 경우, 장애 사이의 평균 시간이 규모에 따라 선형적으로 감소합니다. 모델/데이터 병렬 처리를 위해를 사용하여 동시 체크포인트 액세스를 처리하고 완전한 재시작을 방지합니다.
-
리소스 요구가 높은 대규모 모델: 클러스터 크기로 인해 장애가 불가피한 페타바이트 규모의 LLM 훈련에서 계층형 접근 방식(임시 장애의 경우 5~30분마다 빠른 로컬, 주요 장애의 경우 시간당 내구성)은 복구 시간과 효율성을 최적화합니다.
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은 KarpentercapacityReservationSelectorTerms 필드를 사용하도록 NodeClass를 구성합니다. Karpenter NodePools 설명서를
기타 가속화된 인스턴스 유형 및 크기 고려
Amazon EKS의 ML 워크로드에서 성능과 비용을 모두 최적화하려면 적절한 가속화된 인스턴스와 크기를 선택해야 합니다. 예를 들어 GPU 인스턴스 패밀리마다 GPU 메모리와 같은 성능과 기능이 다릅니다. 가장 경제적인 옵션을 선택하는 데 도움이 되도록 가속 컴퓨팅의 EC2 인스턴스 유형 페이지에서 사용 가능한 GPU 인스턴스를
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 디바이스 플러그인을 사용하여 시간 조각화를 활성화하려면:
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
예제
예를 들어 분수 GPU 할당에 KAI 스케줄러를 사용하려면 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 통합 정책을
WhenEmptyOrUnderutilized 통합 정책은 노드를 조기에 종료하여 실행 시간이 길어질 수 있습니다. 예를 들어, 중단으로 인해 포드 다시 예약, 데이터 다시 로드로 인해 작업 재개가 지연될 수 있으며, 이는 장기 실행 배치 추론 작업에 비용이 많이 들 수 있습니다. 이를 완화하기 위해를 consolidationPolicy 로 설정하고 워크로드 급증 중에 노드를 유지하도록 1시간과 같은 consolidateAfter 기간을 WhenEmpty 구성할 수 있습니다. 예:
disruption: consolidationPolicy: WhenEmpty consolidateAfter: 60m
이 접근 방식은 중단 비용이 컴퓨팅 비용 절감을 능가하는 실시간 온라인 추론 데이터 처리 또는 모델 훈련과 같이 급증하는 배치 추론 워크로드 및 기타 중단에 민감한 작업의 포드 시작 지연 시간을 개선합니다. Karpenter NodePool 중단 예산
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
-
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)
-
세분화된 GPU 할당
-
다중 프로세스 서비스(MPS) 및 다중 인스턴스 GPU(MIG)와 같은 고급 공유 메커니즘
-
NVIDIA GB200 UltraServers를 포함한 차세대 하드웨어 아키텍처 지원
기존 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 UltraServers의 노드 간 메모리 일관성 통신.
추가 리소스
Kubernetes DRA 및 NVIDIA DRA 드라이버에 대한 자세한 내용은 GitHub의 다음 리소스를 참조하세요.
고급 GPU 관리를 위한 동적 리소스 할당 설정
다음 주제에서는 고급 GPU 관리를 위해 동적 리소스 할당(DRA)을 설정하는 방법을 보여줍니다.
사전 조건
Amazon EKS에서 DRA를 구현하기 전에 환경이 다음 요구 사항을 충족하는지 확인합니다.
클러스터 구성
-
버전
1.33이상을 실행하는 Amazon EKS 클러스터 -
Amazon EKS 관리형 노드 그룹(DRA는 현재 Karpenter가 아닌
AL2023 및 Bottlerocket NVIDIA 최적화 AMIs 있는 관리형 노드 그룹에서만 지원됨) -
적절한 인스턴스 유형이 있는 NVIDIA GPU 지원 작업자 노드
필수 구성 요소
-
NVIDIA 디바이스 플러그인 버전
0.17.1이상 -
NVIDIA DRA 드라이버 버전
25.3.0이상
1단계: eksctl을 사용하여 DRA 지원 노드 그룹으로 클러스터 생성
-
라는 클러스터 구성 파일을 생성합니다
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 -
클러스터를 생성합니다.
eksctl create cluster -f dra-eks-cluster.yaml
2단계: NVIDIA 디바이스 플러그인 배포
NVIDIA 디바이스 플러그인을 배포하여 기본 GPU 검색을 활성화합니다.
-
NVIDIA 디바이스 플러그인 Helm 리포지토리를 추가합니다.
helm repo add nvidia https://nvidia.github.io/k8s-device-plugin helm repo update -
디바이스 플러그인에 대한 사용자 지정 값을 생성합니다.
cat <<EOF > nvidia-device-plugin-values.yaml gfd: enabled: true nfd: enabled: true tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule EOF -
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 차트 배포
-
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 -
NVIDIA NGC Helm 리포지토리를 추가합니다.
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia helm repo update -
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 설치 확인
-
DRA API 리소스를 사용할 수 있는지 확인합니다.
kubectl api-resources | grep resource.k8s.io/v1beta1예상 출력은 다음과 같습니다.
deviceclasses resource.k8s.io/v1beta1 false DeviceClass resourceclaims resource.k8s.io/v1beta1 true ResourceClaim resourceclaimtemplates resource.k8s.io/v1beta1 true ResourceClaimTemplate resourceslices resource.k8s.io/v1beta1 false ResourceSlice -
사용 가능한 디바이스 클래스를 확인합니다.
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 관리를 위한 동적 리소스 할당 설정.
-
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 -
템플릿을 적용합니다.
kubectl apply -f basic-gpu-claim-template.yaml -
상태를 확인합니다.
kubectl get resourceclaimtemplates -n gpu-test1다음은 예 출력입니다.
NAME AGE single-gpu 9m16s -
이름이 인 파일과
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" -
포드를 적용하고 모니터링합니다.
kubectl apply -f basic-gpu-pod.yaml -
포드 상태를 확인합니다.
kubectl get pod -n gpu-test1다음은 예상 출력의 예입니다.
NAME READY STATUS RESTARTS AGE gpu-pod 1/1 Running 0 13m -
ResourceClaim상태를 확인합니다.kubectl get resourceclaims -n gpu-test1다음은 예상 출력의 예입니다.
NAME STATE AGE gpu-pod-gpu0-l76cg allocated,reserved 9m6s -
포드 로그를 보고 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 컴퓨팅 리소스를 공유할 수 있으므로 산발적인 GPU 사용량이 있는 추론 워크로드에 적합합니다. 예제는 시간 조각화로 GPU 워크로드 최적화 섹션을 참조하세요.
-
다중 프로세스 서비스(MPS)를 사용하면 시간 조각화보다 더 나은 격리를 통해 단일 GPU에서 여러 CUDA 프로세스를 동시에 실행할 수 있습니다. 예제는 MPS를 사용하여 GPU 워크로드 최적화 섹션을 참조하세요.
-
다중 인스턴스 GPU(MIG)는 하드웨어 수준 파티셔닝을 제공하여 전용 컴퓨팅 및 메모리 리소스를 사용하여 격리된 GPU 인스턴스를 생성합니다. 예제는 다중 인스턴스 GPU를 사용하여 GPU 워크로드 최적화 섹션을 참조하세요.
-
Internode Memory Exchange(IMEX)를 사용하면 NVIDIA GB200 시스템에서 분산 훈련을 위해 노드 간 메모리 일관성 통신을 사용할 수 있습니다. 예제는 GB200 P6e 인스턴스를 사용하여 IMEX로 GPU 워크로드 최적화 섹션을 참조하세요.
이러한 기술은 리소스 사용률을 크게 개선할 수 있습니다. 조직은 GPU 사용률이 기존 할당의 경우 30~40%에서 최적화된 공유 전략의 경우 80~90%로 증가한다고 보고합니다. 기술 선택은 워크로드 특성, 격리 요구 사항 및 하드웨어 기능에 따라 달라집니다.
시간 조각화로 GPU 워크로드 최적화
시간 분할을 사용하면 여러 워크로드가 동일한 물리적 GPU에서 순차적으로 실행되도록 예약하여 GPU 컴퓨팅 리소스를 공유할 수 있습니다. 산발적인 GPU 사용량이 있는 추론 워크로드에 적합합니다.
다음 단계를 수행합니다.
-
라는 파일을 사용하여 시간 조각화를
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 -
라는 파일을 사용하여 시간 조각을 사용하여 포드를 정의합니다
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 -
템플릿과 포드를 적용합니다.
kubectl apply -f timeslicing-claim-template.yaml kubectl apply -f timeslicing-pod.yaml -
리소스 클레임 모니터링:
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 컨텍스트를 동시에 실행할 수 있습니다.
다음 단계를 수행합니다.
-
이름이 인 파일을 사용하여 MPS
ResourceClaimTemplate용을 정의합니다mps-claim-template.yaml.--- 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 -
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 -
템플릿을 적용하고 여러 MPS 포드를 생성합니다.
kubectl apply -f mps-claim-template.yaml kubectl apply -f mps-pod.yaml -
리소스 클레임을 모니터링합니다.
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 운영자WITH0—REBOOT=true에서를 활성화해야 합니다.
Amazon EKS에서 MIG를 사용하려면 NVIDIA DRA 드라이버
1단계: NVIDIA GPU 운영자 배포
-
NVIDIA GPU Operator 리포지토리를 추가합니다.
helm repo add nvidia https://nvidia.github.io/gpu-operator helm repo update -
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 -
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 컨테이너 런타임)
-
운영자 컨트롤러(수명 주기 관리)
-
-
배포 포드를 확인합니다.
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 -
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 IDNVIDIA GPU 운영자는 노드에 추가된 레이블을
nvidia.com/mig.config: "p4de-half-balanced"사용하고 지정된 프로필로 GPU를 분할합니다. -
p4de인스턴스에 로그인합니다. -
다음 명령을 실행합니다.
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 리소스를 공유할 수 있도록 합니다.
-
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' -
세 가지 템플릿을 적용합니다.
kubectl apply -f mig-claim-template.yaml -
다음 명령을 실행합니다.
kubectl get resourceclaimtemplates -n mig-gpu다음은 예 출력입니다.
NAME AGE mig-large-template 71m mig-medium-template 71m mig-small-template 71m -
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 -
세 개의 포드를 배포해야 하는이 사양을 적용합니다.
kubctl apply -f mig-pod.yaml이러한 포드는 DRA 드라이버에서 예약해야 합니다.
-
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**],}] -
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의해 보류 중에서 로 이동했습니다. -
노드
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 UltraServers에서 분산 훈련을 위해 노드 간 메모리 일관성 통신을 사용할 수 있습니다.
다음 단계를 수행합니다.
-
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 -
이름이 인 파일을 사용하여 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 인스턴스가 필요합니다.
-
ComputeDomain및 템플릿을 적용하여 IMEX를 배포합니다.kubectl apply -f imex-claim-template.yaml kubectl apply -f imex-compute-domain.yaml kubectl apply -f imex-pod.yaml -
ComputeDomain상태를 확인합니다.kubectl get computedomain distributed-training-domain -
IMEX 데몬 배포를 모니터링합니다.
kubectl get pods -n nvidia-dra-driver -l resource.nvidia.com/computeDomain -
포드에서 IMEX 채널을 확인합니다.
kubectl exec imex-distributed-training -- ls -la /dev/nvidia-caps-imex-channels/ -
포드 로그 보기:
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 예제