Linuxシステムを一つのファイルに閉じ込める(UKI, USI)
背景
Linux のブートプロセスは次のようになっている。(UEFI環境として)UEFI→ブートローダ(grubなど)→カーネル(vmlinuzなど)→initrd/initramfs→init(systemdなど)。
ところで、Linux ディストリビューションのディスクイメージを作成できるツールdebootstrap、 mmdebstrap(どちらもdebianのディスクイメージを作成できる)や様々なディストリビューションのディスクイメージを作成できるmkosiを知った。
特にmkosiでは上記のブートプロセスで登場するinitramfsやvmlinuzとカーネルオプションを一つのファイルにまとめたUKI (Unified Kernel Image) または USI (Unified System Image) ファイルを出力する機能があり、そこに指定したパッケージをインストールすることができそう。
そこでカーネル+initramfsのUKI/USIでどこまでの機能を盛り込むことができるのかmkosiでファイルを出力し、VMやUSBメモリに書き込み実際にPCでの起動を試してみた。
ちなみにUKI/USIの本来の主な目的はセキュリティの向上やセキュリティ向上のプロセスの簡略化のようだ。一つのファイルにシステムを収められ、セキュアブート用の署名が可能という効果となる。
他には、複数のVMや物理PCに同じ環境を手軽に再現するために使用できる。ディストリビューションが公開しているISOファイルからの起動でも近いことはできるが、あらかじめ設定を入れることや特定の選択したアプリケーションのインストールなどはできない。
ディスクイメージの作成
実験環境はDebian Trixie
mkosi でディスクイメージを作成するが、使い方の細かい点については参考URLを参照したほうがわかると思うのでここでは自分が試した設定ファイル、コマンドをリストする。
uvとmkosiのインストール
# uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# 画面の指示に従う
# mkosi
uv tool install git+https://github.com/systemd/mkosi.git
mkosi の設定ファイル
本来のUKIの目的を達成するにはイメージ作成時に署名を追加する。(未調査)
[Distribution]
Distribution=debian
Release=bookworm # Trixie ではファイル出力途中でエラーになってしまった
[Content]
Keymap=jp # 環境に合わせる
Timezone=Asia/Tokyo # 入れておかないと、起動時に聞かれるのであらかじめ設定
Locale=en_US.UTF-8 # 同上
MakeInitrd=no # 完全なシステムとして認識させる (Unified System Image)
RootPassword=root # 動作確認なので簡単なパスワード
KernelCommandLine=rw console=ttyS0,115200 console=tty0
# console=ttyS0 を指定するとSSH経由でQemuを起動しているときに起動している様子がみられる
Packages=
base
linux-image-amd64
systemd
systemd-boot
# ここまでは起動に必須だった
# 以下からCLIツール、GUI環境など
vim
task-gnome-desktop
[Output]
Format=uki
mkosi でイメージファイルの作成
mkdir mkosi.cache # パッケージファイルなどをキャッシュとして保存しておいてくれる
mkosi -f # -f で既に作成済みでも上書きしてくれる
ls
# image image.initrd mkosi.cache mkosi.tools
# image.efi image.vmlinuz mkosi.conf mkosi.tools.manifest
image.efi, image.initrd, image.vmlinuzなどが作成されている。image.efiのサイズは685MiBであった。(ls -lh)
UKI/USI の起動
QemuでVMとして起動する
# gnome をインストールし、メモリ4Gの割り当てでは起動しなかった
# 8G割り当てでは起動した
# -bios で指定しているのはUEFIのイメージ
# -kernel で作成されたimage.efi を指定する
qemu-system-x86_64 -m 8G -machine q35 -enable-kvm -bios /usr/share/ovmf/OVMF.fd -kernel ./image.efi -cpu host
# SSH などで操作している場合、-nographic 等をつけるとコンソール出力がみれる
# ただし上記でインストールしたgnomeの様子はみれない
qemu-system-x86_64 -m 8G -machine q35 -enable-kvm -bios /usr/share/ovmf/OVMF.fd -kernel ./image.efi -cpu host -nographic
画面(VGA出力):

上記の設定だけだとgnomeのログイン画面からログインできなかった。コンソールからユーザを作成したところ、そのユーザでログインができた。
PC実機で起動する
作成したEFIファイル(USI)をPCにUSBメモリから起動できるか確認した。
qemu の例では便利な-kernelオプションがあったため作成したファイルを指定するだけで起動ができた。しかし、実際のUEFIシステムのPCではESP(EFI System Partition)が必要になる。
USBメモリのパーティション操作が簡単になるようにrawディスクイメージを作成し、Rufusで書き込む方式にした。
(実験をした環境ではVMのLinuxマシンしかなく、メインで使用しているのがWindowsのため)
# ブランクのrawディスクイメージファイルを作成
# mkosi で出力されたファイルのサイズが685MiBだったので700MiBのディスクサイズにする
# つまりこのサイズ以上のUSBメモリを用意する必要がある
fallocate -l 700M disk.raw
# ESP の作成
# 全領域を使用してパーティションタイプにef00を指定する
# (GUIDはC12A7328-F81F-11D2-BA4B-00A0C93EC93B)
sudo sgdisk -n "0::" -t "0:ef00" ./disk.raw
sudo sgdisk -p ./disk.raw
# Disk ./disk.raw: 1433600 sectors, 700.0 MiB
# Sector size (logical): 512 bytes
# Disk identifier (GUID): 98997B20-1A83-4C13-BA11-80B2824B5750
# (中略)
#
# Number Start (sector) End (sector) Size Code Name
# 1 2048 1433566 699.0 MiB EF00
sudo blkid ./disk.raw
# ./disk.raw: PTUUID="98997b20-1a83-4c13-ba11-80b2824b5750" PTTYPE="gpt"
# block(loop)デバイスとして認識させる
sudo losetup -fP ./disk.raw
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
# loop0 7:0 0 700M 0 loop
# └─loop0p1 259:0 0 699M 0 part
# 作成したパーティションをFAT32にフォーマットする
sudo mkfs.fat -F 32 /dev/loop0p1
# mkfs.fat 4.2 (2021-01-31)
# パーティションをマウントする
sudo mount /dev/loop0p1 /mnt
# ブートに必要なディレクトリを作成する
sudo mkdir -p /mnt/EFI/BOOT
# mkosi で作成したefiファイルをディスクイメージ内にコピーする
sudo cp ./image.efi /mnt/EFI/BOOT/BOOTX64.EFI
# アンマウントとloopデバイスの削除
sudo umount /mnt
sudo losetup -d loop0
このようにして作成したdisk.raw ファイルをRufusで出力先をUSBメモリとして書き込んだ。
ファイル選択ウインドウで.rawファイルは初期状態で表示されないのでファイルの種類ですべてのファイルを選択しておく必要がある。
ファイルを書き込んだUSBメモリをセキュアブートを無効にしたPCに接続し、電源をいれると無事に起動した。画面のイメージは上のQemuのものとほとんど変わらないので省略する。
参考URL
How Linux Kernel Boots? - GeeksforGeeks
The Linux Boot Process: A Comprehensive Guide — linuxvox.com
Unified Kernel Image | UAPI Group Specifications
Uki_addons - Uki_addons_O97iYns.pdf
systemd/mkosi: 💽 Build Bespoke OS Images
mkosi(1) — Arch manual pages
EFI システムパーティション - ArchWiki
sgdisk(gdisk コマンドライン版) でまとめて変更する使い方 - それ ...
sgdisk (8) - Linux man page
Building USIs with mkosi ⥢ OVERHEAD
Discussion