Raspbian OSのSDカードをリードオンリー化する

(更新

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 バージョンアップにより陳腐化してしまう恐れもありますが、もっとも参考にさせて頂いたのは以下のサイトです。非常に丁寧にステップを踏んで説明していらっしゃるので、私が補足させて頂くことはほとんどありません。

私は 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 /

ここまで。