AWS 기술 블로그

Amazon EC2 가속 컴퓨팅 인스턴스 활용하기 – 파트 3 – Amazon Elastic Kubernetes Service (EKS) 기반의 GPU 클러스터

서론

“Amazon EC2 가속 컴퓨팅 인스턴스 활용하기” 블로그 시리즈는 Amazon EC2의 가속 컴퓨팅 인스턴스와 AWS에서 제공되는 다양한 리소스를 이용하여, 멀티 노드 가속 컴퓨팅 환경을 쉽게 구축할 수 있는 방법에 대해서 알아 봅니다. 블로그는 아래와 같이 3개의 시리즈로 구성이 되어 있으며, 파트 1에서는 AWS Deep Learning AMI (DLAMI)를 사용한 쉽고 빠른 컴퓨팅 환경 구축에 대해서 알아 보았습니다. 그리고, 파트 2에서는 DLAMI의 장점에 더해서 AWS ParallelCluster를 사용하여, 가속 컴퓨팅 클러스터를 손쉽게 구축하고, 유연한 스케일링과 효율적으로 작업을 관리할 수 있는 방법에 대해서 알아 보았습니다.

  1. Amazon EC2 가속 컴퓨팅 인스턴스 활용하기 – 파트 1 – AWS Deep Learning AMI (DLAMI)
  2. Amazon EC2 가속 컴퓨팅 인스턴스 활용하기 – 파트 2 – AWS ParallelCluster
  3. Amazon EC2 가속 컴퓨팅 인스턴스 활용하기 – 파트 3 – Amazon Elastic Kubernetes Service (EKS) 기반의 GPU 클러스터

Amazon EKS는 AWS의 완전 관리형 Kubernetes 서비스로, 컨테이너 기반의 워크로드를 실행할 수 있는 신뢰할 수 있는 서비스입니다. 또한, 파트 1과 2에서 EC2 인스턴스와 DLAMI를 사용하여 빠른 가속 컴퓨팅 환경을 구축할 수 있는 것과 같이, EKS는 컨테이너 기반의 가속 컴퓨팅 환경을 쉽고 빠르게 구축할 수 있도록 Amazon EKS 최적화 가속 Amazon Linux AMIAWS Deep Learning Containers (DLC)를 제공하고 있습니다. 따라서 파트 3에서는 Amazon EKS 환경에서 컨테이너 기반의 워크로드를 위한, 가속 컴퓨팅 클러스터를 구축할 수 있는 방법에 대해서 알아보겠습니다.
이 블로그 시리즈는 EC2 기반의 가속 컴퓨팅 환경을 활용하는 데에 중점을 두고 있습니다. 만약, AWS의 완전 관리형 기계 학습 서비스에 더 많은 관심이 있으시다면 Amazon SageMakerAmazon SageMaker HyperPod을 검토하여 보시기 바랍니다.

Amazon EKS 가속 컴퓨팅 환경 구축

이제부터 Amazon EKS, EKS 최적화 가속 Amazon Linux AMI, 그리고 AWS DLC를 사용하여 가속 컴퓨팅 클러스터를 구축하는 방법에 대해서 알아 보겠습니다. EKS 기반의 가속 컴퓨팅 클러스터를 사용하기 위해서는, 먼저 아래의 사전 필요 사항을 충족해야 하며 링크를 참고하여 구성할 수 있습니다.

사전 필요 사항

  • AWS 계정, 관리용 사용자, AWS CLI
    • 만약, AWS CLI의 프로파일을 “default”로 설정하지 않은 경우에는, 블로그에 사용된 AWS CLI 명령에 추가적으로 --profile PROFILE_NAME을 명시해야 올바르게 동작합니다.
  • EKS 클러스터 관리 도구 – kubectl, eksctl, helm
  • Amazon EC2 vCPU 서비스 할당량 증가
    • 블로그 시리즈에서는 2개 이상의 G6 유형의 EC2 인스턴스를 사용합니다. 독자의 환경에 따라 새로운 EC2 인스턴스를 생성하려고 시도할 때, vCPU 할당량 부족으로 인해 오류가 발생하며 생성이 실패할 수 있습니다.
    • 이 re:Post 게시글을 참고하여 vCPU 서비스 할당량을 사전에 증가시키고 실습하시는 것을 권장하여 드립니다.

