Ubuntu20.04 電源ブチ切り myinit Ver.1

過去記事で総括していますが、普通はoverlayrootを使えば良いと思います。ここでは、組込での実装や長期運用を意識し、ラズパイで実施したtmpfs-bind方式と同じ方法で電源ブチ切り対応にします。
この実装でよく起こる初期トラブルは、/homeや/varの下で膨れ上がったユーザデータとキャッシュの処置漏れによるメモリ容量オーバです。ご利用は自己責任でお願いします。OSが起動しなくなったら、リカバリモードで起動して調整すれば復旧できます。

前提

Ubuntu20.04 LTS デスクトップが下記の構成でインストールされていることとします。
なお、ラベルの適用は任意ですが、ここではfstabはラベルで記載することとします。

NAME   LABEL      MOUNTPOINT
sda                                                                                    
|-sda1  BOOT     /boot
`-sda2  ROOTFS    /

参考:ファイルシステム別のラベル付与方法

1. fstab_roの作成

# <file system> <mount point>   <type>  <options>       <dump>  <pass>
#LABEL=ROOTFS    /               ext4    defaults,ro     0       0
LABEL=BOOT       /boot           ext4    defaults,ro     0       0
#/swapfile       none            swap    sw              0       0

2. snapの処置

snapはtmpfsrootでの扱いが難しいので、まずは根こそぎ削除します。systemctl disableだけでは無効化できず、少し手間がかかります。参考:リンク
=> snapの完全削除は不要としました。 /sbin/myinit で /etc(tmpfs)からsnap起動ファイルを削除することで処置します。

3. grubの調整

① /etc/default/grubGRUB_CMDLINE_LINUX_DEFAULTを調整

#GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash init=/sbin/myinit"

update-grubを実行

4. /etc/systemd/journald.conf

    SystemMaxUse=200M  ## サイズは環境に合わせること

5. /sbin/myinitを作る

作成後、実行権限の付与を忘れずに行ってください。
chmod 755 /sbin/myinit

#!/bin/bash
if [ -e /.tmpfsroot ] && [ -d /tmpfsroot ] && [ -e /etc/fstab_ro ]; then
    [ -x /usr/games/cowthink ] && \
    /usr/games/cowthink "Maybe my TMPFS-ROOT will start."
    ## tmpfsを作成
    mount -t tmpfs tmpfs /tmpfsroot
    ## 主要rwフォルダをtmpfsにコピーし、bindマウントで元位置に戻す。
    ## tmpfs容量(500MB)より小さくなるように調整すること。/varは別処置する。
    echo "[myinit] copying ..."
    for DIR in etc root home; do
        cp -a /${DIR} /tmpfsroot/
        echo "[myinit] mount -obind /tmpfsroot/${DIR} /${DIR}"
        mount -obind /tmpfsroot/${DIR} /${DIR}
    done

    ##
    ## /varは特別に処置する
    ##
    echo "[myinit] adjusting /var"
    mkdir /tmpfsroot/var
    find /var/ -type d | cpio -pdm /tmpfsroot/
    find /var/ -type l | cpio -pdm /tmpfsroot/

    ## /var/lib
    rm -rf /tmpfsroot/var/lib/*
    # /var/libをtmpfsへコピー。/snapdは容量が大きいので除外。
    find /var/lib -maxdepth 1 -mindepth 1 ! -path "*/snapd" -exec cp -a {} /tmpfsroot/var/lib \;

    ## /var override bind
    echo "[myinit] mount -obind /tmpfsroot/var /var"
    mount -obind /tmpfsroot/var /var

    ##
    ## 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
    ## snap起動ファイルを削除
    find /etc/systemd/system -name "snap*" -exec rm -rf {} \;
    ## xrdpはログファイル必須
    if [ -e /etc/xrdp ]; then
        touch /var/log/xrdp.log
        chmod 666 /var/log/xrdp.log
    fi

    ## /tmp, /var/tmp をtmpfsでマウント
    echo "[myinit] mount -t tmpfs tmpfs /tmp"
    mount -t tmpfs tmpfs /tmp
    chmod 1777 /tmp
    echo "[myinit] mount -t tmpfs tmpfs /var/tmp"
    mount -t tmpfs tmpfs /var/tmp
    chmod 1777 /var/tmp
else
    echo "[myinit] normal boot ..."
fi
## Start systemd
exec /sbin/init

6. デコレーション表示

① cowsayをインストール
apt install cowsay
② /etc/bash.bashrc の末尾に下記を追加

## tmpfsroot環境であることを明示
FS_TYPE=`findmnt -no FSTYPE /etc`
if [ "x${FS_TYPE}" == "xtmpfs" ]; then
    cowsay "This is my TMPFS-ROOT. Your changes will be lost after reboot."
    echo
fi

7. 環境設定と確認

mkdir /tmpfsroot
touch /.tmpfsroot
reboot

成功したら、Terminalログイン時に下記が出力される。

 _____________________________________
/ This is my TMPFS-ROOT. Your changes \
\ will be lost after reboot.          /
 -------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

8. 環境の切り替え

いずれも実行権限を付与して利用して下さい。
① tmpfsrootから元のRW環境に戻すスクリプト
/root/bin/change2rw.sh:

#!/bin/bash
mount -oremount,rw /  
rm /.tmpfsroot  
mount -oremount,ro /  
echo リブート後、RW環境となります。

② RW環境からtmpfsroot環境に移行するスクリプト
/root/bin/change2tmpfsroot.sh:

#!/bin/bash
[ -d /tmpfsroot ] || mkdir /tmpfsroot
touch /.tmpfsroot
echo リブート後、tmpfsroot環境となります。

③ tmpfsrootのまま一時的にRW環境に入るスクリプト
/root/bin/onetime_developer.sh:

#!/bin/bash
FS_TYPE=`findmnt -no FSTYPE /etc`
if [ "x${FS_TYPE}" != "xtmpfs" ]; then
    cowsay "This is not my TMPFS-ROOT, so nothing to do for RW."
    exit
fi
mount -oremount,rw /  
mount /dev/sda2 /mnt  
mount -o bind /dev /mnt/dev  
mount -t proc proc /mnt/proc  
mount -o bind /sys /mnt/sys  
clear
cowsay This is my Onetime Developer\'s Terminal.
chroot /mnt  ## ここでTerminal操作となります。以下はexit後に実行されます。
umount /mnt/sys
umount /mnt/proc
umount /mnt/dev
umount -l /mnt
mount -oremount,ro /



ToDo

  • snap入りでの対応
  • /etc/rwtabでの対応(RHEL Statelessと同様に)
  • 事前のメモリ消費チェック