Elastic Fabric Adapter로 Amazon EKS에서 머신러닝 학습 실행하기

Elastic Fabric Adapter로 Amazon EKS에서 머신러닝 학습 실행하기

이 주제는 Amazon EKS 클러스터에 배포된 Pod에 Elastic Fabric Adapter (EFA)를 통합하는 방법을 설명합니다. Elastic Fabric Adapter (EFA)는 AWS에서 높은 수준의 노드 간 통신이 필요한 애플리케이션을 대규모로 실행할 수 있게 해 주는 Amazon EC2 인스턴스용 네트워크 인터페이스예요. 맞춤 제작된 운영체제 바이패스 하드웨어 인터페이스는 인스턴스 간 통신 성능을 향상시키는데, 이는 이러한 애플리케이션을 확장하는 데 중요합니다. EFA를 사용하면 Message Passing Interface (MPI)를 사용하는 High Performance Computing (HPC) 애플리케이션과 NVIDIA Collective Communications Library (NCCL)를 사용하는 Machine Learning (ML) 애플리케이션을 수천 개의 CPU 또는 GPU로 확장할 수 있어요. 그 결과, 온프레미스 HPC 클러스터의 애플리케이션 성능을 AWS 클라우드의 온디맨드 탄력성과 유연성과 함께 얻을 수 있습니다. Amazon EKS 클러스터에서 실행되는 애플리케이션에 EFA를 통합하면 클러스터에 인스턴스를 추가하지 않고도 대규모 분산 학습 워크로드를 완료하는 시간을 줄일 수 있어요. EFA에 대한 자세한 내용은 Elastic Fabric Adapter를 참고하세요.

EFA DRA driver, EFA device plugin, 토폴로지 인식 할당, 네트워크 인터페이스 구성을 포함해 EKS 클러스터에서 EFA 장치를 설치·관리하는 방법은 Manage EFA devices on Amazon EKS를 참고하세요.

EFA가 있는 인스턴스 유형

AWS EFA Kubernetes Device Plugin은 EFA가 있는 모든 Amazon EC2 인스턴스 유형을 지원합니다. EFA가 있는 모든 인스턴스 유형 목록은 Amazon EC2 User Guide의 Supported instance types를 참고하세요. 다만 ML 애플리케이션을 빠르게 실행하려면 EFA에 더해 NVIDIA GPUs, AWS Inferentia 칩, AWS Trainium 칩 같은 하드웨어 가속 칩이 있는 인스턴스를 사용하는 것을 권장합니다. 하드웨어 가속 칩과 EFA가 모두 있는 인스턴스 유형 목록은 Amazon EC2 User Guide의 Accelerated computing을 참고하세요.

인스턴스 유형을 비교해서 선택할 때는 해당 인스턴스 유형에 사용 가능한 EFA 네트워크 카드 수와 가속기 카드 수, CPU 양, 메모리 양을 고려하세요. 네트워크 카드당 최대 하나의 EFA를 할당할 수 있어요. EFA는 네트워크 인터페이스로 간주됩니다. EFA가 있는 각 인스턴스 유형에서 사용 가능한 EFA 수를 보려면 Amazon EC2 User Guide의 Network cards 목록을 참고하세요.

EFA 및 EFA-only 인터페이스

Elastic Fabric Adapter (EFA)는 Elastic Network Adapter (ENA)의 기능과 OS 바이패스 인터페이스를 결합한 네트워크 인터페이스로, AWS Scalable Reliable Datagram (SRD) 프로토콜로 구동됩니다. EFA 기능을 사용하면 애플리케이션이 낮은 지연 시간 전송을 위해 하드웨어와 직접 통신할 수 있어요. EFA-only 인터페이스를 사용해 EFA 기능에만 접근하도록 선택할 수 있으며, 통신을 같은 가용 영역 안의 인터페이스로 제한할 수 있습니다.