1. (선택 사항) 온디맨드 용량 예약(On-Demand Capacity Reservations, ODCR) 생성

이 단계는 선택 사항입니다. 가속 컴퓨팅 수요의 급증으로 공급이 원활하지 않은 인스턴스를 사용해야 할 경우에는 온디맨드 용량 예약을 사용하여 필요한 컴퓨팅 용량을 예약하고 확보할 수 있습니다. 하지만 용량이 계정에 프로비저닝되면 인스턴스가 예약 용량으로 실행되는지의 여부와 관계 없이, 선택한 인스턴스 유형에 대한 용량 예약이 온디맨드 요금으로 청구되는 점을 유의해야 합니다. 필요한 인스턴스 유형, 플랫폼, 리전과 가용 영역등을 지정하여 용량 예약을 생성합니다.

aws ec2 create-capacity-reservation \
--instance-type g6.12xlarge \
--instance-platform Linux/UNIX \
--availability-zone us-west-2c \
--instance-count 2 \
--region us-west-2

 2. EC2 배치 그룹 생성 및 환경 변수 설정

EC2 배치 그룹을 사용하여 EKS 클러스터의 노드를 서로 가깝게 배치할 수 있습니다. 가속 컴퓨팅 인스턴스는 앞선 블로그 시리즈에서 확인한 바와 같이, 고성능 네트워크 인터페이스인 Elastic Fabric Adaptor(EFA)를 사용할 수 있기 때문에, 최적의 네트워크 성능을 위해 클러스터 배치 전략을 사용합니다. EFA에 대한 보다 자세한 사항은 “AWS 고성능 컴퓨팅 네트워크, 1부: AWS가 제공하는 고속 네트워크 인터페이스, EFA(Elastic Fabric Adaptor)” 블로그를 참고하실 수 있습니다. 이와 함께, 온디맨드 용량 예약을 생성하였다면, 배치 그룹과 용량 예약을 위한 환경 변수를 설정합니다. 이 단계에서 유의해야 할 사항은, AWS CLI에 사용된 리소스 조회를 위한 인덱스 번호입니다. 사용자의 환경에 맞게 인덱스를 수정하여, 사용하고자 하는 리소스를 지정해 주시기 바랍니다.

aws ec2 create-placement-group --group-name eks-gpu-cluster --strategy cluster

PG_NAME=$(aws ec2 describe-placement-groups --group-names eks-gpu-cluster --query "PlacementGroups[0].GroupName" --output text)
CR_ID=$(aws ec2 describe-capacity-reservations --query "CapacityReservations[0].CapacityReservationId" --output text)

echo $PG_NAME
echo $CR_ID

3. EKS 클러스터 생성

eksctl을 사용하여 클러스터를 생성합니다. 구성 파일은 EKS에서 EFA를 사용하기 위하여 EKS 사용자 가이드를 참고하였습니다. 그리고, eksctl을 사용하여 클러스터를 생성하는 다른 예제 파일들은 eksctl 깃허브를 참고할 수 있습니다. 이 블로그에서는 EFA를 연결한 2개의 G6 유형의 인스턴스를 생성할 것이며, G6 유형의 인스턴스는 최대 8개의 NVIDIA L4 Tensor Core GPU와 192GB의 GPU 메모리를 제공합니다. 이 구성은 us-west-2 리전에, 최소 2개에서 최대 5개의 g6.12xlarge 인스턴스를 사용하는 EKS 클러스터를 생성합니다.

cat > eks-gpu-cluster.yaml << EOF
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: eks-gpu-cluster
  region: us-west-2
  version: "1.30"
iam:
  withOIDC: true
