在 LXC/Incus 容器中复用宿主机的 Nvidia 显卡进行视频转码 —— 以 Tesla P4 为例
0x00 前言
在容器中复用宿主机显卡有诸多好处,比如,只需要在宿主机安装驱动即可便捷传入容器,使得容器部署方便且环境干净;同时,容器部署带来了天然的显卡复用优势,使得多个实例都能够调用显卡。当然,这种方式也有一定的缺陷,会在一定程度上使宿主机环境变重,增加了 Idle 负担。结合需求使用即可。
在虚拟机尝试进行显卡直通失败后,我选择了容器部署的方式。
0x01 参考资料
- Enabling GPU passthrough post launch? --discuss.linuxcontainers.org
- NVIDIA CUDA Installation Guide for Linux --docs.nvidia.com
- Installing the NVIDIA Container Toolkit --docs.nvidia.com
0x02 驱动安装
由于 Nvidia 驱动的特殊性,可能会破坏桌面环境,需要做好备份工作与救援准备。
我的系统环境是:
- Debian GNU/Linux 12 (bookworm)
- Incus 6.0.1 (从 APT 的
bookworm-backports
源安装) - GPU: Tesla P4
如前所述,我们需要在宿主机安装显卡驱动与 CUDA 工具包。当前 (2024/08/20) 它们的最新版分别是:
- NVIDIA Driver Version: 560.28.03
- CUDA Version: 12.6
参考资料 2 中给出了驱动的下载地址:CUDA Toolkit 12.6 Downloads,下载页面中也给出了安装方式,摘录如下。其中,第 4 行的 add-apt-repository
操作依赖 software-properties-common
包,其实这一步我们自己编辑 /etc/apt/sources.list
就可以,确保其中包括 contrib
源即可。
1 | wget https://developer.download.nvidia.com/compute/cuda/12.6.0/local_installers/cuda-repo-debian12-12-6-local_12.6.0-560.28.03-1_amd64.deb |
以上仅安装了 cuda-toolkit
,我们还需要安装驱动。下载页面同样给出了安装方式,包含两个选项,分别是新的开源内核模块 nvidia-open
和旧的闭源模块 cuda-drivers
,按显卡支持情况安装其中之一。由于 Tesla P4 属于较老的 Pascal 架构,新模块尚未支持,因此这里选择后者。
1 | sudo apt install cuda-drivers |
安装程序会帮助我们禁用默认的 nouveau
驱动。安装完成后,可能需要重启才能够驱动显卡。之后,通过 nvidia-smi
查看显卡驱动状态。
另外,可以依照参考资料 2 中的推荐操作部分,尝试编译运行一些调用 CUDA 的实例,以确保 CUDA 能够正常运行。
最后,我们需要安装 nvidia-container-toolkit
。粗略地说,这个工具可以控制容器使用的显卡功能,并将必要的设备信息、工具接口传入容器中,使得我们可以在容器中不额外安装驱动便可以调用显卡,并正常使用 nvidia-smi
等工具。
参考资料 3 中给出了其下载方式与链接,但我在网络良好的情况下仍会出现 Handshake Error,可能是官方源存在一些问题,因此采用 USTC 的镜像。
1 | curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \ |
至此,所有驱动与工具均安装完毕,可以开始进行容器的部署。
0x03 容器部署
首先,我们需要获取显卡的 PCI 插槽。以 P4 为例:
1 | incus info --resources | grep -in -A5 -B5 -- 'p4' |
查询得到的插槽标识是 0000:04:00.0
,这个写法是 Incus 要求的标准格式,不接受其他写法。
接着,创建一个容器。其中,nvidia.driver.capabilities
配置项对应着 NVIDIA_DRIVER_CAPABILITIES
变量 (详见 Driver Capabilities),Incus 会将这个值透传给 nvidia-container-toolkit
,以配置我们的容器。由于我将利用该显卡进行视频转码,所以 video
值是必要的,注意 Incus 的默认设置是 utility,compute
,并不包含 video
,因此默认配置会导致我们无法进行转码。方便地写,也可以选择全都要,配置为 all
。
1 | incus create images:debian/12 jelly -c nvidia.runtime=true -c nvidia.driver.capabilities=all |
启动该容器,应当可以在容器内执行 nvidia-smi
并获取显卡驱动状态。
1 | watch -n 0.5 nvidia-smi |
通过容器内部署 Jellyfin
并进行视频转码来验证显卡可用性,不再赘述。读者可以自行验证,以 ffmpeg
为例:
1 | # Example ffmpeg command |
0x04 记录/小插曲
部署后,出现过 ffmpeg
转码失败的现象,提示 Unknown Error... caused by external library
字样。最初,在容器内安装 nvidia-container-toolkit
后问题消失了,而在容器内卸载这个包之后问题也没有再出现,新建容器也没能复现这个问题。在之后的尝试中发现,这个问题往往发生在宿主机重启之后。事实上,在长时间运行的宿主机上,这个问题并不会中途发生,原因大概率是宿主机启动时 LXC 未能成功挂载设备 (比如 /dev/nvidia_uvm
)。这可能是不完美的启动顺序导致的,但我没有深入试验。
目前来看,这个问题与容器无关,仅与 Incus daemon 有关,注意仅重启 incus.service
并不能解决问题,需要完全关闭并重新启动 Incus daemon:
1 | incus admin shutdown && incus start --all |
需要注意的是,在 Incus 尚未完全启动 (如容器正在启动) 时进行 shutdown
可能会导致 Incus 卡住,因此最好不要在刚启动宿主机时操作。
更暴力地,也可以尝试运行几次下面的脚本:
1 |
|