mkosi 是一个轻量级工具,用于从发行版软件包构建镜像。
mkosi 特性
mkosi 支持一些输出格式,但最重要的是 可发现磁盘镜像Discoverable Disk Images(DDI)。同一个 DDI 可用于引导容器、或运行在虚拟机、抑或是复制到 U 盘以引导真实物理机,然后从 U 盘复制到磁盘以引导系统。该镜像具有标准化的布局和描述其用途的元数据。
mkosi 依赖其他工具来完成大部分工作:使用 systemd-repart
在磁盘镜像上创建分区,使用 mkfs.btrfs
/ mkfs.ext4
/ mkfs.xfs
等创建文件系统,并使用 dnf
/ apt
/ pacman
/ zypper
下载和解压包。
mkosi 支持一系列发行版:Debian 和 Ubuntu、Arch Linux、openSUSE,当然还包括
Fedora、CentOS Stream 及其衍生版本,以及最近的 RHEL UBI 和 RHEL
。由于实际的“重活”是由其他工具完成的,mkosi
可以进行交叉构建。这意味着可以使用一个发行版构建各种其他发行版的镜像。唯一的要求是主机上安装了相应的工具。Fedora 有原生的 apt
、pacman
和 zypper
,因此它为使用 mkosi 构建任何其他发行版提供了良好的基础。
它还有一些有趣的功能:镜像可以由非特权用户创建,或者在没有设备文件的容器中创建,特别是没有对回环设备的访问权限。它还可以在没有特权的情况下将这些镜像启动为虚拟机(使用 qemu
)。
配置是声明性的,非常容易创建。使用 systemd-repart
创建磁盘分区,并使用 repart.d
配置文件定义应该如何完成此操作。
有关更多详细信息,请参见 Daan DeMeyer 在 All Systems Go 大会上的两个演讲:《systemd-repart: Building Discoverable Disk Images》 和 《mkosi: Building Bespoke Operating System Images》。
项目目标
mkosi
的一个目标是允许对软件项目进行针对不同发行版的测试。它将为一个发行版创建一个镜像(使用该发行版的软件包),然后将软件项目编译并安装到该镜像中,插入不属于软件包的额外文件。但是,首个阶段,即从软件包创建镜像的过程,本身就是有用的。这是我们将首先展示的内容。
我们 [1] 最近添加了对 RHEL 和 RHEL UBI 的支持。让我们从 RHEL UBI 开始,利用发行版软件包创建镜像。
请注意,下面的示例要求 mkosi 19,而且不适用于更早的版本。
带有 Shell 的基本 RHEL UBI 镜像
$ mkdir -p mkosi.cache
$ mkosi
-d rhel-ubi
-t directory
-p bash,coreutils,util-linux,systemd,rpm
--autologin
上面的命令指定了发行版 rhel-ubi
,输出格式 directory
,并请求安装软件包 bash
、coreutils
、…、rpm
。rpm
通常不需要放到镜像内部,但在这里用于内省会很有用。我们还启用了以 root 用户自动登录。
在启动构建之前,我们创建了缓存目录 mkosi.cache
。当存在缓存目录时,mkosi 会自动使用它来持久化下载的 RPM 包。这将使相同软件包集合的后续调用速度更快。
然后,我们可以使用 systemd-nspawn
将此镜像作为容器启动:
$ sudo mkosi
-d rhel-ubi
-t directory
boot
systemd 252-14.el9_2.3 running in system mode (+PAM +AUDIT +SELINUX -APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS -FIDO2 +IDN2 -IDN -IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -BPF_FRAMEWORK +XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified)
Detected virtualization systemd-nspawn.
Detected architecture x86-64.
Detected first boot.
Red Hat Enterprise Linux 9.2 (Plow)
...
[ OK ] Created slice Slice /system/getty.
[ OK ] Created slice Slice /system/modprobe.
[ OK ] Created slice User and Session Slice.
...
[ OK ] Started User Login Management.
[ OK ] Reached target Multi-User System.
Red Hat Enterprise Linux 9.2 (Plow)
Kernel 6.5.6-300.fc39.x86_64 on an x86_64
image login: root (automatic login)
[root@image ~]# rpm -q rpm systemd
rpm-4.16.1.3-22.el9.x86_64
systemd-252-14.el9_2.3.x86_64
正如前面提到的,此镜像可以用于启动虚拟机。但在此设置下,这是不可能的 —— 我们的镜像没有内核。事实上,RHEL UBI 根本不提供内核,因此我们无法使用它进行引导(无论是在虚拟机上还是在裸机上)。
创建镜像
我一开始说是要创建镜像,但到目前为止我们只有一个目录。让我们开始实际创建一个镜像:
$ mkosi
-d rhel-ubi
-t disk
-p bash,coreutils,util-linux,systemd,rpm
--autologin
这将生成 image.raw
,一个带有 GPT 分区表和单个根分区(用于本机架构)的磁盘镜像。
$ sudo systemd-dissect image.raw
Name: image.raw
Size: 301.0M
Sec. Size: 512
Arch.: x86-64
Image UUID: dcbd6499-409e-4b62-b251-e0dd15e446d5
OS Release: NAME=Red Hat Enterprise Linux
VERSION=9.2 (Plow)
ID=rhel
ID_LIKE=fedora
VERSION_ID=9.2
PLATFORM_ID=platform:el9
PRETTY_NAME=Red Hat Enterprise Linux 9.2 (Plow)
ANSI_COLOR=0;31
LOGO=fedora-logo-icon
CPE_NAME=cpe:/o:redhat:enterprise_linux:9::baseos
HOME_URL=https://www.redhat.com/
DOCUMENTATION_URL=https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9
BUG_REPORT_URL=https://bugzilla.redhat.com/
REDHAT_BUGZILLA_PRODUCT=Red Hat Enterprise Linux 9
REDHAT_BUGZILLA_PRODUCT_VERSION=9.2
REDHAT_SUPPORT_PRODUCT=Red Hat Enterprise Linux
REDHAT_SUPPORT_PRODUCT_VERSION=9.2
Use As: ✗ bootable system for UEFI
✓ bootable system for container
✗ portable service
✗ initrd
✗ sysext extension for system
✗ sysext extension for initrd
✗ sysext extension for portable service
RW DESIGNATOR PARTITION UUID PARTITION LABEL FSTYPE ARCHITECTURE VERITY GROWFS NODE PARTNO
rw root 1236e211-4729-4561-a6fc-9ef8f18b828f root-x86-64 xfs x86-64 no yes /dev/loop0p1 1
好的,我们现在有一个镜像,镜像中包含了一些来自 RHEL UBI 软件包的内容。我们如何在其上加点我们自己的东西呢?
使用自己的文件扩展镜像
有几种方法可以扩展镜像,包括从头开始编译某些东西。但在那之前,让我们做一些更简单的事情,将一个现成的文件系统注入到镜像中:
$ mkdir -p mkosi.extra/srv/www/content
$ cat >mkosi.extra/srv/www/content/index.html