availabilityZones: ["us-west-2a","us-west-2c"]
nodeGroups:
  - name: gpu-node-group
    instanceType: g6.12xlarge
    minSize: 1
    desiredCapacity: 2
    maxSize: 5
    availabilityZones: ["us-west-2c"]
    efaEnabled: true
    privateNetworking: true
    placement:
      groupName: ${PG_NAME}
#    capacityReservation:
#      capacityReservationTarget:
#        capacityReservationID: ${CR_ID}
EOF

eksctl 명령을 사용하여 클러스터를 생성합니다. 만약 온디맨드 용량 예약을 생성하였다면, capacityReservation, capacityReservationTarget, capacityReservationID의 주석을 제거하고 클러스터를 생성합니다. 명령이 올바르게 시작이 되면, 아래와 같은 결과를 보여주며 생성이 진행됩니다.

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

4. 클러스터 생성 확인

클러스터는 Amazon EKS 최적화 가속 Amazon Linux AMI를 사용하여 생성이 됩니다. EKS 최적화 가속 AMI에는 Kubernetes 클러스터를 위한 구성 요소들과 NVIDIA 드라이버, EFA 드라이버등이 사전에 배포되어 있습니다. 클러스터의 생성은 15분 ~ 20분 정도가 소요가 됩니다. 클러스터 생성이 완료되면, 아래와 같이 모든 노드가 Ready 그리고 모든 파드들이 Running 상태인지 확인합니다.

kubectl get node -A
kubectl get pod -A

만약 aws-efa-k8s-device-plugin-daemonset에 오류가 발생하였다면, 아래와 같이 helm을 이용하여 다시 설치하면 됩니다.

kubectl delete ds aws-efa-k8s-device-plugin-daemonset -n kube-system
helm repo add eks https://aws.github.io/eks-charts
helm install aws-efa-k8s-device-plugin --namespace kube-system eks/aws-efa-k8s-device-plugin

마지막으로, 아래와 같이 생성된 EKS 노드 그룹의 스케일링 정책, 인스턴스 유형, 그리고 EKS 최적화 가속 AMI가 잘 적용이 되어있는지 확인합니다. EKS 최적화 가속 AMI에 대한 보다 자세한 사항은 릴리즈 노트를 참고할 수 있습니다. 또한, 이미지의 ID는 사용자 가이드를 바탕으로 확인할 수 있습니다.

aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.30/amazon-linux-2-gpu/recommended/image_id --region us-west-2 --query "Parameter.Value" --output text
eksctl get nodegroups --cluster eks-gpu-cluster --output yaml

5. GPU 사용 테스트

EKS 클러스터가 올바르게 구성된 것을 확인하였으므로, GPU를 사용하는 기본적인 테스트를 수행합니다. EKS 최적화 가속 AMI의 사용자 가이드를 참고합니다. 테스트를 위한 CUDA 컨테이너 이미지는 NVIDIA 도커 허브를 참고하여 수정을 하였으며, nvidia.com/gpu도 사용이 되는 인스턴스의 규격에 맞게 수정을 하여 구성 파일을 생성합니다.

cat > nvidia-smi.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: nvidia-smi
spec:
  restartPolicy: OnFailure
  containers:
  - name: nvidia-smi
    image: nvidia/cuda:12.6.0-base-ubuntu22.04
    args:
    - "nvidia-smi"
    resources:
      limits:
        nvidia.com/gpu: 4
EOF

nvidia-smi.yaml 구성 파일을 적용하여 파드가 올바르게 실행되고 완료된 것을 확인합니다. 해당 파드의 로그를 확인하면, 아래와 같이 4개의 GPU를 확인할 수 있습니다.

kubectl apply -f nvidia-smi.yaml
kubectl get pod -A
kubectl logs nvidia-smi

6. NVIDIA Collective Communication Library (NCCL) 테스트

NCCL은 멀티 GPU 및 멀티 노드간의 통신을 위한 소프트웨어 라이브러리입니다. 현재 EKS 클러스터는 고성능 네트워크 인터페이스인 EFA를 활성화하였으므로, EFA 성능 테스트 사용자 가이드를 참고하여 성능 테스트를 수행해 봅니다.

