TranslateProject/translated/tech/20231124 Building RHEL and RHEL UBI images with mkosi.md
2023-11-26 01:54:44 +08:00

15 KiB
Raw Blame History

使用 mkosi 构建 RHEL 和 RHEL UBI 镜像

图片: Saad Salim 发布于 Unsplash

mkosi 是一个轻量级工具,用于从发行包构建镜像。本文介绍如何使用 mkosi 从 RHEL(Red Hat Enterprise Linux) 和RHEL 通用基础镜像Universal Base ImageUBI 的软件包构建镜像。RHEL UBI 是 RHEL的一个子集可以在没有订阅的情况下免费使用。

mkosi 特性

mkosi 支持一些输出格式,但最重要的是 可发现磁盘镜像Discoverable Disk ImagesDDI。相同的 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 对于 aptpacmanzypper 有本机包,因此它为使用 mkosi 构建任何其他发行版提供了良好的基础。

还有一些巧妙的功能:镜像可以由非特权用户创建,或者在没有设备文件的容器中创建,特别是没有对回环设备的访问权限。它还可以在没有特权的情况下将这些镜像启动为虚拟机(使用 qemu)。

配置是声明性的,非常容易创建。使用 systemd-repart 创建磁盘分区,并使用 repart.d 配置文件定义应该如何完成此操作。

有关更多详细信息,请参见 Daan DeMeyer 在 All Systems Go 大会上的两个演讲:systemd-repart: Building Discoverable Disk Imagesmkosi: 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',并请求安装软件包 bashcoreutils、…、rpmrpm 通常不需要在镜像内部,但在这里用于内省会很有用。我们还启用了以 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 下编译期间创建的文件将由调用者拥有。

现在让我们使用我们的程序构建镜像。与之前的调用相比,我们需要额外的软件包:mesongcc。由于我们现在有了构建脚本mkosi 将执行两个构建阶段首先创建一个构建镜像并在其中调用构建脚本将安装产物存储在一个临时目录中然后构建最终镜像并将安装产物注入其中。mkosi 设置 $DESTDIRmeson 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 的最简单方法是使用 开发者许可证。它提供了权限注册 16 个运行 RHEL 的物理或虚拟节点,并提供自助式支持。

首先,创建一个账户。然后,转到 管理页面 并确保启用了“用于 Red Hat 订阅管理的简化内容访问”。接下来,创建一个新的激活密钥,选择“ 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 对于开发来说更快且更友好。

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

最后

今天就是这些了。如果您有问题,可以在 Matrix 上找到我们,地址为 #mkosi:matrix.org,或者在 systemd 邮件列表 上找到我们。

--

  1. Daan DeMeyer、Lukáš Nykrýn、Michal Sekletár、Zbigiew Jędrzejewski-Szmek ↩︎

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

作者:Zbigniew Jędrzejewski-Szmek 选题:lujun9972 译者:GlassFoxowo 校对:校对者ID

本文由 LCTT 原创编译,Linux中国 荣誉推出