Dockerで、GPU対応なコンテナ環境を整備する

**Docker 19.03リリース**にて、DockerでGPU対応コンテナ環境が作成できるようになったようです。
そこで、実際に、Dockerで、GPU対応なコンテナが作成できるところまで確認してみました。


"[Docker 19.03新機能 (root権限不要化、GPU対応強化、CLIプラグイン…)](https://medium.com/nttlabs/docker-1903-5155754ff8ac)"より、引用
> **GPU対応強化**
従来のDockerでNVIDIAのGPUを用いるには、 ```docker``` コマンドの代わりに ```nvidia-docker``` コマンドを用いたり、OCIランタイムとして ```--runtime=nvidia``` (nvidia-docker2) を指定したりする必要がありました。
Docker 19.03では、Docker自体にGPU連携機能 ```docker run --gpus …```が組み込まれました。
```$ docker run --gpus all```
```$ docker run --gpus 2,driver=nvidia,capabilities=compute```
現時点では nvidia ドライバのみが実装されていますが、将来的にはNVIDIA以外のベンダのGPU向けのドライバも実装されることが期待されます。


# ⬛︎ Ubuntuサーバ構成を確認しておく
## (1) Ubuntuサーバ環境の確認

- OSバージョンの確認

```
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.3 LTS"
```
- GPUボード搭載されていることを確認

```
$ lspci -vv|grep -i nvidia
01:00.0 VGA compatible controller: NVIDIA Corporation GP106 [GeForce GTX 1060 3GB] (rev a1) (prog-if 00 [VGA controller])
	Kernel modules: nvidiafb, nouveau
01:00.1 Audio device: NVIDIA Corporation GP106 High Definition Audio Controller (rev a1)
```

## (2) Docker環境の確認

2019.9時点での最新Docker（19.03.2）にバージョンアップしておいた

```
$ docker version
Client: Docker Engine - Community
 Version:           19.03.2
 API version:       1.40
 Go version:        go1.12.8
 Git commit:        6a30dfc
 Built:             Thu Aug 29 05:28:19 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.2
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.8
  Git commit:       6a30dfc
  Built:            Thu Aug 29 05:26:54 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.4
  GitCommit:        e6b3f5632f50dbc4e9cb6288d911bf4f5e95b18e
 runc:
  Version:          1.0.0-rc6+dev
  GitCommit:        6635b4f0c6af3810594d2770f662f34ddc15b40d
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
```

## (3) Docker-compose環境の確認
2019.9時点での最新docker-compose（1.24.1）にバージョンアップしておいた

```
$ $ docker-compose -v
docker-compose version 1.24.1, build 4667896b
```
# ⬛︎ NVIDIAドライバーのインストール

## (1) デフォルトでロードされている nouveau ドライバーを無効化しておく
- /etc/modprobe.d/blacklist-nouveau.conf を作成し、以下の設定を行う

```text:/etc/modprobe.d/blacklist-nouveau.conf
blacklist nouveau
options nouveau modeset=0
```
- Initramfsを再構築する

```
$ sudo update-initramfs -u
```
```
$ sudo reboot
```

