纯水,因为和 emacs 无关
是什么
systemd 管理的容器
man 1 systemd-nspawn
能做什么
- 提供完全的隔离环境,和宿主机共用内核,但是具有自己隔离的进程、用户,网络,文件命名空间
- 隔离级别可定制
- 允许绑定挂载,overlay 挂载
- 性能和 docker 持平
使用场景
- 运行隔离环境,取代大多数 docker 使用场景
- 运行无头桌面
特点
- 默认就是容器内写入持久化模式,但也可修改为易失模式(重启后修改丢失)
不能做什么
安装
apt-get install systemd-container
配置
内核变量
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
防火墙
防火墙配置仅在 容器连到了一个虚拟的网桥 的情况的情况下需要定制,否则不需要
本文为了简单起见,不引入任何虚拟网桥,让容器直接使用宿主机的网络(完全访问权限)
虚拟网桥
本文为了简单起见,不引入任何虚拟网桥,让容器直接使用宿主机的网络(完全访问权限)
创建
/etc/systemd/nspawn/container-test.nspawn
持久化容器配置,container-test 替换为容器名(容器根目录名)
[Exec]
PrivateUsers=pick
Timezone=bind
# 如果启用下面一行,那么不 bind 宿主机的 /etc/resolv.conf 到容器中
# ResolvConf=off
# 如果宿主机的 /etc/resolv.conf 内是一个互联网的 dns 服务器地址,那么可以 bind 进容器
ResolvConf=bind-host
[Files]
PrivateUsersOwnership=auto
# 绑定挂载:参考下述写法
# 如果不使用 owneridmap 这类选项,那么需要事先准备好 挂载源目录 的所有者和权限。选项解释见 man 5 systemd.nspawn
# BindReadOnly=/default/etc/resolv.conf:/etc/resolv.conf
# Bind=/path/to/postgres:/var/lib/postgresql:owneridmap
[Network]
Private=no
创建容器根目录
cd /var/lib/machines
# container-test 替换为你的容器名
debootstrap --include=apt-transport-https,ca-certificates,dbus,libpam-systemd,systemd --arch=amd64 stable https://mirrors.tuna.tsinghua.edu.cn/debian/
启动
sudo machinectl start container-test
# 无密码直接取得 root shell
sudo machinectl shell container-test
友情链接
https://linux.do/t/topic/691953
这个人是懂 nspawn 的,但如果想要在网络层面隔离容器和宿主机,或许不需要像上面链接里面说的配这么复杂
用 nspawn 运行 sway 桌面
1 个赞
NixOS Containers 用的就是 systemd-nspawn
1 个赞
继续水
用于 ai code 工具隔离
优势
- 如果不希望 ai 进行任何修改,可让项目目录在文件系统级别表现为只读
- 如果用于 ai 读写,可确保永远不会干扰/破坏容器外的环境,因为文件系统命名空间是隔离的
准备步骤
下文以一个完全可读写的容器为例:
nspawn 配置文件示例
[Exec]
PrivateUsers=pick
Timezone=bind
ResolvConf=off
[Files]
PrivateUsersOwnership=auto
# Volatile=overlay
# BindReadOnly=/default/etc/resolv.conf:/etc/resolv.conf
# Overlay=+/etc/systemd/network:/default/etc/systemd/network:/overlay/etc/systemd/network:/etc/systemd/network
# Bind=/path/to/postgres:/var/lib/postgresql:owneridmap
Bind=/path/on/host/emacs-config:/home/user/mount/project/emacs-config:owneridmap
[Network]
Private=no
然后
# 宿主机
# debootstrap 创建容器文件系统目录的命令,见上文
# 创建完了,别急着用 machinectl 启动,先创建一个容器内的普通用户。假设容器根目录在 /var/lib/machines, 那么先手动启动一次
sudo systemd-nspawn --settings=no --resolv-conf=bind-host --timezone=bind -U -D /var/lib/machines/container-test
# 此时会取得 root shell
# 容器内
# 容器内新建一个用户
useradd -d /home/user -m -s /usr/bin/bash user
# 退出容器 shell, 容器自动关闭
# 宿主机
# 提前创建容器内的挂载目标目录,因为我们使用了 owneridmap 选项,用 /path/on/host/emacs-config 的所有者身份执行即可
mkdir -p /var/lib/machines/container-test/home/user/mount/project
# 在宿主机上执行下述命令,以后台方式启动容器
sudo machinectl start container-test
# 取得容器 root shell
sudo machinectl shell container-test
# 容器内
# 此时宿主机的 /path/on/host/emacs-config 被以读写方式挂载到容器内的 /home/user/mount/project/emacs-config
su - user
# 安装 code cli
npm ....
提示
- 容器内可运行 ssh 服务器,但在
Private=no 的 network 模式下,需要使用一个和宿主机 ssh 服务不重复的端口,避免冲突
- 容器内可运行 tun 模式网卡,但我仅在
Private=yes 的 network 模式下测试过
AI 工具隔离还是用工具自带的sandbox或者 nono 比较好 (claude-code sandbox bug挺多; codex 还挺好的)
这个看起来还可以,不过可能我自己更加习惯样貌上隔离出来一个看似完整的操作环境
容器内运行游戏
时代确实变了,GNU/Linux 上现在甚至可以通过 proton 运行许多的 m$win 游戏了。m$win 世袭的独占的领域,我认为也离崩塌不远了
下面是翻译(原文是用英语瞎写的)
这个解决方案是我和 Gemini 之间对话的结果。它已经过测试,似乎是一个很好的方法。
我这样做是为了隔离游戏环境并提高安全性。
游戏在容器中运行,但在主机上显示。
步骤:
创建一个 systemd-nspawn 容器
请参考互联网上的指南。我建议您运行非特权容器(启用 PrivateUsers)并启用尽可能多的安全选项。
必须:
- 将
/dev/dri 绑定到容器中,并确保容器中的用户可以访问 dri 设备(可以通过 acl 来做到这一点)
- 将另一个自定义路径绑定到容器中。我们需要从主机到容器共享 gamescope 文件,例如,在本主题中,我在主机和容器上都使用路径
/share-with-container/
提示:您可以在容器中运行无头桌面,例如:sway。
在容器中安装 proton 和游戏
你可以使用一些启动器来帮助你设置 proton 和游戏,我使用 Faugus。
运行
在主机上(请注意,我假设您已经在主机上运行桌面,其套接字为 /run/user/1000/wayland-1):
touch /share-with-container/tmp/.X{0..10}-lock
systemd-run --user --wait --pty -p "BindPaths=/share-with-container/tmp:/tmp" \
-E XDG_RUNTIME_DIR=/share-with-container/runtime \
-E WAYLAND_DISPLAY=/run/user/1000/wayland-1 \
gamescope \
--expose-wayland -W 2560 -H 1422 --borderless --force-windows-fullscreen -- sleep infinity
systemd-run --user --wait -p "BindPaths=/share-with-container/tmp:/tmp" \
-E DISPLAY=:11 \
xhost '+'
在容器中(将 … 部分替换为游戏启动命令):
systemd-run --user --wait -p "BindPaths=/share-with-container/tmp:/tmp" \
-E DISPLAY=:11 \
-E WAYLAND_DISPLAY=/share-with-container/runtime/gamescope-0 \
...
音频
我用的是 pipewire netjack2 来将容器内的声音转到宿主机播放
我测试过的游戏