如果你想要运行一个发行版容器,而又不想被 docker 一类的重量级方案打扰,现在有一个新的简单方案了。
nspawn.org 目前提供了 Arch、CentOS、Debian、Fedora、Ubuntu 的各版本镜像,并可以直接用 systemd-nspawn 的验证机制进行签名验证。
推荐的用法是使用其提供的 “nspawn” 工具。下面以创建一个 Fedora 30 容器为例:
1、获取工具:
$ wget https://raw.githubusercontent.com/nspawn/nspawn/master/nspawn
$ chmod +x nspawn
2、获取 Fedora 30 镜像:
$ sudo ./nspawn init fedora/30/tar
3、启动容器并获取 shell:
$ sudo machinectl start fedora-30-tar
$ sudo machinectl shell fedora-30-tar
Connected to machine fedora-30-tar. Press ^] three times within 1s to exit session.
[root@fedora30 ~]#
一些背景:容器默认的存储路径在 /var/lib/machines/。nspawn.org 的创建者是 shibumi,目前是 Arch Linux Trusted User。所有的镜像使用 mkosi 制作,定义文件均在 GitHub 上。除了 nspawn 容器镜像,这个站点还提供可引导的 GPT-UEFI 镜像。
systemd-nspawn 踩坑记
systemd-nspawn
),正好踩中了一些坑,所以随便写写记录一下,也算是重新开始做起博客这件事情了吧。What & Why
online.net
捡来的特价独服,因为只有一个人使用,所以我直接在主机上开了很多个 KVM 虚拟机,使用(几乎是)一个服务一个虚拟机的方式来部署自己的服务。这在一个人使用的时候确实没有什么太大的问题,唯一的问题可能就是因为自己懒,而虚拟机的数量太多,所以经常忘记更新 / 维护那些虚拟机。E5-2680v2
的独服(购买的时候下单的是 E5-2660v1
,但是不知道是商家特别有钱还是那天机房小哥心情好,给弄了一台 E5-2680v2
),几个人合用一台。因为是合用,所以大家各自开了一个 KVM 虚拟机,各自隔离。这时候,我就不能再使用虚拟机的方式来隔离自己的服务了,因为我本身已经被隔离在了一个虚拟机里,双重虚拟机可从来都不是什么好主意。Docker
不太适合我的情况 —— 我并不是希望把所有服务都做成不可变的镜像然后到处部署,我的目的仅仅是简单的隔离(看起来整洁 / 给有些傲娇的应用提供最适合的环境)。因此,我转向了 systemd-nspawn
,毕竟我是 Systemd
的Systemd
的 ArchLinux
在安装完成后就自带这个东西。dirty fix
预警)非特权用户 (Private Users)
/var/lib/machines
下创建目录并部署系统是一个很快的工作。然而,当我部署成功并尝试启动容器的时候,我却根本看不到任何反应,无论 machinectl status
还是 systemctl status
都没法给出任何有用的信息。/var/lib/machines
下我部署的目录的时候,发现里面文件的权限全部被修改成了奇怪的 UID 和 GID 值。从 ArchWiki 上的描述来看,这似乎是启用了 Private Users
的正常现象。然而,死马当活马医,我尝试在 /etc/systemd/nspawn/myContainer.nspawn
(myContainer 是我的容器的名字) 里面加入了[Exec]
PrivateUsers=no
systemd-nspawn
完全没有给我任何有用的错误信息。更奇怪的是,我直接用命令行的 systemd-nspawn
去启动容器是完全正常的,而使用 systemd-nspawn@.service
就必须关掉 Private Users 才能正常使用。鉴于我的使用场景并不需要多么严格的安全策略(另一方面,Linux 下的容器这个概念本身也不是用来做安全的),我暂时并没有去处理这个问题。所以,这算是一个 dirty fix
吧。无法访问容器的 TTY
machinectl login myContainer
, 直接给我扔了一个 protocol error
出来。在 Google 上找了很久也没有找到任何一个人遇到类似的问题。最多只有进入了容器的登录界面却无法登录的问题,而遇到那种问题的人至少已经获得了容器的一个 Login Shell, 而我则是什么都没有……systemd-nspawn
命令启动容器进去,配置好 openssh
,然后用 machinectl
启动容器,并在外面直接使用 ssh
访问容器内部的 shell。是的,你没有看错,直接在 /var/lib/machines
使用 systemd-nspawn -b -D myContainer
命令启动容器是完全可以访问容器内的 shell 的,而从外面使用 machinectl login
或者 machinectl shell
就是不行的……machinectl login
可以获取到容器的正常的 Login Shell... 天知道把我折腾的要死要活的那个问题是怎么回事…… 而且从出现那个问题到现在我并没有更新服务器上的任何软件,也没有针对这个问题做任何处理…… 而当时我重启了不知道多少遍都完全没有作用。容器内的内核模块问题 (OverlayFS / ip6tables / FUSE ...)
/etc/modules-load.d/
自动载入)Systemd-nspawn 内运行 Docker
Mastodon
节点,这个东西是主要使用 Ruby on Rails
编写但同时有很多其他依赖的东西。在以前的机器上,我是使用 docker-compose
通过容器的组合在一个虚拟机里直接部署上这一系列依赖。而现在我需要迁移到我的新独服上,我不能使用虚拟机,也不想自找麻烦手动部署,也不想让 Docker
产生的一大堆网络接口之类的东西挂在主机的 namespace 里。总而言之,因为这种 Ruby on Rails
程序是好多个大怪兽,虽然各自有笼子,但是因为数量比较多,分散放置还是感觉很凌乱,所以我想进一步把它们的笼子也都关进一个动物园里统一管理。systemd-nspawn
似乎并不支持在它内部再启动 Docker
容器。尝试使用 Docker
容器会直接带来 Operation not permitted
异常。从 https://github.com/opencontainers/runc/issues/1456 了解到,Docker
依赖了 cgroups
功能,并且需要比较高的权限,而在默认情况下,systemd-nspawn
是隔离了 cgroup
命名空间的,而且也没有给予不必要的权限。所以,作为测试,我在 /etc/systemd/nspawn/myContainer.nspawn
里加入了[Exec]
Capability=all
[Files]
Bind=/sys/fs/cgroup
cgroup
命名空间共享给了容器里的系统,并给予了所有可以给予的 Capabilities
。同时,还需要关闭 systemd-nspawn
的 cgroup
隔离功能,只需要 systemctl edit systemd-nspawn@myContainer
[Service]
Environment=SYSTEMD_NSPAWN_USE_CGNS=0
Docker
已经可以使用了,但是很不幸的是,并不能。这回出现的是一个莫名奇妙的 session key
无法创建的异常。这次这个异常我就完全没有看懂了……Docker
在尝试使用 kernel keyring
,而这个功能是不支持(ref: https://github.com/moby/moby/issues/10939)命名空间隔离的。所以,为了安全,systemd-nspawn
默认把与此相关的系统调用都过滤掉了,不允许内部的系统调用。因此,只需要开启这两个系统调用的权限(在 /etc/systemd/nspawn/myContainer.nspawn
的 [Exec]
段中加入)SystemCallFilter=add_key keyctl
nspawn
容器即可使用 Docker
。Docker
正常运行之后,我发现一个问题,那就是它在使用非常慢、非常不科学的 vfs
作为存储后端。根据文档,这个存储后端会对每个 layer 都创建一个拷贝。于是我想起来了遇到的上一个问题 —— 主机没有加载 OverlayFS 的内核模块,因此默认的 overlay2
存储后端加载失败了。尝试在主机上加载 overlay
模块,然后重新启动容器里的 Docker
,发现 overlay2
存储后端果然已经在正确运行了。ArchWiki
上的对应章节, 因为我发现我在整个网络上都找不到关于这件事情的文档,有的只是一段 Twitter 对话,而且他们其实还并没有解决这个问题……希望我并不是唯一一个有这种奇葩需求的人吧。nspawn
容器就成为了名副其实的特权容器,拥有很多很多高权限操作的能力。考虑到我的本意仅仅是出于洁癖一般的理由,这个问题我觉得并不是非常大……总之,给大家一个参考。容器内使用 FUSE
FUSE
是指用户态文件系统,比如 sshfs
, ntfs-3g
等。想要直接在 systemd-nspawn
容器里使用它们是会直接失败的。当然,这个解决办法很简单,因为这仅仅是因为容器里没有 /dev/fuse
。fuse
内核模块。然后,你需要在 /etc/systemd/nspawn/myContainer.nspawn
加入[Exec]
Capability=CAP_MKNOD
DeviceAllow=/dev/fuse rwm
Capability
设为 all
了,那就不用再单独设置一次 CAP_MKNOD
了)mknod /dev/fuse c 10 229
其他:网络配置
/etc/systemd/nspawn/myContainer.nspawn
里给容器增加一个网络接口[Network]
VirtualEthernetExtra=name_on_host:name_in_container
[Network]
Private=true
VirtualEthernet=true
结论
Systemd
坑很多,而且很玄学.systemd-nspawn搭建容器
作为一个 systemd 重度使用者,经常用 docker 容器进行一些本地测试或搭建一个开发环境, 因为 systemd 提供了 systemd-nspawn 来模拟 chroot,无疑它也是可以用来构建容器的, 所以就摸索了一番,总结如下。
什么是 systemd-nspawn?
systemd-nspawn 很像 chroot 命令,但是更为强大, 它可以全面虚拟化整个文件系统、进程树、各种各样的 IPC 子系统以及主机名和域名。 它可以用来在一个轻量级的容器内运行一个命令或操作系统。
由于是基于 systemd init 系统,所以它也可以利用现有的 systemd 各种组件命令。
构建一个最小的 Archlinux 容器
因为我本机是 Archlinux,所以就以构建 Archlinux 容器为例了,首先创建一个容器的顶层文件夹:
mkdir -p ~/Containers/arch
利用 pacstrap 将 Archlinux 基本系统安装进容器文件夹:
pacstrap -c -d ~/Containers/arch base
因为容器和宿主机可以共享 linux 内核,所以初始化容器文件夹可以忽略 linux 包:
# pacstrap -i -c -d ~/Containers/arch base --ignore linux
==> Creating install root at arch
==> Installing packages to arch
:: Synchronizing package databases...
core 120.1 KiB 619K/s 00:00 [######################################] 100%
extra 1755.6 KiB 1600K/s 00:01 [######################################] 100%
community 3.6 MiB 2.90M/s 00:01 [######################################] 100%
:: linux is in IgnorePkg/IgnoreGroup. Install anyway? [Y/n] n
其中 -i
选项避免自动确认。
这样一个 Archlinux 容器就构建成功了,你可以通过 systemd-nspawn 命令开启容器:
systemd-nspawn -b -D ~/Containers/arch -n
容器启动后,可以用 root 用户名登陆,无须密码,进入容器系统后,你就可以搭建自己的容器环境了。
当然,你也可以很容易的构建一个 debian 或 fedora 容器,网上有许多构建方法,这里不再讲解。
machinectl
systemd-nspawn 开启的容器被称为 machine,可以利用 systemd 中的 machinectl 命令进行各种容器操作。
machinectl 命令默认会到 /var/lib/machines
、/usr/local/lib/machines/
和 /usr/lib/machines/
目录搜索容器。
为了可以用 machinectl 命令,我们将上面的容器文件夹移动到 /var/lib/machines/arch
文件夹下:
mv ~/Containers/arch /var/lib/machines/arch
因为 archlinux 中 pam_security 的控制,machinectl 命令是不能 root 登陆的,为了解决这个问题,需要在容器里修改文件
/etc/securetty
,添加 pts/0
。
这样我们就可以用 machinectl 来控制我们的 arch
容器了。
开启 arch
容器:
machinectl start arch
登录 arch
容器:
machinectl login arch
运行 arch
容器命令:
machinectl shell arch /usr/bin/pwd
关闭 arch
容器:
machinectl poweroff arch
machinectl 其他一些命令:
machinectl reboot container_name
machinectl terminate container_name
machinectl kill container_name
machinectl list
machinectl status container_name
machinectl show container_name
machinectl enable container_name
machinectl disable container_name
machinectl bind container_name PATH [PATH]
machinectl copy-to container_name PATH [PATH]
machinectl copy-from container_name PATH [PATH]
machinectl 也有很多与容器 image 相关的命令,这里不再介绍。
与其他 systemd 组件配合使用
systemd 系列组件大多都有一个 -M
选项,此选项就是用来指定容器,容器搜索路径与 machinectl 一样。
我们可以这样用 systemd-nspawn 来开启容器:
systemd-nspawn -M container_name
查看容器日志:
journalctl -M container_name
查看容器进程控制组内容:
systemd-cgls -M container_name
分析容器启动过程:
systemd-analyze -M container_name
开机自启动指定容器
systemctl enable systemd-nspawn@container_name.service
No comments:
Post a Comment