在有一段时间里我因为不同发行版环境问题而头疼,无论是开发、还是日常使用。编译时遇到 glibc、各种各样库版本不同等问题,那个发行版有二进制包呀这个发行版没有的。
尝试过 chroot (觉得不太得劲),又去研究 bwrap (据说 flatpak 也在用的方案) 跑了 debian 和 Arch 容器, https://blog.glumi.cn/bwrap-debian ,https://blog.glumi.cn/bwrap-arch 。但因为工具又太过于轻量,用起来比较麻烦。最近又尝试了另一种陌生的方案,也就是 systemd-nspawn
那这又是个什么东西呢,可以简单理解为同时兼顾 chroot 的易用性又有 bwrap 的灵活性且与 systemd 深度集成的容器工具。
它既可以作为轻量级的隔离环境快速启动,满足临时测试需求;又能支撑起完整的系统容器,跑起一整套带服务的发行版环境,可以在上面运行 GUI 程序,且带硬件加速,可共享宿主机接口资源,几乎没有什么性能损失。
在开始前,以 Debian 系为例。需要安装 systemd-container 包获得相关工具。
sudo apt install systemd-container
接下来准备一个 rootfs ,(Arch 的话可以使用 pacstrap,这里我就不展开了) 这里以 Debian 为示例,可以使用 debootstrap
来下载一个 debian rootfs
sudo debootstrap --arch amd64 sid /path/to/debian/ https://deb.debian.org/debian
其中 sid 为 unstable,也可以换成 bookworm 等版本代号。amd64 就是相当于 x86_64,可以是其他架构。
systemd-nspawn 可以像 chroot 一样简单
其中 -D 代表 --directory 参数简写。
sudo systemd-nspawn -D rootfs
它默认会做一些基础的配置,能够直接使用宿主网络,且以 root 用户为起始。
如果你需要从普通用户启动,你需要提前在容器内做好 useradd 相关操作。
sudo systemd-nspawn -D rootfs --user=用户名或UID
容器内使用 sudo 提权也是没问题的,也仅限于容器内的提权,不会涉及到外部权限。
回到本文标题,让我们跑 GUi 之前。我们可以再挂载以下内容。
参数较多,接下来为了排版直观,我编写一个 run.sh
文件为示例。
#!/bin/bash
container_path=./debian_bookworm
XAUTH=/tmp/container_xauth
DISPLAY=":1"
uid=1000
sudo systemd-nspawn \
--directory="$container_path" \
-E XAUTHORITY="$XAUTH" \
-E DISPLAY="$DISPLAY" \
-E XDG_RUNTIME_DIR="/run/user/$uid" \
-E LANG="zh_CN.UTF-8" \
-E PULSE_SERVER="unix:/run/user/$uid/pulse/native" \
--hostname="Debian12" \
--property=AppArmorProfile=unconfined \
--bind="$XAUTH" \
--bind="/dev/dri" \
--bind="/tmp/.X11-unix" \
--bind="/dev/input" \
--bind="/run/user/$uid/pulse" \
--bind="/run/user/$uid/bus" \
--user="$uid" \
--bind-ro="/etc/resolv.conf"
这里面涉及到很多内容,其中 --bind
为挂载的意思,为了跑起 GUi,我们需要告诉容器需要用到哪个显示器(DISPLAY) 使用 -E DISPLAY="$DISPLAY"
其中 -E
就是传递变量的意思,根据个人情况来写,我这里是 :1
然后挂载 --bind="/tmp/.X11-unix"
让容器与宿主 X11 通信,而一般情况下 x 有个机制默认不允许非本机与 x 进行通信,而容器其实就相当于另一个主机。为了让它们建立连接,需要使用 xhost 去开放权限如:xhost +local:
其中 local 为本地主机的意思 + 为添加白名单 - 为移除。但是一般不推荐这么做(可能导致 X 服务器被未授权访问)。也可以使用了另一种方法。
首先输入:
touch /tmp/container_xauth | xauth nextract - ":1" | xauth -f /tmp/container_xauth nmerge -
再增加一个 XAUTHORITY
变量指向 /tmp/container_xauth
即可
--hostname="Debian12"
:的意思很明了,就是给容器设置一个主机名
--bind="/dev/dri"
:将宿主 dri 挂载到容器以实现 GPU 加速
--property=AppArmorProfile=unconfined
如果GPU加速不正常,可能需要
如果需要在容器用到 systemd 需要加上 --boot
,可以模拟完整系统的启动。
需要退出时,可以先按住 ctrl
,再按三次 ]
键退出容器。
如果你需要音频,那必须得加入以下参数和 PulseAudio 进行通信:
--bind="/run/user/$uid/pulse"
-E PULSE_SERVER="unix:/run/user/$uid/pulse/native"
无法在容器使用输入法问题,可以加入以下变量解决:
-E XMODIFIERS="@im=fcitx" \
-E QT_IM_MODULE="fcitx" \
-E GTK_IM_MODULE="fcitx" \
当你的网络不太正常,也许要手动挂载一下 /etc/resolv.conf , --bind-ro 是只读的
--bind-ro="/etc/resolv.conf"
准备完后,使用 chmod +x run.sh
给以执行权限,就可以跑了。以下为测试效果。
需要各种发行版?也没问题。
GUI 程序视频解码占用还不错(测试硬件 Ai9 365 )
有关 docker、bwrap、nspawn 的 Linux 容器图形性能测试:
https://blog.glumi.cn/docker-bwrap-nspawn-gpu-test
关于更详细教程推荐阅读: