EMR上でPython3系でpysparkする


## 概要

- 機械学習・データサイエンスといえばpython（numpy/iPython/scikit-learn）なのでpythonで書いていきたい。
- しかし、仕事柄大規模なデータを扱うことになるので、分散できるsparkのAPIで取り扱いたい。

- ということで（EMR）クラスタ上でpysparkを動かしてみます。
- 今からpython学ぶなら３系だろうということで３系（Anaconda3-4.0）で動かします。
- 不安だったので確認したけど、sparkは1,4からpython3系に対応してますね。

----

## 手順

基本的にこちらの記事を参考にしました。丁寧に書かれていて素晴らしいです。

[Spark + IPython環境をAmazon EMR上で構築し、簡単なData解析を動かして見る (第1回：環境構築編)](http://qiita.com/taka4sato/items/377a9dedb6842f056e60)

- スクリプト準備
- EMRクラスタ立ち上げ
- iPython Notebook起動
- 動作確認

----

### スクリプト準備

以下の２つのshellを用意します。

- EMRのbootstrap時に全ノードにanaconda3を入れるためのスクリプト

```bash:anaconda3-bootstrap.sh
#Environments
ANACONDA_VERSION=3-4.0.0

PYENV=~/.pyenv
PYENV_BIN=$PYENV/bin


##Install git and Anaconda
sudo yum -y install git
git clone https://github.com/yyuu/pyenv.git $PYENV

echo -e "\nexport PYENV_ROOT=$PYENV" | tee -a ~/.bash_profile  >> /dev/null
echo -e "\nexport PATH=$PYENV_BIN:$PATH" | tee -a ~/.bash_profile  >> /dev/null
echo -e "\neval '$($PYENV_BIN/pyenv init -)'" | tee -a ~/.bash_profile  >> /dev/null
source ~/.bash_profile

pyenv install anaconda$ANACONDA_VERSION
pyenv rehash
pyenv global anaconda$ANACONDA_VERSION

echo -e "\nexport PATH='$PYENV/versions/anaconda$ANACONDA_VERSION/bin/:$PATH'" | tee -a ~/.bash_profile  >> /dev/null
source ~/.bash_profile
conda update --yes conda

##Install addtional python libraries
conda install --yes seaborn
```

- MasterNodeでiPyhtonの設定を行うスクリプト

```bash:setup-iPython.sh
#Environments
SPARK_PACKAGES=com.databricks:spark-avro_2.10:2.0.1,com.databricks:spark-csv_2.10:1.4.0
ANACONDA_VERSION=3-4.0.0
JUPYTER_LOG=/home/hadoop/.jupyter/jupyter.log

PYENV=~/.pyenv
PYENV_BIN=$PYENV/bin

##Configure Jupyter
jupyter notebook --generate-config

JUPYTER_NOTEBOOK_CONFIG=/home/hadoop/.jupyter/jupyter_notebook_config.py
sed -i -e "3a c.NotebookApp.ip = '*'" $JUPYTER_NOTEBOOK_CONFIG
sed -i -e "3a c.NotebookApp.open_browser =False" $JUPYTER_NOTEBOOK_CONFIG
sed -i -e "3a c.NotebookApp.port = 8080" $JUPYTER_NOTEBOOK_CONFIG
sed -i -e "3a c = get_config()" $JUPYTER_NOTEBOOK_CONFIG

IPYTHON_KERNEL_CONFIG=/home/hadoop/.ipython/profile_default/ipython_kernel_config.py
ipython profile create
sed -i -e "3a c.InteractiveShellApp.matplotlib = 'inline'" $IPYTHON_KERNEL_CONFIG


##Launch Jupyter by executing "pyspark"
JUPYTER_PYSPARK_BIN=/home/hadoop/.jupyter/start-jupyter-pyspark.sh

cat << EOF > $JUPYTER_PYSPARK_BIN
export SPARK_HOME=/usr/lib/spark/
export PYSPARK_PYTHON=$PYENV/versions/anaconda$ANACONDA_VERSION/bin/python
export PYSPARK_DRIVER_PYTHON=ipython
export PYSPARK_DRIVER_PYTHON_OPTS='notebook'
export SPARK_PACKAGES=$SPARK_PACKAGES
nohup pyspark --packages $SPARK_PACKAGES > $JUPYTER_LOG 2>&1 &
EOF

chmod +x $JUPYTER_PYSPARK_BIN
```

----

### EMRクラスタ立ち上げ

基本は上記参考記事のままなのですが、

- BootStrapActionで上記のanaconda3-bootstrap.shを読ませる。
- とりあえず使うときはSpot Instanceを使うと安いよ。
- Security-Groupは、とりあえず自分のマシンからMasterNodeにsshログインさえできればOK。
  - 実運用ではEMRを外に晒したくないので踏み台とか経由すると思います
  - が、ちょいと面倒なので、とりあえずKeyがないとsshできなくて、他のportが空いてなければいいかなと。
  - CoreNodeはとりあえずEmrManagedSlaveSecurityGroupを使っとけば大丈夫。

設定例の全体はこんな感じ。

`aws emr create-cluster --applications Name=Hadoop Name=Spark Name=Ganglia --bootstrap-actions '[{"Path":"s3://<bucket>/anaconda3-bootstrap.sh","Name":"Custom action"}]' --ec2-attributes '{"KeyName":"","AdditionalSlaveSecurityGroups":[],"InstanceProfile":"EMR_EC2_DefaultRole","SubnetId":"","EmrManagedSlaveSecurityGroup":"","EmrManagedMasterSecurityGroup":""}' --service-role EMR_DefaultRole --enable-debugging --release-label emr-4.7.2 --log-uri '' --name 'jupyter pyspark' --instance-groups '[{"InstanceCount":1,"BidPrice":"0.1","EbsConfiguration":{"EbsBlockDeviceConfigs":[{"VolumeSpecification":{"SizeInGB":32,"VolumeType":"gp2"},"VolumesPerInstance":1}]},"InstanceGroupType":"MASTER","InstanceType":"m4.large","Name":"Master instance group - 1"},{"InstanceCount":2,"BidPrice":"0.1","EbsConfiguration":{"EbsBlockDeviceConfigs":[{"VolumeSpecification":{"SizeInGB":32,"VolumeType":"gp2"},"VolumesPerInstance":1}]},"InstanceGroupType":"CORE","InstanceType":"m4.large","Name":"Core instance group - 2"}]' --configurations '[{"Classification":"spark","Properties":{"maximizeResourceAllocation":"true"},"Configurations":[]}]' --region ap-northeast-1`

----

### iPython Notebook起動

立ち上げた後、MasterNodeにSSHして、上記のsetup-iPython.shを流し、

`$JUPYTER_PYSPARK_BIN`を実行すればiPython notebookが立ち上がります。

後は自分のマシンから
`ssh -i ~/.ssh/my.pem -ND 8157 hadoop@ec2-XXXX.com`のようにsshトンネリングを行い、ブラウザにproxyを立てればOK（やり方はEMRに書いてある）

ブラウザから`http://ec2-XXXX.com:8080`に繋げたら設定完了。

----

### 動作確認

こんな感じになりました。試しにKMeansをscikit-learnと比較してみました。

https://github.com/uryyyyyyy/jupyterSamples/blob/master/20160725_pyspark_scikit_learn/pyspark.ipynb

----

## まとめ

- EMR使うとクラスタ上でのpyspark環境が楽に用意できますよ。
- ただ、sparkＣontext張りっぱなしなので一度に複数のNOTEBOOKは操作できないかも。
- iPyhtonで試して、上手く行ったらそれをそのままScalaに直して実運用！とかできそう。
- MasterNodeにもWorkerNodeにもPython３系が必要です
  - もしバージョンが合ってないと実行時に怒られます。