CUBE SUGAR CONTAINER

技術系のこと書きます。

NVIDIA Container Toolkit を使って Docker コンテナで GPU を使う

今回は NVIDIA Container Toolkit を使って Docker コンテナから Docker ホストの GPU を使う方法について書く。 これまで Docker コンテナで GPU を使う方法は、nvidia-docker と nvidia-docker2 という二つの世代を経てきた。 それも、ここに来てやっと一息ついたかな、という印象がある。 GPU の基本的なサポートが Docker 本体側に取り込まれて (v19.03 以降)、GPU ベンダーはそのドライバを提供する形に落ち着いた。 そして、従来の nvidia-docker2 は非推奨 (Deprecated) な方法となっている。

なお、GPU ベンダーがドライバを提供する、と前述した通り NVIDIA のリソースが一切不要になったわけではない。 そのため、インストール手順やインターフェースの使い勝手という観点では nvidia-docker2 から大して変わっていない、というのが実状ではあるだろう。

使った環境は次の通り。

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.3 LTS"
$ uname -r
4.15.0-1044-gcp
$ lspci -vv | grep -i nvidia
00:04.0 3D controller: NVIDIA Corporation Device 1eb8 (rev a1)
    Subsystem: NVIDIA Corporation Device 12a2
    Kernel modules: nvidiafb
$ sudo 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:29:11 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:27:45 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.6
  GitCommit:        894b81a4b802e4eb2a91d1ce216b8817763c29fb
 runc:
  Version:          1.0.0-rc8
  GitCommit:        425e105d5a03fabd737a126ad93d62a9eeede87f
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

もくじ

Docker をインストールする

まずは Docker をインストールする。

必要なパッケージをインストールしておく。

$ sudo apt -y install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

Docker のリポジトリを APT に登録する。

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$ sudo apt update

そして、Docker のコンポーネントをインストールする。

$ sudo apt -y install docker-ce docker-ce-cli containerd.io
$ sudo 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:29:11 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:27:45 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.6
  GitCommit:        894b81a4b802e4eb2a91d1ce216b8817763c29fb
 runc:
  Version:          1.0.0-rc8
  GitCommit:        425e105d5a03fabd737a126ad93d62a9eeede87f
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

NVIDIA Graphics Driver をインストールする

続いて Docker ホストに NVIDIA の Graphics Driver をインストールする。

ドライバのリポジトリを APT に登録する。

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

推奨されているドライバをインストールする。

$ sudo apt -y install ubuntu-drivers-common
$ sudo ubuntu-drivers autoinstall

NVIDIA Container Toolkit をインストールする

次に Docker ホストに NVIDIA Container Toolkit をインストールする。 この中に Docker で NVIDIA の GPU を使うのに必要なランタイムなどが含まれている。

まずはリポジトリを APT に登録する。

$ curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
$ curl -s -L https://nvidia.github.io/nvidia-docker/$(. /etc/os-release;echo $ID$VERSION_ID)/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
$ sudo apt update

その上でツールキットをインストールする。

$ sudo apt -y install nvidia-container-toolkit

ここまで終わったら、一旦マシンを再起動しておく。

$ sudo shutdown -r now

インストール後の確認作業

まずは nvidia-container-cli info コマンドを使って、ちゃんと GPU が認識できていることを確認しよう。 また、同時に使用できるドライバと CUDA のバージョンが確認できる。

$ nvidia-container-cli info
NVRM version:   435.21
CUDA version:   10.1

Device Index:   0
Device Minor:   0
Model:          Tesla T4
Brand:          Tesla
GPU UUID:       GPU-bcf7de51-87c5-4b98-f8c1-b1a9072696ca
Bus Location:   00000000:00:04.0
Architecture:   7.5

ちなみに nvidia-container-cli list を使うと Docker コンテナ側に注入されるファイル一覧が確認できる。 これらのファイルがコンテナからアクセスできるようになることで GPU が使えるようになる。

