- 任何对容器化工具的非标准封装都是平台绑定
- 任何对容器化工具的标准封装都是在发明另一个Kubernetes
- NAS是拿来用的而不是拿来玩的。天天拆装重启是不正常的使用方法
基于NAS是拿来用的,默认此教程的读者:
- 拥有真实的存算需求
- 拥有合理的基础设施:稳定的供电、通畅的网络、合理的性能等
- 拥有合理的备用设备:足够的磁盘、321备份机制等
nspawn 是 systemd 套件中 systemd-container 组件的一个工具(程序)。用于启动一个Linux容器。
你一定遇到以下的某一个问题:
- GUI不支持某些设置必须手动改cfg,手动改过的cfg被又被GUI覆盖掉,导致不敢重启/不敢改
- 高层封装的GUI工具因为某些取舍有某些奇怪的设计,看不懂具体做什么,要查文档甚至问论坛
- 各家系统都基于自己的GUI设计了不同的逻辑,得去学习各家的设计逻辑,操作还都不一样
- 系统灾难性损坏后 docker 的 overlay 难以救援 (如果机器死了用不了docker命令)
- 系统灾难性损坏后各家NAS的文件散乱一地,不知道哪些有用
- 各家系统为了留住用户故意设计不通用的工具,并且应用逻辑都不同,完全无法跨平台无感迁移
相对于以上又笨又蠢还坏的东西 systemd-container 拥有以下绝对优势:
nspawn的所有选项都可以以参数形式传递,而且参数所见即所得。不存在 1 / 2 / 3 问题。
systemd-nspawn \
--keep-unit \
--boot \
--machine=demo \
--directory=/opt/demo/rootfs \
--network-bridge=xxxxxx- 作为systemd套件的一部分,任何正常的Linux发行版中都内置了软件包。
- 基于rootfs的容器化,不存在docker的坑爹网络、compose的套件绑定、品牌NAS的操作限制。
Docker不支持网桥,要么忍着docker的烂NAT,要么macvlan在交换机里绕圈,要么就得上K8s通过LB解决 比如TrueNAS的AppChart,以前基于k3s+Helm的还有通用性,24.10换成compose以后彻底锁死 比如品牌NAS,直接把控制台做的跟桌面一样,App呈现就是个窗口,换个平台根本用不了
故不存在 4 问题。
nspawn 不引入 overlay 层:
- 无性能开销,比如PVE的LXC默认只能使用qcow虚拟磁盘,改成rootfs需要手动改配置文件
- 不需要任何转换工具就可以迁移/备份/救援,可以只对特定文件备份,缩减备份体积和耗时、提高备份原子性
nspawn 可以使用自家 systemd 套件的功能:
- 直接使用systemd的unit功能,实现诸如后台启动、服务依赖、日志查询等功能
- 直接使用systemd-network,容器内网络配置极为容易。不需要iproute2那样写命令,也不需要逆天nmcli的daemon常驻
故不存在 5 / 6 问题。
- rootfs 是什么
- cgroup 是什么
- cgroup 的 UID/GID 映射机制
- 网桥 / 交换 / 路由 的网络知识
- systemd-run / unit / service 的基础用法
-
https://man7.org/linux/man-pages/man5/systemd.network.5.html
-
linux-container的社区镜像
https://jenkins.linuxcontainers.org -
关于nspawn的一些工具和例子
https://github.com/Jip-Hop/jailmaker
- 本教程基于 Debian 12 演示,安装了Proxmox的内核和OVS,但不安装Proxmox-VE
- nspawn支持多种网络。此处只用 --network-bridge= 网桥模式
- nspawn支持多种user-namespacing。此处只用 --private-users=no 模式
注意: --private-users=no 不安全,只有可信的情况下才可以使用
- 安装 nspawn
apt install systemd-container - 下载 debian bookworm x64 最新的 rootfs
https://jenkins.linuxcontainers.org/view/Images/job/image-debian/lastStableBuild/architecture=amd64,release=bookworm,variant=default/
root@BTS-SRV-005:/opt/nspawn# ls -Alh
total 92M
-rw-r--r-- 1 root root 92M Jul 19 15:11 rootfs.tar.xz
root@BTS-SRV-005:/opt/nspawn/demo# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
执行以下命令以启动一个最简单的nspawn容器,启动完成后会进入容器的shell。
systemd-nspawn \
--directory=/opt/nspawn/demo在不指定boot参数的情况下,会自动在容器中启动bash,即PID 1。退出shell会导致容器停止
root@demo:~# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 7592 3596 pts/0 Ss 15:40 0:00 -bash
正常Linux容器是当作虚拟机使用的,而不是docker风格。使用--boot参数会传递ARGS作为init的参数(也同时启动init)。启动完成后会进入容器的login
systemd-nspawn \
--boot \
--directory=/opt/nspawn/demo因为没有修改密码所以无法登录。按住ctrl后按三次]以结束nspawn。
注意:按
X+EEE或者输入2887没有用
两种方法:
- 无 --boot 参数启动后直接 passwd 修改
- rootfs的优势在于外部可以直接修改内部的文件,修改 etc/shadow 文件
现在容器已经可以登录了
systemd-container 还附带另一个工具machinectl。使用--register=yes将这个容器注册到machined,同时可以用使用--machine=指定注册的名字
systemd-nspawn \
--boot \
--register=yes \
--directory=/opt/nspawn/demo打开另一个shell。machinectl以列出容器。machinectl login demo 以终端形式链接到容器的tty。如果有init则可以直接machinectl shell demo 以跳过登录。
root@BTS-SRV-005:~# machinectl
MACHINE CLASS SERVICE OS VERSION ADDRESSES
demo container systemd-nspawn debian 12 -
1 machines listed.
root@BTS-SRV-005:~# machinectl shell demo
Connected to machine demo. Press ^] three times within 1s to exit session.
root@LXCNAME:~# hostname
LXCNAME
正常使用中不可能使用用户shell来启动容器,nspawn本身不包含类似docker-daemon的设计。因为systemd本身有unit(或者systemd-run)功能,直接以临时service的形式运行。
systemd-run \
--unit=nspawn-demo \
-- \
systemd-nspawn \
--keep-unit \
--boot \
--register=yes \
--directory=/opt/nspawn/demoRunning as unit: nspawn-demo.service
root@BTS-SRV-005:~# journalctl -u nspawn-demo
Jul 20 20:00:00 BTS-SRV-005 systemd[1]: Started nspawn-demo.service - ......
Jul 20 20:00:00 BTS-SRV-005 systemd-nspawn[4351]: Spawning container d......
注:准确来说nspawn实现了machined,而不是附带。machined是虚拟机的管理框架,nspawn将启动的容器适配machined的接口,通过machined管理。
同样的,改写成service文件也很简单。但是注意必须是After=network.target否则在主机网络准备好之前就启动会导致容器内网络异常。
[Unit]
After=network.target nss-lookup.target
[Install]
WantedBy=multi-user.target
[Service]
User=root
LimitNPROC=10000
LimitNOFILE=1000000
WorkingDirectory=/opt/nspawn/demo
ExecStart=systemd-nspawn \
--keep-unit \
--boot \
--machine=demo \
--directory=/opt/nspawn/demo
我们已经成功的以服务形式启动了容器,但是还没有做任何配置。
一个网桥的例子
auto enp1s0
iface enp1s0 inet manual
auto enp2s0
iface enp2s0 inet manual
auto enp3s0
iface enp3s0 inet manual
auto vmbr0
iface vmbr0 inet static
bridge_fd 0
bridge_stp off
bridge_waitport 0
bridge-ports enp1s0 enp2s0 enp3s0
address 10.0.0.10/8
gateway 10.0.0.1
一个基于OVS的网桥例子
auto enp1s0
iface enp1s0 inet manual
ovs_type OVSPort
ovs_bridge vmbr0
auto enp2s0
iface enp2s0 inet manual
ovs_type OVSPort
ovs_bridge vmbr0
auto enp3s0
iface enp3s0 inet manual
ovs_type OVSPort
ovs_bridge vmbr0
auto vmbr0
iface vmbr0 inet static
ovs_type OVSBridge
ovs_ports enp1s0 enp2s0 enp3s0
address 10.0.0.10/8
gateway 10.0.0.1
使用--network-bridge=xxxx即可给容器分配桥接网卡
systemd-run \
--unit=nspawn-demo \
-- \
systemd-nspawn \
--keep-unit \
--boot \
--register=yes \
--directory=/opt/nspawn/demo \
--network-bridge=vmbr0nspawn网桥模式会生成一个虚拟网卡,容器内他的名字叫做 host0 (除非手动指定)
?: host0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> .......
host0的对端则是在主机系统创建了一块虚拟网卡并接入网桥
root@BTS-SRV-005:~# ip l
?: vb-demo@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> ......
root@BTS-SRV-005:~# brctl show
bridge name bridge id STP enabled interfaces
vmbr0 8000.???????????? no eno0
vb-demo
linux-container社区提供的镜像使用systemd-network配置网络。修改 /etc/systemd/network/80-container-host0.network。
systemd-network的具体使用与本文无关,进一步资料自行查阅。
[Match]
Name=host0
[Network]
Address=10.0.0.10/8
Gateway=10.0.0.1
使用 --bind 和 --bind-ro 可以方便的将主机目录穿透进容器。
注意:此处未使用
--private-user此时容器将不会隔离UID/GID,这是一个安全风险。
systemd-run \
--unit=nspawn-demo \
-- \
systemd-nspawn \
--keep-unit \
--boot \
--register=yes \
--directory=/opt/nspawn/demo \
--network-bridge=vmbr0 \
--bind-ro=/opt/acme:/nginx/cert \
--bind=/opt/nginx:/nginx \
--bind=/host-folder:/in-contianer如果想在nspawn内嵌套使用cgroup,比如运行docker或者k3s之类的工具。则需要:
- 穿透 cgroup
- 增加 capabilities
注意:此处使用了
--capability=all而不是最小权限,这是一个安全风险。
systemd-run \
--unit=nspawn-demo \
-- \
systemd-nspawn \
--keep-unit \
--boot \
--register=yes \
--directory=/opt/nspawn/demo \
--network-bridge=vmbr0 \
--capability=all \
--system-call-filter='add_key keyctl bpf'
--bind=/dev/kmsg \
--bind-ro=/sys/module \
--inaccessible=/sys/module/apparmor