Spark Standalone Cluster with Docker Swarm

이번에 연구실에서 Apache Spark Cluster를 구축할 일이 있어서 구축 방법을 문서화했다. 이 포스트에서는 그 문서에 살점을 조금 더 붙여서 올려본다. 아울러 데이터가 최대한 내부망에서 처리되는 것을 원하시는 교수님 + 클라우드로 연구비를 사용하기 힘듦 등의 이유로 AWS를 사용할 수 없는 환경이라 직접 설치 / 설정했음을 밝혀둔다.

Why Standalone?

YARN, Mesos, Kubernetes와 같은 방법 대신에 왜 Docker Swarm을 활용한 Standalone으로 구축했을까? 이유는 크게 세 가지다.

먼저, 설정이 간편하다. 설라고 부르기 민망할 정도로 쉽게 설할 수 있다. 이는 DevOps에 익숙하지 않은 사람이 이후에 이 프로젝트를 인수인계 받더라도 개발환경을 구축하는 데에 최소한의 시간을 쓸 수 있는 방법이라 확신한다.

두 번째 이유는 소규모라는 것이다. 8C16T 64GB * 5 정도의 클러스터를 구축하는 데에 굳이 Kubernetes를 쓰는 것도 웃기고, 역시나 우리 연구실에서 해결하고자 하는 문제에는 내 생각에 이미 충분한 컴퓨팅 파워라고 생각한다. 또한, Docker swarm은 러닝커브가 매우 완만하다. 이 또한, 연구실에서 사용할 클러스터에 쓰일 적합한 환경이라고 봤다.

마지막으로, 1 Gigabit Ethernet을 사용 중이다. 아마, 병목현상이 발생한다면 Spark Cluster의 Utilization rate가 아닌 느린 네트워크에서 올 거라고 생각했다. 이런 상황에서는 Standalone을 Mesos로 바꾼다고 해결될 일이 아니다. 서버마다 10G 이더넷 카드를 꼽고, 10G 스위칭 허브를 구매해야한다. 그래도 병목현상이 생기고, 더 높은 성능을 원한다면 그 때 가서 바꿔도 늦지 않을거라 판단했다.

Infrastructure

연구실이 학교로부터 할당받은 IP가 한정되어있고, 각 노드별로 별도의 IP를 넣는 것은 말이 안된다고 생각했다. 간단하게 하나의 공유기에 5개의 서버를 연결했다. 각 서버는 공유기에서 포트포워딩으로 접근했다. 각 서버에 리눅스를 설치한 뒤에 ~/.ssh/config 에 서버들을 추가했다. ssh spark1 의 명령어로 1번 서버로 ssh 접속을 하는 식이다.

하나의 명령어를 5개의 쉘에서 수행하는 것은 상당한 노가다였다. vi ~/.ssh/authorized_keys로 ssh 설정을 하면서 뼈저리게 느꼈다. 이 노가다는 개발자라면 응당 참지 말고 해결해야한다고. 여기저기 찾아봤다. 맥에서 쓰기 좋은 csshx를 찾았고 잘 동작했지만, 오래된 프로젝트기도 했고, 다른 운영체제에서는 사용할 수 없는 솔루션이라 판단했다. 친구의 추천으로 tmux의 :setw syncronize-panes 설정을 이용했다. tmuxinator 설정에서 각 서버로 ssh 접속하는 명령어로 세팅해놓아 한 번에 5개 서버에 접속된 tmux가 띄워지는 방식이다. 밑의 yml 파일이 바로 그 tmuxinator 설정파일이다.

Create a Docker Swarm

Docker Swarm은 Docker 컨테이너를 클러스터링, 스케줄링하는 도구이다. 1 이전에는 별도의 standalone swarm으로 돌아갔지만, docker 1.12.0부터 docker에 통합됐다. 거의 그냥 Docker를 쓰는 느낌으로 사용할 수 있다. docker의 rundocker swarm에서의 service라고 생각하면 될 듯. Docker swarm에는 Manager, Worker 노드가 있는데, Manager에서 service를 띄우면 알아서 swarm에 참여하고 있는 노드들에게 instance를 띄워주는 느낌. 더 자세한 설명은 docker 문서 참고!

적당한 서버를 골라서 Docker Swarm Manager를 생성한다. 내 경우에는 spark5 (= 192.168.0.55)가 128GB라서 여기에 올렸다.

192.168.0.55 는 spark5의 내부 아이피 주소다. 위 명령어를 실행하면 어떤 명령어를 실행해야하는지 친절하게 알려준다. 그 명령어들을 나머지 서버에서 실행하여 Docker Swarm에 Worker들을 추가한다.

추가한 결과는 docker info 로 결과를 확인해 볼 수 있다.

위 명령어로 spark network도 생성해준다.

Create Docker Image for Spark Cluster Master

gettyimages의 Docker Image를 기반으로 진행한다. Spark를 돌리는 데에 필수적인 것은 모두 설치되어있지만 Zeppelin을 추가하고 싶어서 Dockerfile을 다음과 같이 작성했다.

중간에 보이는 pastebin 링크는 Zeppelin 포트를 8080에서 8888바꿔서 올려 놓은 것이다. Spark Master WebUI의 포트와 Zeppelin WebUI의 포트가 겹쳐서 불가피하게 Zeppelin의 포트를 다른 것으로 교체했다. 위 Dockerfile로 docker build -t adoji/spark-master:1.0.0 . 을 실행한다. spark1-5, 5개의 서버에서 전부!

Launch Service

이제 Swarm과 Image가 준비되었으니 서비스를 띄워보도록 하자. 앞서 말한 것과 같이, single host에서 image를 기반으로 container를 생성할 때 run 명령어를 썼다면, swarm에서 image 기반으로 container를 생성할 때는 service 명령어를 사용한다.

이후 공유기에서 Swarm Manager가 돌고있는 192.168.0.55:8888 를 포트 포워딩으로 뚫어준 뒤에 Spark WebUI에 접속해보면 다음과 같은 그림을 볼 수 있다. (8888 = Zeppelin WebUI, 8080 = Spark WebUI, 6066 = Spark restFUL, 7077 = Spark)

spark master web ui.png

Spark Master at Spark://10.0.0.215:7077 에 있는 저 spark url을 잘 복사해둔다.

이번에는 Worker를 띄워준다. 마지막에 위에서 복사해놓은 SparkURL을 넣어서 실행한다. 이제 Spark Cluster 설정이 완료됐다!

Zeppelin Configuration

공유기에서 포트포워딩으로 192.168.0.55:8888 를 열어준다. 접속하면 Zeppelin WebUI에 접근할 수 있다.

spark1.png

제플린 우 상단에 anonymous를 클릭한 뒤 Interpreter를 클릭한다.

 

검색 창에 spark를 검색한 후

spark3.png

오른쪽에 Edit 클릭

spark4.png

master에는 아까 복사해놓은 SparkURL, 그리고 spark.executor.memory에도 값을 넣어준다. 이 값이 비어있으면 기본적으로 1GB만 사용하니 주의.

위에 있는 간단한 테스트 코드를 실행하면

htop.png

위와 같이 htop에서 리소스 사용을 확인할 수 있다.

 

Leave a Reply