Ubuntu Weekly Recipe

第461回 DockerでCUDA 8.0を使用する

この記事を読むのに必要な時間:およそ 8.5 分

CUDAコンテナの起動

次はCUDAコンテナを起動してみましょう。まずはNVIDIA Dockerが正しく動くかどうかの確認がてら,コンテナを検索してみます。

$ nvidia-docker search nvidia/cuda
NAME                                   DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
nvidia/cuda                            CUDA and cuDNN images from gitlab.com/nvid...   94
(以下略)

いくつも候補が出てくると思いますが先頭にあるnvidia/cudaがNVIDIAの公式イメージです。Docker Hubのサイトを確認するとCUDAのバージョンごとにタグが割り振られていることがわかります。2017年3月時点でのタグ未指定時に使われる「latest」は,Ubuntu 16.04 LTSのコンテナ上にCUDA 8.0とその開発用のファイル一式が入った「8.0-devel-ubuntu16.04」です。

まずはlatestタグのコンテナでnvidia-smiコマンドを使ってみましょう。

$ nvidia-docker run --rm nvidia/cuda nvidia-smi
Using default tag: latest
latest: Pulling from nvidia/cuda
d54efb8db41d: Pull complete
f8b845f45a87: Pull complete
e8db7bf7c39f: Pull complete
9654c40e9079: Pull complete
6d9ef359eaaa: Pull complete
cdfa70f89c10: Pull complete
3208f69d3a8f: Pull complete
eac0f0483475: Pull complete
4580f9c5bac3: Pull complete
6ee6617c19de: Pull complete
Digest: sha256:2b7443eb37da8c403756fb7d183e0611f97f648ed8c3e346fdf9484433ca32b8
Status: Downloaded newer image for nvidia/cuda:latest
Fri Mar  4 08:26:58 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 375.39                 Driver Version: 375.39                    |
|-------------------------------+----------------------+----------------------+
| 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 105...  Off  | 0000:01:00.0     Off |                  N/A |
|  0%   33C    P8    35W /  72W |      0MiB /  4038MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

コンテナの中からでもGPUデバイスにアクセスできていることがわかりますね。

普通のDockerとNVIDIA Dockerの違い

nvidia-dockerコマンドとdockerコマンドを比べてみると,どちらも同じイメージファイルが見えています。

$ nvidia-docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nvidia/cuda         latest              1cce8839a2c5        36 hours ago        1.62 GB
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nvidia/cuda         latest              1cce8839a2c5        36 hours ago        1.62 GB

ではdockerコマンドからnvidia-smiを実行するとどうなるのでしょうか。

$ docker run --rm nvidia/cuda nvidia-smi
container_linux.go:247: starting container process caused "exec: \"nvidia-smi\": executable file not found in $PATH"
docker: Error response from daemon: oci runtime error: container_linux.go:247: starting container process caused "exec: \"nvidia-smi\": executable file not found in $PATH".

nvidia-smiが見つからなかったようです。nvidia-dockerのほうでコマンドの位置を確認してみましょう。

$ nvidia-docker run --rm nvidia/cuda which nvidia-smi
/usr/local/nvidia/bin/nvidia-smi
$ docker run --rm nvidia/cuda ls /usr/local/nvidia
ls: cannot access '/usr/local/nvidia': No such file or directory

nvidia-dockerコマンド経由だと/usr/local/nvidia/bin以下に存在するにもかかわらず,dockerコマンド経由だとディレクトリ自体が存在しないようです。つまり「nvidia/cuda」コンテナのイメージにはこのディレクトリが存在しないことになります。

実はDockerには「Volume」というホストの一部の領域をコンテナからもアクセスできるようにする仕組みが存在します。/usr/local/nvidiaはこのVolumeとしてマウントされた領域です。実際にコンテナの中でmountコマンドを実行してみると,それがわかります。

$ nvidia-docker run --rm nvidia/cuda mount | grep nvidia
/dev/sda2 on /usr/local/nvidia type btrfs (ro,relatime,ssd,space_cache,subvolid=257,subvol=/@/var/lib/nvidia-docker/volumes/nvidia_driver/375.39)

どうやらホストの/var/lib/nvidia-docker/volumes/nvidia_driver/375.39がコンテナ内部の/usr/local/nvidiaになっているようですね※3⁠。Volumeの状態はdockervolumeサブコマンドを使うと確認できます。ちなみにこのVolumeはnvidia-docker-pluginが作成しています

※3
今回使用しているホストはルートファイルシステムがBtrfsなために上記のような表示になっています。Storage Driverとしてaufsを使っている場合は,もう少し別の表示になります。
$ docker volume ls
DRIVER              VOLUME NAME
nvidia-docker       nvidia_driver_375.39
$ docker volume inspect nvidia_driver_375.39
[
    {
        "Driver": "nvidia-docker",
        "Labels": null,
        "Mountpoint": "/var/lib/nvidia-docker/volumes/nvidia_driver/375.39",
        "Name": "nvidia_driver_375.39",
        "Options": {},
        "Scope": "local"
    }
]
$ ls /var/lib/nvidia-docker/volumes/nvidia_driver/375.39/
bin  lib  lib64
$ ls /var/lib/nvidia-docker/volumes/nvidia_driver/375.39/bin/
nvidia-cuda-mps-control  nvidia-cuda-mps-server  nvidia-debugdump  nvidia-persistenced  nvidia-smi

NVIDIAのカーネルドライバーはたんなるカーネルモジュールだけでなく,カーネルモジュールとユーザーランドとの橋渡しを行うライブラリも提供しています。CUDAは原則としてこのライブラリを経由してGPUを操作するのです。よってこれらのライブラリは,コンテナの中から見える必要があります。ところがこれらのライブラリそのものは,カーネルモジュールのバージョンに紐付いています。つまりカーネルドライバーのバージョンがあがると,これらのライブラリも同じバージョンとして更新する必要があるということです。

もしこれらのライブラリをコンテナイメージの中に同梱してしまうと,ホストのカーネルドライバーの更新のたびにコンテナイメージを再構築する必要がでてきます。これではDockerの有りがたみが薄れてしまいます。そこでNVIDIA Dockerでは,カーネルモジュールに紐付いているライブラリや実行バイナリは,DockerのVolumeを使ってコンテナの中からもアクセスできるようにしているわけです。

なおデバイスファイルについても,前述したとおりnvidia-docker経由でのみアクセスできます。

$ nvidia-docker run --rm nvidia/cuda sh -c 'ls -l /dev/nvidia*'
crw-rw-rw- 1 root root 241,   0 Mar  3 09:54 /dev/nvidia-uvm
crw-rw-rw- 1 root root 241,   1 Mar  3 09:54 /dev/nvidia-uvm-tools
crw-rw-rw- 1 root root 195,   0 Mar  3 09:54 /dev/nvidia0
crw-rw-rw- 1 root root 195, 255 Mar  3 09:54 /dev/nvidiactl
$ docker run --rm nvidia/cuda sh -c 'ls -l /dev/nvidia*'
ls: cannot access '/dev/nvidia*': No such file or directory

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。