みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

YoctoProject initramfsでOverlayFS

はじめに

YoctoProject ラズベリーパイ4でinitramfsではラスベリーパイ4の実機でinitramfsを試した。ただ、initramfsのシェルにフォールバックするだけだったので、今回は少し実践的な例を示す。

具体的にはYoctoProject kirkstoneでリードオンリーのファイルシステムでやったようなことを、initramfsを使って実現する。

initramfsでOverlayFS

YoctoProjectでinitramfs入門ではきちんと触れなかったが、initramfsのモジュールを提供するinitramfs-framework_1.0.bbでは次のようなパッケージも提供している。

SUMMARY:initramfs-module-overlayroot = "initramfs support for mounting a RW overlay on top of a RO root filesystem"
RDEPENDS:initramfs-module-overlayroot = "${PN}-base initramfs-module-rootfs"
FILES:initramfs-module-overlayroot = "/init.d/91-overlayroot"

ovarlayrootについて

initramfs-module-overlayrootではoverlayrootというスクリプトを提供している。

スクリプト冒頭のコメントを見ると、リードオンリーなルートファイルシステム/に書き込み可能なファイルシステムをオーバーレイするとのこと。

# Simple initramfs module intended to mount a read-write (RW)
# overlayfs on top of /, keeping the original root filesystem
# as read-only (RO), free from modifications by the user.

このスクリプトの中で/をリードオンリーでマウントし直すため、IMAGE_FEATUREへのread-onlyの設定は不要。

# NOTE: The read-only IMAGE_FEATURE is not required for this to work

このスクリプトはoverlay-etc.bbclassをベースにしているとのこと。overlay-etc.bbclassはkirkstoneから導入されたので、overlayrootもkirkstoneより前のバージョンには存在しないことが読み取れる。また、overlay-etc.bbclassをベースにしているため、不揮発な領域をオーバーレイすると問題が発生する/etcについても問題なくオーバーレイすることができる。

# This script is based on the overlay-etc.bbclass, which sets up
# an overlay on top of the /etc directory, but in this case allows
# accessing the original, unmodified rootfs at /rofs after boot.

不揮発な領域を/etcにオーバーレイすると発生する問題とは、具体的には下記のようなものである。

  1. OverlayFS上で/etcのファイルを書き換える。
  2. そのデータはupper_dirに保存される
  3. 次回システム起動時にinitプロセスが/etcのファイルを参照する
  4. /etc参照後にupper_dirをオーバーレイする
  5. 結果として、書き換え前の状態の/etcを参照することになる

overlay-etc.bbclassは/sbin/initをOverlayFSをマウントするスクリプトで置き換えることにより、initプロセスの起動よりも前にupper_dirをオーバーレイすることでこの問題を回避している。

initramfsは本来のルートファイルシステムをマウントする前に、RAM上に展開されinitramfs上のinitプログラムおよびモジュールを実行するため、initプロセスの起動よりも前に、任意の処理を実行することができる。 そのため、initramfsのモジュールとしてoverlayrootを実行することにより/etcのオーバーレイのタイミングの問題を回避しながら、書き込み可能な領域をオーバーレイすることができる。

overlayrootではオーバーレイする書き込み可能な領域をブートパラメータのrootrwで指定する。

# It relies on the initramfs-module-rootfs to mount the original
# root filesystem, and requires 'rootrw=<foo>' to be passed as a
# kernel parameter, specifying the device/partition intended to
# use as RW.

注意点としては、rootfsモジュールのあとに実行される必要がある。

# This module needs to be executed after the initramfs-module-rootfs
# since it relies on it to mount the filesystem at initramfs startup
# but before the finish module which normally switches root.
# After overlayroot is executed the usual boot flow continues from
# the real init process.

overlayrootの実装について

実装をきちんと見始めると実はこのスクリプトはモジュールとしていくつか問題がありそうなことがわかる。

  1. <module>_enable<module>_run関数を実装していない
  2. rootrwのためのfstypeとマウントパラメータに、rootfstypeとrootflagsを参照している