$ nvidia-container-cli list
/dev/nvidiactl
/dev/nvidia-uvm
/dev/nvidia-uvm-tools
/dev/nvidia-modeset
/dev/nvidia0
/usr/bin/nvidia-smi
/usr/bin/nvidia-debugdump
/usr/bin/nvidia-persistenced
/usr/bin/nvidia-cuda-mps-control
/usr/bin/nvidia-cuda-mps-server
/usr/lib/x86_64-linux-gnu/libnvidia-ml.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-cfg.so.435.21
/usr/lib/x86_64-linux-gnu/libcuda.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-ptxjitcompiler.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-fatbinaryloader.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-compiler.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-encode.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-opticalflow.so.435.21
/usr/lib/x86_64-linux-gnu/libnvcuvid.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-eglcore.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-glcore.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-tls.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-glsi.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-fbc.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-ifr.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-rtcore.so.435.21
/usr/lib/x86_64-linux-gnu/libnvoptix.so.435.21
/usr/lib/x86_64-linux-gnu/libGLX_nvidia.so.435.21
/usr/lib/x86_64-linux-gnu/libEGL_nvidia.so.435.21
/usr/lib/x86_64-linux-gnu/libGLESv2_nvidia.so.435.21
/usr/lib/x86_64-linux-gnu/libGLESv1_CM_nvidia.so.435.21
/usr/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.435.21
/run/nvidia-persistenced/socket

Docker コンテナから GPU を使ってみる

それでは、実際に Docker コンテナから GPU を使ってみることにしよう。

まずは、特に何もケアせずに Ubuntu のコンテナを起動して nvidia-smi コマンドを実行してみる。

$ sudo docker run \
    --rm \
    -it ubuntu \
    nvidia-smi
docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"nvidia-smi\": executable file not found in $PATH": unknown.

プレーンな Ubuntu のコンテナには、NVIDIA の GPU を扱うためのファイル群が含まれていない。 そのため、この実行は上記の通り失敗に終わる。

続いては --gpus オプションを指定して同じコマンドを実行してみよう。 すると、次のようにちゃんとコマンドの実行が成功する。

$ sudo docker run \
    --rm \
    --gpus all \
    -it ubuntu \
    nvidia-smi
Sun Sep 22 05:31:27 2019       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 435.21       Driver Version: 435.21       CUDA Version: N/A      |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla T4            Off  | 00000000:00:04.0 Off |                    0 |
| N/A   44C    P0    26W /  70W |      0MiB / 15109MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

同じように CentOS のイメージでも動作するか確認してみよう。 こちらも上手くいく。

$ sudo docker run \
    --rm \
    --gpus all \
    -it centos \
    nvidia-smi
Sun Sep 22 06:10:59 2019       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 435.21       Driver Version: 435.21       CUDA Version: N/A      |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla T4            Off  | 00000000:00:04.0 Off |                    0 |
| N/A   71C    P0    32W /  70W |      0MiB / 15109MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

上記から、もはや NVIDIA が提供している公式イメージも必須ではなくなったことがわかる。

上記は、前述した通り必要なファイルがコンテナからアクセスできるようになることで実現されている。 まず、何もオプションを付けずに実行したときの /usr/lib/x86_64-linux-gnu/ ディレクトリを確認してみよう。

$ sudo docker run \
    --rm \
    -it ubuntu \
    ls /usr/lib/x86_64-linux-gnu/
