Edited at

[メモ] Raspberry Piで OverlayFS (組み込み向け設定・スクリプト)

More than 3 years have passed since last update.


概要



  • raspberry pi overlayfs とかで、検索していたら、Domoticz - Open Source Home Automation SystemというプロジェクトのWikiに気になる記事が。


  • Setting up overlayFS on Raspberry Pi



    • /bootrootファイルシステムは通常、読み取り専用(ro)ーに変更.


    • /var/home は読み書きできる(rw)オーバーレイファイルシステムに変更.


    • /etc/init.d/saveoverlaysのinitスクリプトで、システムのシャットダウンと再起動時にSDに同期させる。



  • これを使用すると、ほぼ完全にSDカードの書き込みを排除することができます。 読み書きの部分は、RAMベース(=Ramdisk)なので、(Domoticzを)スピードアップします。


#1 Google翻訳+ちょいと改

#2 Domoticz: Open Source Home Automation System というのが気になる。。がまたの機会に..

#3 再起動前にsudo service saveoverlays syncをするのがよいかも。(RAMDisk上のファイルをSD側へ、rsync(なければコピー)してくれる)

#4 rootfsをroにすると、apt-getやら、できなくなる。設定の済んだ、組み込み以外は、あつかいが大変カモ?


環境


  • Raspberry Pi 2/3 (1でもZeroでもいけるかと)

  • 2016-05-27-raspbian-jessie-lite.img


手順


  1. 書いてあるとおりに..

  2. いつもの+設定など行う (/home/varしか、書き込みできなくなるので、そのようにする)


  3. こぴぺ

    cd /tmp
    
    wget http://hansrune.net/domo/mount_overlay
    wget http://hansrune.net/domo/saveoverlays
    chmod a+rx saveoverlays mount_overlay
    sudo cp mount_overlay /usr/local/bin
    sudo cp saveoverlays /etc/init.d/
    #
    #
    # Swapをとめる.
    sudo dphys-swapfile swapoff
    sudo dphys-swapfile uninstall
    sudo update-rc.d dphys-swapfile disable
    #
    #
    # fake-hwclockサービスは、`/etc/fake-hwclock.data`ファイルを読み書きするので、実態を/var以下に移して、リンクをはる.
    # ここでサービスをdisableするが、saveoverlaysサービスで、呼ばれる
    sudo service fake-hwclock stop
    sudo mv /etc/fake-hwclock.data /var/log/fake-hwclock.data
    sudo ln -s /var/log/fake-hwclock.data /etc/fake-hwclock.data
    sudo service fake-hwclock start
    sudo update-rc.d fake-hwclock disable
    #
    # インストール fuse lsof rsync
    # lsof: ファイルシステムをリードオンリーに再マウントできないとき、オープンしてるファイルをみつけるのに役立つよ.
    sudo apt-get install -y fuse lsof rsync
    #
    # roを開始するとめんどうになるので、いろんな設定をしといたほうがいいかと。
    #
    #
    #
    #サービス開始
    sudo systemctl enable saveoverlays.service



  4. sudo vi /boot/cmdline.txt


    /boot/cmdline.txt

    dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait noswap fastboot ro
    



    • noswap fastboot roを追加




  5. sudo vi /etc/fstab


    /etc/fstab

    proc            /proc           proc    defaults          0       0
    
    /dev/mmcblk0p1 /boot vfat ro 0 2
    /dev/mmcblk0p2 / ext4 ro,noatime 0 1
    mount_overlay /var fuse nofail,defaults 0 0
    mount_overlay /home fuse nofail,defaults 0 0
    none /tmp tmpfs defaults 0 0



    • /dev/mmcblk0p1/dev/mmcblk0p2roになってるよ




  6. Overlay関連


    こぴぺ

    #
    
    #Overlayに使うのディレクトリ
    sudo mv /home /home_org
    sudo mkdir /home /home_rw
    sudo mv /var /var_org
    sudo mkdir /var /var_rw
    #
    #マウントしてみて、確認.
    sudo mount /home
    sudo mount /var
    #
    mount | grep home ; mount | grep var
    ls -la /home/pi


    こんなかんじ

    pi@raspberrypi:~ $ mount | grep home ; mount | grep var 
    
    ramdisk on /home_rw type tmpfs (rw,relatime)
    overlay on /home type overlay (rw,relatime,lowerdir=/home_org,upperdir=/home_rw/upper,workdir=/home_rw/work)
    ramdisk on /var_rw type tmpfs (rw,relatime)
    overlay on /var type overlay (rw,relatime,lowerdir=/var_org,upperdir=/var_rw/upper,workdir=/var_rw/work)
    pi@raspberrypi:~ $ ls -la /home/pi
    total 20
    drwxr-xr-x 2 pi pi 4096 Jun 14 23:03 .
    drwxr-xr-x 1 root root 40 Jun 14 23:28 ..
    -rw-r--r-- 1 pi pi 220 May 27 11:09 .bash_logout
    -rw-r--r-- 1 pi pi 3512 May 27 11:09 .bashrc
    -rw-r--r-- 1 pi pi 675 May 27 11:09 .profile
    -rw------- 1 pi pi 57 Jun 14 23:03 .Xauthority



  7. sudo reboot



  8. マウントできてるか確認


    mount

    pi@raspberrypi:~ $ mount
    
    /dev/mmcblk0p2 on / type ext4 (ro,noatime,data=ordered)
    .
    .
    .
    fusectl on /sys/fs/fuse/connections type fusectl (rw,relatime)
    none on /tmp type tmpfs (rw,relatime)
    ramdisk on /home_rw type tmpfs (rw,relatime)
    ramdisk on /var_rw type tmpfs (rw,relatime)
    /dev/mmcblk0p1 on /boot type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)
    overlay on /var type overlay (rw,relatime,lowerdir=/var_org,upperdir=/var_rw/upper,workdir=/var_rw/work)
    overlay on /home type overlay (rw,relatime,lowerdir=/home_org,upperdir=/home_rw/upper,workdir=/home_rw/work)
    pi@raspberrypi:~ $


    • 見所

      /dev/mmcblk0p2 ... (ro, ...) : リードオンリー

      /dev/mmcblk0p1 ... (ro, ...) : リードオンリー

      /home_rw/var_rw : RAMディスク

      /home/var : lowerdirをベースにupperdirを重ねる(lowerdir=/XXX_org,upperdir=/XXX_rw/upper,workdir=/XXX_rw/workで、overlay)




