容器使用 CUDA
首先是 nvidia-container-toolkit 需要安装。然后 incus launch 时需要加上一些 flag。 常规的包括:
-c security.nesting=true
-c security.syscalls.intercept.mknod=true
-c security.syscalls.intercept.setxattr=true
这几个都是在 incus 里面运行 docker 所需要的,常规操作没有太多变化。而为了 CUDA,还需要以下flag:
-c nvidia.runtime=true
,这样就会自动挂载一些 binary 和 library 进去,从而不需要在里面浪费空间再装一次。但是这里有坑,详见 capabilities 配置-c nvidia.driver.capabilities=compute,utility,video
,特别是最后的 video。默认情况下只会设置 compute 和 utility,导致容器内找不到 encoder 的库,用不了硬件加速 codec。
为了最大兼容性,毕竟 ubuntu 等系统的驱动版本可能不一致,导致其他应用不兼容,所以容器的系统我用了跟 host 一样的 archlinux,所以完整的 launch 是:
1 | incus launch images:archlinux/cloud/amd64 dockerd \ |
除了这些flag,还需要把 gpu 添加给容器[1]。使用 physical type 也没有关系,不会导致 container 独占GPU的:
incus config device add dockerd gpu gpu
此时在容器内安装 ffmpeg 并用以下命令测试[2],应该不会提示任何错误的:
1 | ffmpeg -loglevel error -f lavfi -i color=black:s=1080x1080 -vframes 1 -an -c:v hevc_nvenc -f null - |
docker 配置
容器内依然需要安装 nvidia-container-toolkit
,而且需要修改配置禁用 cgroup,否则会报错:
nvidia-container-cli: mount error: failed to add device rules: unable to find any existing device filters attached to the cgroup: bpf_prog_query(BPF_CGROUP_DEVICE) failed: operation not permitted
修改 /etc/nvidia-container-runtime/config.toml 文件,忽略其他内容,把 no-cgroups 设置为 true 即可:
1 | [nvidia-container-cli] |
但是此时用docker启动cuda测试的时候会报错:
nvidia-container-cli: mount error: stat failed: /proc/driver/nvidia/gpus/0000:41:00.0: no such file or directory
这是因为没有把 PCI Bus 挂载到容器里。解决方法就是手动挂载过去,用一个script来方便 host 启动时自动挂载再启动容器,避免 docker 有 restart=always 的容器时无法启动。
挂载 GPU PCI Bus 目录到容器
如果在容器启动前就挂载,启动之后由于host的 nvidia-container-toolkit 会挂载 /proc/driver/nvidia,导致 gpus 目录被隐藏掉。所以需要在容器启动后, docker.service 启动前再挂载。这就是以下这个脚本的逻辑:
1 |
|
出现下面这个错误的主要原因是 container 还没完全启动,导致内部的 systemd 还没初始化完成,所以会抱怨找不到 bus。
Failed to connect to system scope bus via local transport: No such file or directory
只需要在 script 里面加上 sleep 3
再 incus exec
就可以了。
至于 systemd service 我就不写了。
测试
用 nvidia 的 cuda 容器测试一下:
1 | docker run --rm --gpus all nvidia/cuda:12.8.1-cudnn-runtime-ubuntu24.04 nvidia-smi |
能够正确输出 GPU 信息,就成功了。
其他有用的信息
Capabilities
参考nvidia的文档:driver capabilities。简单来说:
compute
: CUDAutility
: 使用nvidia-smi
需要这个video
: 视频编解码
其他的通常无关紧要。为什么需要手动指定这三个而不 all
,因为 all
会直接让 incus start
报错,启动失败。
快捷控制脚本
把这个放到 /usr/local/bin/dockerd-ctl
,就可以方便地启动、停止 docker 容器了。
1 |
|