このmyinitは、initrdで地ならしした環境でないと、うまくいかないようです。x86系では問題なく適用できましたが、ARMではダメでした。少なくとも、多重ループを避けた記述をしたり、記述によってはprocのマウントを強いられるなど、難しい対応となるため、ARMではmyinit Ver.1でいいかと思います。initrdの無い環境で/etc/myrwtabを活用する良い方法が無いか模索中です。
Oracle Linux 9/RHEL9での電源ブチ切りでは、メモリに飛ばすfileとdirを定義ファイル(/etc/rwtab)で指定できる汎用性がありました。この仕組みをUbuntu20.04にも取り入れたいと思います。本当はx86_64ではinitramfsのフェーズでメモリに飛ばすべきですが、grubやinitramfsを適用していない組込のケースも視野に入れ、/sbin/myinitで/etc/(my)rwtabを参照しながらメモリに展開することにします。
なお、繰り返しになりますが、この実装でよく起こる初期トラブルは、/homeや/varの下で膨れ上がったユーザデータとキャッシュの処置漏れによるメモリ容量オーバです。ご利用は自己責任でお願いします。OSが起動しなくなったら、リカバリモードで起動して調整すれば復旧できます。
bindマウントとは
既存ファイルシステムツリーの枝葉に、別のツリーを上から被せるイメージです。dirだけでなくfileのbindも可能です。RHEL系Stateless実装では、以下の流れになります。
① RWする予定のfileとdirをtmpfs領域(RAMDISK)にコピー。
② 元のファイルシステムツリーに①を上から被せる。
bindというよりはoverlayなのですが、overlayfsは別の実装です。Stateless(tmpfsroot)方式は、overlayfs方式での長期運用に懸念を感じる故の実装です。
流用元実装の解析
/etc/rwtabの文法:マニュアル
実行スクリプト:
手元のOracle Linux9でdnf install readonly-root後の/usr/libexec/readonly-rootを見るか、githubを確認します。以下は要所のピックアップであり、##コメントでフォローしました。
MOUNTS=() ## bindマウントのリストを宣言 if is_true "$READONLY" || is_true "$TEMPORARY_STATE"; then add_mount() { mnt=${1%/} ## 末尾の「/」を削除 MOUNTS=("${MOUNTS[@]}" "$mnt") ## リストにマウントポイントを追加 } cp_empty() { ## dir先頭のみが空の状態でtmpfs($RW_MOUNT)にコピーされる if [ -e "$1" ]; then echo "$1" | cpio -p -vd "$RW_MOUNT" &>/dev/null add_mount $1 ## 後でbindマウントするためリストに追加 fi } cp_dirs() { ## dirツリー構造全体が空の状態でtmpfs($RW_MOUNT)にコピーされる if [ -e "$1" ]; then mkdir -p "$RW_MOUNT$1" find "$1" -type d -print0 | cpio -p -0vd "$RW_MOUNT" &>/dev/null add_mount $1 ## 後でbindマウントするためリストに追加 fi } cp_files() { ## fileやdirがそのままcp -a(全コピー)でtmpfs($RW_MOUNT)にコピーされる if [ -e "$1" ]; then cp -a --parents "$1" "$RW_MOUNT" add_mount $1 ## 後でbindマウントするためリストに追加 fi } ### 中略 for file in /etc/rwtab /etc/rwtab.d/* /run/initramfs/rwtab ; do is_ignored_file "$file" && continue ## .bakなどの拡張子ファイルは除外 [ -f $file ] && while read type path ; do ## /etc/rwtabから順次エントリを読み、処置 case "$type" in empty) cp_empty $path ;; files) cp_files $path ;; dirs) cp_dirs $path ;; *) ;; esac done < <(cat $file) done ## 親dirがあるエントリを除外しbindマウント for m in "${MOUNTS[@]}"; do prefix=0 for mount_point in "${MOUNTS[@]}"; do [[ $m = $mount_point ]] && continue ## 完全一致は判定スキップ if [[ $m =~ ^$mount_point/.* ]] ; then ## 親dir有りなら除外 prefix=1 break fi done [[ $prefix -eq 1 ]] && continue mount -n --bind $bindmountopts "$RW_MOUNT$m" "$m" selinux_fixup "$m" done ### 以下略
新しい/sbin/myinit (Ver.2)
上記を/sbin/myinitに、ほぼそのままの形で流用します。
/etc/myrwtabを読む所ではforループ不要ですが、汎用性や拡張性を優先するLinuxの文化に習い、あえてそのままとしました。
#!/bin/bash # myinit Ver 2.0 if [ -e /.tmpfsroot ] && [ -d /tmpfsroot ] \ && [ -e /etc/fstab_ro ] && [ -e /etc/myrwtab ]; then /usr/games/cowsay "TMPFSROOT Start!" ## tmpfsを作成 RW_MOUNT="/tmpfsroot" mount -t tmpfs tmpfs $RW_MOUNT ## Start MOUNTS=() ## bindマウントのリストを宣言 add_mount() { mnt=${1%/} ## 末尾の「/」を削除 MOUNTS=("${MOUNTS[@]}" "$mnt") ## リストにマウントポイントを追加 } cp_empty() { ## dir先頭のみが空の状態でtmpfs($RW_MOUNT)にコピーされる if [ -e "$1" ]; then echo "[myinit] cp_empty $1 $RW_MOUNT" echo "$1" | cpio -p -vd "$RW_MOUNT" &>/dev/null add_mount $1 fi } cp_dirs() { ## dirツリー構造全体が空の状態でtmpfs($RW_MOUNT)にコピーされる if [ -e "$1" ]; then echo "[myinit] cp_dirs (include symlink) $1 $RW_MOUNT" mkdir -p "$RW_MOUNT$1" find "$1" -type d -print0 | cpio -p -0vd "$RW_MOUNT" &>/dev/null ## 改良:シンボリックリンクもコピーする find "$1" -type l -print0 | cpio -p -0vd "$RW_MOUNT" &>/dev/null add_mount $1 fi } cp_files() { ## fileやdirがそのままcp -a(全コピー)でtmpfs($RW_MOUNT)にコピーされる if [ -e "$1" ]; then echo "[myinit] cp_files -a --parents $1 $RW_MOUNT" cp -a --parents "$1" "$RW_MOUNT" add_mount $1 fi } ## /etc/myrwtabから順次エントリを読み、処置 # for file in /etc/myrwtab ; do # [ -f $file ] && while read type path; do while read type path; do case "$type" in empty) cp_empty $path ;; files) cp_files $path ;; dirs) cp_dirs $path ;; *) ;; esac # done < <(cat $file) # done done < "/etc/myrwtab" ## 親dirがあるエントリを除外しbindマウント for m in "${MOUNTS[@]}"; do prefix=0 for mount_point in "${MOUNTS[@]}"; do [[ $m = $mount_point ]] && continue ## 完全一致は判定スキップ if [[ $m =~ ^$mount_point/.* ]] ; then ## 親dir有りなら除外 prefix=1 break fi done [[ $prefix -eq 1 ]] && continue mount -n --bind "$RW_MOUNT$m" "$m" echo $m >> ${RW_MOUNT}/mount_list ## for debug done ## tmpfsroot ユーザランド調整 [ -e /etc/fstab_ro ] && mv -f /etc/fstab_ro /etc/fstab [ -e /etc/issue_tmpfsroot ] && mv -f /etc/issue_tmpfsroot /etc/issue [ -e /etc/motd_tmpfsroot ] && mv -f /etc/motd_tmpfsroot /etc/motd ## Add the sticky bit chmod 1777 /tmp chmod 1777 /var/tmp else echo "[myinit] normal boot ..." fi ## Start systemd exec /sbin/init
/etc/myrwtabの例
/var/lib の扱いが難しいのですが、まずは下記コマンドでリストを作ります。
find /var/lib -maxdepth 1 -mindepth 1 | sort | xargs -I{} echo "files {}" >> /etc/myrwtab
次に、容量の大きいdirを下記コマンドで確認します。
du --max-depth 1 -h /var/lib | sort -hr
容量の大きいdirや、ReadOnlyで良いことがわかっているdirは、コメントアウトします。
## /tmp empty /tmp empty /var/tmp ## /var dirs /var/backups dirs /var/cache dirs /var/crash #files /var/lib dirs /var/local files /var/lock dirs /var/log files /var/log/xrdp.log files /var/log/xrdp-sesman.log dirs /var/mail files /var/metrics dirs /var/opt files /var/run # files /var/snap fiies /var/spool ## for Login files /etc files /root files /home ## /var/lib files /var/lib/AccountsService files /var/lib/BrlAPI files /var/lib/NetworkManager files /var/lib/PackageKit files /var/lib/VBoxGuestAdditions files /var/lib/acpi-support files /var/lib/alsa files /var/lib/app-info files /var/lib/apport #files /var/lib/apt files /var/lib/aspell files /var/lib/avahi-autoipd files /var/lib/bluetooth files /var/lib/boltd files /var/lib/colord files /var/lib/command-not-found files /var/lib/dbus files /var/lib/dhcp files /var/lib/dictionaries-common #files /var/lib/dpkg files /var/lib/emacsen-common files /var/lib/fprint files /var/lib/fwupd files /var/lib/gdm3 files /var/lib/geoclue files /var/lib/ghostscript files /var/lib/grub files /var/lib/hp files /var/lib/initramfs-tools files /var/lib/ispell files /var/lib/libreoffice files /var/lib/locales files /var/lib/logrotate files /var/lib/man-db files /var/lib/misc files /var/lib/openvpn files /var/lib/os-prober files /var/lib/pam files /var/lib/plymouth files /var/lib/polkit-1 files /var/lib/private files /var/lib/python files /var/lib/sgml-base #files /var/lib/snapd files /var/lib/snmp files /var/lib/sudo files /var/lib/systemd files /var/lib/tpm files /var/lib/ubiquity files /var/lib/ubuntu-advantage files /var/lib/ubuntu-drivers-common files /var/lib/ubuntu-release-upgrader files /var/lib/ucf files /var/lib/udisks2 files /var/lib/unattended-upgrades files /var/lib/update-manager files /var/lib/update-notifier files /var/lib/upower files /var/lib/usb_modeswitch files /var/lib/usbutils files /var/lib/vim files /var/lib/whoopsie files /var/lib/xfonts files /var/lib/xkb files /var/lib/xml-core