Systemd-nspawn 桌面化
System-nspawn 简单的配置和使用可以参考 Systemd-nspawn 配置与使用。本文以名为 debian-11 的 debian 系容器,在名为 gentoo 的 gentoo 系主机下操作。
基础配置
主机配置
在 /var/lib/machines
中的容器,可以由 systemd 直接管理到,每个容器可以有自己的配置:
cp /lib/systemd/system/[email protected] /etc/systemd/system/[email protected]
因为 nspawn 的对网络的支持比较有限,所以直接使用主机网络,将 --network-veth
选项去掉。
因为 nspawn 对 subuid/subgid 的支持相对有限,所以直接使用主机的 uid/gid,讲 -U
选项去掉。
重载 systemd 的配置,启动容器并开机随主机启动容器:
systemctl daemon-reload
systemctl enable --now systemd-nspawn@debian-11
用户配置
容器的 uid 要和主机的 uid 保持一致,才不会出现各种 video 组的相关权限问题,当然,容器里的 video gid 和主机的也要保持一致。
machinectl shell debian-11
useradd -m -u $user_uid $user_name
sed -ri 's/^(video|audio)/# \1/' /etc/group
echo "video:x:$video_gid:" >> /etc/group
echo "audio:x:$audio_gid:" >> /etc/group
gpasswd -a $user_name video
gpasswd -a $user_name audio
exit
记得要检查容器的 /etc/group
,有没有 id 冲突,debian 的 sudo gid 和 gentoo 的 video gid 是冲突的。
系统配置
容器里的系统配置,可以参考 Linux 基础配置。
主题样式(可选)
主题样式如果要和主机保持一致,需要在容器也安装一致的主题和设置,如 debian 容器的 qt 主题设置为 breeze theme:
machinectl shell debian-11
apt install -y breeze breeze-gtk-theme
su - $user_name
echo 'export QT_STYLE_OVERRIDE=Breeze' >> ~/.profile
exit
中文(可选)
容器需要配置 zh_CN 的 locale.gen 和 dbus,并安装中文字体:
machinectl shell debian-11
apt install -y locales \
fonts-noto-cjk
exit
输入法是使用主机的,像主机使用 fcitx,程序是需要有 fctix 的 gtk 和 qt 的 so 库支持,所以需要在容器安装并设置环境变量:
machinectl shell debian-11
apt install -y fcitx-frontend-qt5 fcitx-frontend-gtk2 fcitx-frontend-gtk3 \
fcitx5-frontend-qt5 fcitx5-frontend-gtk2 fcitx5-frontend-gtk3
su - $user_name
cat << EOF >> ~/.profile
export LC_CTYPE=zh_CN.utf8
export XMODIFIERS="@im=fcitx"
export XIM=fcitx
export XIM_PROGRAM=fcitx
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
EOF
exit
能随主机启动配置
容器跟随主机开机时自动启动(systemctl enable systemd-nspawn@xxx
)可以预先配置的选项,如果出现了“不能随机启动的配置选项”,就会报错,无法启动容器。
GPU
需要的通用 GPU 配置,需要在 /etc/systemd/system/[email protected]
中的 ExecStart
追加:
--bind=/dev/dri \
--bind=/dev/shm \
Nvidia
Nvidia 的显卡需要驱动和一些辅助工具才能工作,可以在容器安装和主机版本一致的驱动,也可以将主机机的显卡驱动和辅助工具都给容器,但是需要每次主机更新驱动后,配置文件都需要重新修改下 --bind
。
首先是辅助工具的文件:
--bind=/usr/bin/nvidia-bug-report.sh:/usr/bin/nvidia-bug-report.sh \
--bind=/opt/bin/nvidia-cuda-mps-control:/usr/bin/nvidia-cuda-mps-control \
--bind=/opt/bin/nvidia-cuda-mps-server:/usr/bin/nvidia-cuda-mps-server \
--bind=/opt/bin/nvidia-debugdump:/usr/bin/nvidia-debugdump \
--bind=/usr/bin/nvidia-modprobe:/usr/bin/nvidia-modprobe \
--bind=/opt/bin/nvidia-ngx-updater:/usr/bin/nvidia-ngx-updater \
--bind=/opt/bin/nvidia-powerd:/usr/bin/nvidia-powerd \
--bind=/usr/bin/nvidia-sleep.sh:/usr/bin/nvidia-sleep.sh \
--bind=/opt/bin/nvidia-smi:/usr/bin/nvidia-smi \
--bind=/usr/bin/nvidia-xconfig:/usr/bin/nvidia-xconfig \
然后是配置文件:
--bind=/etc/modprobe.d/nvidia.conf:/usr/lib/x86_64-linux-gnu/modprobe.d/nvidia-utils.conf \
--bind=/usr/share/X11/xorg.conf.d/nvidia-drm-outputclass.conf:/usr/share/X11/xorg.conf.d/10-nvidia-drm-outputclass.conf \
--bind=/usr/share/egl/egl_external_platform.d/15_nvidia_gbm.json:/usr/share/egl/egl_external_platform.d/15_nvidia_gbm.json \
--bind=/usr/share/glvnd/egl_vendor.d/10_nvidia.json:/usr/share/glvnd/egl_vendor.d/10_nvidia.json \
--bind=/usr/share/vulkan/icd.d/nvidia_icd.json:/usr/share/vulkan/icd.d/nvidia_icd.json \
--bind=/usr/share/vulkan/implicit_layer.d/nvidia_layers.json:/usr/share/vulkan/implicit_layer.d/nvidia_layers.json \
最后是 so 文件,但是这些跟随版本变动大,所以可以在主机执行命令生成,并将生成的文本加入到 ExecStart
中。
ldconfig -p | grep -Ei '(nvidia|libcuda)' | awk -F '=> ' '{print $2}' | sed -E 's|(/usr/lib64/(.*))|bind=\1:/usr/lib/x86_64-linux-gnu/\2 \\|g'
首次映射需要在容器执行 ldconfig
:
machinectl shell -q debian-11 /sbin/ldconfig
Xorg
要使用主机的 Xorg 服务,首先是需要将 Xorg 服务的 socket 给到容器:
--bind-ro=/tmp/.X11-unix \
然后设置环境变量:
machinect shell $user_name@debian-11
echo "export DISPLAY=$HOST_DISPLAY" >> ~/.profile
exit
最后如果 Xorg 服务是带又权限的,主机可以执行 xhost +local:
。
用户共享(可选)
临时的共享可以通过 machinectl bind
来,具体查找是否挂载可以在容器中执行 findmnt
。永久的挂载共享建议追加:
--bind=/home/$user_name/Downloads/Work:/Work \
不能随主机启动配置
容器不能跟随主机开机时自动启动可以预先配置的选项。
Dbus(可选)
Systemd 默认 dbus 的 socket 是 /run/user/$user_uid/bus
,是用户阶段生成的,并非系统阶段。需要用户的 dbus 生成后,才能去手动映射到容器,而且不能映射到容器 /run
下,因为容该目录是变动的,每次重启重启后都需要执行一次:
machinectl bind--mkdir debian-11 /run/user/$user_uid/bus /home/chongwish/.bus
之后需要告诉容器 dbus 的位置:
machinect shell $user_name@debian-11
echo 'export DBUS_SESSION_BUS_ADDRESS=unix:path=$HOME/.bus' >> ~/.profile
exit
如果容器的应用需要用到 dbus 的全局菜单,需要:
machinectl shell debian-11
apt install -y libdbusmenu-gtk4 libdbusmenu-gtk3-4 libdbusmenu-qt5-2 \
vala-panel-appmenu
exit
PulseAudio(可选)
PulseAudio 默认的 socket 是在 /run/user/$user_uid/pulse/native
,同 dbus 一样映射:
machinectl bind debian-11 /run/user/$user_uid/pulse/native /home/$user_name/.pulse-native
然后写入到环境变量:
machinectl shell $user_name@debian-11
echo "export PULSE_SERVER=\$HOME/.pulse-native" >> ~/.profile
exit
如果声音有独占问题,可以在执行:
machinectl shell debian-11
sed -i "s/; enable-shm = yes/enable-shm = no/g" /etc/pulse/client.conf
exit
GPU
Nvidia
Nvidia 的驱动设备在首次使用后生成,在此之后容器才能挂载使用:
machinectl bind debian-11 /dev/nvidia0
machinectl bind debian-11 /dev/nvidiactl
machinectl bind debian-11 /dev/nvidia-modeset