EFA-only 인터페이스를 가질 수 있는 노드를 만들려면 사용자 정의 EC2 Launch Template을 사용하고 InterfaceType을 efa-only로 설정해야 해요. 사용자 정의 Launch Template에서는 네트워크 카드 0을 EFA-only 인터페이스로 설정할 수 없는데, 이는 EC2 인스턴스의 기본 네트워크 카드이자 네트워크 인터페이스이기 때문입니다. EFA-only 인터페이스에는 VPC CNI 버전 1.18.5 이상이 필요해요. Amazon Linux 2를 사용한다면 EFA-only 인터페이스에는 ami 버전 v20240928 이상이어야 합니다.

다음 절차는 NVIDIA GPU와 EFA 인터페이스가 있는 노드로 eksctl을 사용해 EKS 클러스터를 만드는 방법을 안내합니다. EFA-only 인터페이스를 사용하는 노드와 노드 그룹을 만드는 데에는 eksctl을 사용할 수 없어요.

사전 조건 (Prerequisites)

  • 기존 Amazon EKS 클러스터. 기존 클러스터가 없다면 Get started with Amazon EKS로 하나 만드세요. 클러스터는 노드를 배포할 수 있을 만큼 충분한 사용 가능한 IP 주소가 있는 개인 서브넷이 하나 이상 있는 VPC에 배포되어야 합니다. 개인 서브넷은 NAT 게이트웨이 같은 외부 장치가 제공하는 아웃바운드 인터넷 접근이 있어야 해요.
  • 노드 그룹을 만드는 데 eksctl을 사용할 계획이라면 eksctl이 클러스터도 만들어 줍니다.
  • 기기나 AWS CloudShell에 AWS Command Line Interface (AWS CLI) 버전 2.12.3 이상 또는 1.27.160 이상이 설치·구성되어 있어야 합니다. 현재 버전을 확인하려면 aws --version | cut -d / -f2 | cut -d ' ' -f1을 사용하세요. yum, apt-get, macOS용 Homebrew 같은 패키지 관리자는 종종 최신 AWS CLI 버전보다 여러 버전 뒤처져 있어요. 최신 버전을 설치하려면 AWS Command Line Interface User Guide의 Installing and Quick configuration with aws configure를 참고하세요. AWS CloudShell에 설치된 AWS CLI 버전도 최신 버전보다 여러 버전 뒤처져 있을 수 있습니다. 업데이트하려면 AWS CloudShell User Guide의 Installing AWS CLI to your home directory를 참고하세요.
  • 기기나 AWS CloudShell에 kubectl 명령줄 도구가 설치되어 있어야 합니다. 버전은 클러스터의 Kubernetes 버전과 같거나 한 마이너 버전 이전·이후일 수 있어요. 예를 들어 클러스터 버전이 1.29라면 kubectl 버전 1.28, 1.29, 1.30을 함께 사용할 수 있습니다. kubectl을 설치하거나 업그레이드하려면 Set up kubectl and eksctl을 참고하세요.
  • p4d나 p5 같은 여러 Elastic Fabric Adapter를 지원하는 워커 노드를 시작하기 전에 Amazon VPC CNI plugin for Kubernetes 버전 1.7.10 이상이 설치되어 있어야 합니다. Amazon VPC CNI plugin for Kubernetes 버전 업데이트에 대한 자세한 내용은 Assign IPs to Pods with the Amazon VPC CNI를 참고하세요.
  • p6-b200 인스턴스의 경우 EFA Device Plugin 버전 v0.5.6 이상을 사용해야 해요.

중요

Kubernetes에서 EFA를 채택할 때 중요한 고려 사항은 Huge Pages를 클러스터의 리소스로 구성하고 관리하는 것입니다. 자세한 내용은 Kubernetes 문서의 Manage Huge Pages를 참고하세요. EFA 드라이버가 설치된 Amazon EC2 인스턴스는 5128개의 2MiB Huge Pages를 사전 할당하며, 이를 작업 사양에서 소비할 리소스로 요청할 수 있어요.

노드 그룹 생성

다음 절차는 EFA 인터페이스와 GPUDirect RDMA가 있는 p4d.24xlarge 기반 노드 그룹을 만들고, EFA를 사용하는 다중 노드 NCCL 성능을 위한 예제 NVIDIA Collective Communications Library (NCCL) 테스트를 실행하는 데 도움을 줍니다. 이 예제는 EFA를 사용하는 Amazon EKS에서의 분산 딥러닝 학습 템플릿으로 사용할 수 있어요.

