GCP + Docker + Jupyter Labで任意の機械学習環境をつくる

# 概要

GCP(GCE) + Docker + Jupyter Lab を使って、GPUをゴリゴリ使うpython 3.6系の機械学習用の作業環境を作ることができたので、まとめます。

## 背景

日々いろんな機械学習モデルが出てきていてGitHubにもソースコードが上がっており、気軽に試したいと思いつつも、pythonが3.6以上だったり、GPUを使わなければならなかったりします。
しかし、GCPのAIプラットフォームでノートブックインスタンスを作ろうにも、

> Python 3.5 は、AI Platform のランタイム バージョン 1.4 以上で利用できます。Python 3.5 でトレーニング ジョブを送信するには、Python のバージョンを「3.5」に、ランタイムのバージョンを 1.4 以上に設定します。
> [ランタイム バージョンの管理 TensorFlow 用 AI Platform]


とあるように、プリセットで提供されているランタイムバージョンではpython 3.5までしか無いようです。
つまり、python 3.6以上を使いたければ自作するしかないので、自作しました。

## 今回作成した環境

```
# ホストマシンの環境
~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.3 LTS"

~$ uname -r
5.0.0-1025-gcp

~$ sudo docker version
Client: Docker Engine - Community
 Version:           19.03.5
 API version:       1.40
 Go version:        go1.12.12
 Git commit:        633a0ea838
 Built:             Wed Nov 13 07:29:52 2019
 OS/Arch:           linux/amd64
 Experimental:      false
Server: Docker Engine - Community
 Engine:
  Version:          19.03.5
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.12
  Git commit:       633a0ea838
  Built:            Wed Nov 13 07:28:22 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.10
  GitCommit:        b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
  Version:          1.0.0-rc8+dev
  GitCommit:        3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

# Docker コンテナ内の環境
~$ python -V
Python 3.6.9

~$ nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2018 NVIDIA Corporation
Built on Sat_Aug_25_21:08:01_CDT_2018
Cuda compilation tools, release 10.0, V10.0.130
```

# インスタンスの作成

GCPのコンソールから以下の画像のようにポチポチと作成していきます。
ディストリビューションはデフォルトだとDebianになっているのですが、後述するNVIDIA Container Toolkit のインストールでハマってしまったので、既存のやり方を発見できたUbuntuを採用しました。

![スクリーンショット 2019-11-19 13.26.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175708/75451f2d-7668-ef0c-3c4c-0680fe94bb6a.png)
![スクリーンショット 2019-11-19 13.27.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175708/3be372dc-8c42-2cc0-b0f7-f1aad16e1fda.png)
![スクリーンショット 2019-11-19 13.27.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175708/79b5ac68-c30d-615c-36be-ebdef9dd95ea.png)

ログインコマンドは以下。手元のPCのブラウザからJupyter Labを触れるようにlocalhostのポートを開けます。

```
~$ gcloud compute ssh --zone "ZONE" "INSTANCE_NAME" \
    -- -L 8888:localhost:8888
```

# DockerコンテナからGPUが触れるようにする
このセクションの方法は [NVIDIA Container Toolkit を使って Docker コンテナで GPU を使う - CUBE SUGAR CONTAINER] のやり方を真似しました。方法はぜひリンク先のブログを参照ください 

DockerコンテナからGPUが触れるようになったかは以下のように試してみると確認できます。

```
~$ docker run --gpus all nvidia/cuda:9.0-base nvidia-smi
```
> [NVIDIA/nvidia-docker: Build and run Docker containers leveraging NVIDIA GPUs]

# 好みの環境のDockerfileを作成してGCRにpushする

このセクションでは [GCPとDockerでKaggle用計算環境構築 - Qiita] を参考にしました。詳細な方法はリンク先をご覧ください。ただし、後述しますが細かい部分で記事とは異なる部分もあります。

python 3.6を使うDockerfileは以下です。

