Yukitoha 站站站

v0.13 ... 能跑就行了吧?

用 systemd-nspawn 启动一个当前系统的副本容器

Linux 有点太灵活了。

目标

在当前系统下启动一个软件包和配置与当前系统一致的容器。 同时容器内写入不影响主机文件,并且可不写入磁盘。

方案一:手动 overlay

相对更灵活一点,也能明确知道容器目录在哪里。 可以在启动前自己手动清理不需要的服务与文件等。

#!/bin/bash
MIRROR_PARENT=/run/user/$UID/
MIRROR_NAME=mirrorbox
MIRROR_PATH="$MIRROR_PARENT"/"$MIRROR_NAME"
cd "$MIRROR_PARENT"
if [ -d "$MIRROR_NAME" ]; then
    echo "mirrorbox 存在。"
else
    echo "mirrorbox 不存在。创建之。"
    mkdir -p "$MIRROR_NAME"
    sudo mount -t tmpfs tmpfs -o size=12G  "$MIRROR_NAME"
fi
cd "$MIRROR_PATH"
chmod 755 .

sudo mkdir {etc,usr,var,opt}{,wk}

sudo mount -t overlay overlay -o upperdir=usr,lowerdir=/usr,workdir=usrwk usr
sudo mount -t overlay overlay -o upperdir=etc,lowerdir=/etc,workdir=etcwk etc
sudo mount -t overlay overlay -o upperdir=var,lowerdir=/var,workdir=varwk var
sudo mount -t overlay overlay -o upperdir=opt,lowerdir=/opt,workdir=optwk opt
sudo mkdir home{,wk}
sudo mount -t overlay overlay -o upperdir=home,lowerdir=/home,workdir=homewk home

# 清理可能敏感的文件
rm -rf  "$PWD/home/$USER/.ssh"
# 清理不需要的服务
sudo rm -f $PWD/etc/systemd/system/multi-user.target.wants/sshd.*
sudo rm -f $PWD/etc/systemd/system/multi-user.target.wants/Network*
sudo rm -f $PWD/etc/systemd/system/multi-user.target.wants/tlp*

# 取消注释可以让一些工具开心
#sudo chown root:root .
sudo systemd-nspawn -b -D . --bind=/dev/dri/:/dev/dri/ --property=DeviceAllow='char-drm rw' \
    --bind=/run/user/$UID/$WAYLAND_DISPLAY:/run/host/wayland \
    --bind-ro=/run/user/$UID/pulse:/run/host/pulse \
    $@

sudo umount usr etc var opt
sudo umount home
cd ..
read -p "要清理掉 mirrorbox 的所有文件吗 (y/N) " answer
if [ "$answer" = "y" ] || [ "$answer" = "Y" ]; then
    echo "将移除 mirrorbox 的 tmpfs 并移除挂载点"
    sudo umount "$MIRROR_PATH"
    sudo rmdir "$MIRROR_PATH"
    echo "清理完成"
else
    echo "不进行清理。"
fi

方案二:完全交给 systemd-nspawn

首先准备一个目录用于存放容器内写入。 若不打算保留这些写入则找一个 tmpfs 的目录。

例如:在当前目录下的 tmppath 文件夹处挂载一个 12G 的 tmpfs

sudo mount -t tmpfs tmpfs -o size=12G tmppath

随后在 tmppath 内建一个新的文件夹作为 upperdir

mkdir tmppath/u1

启动容器:

systemd-nspawn -bD / --volatile=yes --overlay=/etc/:tmppath/u1:/etc

这里也可以像方案一中那样 bind 一些设备与某些 socket 用于共享图形与音频等。 对于 /var /opt /home 也用类似的方案 --overlay 一下就可以了。

实话讲全都用参数指定感觉也没比方案一容易多少。

关于为什么要在 tmppath 下再新建文件夹, 是因为 systemd-nspawn 会自动把 overlayfs 中需要使用的 workdir 直接指定为提供的 upperdir 的上一级目录中的一个随机命名的新目录。 而在这里,tmppath 的上级目录显然与 tmppath 内不在同一文件系统,这并不符合 overlayfs 的要求,于是会报错。

而关于为什么要指定 overlay,则是因为不指定时只有 /usr 被自动 overlay,启动时会执行 systemd-firstboot,最后得到的是一个 /etc 干净到连 pacman 都用不了的系统。而 /var 也需要 overlay 进来,因为 pacman 数据库在这里。

要是用 --bind 等参数,那写入就会被写入到宿主系统了,这显然不是我们想要的了。

Written on November 16, 2025