使用 mkosi 构建 RHEL 和 RHEL UBI 镜像
镜像 mkosi
![](/data/attachment/album/202311/27/232213c9y5jt85t7vj8men.png)


mkosi 是一个轻量级工具，用于从发行版软件包构建镜像。本文介绍如何使用 mkosi 从 RHEL 和 RHEL <ruby> <a href="https://uapi-group.org/specifications/specs/discoverable_disk_image/">  通用基础镜像 </a> <rt>  Universal Base Image </rt></ruby>（UBI）的软件包构建镜像。RHEL UBI 是 RHEL 的一个子集，可以在没有订阅的情况下免费使用。


### mkosi 特性


mkosi 支持一些输出格式，但最重要的是 <ruby> <a href="https://uapi-group.org/specifications/specs/discoverable_disk_image/">  可发现磁盘镜像 </a> <rt>  Discoverable Disk Images </rt></ruby>（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](https://media.ccc.de/v/all-systems-go-2023-191-systemd-repart-building-discoverable-disk-images)》 和 《[mkosi: Building Bespoke Operating System Images](https://media.ccc.de/v/all-systems-go-2023-190-mkosi-building-bespoke-operating-system-images)》。


### 项目目标


mkosi 的一个目标是允许对软件项目进行针对不同发行版的测试。它将为一个发行版创建一个镜像（使用该发行版的软件包），然后将软件项目编译并安装到该镜像中，插入不属于软件包的额外文件。但是，首个阶段，即从软件包创建镜像的过程，本身就是有用的。这是我们将首先展示的内容。


我们 <sup class="footnote-ref"> <a href="#fn1" id="fnref1">  [1] </a></sup> 最近添加了对 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 <<'EOF'
<h1>Hello, World!</h1>
EOF

```

现在，该镜像将包含 `/srv/www/content/index.html`。


这种方法用于注入额外的配置或简单的程序。


### 从源代码构建


现在让我们过一遍完整流程，从源代码构建一些东西。例如，一个简单的 Meson 项目，有一个单独的 C 文件：



```
$ cat >hello.c <<'EOF'
#include <stdio.h>

int main(int argc, char **argv) {
    char buf[1024];

    FILE *f = fopen("/srv/www/content/index.html", "re");
    size_t n = fread(buf, 1, sizeof buf, f);

    fwrite(buf, 1, n, stdout);
    fclose(f);
    return 0;
}
EOF

$ cat >meson.build <<'EOF'
project('hello', 'c')
executable('hello', 'hello.c',
            install: true)
EOF

```


```
$ cat >mkosi.build <<'EOF'
set -ex

mkosi-as-caller rm -rf "$BUILDDIR/build"
mkosi-as-caller meson setup "$BUILDDIR/build" "$SRCDIR"
mkosi-as-caller meson compile -C "$BUILDDIR/build"
meson install -C "$BUILDDIR/build" --no-rebuild
EOF
$ chmod +x mkosi.build

```

总结一下：我们有一些源代码（`hello.c`），一个构建系统配置文件（`meson.build`），以及一个由 mkosi 调用的胶水脚本（`mkosi.build`）。对于实际的项目，也会有相同的元素，只是更加复杂。


这个脚本需要一些解释。mkosi 在创建镜像时使用用户命名空间。这允许包管理器（例如 `dnf`）安装由不同用户拥有的文件，即使它是由一个普通非特权用户调用的。我们使用 `mkosi-as-caller` 切换回调用者以进行编译。这样，在 `$BUILDDIR` 下编译期间创建的文件将由调用者拥有。


现在让我们使用我们的程序构建镜像。与之前的调用相比，我们需要额外的软件包：`meson`、`gcc`。由于我们现在有了构建脚本，mkosi 将执行两个构建阶段：首先创建一个构建镜像，并在其中调用构建脚本，将安装产物存储在一个临时目录中，然后构建最终镜像，并将安装产物注入其中。（mkosi 设置 `$DESTDIR`，`meson install` 自动使用 `$DESTDIR`，因此并不需要我们明确指定。）



```
$ mkosi \
    -d rhel-ubi \
    -t disk \
    -p bash,coreutils,util-linux,systemd,rpm \
    --autologin \
    --build-package=meson,gcc \
    --build-dir=mkosi.builddir \
    --build-script=mkosi.build \
    -f

```

此时，我们有了带有自定义载荷的镜像 `image.raw`。我们可以启动我们新创建的可执行文件作为 shell 命令：



```
$ sudo mkosi -d rhel-ubi -t directory shell hello
<h1>Hello, World!</h1>

```

### 获取 RHEL 的开发者订阅


RHEL UBI 主要用作容器构建的基础层。它提供了有限的软件包（约 1500 个）。现在让我们切换到完整的 RHEL 安装。


获取 RHEL 的最简单方法是使用 [开发者许可证](https://developers.redhat.com/articles/faqs-no-cost-red-hat-enterprise-linux)。它提供了权限注册 16 个运行 RHEL 的物理或虚拟节点，并提供自助式支持。


首先，[创建一个账户](https://developers.redhat.com/register)。然后，转到 [管理页面](https://access.redhat.com/management) 并确保启用了“用于 Red Hat 订阅管理的简化内容访问”。接下来，[创建一个新的激活密钥](https://access.redhat.com/management/activation_keys/new)，选择 “Red Hat 个人开发者订阅”。记下显示的组织 ID。在下面，我们将使用密钥名称和组织 ID 分别表示为 `$KEY_NAME` 和 `$ORGANIZATION_ID`。


现在，我们准备使用 RHEL 内容：



```
$ sudo dnf install subscription-manager
$ sudo subscription-manager register \
    --org $ORGANIZATION_ID --activationkey $KEY_NAME

```

### 使用 RHEL 构建镜像


在之前的示例中，我们通过参数开关指定了所有配置。这对于快速开发很友好，但可能在情况复杂时变得难以处理。RHEL 是一个严肃的发行版，所以让我们改为使用配置文件：



```
$ cat >mkosi.conf <<'EOF'
[Output]
Format=directory
Output=rhel-directory

[Distribution]
Distribution=rhel

[Content]
Packages=
bash
coreutils
util-linux
systemd
systemd-boot
systemd-udev
kernel-core

Bootable=yes
Bootloader=uki
Autologin=yes
WithDocs=no
EOF

```

首先，让我们检查一下一切是否正常：



```
$ mkosi summary

```

现在让我们构建镜像（呃，或者说，目录）：



```
$ mkosi build
$ mkosi qemu

```


```
Welcome to Red Hat Enterprise Linux 9.2 (Plow)!

[ OK ] Created slice Slice /system/modprobe.
[ OK ] Reached target Initrd Root Device.
[ OK ] Reached target Initrd /usr File System.
[ OK ] Reached target Local Integrity Protected Volumes.
[ OK ] Reached target Local File Systems.
[ OK ] Reached target Path Units.
[ OK ] Reached target Remote Encrypted Volumes.
[ OK ] Reached target Remote Verity Protected Volumes.
[ OK ] Reached target Slice Units.
[ OK ] Reached target Swaps.
...
[ OK ] Listening on Journal Socket.
[ OK ] Listening on udev Control Socket.
[ OK ] Listening on udev Kernel Socket.
...
Red Hat Enterprise Linux 9.2 (Plow)
Kernel 5.14.0-284.30.1.el9_2.x86_64 on an x86_64

localhost login: root (automatic login)
[root@localhost ~]#

```

很好，我们将“镜像”构建为一个带有文件系统树的目录，并在虚拟机中引导了它。


在引导的虚拟机中，`findmnt /` 显示根文件系统是 virtiofs。这是一个虚拟文件系统，将主机的目录暴露给客户机。我们其实也可以构建一个更传统的镜像，其中包含文件系统和文件内的分区表，但“目录 + virtiofs” 对于开发来说更快且更友好。


我们刚刚引导的镜像未注册。要允许从镜像“内部”下载更新，我们必须将 `yum` 、`subscription-manager` 和 `NetworkManager` 添加到软件包列表，并在下载任何更新之前以与上述相同的方式调用 `subscription-manager`。在这之后，我们在基本仓库中就有大约 4500 个软件包可用，并且还有一些包含更多专业软件包的额外仓库。


### 最后


今天就是这些了。如果您有问题，可以在 Matrix 上找到我们，地址为 [#mkosi:matrix.org](https://matrix.to/#/#mkosi:matrix.org)，或者在 [systemd 邮件列表](https://lists.freedesktop.org/mailman/listinfo/systemd-devel) 上找到我们。


*（题图：MJ/a6e60316-ed03-4e23-b8b4-22332a0f5bfa）*




---



1. Daan DeMeyer、Lukáš Nykrýn、Michal Sekletár、Zbigiew Jędrzejewski-Szmek [↩︎](#fnref1)





---


via: <https://fedoramagazine.org/create-images-directly-from-rhel-and-rhel-ubi-package-using-mkosi/>


作者：[Zbigniew Jędrzejewski-Szmek](https://fedoramagazine.org/author/zbyszek/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[GlassFoxowo](https://github.com/GlassFoxowo-Dev) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


OK
Mkosi is a lightweight tool to build images from distribution packages. This article describes how to use mkosi to build images with packages from RHEL (Red Hat Enterprise Linux) and RHEL UBI. RHEL Universal Base Image is a subset of RHEL that is freely available without a subscription.

## Mkosi features

Mkosi supports a few output formats, but the most important one is [Discoverable Disk Images](https://uapi-group.org/specifications/specs/discoverable_disk_image/) (DDIs). The same DDI can be used to boot a container, as a virtual machine, copied to a USB stick and used to boot a real machine, and finally copied from the USB stick to the disk to boot from it. The image has a standarized layout and metadata that describes its purpose.

Mkosi relies on other tools to do most of the work: *systemd-repart* to create partitions on a disk image, *mkfs.btrfs*/*mkfs.ext4*/*mkfs.xfs*/… to write the file systems, and *dnf*/*apt*/*pacman*/*zypper* to download and unpack packages.

Mkosi supports a range of distributions: Debian and Ubuntu, Arch Linux, OpenSUSE, and of course Fedora, CentOS Stream and derivatives, and now RHEL UBI and RHEL since the last release. Because the actual “heavy lifting” is done by other tools, mkosi can do cross builds. This is where one distro is used to build images for various other distros. The only requirement is that the appropriate tools are installed on the host. Fedora has native packages for *apt*, *pacman*, and *zypper*, so it provides a good base to use mkosi to build any other distribution.

There are some nifty features: images can be created by an unprivileged user, or in a container without device files, in particular access to loopback devices. It can also launch those images as VMs (using *qemu*) without privileges.

The configuration is declarative and very easy to create. *systemd-repart* is used to create disk partitions, and *repart.d *configuration files are used to define how this should be done.

For more details see two talks by Daan DeMeyer at the All Systems Go conference: [systemd-repart: Building Discoverable Disk Images](https://media.ccc.de/v/all-systems-go-2023-191-systemd-repart-building-discoverable-disk-images) and [mkosi: Building Bespoke Operating System Images](https://media.ccc.de/v/all-systems-go-2023-190-mkosi-building-bespoke-operating-system-images).

## Project goal

One goal for Mkosi is to allow the testing of a software project against various distributions. It will create an image for a distribution (using packages from that distribution) and then compile and install the software project into that image, inserting additional files that are not part of a package. But the first stage, the creation of an image from packages, is useful on its own. This is what we will show first.

We 1 recently added support for RHEL and RHEL UBI. Let’s start with RHEL UBI, just building an image out of distro packages.

Please note that the examples below require mkosi 19, and will not work with earlier versions.

## A basic RHEL UBI image with a shell

$mkdir -p mkosi.cache$mkosi \ -d rhel-ubi \ -t directory \ -p bash,coreutils,util-linux,systemd,rpm \ --autologin

The commands above specify the distribution ‘rhel-ubi’, the output format ‘directory’, and request that packages *bash*, *coreutils*, …, *rpm* be installed. *rpm* isn’t normally needed *inside* of the image, but here it will be useful for introspection. We also enable automatic login as the root user.

Before the build is started, we create the cache directory *mkosi.cache*. When a cache directory is present Mkosi uses it automatically to persist downloaded rpms. This will make subsequent invocations on the same package set much faster.

We can then boot this image as a container using *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

As mentioned earlier, the image can be used to boot a VM. In this setup, it is not possible — our image doesn’t have a kernel. In fact, RHEL UBI doesn’t provide a kernel at all, so we can’t use it to boot (in a VM or on bare metal).

## Creating an image

I also promised an image, but so far we only have a directory. Let’s actually create an image:

$mkosi\-d rhel-ubi \-t disk \-p bash,coreutils,util-linux,systemd,rpm \--autologin

This produces *image.raw*, an disk image with a GPT partition table, and a single root partition (for the native architecture).

$sudo systemd-dissect image.rawName: 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

OK, we have an image, the image has some content from RHEL UBI packages. How do we add our own stuff on top?

## Extending an image with your own files

There are a few ways to extend the image, including compiling something from scratch. But first let’s do something easier and inject a provided file system into the image:

$mkdir -p mkosi.extra/srv/www/content$cat >mkosi.extra/srv/www/content/index.html<<'EOF'<h1>Hello, World!</h1>EOF

The image will now have */srv/www/content/index.html*.

This method is used to inject additional configuration or simple programs.

## Building from source

Now let’s do the full monty and build something from sources. For example, a trivial meson project with a single C file:

$cat >hello.c<<'EOF'#include <stdio.h> int main(int argc, char **argv) { char buf[1024]; FILE *f = fopen("/srv/www/content/index.html", "re"); size_t n = fread(buf, 1, sizeof buf, f); fwrite(buf, 1, n, stdout); fclose(f); return 0; }EOF$cat >meson.build <<'EOF'project('hello', 'c') executable('hello', 'hello.c', install: true)EOF

$cat >mkosi.build <<'EOF'set -ex mkosi-as-caller rm -rf "$BUILDDIR/build" mkosi-as-caller meson setup "$BUILDDIR/build" "$SRCDIR" mkosi-as-caller meson compile -C "$BUILDDIR/build" meson install -C "$BUILDDIR/build" --no-rebuildEOF$chmod +x mkosi.build

To summarize: we have some source code (*hello.c*), a build system configuration (*meson.build*), and a glue script (*mkosi.build*) that is to be invoked by *mkosi*. For a “real” project, we would have the same elements, just more complex.

The script requires some explanation. Mkosi uses user namespaces when creating the image. This allows the package managers (e.g. *dnf*) to install files owned by different users even though it is invoked by a normal unprivileged user. We are using *mkosi-as-caller* to switch back to the calling user to do the compilation. This way, the files created during compilation under $BUILDDIR will be owned by the calling user.

Now let’s build the image with our program. Compared to the previous invocation, we need additional packages: *meson*, *gcc*. Since we now have a build script, mkosi will execute two build stages: first an build image is built, and the build script is invoked in it, and the installation artifacts are stashed in a temporary directory, then a final image is built, and the installation artifacts are injected. (Mkosi sets $DESTDIR, and *meson install* uses $DESTDIR automatically, so the right things happen without us having to specify things explicitly.)

$mkosi \ -d rhel-ubi \ -t disk \ -p bash,coreutils,util-linux,systemd,rpm \ --autologin \ --build-package=meson,gcc \ --build-dir=mkosi.builddir \ --build-script=mkosi.build \ -f

At this point we have the image *image.raw* with a custom payload. We can start our freshly minted executable as a shell command:

$sudo mkosi -d rhel-ubi -t directory shell hello<h1>Hello, World!</h1>

## Obtaining a developer subscription for RHEL

RHEL UBI is intended for use primarily as a base layer for container builds. It has a limited set of packages available (about 1500). Let’s now switch to a full RHEL installation.

The easiest way to get access to RHEL is with a [developer license](https://developers.redhat.com/articles/faqs-no-cost-red-hat-enterprise-linux). It provides an entitlement to register 16 physical or virtual nodes running RHEL, with self-service support.

First, [create an account](https://developers.redhat.com/register). After that, head over to [management](https://access.redhat.com/management) and make sure “Simple content access for Red Hat Subscription Management” is enabled. Then, [create a new activation key](https://access.redhat.com/management/activation_keys/new) with “Red Hat Developer Subscription for Individuals” selected. Make note of the Organization ID that is shown. We’ll refer to the key name and organization ID as $KEY_NAME and $ORGANIZATION_ID below.

Now we are ready to consume RHEL content:

$sudo dnf install subscription-manager$sudo subscription-manager register \--org $ORGANIZATION_ID --activationkey $KEY_NAME

## Building an image using RHEL

In previous examples, we specified all configuration through parameter switches. This is nice for quick development, but can become unwieldy. RHEL is a serious distribution, so let’s use a configuration file instead:

$cat >mkosi.conf <<'EOF'[Output] Format=directory Output=rhel-directory [Distribution] Distribution=rhel [Content] Packages= bash coreutils util-linux systemd systemd-boot systemd-udev kernel-core Bootable=yes Bootloader=uki Autologin=yes WithDocs=noEOF

Let’s first check if everything is kosher:

$mkosi summary

And now let’s build the image (err, directory):

$mkosi build$mkosi qemu

Welcome to Red Hat Enterprise Linux 9.2 (Plow)! [ OK ] Created slice Slice /system/modprobe. [ OK ] Reached target Initrd Root Device. [ OK ] Reached target Initrd /usr File System. [ OK ] Reached target Local Integrity Protected Volumes. [ OK ] Reached target Local File Systems. [ OK ] Reached target Path Units. [ OK ] Reached target Remote Encrypted Volumes. [ OK ] Reached target Remote Verity Protected Volumes. [ OK ] Reached target Slice Units. [ OK ] Reached target Swaps. ... [ OK ] Listening on Journal Socket. [ OK ] Listening on udev Control Socket. [ OK ] Listening on udev Kernel Socket. ... Red Hat Enterprise Linux 9.2 (Plow) Kernel 5.14.0-284.30.1.el9_2.x86_64 on an x86_64 localhost login: root (automatic login) [root@localhost ~]#

Yes, we built the “image” as a directory with a file system tree, and booted it as a virtual machine.

In the booted virtual machine, *findmnt /* shows that the root file systems is virtiofs. This is a virtual file system that exposes a directory from the host to the guest. We *could* build a more traditional image with a partition table and file systems inside of a file, but a directory+virtiofs is quick and nicer for development.

The image that we just booted is not registered. To allow updates to be downloaded from *inside* of the image, we would have to add *yum*, *subscription-manager*, and *NetworkManager* to the package list, and before we download any updates, call *subscription-manager* in the same way as above. Once we do that, we have about 4500 packages at our disposal in the basic repositories, and a few dozen additional repositories with more specialized packages.

## Finally

And that’s all I have for today. If you have questions, find us on Matrix at [#mkosi:matrix.org](https://matrix.to/#/#mkosi:matrix.org) or on the [systemd mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel).

- Daan DeMeyer, Lukáš Nykrýn, Michal Sekletár, Zbigiew Jędrzejewski-Szmek
[↩︎](#a08aaa7d-ee14-4565-80a0-e3d19b21ad26-link)

## You Really Don't Want to Know

This is the best tech article I have seen here … or anywhere else, come to that.

## Peter Boy

Very interesting! When I looked into Mkosi several years ago, I got the impression that it might have lost its momentum. But that is clearly not (or no longer) the case.

I would be very happy if you could contribute a (small or not so small) step-by-step guide to the nspawn section of our Fedora Server Edition documentation, especially about creating foreign distributions (from Fedora’s point of view) nspawn-containers and probably VMs. Would be really great!

## Zbigniew Jędrzejewski-Szmek

Mkosi had a period of stagnation… but it very active now. So far 1258 commits this year, and there are more plans.

https://docs.fedoraproject.org/en-US/fedora-server/containerization/systemd-nspawn-setup/, right? That’s a nice challenge. Mkosi could make some of the things there much easier, for example it’d remove the need to manually configure repositories. I’ll think about it.

## Klaas

How does this compare to osbuild https://github.com/osbuild/osbuild ?

## Zbigniew Jędrzejewski-Szmek

This is a complex issue. Some obvious differences:

– mkosi is tool to be invoked locally, and is essentially a glorified python script. OsBuild is a beast: it a frontend, a backend, a composer, and a worker fleet.

– both mkosi and OsBuild allow selection of packages, so they seem to be quite similar in this regard. In mkosi, the list of “packages” is passed to the tool, so actually the arguments can be package names, file paths, or nevra strings, or virtual provides or “rpm rich dependencies”, groups, i.e. anything that is supported by the underlying package manager. OsBuild seems to be less flexible here.

– OsBuild has custom configuration language for most things: packages, groups, firewall customizations, service enablement, etc. OTOH, mkosi doesn’t have any understanding of those things: the user would generally be expected to just provide the right configuration file themselves. For example, drop in a config file snippet in

mkosi.extradirectory, and then mkosi will push it to the image. This is actually nice, because it’s more flexible, and the user doesn’t have to learn yet-another method to configure things. Of course, this works nicely for systems which are designed to be configured in this way. All systemd components support drop-in files and very simple text config, so this works out nicely. But for some other software this might not be the right approach.– OsBuild has native understanding of containers and is designed to work in this model. Mkosi is more traditional: some local config results in a disk image or a tarball, that’s it.

– Mkosi now does most things without requiring any privileges. I’m not sure about OsBuild.

– With mkosi, it’s fairly easy to customize the output very very precisely. For example, we build initrd images via mkosi.

– Mkosi has native understanding of dm-verity and signatures, and will produce images with signatures. In general, it is intended to make use of systemd features to the maximum possible extent.

With mkosi the focus is on local workflows and flexibility and experimentation with systemd features, while with OsBuild the focus is on reliable builds in a cloud environment. A lot of this is just philosophy and both systems are flexible, so it’s possible that with enough determination, both would allow many different things to be done.

It’d be great if somebody who has experience with mkosi, osbuild, and kiwi, would create an in-depth comparison.

## Dirk Gottschalk

Hello. Really nice article.

I tried mkosi on my system and everytime I try to run it as regular user I get a “permission denied” error. Do I have to add a group membership or something like that?

My Environment: Fedora Workstation 39, login with FreeIPA 4.11

## Zbigniew Jędrzejewski-Szmek

Older mkosi versions (<=14) required root privileges, but Fedora 39 has version 19. Newer versions are mostly supposed to run as an unprivileged user. The only operation that requires root privileges is

mkosi shellormkosi boot, because that usessystemd-nspawn.It’d help if you showed the exact package version, the command that is failing, and the error message.

## Dirk Gottschalk

I am using mkosi version 19.

I get this error message:

mkosi \

-d rhel-ubi \

-t directory \

-p bash,coreutils,util-linux,systemd,rpm \

–autologin

Traceback (most recent call last):

File “/usr/bin/mkosi”, line 8, in

sys.exit(main())

^^^^^^

File “/usr/lib64/python3.12/contextlib.py”, line 81, in inner

return func(*args, **kwds)

^^^^^^^^^^^^^^^^^^^

File “/usr/lib/python3.12/site-packages/mkosi/__main__.py”, line 52, in main

run_verb(args, images)

File “/usr/lib/python3.12/site-packages/mkosi/__init__.py”, line 2801, in run_verb

if not needs_build(args, config) and args.verb != Verb.clean:

^^^^^^^^^^^^^^^^^^^^^^^^^

File “/usr/lib/python3.12/site-packages/mkosi/__init__.py”, line 2582, in needs_build

(args.force > 0 or not (config.output_dir_or_cwd() / config.output_with_compression).exists())

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

File “/usr/lib64/python3.12/pathlib.py”, line 861, in exists

self.stat(follow_symlinks=follow_symlinks)

File “/usr/lib64/python3.12/pathlib.py”, line 841, in stat

return os.stat(self, follow_symlinks=follow_symlinks)

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

PermissionError: [Errno 13] Permission denied: ‘/home/dgottschalk/osi/image’

## Zbigniew Jędrzejewski-Szmek

It looks like it fails when trying to access this file. You probably have a left-over files owned by root (or some other user).

## unscramble words

Hey Zbigniew, great walkthrough on building RHEL and RHEL UBI images with mkosi! Love the flexibility mkosi offers, especially for cross builds. Excited to see the impact on software testing across different distros!