Debian で Linux Kernel の initramfs の圧縮形式を変更する🐧🗜️

先日「Debian sidでLinux Kernel(5.11)をbuildするメモ 」を書いたのですが，Twitterで@henrichさんからこんなメッセージが．

/bootの容量のためにはinitramfsの圧縮をgzipからxzやzstdにするのもありですね

— henrich (@henrich) March 23, 2021

そういえば圧縮形式色々あったなと試してみました．

現在の圧縮形式を確認する

$ grep COMPRESS /etc/initramfs-tools/initramfs.conf
# COMPRESS: [ gzip | bzip2 | lz4 | lzma | lzop | xz | zstd ]
COMPRESS=gzip

$ file --mime-type /boot/initrd.img-`uname -r`
/boot/initrd.img-5.11.8+: application/gzip

設定は gzip で，実際のファイルもgzipになっています．
圧縮形式は gzip 以外に bzip2, lz4, lzma, lzop, xz, zstd が選べるようです．

圧縮形式の比較

$ ls -1sS --block-size=1 ./initrd.img-5.11.8+.*
183033856 ./initrd.img-5.11.8+.raw
81076224 ./initrd.img-5.11.8+.lz4
78315520 ./initrd.img-5.11.8+.lzop
54329344 ./initrd.img-5.11.8+.gzip
51761152 ./initrd.img-5.11.8+.zstd
50081792 ./initrd.img-5.11.8+.bz2
36548608 ./initrd.img-5.11.8+.xz
36540416 ./initrd.img-5.11.8+.lzma

容量がどのくらいになるのか各アーカイバで圧縮してみました．

$ \time -f %e cat ./initrd.img-5.11.8+.raw > /dev/null
0.03
$ \time -f %e lz4cat ./initrd.img-5.11.8+.lz4 > /dev/null
0.38
$ \time -f %e lzop -d -c ./initrd.img-5.11.8+.lzop > /dev/null
0.42
$ \time -f %e zstdcat ./initrd.img-5.11.8+.zstd > /dev/null
0.44
$ \time -f %e zcat ./initrd.img-5.11.8+.gzip > /dev/null
1.38
$ \time -f %e lzma -d -c ./initrd.img-5.11.8+.lzma > /dev/null
3.22
$ \time -f %e xzcat ./initrd.img-5.11.8+.xz > /dev/null
3.37
$ \time -f %e bzcat ./initrd.img-5.11.8+.bz2 > /dev/null
9.39

展開時間も確認してみました．

圧縮形式 | 圧縮データ量(byte) | 展開時間(秒)

raw

183033856

0.03

lz4

81076224

0.38

lzop

78315520

0.43

gzip

54329344

1.38

zstd

51761152

0.44

bzip2

50081792

9.39

xz

36548608

3.37

lzma

36540416

3.22

圧縮データの圧縮率などは既定値のままで，展開時間は \time -f %e zcat ./initrd.img-5.11.8+.gzip > /dev/null 若しくは \time -f %e lzma -d -c ./initrd.img-5.11.8+.lzma > /dev/null のようにして測りました．

サイズでは raw > lz4 ≒ lzop > gzip ≒ zstd ≒ bz2 > xz ≒ lzma な感じで，
展開速度は bz2 > xz ≒ lzma > gzip > zstd ≒ lzop ≒ lz4 > raw な感じです．

Caution

時間についてはデスクトップ環境で色々動いている状態で1回しか測っていないので目安程度にしてください．

Caution

ここでは既定値で圧縮しましたが，zstd, lz4, xzについてはinitramfs作成時に圧縮オプションなどが既定値と違うようなので目安程度にしてください．

容量的にはxz, lzmaが良さそうですが展開時間がとても増えそうです．zstdはgzipより速くて小さくなって良さそうです．
ということでzstdにしてみます．

initramfsを zstd で作り直す

