安装NVIDIA Container Toolkit,让docker容器也能用上GPU

最近在服务器上折腾 GPU 容器,想把需要显卡的服务也统一放进 docker 里跑,省得在宿主机上装一堆环境。

但是我们都知道,docker 容器默认是看不到宿主机显卡的,宿主机上 nvidia-smi 再正常,容器里也用不了。所以这里我们就需要用到 NVIDIA Container Toolkit 了。

什么是 NVIDIA Container Toolkit

NVIDIA Container Toolkit 是 NVIDIA 官方提供的一套工具,让用户可以构建和运行使用 GPU 加速的容器。

简单的说,它就是宿主机上的 GPU 和容器之间的一座桥。

理解它之前,先搞清楚三方各自负责什么:

  1. GPU 驱动只装在宿主机上,容器镜像里不需要、也不应该打包驱动;
  2. NVIDIA Container Toolkit 也装在宿主机上,容器启动时由它把 GPU 设备节点和驱动库挂载进容器;
  3. docker 这类容器运行时只需要配置一次,让它在启动容器时调用 NVIDIA 的运行时钩子。

所以整个过程其实就两件事:在宿主机上装好工具包,再告诉 docker 用它。

注意

  1. 宿主机必须已经装好 NVIDIA 显卡驱动,官方推荐用发行版自带的包管理器安装。执行 nvidia-smi 能正常输出显卡信息再往下走;
  2. 宿主机已经装好 docker,还没装的可以参考我之前写的使用docker和安装docker-compose发现容器编排之美;
  3. 本文需要 root 权限的指令前都加上了 sudo,如果你是 root 用户,可以省去 sudo。

一、安装 NVIDIA Container Toolkit

此处是 Ubuntu/Debian 系统(apt)的安装教程。RHEL/CentOS/Fedora/Amazon Linux(dnf)和 openSUSE/SLE(zypper)用的是同一个软件源,步骤大同小异,请查看官方文档并选择你对应的系统。

1. 安装依赖

sudo apt-get update && sudo apt-get install -y --no-install-recommends \
   ca-certificates \
   curl \
   gnupg2

这个命令更新了 apt 包索引,并安装了后面添加软件源要用到的证书、curl 和 gpg 工具。

2. 配置 NVIDIA 的软件源

工具包不在系统默认的源里,需要先把 NVIDIA 的仓库加进来

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
  && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
    sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

这一长串看着吓人,其实只做了两件事:

  1. 下载 NVIDIA 仓库的 GPG 公钥,转换格式后保存到 /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg;
  2. 下载稳定版(stable)的源列表,用 sed 给每一行加上 signed-by 指向刚才的公钥,最后写入 /etc/apt/sources.list.d/nvidia-container-toolkit.list。

源列表里还有实验性的包,默认是注释掉的。如果你确实需要,可以执行下面的命令把它打开,一般用不着:

sudo sed -i -e '/experimental/ s/^#//g' /etc/apt/sources.list.d/nvidia-container-toolkit.list

3. 锁定版本安装

sudo apt-get update

export NVIDIA_CONTAINER_TOOLKIT_VERSION=1.19.1-1
sudo apt-get install -y \
    nvidia-container-toolkit=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
    nvidia-container-toolkit-base=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
    libnvidia-container-tools=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
    libnvidia-container1=${NVIDIA_CONTAINER_TOOLKIT_VERSION}

可以看到,这里先用一个环境变量定下版本号,再把四个包按同一个版本装上。这四个包要保持同版本,所以显式锁定版本号比直接装最新版更稳妥,以后升级也心里有数。

tip:本文写作时版本为 1.19.1-1,请到官方安装指南确认最新版本,替换 NVIDIA_CONTAINER_TOOLKIT_VERSION 的值即可。

安装完成后,宿主机上就多了一个 nvidia-ctk 命令,后面的配置全靠它。

二、配置 Docker

工具包装好了,docker 还不知道它的存在。不用自己手改 docker 的配置文件,nvidia-ctk 一条命令就能搞定。

1. 标准模式

sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

以上两条命令的意义依次分别是:

  1. 修改 docker 的配置文件 /etc/docker/daemon.json,把 NVIDIA 的运行时注册进去;
  2. 重启 docker,让配置生效。

2. rootless 模式

如果你的 docker 跑在 rootless 模式下,配置文件在用户目录里,命令要换成下面这样:

nvidia-ctk runtime configure --runtime=docker --config=$HOME/.config/docker/daemon.json
systemctl --user restart docker
sudo nvidia-ctk config --set nvidia-container-cli.no-cgroups --in-place

以上三条命令的意义依次分别是:

  1. 把 NVIDIA 运行时写入当前用户的 docker 配置 $HOME/.config/docker/daemon.json,注意这里不用 sudo;
  2. 以用户身份重启 docker;
  3. 修改工具包自身的配置,关闭 cgroups 限制。

注意: 第三步不能漏。rootless 模式下 docker 没有 root 权限去管理 cgroups,所以必须打开 nvidia-container-cli.no-cgroups,让工具包跳过 cgroups 相关的操作。

三、验证

docker run --rm --gpus all ubuntu nvidia-smi

这个命令用一个干净的 ubuntu 镜像启动了一个临时容器,--gpus all 表示把宿主机所有的 GPU 都交给这个容器,--rm 表示退出后自动删除容器。

ubuntu 镜像里本身既没有驱动,也没有 nvidia-smi,这些都是工具包在容器启动时挂进去的,这也正好印证了前面说的"驱动只装在宿主机"。如果输出了和宿主机上一样的显卡信息,说明容器已经能用上 GPU 了。

大功告成。

四、一个已知的坑

注意: 如果 docker 使用的是 systemd cgroup 驱动,在宿主机上执行 systemctl daemon-reload 可能会导致正在运行的容器丢失 GPU 访问权限。

这个坑的麻烦之处在于,daemon-reload 是很常见的运维操作,改个 systemd 服务文件就要执行一次,很容易在不知不觉中触发。所以在跑着 GPU 容器的机器上做系统维护时要留个心眼,执行完之后检查一下 GPU 容器,必要时重启受影响的容器。

延申:其他容器运行时

nvidia-ctk 不只能配 docker,换成别的运行时也是同一个套路:

# containerd(比如 Kubernetes 节点)
sudo nvidia-ctk runtime configure --runtime=containerd
sudo systemctl restart containerd

# CRI-O
sudo nvidia-ctk runtime configure --runtime=crio
sudo systemctl restart crio

有两个例外:用 nerdctl 操作 containerd 时不需要任何配置,直接 nerdctl run --gpus=all 就行;Podman 则不走这套运行时配置,官方推荐用 CDI(Container Device Interface)的方式暴露 GPU 设备。

相关资料

官方安装指南:https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html

GitHub:https://github.com/NVIDIA/nvidia-container-toolkit