성능 테스트를 위하여, 먼저 Message Passing Interface (MPI) 오퍼레이터를 설치합니다. MPI 오퍼레이터 깃허브를 참고할 수 있습니다.

kubectl apply --server-side -f https://raw.githubusercontent.com/kubeflow/mpi-operator/v0.5.0/deploy/v2beta1/mpi-operator.yaml

사용자 가이드는 EC2 p5.48xlarge 인스턴스를 기준으로 적성이 되었으므로, 현재 클러스터에 환경에 맞게 구성 파일을 작성하기 위해서 g6.12xlarge 인스턴스의 규격을 확인합니다. g6.12xlarge 인스턴스에는 4개의 GPU와 1개의 EFA가 장착되어 있습니다.

kubectl describe node

확인한 인스턴스의 규격에 맞게 nccl-tests.yaml 파일을 생성하고 파라미터를 수정합니다. 사용자 가이드의 예제 구성 파일에서 굵게 표시가 되어 있는 slotsPerWorker, mpirun 명령의 -np와 -N 옵션의 값, node.kubernetes.io/instance-type, nvidia.com/gpu, vpc.amazonaws.com/efa가 수정이 되었습니다.

vi nccl-tests.yaml

apiVersion: kubeflow.org/v2beta1
kind: MPIJob
metadata:
  name: nccl-tests
spec:
  runPolicy:
    cleanPodPolicy: Running
    backoffLimit: 20
  slotsPerWorker: 4
  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
             - name: LD_LIBRARY_PATH
               value: /opt/amazon/openmpi/lib:/opt/nccl/build/lib:/opt/amazon/efa/lib:/opt/aws-ofi-nccl/install/lib:/usr/local/nvidia/lib:$LD_LIBRARY_PATH
             - name: NCCL_DEBUG
               value: INFO
             - name: NCCL_BUFFSIZE
               value: '8388608'
             - name: NCCL_P2P_NET_CHUNKSIZE
               value: '524288'
             - name: NCCL_TUNER_PLUGIN
               value: /opt/aws-ofi-nccl/install/lib/libnccl-ofi-tuner.so
            command:
            - /opt/amazon/openmpi/bin/mpirun
            - --allow-run-as-root
            - --tag-output
            - -np
            - "8"
            - -N
            - "4"
            - --bind-to
            - none
            - -x
            - PATH
            - -x
            - LD_LIBRARY_PATH
            - -x
            - NCCL_DEBUG=INFO
            - -x
            - NCCL_BUFFSIZE
            - -x
            - NCCL_P2P_NET_CHUNKSIZE
            - -x
            - NCCL_TUNER_PLUGIN
            - --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: "g6.12xlarge"
          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: 4
                hugepages-2Mi: 5120Mi
                vpc.amazonaws.com/efa: 1
                memory: 32000Mi
              requests:
                nvidia.com/gpu: 4
                hugepages-2Mi: 5120Mi
                vpc.amazonaws.com/efa: 1
                memory: 32000Mi
          volumes:
          - name: shmem
            hostPath:
              path: /dev/shm

이제 nccl-tests.yaml 구성 파일을 적용하고, 관련 파드의 상태를 확인합니다. 하나의 mpi-operator와 nccl-tests-launcher, 그리고 2개의 nccl-tests-worker 파드가 Running 상태여야 합니다. nccl-tests-launcher가 Running 상태가 되는데 수십초 정도가 소요될 수 있습니다.

kubectl apply -f nccl-tests.yaml
kubectl get pod -A

모든 파드가 Running 상태가 되면, 각 파드의 nvidia-smi 명령을 통하여 동작 상태를 확인합니다. GPU의 사용률과 AllReduce 성능 테스트 프로세스가 동작하고 있음을 확인할 수 있습니다. nccl-tests-worker-0과 nccl-tests-worker-1 2개 파드의 상태를 모두 확인하여 봅니다.

kubectl exec -it nccl-tests-worker-0 -- nvidia-smi

