systemd-nspawn是一个类似chroot的命令,但是比chroot更加强大。我们可以用systemd-nspawn创建容器来跑一些服务,或者用作测试环境。安装:
apt install systemd-container
安装完成后systemd-nspawn和machinectl就可以用了。但是在开始用之前,先修改一些基本配置,方便后续使用。
首先是关于网络这块的配置,默认情况下systemd-nspawn有一个开箱即用的网络:虚拟以太网链接(network-veth)。但是要用这个网络的话,前提是需要将系统的网络管理工具修改为使用systemd-networkd,下面介绍下如何修改。
debian系统默认的网络管理工具是ifupdown,我们先禁用ifupdown的开机自启:
systemctl disable networking
然后设置systemd-networkd的开机自启:
systemctl enable systemd-networkd
接下来看一下ifupdown的配置:
cat /etc/network/interfaces
我这台机器内的配置如下,可以看到基本就是用dhcp来获取ip的:
auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto eth1 iface eth1 inet dhcp auto eth2 iface eth2 inet6 auto
eth0是公网ip,eth1是内网ip,eth2是公网ipv6:
知道目前机器的网络配置后,把这份配置文件重命名保存一下以便在配置systemd-networkd后不会使用它:
mv /etc/network/interfaces /etc/network/interfaces.save
接下来配置systemd-networkd,新建eth0.network文件:
nano /etc/systemd/network/eth0.network
写入如下配置:
[Match] Name=eth0 [Network] DHCP=yes
新建eth1.network文件
nano /etc/systemd/network/eth1.network
写入如下配置:
[Match] Name=eth1 [Network] DHCP=yes
新建eth2.network文件:
nano /etc/systemd/network/eth2.network
写入如下配置:
[Match] Name=eth2 [Network] DHCP=yes
做完这些配置之后,重启机器使修改生效:
reboot
重新登录到机器内后,现在的虚拟以太网链接(network-veth)就是可用的了,但是我不打算用这个网络,因为用这个网络创建的容器获取到的ip是不固定的,也就是说容器重启后又会获取另外一个与之前不同的ip,这样使用的话太不方便。为了解决这个问题,我们可以自己创建一个网桥,然后让容器都用网桥来联网。
新建br0.netdev文件:
nano /etc/systemd/network/br0.netdev
写入如下配置:
[NetDev] Name=br0 Kind=bridge
新建br0.network文件:
nano /etc/systemd/network/br0.network
写入如下配置:
[Match] Name=br0 [Network] Address=10.50.0.1/24 DHCPServer=yes IPMasquerade=yes LLDP=yes [DHCPServer] EmitDNS=yes DNS=8.8.8.8
这里着重解释一下br0.network的配置,由于我们的服务器上没有多个公网ip,所以ip地址就用保留地址,然后用IPMasquerade启用nat转发。另外systemd-networkd是有dhcp服务器功能的,使用DHCPServer参数来启动dhcp服务器,为容器自动分配ip地址。做完这些配置之后重启systemd-networkd使其生效:
systemctl restart systemd-networkd
网桥创建完成后,现在还需要让systemd-nspawn使用我们刚创建的网桥,编辑systemd-nspawn@服务的配置文件:
systemctl edit systemd-nspawn@
覆盖systemd-nspawn@服务默认的启动配置,去掉–network-veth,添加–network-bridge=br0:
[Service] ExecStart= ExecStart=systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-bridge=br0 -U --settings=override --machine=%i
现在来制作操作系统镜像。这里我用mkosi制作一个debian11的镜像来演示。
不要使用debian官方存储库内的mkosi,因为版本太旧有很多问题,这里安装最新版。先安装mkosi需要用到的依赖:
apt -y install arch-install-scripts btrfs-progs debootstrap dosfstools ovmf e2fsprogs squashfs-tools gnupg python3 tar cryptsetup-bin xfsprogs xz-utils zypper sbsigntool debian-archive-keyring python3-pip git
接下来安装mkosi:
python3 -m pip install git+https://github.com/systemd/mkosi.git@v13
准备工作目录:
mkdir debian mkdir debian/mkosi.cache mkdir debian/mkosi.extra
进入工作目录,新建mkosi.default配置文件:
cd debian nano mkosi.default
写入如下配置,修改Password后面的值来设置root密码:
[Distribution] Distribution=debian Release=bullseye [Output] Format=directory Output=/var/lib/machines/debian Bootable=no Hostname=imlala [Content] BasePackages=yes Packages=locales,tzdata,procps,dialog,iproute2,iputils-ping,lsof,net-tools,nano,curl,git Password=rootpassword
新建mkosi.prepare脚本文件:
nano mkosi.prepare
对镜像做一些基本的配置修改,并设置镜像内的systemd-networkd为开机自启:
#!/bin/sh systemctl enable systemd-networkd sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen dpkg-reconfigure --frontend=noninteractive locales update-locale LANG=en_US.UTF-8 rm -rf /etc/apt/sources.list.d/bullseye-security.list rm -rf /etc/apt/sources.list.d/bullseye-updates.list
给脚本执行权限:
chmod +x mkosi.prepare
进入到之前创建好的mkosi.extra目录,这个目录的作用是向镜像内添加文件或者替换文件:
cd mkosi.extra
这里替换掉镜像内的存储库配置,创建相应的目录:
mkdir -p etc/apt
新建sources.list:
nano etc/apt/sources.list
写入如下配置:
deb http://deb.debian.org/debian bullseye main contrib non-free deb-src http://deb.debian.org/debian bullseye main contrib non-free deb http://deb.debian.org/debian-security/ bullseye-security main contrib non-free deb-src http://deb.debian.org/debian-security/ bullseye-security main contrib non-free deb http://deb.debian.org/debian bullseye-updates main contrib non-free deb-src http://deb.debian.org/debian bullseye-updates main contrib non-free
这是mkosi.extre目录的正确结构:
root@debian-1cpu-1gb-sg-sin1:~/debian/mkosi.extra# tree . └── etc └── apt └── sources.list
回到上级目录执行mkosi命令即可开始构建镜像:
cd ../ mkosi
构建完成后即可使用machinectl启动:
machinectl start debian
登录到系统内,要登出的话就按键盘组合键ctrl+]]]:
machinectl login debian
设置容器开机自启,或者禁用开机自启:
machinectl enable debian machinectl disable debian
重启、关机、杀掉容器:
machinectl reboot debian machinectl poweroff debian machinectl kill debian
列出全部正在运行的容器,查看某一个容器的状态:
machinectl list machinectl status
下面介绍个比较实用的功能:端口映射(Port mapping)
使用端口映射可以把宿主机的端口映射到容器内的端口,比如容器内装了一个nginx使用了80端口,宿主机有一个8080端口空闲,我们就可以把宿主机的8080端口映射到容器内的80端口,这样通过外部网络就能访问到容器内的nginx服务了。
在之前我们已经通过machinectl启动了一个名为debian的容器,实际上machinectl也是调用的systemd-nspawn@服务,所以要配置某一个容器的端口映射,只需要编辑相应的systemd服务:
systemctl edit systemd-nspawn@debian
覆盖配置,添加–port参数,有多个端口就添加多个–port参数:
[Service] ExecStart= ExecStart=systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-bridge=br0 --port=tcp:8080:80 -U --settings=override --machine=%i
要使配置生效,必须先关闭容器再启动,直接reboot是不行的:
machinectl poweroff debian machinectl start debian
在容器使用網橋通過外部物理網絡DHCP獲取到地址後,還需要nspawn service 中的port forward來轉發端口嗎?