노드를 배포하려는 AWS 리전에서 EFA를 지원하는 Amazon EC2 인스턴스 유형이 사용 가능한지 확인합니다. region-code를 노드 그룹을 배포하려는 AWS 리전으로 바꾸세요.

aws ec2 describe-instance-types --region region-code \
    --filters Name=network-info.efa-supported,Values=true \
    --query "InstanceTypes[*].[InstanceType]" --output text

노드를 배포할 때 배포하려는 인스턴스 유형은 클러스터가 있는 AWS 리전에서 사용 가능해야 합니다.

배포하려는 인스턴스 유형이 사용 가능한 가용 영역을 확인합니다. 이 튜토리얼에서는 p5.48xlarge 인스턴스 유형을 사용하며, 이전 단계에서 지정한 AWS 리전의 출력에 반환되어야 합니다. 프로덕션 클러스터에 노드를 배포할 때는 p5.48xlarge를 이전 단계에서 반환된 인스턴스 유형으로 바꾸세요.

aws ec2 describe-instance-type-offerings --region region-code \
    --location-type availability-zone --filters Name=instance-type,Values=p4d.24xlarge,p5.48xlarge \
    --query 'InstanceTypeOfferings[*].Location' --output text

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

us-west-2a    us-west-2c    us-west-2b

이후 단계에서 사용할 반환된 가용 영역을 기록해 두세요. 클러스터에 노드를 배포할 때 VPC에는 출력에서 반환된 가용 영역 중 하나에 사용 가능한 IP 주소가 있는 서브넷이 있어야 합니다.

eksctl을 사용해서 노드 그룹을 만듭니다. 기기나 AWS CloudShell에 eksctl 명령줄 도구 버전 0.215.0 이상이 필요해요. eksctl을 설치하거나 업데이트하려면 eksctl 문서의 Installation을 참고하세요.

다음 내용을 efa-cluster.yaml이라는 파일에 복사합니다. 예제 값을 자신의 값으로 바꾸세요. p5.48xlarge를 다른 인스턴스로 바꿀 수 있지만, 그렇게 하려면 availabilityZones 값이 1단계에서 해당 인스턴스 유형에 대해 반환된 가용 영역인지 확인하세요.

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: my-efa-cluster
  region: region-code
  version: "1.XX"

iam:
  withOIDC: true

availabilityZones: ["us-west-2a", "us-west-2c"]

managedNodeGroups:
  - name: my-efa-ng
    instanceType: p5.48xlarge
    minSize: 1
    desiredCapacity: 2
    maxSize: 3
    availabilityZones: ["us-west-2a"]
    volumeSize: 300
    privateNetworking: true
    efaEnabled: true

기존 클러스터에 관리형 노드 그룹을 만듭니다.

eksctl create nodegroup -f efa-cluster.yaml

기존 클러스터가 없다면 다음 명령을 실행해서 클러스터와 노드 그룹을 만들 수 있어요.

eksctl create cluster -f efa-cluster.yaml

참고

이 예제에서 사용하는 인스턴스 유형에는 GPU가 있으므로, Amazon Linux 2를 사용할 때 eksctl이 각 인스턴스에 NVIDIA Kubernetes device plugin을 자동으로 설치합니다. Bottlerocket에서는 NVIDIA device plugin이 Bottlerocket의 EKS NVIDIA 변형에 내장되어 있으므로 이는 필요하지 않아요. 노드 그룹 구성에서 efaEnabled가 true로 설정되면 eksctl은 또한 노드에 EFA device plugin을 자동으로 배포합니다.

Bottlerocket을 EFA와 함께 사용

Bottlerocket AMI 버전 1.28.0 이상은 EFA에 대한 공식 지원을 포함합니다. EFA 지원 노드에 Bottlerocket을 사용하려면 구성에 amiFamily: Bottlerocket을 지정하세요. 사용자 정의 AMI ID를 사용해야 한다면 managedNodeGroups 대신 표준 nodeGroups를 사용해야 해요.

