dockerコンテナを用いた開発

## dockerコンテナを使って開発しよう
環境が汚れないし再現性も高められるので何かと良さそうである。

### step1. Dockerrfileを書こう
まずは、基本となるイメージを何にするかを考える。このときには、使いたいライブラリの要件などによって選べばいいと思う。[tensorflowが最初から使えるイメージ](https://hub.docker.com/r/tensorflow/tensorflow/)や[pytorchが使えるイメージ](https://hub.docker.com/r/pytorch/pytorch/tags)が公開されているのでそちらを使えばいいし、特に決まっていないのであれば`nvidia/cuda:9.0-cudnn7-runtime-ubuntu16.04`などを入れれば良い。いずれにせよ、自分がどのバージョンを入れるかを考える時に必要になるのが、CUDAとcuDNNのバージョンである。

#### CUDAとcuDNNの話
CUDAはNvidiaとかのGPUを使うのに必要だが、これはDockerイメージを引っ張ってくることが出来る。
まずは、

```bash
nvidia-smi
```

を叩いて、出てきた結果の一番上のDriver Versionに着目しよう。
![スクリーンショット 2020-01-28 20.13.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295781/ff8bf3f7-2c77-bbff-d19d-287ee11ddd02.png)
このドライバーはDockerコンテナでどうにか出来るものではないので、これによってバージョンが規定される。
次に、[Cuda compatibilityのページ](https://docs.nvidia.com/deploy/cuda-compatibility/index.html)を見て、どのバージョンまでのCUDAなら入るかを確認する。この場合は、CUDA10.1までは入ることがわかる。
![スクリーンショット 2020-01-28 20.15.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295781/38580d73-7c82-338f-5733-9a10f545c8ae.png)（2020/01/28時点）

さて、そうしたら[Dockerのホームページ](https://hub.docker.com/r/nvidia/cuda/tags?page=1)に行って適切なタグを探そう。今回はできるだけ新しいものを入れたかったので、`10.1-cudnn7-runtime-ubuntu16.04`を使った。

```text:Dockerfile
FROM nvidia/cuda:10.1-cudnn7-runtime-ubuntu16.04
RUN hogehoge
```
とすれば完成である。
例えば、pytorchの1.1.0じゃなくて1.2.0が使いたい時などは、CUDA>=9.2が要求されるので、このように調べて適切なコンテナ内でインストールする必要がある。



### step2. docker-composeを使おう
docker-compose を使わないと、コンテナを作る時に
`docker run -it --shm-size=8g -v /home/src:/root/src -p 8080:8080 (image_name)`といった感じのコマンドを打つ必要があります。長くてめんどくさいです。ファイルに書いてしまいましょう。

docker-compose.ymlをDockerfileと同じフォルダに作成する。
#### 書こう
下記のものさえ書いてあれば最低限やりたいことはできる。

```yml:docker-compose.yml
version: "2.3" #docker-compose のバージョンであって自分が作っている物のバージョンではない
services:
  algorithm_module:
    build:
      context: . #これで同じディレクトリのDockerfileを参照することを指定
    image: IMAGE_NAME #この名前のイメージが、`docker images`で出てくるようになる。
    command: /bin/bash #入った時にどうするか
    volumes: #ディレクトリをマウントする。これは後から変えられないのでとても大事。開発のしやすさはここをしっかり考えることで上がる。
      - .:/root/hoge #現在のディレクトリを/root/hogeにマウント　ソースコードフォルダをマウントしよう
      - /datafolder:/root/datafolder #データを置いている場所は忘れずマウント
      - `~/.ssh/id_rsa:/root/.ssh/id_rsa` -> githubで使う
    ports:
      - 8899:8899 #これも書いておかないと後述のJupyter系が使えないので大事。他の人とサーバーを共有している場合は他の人が使わなさそうな番号にしておこう。
    runtime: nvidia #GPUを使うなら書こう
    shm_size: 4g  #メモリのサイズ。GPUのメモリではなくCPUのメモリ
```
#### buildしよう
中に色々書き込む。
`docker build`
をしたらしばらく待つ。

#### runしよう
docker composeを使うときは多少注意が必要で、
docker-compose.ymlに
-ports 8888:8888
と書いた上で、
docker-compose run -p 8888:8888 module
としなければならない。こうしないとバグる。


--runtime=nvidia を入れないとGPUが使えないので注意


### step3. jupyterlab/notebook を使用しよう
`jupyter lab --port=8080 --allow-root --ip '0.0.0.0'`と打つことで手元のパソコンで`localhost:8080`と打つとJupyter labを開くことが出来ます。
しかし、毎回打つのはめんどくさいですので、設定に書いてしまいましょう。
#### やること
`jupyter notebook --generate-config`
でconfigを作成する。パスが表示されるのでそれをエディタで開き、
`vim /home/ubuntu/.jupyter/jupyter_notebook_config.py`

①

```
## Whether to allow the user to run the notebook as root.
#c.NotebookApp.allow_root = False
```
を探し、その下に
`c.NotebookApp.allow_root = True`
を書き加える。
②同様に、

```
## The port the notebook server will listen on.
#c.NotebookApp.port = 8888
```

`c.NotebookApp.port = 8080`
③

```
## The IP address the notebook server will listen on.
#c.NotebookApp.ip = 'localhost'
```

`c.NotebookApp.ip = '0.0.0.0'`

を書き加える。こうすることで、`jupyter lab`のコマンドのみで開けるようになる。

tokenが毎回求められてめんどくさいので、
```
jupyter notebook password
```
で設定してしまえば楽。


### Jupiter lab extensionを使いこなそう

### code foldingはextensionがいらない。
コードの折りたたみはあるとかなり快適です。普通に使っていると出てこなくて、extensionを調べていてもなかなか見つからなかったのですが、どうやら最近[ jupyter labの基本に実装されたらしい](https://github.com/jupyterlab/jupyterlab/issues/4083#issuecomment-481453048)ので是非使って下さい。
やることは、
Settings -> Advanced Settings Editor -> Text Editor を押して、右側の自由に編集できる部分に

```yml
{
    "editorConfig": {
         "codeFolding": true
    }
}
```

を貼り付けるだけです。


## ssh 周りの話

ssh -L8888:localhost:8888 username@hostname


`vim .ssh/config`で開き

```
Host 分かりやすい名前
    HostName ip.ip.ip.ip
    User YOURNAME
    IdentityFile ~/.ssh/id_rsa
    ForwardAgent yes
```
などと書き込む。詳しくは[こちら](https://qiita.com/passol78/items/2ad123e39efeb1a5286b)を参考にした。

ssh -A で秘密鍵を携えてsshすることができる。これによってサーバーに秘密鍵を置く必要がなくなるので嬉しい。
ssh-add で毎回id_rsa用のパスワードを聞かれることがなくなる。楽である。

また、
`ssh -NfL localhost:8889:localhost:8889`
をするとポートフォワーディングができる。