使用 mkosi 構(gòu)建 RHEL 和 RHEL UBI 鏡像
mkosi 是一個輕量級工具,用于從發(fā)行版軟件包構(gòu)建鏡像。
mkosi 特性
mkosi 支持一些輸出格式,但最重要的是 可發(fā)現(xiàn)磁盤鏡像Discoverable Disk Images(DDI)。同一個 DDI 可用于引導容器、或運行在虛擬機、抑或是復制到 U 盤以引導真實物理機,然后從 U 盤復制到磁盤以引導系統(tǒng)。該鏡像具有標準化的布局和描述其用途的元數(shù)據(jù)。
mkosi 依賴其他工具來完成大部分工作:使用 systemd-repart
在磁盤鏡像上創(chuàng)建分區(qū),使用 mkfs.btrfs
/ mkfs.ext4
/ mkfs.xfs
等創(chuàng)建文件系統(tǒng),并使用 dnf
/ apt
/ pacman
/ zypper
下載和解壓包。
mkosi 支持一系列發(fā)行版:Debian 和 Ubuntu、Arch Linux、openSUSE,當然還包括
Fedora、CentOS Stream 及其衍生版本,以及最近的 RHEL UBI 和 RHEL
。由于實際的“重活”是由其他工具完成的,mkosi
可以進行交叉構(gòu)建。這意味著可以使用一個發(fā)行版構(gòu)建各種其他發(fā)行版的鏡像。唯一的要求是主機上安裝了相應的工具。Fedora 有原生的 apt
、pacman
和 zypper
,因此它為使用 mkosi 構(gòu)建任何其他發(fā)行版提供了良好的基礎(chǔ)。
它還有一些有趣的功能:鏡像可以由非特權(quán)用戶創(chuàng)建,或者在沒有設備文件的容器中創(chuàng)建,特別是沒有對回環(huán)設備的訪問權(quán)限。它還可以在沒有特權(quán)的情況下將這些鏡像啟動為虛擬機(使用 qemu
)。
配置是聲明性的,非常容易創(chuàng)建。使用 systemd-repart
創(chuàng)建磁盤分區(qū),并使用 repart.d
配置文件定義應該如何完成此操作。
有關(guān)更多詳細信息,請參見 Daan DeMeyer 在 All Systems Go 大會上的兩個演講:《systemd-repart: Building Discoverable Disk Images》 和 《mkosi: Building Bespoke Operating System Images》。
項目目標
mkosi 的一個目標是允許對軟件項目進行針對不同發(fā)行版的測試。它將為一個發(fā)行版創(chuàng)建一個鏡像(使用該發(fā)行版的軟件包),然后將軟件項目編譯并安裝到該鏡像中,插入不屬于軟件包的額外文件。但是,首個階段,即從軟件包創(chuàng)建鏡像的過程,本身就是有用的。這是我們將首先展示的內(nèi)容。
我們 [1] 最近添加了對 RHEL 和 RHEL UBI 的支持。讓我們從 RHEL UBI 開始,利用發(fā)行版軟件包創(chuàng)建鏡像。
請注意,下面的示例要求 mkosi 19,而且不適用于更早的版本。
帶有 Shell 的基本 RHEL UBI 鏡像
$ mkdir -p mkosi.cache
$ mkosi \
-d rhel-ubi \
-t directory \
-p bash,coreutils,util-linux,systemd,rpm \
--autologin
上面的命令指定了發(fā)行版 rhel-ubi
,輸出格式 directory
,并請求安裝軟件包 bash
、coreutils
、…、rpm
。rpm
通常不需要放到鏡像內(nèi)部,但在這里用于內(nèi)省會很有用。我們還啟用了以 root 用戶自動登錄。
在啟動構(gòu)建之前,我們創(chuàng)建了緩存目錄 mkosi.cache
。當存在緩存目錄時,mkosi 會自動使用它來持久化下載的 RPM 包。這將使相同軟件包集合的后續(xù)調(diào)用速度更快。
然后,我們可以使用 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
正如前面提到的,此鏡像可以用于啟動虛擬機。但在此設置下,這是不可能的 —— 我們的鏡像沒有內(nèi)核。事實上,RHEL UBI 根本不提供內(nèi)核,因此我們無法使用它進行引導(無論是在虛擬機上還是在裸機上)。
創(chuàng)建鏡像
我一開始說是要創(chuàng)建鏡像,但到目前為止我們只有一個目錄。讓我們開始實際創(chuàng)建一個鏡像:
$ mkosi \
-d rhel-ubi \
-t disk \
-p bash,coreutils,util-linux,systemd,rpm \
--autologin
這將生成 image.raw
,一個帶有 GPT 分區(qū)表和單個根分區(qū)(用于本機架構(gòu))的磁盤鏡像。
$ 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
好的,我們現(xiàn)在有一個鏡像,鏡像中包含了一些來自 RHEL UBI 軟件包的內(nèi)容。我們?nèi)绾卧谄渖霞狱c我們自己的東西呢?
使用自己的文件擴展鏡像
有幾種方法可以擴展鏡像,包括從頭開始編譯某些東西。但在那之前,讓我們做一些更簡單的事情,將一個現(xiàn)成的文件系統(tǒng)注入到鏡像中:
$ mkdir -p mkosi.extra/srv/www/content
$ cat >mkosi.extra/srv/www/content/index.html <<'EOF'
<h1>Hello, World!</h1>
EOF
現(xiàn)在,該鏡像將包含 /srv/www/content/index.html
。
這種方法用于注入額外的配置或簡單的程序。
從源代碼構(gòu)建
現(xiàn)在讓我們過一遍完整流程,從源代碼構(gòu)建一些東西。例如,一個簡單的 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
總結(jié)一下:我們有一些源代碼(hello.c
),一個構(gòu)建系統(tǒng)配置文件(meson.build
),以及一個由 mkosi 調(diào)用的膠水腳本(mkosi.build
)。對于實際的項目,也會有相同的元素,只是更加復雜。
這個腳本需要一些解釋。mkosi 在創(chuàng)建鏡像時使用用戶命名空間。這允許包管理器(例如 dnf
)安裝由不同用戶擁有的文件,即使它是由一個普通非特權(quán)用戶調(diào)用的。我們使用 mkosi-as-caller
切換回調(diào)用者以進行編譯。這樣,在 $BUILDDIR
下編譯期間創(chuàng)建的文件將由調(diào)用者擁有。
現(xiàn)在讓我們使用我們的程序構(gòu)建鏡像。與之前的調(diào)用相比,我們需要額外的軟件包:meson
、gcc
。由于我們現(xiàn)在有了構(gòu)建腳本,mkosi 將執(zhí)行兩個構(gòu)建階段:首先創(chuàng)建一個構(gòu)建鏡像,并在其中調(diào)用構(gòu)建腳本,將安裝產(chǎn)物存儲在一個臨時目錄中,然后構(gòu)建最終鏡像,并將安裝產(chǎn)物注入其中。(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
。我們可以啟動我們新創(chuàng)建的可執(zhí)行文件作為 shell 命令:
$ sudo mkosi -d rhel-ubi -t directory shell hello
<h1>Hello, World!</h1>
獲取 RHEL 的開發(fā)者訂閱
RHEL UBI 主要用作容器構(gòu)建的基礎(chǔ)層。它提供了有限的軟件包(約 1500 個)?,F(xiàn)在讓我們切換到完整的 RHEL 安裝。
獲取 RHEL 的最簡單方法是使用 開發(fā)者許可證。它提供了權(quán)限注冊 16 個運行 RHEL 的物理或虛擬節(jié)點,并提供自助式支持。
首先,創(chuàng)建一個賬戶。然后,轉(zhuǎn)到 管理頁面 并確保啟用了“用于 Red Hat 訂閱管理的簡化內(nèi)容訪問”。接下來,創(chuàng)建一個新的激活密鑰,選擇 “Red Hat 個人開發(fā)者訂閱”。記下顯示的組織 ID。在下面,我們將使用密鑰名稱和組織 ID 分別表示為 $KEY_NAME
和 $ORGANIZATION_ID
。
現(xiàn)在,我們準備使用 RHEL 內(nèi)容:
$ sudo dnf install subscription-manager
$ sudo subscription-manager register \
--org $ORGANIZATION_ID --activationkey $KEY_NAME
使用 RHEL 構(gòu)建鏡像
在之前的示例中,我們通過參數(shù)開關(guān)指定了所有配置。這對于快速開發(fā)很友好,但可能在情況復雜時變得難以處理。RHEL 是一個嚴肅的發(fā)行版,所以讓我們改為使用配置文件:
$ 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
現(xiàn)在讓我們構(gòu)建鏡像(呃,或者說,目錄):
$ 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 ~]#
很好,我們將“鏡像”構(gòu)建為一個帶有文件系統(tǒng)樹的目錄,并在虛擬機中引導了它。
在引導的虛擬機中,findmnt /
顯示根文件系統(tǒng)是 virtiofs。這是一個虛擬文件系統(tǒng),將主機的目錄暴露給客戶機。我們其實也可以構(gòu)建一個更傳統(tǒng)的鏡像,其中包含文件系統(tǒng)和文件內(nèi)的分區(qū)表,但“目錄 + virtiofs” 對于開發(fā)來說更快且更友好。
我們剛剛引導的鏡像未注冊。要允許從鏡像“內(nèi)部”下載更新,我們必須將 yum
、subscription-manager
和 NetworkManager
添加到軟件包列表,并在下載任何更新之前以與上述相同的方式調(diào)用 subscription-manager
。在這之后,我們在基本倉庫中就有大約 4500 個軟件包可用,并且還有一些包含更多專業(yè)軟件包的額外倉庫。
最后
今天就是這些了。如果您有問題,可以在 Matrix 上找到我們,地址為 #mkosi:matrix.org,或者在 systemd 郵件列表 上找到我們。