WonderPlanet Tech Blog

アドバンストテクノロジー部やCTO室のメンバーを中心に最新の技術情報を発信しています。

nvidia-dockerをdocker-composeから使う

こんにちは。アドバンストテクノロジー部のR&Dチーム所属岩原です。
好きなKerasのLayerはDropoutです。

今回は、docker上でNVIDIA製GPUを使ったディープラーニング環境を構築してくれるnvidia-dockerの紹介と、
nvidia-dockerDocker-compose で使用する方法を導入から紹介したいと思います。

nvidia-dockerとは

Docker上でCUDAやcuDNNを使えるようにしたNVIDIA製のDockerプラグインです。
NVIDIA/nvidia-docker: Build and run Docker containers leveraging NVIDIA GPUs

Tensorflow-gpuがDocker上で動かせるようになるため、環境を選ばず(といってもNVIDIA製GPUは必要ですが)どこでもTensorflowやKerasをぶん回すことができるようになります。

ただし、nvidia-dockerを実行する場合、通常は以下のようなコマンドで実行します。

nvidia-docker run --rm nvidia/cuda nvidia-smi

この場合、docker-composeではnvidia-dockerが使えない事になります。

eywalker/nvidia-docker-compose: Simple wrapper for docker-compose to use GPU enabled docker under nvidia-dockerのようなdocker-composeを拡張したモジュールもありますが、
今回はそのような物をインストールせず、手軽に対応する方法をご紹介したいと思います。

まずは導入から始めましょう。

環境

  • OS:Ubuntu 16_04 LTS
  • GPU:GTX 1080 Ti 11GB
    • GPUは最近のNVIDIA製GPUであれば問題はないかと思います。
      GPUのアーキテクチャが Kepler であれば最新のCUDA9にも対応しています。

CUDA(& Driver)のインストール

nvidia-dockerにCUDAは本来必要ないのですが、ドライバー単体のインストールは面倒なので、CUDAもついでにインストールしてしまいます。
ドライバー単体はNVIDIAドライバダウンロードからダウンロード出来ます。
今回はCUDAのインストール手順をご紹介します。

手順

  1. CUDA Toolkit Download | NVIDIA DeveloperよりLinux -> x86_64 -> Ubuntu -> 16.04 -> deb(network) を選びます。
  2. Downloadのリンクアドレスをコピーします。
    • http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-repo-ubuntu1604_9.0.176-1_amd64.deb のようなURLになっているはずです。バージョンによってURLは変わります。
  3. wget 2.でコピーしたURLでダウンロードします。
  4. 以下、Installation Instructions に沿ってコマンドを実行していきます。このあたりはCUDAのバージョンによって変わってくるので、画面の指示に従いましょう。以下は2017/11/2時点での最新の場合のコマンドになります。なお、最後に再起動するコマンドを追加しています。
sudo dpkg -i cuda-repo-ubuntu1604_9.0.176-1_amd64.deb
sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/7fa2af80.pub
sudo apt-get update
sudo apt-get install cuda
sudo reboot

エラーが出た場合

sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/7fa2af80.pub

上記実行時、