audit              libgnutls.so.30   libpanelw.so.5
coreutils          libgnutls.so.30.14.10  libpanelw.so.5.9
gconv              libhogweed.so.4   libpcreposix.so.3
libapt-pkg.so.5.0     libhogweed.so.4.4     libpcreposix.so.3.13.3
libapt-pkg.so.5.0.2      libidn2.so.0          libsemanage.so.1
libapt-private.so.0.0     libidn2.so.0.3.3     libstdc++.so.6
libapt-private.so.0.0.0    liblz4.so.1         libstdc++.so.6.0.25
libdb-5.3.so          liblz4.so.1.7.1      libtasn1.so.6
libdebconfclient.so.0     libmenu.so.5          libtasn1.so.6.5.5
libdebconfclient.so.0.0.0  libmenu.so.5.9      libtic.so.5
libffi.so.6           libmenuw.so.5     libtic.so.5.9
libffi.so.6.0.4          libmenuw.so.5.9   libunistring.so.2
libform.so.5          libnettle.so.6    libunistring.so.2.1.0
libform.so.5.9        libnettle.so.6.4      libzstd.so.1
libformw.so.5         libp11-kit.so.0   libzstd.so.1.3.3
libformw.so.5.9           libp11-kit.so.0.3.0      perl
libgmp.so.10          libpanel.so.5     perl-base
libgmp.so.10.3.2     libpanel.so.5.9

特に GPU 関連のファイルが含まれている形跡はない。

続いて --gpus オプションをつけて同じコマンドを実行してみよう。

$ sudo docker run \
    --rm \
    --gpus all \
    -it ubuntu \
    ls /usr/lib/x86_64-linux-gnu/
audit              libgnutls.so.30.14.10    libpanel.so.5
coreutils          libhogweed.so.4     libpanel.so.5.9
gconv              libhogweed.so.4.4       libpanelw.so.5
libapt-pkg.so.5.0     libidn2.so.0            libpanelw.so.5.9
libapt-pkg.so.5.0.2      libidn2.so.0.3.3       libpcreposix.so.3
libapt-private.so.0.0     liblz4.so.1         libpcreposix.so.3.13.3
libapt-private.so.0.0.0    liblz4.so.1.7.1      libsemanage.so.1
libdb-5.3.so          libmenu.so.5            libstdc++.so.6
libdebconfclient.so.0     libmenu.so.5.9      libstdc++.so.6.0.25
libdebconfclient.so.0.0.0  libmenuw.so.5     libtasn1.so.6
libffi.so.6           libmenuw.so.5.9     libtasn1.so.6.5.5
libffi.so.6.0.4          libnettle.so.6      libtic.so.5
libform.so.5          libnettle.so.6.4        libtic.so.5.9
libform.so.5.9        libnvidia-cfg.so.1      libunistring.so.2
libformw.so.5         libnvidia-cfg.so.435.21  libunistring.so.2.1.0
libformw.so.5.9           libnvidia-ml.so.1       libzstd.so.1
libgmp.so.10          libnvidia-ml.so.435.21   libzstd.so.1.3.3
libgmp.so.10.3.2     libp11-kit.so.0     perl
libgnutls.so.30           libp11-kit.so.0.3.0        perl-base

すると、libnvidia-* といった GPU 関連の共有ライブラリが確認できるようになる。

Docker Compose の対応について

残念なお知らせだけど、まだ現時点 (2019/09/22) では Docker Compose が上記の --gpus オプションに対応できていない。

$ sudo apt -y install python3-pip
$ sudo pip3 install docker-compose
$ docker-compose version
docker-compose version 1.24.1, build 4667896
docker-py version: 3.7.3
CPython version: 3.6.8
OpenSSL version: OpenSSL 1.1.1  11 Sep 2018

Docker Compose と一緒に万全の体制で使うには、もう少し時間を必要としそうだ。

Support for NVIDIA GPUs under Docker Compose · Issue #6691 · docker/compose · GitHub

補足

今回使った GPU マシン環境は次のようにして用意した。

$ gcloud compute instances create gcp-gpu-t4-x1 \
  --preemptible \
  --zone us-central1-a \
  --machine-type n1-standard-2 \
  --accelerator type=nvidia-tesla-t4,count=1 \
  --maintenance-policy TERMINATE \
  --restart-on-failure \
  --image-project ubuntu-os-cloud \
  --image-family ubuntu-1804-lts

いじょう。