TensorFlowOnSparkをDocker runしてみた

# TensorFlowOnSparkをDocker runしてみた

TensorFlowOnSparkのスタンドアローン版をDockerイメージにしてみました。

TensorFlowOnSparkはTensorFlowをSpark上で稼働させられるようにしたもので、Yahooが先週くらいに発表していました（2017/02/13）。
https://github.com/yahoo/TensorFlowOnSpark/wiki

出た当日にスタンドアローン版の構築を試してみました。
http://qiita.com/cvusk/items/39a1861715e21b1dc191
ついでにDockerfileにしましたのが今回です。
練習がてら。


## TensorFlowOnSpark on Docker
ホストOSにはCentOS7.3を使っています。
Dockerのインストール、起動以外は以下のとおりです。


```
sudo yum -y install docker
sudo systemctl start docker
sudo systemctl enable docker

```

Dockerfileを作るディレクトリを用意します。


```

sudo su -
mkdir -p /opt/tfos
cd /opt/tfos

```


今回はUbuntuイメージを使います。
https://hub.docker.com/_/ubuntu/
事前にpullしておきます。

```
docker pull ubuntu

```

## Dockerfileを作る
/opt/tfos/配下にDockerfileを作成し、以下を記入して保存します。
（2017/02/23　修正しました。）


```
FROM ubuntu
MAINTAINER cvusk

# insert hostname on environmental variable
ENV HOSTNAME tensorflow.spark

# install prerequisites
RUN apt-get -y update
RUN apt-get -y upgrade
RUN apt-get -y install apt-utils
RUN apt-get -y install software-properties-common python-software-properties
RUN add-apt-repository ppa:openjdk-r/ppa
RUN apt-get -y update
RUN apt-get -y install wget curl zip unzip vim openjdk-7-jre openjdk-7-jdk git python-pip python-dev python-virtualenv
ENV JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64

# git clone tensorflowonspark.git
WORKDIR /opt/
RUN git clone --recurse-submodules https://github.com/yahoo/TensorFlowOnSpark.git

WORKDIR /opt/TensorFlowOnSpark/
RUN git submodule init
RUN git submodule update --force
RUN git submodule foreach --recursive git clean -dfx

# environmental variable for tensorflowonspark home
ENV TFoS_HOME=/opt/TensorFlowOnSpark

WORKDIR /opt/TensorFlowOnSpark/src/
RUN zip -r /opt/TensorFlowOnSpark/tfspark.zip /opt/TensorFlowOnSpark/src/*
WORKDIR /opt/TensorFlowOnSpark/

# setup spark
RUN sh /opt/TensorFlowOnSpark/scripts/local-setup-spark.sh
ENV SPARK_HOME=/opt/TensorFlowOnSpark/spark-1.6.0-bin-hadoop2.6
ENV PATH=/opt/TensorFlowOnSpark/src:${PATH}
ENV PATH=${SPARK_HOME}/bin:${PATH}
ENV PYTHONPATH=/opt/TensorFlowOnSpark/src


# install tensorflow, jupyter and py4j
RUN pip install pip --upgrade
RUN python -m pip install tensorflow
RUN pip install jupyter jupyter[notebook]
RUN pip install py4j


# download mnist data
RUN mkdir /opt/TensorFlowOnSpark/mnist
WORKDIR /opt/TensorFlowOnSpark/mnist/
RUN curl -O "http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz"
RUN curl -O "http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz"
RUN curl -O "http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz"
RUN curl -O "http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz"


# create shellscript for starting spark standalone cluster
RUN echo '${SPARK_HOME}/sbin/start-master.sh' >> ${TFoS_HOME}/spark_cluster.sh
RUN echo 'MASTER=spark://${HOSTNAME}:7077' >> ${TFoS_HOME}/spark_cluster.sh
RUN echo 'SPARK_WORKER_INSTANCES=2' >> ${TFoS_HOME}/spark_cluster.sh
RUN echo 'CORES_PER_WORKER=1' >> ${TFoS_HOME}/spark_cluster.sh
RUN echo 'TOTAL_CORES=$((${CORES_PER_WORKER}*${SPARK_WORKER_INSTANCES}))' >> ${TFoS_HOME}/spark_cluster.sh
RUN echo '${SPARK_HOME}/sbin/start-slave.sh -c $CORES_PER_WORKER -m 3G ${MASTER}' >> ${TFoS_HOME}/spark_cluster.sh
RUN echo '${SPARK_HOME}/sbin/start-slave.sh -c $CORES_PER_WORKER -m 3G ${MASTER}' >> ${TFoS_HOME}/spark_cluster.sh


ENV MASTER=spark://${HOSTNAME}:7077
ENV SPARK_WORKER_INSTANCES=2
ENV CORES_PER_WORKER=1
ENV TOTAL_CORES=2


# create shellscript for pyspark on jupyter and mnist data
WORKDIR /opt/TensorFlowOnSpark/

RUN echo "PYSPARK_DRIVER_PYTHON=\"jupyter\" PYSPARK_DRIVER_PYTHON_OPTS=\"notebook --no-browser --ip=* --NotebookApp.token=''\" pyspark  --master ${MASTER} --conf spark.cores.max=${TOTAL_CORES} --conf spark.task.cpus=${CORES_PER_WORKER} --py-files ${TFoS_HOME}/tfspark.zip,${TFoS_HOME}/examples/mnist/spark/mnist_dist.py --conf spark.executorEnv.JAVA_HOME=\"$JAVA_HOME\"" > ${TFoS_HOME}/pyspark_notebook.sh

RUN echo "${SPARK_HOME}/bin/spark-submit --master ${MASTER} ${TFoS_HOME}/examples/mnist/mnist_data_setup.py --output examples/mnist/csv --format csv" > ${TFoS_HOME}/mnist_data_setup.sh


```


