pysparkをnotebookで書けるspark clusterをかんたんに構築できるdockerfile作ったったwww


はじめに

学部3年、現在就活中の大木です。

現在インターン先でレコメンドシステムをsparkをもちいて構築しているのですが、

EMRでsparkを動かしてゴニョゴニョする前にローカルで色々試してみたく、

またどうせならspark clusterを構築してワチャワチャやりたいと思いそのためにdockerfileを作ったので共有します。

こちら

2357gi/apache-spark-cluster: localでspark clusterを立ち上げて遊ぶ用 pyspark用のnotebookもあるヨ!


🐰使い方

cloneして

$ git clone https://github.com/2357gi/apache-spark-cluster

jupyter notebookを使いたい場合はDockerfile内の以下の環境変数を有効化して

make run &&make notebookで立ち上がります。


Dockerfile

    PYSPARK_DRIVER_PYTHON=/usr/local/bin/jupyter \

PYSPARK_DRIVER_PYTHON_OPTS="notebook --no-browser --port 8888 --ip=0.0.0.0 --allow-root"

\で環境変数を複数行に渡り書き連ねているので、その辺を気をつけてください。

pythonfileをsubmitしたい場合は上記の環境変数に関する記述を消してmake run &&make kick-py <pythonfile>でどうぞ。

UI用のポートも開放しているので

Spark Master at spark://master:7077



Spark Jobs(spark taskが実行されている瞬間のみ)

も使えるようになっています。


解説


Dockerfile

FROM ubuntu:18.04

ベースとなるイメージはUbuntu18.04を使用しています。

採用理由は個人的に使い慣れているだけです。

python等のイメージを使用しても良かったのですが、理解しながら作業を進めたかったのであえてこちらを使用しました。

# Environment variables

ENV SPARK_VERSION=2.4.0 \
HADOOP_VERSION=2.7 \
SPARK_HOME=/spark \
PATH=$SPARK_HOME/bin:$PATH \
PYSPARK_PYTHON=/usr/bin/python3 \
PYSPARK_DRIVER_PYTHON=/usr/local/bin/jupyter \
PYSPARK_DRIVER_PYTHON_OPTS="notebook --no-browser --port 8888 --ip=0.0.0.0 --allow-root"

環境変数を設定します。

SPARK_VERSIONやHADOOP_VERSIONを変数として登録し、後のsparkをダウンロードする際のダウンロードUrlに変数を使用しています。

これによりバージョン変更に強くなります。

ミソとなるのがPYSPARK_PYTHON, PYSPARK_DRIVER_PYTHON, PYSPARK_DRIVER_PYTHON_OPTSです。

PYSPARK_PYTHONここでpysparkのpythonのバージョン指定を行なっています。

PYSPARK_DRIVER_PYTHON, PYSPARK_DRIVER_PYTHON_OPTSこちらでpysparkがjupyter notebookで立ち上がるように設定しています。

githubに上がっているDockerfileにはこの2種の変数の記述はないので、

もしnotebookを使用したいのであれば適宜追加してください。

notebookの引数諸々はdocker内で起動したjupyter notebookをホスト側から接続するための諸々です。

この辺はすこしセキュアではないのですが、ローカルで遊ぶだけなので許容範囲としました。

# Install required apt package and ensure that the packages were installed

RUN apt-get update && apt-get install -y \
bc \
curl \
default-jdk \
git \
python3 \
python3-pip \
scala

(説明略)

# Install spark

RUN mkdir spark &&\
curl -sL \
http://ftp.tsukuba.wide.ad.jp/software/apache/spark/spark-${SPARK_VERSION}/spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz \
| tar zx -C spark --strip-components 1

sparkを引っ張ってきて展開し、/sparkに設置しています。

近さ的に筑波のミラーから引っ張ってきていますがココらへんはあんまりよくないかもしれません。

展開の方法ですがcurlで引っ張ってきたものをtarにパイプで渡すことによってcurl &&tar &&mv && rmなどといったナンセンスなコマンドの羅列にならないように工夫しましたがココらへんは悔しさが残ります。(詳細

RUN pip3 install jupyter ipyparallel \

&& ipcluster nbextension enable

jupyterをpipでinstallします。

pipでjupyterを導入するとpython3のkernelを読み込んでくれない?ここがすこしうまく行かなかったので

別途ipyparallelをpipでインストールしてnbextensiionをenableします。

これでjupyter notebook上でpython3がつかえるようになります。


Docker-compose

みんな大好きdocker-compose


docker-compose.yml

version: '3'

services:
master:
image: 2357gi/apache-spark
hostname: master
ports:
- 4040:4040
- 6066:6066
- 8080:8080
- 7077:7077
environment:
MASTER: spark://master:7077
SPARK_CONF_DIR: /conf
SPARK_PUBLIC_DNS: localhost
volumes:
- ./work:/work
- ./data:/data
command: /spark/bin/spark-class org.apache.spark.deploy.master.Master -h master

worker1:
image: 2357gi/apache-spark
hostname: worker1
depends_on:
- master
ports:
- 8081:8081
environment:
SPARK_CONF_DIR: /conf
SPARK_WORKER_CORES:
SPARK_WORKER_MEMORY: 2g
SPARK_WORKER_PORT: 8081
SPARK_WORKER_WEBUI_PORT: 8081
SPARK_PUBLIC_DNS: localhost
volumes:
- ./work:/work
- ./data:/data
command: /spark/bin/spark-class org.apache.spark.deploy.worker.Worker spark://master:7077

worker2:
image: 2357gi/apache-spark
hostname: worker1
depends_on:
- master
ports:
- 8082:8082
environment:
SPARK_CONF_DIR: /conf
SPARK_WORKER_CORES:
SPARK_WORKER_MEMORY: 2g
SPARK_WORKER_PORT: 8082
SPARK_WORKER_WEBUI_PORT: 8082
SPARK_PUBLIC_DNS: localhost
volumes:
- ./work:/work
- ./data:/data
command: /spark/bin/spark-class org.apache.spark.deploy.worker.Worker spark://master:7077


$ docker-compose up --scale worker=nでスケールするようにしたかったのですが、

ポートの変更とSPARK_WORKER_PORTの変数を連動させるのが難しかったのでこの形に落ち着きました。

誰か氏〜〜〜!w


Makefile


WORKER=1

build_docker: Dockerfile
@echo "\n📦 build 2357gi/apache-spark"
@docker build ./ -t 2357gi/apache-spark

build_compose:
@echo "\n🏗 Pile up containers"
@docker-compose up -d --scale worker=$(WORKER)

run: docker-compose.yml
@echo "\n✨ apache-spark cluster setup is start!"
@make build_docker
@make build_compose
@echo "\n🍾 set up is finished! \n you wanna access ⚡spaek shell, hit it! \n$$ make s-shell"
@echo "or you wanna start 📔 pyspark notebook, hit it! \n$$ make notebook"
@echo "... if u wanna open 🐚 shell? do this. \n$$ make shell"

s-shell:
@echo "\n⚡ start spark-shell...."
@docker-compose exec master /spark/bin/spark-shell --master spark://localhost:7077

notebook:
@echo "\n📔 start pyspark notebook...."
@docker-compose exec master /spark/bin/pyspark
@docker-compose exec

shell:
@echo "\n🐚 start master shell...."
@docker-compose exec master /bin/bash

WORKERを変数にすることによって$ make run WORKER=<任意のworker node数を指定できるようにしました。

あとは普通な感じです。

これは蛇足ですが、

makefileに絵文字を入れると可愛くていいですね。