$ sudo git -C /etc diff HEAD /etc/initramfs-tools/initramfs.conf
diff --git a/initramfs-tools/initramfs.conf b/initramfs-tools/initramfs.conf
index 01bdd85..a0c051b 100644
--- a/initramfs-tools/initramfs.conf
+++ b/initramfs-tools/initramfs.conf
@@ -41,7 +41,7 @@ KEYMAP=n
# COMPRESS: [ gzip | bzip2 | lz4 | lzma | lzop | xz | zstd ]
#

-COMPRESS=gzip
+COMPRESS=zstd

#
# DEVICE: ...

/etc/initramfs-tools/initramfs.conf を編集して， COMPRESS を zstd に変更します．

$ update-initramfs -h

Usage: update-initramfs {-c|-d|-u} [-k version] [-v] [-b directory]

Options:
-k version Specify kernel version or 'all'
-c Create a new initramfs
-u Update an existing initramfs
-d Remove an existing initramfs
-b directory Set alternate boot directory
-v Be verbose

See update-initramfs(8) for further details.

$ sudo update-initramfs -k `uname -r` -u -v

update-initramfs で更新を行います．

$ ls -s --block-size=1 /boot/initrd.img-5.11.8+
54542336 /boot/initrd.img-5.11.8+
$ ls -s --block-size=1 /boot/initrd.img-5.11.8+
41611264 /boot/initrd.img-5.11.8+
$ file --mime-type /boot/initrd.img-5.11.8+
/boot/initrd.img-5.11.8+: application/zstd
$ zstd -lv /boot/initrd.img-5.11.8+
*** zstd command line interface 64-bits v1.4.8, by Yann Collet ***
/boot/initrd.img-5.11.8+
# Zstandard Frames: 1
Window Size: 8.00 MB (8388608 B)
Compressed Size: 39.53 MB (41446437 B)
Decompressed Size: 174.55 MB (183032320 B)
Ratio: 4.4161
Check: XXH64

出来上がったファイルを確認すると zstd になっていました．
再起動しても問題ありませんでした :)
上で確認した容量より大分小さ唸っているのは圧縮レベルの差でしょうか?

initramfs圧縮レベルの確認

$ grep zstd /usr/sbin/mkinitramfs
zstd) compress="zstd -q -19 -T0" ;;

update-initramfs から呼ばれる mkinitramfs を確認するとzstdの圧縮レベルオプションは -19 で最高圧縮率でした．(既定値は -3 )

$ \time -f%e zstdcat -T1 /boot/initrd.img-5.11.8+ > /dev/null
0.50

その分展開時間も少し伸びていそうです．

case "${compress}" in
gzip) # If we're doing a reproducible build, use gzip -n
if [ -n "${SOURCE_DATE_EPOCH}" ]; then
compress="gzip -n"
# Otherwise, substitute pigz if it's available
elif command -v pigz >/dev/null; then
compress=pigz
fi
;;
lz4) compress="lz4 -9 -l" ;;
zstd) compress="zstd -q -19 -T0" ;;
xz) compress="xz --check=crc32"
# If we're not doing a reproducible build, enable multithreading
test -z "${SOURCE_DATE_EPOCH}" && compress="$compress --threads=0"
;;
bzip2|lzma|lzop)
# no parameters needed
;;
*) echo "W: Unknown compression command ${compress}" >&2 ;;
esac

周りを見てみると， lz4 は -9 で最高( -12 )ではないけど既定値( 1 )より圧縮率を高くしてあります．xzには整合性チェックがオプションがあるけど容量にはあまり関係ないかな?
その他は既定値のようです．