예제 구성은 다음과 같습니다.

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: my-efa-bottlerocket-cluster
  region: region-code
  version: "1.XX"

iam:
  withOIDC: true

availabilityZones: ["us-west-2a", "us-west-2c"]

managedNodeGroups:
  - name: my-efa-bottlerocket-ng
    instanceType: p5.48xlarge
    minSize: 1
    desiredCapacity: 2
    maxSize: 3
    availabilityZones: ["us-west-2a"]
    volumeSize: 300
    privateNetworking: true
    efaEnabled: true
    amiFamily: Bottlerocket
    bottlerocket:
      enableAdminContainer: true
      settings:
        kernel:
          sysctl:
            "vm.nr_hugepages": "3000"  # Configures 3000 * 2Mi = 6000Mi hugepages

위의 vm.nr_hugepages sysctl 설정은 2Mi hugepages의 수를 구성합니다. 이 예제에서 3000은 3000 * 2Mi = 6000Mi의 hugepages를 의미해요.

EFA device plugin 설치 확인

efaEnabled: true로 노드 그룹을 만들면 eksctl이 자동으로 EFA Kubernetes device plugin을 배포합니다. device plugin이 설치되고 올바르게 작동하는지 확인할 수 있어요.

DaemonSet 상태를 확인합니다.

kubectl get daemonsets -n kube-system

샘플 출력:

NAME                                  DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
aws-efa-k8s-device-plugin-daemonset   2         2         2       2            2                     6m16s
...

여기서 EFA device plugin DaemonSet이 두 노드에서 실행되고 있습니다. 둘 다 READY와 AVAILABLE입니다.

다음으로 DaemonSet이 만든 Pod를 확인합니다.

kubectl get pods -n kube-system -l name=aws-efa-k8s-device-plugin

샘플 출력:

NAME                                        READY   STATUS    RESTARTS   AGE
aws-efa-k8s-device-plugin-daemonset-d68bs   1/1     Running   0          6m16s
aws-efa-k8s-device-plugin-daemonset-w4l8t   1/1     Running   0          6m16s

EFA device plugin Pod는 Running 상태이며, 플러그인이 성공적으로 배포되어 작동 중임을 확인합니다.

리소스 등록을 확인합니다.

vpc.amazonaws.com/efa 리소스가 kubelet에 등록되었는지 노드를 설명해서 확인할 수 있어요.

kubectl describe nodes

EFA 리소스가 올바르게 등록되면 노드의 Capacity와 Allocatable 리소스 아래에 나열된 것을 볼 수 있습니다. 예를 들면 이렇습니다.

Capacity:
  ...
  vpc.amazonaws.com/efa:  4
Allocatable:
  ...
  vpc.amazonaws.com/efa:  4

이 출력은 노드가 EFA 리소스를 인식하며 요청하는 Pod에 사용할 수 있게 만들었음을 확인해 줍니다.

(선택) EFA 성능 테스트

EFA 설정을 테스트하는 것을 권장합니다. GitHub의 aws-samples/awsome-distributed-training 저장소에 있는 NCCL Tests를 사용할 수 있어요. NCCL Tests는 NVIDIA Collective Communications Library를 사용해서 네트워크 성능을 평가합니다. 다음 단계는 Amazon EKS에서 NCCL 테스트를 제출합니다.

Kubeflow MPI Operator 배포:

NCCL 테스트에는 Kubeflow MPI Operator를 적용할 수 있습니다. MPI Operator는 Kubernetes에서 Allreduce 방식 분산 학습을 쉽게 실행하게 해 줍니다. 자세한 내용은 GitHub의 MPI Operator를 참고하세요.

GPUDirectRDMA/EFA를 확인하는 다중 노드 NCCL 성능 테스트 실행:

EFA 위에서 GPUDirectRDMA로 NCCL 성능을 확인하려면 표준 NCCL Performance 테스트를 실행합니다. 자세한 내용은 GitHub의 공식 NCCL-Tests 저장소를 참고하세요.