```Dockerfile
FROM nvidia/cuda:10.0-cudnn7-devel-ubuntu16.04

# install basic dependencies
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \
    sudo git wget cmake nano vim gcc g++ build-essential ca-certificates software-properties-common \
    && rm -rf /var/lib/apt/lists/*

# install python 3.6
RUN add-apt-repository ppa:deadsnakes/ppa \
    && apt-get update \
    && apt-get install -y python3.6 python3-distutils \
    && wget -O ./get-pip.py https://bootstrap.pypa.io/get-pip.py \
    && python3.6 ./get-pip.py \
    && ln -s /usr/bin/python3.6 /usr/local/bin/python3 \
    && ln -s /usr/bin/python3.6 /usr/local/bin/python

# install common python packages
ADD ./requirements.txt /tmp
RUN pip install pip setuptools -U && pip install -r /tmp/requirements.txt

# set working directory
WORKDIR /root/user

# config and clean up
RUN ldconfig \
    && apt-get clean \
    && apt-get autoremove
```

ほとんど記事のままですが、以下の問題の対策のために1点 `python3-distutils` のインストールも追加してあります。

>Ubuntu 16.04 などで Python 3.6 を使いたい場合、こちら にある通り、 ppa:jonathonf/python-3.6 を利用します。
>2018/05/03 にそのパッケージが3.6.5に置き換わったよう で、そちらにUpgradeしたらエラーが出るようになりました。
>[ModuleNotFoundError: No module named &#39;distutils.sysconfig&#39;。2018-05-07 - Qiita]


自分は `requrirements.txt` の中身は以下のようにしました。個人のデフォルトでよく使うライブラリなど入れておくと便利だと思います。

```requrirements.txt
requests
numpy
pandas
pillow
matplotlib
jupyter
jupyterlab
scikit_learn
```

## Dockerコンテナをサーバ内に立てる

GCRに登録したDocker iamgeをサーバ内にて`docker pull`してきたらコンテナを立てます。

```
~$ docker run --name ml-workspace-container --gpus all \ 
    -p 8888:8888 -v ~/ml-workdir:/root/user/ml-workdir -itd \
    gcr.io/YOUR_PROJECT/IMAGE_NAME:TAG /bin/bash 
```
- `--name` は適当です。お好みでどうぞ
- `--gpus all` はNVIDIA Container Toolkitを使っている関係です。`--runtime=nvidia` はもう古いのでありません

## コンテナに入りJupyter Labを立ち上げる

```
~$ docker exec -it ml-workspace-container /bin/bash
~$ jupyter lab --port 8888 --ip=0.0.0.0 --allow-root
```

ここまでやれば手元のブラウザでJupyter Labが見られるようになります。

これで好きなようにtensorflowをpip installするなりして自由に実験や作業を行うことができるようになりました。
最後に過剰に課金されないように実験終了後はGCEのインスタンスを停止させるのを忘れないようにしましょう！w

みなさまのお役に立てば幸いです。

# 参考文献
- [ランタイム バージョンの管理 TensorFlow 用 AI Platform] 
- [NVIDIA Container Toolkit を使って Docker コンテナで GPU を使う - CUBE SUGAR CONTAINER]
- [NVIDIA/nvidia-docker: Build and run Docker containers leveraging NVIDIA GPUs]
- [GCPとDockerでKaggle用計算環境構築 - Qiita]
- [Google Compute Engine で SSH Port Forwarding する]
- [NVIDIA Docker って今どうなってるの？ (19.11版) - Qiita]
- [ModuleNotFoundError: No module named &#39;distutils.sysconfig&#39;。2018-05-07 - Qiita]
- [Docker Hub nvidia/cuda]


[ランタイム バージョンの管理 TensorFlow 用 AI Platform]: https://cloud.google.com/ml-engine/docs/tensorflow/versioning?hl=ja
[NVIDIA Container Toolkit を使って Docker コンテナで GPU を使う - CUBE SUGAR CONTAINER]: https://blog.amedama.jp/entry/docker-nvidia-container-toolkit
[NVIDIA/nvidia-docker: Build and run Docker containers leveraging NVIDIA GPUs]: https://github.com/NVIDIA/nvidia-docker
[GCPとDockerでKaggle用計算環境構築 - Qiita]: https://qiita.com/lain21/items/a33a39d465cd08b662f1
[Google Compute Engine で SSH Port Forwarding する]: https://blog.amedama.jp/entry/2019/03/23/151554
[NVIDIA Docker って今どうなってるの？ (19.11版) - Qiita]: https://qiita.com/ksasaki/items/b20a785e1a0f610efa08
[ModuleNotFoundError: No module named &#39;distutils.sysconfig&#39;。2018-05-07 - Qiita]: https://qiita.com/abetomo/items/84d30726782268e35ef3
[Docker Hub nvidia/cuda]: https://hub.docker.com/r/nvidia/cuda