성능 테스트가 완료가 되면, nccl-tests-launcher 파드의 로그를 확인합니다. 테스트 결과의 Algorithm bandwidth (algbw)와 Bus bandwidth (busbw)를 확인할 수 있으며, busbw가 g6.12xlarge 인스턴스에서 제공할 수 있는 최대 네트워킹 성능인 40Gbps (5GBps)에 육박하는 4.93GBps를 보여 주는 것을 확인할 수 있습니다. EC2 인스턴스의 유형에 따라 다르지만, 이론적으로 p5.48xlarge 인스턴스는 최대 3,200Gbps의 대역폭을 제공할 수 있습니다.

kubectl logs nccl-tests-launcher-?????

7. AWS Deep Learning Containers 사용해보기

AWS Deep Learning Containers (DLC)는 주요 딥 러닝 프레임워크의 최신 버전이 사전에 설치되고 테스트를 마친 도커 이미지입니다. Deep Learning Containers를 사용하면 환경을 처음부터 구축하고 최적화할 필요 없이 사용자 지정 기계 학습 환경을 빠르게 배포할 수 있습니다. SageMaker, EC2, 그리고 EKS등 다양한 컴퓨팅 리소스를 지원하며 TensorFlow와 PyTorch 같은 주요 프레임워크를 즉시 사용할 수 있습니다. 보다 더 자세한 사항은 Deep Learning Containers의 릴리즈 노트를 참고할 수 있습니다.
이제 DLC의 PyTorch GPU training 사용자 가이드를 참고하여, PyTorch 프레임워크를 사용해 mnist 데이터셋에 대한 예제를 수행해 보겠습니다. 굵게 표시된 부분과 같이, 사용자 가이드의 예제 구성 파일에서, 최신 이미지를 사용할 수 있도록 수정이 되었으며, GPU를 사용할 수 있도록 --no-cuda 옵션을 삭제하였습니다. 사용 가능한 모든 이미지의 목록은 DLC 깃허브에서 확인할 수 있습니다.

cat > pytorch.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: pytorch-training
spec:
  restartPolicy: OnFailure
  containers:
  - name: pytorch-training
    image: 763104351884.dkr.ecr.us-west-2.amazonaws.com/pytorch-training:2.3.0-gpu-py311-cu121-ubuntu20.04-ec2-v1.0
    command:
      - "/bin/sh"
      - "-c"
    args:
    - "git clone https://github.com/pytorch/examples.git && python examples/mnist/main.py"
    env:
    - name: OMP_NUM_THREADS
      value: "36"
    - name: KMP_AFFINITY
      value: "granularity=fine,verbose,compact,1,0"
    - name: KMP_BLOCKTIME
      value: "1"
EOF

수정된 구성 파일을 적용하여 파드를 생성하고 pytorch-training 파드가 Running 상태인지 확인합니다. 처음 파드의 생성은 수십초 이상 소요될 수 있습니다.

kubectl create -f pytorch.yaml
kubectl get pods -A

실행되는 파드의 GPU 사용 현황을 모니터링합니다. 아래의 예제는 1개의 GPU를 사용하고 있습니다. 이는 main.py 스크립트의 num_workers의 값에 따라서 사용되는 GPU의 수는 달라질 수 있습니다.

kubectl exec -it pytorch-training -- nvidia-smi

실행이 완료가 되면 파드의 로그를 확인합니다. 학습과 테스트가 성공적으로 완료가 되었고, 99%의 정확도가 나오는 결과값을 확인할 수 있습니다.

kubectl logs pytorch-training

8. 리소스 정리

실습을 완료하고 테스트 환경을 지속적으로 사용하지 않을 계획이라면, 추가 과금을 방지하기 위하여 EKS 클러스터를 삭제하고 온디맨드 용량 예약을 취소합니다.

kubectl delete -f pytorch.yaml
kubectl delete -f nvidia-smi.yaml
kubectl delete -f nccl-tests.yaml
kubectl delete -f https://raw.githubusercontent.com/kubeflow/mpi-operator/v0.5.0/deploy/v2beta1/mpi-operator.yaml
eksctl delete cluster -f eks-gpu-cluster.yaml
aws ec2 cancel-capacity-reservation --capacity-reservation-id $CR_ID