やっていることとして、TensorFlowOnSparkスタンドアローン版の構築手順をDocker用に書き換えています。
https://github.com/yahoo/TensorFlowOnSpark/wiki/GetStarted_standalone

spark master、slaveの起動をシェルスクリプトにしておきたかったので、その変数のために環境変数にHOSTNAME=tensorflow.sparkを定義しています。
もうちょいかっこいい方法でホスト名を使いたいのですが・・・。


これをビルドします。

```
cd /opt/tfos
docker build -t tfos/tfos .

```

こんな感じにできあがります。

```
docker images

```

![2017-02-22.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/c7de4532-52af-3e01-7d81-3a96180a0b28.png)



## 動かしてみます
Dockerイメージができあがったら以下でrunします。

```
docker run -it -p 8889:8888 -h tensorflow.spark 5db5add35c10 /bin/bash

```

ポート番号は8889:8888（ホストOSのポート：コンテナのポート）で公開します。これはpyspark（Jupyter Notebook）用です。
ホスト名にtensorflow.sparkを指定しますが、これはDockerfileに書いたものを使っています。


![2017-02-22_3.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/75c73d42-0202-4853-8c07-b6333c14f823.png)



はい、起動しました。

/opt/TensorFlowOnSpark配下のspark_cluster.shを実行すると、このDockerコンテナがspark master兼slaveになります。
なお、slaveは２インスタンス起動しています。


```
cd /opt/TensorFlowOnSpark
sh ./spark_cluster.sh

```

![2017-02-22_4.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/43f5abf2-91e4-9fe4-7ec6-411d2cf30f73.png)


/opt/TensorFlowOnSpark/配下にMNISTのデータセットアップ用シェルスクリプトを用意しています。
これを実行して、MNISTのトレーニングとテストデータを生成します。

```
cd /opt/TensorFlowOnSpark/
sh ./mnist_data_setup.sh

```


/opt/TensorFlowOnSpark/配下にpysparkの起動用シェルスクリプトとしてpyspark_notebook.shを配置しています。

これを実行すると、pysparkがJupyter Notebookで起動します。


```
cd /opt/TensorFlowOnSpark/
sh ./pyspark_notebook.sh

```

![2017-02-22_5.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/40427d08-cf65-2e0b-a870-bb4eeadac8d9.png)



起動できました。
ブラウザで　http://<ホストOSのIPアドレス>:8889　にアクセスします。


![2017-02-22_6.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/01d78640-3871-3f25-9f21-4ac9dfbe64ca.png)



このように、Jupyter Notebookにアクセスできます。

## 最後に・・・できなかったこと（誰か教えてくださいm(_ _)m）　→　解決しました。

~~ここでMNISTのデモ用に用意されている　TFOS_spark_demo.ipynb　を実行したいのですが、com.yahoo.ml.tf　がインポートできなくてエラーになります。~~
~~(T_T)~~


~~ググっても情報が出てきませんし、どなたか解決策をご存じないでしょうか・・・？
なんか半煮えな終わり方ですが、ひとまずTensorFlowOnSparkOnDockerを構築できました（？）。~~


解決しました。
PYTHONPATHの指定が足りなかったようです。
Dockerfileに ENV PYTHONPATH=/opt/TensorFlowOnSpark/src 追加しました。

これでMNISTのデモ＠Jupyter Notebookも成功します。

![2017-02-22_8.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/e2e95fe9-b6d9-c166-a653-2153f0c687df.png)