3行まとめ
- RootlessモードでもGPUは使用できる。
- RootlessモードでGPUを使用するためにはnvidia-container-runtimeの設定を変更する必要がある。
- Rootlessモードと通常モードを共存する方法はまだ未解決。
1. はじめに
機械学習分野では、Docker/Kubernetes上でNVIDIAのGPU(以下、単に「GPU」)を使うということがよく行われます。
複数人で共有するGPUマシンのセキュリティをより高めるため、RootlessモードのDockerでGPUが使用できるかどうか調査してみました。
結果から言えば、簡単な設定変更を行うだけで使用できました。(少なくともPyTorchから認識できました)
2. 環境
今回、調査に使用した環境は以下の通りです。
- OS: Ubuntu 19.10(Eoan Ermine)
- GPU: GeForce GTX1070 8GB x4台(ただし1台のみ電源オン状態)
- NVIDIAドライバ: 440.82
- 通常モードのDocker: 19.03.8
- RootlessモードのDocker: 19.03.8
より詳細な環境の情報は以下の通りです。
yuya$ cat /etc/os-release
NAME="Ubuntu"
VERSION="19.10 (Eoan Ermine)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 19.10"
VERSION_ID="19.10"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=eoan
UBUNTU_CODENAME=eoan
yuya$ uname -a
Linux ml-1 5.3.0-51-generic #44-Ubuntu SMP Wed Apr 22 21:09:44 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
yuya$ dpkg -l | grep nvidia
ii libnvidia-cfg1-440:amd64 440.82-0ubuntu0~0.19.10.1 amd64 NVIDIA binary OpenGL/GLX configuration library
ii libnvidia-compute-440:amd64 440.82-0ubuntu0~0.19.10.1 amd64 NVIDIA libcompute package
ii libnvidia-container-tools 1.0.7-1 amd64 NVIDIA container runtime library (command-line tools)
ii libnvidia-container1:amd64 1.0.7-1 amd64 NVIDIA container runtime library
ii nvidia-compute-utils-440 440.82-0ubuntu0~0.19.10.1 amd64 NVIDIA compute utilities
ii nvidia-container-runtime 3.1.4-1 amd64 NVIDIA container runtime
ii nvidia-container-toolkit 1.0.5-1 amd64 NVIDIA container runtime hook
ii nvidia-dkms-440 440.82-0ubuntu0~0.19.10.1 amd64 NVIDIA DKMS package
ii nvidia-docker2 2.2.2-1 all nvidia-docker CLI wrapper
ii nvidia-headless-440 440.82-0ubuntu0~0.19.10.1 amd64 NVIDIA headless metapackage
ii nvidia-headless-no-dkms-440 440.82-0ubuntu0~0.19.10.1 amd64 NVIDIA headless metapackage - no DKMS
ii nvidia-kernel-common-440 440.82-0ubuntu0~0.19.10.1 amd64 Shared files used with the kernel module
ii nvidia-kernel-source-440 440.82-0ubuntu0~0.19.10.1 amd64 NVIDIA kernel source package
ii nvidia-modprobe 418.56-1 amd64 utility to load NVIDIA kernel modules and create device nodes
ii nvidia-utils-440 440.82-0ubuntu0~0.19.10.1 amd64 NVIDIA driver support binaries
yuya$ nvidia-smi
Fri May 8 15:31:23 2020
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.82 Driver Version: 440.82 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 1070 Off | 00000000:01:00.0 Off | N/A |
| 0% 36C P8 7W / 195W | 0MiB / 8119MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
...
yuya$ docker version
Client: Docker Engine - Community
Version: 19.03.8
API version: 1.40
Go version: go1.12.17
Git commit: afacb8b7f0
Built: Wed Mar 11 01:25:55 2020
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.8
API version: 1.40 (minimum version 1.12)
Go version: go1.12.17
Git commit: afacb8b7f0
Built: Wed Mar 11 01:24:26 2020
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.2.13
GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429
nvidia:
Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version: 0.18.0
GitCommit: fec3683
yuya$ docker info
...
Security Options:
apparmor
seccomp
Profile: default
...
yuya$ cat /etc/docker/daemon.json
{
"default-runtime": "nvidia",
"runtimes": {
"nvidia": {
"path": "nvidia-container-runtime",
"runtimeArgs": []
}
}
}
nvidia-docker2
パッケージをインストールし、default-runtime
をnvidia
に設定しているのは、Kubernetes上でGPUを使用するためです。
主題のRootlessモードには関係ありません。
なお、記事中で使用しているyuya
ユーザはdocker
グループに属しており、通常モードのDockerにアクセスできます。
yuya$ id yuya
uid=1000(yuya) gid=1000(yuya) groups=1000(yuya),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),115(lxd),998(docker)
3. 通常モードでのGPUの使用
通常モード(root
権限でDockerデーモンが動作している一般的な状態)では、nvidia-container-toolkit
パッケージをインストールし、--gpus all
オプションを付加することで、DockerコンテナでGPUを使用することができます。
nvidia-smi
の実行結果は以下の通りです。Dockerコンテナ内でGPUを認識していることが確認できます。
yuya$ docker container run --tty --rm --gpus all nvidia/cuda:10.2-base-ubuntu18.04 nvidia-smi
Fri May 8 06:54:54 2020
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.82 Driver Version: 440.82 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 1070 Off | 00000000:01:00.0 Off | N/A |
| 0% 36C P8 7W / 195W | 0MiB / 8119MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
...
4. RootlessモードでのGPUの使用
4.1. RootlessモードのDockerをセットアップする
まずは、Rootlessモードの実行に必要なuidmap
パッケージ、slirp4netns
パッケージをインストールします。
yuya$ sudo apt update && sudo apt install --yes uidmap slirp4netns
続いて、Rootlessモードの動作確認を行うため、docker
グループに属していない新しいユーザrootless
を作成します。
yuya$ sudo adduser rootless
yuya$ id rootless
uid=1001(rootless) gid=1001(rootless) groups=1001(rootless)
準備が整ったら、RootlessモードのDockerをインストールしたいユーザ(今回はrootless
ユーザ)でログインし、RootlessモードのDockerをインストールします。
この際、su - rootless
などユーザを切り替えるのではなく、ログインシェルが起動する状態でログインする必要があります。そうしないとユーザ毎のsystemdデーモンが起動しないので注意が必要です。念のため、systemctl --user daemon-reload
が実行できることを確認しておきましょう。
インストール自体はcurl -fsSL https://get.docker.com/rootless | sh
で一発です。sudo
コマンドを使用していないことに気を付けましょう。
rootless$ systemctl --user daemon-reload
rootless$ echo $?
0
rootless$ curl -fsSL https://get.docker.com/rootless | sh
# Installing stable version 19.03.8
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 60.7M 100 60.7M 0 0 2995k 0 0:00:20 0:00:20 --:--:-- 2904k
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 17.8M 100 17.8M 0 0 2511k 0 0:00:07 0:00:07 --:--:-- 2559k
# starting systemd service
● docker.service - Docker Application Container Engine (Rootless)
Loaded: loaded (/home/rootless/.config/systemd/user/docker.service; disabled; vendor preset: enabled)
Active: active (running) since Fri 2020-05-08 15:39:07 JST; 3ms ago
Docs: https://docs.docker.com
Main PID: 14632 (dockerd-rootles)
CGroup: /user.slice/user-1001.slice/user@1001.service/docker.service
├─14632 /bin/sh /home/rootless/bin/dockerd-rootless.sh --experimental --storage-driver=overlay2
└─14639 [grep]
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + : auto
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + net=
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + mtu=
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + [ -z ]
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + which slirp4netns
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + slirp4netns --help
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + grep -- --disable-host-loopback
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: --disable-host-loopback prohibit connecting to 127.0.0.1:* on the host namespace
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + net=slirp4netns
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + [ -z ]
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + mtu=65520
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + [ -z slirp4netns ]
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + [ -z 65520 ]
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + [ -z ]
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + _DOCKERD_ROOTLESS_CHILD=1
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + export _DOCKERD_ROOTLESS_CHILD
May 08 15:39:07 ml-1 dockerd-rootless.sh[14632]: + exec rootlesskit --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/rootless/bin/dockerd-rootless.sh --experimental --storage-driver=overlay2
Client: Docker Engine - Community
Version: 19.03.8
API version: 1.40
Go version: go1.12.17
Git commit: afacb8b7f0
Built: Wed Mar 11 01:22:56 2020
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.8
API version: 1.40 (minimum version 1.12)
Go version: go1.12.17
Git commit: afacb8b7f0
Built: Wed Mar 11 01:30:32 2020
OS/Arch: linux/amd64
Experimental: true
containerd:
Version: v1.2.13
GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc:
Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version: 0.18.0
GitCommit: fec3683
# Docker binaries are installed in /home/rootless/bin
# WARN: dockerd is not in your current PATH or pointing to /home/rootless/bin/dockerd
# Make sure the following environment variables are set (or add them to ~/.bashrc):
export PATH=/home/rootless/bin:$PATH
export DOCKER_HOST=unix:///run/user/1001/docker.sock
#
# To control docker service run:
# systemctl --user (start|stop|restart) docker
#
4.2. RootlessモードのDockerの動作を確認する
インストールが完了したら、関連する環境変数を設定し、docker info
を実行します。Security Options:
にrootless
が含まれていれば、Rootlessモードで動作しています。
また、適宜.bashrc
などを編集し、環境変数の設定を追加します。
rootless$ export PATH=/home/rootless/bin:$PATH
rootless$ export DOCKER_HOST=unix:///run/user/1001/docker.sock
rootless$ docker version
Client: Docker Engine - Community
Version: 19.03.8
API version: 1.40
Go version: go1.12.17
Git commit: afacb8b7f0
Built: Wed Mar 11 01:22:56 2020
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.8
API version: 1.40 (minimum version 1.12)
Go version: go1.12.17
Git commit: afacb8b7f0
Built: Wed Mar 11 01:30:32 2020
OS/Arch: linux/amd64
Experimental: true
containerd:
Version: v1.2.13
GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc:
Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version: 0.18.0
GitCommit: fec3683
rootless$ docker info
...
Security Options:
seccomp
Profile: default
rootless
...
4.3. nvidia-container-runtime
の設定を変更する
RootlessモードのDockerでnvidia-smi
を実行すると、以下の通りエラーとなります。
rootless$ docker container run --tty --rm --gpus all nvidia/cuda:10.2-base-ubuntu18.04 nvidia-smi
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \"process_linux.go:432: running prestart hook 0 caused \\\"error running hook: exit status 1, stdout: , stderr: nvidia-container-cli: mount error: open failed: /sys/fs/cgroup/devices/user.slice/devices.allow: permission denied\\\\n\\\"\"": unknown.
moby
リポジトリのIssue#38729を参考に、nvidia-container-runtime
の設定を変更します。具体的にはno-cgroups
をデフォルト値のfalse
からtrue
に変更します。
参考: nvidia-container-runtime doesn't work with rootless mode · Issue #38729 · moby/moby
yuya$ sudo cp /etc/nvidia-container-runtime/config.toml /etc/nvidia-container-runtime/config.toml.20200508
yuya$ sudo vim /etc/nvidia-container-runtime/config.toml
yuya$ diff -U 3 /etc/nvidia-container-runtime/config.toml.20200508 /etc/nvidia-container-runtime/config.toml
--- /etc/nvidia-container-runtime/config.toml.20200508 2020-05-08 15:50:44.605107694 +0900
+++ /etc/nvidia-container-runtime/config.toml 2020-05-08 15:50:57.125141605 +0900
@@ -8,7 +8,7 @@
#debug = "/var/log/nvidia-container-toolkit.log"
#ldcache = "/etc/ld.so.cache"
load-kmods = true
-#no-cgroups = false
+no-cgroups = true
#user = "root:video"
ldconfig = "@/sbin/ldconfig.real"
4.4. RootlessモードのDockerでGPUを使用する
nvidia-container-runtime
の設定を変更すると、nvidia-smi
が実行できるようになります。なお、Dockerデーモンの再起動は不要です。
rootless$ docker container run --tty --rm --gpus all nvidia/cuda:10.2-base-ubuntu18.04 nvidia-smi
Fri May 8 06:56:51 2020
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.82 Driver Version: 440.82 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 1070 Off | 00000000:01:00.0 Off | N/A |
| 0% 36C P8 7W / 195W | 0MiB / 8119MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
...
rootless$ docker container run --interactive --tty --rm --gpus all nvidia/cuda:10.2-base-ubuntu18.04 /bin/bash
root@docker# apt update && apt install --yes python3-pip
root@docker# pip3 install torch
root@docker# python3
Python 3.6.9 (default, Apr 18 2020, 01:56:04)
...
>>> import torch
>>> print(torch.cuda.is_available())
True
>>> torch.cuda.get_device_name(0)
'GeForce GTX 1070'
上記の通り、Dockerコンテナ内のPyTorchからもGPUが認識できています。
5. 【未解決】通常モードとRootlessモードの共存
/etc/nvidia-container-runtime/config.toml
にno-cgroups = true
を追加し、RootlessモードのDockerでGPUを使用できるようにすると、逆に通常モードのDockerではGPUを使用できなくなってしまいます。
こちらの問題についてはまだ調査しておらず、未解決です。何か情報があれば、お知らせ頂けると嬉しいです。
yuya$ grep no-cgroups /etc/nvidia-container-runtime/config.toml
no-cgroups = true
yuya$ docker container run --tty --rm --gpus all nvidia/cuda:10.2-base-ubuntu18.04 nvidia-smi
Failed to initialize NVML: Unknown Error