Amazon EMR on EKS에서 Apache Spark의 사용자 지정 스케줄러로 YuniKorn 사용 - Amazon EMR

Amazon EMR on EKS에서 Apache Spark의 사용자 지정 스케줄러로 YuniKorn 사용

Amazon EMR on EKS을 사용하면 Spark 운영자 또는 spark-submit을 사용하여 Kubernetes 사용자 지정 스케줄러로 Spark 작업을 실행할 수 있습니다. 이 자습서에서는 사용자 지정 대기열 및 단체 예약(gang scheduling)에서 Volcano 스케줄러를 통해 Spark 작업을 실행하는 방법을 다룹니다.

개요

Apache Yunikorn은 앱 인식 예약 기능으로 Spark 예약 관리를 지원하므로, 이를 통해 리소스 할당량 및 우선순위를 세밀하게 제어할 수 있습니다. 단체 예약 기능을 통해 YuniKorn은 앱에 대한 최소한의 리소스 요청을 충족할 수 있는 경우에만 앱을 예약합니다. 자세한 내용은 Apache YuniKorn 설명서 사이트에서 What is gang scheduling을 참조하세요.

클러스터를 생성하고 YuniKorn 설정

다음 단계에 따라 Amazon ECS 클러스터를 배포합니다. AWS 리전(region) 및 가용 영역(availabilityZones)을 변경할 수 있습니다.

  1. Amazon EKS 클러스터를 정의합니다.

    cat <<EOF >eks-cluster.yaml --- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: emr-eks-cluster region: eu-west-1 vpc: clusterEndpoints: publicAccess: true privateAccess: true iam: withOIDC: true nodeGroups: - name: spark-jobs labels: { app: spark } instanceType: m5.xlarge desiredCapacity: 2 minSize: 2 maxSize: 3 availabilityZones: ["eu-west-1a"] EOF
  2. 클러스터를 생성합니다.

    eksctl create cluster -f eks-cluster.yaml
  3. Spark 작업을 실행할 spark-job 네임스페이스를 생성합니다.

    kubectl create namespace spark-job
  4. 다음으로 Kubernetes 역할 및 역할 바인딩을 생성합니다. 이는 Spark 작업 실행에서 사용하는 서비스 계정에 필요합니다.

    1. Spark 작업에 대한 서비스 계정, 역할 및 역할 바인딩을 정의합니다.

      cat <<EOF >emr-job-execution-rbac.yaml --- apiVersion: v1 kind: ServiceAccount metadata: name: spark-sa namespace: spark-job automountServiceAccountToken: false --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: spark-role namespace: spark-job rules: - apiGroups: ["", "batch","extensions"] resources: ["configmaps","serviceaccounts","events","pods","pods/exec","pods/log","pods/portforward","secrets","services","persistentvolumeclaims"] verbs: ["create","delete","get","list","patch","update","watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: spark-sa-rb namespace: spark-job roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: spark-role subjects: - kind: ServiceAccount name: spark-sa namespace: spark-job EOF
    2. 다음 명령을 사용하여 Kubernetes 역할 및 역할 바인딩 정의를 적용합니다.

      kubectl apply -f emr-job-execution-rbac.yaml

YuniKorn 설치 및 설정

  1. 다음 kubectl 명령을 사용하여 Yunikorn 스케줄러를 배포하기 위해 네임스페이스(yunikorn)를 생성합니다.

    kubectl create namespace yunikorn
  2. 스케줄러를 설치하려면 다음 Helm 명령을 실행합니다.

    helm repo add yunikorn https://apache.github.io/yunikorn-release
    helm repo update
    helm install yunikorn yunikorn/yunikorn --namespace yunikorn

Spark 운영자를 사용하여 YuniKorn 스케줄러에서 Spark 애플리케이션 실행

  1. 아직 실행하지 않았다면, 다음 단계를 완료하여 설정합니다.

    1. 클러스터를 생성하고 YuniKorn 설정

    2. YuniKorn 설치 및 설정

    3. Amazon EMR on EKS에 대한 Spark 운영자 구성

    4. Spark 운영자 설치

      helm install spark-operator-demo 명령을 실행할 때 다음 인수를 포함합니다.

      --set batchScheduler.enable=true --set webhook.enable=true
  2. SparkApplication 정의 파일 spark-pi.yaml을 생성합니다.

    YuniKorn을 작업 스케줄러로 사용하려면 애플리케이션 정의에 특정 주석과 레이블을 추가해야 합니다. 주석과 레이블은 작업 대기열 및 사용하려는 예약 전략을 지정합니다.

    다음 예제에서는 schedulingPolicyParameters 주석을 사용하여 애플리케이션에 대한 단체 예약을 설정합니다. 그런 다음, 이 예제에서는 작업 그룹(즉 작업 '단체')을 생성하여 작업 실행을 위해 포드를 예약하기 전에 사용할 수 있어야 하는 최소 용량을 지정합니다. 마지막으로, 클러스터를 생성하고 YuniKorn 설정 섹션에 정의된 대로 작업 그룹 정의에서 "app": "spark" 레이블의 노드 그룹을 사용하도록 지정합니다.

    apiVersion: "sparkoperator.k8s.io/v1beta2" kind: SparkApplication metadata: name: spark-pi namespace: spark-job spec: type: Scala mode: cluster image: "895885662937.dkr.ecr.us-west-2.amazonaws.com/spark/emr-6.10.0:latest" imagePullPolicy: Always mainClass: org.apache.spark.examples.SparkPi mainApplicationFile: "local:///usr/lib/spark/examples/jars/spark-examples.jar" sparkVersion: "3.3.1" restartPolicy: type: Never volumes: - name: "test-volume" hostPath: path: "/tmp" type: Directory driver: cores: 1 coreLimit: "1200m" memory: "512m" labels: version: 3.3.1 annotations: yunikorn.apache.org/schedulingPolicyParameters: "placeholderTimeoutSeconds=30 gangSchedulingStyle=Hard" yunikorn.apache.org/task-group-name: "spark-driver" yunikorn.apache.org/task-groups: |- [{ "name": "spark-driver", "minMember": 1, "minResource": { "cpu": "1200m", "memory": "1Gi" }, "nodeSelector": { "app": "spark" } }, { "name": "spark-executor", "minMember": 1, "minResource": { "cpu": "1200m", "memory": "1Gi" }, "nodeSelector": { "app": "spark" } }] serviceAccount: spark-sa volumeMounts: - name: "test-volume" mountPath: "/tmp" executor: cores: 1 instances: 1 memory: "512m" labels: version: 3.3.1 annotations: yunikorn.apache.org/task-group-name: "spark-executor" volumeMounts: - name: "test-volume" mountPath: "/tmp"
  3. 다음 명령을 사용하여 Spark 애플리케이션을 제출합니다. 이렇게 하면 spark-pi라고 하는 SparkApplication 객체도 생성됩니다.

    kubectl apply -f spark-pi.yaml
  4. 다음 명령을 사용하여 SparkApplication 객체에 대한 이벤트를 확인합니다.

    kubectl describe sparkapplication spark-pi --namespace spark-job

    첫 번째 포드 이벤트는 YuniKorn에서 해당 포드를 예약했음을 보여줍니다.

    Type    Reason            Age   From                          Message
    ----    ------            ----  ----                          -------
    Normal Scheduling        3m12s yunikorn   spark-operator/org-apache-spark-examples-sparkpi-2a777a88b98b8a95-driver is queued and waiting for allocation
    Normal GangScheduling    3m12s yunikorn   Pod belongs to the taskGroup spark-driver, it will be scheduled as a gang member
    Normal Scheduled         3m10s yunikorn   Successfully assigned spark
    Normal PodBindSuccessful 3m10s yunikorn   Pod spark-operator/
    Normal TaskCompleted     2m3s  yunikorn   Task spark-operator/
    Normal Pulling           3m10s kubelet    Pulling

spark-submit을 사용하여 YuniKorn 스케줄러에서 Spark 애플리케이션 실행

  1. 먼저, Amazon EMR on EKS에서 spark-submit 설정 섹션에 나온 단계를 완료합니다.

  2. 다음과 같은 환경 변수의 값을 설정합니다.

    export SPARK_HOME=spark-home export MASTER_URL=k8s://Amazon-EKS-cluster-endpoint
  3. 다음 명령을 사용하여 Spark 애플리케이션을 제출합니다.

    다음 예제에서는 schedulingPolicyParameters 주석을 사용하여 애플리케이션에 대한 단체 예약을 설정합니다. 그런 다음, 이 예제에서는 작업 그룹(즉 작업 '단체')을 생성하여 작업 실행을 위해 포드를 예약하기 전에 사용할 수 있어야 하는 최소 용량을 지정합니다. 마지막으로, 클러스터를 생성하고 YuniKorn 설정 섹션에 정의된 대로 작업 그룹 정의에서 "app": "spark" 레이블의 노드 그룹을 사용하도록 지정합니다.

    $SPARK_HOME/bin/spark-submit \ --class org.apache.spark.examples.SparkPi \ --master $MASTER_URL \ --conf spark.kubernetes.container.image=895885662937.dkr.ecr.us-west-2.amazonaws.com/spark/emr-6.10.0:latest \ --conf spark.kubernetes.authenticate.driver.serviceAccountName=spark-sa \ --deploy-mode cluster \ --conf spark.kubernetes.namespace=spark-job \ --conf spark.kubernetes.scheduler.name=yunikorn \ --conf spark.kubernetes.driver.annotation.yunikorn.apache.org/schedulingPolicyParameters="placeholderTimeoutSeconds=30 gangSchedulingStyle=Hard" \ --conf spark.kubernetes.driver.annotation.yunikorn.apache.org/task-group-name="spark-driver" \ --conf spark.kubernetes.executor.annotation.yunikorn.apache.org/task-group-name="spark-executor" \ --conf spark.kubernetes.driver.annotation.yunikorn.apache.org/task-groups='[{ "name": "spark-driver", "minMember": 1, "minResource": { "cpu": "1200m", "memory": "1Gi" }, "nodeSelector": { "app": "spark" } }, { "name": "spark-executor", "minMember": 1, "minResource": { "cpu": "1200m", "memory": "1Gi" }, "nodeSelector": { "app": "spark" } }]' \ local:///usr/lib/spark/examples/jars/spark-examples.jar 20
  4. 다음 명령을 사용하여 SparkApplication 객체에 대한 이벤트를 확인합니다.

    kubectl describe pod spark-driver-pod --namespace spark-job

    첫 번째 포드 이벤트는 YuniKorn에서 해당 포드를 예약했음을 보여줍니다.

    Type    Reason           Age   From                          Message
    ----    ------           ----  ----                          -------
    Normal Scheduling        3m12s yunikorn   spark-operator/org-apache-spark-examples-sparkpi-2a777a88b98b8a95-driver is queued and waiting for allocation
    Normal GangScheduling    3m12s yunikorn   Pod belongs to the taskGroup spark-driver, it will be scheduled as a gang member
    Normal Scheduled         3m10s yunikorn   Successfully assigned spark
    Normal PodBindSuccessful 3m10s yunikorn   Pod spark-operator/
    Normal TaskCompleted     2m3s  yunikorn   Task spark-operator/
    Normal Pulling           3m10s kubelet    Pulling