gpgkeys: protocol `https' not supported

というメッセージが出る場合があります。
この場合、

sudo apt-get install gnupg-curl

を実行した上で、再度コマンド実行すればOKです。

動作確認

nvidia-smiコマンドがエラーにならなければ正常にインストールされたことになります。

Dockerのインストール

古いDockerの削除(Docker CEになる前にdockerをインストールした人のみ)

sudo apt-get remove docker docker-engine docker.io で古いDockerを削除します。

手順

  1. 必要なパッケージのインストール
    sudo apt-get install apt-transport-https ca-certificates curl software-properties-common で必要なパッケージのインストールを行います。

  2. GPGキーの取得
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - でDocker公式のGPGキーを取得&追加します。

  3. リポジトリの追加
    sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" でリポジトリの追加を行います。 今回はstable ですが testedgeと言ったバージョンも指定できます。

  4. パッケージの更新
    sudo apt-get update でパッケージの更新を再度行います。

  5. Docker CEのインストール
    sudo apt-get install docker-ce でdockerのインストールを行います。

  6. 動作確認
    sudo docker run hello-worldHello from Docker! が表示されれば問題なくインストールされています。

nvidia-dockerのインストール

wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.1/nvidia-docker_1.0.1-1_amd64.deb
sudo dpkg -i /tmp/nvidia-docker*.deb && rm /tmp/nvidia-docker*.deb

コレだけです。

nvidia-dockerを動かしてみる

nvidia-docker run --rm nvidia/cuda nvidia-smi

を実行し、GPUドライバの情報が出力されれば、インストールは成功です。

Docker-composeのインストール

手順

以下のコマンドを実行します。

sudo curl -L https://github.com/docker/compose/releases/download/1.16.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose version 1.16.1, build 1719ceb

のような表示がされればOKです。

Docker-composeで動かす方法

さて、本題の docker-compose コマンドでnvidia-dockerを動かす方法です。 先にdocker-compose.ymlを示します。

version: "3"
services:
    tensorflow_keras:
        build:
            dockerfile: Dockerfile
        volumes:
            - nvidia_driver_384.90:/usr/local/nvidia:ro
        devices:
            - /dev/nvidiactl
            - /dev/nvidia-uvm
            - /dev/nvidia0
volumes:
    nvidia_driver_384.90:
        external: true

volumes でドライバを、devicesでGPUなどを指定してやる形になります。

nvidia_driver_384.90 の部分はインストールされているドライバのバージョンによって変わります。
nvidia-smiコマンドを実行して確認しましょう。

nvidia-smi
Mon Nov  6 07:14:16 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 384.90                 Driver Version: 384.90                    |
|-------------------------------+----------------------+----------------------+

nvidia-docker を実行した際、必要なvolumeが作られてなければ自動作成されますが、 そうでない場合、以下のコマンドを実行し、自分でvolumeを作成してやる必要があります。

docker volume create --name=nvidia_driver_384.90 -d nvidia-docker

nvidia_driver_384.90384.90 はドライバーのバージョンによって変わりますので、適宜変えるようにしてください。

docker volume ls コマンドでDRIVERnvidia-docker になっているものが存在していればOKです。

docker volume ls | grep nvidia-docker
nvidia-docker       nvidia_driver_384.90

実際に動かしてみる

それでは、実際にnvidia-docker上で動かしてみましょう。
Kerasのサンプルの1つである、mnist + CNNを動かすDockerイメージを作ります。
keras/mnist_cnn.py at master · fchollet/keras

まず、ディレクトリ構造を以下の通りとします。

.
├── docker-compose.yml
└── tensorflow_keras
    ├── Dockerfile
    ├── entrypoint.sh
    ├── mnist_cnn.py
    └── requirements.txt

上から順に記載していきます。

docker-compose.yml

ほぼ先ほど紹介した内容に沿います。Dockerfileのディレクトリだけ違うので、それだけ追加しています。

version: "3"
services:
    tensorflow_keras:
        build:
            context: ./tensorflow_keras
            dockerfile: Dockerfile
        volumes:
            - nvidia_driver_384.90:/usr/local/nvidia:ro
        devices:
            - /dev/nvidiactl
            - /dev/nvidia-uvm
            - /dev/nvidia0
volumes:
    nvidia_driver_384.90:
        external: true

tensorflow_keras/Dockerfile

Tensorflowを使用するため、CUDA8 + CuDNN6の組み合わせのdockerイメージをベースに使用します。
OSはホストと同じUbuntu16.04を使用します。

FROM nvidia/cuda:8.0-cudnn6-runtime-ubuntu16.04

RUN apt-get update && \
    apt-get -y install wget build-essential gcc zlib1g-dev openssl libssl-dev libbz2-dev libreadline-dev libsqlite3-dev libmecab-dev mecab mecab-ipadic-utf8

# setup Python3.6
WORKDIR /tmp
RUN wget https://www.python.org/ftp/python/3.6.1/Python-3.6.1.tgz \
        && tar zxf Python-3.6.1.tgz \
        && cd Python-3.6.1 \
        && ./configure \
        && make altinstall
ENV PYTHONIOENCODING "utf-8"

# pip install
COPY requirements.txt /tmp
RUN pip3.6 install -r /tmp/requirements.txt
RUN python3.6 --version

# setup my script
RUN mkdir /var/tensorflow_keras
COPY mnist_cnn.py /var/tensorflow_keras
WORKDIR /var/tensorflow_keras

# setup endpoint
COPY entrypoint.sh ./
RUN chmod +x ./entrypoint.sh
ENTRYPOINT ./entrypoint.sh

tensorflow_keras/entrypoint.sh

実行の確認さえできればいいので、Pythonスクリプトを実行するだけです。
無限待ちしたい場合は最後の tail -f /dev/null のコメントアウトを外します。

#! /bin/bash
python3.6 mnist_cnn.py
# tail -f /dev/null

tensorflow_keras/mnist_cnn.py

Kerasを使用してmnistの学習を行う簡単なサンプルです。
keras/mnist_cnn.py at master · fchollet/kerasからそのまま持ってきましょう。

requirements.txt

必要なPythonライブラリ群を記したものです。
現在の弊社環境のものを記載します。不要なものも混じってますが、参考までに。
基本的に、kerasとtensorflow-gpuのみ pip install すれば良いはずです。

bleach==1.5.0
h5py==2.7.1
html5lib==0.9999999
Keras==2.0.8
Markdown==2.6.9
numpy==1.13.1
pandas==0.20.3
protobuf==3.4.0
python-dateutil==2.6.1
pytz==2017.2
PyYAML==3.12
scikit-learn==0.19.0
scipy==0.19.1
six==1.10.0
tensorflow-gpu==1.3.0
tensorflow-tensorboard==0.1.6
Werkzeug==0.12.2
mecab-python3==0.7

実行結果

まずはイメージのビルドをします。

docker-compose build

次に実行します。

docker-compose up

すると、大量の出力とともに、結果が以下のように出力されるはずです。

〜〜〜〜 省略 〜〜〜〜
tensorflow_keras_1  | Epoch 10/12
60000/60000 [==============================] - 3s - loss: 0.0383 - acc: 0.9886 - val_loss: 0.0311 - val_acc: 0.9900
tensorflow_keras_1  | Epoch 11/12
60000/60000 [==============================] - 3s - loss: 0.0380 - acc: 0.9887 - val_loss: 0.0292 - val_acc: 0.9901
tensorflow_keras_1  | Epoch 12/12
60000/60000 [==============================] - 3s - loss: 0.0347 - acc: 0.9897 - val_loss: 0.0291 - val_acc: 0.9903
tensorflow_keras_1  | Test loss: 0.0291294318363
tensorflow_keras_1  | Test accuracy: 0.9903
tensorflow_keras_1  | Using TensorFlow backend.
tensorflow_keras_1 exited with code 0

ちなみに、GPUが使われているかどうかを判断するには、以下の出力があるかどうかでわかります。

tensorflow_keras_1  | 2017-11-02 06:12:18.275339: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1045] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 1080 Ti, pci bus id: 0000:01:00.0)

docker-compose.ymlの記述が間違っていたりすると、

tensorflow_keras_1  | 2017-11-02 06:16:04.237736: E tensorflow/stream_executor/cuda/cuda_driver.cc:406] failed call to cuInit: CUDA_ERROR_NO_DEVICE

のような出力がされたり、CPUが使われるようになるので、以下のように学習時間がとても長くなったりします。

tensorflow_keras_1  | Epoch 1/12
60000/60000 [==============================] - 74s - loss: 0.3219 - acc: 0.9015 - val_loss: 0.0803 - val_acc: 0.9744

上記の例では、1epochで74sec掛かってることになります。およそ25倍時間がかかってます。

複数のGPUを持つマシンで、GPUを指定したい

以下のような構成の場合(例:AWSのp2.8xlarge)

ls -la /dev | grep nvidia
crw-rw-rw-  1 root root    195,   0 Nov  6 05:51 nvidia0
crw-rw-rw-  1 root root    195,   1 Nov  6 05:51 nvidia1
crw-rw-rw-  1 root root    195,   2 Nov  6 05:51 nvidia2
crw-rw-rw-  1 root root    195,   3 Nov  6 05:51 nvidia3
crw-rw-rw-  1 root root    195,   4 Nov  6 05:51 nvidia4
crw-rw-rw-  1 root root    195,   5 Nov  6 05:51 nvidia5
crw-rw-rw-  1 root root    195,   6 Nov  6 05:51 nvidia6
crw-rw-rw-  1 root root    195,   7 Nov  6 05:51 nvidia7
crw-rw-rw-  1 root root    195, 255 Nov  6 05:51 nvidiactl
crw-rw-rw-  1 root root    247,   0 Nov  6 05:51 nvidia-uvm
crw-rw-rw-  1 root root    247,   1 Nov  6 05:52 nvidia-uvm-tools

GPUが複数存在する事になります。 この場合、nvidia-dockerで使用するGPUを指定することが可能です。

docker-compose.ymlのdevicesを使用したいGPUにしてやることで可能となります。

version: "3"
services:
    tensorflow_keras:
        build:
            context: ./tensorflow_keras
            dockerfile: Dockerfile
        volumes:
            - nvidia_driver_384.90:/usr/local/nvidia:ro
        devices:
            - /dev/nvidiactl
            - /dev/nvidia-uvm
            - /dev/nvidia1  # ココ!
volumes:
    nvidia_driver_384.90:
        external: true

また、複数指定してやることも可能です。

version: "3"
services:
    tensorflow_keras:
        build:
            context: ./tensorflow_keras
            dockerfile: Dockerfile
        volumes:
            - nvidia_driver_384.90:/usr/local/nvidia:ro
        devices:
            - /dev/nvidiactl
            - /dev/nvidia-uvm
            - /dev/nvidia2  # ココ!
            - /dev/nvidia1  # ココ!
volumes:
    nvidia_driver_384.90:
        external: true

tensorflowでちゃんと複数認識します

tensorflow_keras_1  | 2017-11-06 07:29:00.244487: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1045] Creating TensorFlow device (/gpu:0) -> (device: 0, name: Tesla K80, pci bus id: 0000:00:18.0)
tensorflow_keras_1  | 2017-11-06 07:29:00.244501: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1045] Creating TensorFlow device (/gpu:1) -> (device: 1, name: Tesla K80, pci bus id: 0000:00:19.0)

使用するGPUを割り振ることで、複数Dockerでも効率よく学習を行うことが出来ます。

まとめ

ホストマシンの環境に左右されてしまうところはありますが、環境さえ合わせてしまえば複数マシンで同じイメージが使用できるので、
ディープラーニング環境のポータビリティがよくなります。 また、バージョン違いのTensorflowも1つのマシンで共有できるので、1台のマシンで複数バージョンのCUDAを使用したい場合などに重宝します。

参考