다음 단계를 완료해서 두 노드 NCCL Performance Test를 실행해 보세요. 예제 NCCL 테스트 작업에서 각 워커는 8개의 GPU, 5120Mi의 hugepages-2Mi, 4개의 EFA, 8000Mi의 메모리를 요청하며, 이는 각 워커가 p5.48xlarge 인스턴스의 모든 리소스를 소비함을 의미합니다.

MPIJob 매니페스트 생성:

다음을 nccl-tests.yaml 파일에 복사하세요.

apiVersion: kubeflow.org/v2beta1
kind: MPIJob
metadata:
  name: nccl-tests
spec:
  runPolicy:
    cleanPodPolicy: Running
    backoffLimit: 20
  slotsPerWorker: 8
  mpiReplicaSpecs:
    Launcher:
      replicas: 1
      template:
         spec:
          restartPolicy: OnFailure
          containers:
          - image: public.ecr.aws/hpc-cloud/nccl-tests:latest
            imagePullPolicy: IfNotPresent
            name: test-nccl-launcher
            env:
             - name: PATH
               value: $PATH:/opt/amazon/efa/bin:/usr/bin
            command:
            - /opt/amazon/openmpi/bin/mpirun
            - --allow-run-as-root
            - --tag-output
            - -np
            - "16"
            - -N
            - "8"
            - --bind-to
            - none
            - -x
            - PATH
            - -x
            - LD_LIBRARY_PATH
            - -x
            - NCCL_DEBUG=INFO
            - -x
            - NCCL_BUFFSIZE=8388608
            - -x
            - NCCL_P2P_NET_CHUNKSIZE=524288
            - -x
            - NCCL_TUNER_PLUGIN=/opt/amazon/ofi-nccl/lib/x86_64-linux-gnu/libnccl-ofi-tuner.so
            - --mca
            - pml
            - ^cm,ucx
            - --mca
            - btl
            - tcp,self
            - --mca
            - btl_tcp_if_exclude
            - lo,docker0,veth_def_agent
            - /opt/nccl-tests/build/all_reduce_perf
            - -b
            - "8"
            - -e
            - "16G"
            - -f
            - "2"
            - -g
            - "1"
            - -c
            - "1"
            - -n
            - "100"
    Worker:
      replicas: 2
      template:
        spec:
          nodeSelector:
            node.kubernetes.io/instance-type: "p5.48xlarge"
          containers:
          - image: public.ecr.aws/hpc-cloud/nccl-tests:latest
            imagePullPolicy: IfNotPresent
            name: nccl-tests-worker
            volumeMounts:
            - name: shmem
              mountPath: /dev/shm
            resources:
              limits:
                nvidia.com/gpu: 8
                hugepages-2Mi: 5120Mi
                vpc.amazonaws.com/efa: 32
                memory: 32000Mi
              requests:
                nvidia.com/gpu: 8
                hugepages-2Mi: 5120Mi
                vpc.amazonaws.com/efa: 32
                memory: 32000Mi
          volumes:
          - name: shmem
            hostPath:
              path: /dev/shm

NCCL-tests MPIJob 적용:

매니페스트를 적용해서 MPIJob을 제출합니다. 이는 두 개의 p5.48xlarge Amazon EC2 인스턴스를 만들게 돼요.

kubectl apply -f nccl-tests.yaml

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

mpijob.kubeflow.org/nccl-tests created

작업이 Pod를 시작했는지 확인합니다.

실행 중인 Pod를 확인합니다.

kubectl get pods

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

NAME                             READY   STATUS     RESTARTS   AGE
nccl-tests-launcher-nbql9    0/1     Init:0/1   0          2m49s
nccl-tests-worker-0          1/1     Running    0          2m49s
nccl-tests-worker-1          1/1     Running    0          2m49s

MPI Operator는 launcher Pod와 2개의 worker Pod(각 노드에 하나씩)를 만듭니다.

로그로 작업이 성공적으로 실행되는지 확인합니다.

nccl-tests-launcher Pod의 로그를 확인합니다. nbql9를 출력의 값으로 바꾸세요.

kubectl logs -f nccl-tests-launcher-nbql9

테스트가 성공적으로 완료되면 NVIDIA Collective Communications Library를 사용하는 애플리케이션을 배포할 수 있어요.

더 알아보기 (Learn more)

출처: 문서