MKOSI How To(二)

构建第一个镜像

MKOSI官方提供了一系列内置的镜像模板,如果你只是急于求成,那么不必太过担忧,你只需要这样:

1
2
3
4
mkosi --format=directory \
--include=mkosi-vm \
--distribution=arch \
build
  • --format=directory:产品格式为目录。
  • --include=mkosi-vm:使用内置的mkosi-vm模板。
  • --distribution=arch:构建一个Arch Linux镜像。

如果你的发行版不是Arch,而且系统中没有安装pacman的话,那么构建应当会立刻退出并提醒你:

1
`pacman` is not installed.

你可以选择在你的系统上安装它,但,如果你非常不希望改变你的系统环境,MKOSI提供了另一种思路:Tools Tree。Tools Tree是MKOSI在构建镜像前先下载并构建的、一个专门用于保存构建时可能用到的工具的文件系统,在构建时它会自动使用。你只需要添加一个命令行参数:

1
--tools-tree=default

开始构建后,你应该能看到这样的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
‣ Validating certificates and keys
‣ Populating pacman keyring

......

# pacman包管理器开始安装软件包
:: Synchronizing package databases...
core 120.5 KiB 175 KiB/s 00:01 [##################################################] 100%
extra 8.0 MiB 4.10 MiB/s 00:02 [##################################################] 100%

‣ Generating system users
‣ Generating volatile files
‣ Applying presets…
‣ Generating hardware database
‣ Building default initrd

......

‣ Copying in extra file trees…
‣ Generating system users
‣ Generating volatile files
‣ Applying presets…
‣ Applying first boot settings
/buildroot: /etc/locale.conf written.
‣ Generating hardware database
‣ Removing files…
‣ /var/tmp/mkosi-workspace-krda9q4p/initrd.cpio.zst size is 68.3M, consumes 68.3M.
‣ Running modinfo to fetch kernel module dependencies
‣ Calculating required kernel modules and firmware
‣ aead2 is a dependency of cifs but is not installed, ignoring
‣ sha512 is a dependency of cifs but is not installed, ignoring
‣ aes is a dependency of cifs but is not installed, ignoring
‣ nls is a dependency of cifs but is not installed, ignoring
‣ Creating cpio archive /var/tmp/mkosi-workspace-krda9q4p/kernel-modules-6.16.8-arch3-1.initrd…
‣ /.../image size is 936.7M.

随后我们就可以看到新鲜出炉的镜像了,并且还带有它内部使用的内核和Initrd:

1
2
3
4
5
6
$ ls -l

total 224372
drwxr-xr-x 17 root root 4096 Sep 28 07:46 image
-rw-r--r-- 1 root root 213795740 Sep 28 07:47 image.initrd
-rw-r--r-- 1 root root 15946240 Sep 28 07:47 image.vmlinuz

在我们继续之前,你也许会问,这些流程都是在做什么呢?之前不是只有简单的三步吗?

多出来的这些步骤,要么是为了确保镜像的可复现性而进行的初始化操作(如执行systemd-firstbootsystemd-sysusers等),要么是为了方便用户进行客制化而设置的挂钩点(如Copying in extra file trees…对应mkosi.extra/文件),我们稍后会再次提到。

我想试试这个镜像!

当然没问题!你可以随时使用强大的systemd-nspawn容器将镜像在一个容器中启动进行测试,而不会影响你的宿主系统。

如果你希望不启动镜像,而是直接在镜像的chroot环境中执行命令,那么你只需要使用mkosi shell子命令:

1
2
3
4
5
# 你需要带上命令行参数,这影响镜像信息
mkosi --format=directory \
--include=mkosi-vm \
--distribution=arch \
shell -- [命令]

在没有设置参数的情况下,默认情况下会在镜像中打开bash,就像这样:

1
2
3
$ mkosi --format directory --include mkosi-vm --distribution arch shell

[root@main ~]#

如果你想试一试镜像是否可以从init正常启动,那么也没有问题,你只需要使用mkosi boot子命令即可:

1
2
3
4
mkosi --format=directory \
--include=mkosi-vm \
--distribution=arch \
boot
  • 但不要忘记镜像初始情况下是没有密码的(这意味着你可能没法登录),除非你使用RootPassword=构建配置,或是在镜像中执行passwd明确进行了配置。

执行后,你应该能看到熟悉的systemd启动画面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ mkosi --format directory --include mkosi-vm --distribution arch boot

systemd 258-4-arch running in system mode (+PAM +AUDIT -SELINUX +APPARMOR -IMA +IPE +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBCRYPTSETUP_PLUGINS +LIBFDISK +PCRE2 +PWQUALITY +P11KIT +QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD +BPF_FRAMEWORK +BTF +XKBCOMMON +UTMP -SYSVINIT +LIBARCHIVE)
Detected virtualization systemd-nspawn.
Detected architecture x86-64.
Detected first boot.
Received regular credentials: firstboot.locale, firstboot.timezone
Acquired 2 regular credentials, 0 untrusted credentials.

Welcome to Arch Linux!

Initializing machine ID from container UUID.
Applying preset policy.
Populated /etc with preset unit settings.
Queued start job for default target Graphical Interface.
[ OK ] Created slice Slice /system/getty.
[ OK ] Created slice Slice /system/modprobe.
[ OK ] Created slice User and Session Slice.
......

如果你想要更激进一些,在虚拟机中启动镜像看看Bootloader什么的能不能正常工作,那也是可以的,只要确保你的宿主系统上安装了qemu,然后使用mkosi vm子命令:

1
2
3
4
mkosi --format=directory \
--include=mkosi-vm \
--distribution=arch \
vm

这样操作后,你应该能看到通过终端呈现的虚拟机的Bootloader画面。

这镜像不合我心意!

如果觉得内置的模板对你的需求不够契合,或者你单纯想要构建出最客制化的镜像,你就需要手动编写构建配置了。

首先你需要创建一个项目目录,以testarch为例子:

1
mkdir testarch && cd testarch

在这里,你需要创建一个mkosi.conf,这是MKOSI构建时会读取的配置文件。

1
touch mkosi.conf

接着你就可以开始编写了。

怎么写?

和其他的systemd项目一样,mkosi.conf的格式是标准的INI格式。不同的配置会放在不同的段(Section)中,每个配置项占一行,对于某些可能比较冗长的配置项,有时也支持读取多行。

所有的MKOSI构建配置项,都可以转换为其对应的命令行参数,反之也一样。以我们刚才的构建配置为例,转换为构建配置是这样的:

1
2
3
4
5
6
7
8
[Include]
Include=mkosi-vm

[Distribution]
Distribution=arch

[Output]
Format=directory

如此编写后,你就可以摆脱命令行手动提供参数的烦恼了。要检查配置项的实际效果,使用mkosi summary

1
mkosi summary

你应该能看到这样的输出:

1
2
3
4
5
6
7
8
9
10
11
12
IMAGE: main

CONFIG:
Profiles: none
Dependencies: none
Minimum Version: none
Configure Scripts: none
Pass Environment: none

DISTRIBUTION:
Distribution: arch
......

我想创建自己的软件包列表

自定义镜像的一个良好开始是创建一个自定义的包列表。先删掉我们的Include=段:

1
2
3
4
5
[Distribution]
Distribution=arch

[Output]
Format=directory

然后,转而为其添加Packages=配置项:

1
2
3
4
5
6
7
8
[Distribution]
Distribution=arch

[Output]
Format=directory

[Content]
Packages=

我们从一个最简单的列表开始。要获得一个可以使用的容器镜像,我们至少需要:

  • 一个Init系统(systemd)。
  • 一个Shell(Bash)。
  • 一套用户空间基本工具组(Coreutils和Util-linux)。

因此我们编写:

1
2
3
4
5
6
7
8
9
10
11
[Distribution]
Distribution=arch

[Output]
Format=directory

[Content]
Packages=systemd
bash
coreutils
util-linux

因为我们裁剪掉了大部分其他软件包,这次构建的镜像应当相当轻巧:

1
‣  /.../image size is 484.9M.