1つ目の方は、overlayrootの最後でchrootをexecし、initスクリプトを実行しているプロセスがルートファイルシステム上の/sbin/initに遷移するため、<module>_enable<module>_runはそもそも呼び出されないので問題ない。

2つ目の方は、rootfsモジュールでルートファイルシステムをマウントするためのマウントオプションとして参照するため、overlayrootのrootrwのために値を設定した時に問題が発生する可能性がある。 具体的には、rootrwをtmpfsに割り当てようとrootfstype=tmpfsとかすると、本来のルートファイルシステムのマウントに失敗するようになってしまう。 本来であればこれは変数を分けるべきだと思う。どちらもデフォルトの動作を期待してブートパラメータにrootfstyperootflagsを指定しなければ問題は発生しない。 overlayrootを使用する際はこの2つのブートパラメータは使用しないのが無難そうだ。

ラズベリーパイ4で試す

ベース環境の構築についてはYoctoProject ラズベリーパイ4でinitramfsを参照のこと。

書き込みパーティションの追加

wksの作成

既存のwksファイルをベースに新しくwksを作成し書き込み可能なパーティションを追加する。

$ bitbake-layers create-layer meta-work
$ bitbake-layers add-layer meta-work
$ mkdir meta-work/wic

meta-work/wic/sdimg-raspberrypi-overlay.wksを次の内容で作成する。

part /boot --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 4096 --size 20
part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --label root --align 4096 --size 2048
part --ondisk mmcblk0 --fstype=ext4 --label data --fixed-size 20

第3のパーティションにマウントポイントを指定せずにext4の領域を追加している。

local.confに下記を追加して、参照するwksファイルを差し替える。

WKS_FILE="sdimg-raspberrypi-overlay.wks"

local.confの修正

local.confに下記の内容が追加されるようにする。

INITRAMFS_IMAGE = "core-image-minimal-initramfs"
INITRAMFS_IMAGE_BUNDLE = "1"
BOOT_SPACE = "1073741"
INITRAMFS_MAXSIZE = "315400"
IMAGE_FSTYPES_pn-${INITRAMFS_IMAGE} = "${INITRAMFS_FSTYPES}"

IMAGE_BOOT_FILES = "${BOOTFILES_DIR_NAME}/* \
                 ${@make_dtb_boot_files(d)} \
               ${KERNEL_IMAGETYPE}-${INITRAMFS_LINK_NAME}.bin;${SDIMG_KERNELIMAGE} \
"

INITRAMFS_SCRIPTS = "initramfs-module-overlayroot"
CMDLINE_DEBUG = "debug rootrw=/dev/mmcblk0p3"

WKS_FILE="sdimg-raspberrypi-overlay.wks"

とりあえずCMDLINE_DEBUGにrootrw=/dev/mmcblk0p3を指定し、先程wksに追加したパーティションを書き込み領域に指定する。

動作確認

作成し直したイメージでラズベリーパイ4を起動、ログインして下記のコマンドの結果を確認。

# mount | grep 'overlay'
overlay on / type overlay (rw,relatime,lowerdir=/overlay/rofs,upperdir=/overlay/upper,workdir=/overlay/work)

/がoverlayされている。

ファイルの書き込みをして再起動後に参照できればOK。

まとめ

initramfsを使用してOverlayFSを使用する方法を試した。

メリットとしては、overlay-etc.bbclassと比較すると、マウントポイントの作成などoverlayrootモジュールの中でやってくれるので、 設定する項目が少ない。initramfsなので、処理の追加や変更が柔軟に行える。

デメリットとしては、initramfsをカーネルにバンドルする場合は、カーネルイメージが大きくなる。 バンドルしない場合は、管理対象が増える。initramfsをしない場合と比較して、起動時間が長くなることが多い。

overlayrootモジュールを使用する際の注意点としては、ブートパラメータにrootfstypeおよびrootflagsを使用しないほうが無難。

このモジュールはkirkstoneよりも前のバージョンには存在しない。ただ、同等の処理を実装するかそのものをバックポートすることで、 独自に仕組みを実装したり、overlay-etc.bbclassをバックポートしたりするよりも、簡単で比較的安全にOverlayFSを使用する環境を構築できると思う。