만약 아래와 같은 오류로 eksctl 명령을 통하여 EKS 클러스터의 삭제가 되지 않는다면, Kubernetes의 PodDisruptionBudget의 이슈일 수 있습니다.

따라서 연관된 PodDisruptionBudget을 삭제하고 클러스터 삭제를 다시 시도합니다.

kubectl delete pdb coredns -n kube-system
eksctl delete cluster -f eks-gpu-cluster.yaml
aws ec2 cancel-capacity-reservation --capacity-reservation-id $CR_ID

결론

AWS는 가속 컴퓨팅을 위하여 다양한 유형의 Amazon EC2의 가속 컴퓨팅 인스턴스 뿐만 아니라, 고성능 네트워크 어댑터 Elastic Fabric Adaptor(EFA), 그리고 Amazon EC2 UltraClusters 아키텍처를 사용하여 리소스를 제공합니다. 그러나, 이런 가속 컴퓨팅 리소스를 이용하여 빠르고 효율적으로 환경을 구축하고 활용할 수 있는 방법이 필요합니다.
이를 위하여, AWS는 AWS Deep Learning AMI (DLAMI), Amazon EKS 최적화 가속 Amazon Linux AMI, AWS Deep Learning Containers, 그리고 AWS Batch, AWS ParallelCluster, Amazon EKS와 같은 작업 및 클러스터 관리 도구도 또한 제공하고 있습니다.

파트 1과 2에서 EC2 인스턴스와 DLAMI를 사용하여 빠른 가속 컴퓨팅 환경을 구축할 수 있는 것과 같이, 파트 3에서는 컨테이너 기반의 가속 컴퓨팅 환경을 쉽고 빠르게 구축할 수 있도록, Amazon EKS, Amazon EKS 최적화 가속 Amazon Linux AMI, 그리고 AWS Deep Learning Containers (DLC)를 사용하여, 가속 컴퓨팅 클러스터를 구축할 수 있는 방법에 대해서 알아 보았습니다.
이 블로그 시리즈를 통하여, 가속 컴퓨팅을 위한 AWS의 다양한 리소스와 서비스를 사용하여 시간, 비용, 리소스의 낭비가 없이 효과적으로 가속 컴퓨팅을 즉시 시작하고, 바로 활용할 수 있는 방법들에 대해서 알아 보았습니다.

이제 사용자의 환경에 가장 적절한 방법을 선택하여, 다양한 유형의 Amazon EC2의 가속 컴퓨팅 인스턴스를 보다 더 빠르고 효율적으로 활용할 수 있기를 바랍니다.

블로그 시리즈

  1. Amazon EC2 가속 컴퓨팅 인스턴스 활용하기 – 파트 1 – AWS Deep Learning AMI (DLAMI)
  2. Amazon EC2 가속 컴퓨팅 인스턴스 활용하기 – 파트 2 – AWS ParallelCluster
  3. Amazon EC2 가속 컴퓨팅 인스턴스 활용하기 – 파트 3 – Amazon Elastic Kubernetes Service (EKS) 기반의 GPU 클러스터
SeungYong Baek

SeungYong Baek

백승용 솔루션즈 아키텍트는 다양한 분야의 IT 인프라 스트럭처와 산업군에 대한 엔지니어 경험을 바탕으로, 고객이 AWS 클라우드를 활용하여 비즈니스 성과를 달성할 수 있도록, 고객과 함께 효율적인 아키텍처를 구성하는 역할을 수행하고 있습니다.

EuiJeong Go

EuiJeong Go

고의정 테크니컬 어카운트 매니저는 다양한 산업군의 IT 시스템 운영 경험을 바탕으로, 엔터프라이즈 기업 고객이 AWS Cloud와 함께 고객의 비즈니스 목표를 달성할 수 있도록 최적화된 성능으로 안정적인 운영 환경을 구축하는 데 도움을 드리고 있습니다.