DockerでGPU環境構築 [2020年10月版]

# 概要
本記事ではDockerを使って、tensorflow2.3 + Python3.8の環境構築をします。
任意のTensorFlow、PyTorchのバージョンで、対応するCUDAとPythonのバージョンに本記事の内容を書き換えれば簡単に環境構築ができます。
DockerでGPU環境構築を行う系の記事が混在しているため、2020年10月時点で自分が行ったことを備忘録がてら整理します。

本記事では、`nvidia/cuda:10.1-cudnn7-devel-ubuntu18.04`イメージをベースに`Python3.8`と`Poetry`をインストールし、仮想環境内でPoetryを使って`tensorflow2.3`をインストールします。

# 対象読者
- Docker入門者
- 複数のTensorFlowなどのフレームワークのバージョンの環境を一つのマシン上に構築したい人

# 環境
- Ubuntu 16.04.3 LTS
- GeForce GTX1080

> 以下1~3はこちらの記事の方が簡単にできそうです (自分では試してないですが)
[NVIDIA Docker って今どうなってるの？ (20.09 版)](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)

# 1. NVIDIA ドライバのインストール
GPUの型を確認 (NVIDIA製のものがある確認)。

```shell-session
$ lspci | grep VGA
03:00.0 VGA compatible controller: NVIDIA Corporation Device 1b80 (rev a1)
04:00.0 VGA compatible controller: NVIDIA Corporation Device 1b80 (rev a1)
```

既にNVIDIA ドライバがインストールされていれば、削除しておく。

```shell
$ dpkg -l | grep nvidia-
$ sudo apt-get purge nvidia-*
```

ドライバのrepositoryを追加する。

```shell
$ sudo add-apt-repository ppa:graphics-drivers/ppa
$ sudo apt update
```

推奨ドライバを確認。

```shell-session
$ sudo ubuntu-drivers devices
== /sys/devices/pci0000:00/0000:00:03.0/0000:04:00.0 ==
modalias : pci:v000010DEd00001B80sv00001462sd0000336Bbc03sc00i00
vendor   : NVIDIA Corporation
driver   : nvidia-430 - third-party free recommended
driver   : nvidia-415 - third-party free
driver   : xserver-xorg-video-nouveau - distro free builtin
```

推奨されている`nvidia-430`をインストールする。

```shell
$ sudo apt-get install nvidia-driver-430
```

※ 以下のコマンドで推奨のドライバを自動でインストールすることもできる。

```shell
$ sudo ubuntu-drivers autoinstall
```

OSを再起動する。

```
$ sudo reboot
```

NVIDIA ドライバが無事インストールされているか確認。

```
$ nvidia-smi
Thu Oct 15 16:49:51 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 430.64       Driver Version: 430.64       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| 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 1080    Off  | 00000000:03:00.0 Off |                  N/A |
| 28%   40C    P8    10W / 180W |      2MiB /  8119MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  GeForce GTX 1080    Off  | 00000000:04:00.0  On |                  N/A |
| 28%   41C    P8    13W / 180W |    533MiB /  8085MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    1      1247      G   /usr/lib/xorg/Xorg                           422MiB |
|    1      1583      G   compiz                                       108MiB |
+-----------------------------------------------------------------------------+

```