$ count=1;while [[ $count -lt 19 ]];do zstdcat /boot/initrd.img-5.11.8+ | zstd -T0 -$count -c - > /tmp/zstd-$count.zstd;count=$(( $count+1 ));done
$ ls -1sS --block-size=1 /tmp/zstd-*
56188928 /tmp/zstd-1.zstd
53272576 /tmp/zstd-2.zstd
51761152 /tmp/zstd-3.zstd
51466240 /tmp/zstd-4.zstd
50061312 /tmp/zstd-5.zstd
49614848 /tmp/zstd-6.zstd
47431680 /tmp/zstd-7.zstd
46690304 /tmp/zstd-8.zstd
46424064 /tmp/zstd-9.zstd
45973504 /tmp/zstd-10.zstd
45850624 /tmp/zstd-11.zstd
45686784 /tmp/zstd-12.zstd
45539328 /tmp/zstd-13.zstd
45371392 /tmp/zstd-14.zstd
45309952 /tmp/zstd-15.zstd
44093440 /tmp/zstd-16.zstd
42987520 /tmp/zstd-17.zstd
41705472 /tmp/zstd-18.zstd
41447424 /tmp/zstd-19.zstd
$ ls -1S /tmp/zstd-* | xargs -I{} -n1 sh -c "echo -n \"{} \"; \time -f%e zstdcat -T0 {} > /dev/null"
/tmp/zstd-1.zstd 0.37
/tmp/zstd-2.zstd 0.36
/tmp/zstd-3.zstd 0.40
/tmp/zstd-4.zstd 0.47
/tmp/zstd-5.zstd 0.44
/tmp/zstd-6.zstd 0.42
/tmp/zstd-7.zstd 0.40
/tmp/zstd-8.zstd 0.37
/tmp/zstd-9.zstd 0.35
/tmp/zstd-10.zstd 0.40
/tmp/zstd-11.zstd 0.41
/tmp/zstd-12.zstd 0.38
/tmp/zstd-13.zstd 0.39
/tmp/zstd-14.zstd 0.39
/tmp/zstd-15.zstd 0.43
/tmp/zstd-16.zstd 0.37
/tmp/zstd-17.zstd 0.42
/tmp/zstd-18.zstd 0.53
/tmp/zstd-19.zstd 0.68

圧縮レベルごとの容量と展開時間はこんな感じになりました．色々動いている環境なのでおかしそうなところがありますが大体の目安として．

Kernel build時の動作

Kernel 5.11.9 が来ていたのでビルドしてみました．

$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/incr/patch-5.11.8-9.xz
$ cd linux-5.11
$ xzcat ../patch-5.11.8-9.xz | patch -p1
$ grep ^COMPRESS= /etc/initramfs-tools/initramfs.conf
COMPRESS=zstd
$ time make -j`nproc` bindeb-pkg
$ ls -1sS ../*5.11.9*
737300 ../linux-image-5.11.9+-dbg_5.11.9+-5_amd64.deb
59584 ../linux-image-5.11.9+_5.11.9+-5_amd64.deb
8040 ../linux-headers-5.11.9+_5.11.9+-5_amd64.deb
1124 ../linux-libc-dev_5.11.9+-5_amd64.deb
8 ../linux-5.11.9+_5.11.9+-5_amd64.buildinfo
4 ../linux-5.11.9+_5.11.9+-5_amd64.changes
$ sudo apt install ../linux-image-5.11.9+_5.11.9+-5_amd64.deb ../linux-headers-5.11.9+_5.11.9+-5_amd64.deb ../linux
-libc-dev_5.11.9+-5_amd64.deb
$ file /boot/initrd.img-5.11.9+
/boot/initrd.img-5.11.9+: Zstandard compressed data (v0.8+), Dictionary ID: None
$ ls --block-size=1 -s /boot/initrd.img-5.11.9+
41602048 /boot/initrd.img-5.11.9+
$ sudo shutdown -r now 'kernel upgrade'

問題なく zstd になりました．

参考URL

Debianのinitramfsの圧縮形式をzstdにしようというバグレポート．bullseyeはフリーズされているので入らない．

#976054 – initramfs-tools: [RFC] Compress initramfs file with zstd – Debian Bug report logs

環境

$ dpkg-query -W initramfs-tools-core zstd
initramfs-tools-core 0.140
zstd 1.4.8+dfsg-2.1
$ lsb_release -dr
Description: Debian GNU/Linux bullseye/sid
Release: unstable
$ uname -srm
Linux 5.11.9+ x86_64