4 분 소요

Hail on a Spark Cluster

Hail은 WES이나 또는 샘플 수가 500이 안되는 WGS data에 대해서는 서버 한 대에서 사용하는 것만으로도 효율적인 데이터 처리를 할 수 있지만, 샘플 수가 천이 넘어가는 WGS data의 경우는 처리하는 데에 많은 시간이 걸립니다. 이때가 바로 Spark Cluster가 필요한 순간입니다.

Spark Cluster에는 서버로부터 resources(Core, Memory, etc.)을 얻어 관리하는 Cluster Manager가 있는데, Cluster를 구성할 때 주로 사용하는 Cluster Manager로는 Standalone, Yarn, Mesos 3가지가 있습니다. 3가지 모두 장단점이 존재하지만, 저 같은 경우에는 Cluter를 구성하기에 간편했던 Standalone 모드를 선택하였습니다.

Spark Architecture
< Spark Architecture >


다음은 Spark Architecture에 대한 그림입니다. 이 그림에는 나오지 않지만 보통 우리가 Master, Slave(Worker) node로 Cluster에 사용되는 서버를 분류합니다. Worker node는 말그대로 사용자가 제출한 Task를 할당 받아서 수행하는 역할을 합니다. Master node가 Worker node가 Task를 잘 처리하고 있는지 확인하고 문제가 발생하면 대처하는 역할을 합니다. Cluster로 사용할 서버 중 Master node로 실행되는 서버 하나를 정합니다. 한 서버에서 Master와 Worker를 동시에 맡는 것도 가능합니다. 서버에 사용할 서버 수가 4대라면 1개의 Master node에 4개의 Worker node를 두는 것이 가능하겠네요.

구성된 Spark Cluster에 사용자가 실행하고자 하는 프로그램을 Application이라고 합니다. Hail을 Application의 예라고 할 수 있겠습니다. Application을 실행하게 되면 Master node에 Driver program이, Worker node 안에 Executor들이 생성이 됩니다. Driver program은 SparkContext를 통해서 Application의 code와 수행해야 할 Task를 Executor에 보내고 Executor가 이를 수행하면 결과를 다시 Driver에 전송합니다.

이제 Spark Cluster를 구축하는 과정을 설명할텐데 제가 보여드리는 예시는 x86_64 CPU에 운영체제 Ubuntu 18.04 LTS 기준입니다. 다른 운영체제의 경우에는 조금 과정이 다를 수 있습니다.

1) User, Host 설정

본격적으로 Spark를 설치하기 전에 먼저 서버마다 Hail 클러스터를 사용할 계정을 생성해 줍니다. 주의할 점은 사용자명, 그룹명이 모두 일치해야 합니다. 또한, IP 주소가 아닌 Host명으로 편리하게 Spark 설정을 하기 위해서 /etc/hosts에서 클러스터에 사용할 서버의 Host명을 설정해줍니다.

10.1.1.1    spark-master
10.1.1.2    spark-worker1
10.1.1.3    spark-worker2
10.1.1.4    spark-worker3

2) SSH 접속 설정

Spark에서 서버 간의 통신은 주로 SSH를 통해 이루어집니다. 따라서 클러스터 내 서버 간의 SSH 통신이 암호입력 없이 이루어지도록 공개키를 생성하고 다른 서버에 전달하는 과정이 필요합니다.

$ cd ~/.ssh
$ ssh-keygen -t rsa -P ""
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
$ ssh-copy-id -i ~/.ssh/id_rsa.pub <another node IP>

이렇게 설정하게 되면 SSH로 다른 서버에 접속할 때 암호를 입력하지 않고 바로 접속할 수 있습니다.

3) 방화벽 설정

통신을 원활하게 하기 위해 또 설정해야 할 부분이 바로 방화벽 규칙을 설정하는 것입니다. Spark의 경우 driver, worker가 임의의 포트를 선택하여 통신을 하기 때문에 서버 IP의 모든 포트 접근을 허용하는 방화벽 규칙을 등록해주어야 합니다.

$ sudo ufw allow from <another node IP>

4) Spark 설치

Standalone 모드의 경우에는 APACHE Spark 웹페이지에서 Spark를 다운 받으면 그밖의 설치과정 없이 바로 Cluter를 구성할 수 있습니다. 저는 Spark 3.1.2 version을 다운 받았습니다. Hail 0.2.70 version 기준 Spark 3.1.1을 기본적으로 사용한다고 하니 Hail의 버전을 확인하여 호환되는 Spark 버전을 사용하는 것이 좋습니다. 적당한 곳에 압축을 풀어줍시다.