## (2) GPU環境に対応したNVIDIAドライバーのバージョンを確認する
- [NvidiaのWebサイト](https://www.nvidia.com/Download/index.aspx?lang=en)にアクセスする
- 検索画面から、対応バージョンを確認する
![nvidia_driver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/c701885c-8c6c-6387-40e9-b81fd6d23884.png)



## (3) NVIDIAドライバーをインストールする

```
$ sudo apt update
```
```
$ sudo apt -y install nvidia-driver-430
```
```
$ sudo apt install nvidia-cuda-toolkit
```

## (4) NVIDIAドライバーが動作することを確認する

- NVIDIAドライバーがインストールされたことを確認する

```
$ lspci -vv|grep -i nvidia
01:00.0 VGA compatible controller: NVIDIA Corporation GP106 [GeForce GTX 1060 3GB] (rev a1) (prog-if 00 [VGA controller])
	Kernel driver in use: nvidia
	Kernel modules: nvidiafb, nouveau, nvidia_drm, nvidia
```
-  nvidia-smiコマンドが動作することを確認する

```
$ nvidia-smi
Mon Sep 16 06:17:46 2019       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 430.26       Driver Version: 430.26       CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 106...  Off  | 00000000:01:00.0  On |                  N/A |
| 38%   35C    P8     6W / 120W |      2MiB /  3016MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
```
もし、次のようなエラーに遭遇した場合には、 **Secure Boot**を無効化すると解決するかもしれません。

```
$ nvidia-smi
NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.
```
[NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running](https://askubuntu.com/questions/927199/nvidia-smi-has-failed-because-it-couldnt-communicate-with-the-nvidia-driver-ma)

# ⬛︎ CUDA Toolkit 10.0 のインストール
## (1) CUDA Toolkit環境をセットアップする
- [CUDA Toolkit 10.0 original Archive](https://developer.nvidia.com/cuda-10.0-download-archive?target_os=Linux&target_arch=x86_64&target_distro=Ubuntu&target_version=1804&target_type=debnetwork)のWebサイトにアクセスする
- Installerをダウンロードして、記載された手順で、CUDA Toolkitをインストールする

![cuda_toolkit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/fe615333-b7c9-a71c-f395-f89b1ac56103.png)



```
$ wget http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-repo-ubuntu1804_10.0.130-1_amd64.deb
```
```
$ sudo dpkg -i cuda-repo-ubuntu1804_10.0.130-1_amd64.deb
```
```
$ sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub
```
```
$ sudo apt-get update
```
```
$ sudo apt-get install cuda-10-0
```
```
$ sudo reboot
```
- .bashrcにパス設定を追加する

```text:.bashrc
export PATH="/usr/local/cuda/bin:$PATH"
export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"
```
## (2) CUDA Toolkit環境を確認する
- バージョンを確認する

```
$ 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
```

- NVIDIAドライバーが動作していることを確認する

```
$ lspci -vv|grep -i nvidia
01:00.0 VGA compatible controller: NVIDIA Corporation GP106 [GeForce GTX 1060 3GB] (rev a1) (prog-if 00 [VGA controller])
	Kernel driver in use: nvidia
	Kernel modules: nvidiafb, nouveau
01:00.1 Audio device: NVIDIA Corporation GP106 High Definition Audio Controller (rev a1)
```
-> nvidiaのカーネルモジュールが、組み込まれていない

- nvidia-smiコマンドが動作することを確認する

```
$ nvidia-smi
Failed to initialize NVML: Driver/library version mismatch
```
-> CUDA Toolkitをインストールした直後だと、nvidia-smiコマンドが実行できなくなったため、この後、NVIDIAドライバーを再インストールする必要がある

# ⬛︎ NVIDIAドライバーの再インストール
## (1) NVIDIAドライバーを再度、インストールする
- 古いドライバーを削除する

```
$ sudo apt-get --purge remove nvidia-*
```
- NVIDIAドライバーを再インストールする

```
$ sudo apt -y install nvidia-driver-430
```
```
$ sudo reboot
```
## (2) NVIDIAドライバーが動作することを確認する
- NVIDIAドライバーがインストールされたことを確認する

```
$ lspci -vv|grep -i nvidia
01:00.0 VGA compatible controller: NVIDIA Corporation GP106 [GeForce GTX 1060 3GB] (rev a1) (prog-if 00 [VGA controller])
	Kernel driver in use: nvidia
	Kernel modules: nvidiafb, nouveau, nvidia_drm, nvidia
01:00.1 Audio device: NVIDIA Corporation GP106 High Definition Audio Controller (rev a1)
```
- nvidia-smiコマンドが動作することを確認する

```
$ nvidia-smi
Mon Sep 16 08:10:31 2019       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 430.26       Driver Version: 430.26       CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 106...  Off  | 00000000:01:00.0  On |                  N/A |
| 38%   34C    P8     6W / 120W |      2MiB /  3016MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
```

# ⬛︎ cuDNNのインストール
[cuDNN Archive](https://developer.nvidia.com/rdp/cudnn-archive#a-collapse714-9) から`cuDNN v7.6.2 (July 22, 2019), for CUDA 10.0`をダウンロードして、インストールします
なお、ダウンロードするためには事前にメンバー登録が必要

- ファイルをダウンロードする
 - cuDNN Runtime Library for Ubuntu18.04 (Deb)
 - cuDNN Developer Library for Ubuntu18.04 (Deb)
 - cuDNN Code Samples and User Guide for Ubuntu18.04 (Deb)

- cuDNNをインストールする

```
$ sudo dpkg -i libcudnn7_7.6.2.24-1+cuda10.0_amd64.deb
$ sudo dpkg -i libcudnn7-dev_7.6.2.24-1+cuda10.0_amd64.deb
$ sudo dpkg -i libcudnn7-doc_7.6.2.24-1+cuda10.0_amd64.deb
```

# ⬛︎ nvidia-container-runtime環境セットアップ

[NVIDIA/nvidia-container-runtime](https://github.com/NVIDIA/nvidia-container-runtime)を参考に、インストールを行う

## (1) Debian用リポジトリ環境をセットアップする

```
$ curl -s -L https://nvidia.github.io/nvidia-container-runtime/gpgkey | \
  sudo apt-key add -
```
```
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
```
```
$ curl -s -L https://nvidia.github.io/nvidia-container-runtime/$distribution/nvidia-container-runtime.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-runtime.list
```
```
$ sudo apt-get update
```
## (2) nvidia-container-runtimeパッケージをインストールする
```
$ sudo apt-get install nvidia-container-runtime
```
```
$ sudo reboot
```
## (3) DockerコンテナがGPU対応できていることを確認する
```
$ docker run --gpus all --rm nvidia/cuda nvidia-smi
Sun Sep 15 22:40:52 2019       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 430.26       Driver Version: 430.26       CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 106...  Off  | 00000000:01:00.0  On |                  N/A |
| 38%   32C    P8     6W / 120W |      2MiB /  3016MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
```

# ⬛︎ Docker-composeから、GPU対応コンテナを作成してみる
2019.9時点では、docker-compose側で、```--gpus```オプションに対応できていないようです
そこで、今回は、暫定対応として、```--runtime```オプションを使って、動作させてみました
[Support for NVIDIA GPUs under Docker Compose #6691](https://github.com/docker/compose/issues/6691)

## (1) Docker-engine側での```--runtime```オプション対応
- nvidia-container-runtimeの配置場所を確認する

```
$ which nvidia-container-runtime
/usr/bin/nvidia-container-runtime
```
- /etc/docker/daemon.jsonファイルを設定して、```--runtime```オプションを有効にする

```text:/etc/docker/daemon.json
{
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}
```
```
$ sudo reboot
```

## (2) docker-composeから、GPU対応コンテナを作成する
- docker-compose.ymlファイルを設定する

```text:docker-compose.yml
version: '2.3'
services:
  nvsmi:
    image: tensorflow/tensorflow:latest-gpu-py3
    runtime: nvidia
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
    container_name: tensorflow
    hostname: tensorflow
    command: /bin/sh -c "tail -f /dev/null"
```
- docker-composeを起動する

```
$ docker-compose up -d
Creating tensorflow ... done
```
## (3) Dockerコンテナ内部で、gpuが正しく認識できていることを確認する

- Dockerコンテナにログインして、nvidia-smiコマンドを実行してみる

```
$ docker exec -it tensorflow bash

________                               _______________                
___  __/__________________________________  ____/__  /________      __
__  /  _  _ \_  __ \_  ___/  __ \_  ___/_  /_   __  /_  __ \_ | /| / /
_  /   /  __/  / / /(__  )/ /_/ /  /   _  __/   _  / / /_/ /_ |/ |/ / 
/_/    \___//_/ /_//____/ \____//_/    /_/      /_/  \____/____/|__/


WARNING: You are running this container as root, which can cause new files in
mounted volumes to be created as the root user on your host machine.

To avoid this, run the container by specifying your user's userid:

$ docker run -u $(id -u):$(id -g) args...

```
```
root@tensorflow:/# nvidia-smi
Mon Sep 23 04:44:44 2019       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 430.26       Driver Version: 430.26       CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 106...  Off  | 00000000:01:00.0  On |                  N/A |
| 38%   33C    P8     6W / 120W |      2MiB /  3016MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
```
## (4) Dockerコンテナ内部でのtensorflowが、gpuが正しく認識できていることを確認する
```
root@tensorflow:/# python
Python 3.6.8 (default, Jan 14 2019, 11:02:34) 
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from tensorflow.python.client import device_lib
>>> device_lib.list_local_devices()

...(snip)

2019-09-23 04:46:54.480614: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1326] Created TensorFlow device (/device:GPU:0 with 2647 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1060 3GB, pci bus id: 0000:01:00.0, compute capability: 6.1)
[name: "/device:CPU:0"
device_type: "CPU"
memory_limit: 268435456
locality {
}
incarnation: 392745049389231618
, name: "/device:XLA_GPU:0"
device_type: "XLA_GPU"
memory_limit: 17179869184
locality {
}
incarnation: 15780931418267854734
physical_device_desc: "device: XLA_GPU device"
, name: "/device:XLA_CPU:0"
device_type: "XLA_CPU"
memory_limit: 17179869184
locality {
}
incarnation: 15264979588649984511
physical_device_desc: "device: XLA_CPU device"
, name: "/device:GPU:0"
device_type: "GPU"
memory_limit: 2776039424
locality {
  bus_id: 1
  links {
  }
}
incarnation: 6443476501190914608
physical_device_desc: "device: 0, name: GeForce GTX 1060 3GB, pci bus id: 0000:01:00.0, compute capability: 6.1"
]
```
```
root@tensorflow:/# python
Python 3.6.8 (default, Jan 14 2019, 11:02:34) 
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow as tf
>>> device = tf.test.gpu_device_name()

... (snip)

2019-09-23 04:48:22.635518: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1326] Created TensorFlow device (/device:GPU:0 with 2647 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1060 3GB, pci bus id: 0000:01:00.0, compute capability: 6.1)
>>> device
'/device:GPU:0'
```
はい、期待通りに、tensorFlowから、gpuが認識できました ...

# ⬛︎ 終わりに ...
序盤、CUDAインストールあたりは、色々とトラブルにハマり、心が折れそうになりましたが、
最終的には、Docker-composeから、GPU対応なコンテナが簡単に作成できてしまう様子が確認できました。今後は、kuberneters化を目指して、CloudNativeなAI基盤を作成してみたいと思います。

# ⬛︎ 参考URL
- [NVIDIAドライバーをインストールする](https://www.server-world.info/query?os=Ubuntu_18.04&p=nvidia)
- [UbuntuにCUDAをインストール](https://qiita.com/isishizuka/items/5420af0e8f6fca7aa1da)
- [Ubuntu 18.04へのCUDAインストール方法](https://qiita.com/yukoba/items/4733e8602fa4acabcc35)
- [Ubuntu18.04にCUDAをインストールする](https://qiita.com/protocol1964/items/19de3d1e27306f80d06d)
- [dockerでgpuコンテナの作成](https://qiita.com/uni-3/items/c9480d7e177e29b1316c)
- [docker-composeでgpuを使う方法](https://qiita.com/hgoj/items/1ba050c7d73e0dd5ef90)
- [Docker+Keras+TensorFlow環境構築 その5](http://kikei.github.io/ai/2019/08/10/tensorflow.html)