システムメンテナンスのためのスクリプト



  • systemdを使っているとうまく動かないので、↓のコメントの変更したスクリプトを使用すること!


sysmaint.shというファイル名にしときます.

#!/bin/bash

# remount root rw
mount -o remount,rw /

# prapare target paths
mkdir -p /chroot
mkdir -p /chroot/{bin,boot,dev,etc,home,lib,opt,proc,root,run,sbin,sys,tmp,usr,var}

# mount special filesystems
mount -t proc proc /chroot/proc
mount --rbind /sys /chroot/sys
mount --rbind /dev /chroot/dev

# bind rw directories
for f in {home,var}; do mount --rbind /${f}_org /chroot/$f; done

# bind remaining directories
for f in {bin,boot,etc,lib,opt,root,run,sbin,tmp,usr}; do mount --rbind /$f /chroot/$f; done

# chroot
echo "Note: /boot is still mounted read-only, remount to read-write if needed."
echo -e "\e[33mYou are now in read-write chroot. Use CTRL+D when done to exit chroot and mount read-only again.\e[39m"
chroot /chroot /usr/bin/env PS1="(rw) \u@\h:\w\$ " /bin/bash --noprofile -l

# unmount mounts
for f in /chroot/{bin,boot,dev,etc,home,lib,opt,proc,root,run,sbin,sys,tmp,usr,var}; do
umount -l $f
done

sleep 1