$ wget https://mirror.navercorp.com/apache/spark/spark-3.1.2/spark-3.1.2-bin-hadoop3.2.tgz
$ tar -zxvf spark-3.1.2-bin-hadoop3.2.tgz

5) Hail과 관련 패키지 설치

Application인 Hail을 Master node에 설치합니다. 설치하는 방법은 앞의 글에서 설명을 했으니 참고하시면 됩니다. 또한 Hail은 pip 설치 이외에 Source로부터 Hail을 설치하는 방법도 제공하고 있습니다. 우선 필요 패키지를 설치한 뒤 다음 명령어를 실행해 줍니다.

$ git clone https://github.com/hail-is/hail.git
$ cd hail/hail
$ make install-on-cluster HAIL_COMPILE_NATIVES=1 SCALA_VERSION=2.12.13 SPARK_VERSION=3.1.1

만약 Source를 통해 설치를 했다면 추가적으로 py4j를 설치해주어야 하고, Spark 디렉터리 안에 pyspark 패키지가 python에서 작동할 수 있도록 .bashrc과 같은 shell profile에 python 환경변수에 pyspark 위치를 추가하는 명령어를 넣어줍니다.

$ sudo apt install py4j
# in .bashrc file
export PYTHONPATH=$PYTHONPATH:$SPARK_HOME/python

Hail의 경우 기본적으로 pip을 이용하여 Hail을 설치할 때 pyspark 패키지를 함께 설치를 합니다. 그리고 Cluster를 작동시킬 때도 따로 다운 받은 Spark 안의 pyspark를 사용하는 것이 아니라 python package에 설치된 pyspark를 사용합니다. 이 경우 Hail이 사용하는 pyspark의 Spark 버전과 다운 받은 Spark 버전이 달라질 수 있다는 것을 알아두면 좋습니다.

그리고 Worker node에 BLAS와 LAPACK 패키지를 설치해줍니다.

$ sudo apt install libopenblas liblapack3

6) Spark 설정

Hail 구동을 하려면 Spark 폴더($SPARK_HOME) 내에 설정 파일인 spark-env.sh, workers 파일 수정합니다.

spark-env.sh에는 Master node로 사용할 서버 이름 또는 IP 입력합니다.

export SPARK_MASTER_HOST=spark-master

workers 파일에는 Worker node로 사용할 서버 이름 또는 IP를 입력합니다. Master node에서만 입력해주면 됩니다.

spark-worker1
spark-worker2
spark-worker3

7) Spark master, worker 실행

Master node에서 다음 명령어를 통해 Spark master를 실행합니다. 포트 기본값은 7077입니다.

$ $SPARK_HOME/sbin/start-master.sh

그리고 Worker node에서는 다음 명령어로 Spark worker를 실행할 수 있습니다. 포트 기본값은 7078입니다.

$ $SPARK_HOME/sbin/start-worker.sh spark://spark-master:7077

Spark master와 worker를 한번에 작동시키고 싶다면 start-all.sh 스크립트를 이용합니다.

$ $SPARK_HOME/sbin/start-all.sh

8) Hail 클러스터 실행

Hail 클러스터을 실행하는 방법은 크게 두가지 방법이 있습니다. 하나는 스크립트 내에서 hail.init 함수 parameter에 Spark master의 주소를 입력해주는 방법입니다.

import hail as hl
hl.init(master = 'spark://spark-master:7077')

다른 방법은 Hail로 작성된 스크립트(hail-script.py)를 spark-submit을 통해 Spark에 제출하는 방법입니다.

HAIL_HOME=$(pip3 show hail | grep Location | awk -F' ' '{print $2 "/hail"}')
$SPARK_HOME/bin/spark-submit --jars $HAIL_HOME/hail-all-spark.jar \
--conf spark.driver.extraClassPath=$HAIL_HOME/hail-all-spark.jar \
--conf spark.executor.extraClassPath=./hail-all-spark.jar \
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
--conf spark.kryo.registrator=is.hail.kryo.HailKryoRegistrator \
--master spark://spark-master:7077 \
hail-script.py

9) 클러스터 구동 시 주의할 점

Hail 클러스터를 구동할 때 사용되는 데이터는 모든 서버에서 똑같은 경로로 접근할 수 있어야 합니다. 하나의 worker라도 해당 데이터에 접근할 수 없으면 오류가 발생합니다. 따라서 클러스터를 사용할 떄에는 분산 파일 시스템을 사용하는 것을 추천합니다. 저는 Hadoop의 HDFS가 Spark와 연동이 잘되기 때문에 이를 사용하고 있습니다. 또한 AWS S3 또는 Google Cloud Storge와 같은 클라우드 저장소를 사용할 수도 있습니다.

Reference

댓글남기기