CUDA加速: Incus 与 Podman 的实现方案

Incus 和 Podman 作为容器化方案,都支持挂载显卡让容器内应用能够使用 CUDA 进行加速。不过两者具体的操作方式有所不同,交换的东西也不一样,需要根据实际情况进行取舍。

Incus

Incus 的操作方法在docker in incus with cuda一文有所介绍,不过在这里再总结一下。

incus config edit <容器名>config:device:中,需要有以下配置来利用 nvidia-container-toolkit 自动挂载驱动和库、挂载 pci bus 到容器:

1
2
3
4
5
6
7
8
9
10
config:
# 其他配置
nvidia.driver.capabilities: compute,utility,video
nvidia.runtime: "true"
# 其他配置
devices:
# 其他 device
nvidia0:
pci: "0000:01:00.0"
type: gpu

其中 pci: 的值用 nvidia-smi 来找,例子参见挂载 GPU PCI Bus 目录到容器。同时需要在容器内用一个 systemd service unit 来把 PCI Bus 链接一份到 /proc/driver/nvidia,前面这个链接中有详细介绍。

上述部署完成之后,地基就已经打好了。根据 distribution 的不同可能还需要在容器内安装运行时的库才能让应用成功跑起来,但是这里就不具体介绍了。

不过在我的服务器上,开机时这个容器是启动不起来的,必须要在 host 跑一次 nvidia-smi 才能启动。

Podman (Quadlet)

相比起 Incus 配置上的复杂,podman 和 quadlet 就简单许多了。podman run 只需要加一个参数: --gpus all。Quadlet则是在[Container]中加一行AddDevice=nvidia.com/gpu=all就行了。以 Jellyfin 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Description=Jellyfin Container
After=network-online.target

[Container]
Image=docker.io/jellyfin/jellyfin:latest
ContainerName=jellyfin
PublishPort=8096:8096/tcp
PublishPort=7359:7359/udp
AddDevice=nvidia.com/gpu=all

[Service]
Restart=always

[Install]
WantedBy=default.target

取舍

Podman 虽然方便,但它是 Application Container,容器的环境是以镜像为基础、半永久的——每次 pull 新 image 之后,之前对容器环境的变更都会丢失。出于个人习惯,在跑大型应用比如 gitlab 的时候,我就不太喜欢用 Application Container 因为定制起来太麻烦了。

Incus 作为 System Container 则是另一个极端,对环境的定制非常方便,但是配置环境就需要花费很多精力,对于某些应用则会有“水土不服”的问题——比如 Jellyfin 就经常资料库卡住。