# 2. Dockerのインストール
[公式Document](https://docs.docker.com/engine/install/ubuntu/)に沿ってDockerをインストールすれば良い。
以下は公式Documentの一部のコピペなので、詳しくはDocumentを読んでください。

>
### SET UP THE REPOSITORY
1\. Update the apt package index and install packages to allow apt to use a repository over HTTPS: 
>
```shell-session
$ sudo apt-get update
$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
```
>
2\. Add Docker’s official GPG key:
>
```
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
pub   rsa4096 2017-02-22 [SCEA]
      9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
uid           [ unknown] Docker Release (CE deb) <docker@docker.com>
sub   rsa4096 2017-02-22 [S]
```
>
3\. Use the following command to set up the stable repository.
>
```shell-session
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
```
>
### INSTALL DOCKER ENGINE
1\. Update the apt package index, and install the latest version of Docker Engine and containerd, or go to the next step to install a specific version:

>
```
 $ sudo apt-get update
 $ sudo apt-get install docker-ce docker-ce-cli containerd.io
```
>
3\. Verify that Docker Engine is installed correctly by running the hello-world image.

>
```shell-session
$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete 
Digest: sha256:8c5aeeb6a5f3ba4883347d3747a7249f491766ca1caa47e5da5dfcf6b9b717c0
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
```

Dockerが正しくインストールされたか確認。

```
$ dpkg -l | grep -e docker -e containerd.io
ii  containerd.io                                            1.3.7-1                                       amd64        An open and reliable container runtime
ii  docker-ce                                                5:19.03.13~3-0~ubuntu-xenial                  amd64        Docker: the open-source application container engine
ii  docker-ce-cli                                            5:19.03.13~3-0~ubuntu-xenial                  amd64        Docker CLI: the open-source application container engine
```

```
$ sudo docker version
Client: Docker Engine - Community
 Version:           19.03.13
 API version:       1.40
 Go version:        go1.13.15
 Git commit:        4484c46d9d
 Built:             Wed Sep 16 17:02:59 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.13
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       4484c46d9d
  Built:            Wed Sep 16 17:01:30 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.3.7
  GitCommit:        8fba4e9a7d01810a393d5d25a3621dc101981175
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
```


```shell-session
$ sudo docker images
REPOSITORY          TAG                             IMAGE ID            CREATED             SIZE
hello-world         latest                          bf756fb1ae65        9 months ago        13.3kB
```

[任意] sudoなしにdockerコマンドを実行可能にする。

```shell
$ sudo usermod -aG docker $USER
```

# 3. NVIDIA Container Toolkit のインストール
2020年9月の更新で、`nvidia-docker2`をインストールするだけでよくなったらしい。 ([NVIDIA Docker って今どうなってるの？ (20.09 版)](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`をインストールすれば`nvidia-container-toolkit`も一緒にインストールされる)

```
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
$ curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
$ curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
$ sudo apt-get update
$ sudo apt-get install -y nvidia-docker2
$ sudo systemctl restart docker
```

# 4. Dockerでtensorflow2.3 + Python3.8の環境構築
`Dockerfile_gpu`に以下の内容を書き込む。
CUDA、cuDNNのバージョンは、インストールしたいフレームワークのバージョンに合わせて選択して下さい。
ここでは、tensorflow2.3に合わせて、[CUDA10.1 + cuDNN7のイメージ](https://hub.docker.com/r/nvidia/cuda/tags?page=1&name=10.1-cudnn7-devel-ubuntu18.04)をベースにします。

```Dockerfile:Dockerfile_gpu
FROM nvidia/cuda:10.1-cudnn7-devel-ubuntu18.04

ENV DEBIAN_FRONTEND noninteractive

RUN apt-get update && apt-get install -y --no-install-recommends tzdata
RUN apt-get update && apt-get install -y --no-install-recommends \
    wget \
    curl \
    make \
    build-essential \
    libssl-dev \
    zlib1g-dev \
    libbz2-dev \
    libreadline-dev \
    libsqlite3-dev \
    llvm \
    libncurses5-dev \
    libncursesw5-dev \
    xz-utils \
    tk-dev \
    libffi-dev \
    liblzma-dev \
    vim \
    graphviz

ENV TZ Asia/Tokyo

WORKDIR /root/
RUN wget https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tar.xz \
    && tar xvf Python-3.8.5.tar.xz \
    && cd Python-3.8.5 \
    && ./configure --enable-optimizations \
    && make install
RUN rm Python-3.8.5.tar.xz

WORKDIR /root/Python-3.8.5
RUN ln -fs /root/Python-3.8.5/python /usr/bin/python
RUN curl -kL https://bootstrap.pypa.io/get-pip.py | python 
RUN rm -rf /var/lib/apt/lists/*

RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
ENV SHELL /bin/bash -l

ENV POETRY_CACHE /work/.cache/poetry
ENV PIP_CACHE_DIR /work/.cache/pip

RUN $HOME/.poetry/bin/poetry config virtualenvs.path $POETRY_CACHE

ENV PATH ${PATH}:/root/.poetry/bin:/bin:/usr/local/bin:/usr/bin

CMD ["bash", "-l"]
```

`Makefile`に以下の内容を書き込む (`IMAGE_NAME`は適宜書き換えて下さい)。

```Makefile:Makefile
FOLDER=$$(pwd)
IMAGE_NAME=test:latest

.PHONY: build-gpu
build-gpu: # Build docker image
    echo "Building Dockerfile"
    docker build -t ${IMAGE_NAME} . -f Dockerfile_gpu

.PHONY: start-gpu
start-gpu: build-gpu # Start docker container
    echo "Starting container ${IMAGE_NAME}"
    docker run --gpus all --rm -it -v ${FOLDER}:/work -w /work ${IMAGE_NAME}
```

Dockeイメージをbuildして、Dockerコンテナを作る。

```shell
$ make start-gpu
```

[Poetry](https://cocoatomo.github.io/poetry-ja/)で`tensorflow-gpu 2.3`をインストール。

```shell
# poetry init
# poetry add tensorflow-gpu==2.3
```

任意のファイルを実行する。

```shell
# poetry run python file_name.py
```

# まとめ
本記事ではDockerを使って、tensorflow2.3 + Python3.8の環境構築をする手順を備忘録がてらにまとめました。
ホストOSにNVIDIAドライバ、Docker、NVIDIA Container Toolkitだけをインストールすれば良いのは便利ですね。
本記事ではPoetryでTensorFlowをインストールしましたが、Dockerfileでpip installしてももちろんOKです。

# Troubleshooting
## apt update
Google Chrome絡みのエラー
https://sicklylife.hatenablog.com/entry/2017/08/08/193118

"could not open" list file due to "permission denied"のエラー
https://askubuntu.com/a/1146593

## nouveau
nouveauの無効化
https://qiita.com/kawazu191128/items/8a46308be6949f5bda57#nouveau%E7%84%A1%E5%8A%B9%E5%8C%96

## apt-get install nvidia-driver-300
UEFIのSecure Bootが有効の場合、Secure Bootを無効にするか鍵の設定が必要。

## Temporary failure resolving '....com'
https://github.com/moby/moby/issues/5779#issuecomment-323433618

## DNSサーバを指定
https://mzryuka.hatenablog.jp/entry/2019/08/07/235613