深層学習用Dockerを作る際の注意点

# はじめに
Dockerの利用の主なターゲットとしてはWebサービスの運用などが想定されているように思えます。
よってあまり書かれていない微妙に注意が必要な点があったので良い機会なので書き留めていこうと思います。

# GPUとの連携
NVIDIAのGPU関係との連携はネイティブのDockerではサポートされておらず、nvidia-dockerを入れる必要があります。
インストールにあってはNVIDIAの佐々木さんの書いた
[NVIDIA Docker って今どうなっているの？](https://medium.com/nvidiajapan/nvidia-docker-%E3%81%A3%E3%81%A6%E4%BB%8A%E3%81%A9%E3%81%86%E3%81%AA%E3%81%A3%E3%81%A6%E3%82%8B%E3%81%AE-20-09-%E7%89%88-558fae883f44
)が最新の情報をフォローしていていいように思えます。
最新の情報的には [nvidia-docker2](https://github.com/NVIDIA/nvidia-docker)を入れればOKになっています。

# イメージ
基本的にはpytorch/tensorflowの公式のイメージを使いましょう。
## pythonバージョン指定問題
再現性を確保するために公式のイメージでは足りない場合、pythonをビルドしたりする必要があります。なんか苦戦した覚えがあったのでメモしておこうと思ったのですが、最近はわかりやすいページができていました。pyenvを使ってビルドするのが手軽なように思えます。
https://www.python.jp/install/build_python_unix.html


# Docker内でのnvidia-smiの結果について
nvidia-smiを使うとCUDAのバージョンが表示されますが、これはDocker内で有効化されているCUDAのバージョンとは何も関係なく、ドライバーがサポートするCUDAのバージョンが表示されるだけのようなので、意味がないです。nvidia-dockerを使うモチベーションのひとつに古いCUDAのライブラリを使いたいというものがあるので混乱しがちなので注意しましょう。
バージョンを見たい場合は`nvcc -V`やインストールされているパスを確認するのが一般的なようです。

https://stackoverflow.com/questions/53422407/different-cuda-versions-shown-by-nvcc-and-nvidia-smi


# CUDAを使ったビルドについて
デフォルトではdocker buildの間に、CUDAなどを使うことができません。これはpytorch/tensorflowなどを使っている場合は問題ないですが、独自にライブラリをビルドしたかったりする場合は不便です。これは`/etc/docker/daemon.json`でデフォルトのランタイムをいじることで解決します。
https://stackoverflow.com/questions/59691207/docker-build-with-nvidia-runtime


```
{
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}
```




# docker-composeの利用
正直言って、composeは深層学習用には不要かもしれないです。composeは複数のコンテナを建てなければいけないような状況に向けて作られており、単に適当に学習させるコンテナを建てるだけならオーバースペックの感もあり、さらにnvidiaランタイムとの相性はイマイチです。
composeの利用には

さてcomposeはyamlファイルは結構独特のフォーマットで、最初に使ったときは混乱した覚えがあります。yamlファイルを扱ったことのない人は一度[シンタックス](https://magazine.rubyist.net/articles/0009/0009-YAML.html)を確認しておくとよいでしょう。

composeで出来ることは基本的にコマンドラインからできるので、適当なシェルスクリプトを作った方が良いというシチュエーションもあるかもしれないということを踏まえつつ書きます。

```
    runtime: nvidia
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
      - NVIDIA_DRIVER_CAPABILITIES=all
```

複数米のGPUのうち一部のGPUのみ使いたい場合は
```
    runtime: nvidia
    environment:
      - NVIDIA_VISIBLE_DEVICES=0,1
      - NVIDIA_DRIVER_CAPABILITIES=all
```
などのように書きます。

# ボリュームマウント
## ボリュームのマウント方法
学習用として使いたい場合、結果をコンテナの外に出力したくなります。またデータセットをマウントしたくなることがあります。このためにはVolumeのマウントが必要です。
Dockerのvolumeのマウント方法には二通りの方法があり、一つが`-v/-volume`　オプションもう一つが`--mount`オプションです。--mount オプションは`-v`オプションの上位互換です。docker-composeだとこちらの方が読みやすい気がするのでこちらがおすすめです。
以下`mount`オプションについて軽く説明します。`mount`には`bind`/`volume`/`mount`の三つのオプションがあります。
### bind 
おそらく機械学習などで使う際には手軽に使うには`bind`が便利です。bindはsrcのパスを直接Docker上にマウントできるものです。データセットのマウント、結果用フォルダのマウント両方できます。
### mount
`volume`は書き込んだデータがDocker専用になるので、外から使う時に面倒です。またコンテナ外データを参照するといった作業にも不向きです。
ただしDocker間でデータを共有したい、などの時にはより推奨されるオプションのようです。
### tmpfs
`tmpfs`はメモリ上に一時的なディレクトリを作るというもので、保存には使えないです。


詳細は公式ドキュメントを読むといいでしょう。この三つのページとても似ているため非常に判別がつきにくいです。。。

* [bindマウントの解説](https://docs.docker.com/storage/bind-mounts/)
* [volumeマウントの解説](https://docs.docker.com/storage/volumes/)
* [tmpfsマウントの解説](https://docs.docker.com/storage/tmpfs/)


## Owner 問題
しかしDockerでコンテナにログインすると基本はrootとしてログインすることになります。その状態でファイルを生成するとファイルの権限がrootになってしまいコンテナ外からデータの読み書きをしたい際に非常に不便です。そのため、Dockerfile内にユーザーを作ってログインをしたいところですが、Dockerfileにログインユーザー名を書くと再利用性が下がる懸念があります。

この問題には[dockerでvolumeをマウントしたときのファイルのowner問題](https://qiita.com/yohm/items/047b2e68d008ebb0f001)が参考になりました。
最も手軽なのは`/etc/group`と`/etc/passwd`をマウントしてしまう方法です。
`/etc/passwd`と聞くと何かパスワード関連のファイルをマウントしている感じがあり怪しげですが、最近のディストリビューションでは`/etc/passwd`にパスワードは書いていないことが多いので特に問題はないです。ログインの際に使わなければいけないファイルなので全てのユーザーが読むこともできます。
`-v /etc/group:/etc/group:ro -v /etc/passwd:/etc/passwd:ro `

# 何で開発するか問題
VSCodeを使うと、リモートマシン（例えば計算用のサーバー）で走っているコンテナに直接アクセスできます。ただし、こちらもコンテナのホームディレクトリにプログラムを転送してリモート実行するため、一度ルートで立ち上げた後、ユーザー権限でログインしようとするとアップデートに失敗してログインできなくなるのような現象が起きたりするので注意です。
[ssh接続先のdockerコンテナにVSCodeのRemote Developmentで繋ぐ](https://qiita.com/kanosawa/items/07e26edb19c86091fa48)
なんかが参考になります

# その他
ライセンスの関係でMujocoが使えないので深層強化学習には使いにくかったりします。
Dockerは機械学習用に緩く使っているためあまり良くない習慣なども書いてしまったりしているかもしれないので指摘をお願いしたいです。

# 参考
* https://qiita.com/sht47/items/0706f19c67eb30e0954f#3-nvidia-docker2%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%A8etcdockerdaemonjson%E3%81%AE%E8%A8%AD%E5%AE%9A
*