その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

Raspberry Pi を Read-Only Root-FS にする & メモリがいっぱいになったらどうなるのか

Raspberry Pi を Read-Only Root-FS にしておくと急にマシンがダウンした時でもファイルシステムが破損しにくくなります.
もちろん書き込み内容は永続化されなくなりますが (書き込み内容は tmpfs にストアされるので rebootで揮発する),得てして電プチなどの乱暴なオペレーションに晒されがちな Raspberry Pi にはそういった保護機構を入れておくと何かと良いことがあるかと思います.

というわけでコレを使います:
github.com

この repository にあるツールを利用することで,Raspberry Pi をお手軽に Read-Only Root-FS にすることが可能です.
内部的には OverlayFS を利用しており,Write アクセスについては tmpfs に対して操作し, Read アクセスについては SD カードの Read-Only な FS の内容と tmpfs の内容を重ね合わせることによって,所望の動作を実現しているようです.頭がよいですね.

実際のセットアップについては,repository の README.md に書いてある内容をそのまま実行すれば良いです (aptで引っ張ってくる一部ツールに関する記述が欠落しているのでそれを追加で入れる必要あり: initramfs-tools).

環境
# uname -a
Linux 295057330043532 4.19.75-v7+ #1270 SMP Tue Sep 24 18:45:11 BST 2019 armv7l GNU/Linux
# cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
Installation

基本的に README.md と同じですが,念の為メモ程度に残しておくこととする (オリジナルのドキュメントを参照することを強く勧めます):

sudo su
apt update
apt install -y git rsync gawk busybox bindfs initramfs-tools
dphys-swapfile swapoff
dphys-swapfile uninstall
update-rc.d dphys-swapfile disable
systemctl disable dphys-swapfile
git clone https://github.com/josepsanzcamp/root-ro.git
rsync -va root-ro/etc/initramfs-tools/* /etc/initramfs-tools/
mkinitramfs -o /boot/initrd.gz
echo initramfs initrd.gz >> /boot/config.txt
reboot
Swap が Disabled になっているかどうか見る
pi@raspberrypi:~ $ free
              total        used        free      shared  buff/cache   available
Mem:         443080       32452      199196        5904      211432      352712
Swap:        102396           0      102396
pi@raspberrypi:~ $ free
              total        used        free      shared  buff/cache   available
Mem:         443080       30756      351780        1504       60544      359840
Swap:             0           0           0

Disabled になってますね.

Read-Only になってるかどうかの確認
echo "howdy?" >> am_i_here
sudo reboot
# After a while...
ls am_i_here
# ^ Should be missing
Swap が disabled && Read-Only Root-FS 環境下でめちゃ write されたときの挙動

ちょっとどうなるか気になったので検証.

pi@raspberrypi:~ $ free
              total        used        free      shared  buff/cache   available
Mem:         443080       31296      383472        2240       28312      371056
Swap:             0           0           0

dd で書いてみる:

pi@raspberrypi:~ $ dd if=/dev/zero of=tmpfile bs=314572800 count=1
Killed
pi@raspberrypi:~ $ echo $?
137

Out of memory っぽいエラーコード

1 MiB ずつファイルに書き込むスクリプトで検証:

#!/usr/bin/env perl

use strict;
use warnings;
use utf8;

open my $fh, '>>', './tmp' or die $!;

for (my $i = 0; $i <= 400; $i++) {
    # puts 1 Mib each iteration
    print $fh 'x' x (1024 * 1024) or die "$i MiB: $!";
}
__END__
$ perl check.pl
215 MiB: No space left on device at check.pl line 11.
Warning: unable to close filehandle $fh properly: No space left on device at check.pl line 11.

Disk full と同じような挙動に見えますね.

free してみる:

$ free
              total        used        free      shared  buff/cache   available
Mem:         443080       31496      158672      223020      252912      148164
Swap:             0           0           0

shared が増えて,free及びavailableが減少していますね.

もう一度スクリプトを実行してみる:

$ perl check.pl
0 MiB: No space left on device at check.pl line 11.
Warning: unable to close filehandle $fh properly: No space left on device at check.pl line 11.

今度は書き込めずに Disk Full エラーが返却されていますね.

その一方で free 領域はある程度残っており,マシンがハングアップしてにっちもさっちもいかなくなる,という状況にはならないようです.こういった挙動はおそらく tmpfs 自体の挙動でしょう.

とはいえ swap を切っているので,実際にプロセスがメモリを食い切ると OOM Killer が走ったり,最悪マシンがハングしてしまうと思います.継続的なメモリ使用量の監視が必要かもしれません.そもそもファイルをもりもり書くタイプのワークロードには Read-Only な Root-FS は不向きな気がしました.

以上です.