Raspberry Pi 4 で OverlayFS を併用した Read-Only な rootfs を構築する
背景
組込み機器では、セキュリティやストレージデバイスの摩耗を抑えるなどといった観点からルートファイルシステムを読み取り専用にすることがある。
Raspberry Pi OS では、raspi-configで overlayroot によってルートファイルシステムを読み取り専用に変更することができる。
overlayroot では、既存のルートファイルシステムに OverlayFS を導入しやすくするのツールの一つである。 このパッケージによって読み取り専用となったルートファイルシステムに対して、tmpfsのような一時ファイルシステムを上位のレイヤに追加することで、達成することができる。

Linux では、読み取り専用ファイルシステムがいくつかサポートしている。 こういったファイルシステムは、読み取り専用に設計されているため、圧縮や重複除去といった機能がサポートされている。 Android では、SquashFS(Android 9 以前)、EROFS をシステムパーティションとして利用している。
本稿では、Raspberry Pi 4 のルートファイルシステムを読み取り専用に変更し、そのうえで SquashFS や EROFS として作成することを目指す。
実行環境
Raspberry Pi 4 は microSDカード経由でRaspberry Pi OSを起動させる。

ここで使用するRaspberry Pi 4のスペックについて、必要な情報だけ抜粋したものを下記に示す。
| 項目 | Raspberry Pi 4 |
|---|---|
| CPU | Cortex-A72 (ARM v8) 1.5GHz |
| メモリ | 4GB LPDDR4-3200 |
| OS | Raspberry Pi OS (Mar 15th 2024) |
| Linux kernel | 6.6.261 |
| micro SD card | KTHN-MW016G |
ファイルシステムを Read-Only化する
raspi-configから Non-Interactiveモードで OverlayFS による Read-Only な rootfs を有効化する。(必要であれば追加パッケージのインストールされる)pi@raspberrypi:~$ sudo raspi-config nonint enable_overlayfsシステムを再起動する
pi@raspberrypi:~$ sudo systemctl reboot再起動後にシステムのマウント状況を確認する
pi@raspberrypi:~$ mount | grep -E 'mmc|root-ro|root-rw' /dev/mmcblk0p2 on /media/root-ro type ext4 (ro,relatime) tmpfs-root on /media/root-rw type tmpfs (rw,relatime) overlayroot on / type overlay (rw,relatime,lowerdir=/media/root-ro,upperdir=/media/root-rw/overlay,workdir=/media/root-rw/overlay-workdir/_,uuid=on) /dev/mmcblk0p1 on /boot/firmware type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)
読み取り専用ファイルシステムを使用する
ルートファイルシステムをSquashFSに変更する
SquashFS は 読み取り専用の圧縮ファイルシステムとなっており、ファイルやディレクトリ、それらメタデータを圧縮することができる。(gzip/xz/lzo/zstdなどがサポートされている)
SquashFSファイルシステムを作成するためには、mksquashfs プログラムを使用する必要がある。 mksquashfs では、圧縮アルゴリズムの選択や作成されるファイルシステムのメタ情報を調整することができる。
ここでは、デフォルトのパラメータ(gzipによる圧縮)でSquashFSファイルシステムを作成する。
Raspberry Pi OS の SDメモリカードのイメージをバックアップする
$ sudo dd if=/dev/sdd1 of=bootfs.img $ sudo dd if=/dev/sdd2 of=rootfs.img-
$ sudo mksquashfs /mnt/ rootfs_gzip.sfs 作成したSquashFSファイルシステムを SDメモリカードに書き込む
$ sudo dd if=rootfs_gzip.sfs of=/dev/sdd2
また、overlayrootのインストールによって、カーネルコマンドラインにも変更が加わっている。
SquashFSでマウントするように次のような修正をする。
// 1: --- cmdline.txt.orig 2024-05-05 17:51:26.000000000 +0900 +++ cmdline.txt 2024-05-05 17:51:50.000000000 +0900 @@ -1 +1 @@ -overlayroot=tmpfs console=serial0,115200 console=tty1 root=PARTUUID=9e2953b9-02 rootfstype=ext4 fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles cfg80211.ieee80211_regdom=JP +overlayroot=tmpfs console=serial0,115200 console=tty1 root=PARTUUID=9e2953b9-02 rootfstype=squashfs fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles cfg80211.ieee80211_regdom=JP
このSDメモリカードを Raspberry Pi 4 を起動させたとき、SquashFSでマウントされていることが確認できる。
pi@raspberrypi:~$ mount | grep 'mmcblk0p2'
/dev/mmcblk0p2 on /media/root-ro type squashfs (ro,relatime,errors=continue)
ルートファイルシステムをEROFSに変更する
EROFS も 読み取り専用の(圧縮)ファイルシステムとなっており、ファイルやディレクトリ、それらメタデータを圧縮することができる。(lz4/lzmaなどがサポートされている)
SquashFSファイルシステムを作成するためには、mkfs.erofs プログラムを使用する必要がある。 mkfs.erofs では、圧縮アルゴリズムの選択や作成されるファイルシステムのメタ情報を調整することができる。
ここでは、デフォルトのパラメータ(非圧縮)でSquashFSファイルシステムを作成する。
EROFSファイルシステムを作成する
$ sudo mkfs.erofs rootfs.erofs /mnt作成したEROFSファイルシステムを SDメモリカードに書き込む
$ sudo dd if=rootfs.erofs of=/dev/sdd2
また、EROFSでマウントするようにカーネルコマンドラインを次のような修正をする。
// 1: --- cmdline.txt.orig 2024-05-05 17:51:26.000000000 +0900 +++ cmdline.txt 2024-05-05 17:51:50.000000000 +0900 @@ -1 +1 @@ -overlayroot=tmpfs console=serial0,115200 console=tty1 root=PARTUUID=9e2953b9-02 rootfstype=ext4 fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles cfg80211.ieee80211_regdom=JP +overlayroot=tmpfs console=serial0,115200 console=tty1 root=PARTUUID=9e2953b9-02 rootfstype=erofs fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles cfg80211.ieee80211_regdom=JP
このSDメモリカードを Raspberry Pi 4 を起動させたとき、SquashFSでマウントされていることが確認できる。
pi@raspberrypi:~$ mount | grep mmcblk0p2
/dev/mmcblk0p2 on /media/root-ro type erofs (ro,relatime,user_xattr,acl,cache_strategy=readaround)
測定
起動時間と圧縮率の二つの観点でそれぞれの起動方法を評価していく。
起動時間は systemd-analyzeから kernel と userpaceの合計時間(s)、圧縮率はデフォルトのイメージサイズ (4580120.72 KB) との割合から計測した。
| S No. | 項目 | mkfsのオプション | 起動時間(s) | 圧縮率 |
|---|---|---|---|---|
| 1 | ext4 (R/W) | - | 16.553 | - |
| 2 | ext4 (R/O) | - | 15.988 | - |
| 3 | SquashFS (uncompressed) | -noD -noI -noX -noF |
18.789 | - |
| 4 | SquashFS (gzip) | none | 19.000 | 38.45% |
| 5 | SquashFS (LZ4HC) | -comp lz4 -Xhc |
16.751 | 44.18% |
| 6 | SquashFS (xz) | -comp xz |
28.728 | 32.74% |
| 7 | SquashFS (lzo) | -comp lzo |
17.563 | 41.63% |
| 8 | SquashFS (zstd) | -comp zstd |
16.891 | 36.11% |
| 9 | EROFS (uncompressed) | none | 15.842 | - |
| 10 | EROFS (LZ4HC) | -zlz4hc,12 |
15.842 | 54.70% |
| 11 | EROFS (lzma) | -zlzma |
22.001 | 32.75% |
| 12 | EROFS (big pcluster) | -zlz4hc -C65536 |
15.779 | 48.81% |
| 13 | EROFS (multiple) | --well-compressed=docs/compress-hints.example -zlz4hc,12 |
17.419 | 45.01% |
| 14 | EROFS (well-compressed) | -C1048576 -Eztailpacking -Eall-fragments -Ededupe -zlz4hc,12 |
18.820 | 43.39% |
x軸を起動時間として、y軸を圧縮率としたときに次のようなグラフが得られた。

このグラフでは、プロットが左にあればあるほど起動時間が短く、下にあればあるほどrootfsのイメージサイズが小さいことを表している。
変更履歴
- 2024/05/12: 記事公開
参考文献
- Raspberry Pi で Overlay File System (read-only file system) を試す #RaspberryPi - Qiita
- Raspberry Pi OSのrootfs ROM 化 ― RAMディスク化しつつ、好きなパッケージを後から追加する方法 #RaspberryPi - Qiita
- erofs-utils/docs/PERFORMANCE.md at dev · erofs/erofs-utils · GitHub
- 2024年4月17日現在の Raspberry Pi OS のカーネルでは、SquashFS や EROFS がビルドインされていないため、カーネルは手元でビルドしたものに更新している。↩