# remount read-only again
echo -e "\e[32mChroot left, re-mounting read-only again.\e[39m"
mount -o remount,ro /



  • sudo ./sysmaint.sh


  • => 実行するとchrootつかって、ファイルの読み書きができるようになる(# /etc/fstabで、rwにして再起動とか..)



    pi@raspberrypi:~$ sudo ./sysmaint.sh
    
    Note: /boot is still mounted read-only, remount to read-write if needed.
    You are now in read-write chroot. Use CTRL+D when done to exit chroot and mount read-only again.
    (rw) root@raspberrypi:/$ vi /etc/fstab
    ...
    (rw) root@raspberrypi:/$ logout
    Chroot left, re-mounting read-only again.
    pi@raspberrypi:~$




もし、読み取り専用に再マウントできなかったら

sudo service saveoverlays sync

sudo reboot


SDカードにシンクできてるかな?

/var/log/saveoverlays.log にログってる。


その他


mount_overlay


hansrune.net/domo/mount_overlay

#!/bin/sh

DIR="$1"
[ -z "${DIR}" ] && exit 1
#if ! grep -q overlay /proc/filesystems
#then
# echo "Filesystem overlay is not available. You need a kernel update: apt-get update && apt-get upgrade" >&2
# exit 2
#fi
if [ ! -d "${DIR}_org" ]
then
echo "${DIR}_org does not exist" >&2
exit 1
fi
if
[ ! -d "${DIR}_rw" ]
then
echo "${DIR}_rw does not exist" >&2
exit 1
fi
#
# ro must be the first mount option for root .....
#
ROOT_MOUNT=$( grep -v "^#" /etc/fstab | awk '$2=="/" { print substr($4,1,2) }' )
if [ "$ROOT_MOUNT" != "ro" ]; then
/bin/mount --bind ${DIR}_org ${DIR}
else
/bin/mount -t tmpfs ramdisk ${DIR}_rw
/bin/mkdir ${DIR}_rw/upper
/bin/mkdir ${DIR}_rw/work
OPTS="-o lowerdir=${DIR}_org,upperdir=${DIR}_rw/upper,workdir=${DIR}_rw/work"
/bin/mount -t overlay ${OPTS} overlay ${DIR}
fi


saveoverlays


hansrune.net/domo/saveoverlays

#! /bin/sh

### BEGIN INIT INFO
# Provides: saveoverlays
# Required-Start: $local_fs $time
# Required-Stop: $local_fs $syslog $time
# Default-Start: S
# Default-Stop: 0 6
# Short-Description: Save logs and files from overlay mounts
# Description: Save logs and files from overlay mounts
### END INIT INFO

# Do NOT "set -e"
#
# Testing hints:
# sudo mount -o remount,ro /
# sudo env INIT_VERBOSE=yes /etc/init.d/saveoverlays stop
# cat /var/log/saveoverlays.log

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
DESC="overlay filesystem sync"
NAME=saveoverlays

if [ -f "/etc/default/$NAME" ]; then
. "/etc/default/$NAME"
fi
TMPLOG=/tmp/$NAME.log
LOGFILE=/var_org/log/$NAME.log

SYNCDIRS=${SYNCDIRS:-$( mount | awk '/^overlay/ { print $3 ":" $3 "_org" }' )}
SYNCEXCLUDES=${SYNCEXCLUDES:-'--exclude .unionfs* --exclude .fuse_hidden* --exclude *.leases --exclude stats'}
SYNCFLAGS=${SYNCFLAGS:-"-avH --inplace --delete"}

# Check if we are running with read-only root
ROROOT=$( mount | egrep '^/dev/.*on / .*ro,' )
DOSYNC=${FORCESYNC:-"$ROROOT"}

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
if [ -n "${ROROOT}" ]
then
log_action_msg "Read-only root active"
else
log_action_msg "Read-only root inactive"
fi
if
date '+%Y' | grep -q 1970
then
log_action_msg "Clock is not set. Trying fake-hwclock"
fake-hwclock load
fi
}

#
# Function that syncs the files
#
do_sync()
{
RETVAL=0
#
# If we run with overlayfs, try to sync the dirs
#
if [ -z "${ROROOT}" ] || mount -o remount,rw / >> ${TMPLOG} 2>&1
then
echo "----------------------------" >> ${TMPLOG}
echo "$NAME sync started at `date`" >> ${TMPLOG}
for DIR in ${SYNCDIRS}
do
SOURCE="${DIR%%:*}"
DEST="${DIR##*:}"
log_action_msg "Syncing ${SOURCE} to ${DEST} ..."
echo "----" >> ${TMPLOG}
echo "$NAME sync ${SOURCE} to ${DEST} with options ${SYNCFLAGS} ${SYNCEXCLUDES} at `date`" >> ${TMPLOG}
if [ -d "${SOURCE}" -a -d "${DEST}" ]
then
rsync ${SYNCFLAGS} ${SYNCEXCLUDES} ${SOURCE}/ ${DEST}/ >> ${TMPLOG} 2>&1
elif [ -f "${SOURCE}" ]
then
cp -vp ${SOURCE} ${DEST} >> ${TMPLOG} 2>&1
else
log_action_msg "Skipping this step: ${SOURCE} or ${DEST} not available"
fi
done
cat ${TMPLOG} >> ${LOGFILE}
if [ -w /etc/fake-hwclock.data ]
then
log_action_msg "Saving fake-hwclock"
fake-hwclock save
fi
log_action_msg "Sync changes to disk"
sync; sync; sync
#
# return to read-only only if that is where we started
#
if [ -n "${ROROOT}" ]
then
log_action_msg "Remount read-only"
mount -o remount,ro /
fi
else
log_action_msg "Remounting root as writeable failed!"
RETVAL=2
fi
return
"$RETVAL"
}

#
# Function that stops the daemon/service
#
do_stop()
{
#
# If we run with overlayfs, try to sync the dirs
#
if [ -n "${DOSYNC}" ]
then
do_sync;
return $?
else
log_action_msg "Root is not read-only. No action"
return 0
fi
}

case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) log_end_msg 0 ;;
2) log_end_msg 1 ;;
esac
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) log_end_msg 0 ;;
2) log_end_msg 1 ;;
esac
;;
sync)
log_daemon_msg "Syncing $DESC" "$NAME"
do_sync
case "$?" in
0|1) log_end_msg 0 ;;
2) log_end_msg 1 ;;
esac
;;
status)
;;
restart)
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
exit 3
;;
esac

: