TensorFlow + Keras + Jupyter lab の環境を docker イメージを作って動作させる（作成中）

こんにちは、chlochanです。nvidia-docker2 を使って Ubuntu16.04 + CUDA9.0 の環境を作りその上で TensorFlow Ver1.12 + Keras + Jupyter lab を実行します。既に TensorFlow org でdockerイメージを用意しているのになぜそんな事をしたいかと言うと、TensorFlow org の docker イメージは IDEが Jupyter notebook なので、それを Jupyter lab に変更したいのが動機です。また、RStudio の docker イメージを作ったので、いっその事それと合体して運用しようという思惑があります。

##docker のインストールから RStudio の docker イメージ作成まで
RStudio の docker イメージの作成については以下を参照してください。
[RStudio+Keras を CUDA9.0 の docker イメージを作って動作させる](https://qiita.com/chlochan/items/ab809b9293ff74404a2d)

##TensorFlow の Dockerfile を調べて作成方針を決める
TensorFlow org で用意している Dockerfile をダウンロードします。GitHubから最新のtensorflowを持ってくるとその中にDockerfileも入っていますので以下で持ってこれます。

```bash
$ git clone https://github.com/tensorflow/tensorflow
$ cd tensorflow
$ git checkout r1.12
```
nvidia の GPU向けには、tensorflow/tensorflow/tools/dockerfiles/dockerfiles に以下の4つのDockerfileがありました。

```
nvidia-devel-jupyter.Dockerfile
nvidia-devel.Dockerfile
nvidia-jupyter.Dockerfile
nvidia.Dockerfile
```
この中で、一番いろいろインストールされている
nvidia-devel-jupyter.Dockerfile
を使用する事にします。
このファイルのFROM行を調べたところ、以下の様になっていました。

~~~docker:nvidia-devel-jupyter.Dockerfile
... 省略 ...
ARG UBUNTU_VERSION=16.04
FROM nvidia/cuda:9.0-base-ubuntu${UBUNTU_VERSION}
... 省略 ...
~~~
cuda9.0で、ubuntu16.04 を元にイメージが作成されています。
RStudio の docker イメージを作成しましたが、それと同じCUDAバージョンとディストリビューションなので、作成したRStudio の docker イメージにさらに追加する事にします。jupyter-labという名称にしました。

**cuda:9.0-cudnn7-devel-ubuntu16.04 ⇒ r-ver ⇒ shiny ⇒ rstudio ⇒ tidyverse ⇒ verse ⇒ jupyter-lab**

##Dockerfile を作成する
方針が決まったので、Dockerfile を作成していきます。nvidia-devel-jupyter の FROM 行を以下の様に入れ替えたバージョンを作ります。リポジトリの名称やディレクトリ構成は、RStudio の docker イメージを作成したときと同じにしています。

~~~docker:c9rocker/jupyter-lab/Dockerfile 
... 省略 ...
FROM c9rocker/verse:latest
... 省略 ...
~~~


#... 以降は作成中 ...
rstudio の変更箇所は、FROM行の変更の他に、libssl1.0.0.deb のダウンロード元の変更と、Keras の実行に必要なRUNコマンドの追加があります。
以下の４つのライブラリはインストール済みなので削除します。
cuda-command-line-tools-9-0
cuda-cublas-9-0
libcudnn7=7.2.1.38-1+cuda9.0
libnccl2=2.2.13-1+cuda9.0

~~~docker:c9rocker/jupyter-lab/Dockerfile 
FROM nvidia/cuda:9.0-base-ubuntu16.04
# Pick up some TF dependencies
RUN apt-get update && apt-get install -y --no-install-recommends build-essential \
        cuda-cufft-9-0 cuda-curand-9-0 cuda-cusolver-9-0 cuda-cusparse-9-0 \
        libfreetype6-dev libhdf5-serial-dev libpng12-dev libzmq3-dev pkg-config \
        software-properties-common unzip && apt-get clean && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install nvinfer-runtime-trt-repo-ubuntu1604-5.0.2-ga-cuda9.0 && \
        apt-get update && apt-get install libnvinfer4=4.1.2-1+cuda9.0
ARG USE_PYTHON_3_NOT_2=True
ARG _PY_SUFFIX=${USE_PYTHON_3_NOT_2:+3}
ARG PYTHON=python${_PY_SUFFIX}
ARG PIP=pip${_PY_SUFFIX}
RUN apt-get update && apt-get install -y ${PYTHON} ${PYTHON}-pip
RUN ${PIP} install --upgrade pip setuptools
ARG TF_PACKAGE=tensorflow-gpu
RUN ${PIP} install ${TF_PACKAGE}
COPY bashrc /etc/bash.bashrc
RUN chmod a+rwx /etc/bash.bashrc
RUN mkdir /notebooks && chmod a+rwx /notebooks
RUN mkdir /.local && chmod a+rwx /.local
RUN apt-get install jupyter-notebook -y
RUN ${PIP} install jupyterlab
RUN jupyter serverextension enable --py jupyterlab --sys-prefix
RUN jupyter notebook --generate-config
RUN sed -i -e "s/#c.NotebookApp.ip = 'localhost'/c.NotebookApp.ip = '0.0.0.0'/g"\
    .jupyter/jupyter_notebook_config.py
WORKDIR /notebooks
EXPOSE 8888
CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter lab --notebook-dir=/notebooks \
      --no-browser --allow-root"]
~~~                                         

~~~docker
cuda-curand-9-0
cuda-cusolver-9-0
cuda-cusparse-9-0
cuda-cufft-9-0
nvinfer-runtime-trt-repo-ubuntu1604-4.0.1-ga-cuda9.0
libnvinfer4=4.1.2-1+cuda9.0
libfreetype6-dev
libhdf5-serial-dev
libpng12-dev
libzmq3-dev
pkg-config
software-properties-common

cuda-command-line-tools-9-0
cuda-cublas-9-0
libcudnn7=7.2.1.38-1+cuda9.0
libnccl2=2.2.13-1+cuda9.0

cuda-command-line-tools-cuda9.0
cuda-cublas-9-0=9.0.176.4-1
cuda-cublas-dev-9-0=9.0.176.4-1
libcudnn7=7.4.1.5-1+cuda9.0
libcudnn7-dev=7.4.1.5-1+cuda9.0
libnccl2=2.3.7-1+cuda9.0
libnccl-dev=2.3.7-1+cuda9.0

cuda-libraries-dev-cuda9.0
cuda-nvml-dev-cuda9.0
cuda-minimal-build-cuda9.0
cuda-core-9-0=9.0.176.3-1
cuda-libraries-cuda9.0
cuda-cudart-9.0.176

nvidia-machine-learning-repo-ubuntu1604_1.0.0-1_amd64.deb 4.0KB 2018-09-26 23:25
nvinfer-runtime-trt-repo-ubuntu1604-5.0.2-ga-cuda9.0_1-1_amd64.deb 73MB 2018-11-06 18:26


~~~                                         

~~~docker:c9rocker/rstudio/Dockerfile 
FROM c9rocker/shiny:latest
... libssl1.0.0.deb に関係する部分までは rocker/rstudio と全く同じなので略 ...
  && wget -O libssl1.0.0.deb http://security.ubuntu.com/ubuntu/pool/main/o/openssl/\
libssl1.0.0_1.0.2g-1ubuntu4.14_amd64.deb \
... RUNコマンドを追加します ...
RUN apt-get update
RUN apt-get -y upgrade
RUN apt-get install python-pip
RUN apt-get install python-virtualenv
... 以下は rocker/rstudio と全く同じなので略 ...
~~~
上記の様に、ディストリビューションを debian:stretch から ubuntu16.04 に変更したことから、rstudio の Dockerfile 中で、libssl1.0.0.deb の入力先とファイル名を以下の様に変更しました。変更しないとエラーになります。

http://ftp.debian.org/debian/pool/main/o/openssl/libssl1.0.0_1.0.1t-1+deb8u8_amd64.deb
　　　　　　　**↓**
http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.0.0_1.0.2g-1ubuntu4.14_amd64.deb

tidyverse と verse の変更箇所は FROM行だけです。

~~~docker:c9rocker/tidyverse/Dockerfile 
FROM c9rocker/rstudio:latest
... 以下は rocker/tidyverse と全く同じなので略 ...
~~~

~~~docker:c9rocker/verse/Dockerfile 
FROM c9rocker/tidyverse:latest
... 以下は rocker/tidyverse と全く同じなので略 ...
~~~

## dockerイメージをビルドする時にコピーされるファイルを用意する
GitHubからクローンした rocker-versioned/rstudio　の中の sh ファイルを ./rstudio ディレクトリに持ってきて実行属性を追加します。また、disable_auth_rserver.conf ファイルも持ってきます。下記コマンドの前提条件として、一個上のディレクトリに rocker-versioned をクローンしてあるものとします。また rstudio の Dockerfile は ./rstudio にあるものとします。

```bash
$ cp ../rocker-versioned/rstudio/disable_auth_rserver.conf ./rstudio
$ cp ../rocker-versioned/rstudio/*.sh ./rstudio
$ chmod +x ./rstudio/*sh
```
同様に、GitHubからクローンした rocker/shiny の中の shiny-server.sh ファイルを ./shiny ディレクトリに持ってきて実行属性を追加します。

```bash
$ cp ../shiny/shiny-server.sh ./shiny
$ chmod +x ./shiny/*sh
```
## dockerイメージをビルドする
dockerイメージを下層から順番にビルドします。ディレクトリ c9rocker でイメージをビルドします。tidyverse はかなり時間がかかります。

```bash
$ sudo docker image build -t c9rocker/r-ver:latest ./r-ver
$ sudo docker image build -t c9rocker/shiny:latest ./shiny
$ sudo docker image build -t c9rocker/rstudio:latest ./rstudio
$ sudo docker image build -t c9rocker/tidyverse:latest ./tidyverse
$ sudo docker image build -t c9rocker/verse:latest ./verse
```
dockerイメージが出来ていることを確認します。

```bash
$ sudo docker image ls
```
Dockerfile 中の文法ミスなどでイメージ作成の失敗を繰り返すと、中途半端なコンテナが沢山出来てしまっている事があります。よってコンテナを一旦全部削除します。

```bash
$ sudo docker rm $(sudo docker ps -aq)
```
## コンテナを起動する
c9rocker/verse:latest を起動します。なお、起動時のコマンドで RStudio にログインする時のパスワードを設定します。**`<YOUR_PASS>`** の部分を自分の好きなパスワードに置き換えて実行します。なお、'rstudio' 以外のパスワードにする必要があります。

```bash
$ chmod 777 ~/Downloads
$ sudo docker run --runtime=nvidia -v ~/Downloads:/home/rstudio/Downloads \
  -e PASSWORD=<YOUR_PASS> -d -p 3838:3838 -p 8787:8787 c9rocker/verse:latest
```
上記の例では、ホスト側のディレクトリ ~/Downloads を、コンテナ側のディレクトリ /home/rstudio/Downloads とRW可能で共有しています。また、shiny と RStudio のポートを両方そのままホスト側にフォワードしています。 "-d" を指定することでバックグラウンドで起動します。
コンテナが起動出来たか確認してみます。

```bash
$ sudo docker ps -a
```
# Jupyter lab にログインする
起動出来ていたら、既に外部から接続できる様になっています。ファイアウォールでポート3838と8787を開けます。そして、
**`http://外部IPアドレス:8787`**
で、手元のブラウザからアクセスすると RStudioに接続できます。RStudioのログイン画面がでたら、ログインします。ユーザー名 rstudio 、パスワードはコンテナ起動時に設定したパスワードでログインします。同様に shiny にはポート3838で接続できます。

次に、c9rocker/verse:latest に、Keras と tensorflow をインストールします。

# 必要なライブラリをインストールする
RStudioにログインして、サンプルコードを実行する時に必要なライブラリをまずインストールします。

```R
install.packages("gridExtra")
install.packages("magick")
install.packages("viridis")
install.packages("hashFunction")
```
次にKerasをインストールします。

# Keras をインストールする
R版 の Keras は GitHub の、[R interface to Keras](https://github.com/rstudio/keras) で公開されています。
RStudio にログインして、Keras と tensorflow-gpu をインストールします。以下を実行します。

~~~R
library(devtools)
devtools::install_github("rstudio/keras")
library(keras)
install_keras(tensorflow = "gpu")
~~~
上記で、Keras と同時に tensorflow-gpu バックエンドもインストールしています。2018/12/9 時点で、実行後、RStudio の Packages の Version には、2.2.4 が表示されました。

## サンプルプログラムをダウンロードして実行する
GitHubから下記の教科書のサンプルプログラムをダウンロードして実行します。
[RとKerasによるディープラーニング](https://www.oreilly.co.jp/books/9784873118574/)

```bash
$ git clone https://github.com/jjallaire/deep-learning-with-r-notebooks.git
```
## GPU の utilization を確認する
サンプルコード実行中に、GPU の utilization を確認するには、別ウインドウを開いて以下を実行します。以下の例では1秒毎に表示が更新されます。

```bash
$ watch -n 1 nvidia-smi -a --display=utilization
```
## コンテナの停止
コンテナを停止します。

```bash
$ sudo docker stop $(sudo docker ps -q)
```
コンテナが停止出来たか確認してみます。

```bash
$ sudo docker ps -a
```

以上です。