Docker运行NVIDIA容器

../../_images/nvidia_container_runtime_for_docker.png

在机器学习中,需要通过GPU加速矩阵计算来实现卷积神经网络CNN。在云计算环境中, KVM的GPU直通虚拟化 可以将一块物理GPU显卡分割成多块虚拟GPU卡提供给虚拟机使用。同样类似,在Docker容器中,nVidia通过 nvidia-container-runtime-hook 将Host主机的GPU驱动共享给容器,这样就不需要在每个容器中单独安装驱动。

注解

本文的实践是在MacBook Pro 2013 later笔记本上完成,显卡 GeForce GT 750M Mac Edition

lspci | grep VGA

显示显卡版本:

01:00.0 VGA compatible controller: NVIDIA Corporation GK107M [GeForce GT 750M Mac Edition] (rev a1)

Nvidia官方网站提供了 CUDA Drivers for MAC 可以在Mac平台支持CUDA开发。

nvidia GEFORCE 驱动下载 可以获取最新的Linux驱动。

安装NVIDIA图形卡的CUDA

通过NVIDIA软件仓库安装CUDA

注解

参考 NVIDIA CUDA Installation Guide for Linux 请确保系统:

  • 根据 http://developer.nvidia.com/cuda-gpus 验证自己的显卡是否支持CUDA

  • 确保已经安装了gcc

  • 已经安装了当前内核头文件和开发包:

    sudo apt-get install linux-headers-$(uname -r)
    

参考 NVIDIA graphical card installation guide 安装最新的CUDA版本。注意,在Ubuntu上安装NVIDIA的CUDA有两种方式,一种是Runfile安装,一种是Debian安装。Runfile安装只提供本地安装,需要下载很大的初始安装文件。而Debian安装既可以本地安装也可以通过网络安装,并且网路安装可以只下载你需要的文件。这里案例采用Debian Installer的Network方式:

sudo dpkg --install cuda-repo-<distro>-<version>.<architecture>.deb
sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/<distro>/<architecture>/7fa2af80.pub
sudo apt-get update
sudo apt-get install cuda

注解

这里 <distro>-<version>.<architecture> 请使用 lsb_release -a 命令检查,并从 https://developer.download.nvidia.com/compute/cuda/repos/ 对应下载对应的repo包,例如,我使用Ubuntu 18.10,则下载 cuda-repo-ubuntu1810_10.1.105-1_amd64.deb ,所以实际执行的命令是:

wget https://developer.download.nvidia.cn/compute/cuda/repos/ubuntu1810/x86_64/cuda-repo-ubuntu1810_10.1.105-1_amd64.deb
sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1810/x86_64/7fa2af80.pub
sudo dpkg --install cuda-repo-ubuntu1810_10.1.105-1_amd64.deb
sudo apt-get update
sudo apt-get install cuda

注解

由于安装了NVIDIA的软件仓库,实际安装CUDA时候会将图形卡驱动更新到官方最新版本,比Ubuntu发行版提供的驱动要新很多。

更新环境变量

  • 编辑 ~/.bashrc 添加:

    # CUDA
    export PATH="/usr/local/cuda/bin:$PATH"
    export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"
    

注解

/usr/local/cuda 是软链接到 /usr/local/cuda-10.1 ,当前安装的CUDA版本是 10.1

  • 并且重新加载环境变量:

    source ~/.bashrc
    

NVIDIA持久化服务

在NVIDIA软件启动之前,需要先启动NVIDIA持久化服务 NVIDIA Persistence Daemon,这个持久化服务是为了保持CPU初始化状态,即使没有客户端连接上来,并且会将服务状态保持在CUDA任务处理上。这个步骤是必须的。

注解

在Ubuntu 18.10上测试安装NVIDIA提供的CUDA包,可以看到安装包已经创建了 /lib/systemd/system/nvidia-persistenced.service 配置文件,所以这步虽然是必须的,但也可能可以默认忽略。

  • 创建 /lib/systemd/system/nvidia-persistenced.service (根据发行版规则,这个文件也可能是 /usr/lib/systemd/system/nvidia-persistenced.service )内容如下:

    [Unit]
    Description=NVIDIA Persistence Daemon
    Wants=syslog.target
    
    [Service]
    Type=forking
    PIDFile=/var/run/nvidia-persistenced/nvidia-persistenced.pid
    Restart=always
    ExecStart=/usr/bin/nvidia-persistenced --verbose
    ExecStopPost=/bin/rm -rf /var/run/nvidia-persistenced
    
    [Install]
    WantedBy=multi-user.target
    
  • 然后激活服务:

    sudo systemctl enable nvidia-persistenced
    

禁用一些UDEV规则

udev 规则(物理设备和系统之间的接口)会妨碍NVIDIA驱动正常工作,所以需要编辑 /lib/udev/rules.d/40-vm-hotadd.rules 文件,并注释掉 memory 子系统规则:

# Memory hotadd request
# SUBSYSTEM=="memory", ACTION=="add", DEVPATH=="/devices/system/memory/memory[0-9]*", TEST=="state", ATTR{state}="online"

