近日购入一张技嘉Nvidia RTX 4060 Ti (16GB)用作机器学习用途,小记一波三折的安装过程。
硬件安装
这步骤无非是将显卡插入主板的PCIe插槽,锁紧螺丝以固定,再连接8 pin的辅助供电线缆。此步骤并无更多须关注之细节。
Host OS
我的服务器运转Arch Linux操作系统。按照ArchWiki1所述,(nvidia-dkms
|| nvidia-open-dkms
) && nvidia-utils
需要被安装。由于我的服务器运转linux-hardened
内核,故需要由dkms
编译的包。安装dkms
时,成吨的用于编译的包,如gcc
,被作为依赖安装了。但我又无可奈何——nvidia-open
会不会并入mainline呢?如果能,又在几时呢?无人知晓…
在此之后我犯了一个错误,造成了这台服务器自装机以来第二次Kernel Panic——我在安装后直接运转nvidia-smi
,系统没了反应,登录IPMI的remote KVM发现是KP了。没有日志被留下,猜测可能是因为nouvaeu被加载并与nvidia的内核模块产生某些冲突造成的。
此外,mkinitcpio.conf
也需要被修改,kms
须被从HOOKS
中移除,以避免nouvaeu被加载。
# /etc/mkinitcpio.conf.d/00-override.conf
MODULES=(ext4 vfat)
#HOOKS=(base udev keyboard keymap autodetect microcode modconf kms consolefont block filesystems fsck lvm2 encrypt)
# Nvidia: remove kms to avoid nouvaeu from loading.
HOOKS=(base udev keyboard keymap autodetect microcode modconf consolefont block filesystems fsck lvm2 encrypt)
重启过后,运转nvidia-smi
,应该已经可以查看到GPU相关信息。
Docker
按照ArchWiki2所述,nvidia-container-toolkit
需要被安装。在安装后重新启动Docker (systemctl restart docker.service
),通过docker run --gpus all
或者Nvidia container runtime便可令GPU在Docker容器中使用。
然而事情并不如此简单,在linux-hardened
内核下,直接按前者(--gpus all
)操作会造成如下结果:
docker run --gpus all --rm -it nvidia/cuda:12.1.1-runtime-ubuntu22.04 nvidia-smi
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error running hook #0: error running hook: exit status 1, stdout: , stderr: Auto-detected mode as 'legacy'
nvidia-container-cli: mount error: failed to add device rules: unable to generate new device filter program from existing programs: unable to create new device filters program: load program: invalid argument: last insn is not an exit or jmp
processed 0 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0: unknown.
检索相关关键词,可知3net.core.bpf_jit_harden
这一内核参数如被设置为2
(linux-hardened
的默认值)便会造成这个结果,需设置为1
或0
(一般内核的默认值)方能解决。这样做可能降低系统的安全性。
# /etc/sysctl.d/fuck-nvidia.conf
net.core.bpf_jit_harden = 1
至此,Docker容器中的程序可以使用Nvidia GPU了。
JupyterHub
我在服务器上部署的JupyterHub采用DockerSpawner
,使每个用户的Jupyter Server运转于Docker容器中。JupyterHub的配置文件需要被修改,以使其创建的容器使用Nvidia GPU。
不幸地,网络上现存的资料绝大多数(如果不是全部)皆是基于旧的Nvidia container runtime实现的。而基于(被推荐的)--gpus all
的解决方案我未能搜索到。
阅读DockerSpawner
的(部分)源码4可知,其通过Docker的Python SDK(import docker
)连接宿主机映射至JupyterHub容器内的Docker Socket(-v /var/run/docker.sock:/var/run/docker.sock
)来为用户创建容器。其使用了create_container()
及create_host_config()
等API。
继续查阅Docker Python SDK的文档5和一些StackOverflow回答6可知,在JupyterHub的配置文件中添加如下内容,便可令其创建的容器使用Nvidia GPU:
import docker
c.DockerSpawner.extra_host_config = {"device_requests": [docker.types.DeviceRequest(
device_ids=["all"],
capabilities=[["gpu"]],
)]}
至此,终于可以愉快(真的吗?)地在服务器上运转的Jupyter Notebook使用CUDA了。
后记
Content Warning: 这个段落与正文之技术话题无关,属发泄情绪之内容。如不想阅读,可回避之而不影响正文之完整性。
牢骚话
事实上,这是一次令人作呕的,缺乏优雅的安装。整个过程繁琐冗杂,制造诸多烂摊子,留了一地的屎!包括但不限于:
- 大量修改系统文件和配置,降低安全性
- 安装许多先前从不需要的软件包,只为在内核更新之时编译Nvidia内核模块用
- https://wiki.archlinux.org/title/NVIDIA ↩︎
- https://wiki.archlinux.org/title/Docker ↩︎
- https://github.com/bottlerocket-os/bottlerocket/issues/2504#issuecomment-1410814475 ↩︎
- https://github.com/jupyterhub/dockerspawner/blob/13.0.0/dockerspawner/dockerspawner.py ↩︎
- https://docker-py.readthedocs.io/en/stable/api.html ↩︎
- https://stackoverflow.com/questions/71429711/how-to-run-a-docker-container-with-specific-gpus-using-docker-sdk-for-python/71429712#71429712 ↩︎
我在使用GPU部署时也经历过这些,服务器上有多个GPU,各种软件版本和CUDA对应关系乱七八糟最后整崩溃了,弄一个干净的系统,安装最新的NVIDIA驱动和最新的CUDA,按照官方说明,使用docker部署,后面就比较顺利了。