Trying to mount filesystems of Raspbian OS as read-only. We see a lot of helpful information on the Internet. Thanks to everyone!
今日は、Raspbian(Raspberry Pi の OS)のファイルシステムを、リードオンリー化してみました。最近の SSD(Solid-State Drive)は高耐久化されていて、通常の利用によって寿命が短くなることはほとんどないそうですが、SD カード(microSD カード)をファイルシステムとして使っている Raspberry Pi などでは、長期間の運用で SD カードに書込みができなくなってしまうことがあるようです。(そういう経験を何度かしています。)
そこで、今回は SD カードを通常はリードオンリーでマウントしておき、シャットダウン時のみ SD カードに書き戻す、という設定をしてみました。他のメリットとしては、Raspberry Pi の shutdown コマンド等を実行しないで電源断しても、ファイルシステムが壊れない、ということが挙げられます。
なお、このような設定に関しては、ウェブ上を探すと先達さまがたくさんの功績、工夫をブログ等で公開していらっしゃいますので、私自身の功績(工夫)と言えるものはほとんどありません。一つは備忘録、また、私の失敗談として書かせて頂きたいと思います。
なお、リードオンリー化のためのもう一つの方法もブログで紹介しております。こちらも御参考ください。(2017/10/15)
実際にやってみる
ネット上で探すといろいろと情報がありますし、これらの情報は Raspbian の OS バージョンアップにより陳腐化してしまう恐れもありますが、もっとも参考にさせて頂いたのは以下のサイトです。非常に丁寧にステップを踏んで説明していらっしゃるので、私が補足させて頂くことはほとんどありません。
- Setting up overlayFS on Raspberry Pi – Domoticz (以下、このサイトに従った作業を「上記作業」と呼びます)
私は Raspbian Wheezy を使っていますが、一点だけうまくいかないことがありました。それは、デフォルトでインストールされているカーネル(3.18.11-v7+)では overlayfs が有効になっていない、ということです。
実際に以下のコマンドを実行してみると、overlayfs が有効になっているかどうかが分かります。
$ sudo modprobe configs # ls /proc/config.gz でファイルが見つからない場合 $ zgrep -i overlay /proc/config.gz
もしかすると Jessie ならば問題ないのかも知れませんが、私は systemd を使いたくなかったので Wheezy で頑張りたいと思いました。カーネルをソースからリコンパイルすることなく、カーネルをアップデートする方法はないかと調べたところ、Raspbian では rpi-update というコマンドで可能ということが分かりました。
具体的には、次のようにします。
$ sudo rpi-update
これでリブートしたところ、カーネルが 4.4.27-v7+ となり、無事に overlayfs が使えるようになりました。なお試される方は、「上記作業」に入る前に /proc/config.gz と sudo modprobe overlay を試しておき、overlayfs が有効になっていない場合は、予めカーネルをアップデートしておくことをお勧めします。上記作業で overlayfs がうまく使えないことが分かった段階でリブートすると、ルートパーティションがリードオンリーになっていたりして、混乱するかも知れません。(もちろん Linux グルの方には、その心配は不要でしょう。)
/var/log をどうするか?
さて。上記作業をして無事にリードオンリー化ができたとして、問題は RAM ディスクが溢れてしまうという懸念です。SD メモリに書かないでファイルを作る、というのは魔法でもなんでもなくて、実は RAM ディスク上にファイルを作ることで実現しています。つまり、/var や /home にファイルを書き続けると、OS をシャットダウンして RAM ディスクの内容を SD カードに同期する前に、RAM ディスクが溢れてしまう、という懸念があるわけです。
例えば、ブート直後に df コマンドを実行すると次のようになります。
Filesystem 1K-blocks Used Available Use% Mounted on /dev/root 7513804 2582644 4575664 37% / devtmpfs 469536 0 469536 0% /dev tmpfs 94776 208 94568 1% /run tmpfs 5120 0 5120 0% /run/lock tmpfs 189540 0 189540 0% /run/shm /dev/mmcblk0p1 57288 21560 35728 38% /boot ramdisk 473868 8 473860 1% /var_rw overlay 473868 8 473860 1% /var ramdisk 473868 0 473868 0% /home_rw overlay 473868 0 473868 0% /home none 473868 0 473868 0% /tmp none 473868 0 473868 0% /sys/fs/cgroup
そして、例えば dd if=/dev/zero bs=1M count=100 of=~/foo.dat のようにしてから df を実行すると、
ramdisk 473868 102400 371468 22% /home_rw overlay 473868 102400 371468 22% /home
のようになっていることが分かりますが、さらに vmstat コマンドを実行してみると、
# 前 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 1 0 0 855016 27652 27096 0 0 8 0 108 20 0 0 99 0 # 後 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 1 0 0 752468 27652 129468 0 0 8 0 108 20 0 0 100 0
のようにメモリのフリーエリアが減っていることが分かります。
前置きが長くなりました。ここで問題なのは、/var/log ディレクトリです。/home に関しては自分でファイル量を制御することができますが、/var/log はシステムが勝手に書き込んでいくので /var/log の内容が増えてくることで、RAM が枯渇してしまいます。もちろん /var/log エリアも一杯になって、ログを書き込めなくなってしまうことでしょう。
/var/log への書込みについては、rsyslogd の設定を変更してログ出力を制御したり、logrotate の設定を変更して、古いログを早めに消す、ということができます。また、rsyslogd の設定を変えて、ログをリモートホストに出力する、ということも可能かも知れません。
私は上記のいずれもメンドーだったので(KISS: Keep-It-Simple… の法則と自称)、NFS マウント先のディレクトリを /var/log にリンクして、問題を忘れることにしました。もう一つの方法としては、Raspberry Pi に SSD を繋いで /var/log に SSD をマウントする、という方法も考えられますが(なにしろ、SSD のほうが SD メモリカードよりも耐久性が高いらしいので)、本末転倒な気がしてきたので、それはやめました。
とりあえず、今日はここまでです。
補足(2016/10/28)
一点だけ。Domoticz プロジェクトさんの上記説明にあるスクリプトファイル saveoverlays は良くできているのですが、手元の Raspbian の /bin/sh では、スクリプト中の if [ “${ROROOT}” == “ro” ] という構文がエラーになってしまいます。「[」というのは test コマンド(現在はシェルのビルトインコマンドになっていることが普通)のエイリアスなのですが、== というのが正式には誤りで、= と書くのが正解です。以下のパッチを当てて御利用ください。
--- saveoverlays.ORIG 2016-10-27 23:43:23.604678450 +0000 +++ saveoverlays 2016-10-28 03:14:34.169908335 +0000 @@ -50,7 +50,7 @@ # do_start() { - if [ "${ROROOT}" == "ro" ] + if [ "${ROROOT}" = "ro" ] then log_action_msg "Read-only root active" else @@ -104,7 +104,7 @@ # # return to read-only only if that is where we started # - if [ "${ROROOT}" == "ro" ] + if [ "${ROROOT}" = "ro" ] then log_action_msg "Remount root as read-only" mount -o remount,ro /
ここまで。