验证CUDA安装

重启主机,然后测试CUDA是否安装正确。这个验证还是通过以下案例的编译来验证的:

cuda-install-samples-10.1.sh ~
cd ~/NVIDIA_CUDA-10.1_Samples
make

然后以下文件的输出:

# take a book, go for a walk, or any other activity that takes time...

./bin/x86_64/linux/release/deviceQuery | tail -n 1

如果输出内容 Result = PASS 则表明CUDA安装成功。

安装NVIDIA Docker

安装 nvidia-docker 仓库

  • 执行以下命令安装 nvidia-docker 仓库配置:

    curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | \
      sudo apt-key add -
    distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
    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
    

注解

nvidia-docker Repostory configuration 提供了针对不同发行版的软件仓库,但是 nvidia-docker 只提供Ubuntu LTS发行版支持,所以不直接支持Ubuntu 18.10。参考 Ubuntu 18.10 is definitely missingubuntu:18.10 support 采用 nvidia-docker version for ubuntu 18.04

curl -s -L https://nvidia.github.io/nvidia-docker/ubuntu18.04/nvidia-docker.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-docker.list

安装 docker-ce

在安装 nvidia-docker2 工具之前,需要确保系统使用的是最新官方Docker版本,即 docker-ce ,参考 official documentation 先卸载发行版docker,然后安装官方docker:

# remove all previous Docker versions
sudo apt remove docker docker-engine docker.io

# add Docker official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# Add Docker repository (for Ubuntu Bionic) 注意:nvidia-docker会检查docker-ce版本,强制要求 ubuntu-bionic
# 所以这里必须采用 bionic 仓库安装 docker-ce
sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"

sudo apt update
sudo apt install docker-ce

安装 nvidia-docker

  • 安装 nvidia-docker 并重新加载Docker daemon配置:

    # Install nvidia-docker2 and reload the Docker daemon configuration
    sudo apt-get install -y nvidia-docker2
    sudo pkill -SIGHUP dockerd
    

验证NVIDIA容器的GPU

验证容器中的nvidia GPU

  • 使用名为 nvidia-smi 的Docker镜像来验证,这个镜像允许NVidia工具监控和管理GPU:

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

完成后会提示信息显示GPU信息:

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.40.04    Driver Version: 418.40.04    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 GT 750M     Off  | 00000000:01:00.0 N/A |                  N/A |
| N/A   64C    P0    N/A /  N/A |      1MiB /  1999MiB |     N/A      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0                    Not Supported                                       |
+-----------------------------------------------------------------------------+

注解

从NVIDIA下载的官方镜像 nvidia/cuda 提供了完整的CUDA支持,并且具备了类似 nvidia-smi 这样的监控工具。后续可以使用这个镜像来启动创建容器,并安装TensorFlow(由于硬件限制需要自己编译TensorFlow)。

对比GPU和CPU运行TensorFlow案例

既然已经完成了 nvidia-docker 的部署,我们可以通过运行TensorFlow的案例来比较使用GPU加速和使用CPU的运算效率。这段benchmark脚本是 learningtensorflow.com 提供的,请将以下这段脚本保存为 benchmark.py

import sys
import numpy as np
import tensorflow as tf
from datetime import datetime

device_name = sys.argv[1]  # Choose device from cmd line. Options: gpu or cpu
shape = (int(sys.argv[2]), int(sys.argv[2]))
if device_name == "gpu":
    device_name = "/gpu:0"
else:
    device_name = "/cpu:0"

with tf.device(device_name):
    random_matrix = tf.random_uniform(shape=shape, minval=0, maxval=1)
    dot_operation = tf.matmul(random_matrix, tf.transpose(random_matrix))
    sum_operation = tf.reduce_sum(dot_operation)

startTime = datetime.now()
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as session:
        result = session.run(sum_operation)
        print(result)

# It can be hard to see the results on the terminal with lots of output -- add some newlines to improve readability.
print("\n" * 5)
print("Shape:", shape, "Device:", device_name)
print("Time taken:", str(datetime.now() - startTime))

以上脚本有2个参数, cpugpu 以及矩阵大小。以下容器命令采用了 tensorflow/tensorflow:latest-gpu 的Docker镜像:

docker run \
 --runtime=nvidia \
 --rm \
 -ti \
 -v "${PWD}:/app" \
 tensorflow/tensorflow:latest-gpu \
 python /app/benchmark.py cpu 10000

然后,我们再将上述docker命令的 cpu 参数替换成 gpu 对比。

注解

在Docker中运行Tensorflow参考 TensorFlow 官方Docker文档

警告

TensorFlow发行版本对CUDA要求3.5,即对GPU硬件有要求,当前MacBook Pro 2015 later使用的NVIDIA GeForce GT 750M仅支持CUDA 3.0。所以上述测试会出现报错,解决方法是 源代码编译安装TensorFlow

升级Nvidia驱动

注解

这个问题我还没有实践,待续