SDカード書込み回数抑制対策

 ラズパイのシステムで使っているSDカードは、フラッシュメモリーのため書き込み回数が多くなると不良個所が発生して使えなくなる可能性があるようである。

ハードディスクでも回転部分とかディスクの表面が破損するとかで寿命はあったので、同じといえば同じではあるが、スタティックであることからハードディスクよりは強いのではという先入観がある。

 しかし、そうではなく寿命があり突然止まってしまう事もあるようなので、サーバー用途などで常時稼働させる場合は寿命を延ばすための何らかの対策を取っておくことが望ましいようである。

ネットで調べてみると対策としては

(1)スワップを無効にする
(2)ログファイルの一次作成場所をtmpfs(Ramdisk上)にする
(3)ログ出力を減らす
(4)テンポラリ領域をtmpfs(Ramdisk上)にする
(5)容量の大きなSDカードを使う
(番外編)fstabでcommitの時間を長く設定する(ちょっとリスク高め)

という方法があるようである。

番外編は難しそうなので(1)から(4)までを一つずつ試してみることとした。
(5)はもともとそれなりの容量のものを使っているので除外する。

(1)スワップを無効にする
 メモリが足りない時に一時的に作られるスワップファイル。ラズパイではそこまでメモリを食う作業をやらないので、スワップしている所はほとんど見たことがない。
試しにどのくらい使っているか見てみると、
pi@raspberrypi0:~ $ free -h
       total    used    free   shared  buff/cache  available
Mem:     465M     39M    279M    3.7M    146M    374M
Swap:     99M      0B    99M


全然使っていないことがわかる。(元々100MB程度しか確保さていない!)
ということで、スワップを無効化する。
pi@raspberrypi0:~ $ sudo swapoff --all
pi@raspberrypi0:~ $ free -h
              total        used        free      shared  buff/cache   available
Mem: 465M 39M 279M 3.7M 146M 374M
Swap: 0B 0B 0B


これで一旦スワップ領域を無くすことがでるが、再起動すると元に戻ってしまうためサービスを止める必要がある。
ラズパイのスワップファイルは dphys-swapfile を使っていて自動起動になっているようである。

自動起動をコントロールするためにinsservをインストールする。
$ sudo apt-get install insserv
$ sudo apt-get install chkconfig

dphys-swapfile の存在を確認する。
pi@raspberrypi0:~ $ insserv -s | grep dphys-swapfile
S:04:2 3 4 5:dphys-swapfile

現時点での状態を確認する。
pi@raspberrypi0:~ $ systemctl status dphys-swapfile
● dphys-swapfile.service - LSB: Autogenerate and use a swap file
Loaded: loaded (/etc/init.d/dphys-swapfile; generated; vendor preset: enabled
Active: active (exited) since Wed 2019-02-27 21:18:35 JST; 17h ago
Docs: man:systemd-sysv-generator(8)
CGroup: /system.slice/dphys-swapfile.service

Feb 27 21:18:32 raspberrypi0 systemd[1]: Starting LSB: Autogenerate and use a sw
Feb 27 21:18:33 raspberrypi0 dphys-swapfile[230]: Starting dphys-swapfile swapfi
Feb 27 21:18:34 raspberrypi0 dphys-swapfile[230]: want /var/swap=100MByte, check
Feb 27 21:18:35 raspberrypi0 dphys-swapfile[230]: done.
Feb 27 21:18:35 raspberrypi0 systemd[1]: Started LSB: Autogenerate and use a swa


自動起動を停止します。

pi@raspberrypi0:~ $ sudo systemctl stop dphys-swapfile

pi@raspberrypi0:~ $ sudo systemctl disable dphys-swapfile
dphys-swapfile.service is not a native service, redirecting to systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install disable dphys-swapfile
insserv: warning: current start runlevel(s) (empty) of script `dphys-swapfile' overrides LSB defaults (2 3 4 5).
insserv: warning: current stop runlevel(s) (2 3 4 5) of script `dphys-swapfile' overrides LSB defaults (empty).

確認する。
pi@raspberrypi0:~ $ systemctl status dphys-swapfile
● dphys-swapfile.service - LSB: Autogenerate and use a swap file
Loaded: loaded (/etc/init.d/dphys-swapfile; generated; vendor preset: enabled)
Active: inactive (dead)
Docs: man:systemd-sysv-generator(8)

Feb 27 21:18:32 raspberrypi0 systemd[1]: Starting LSB: Autogenerate and use a swap f
Feb 27 21:18:33 raspberrypi0 dphys-swapfile[230]: Starting dphys-swapfile swapfile s
Feb 27 21:18:34 raspberrypi0 dphys-swapfile[230]: want /var/swap=100MByte, checking
Feb 27 21:18:35 raspberrypi0 dphys-swapfile[230]: done.
Feb 27 21:18:35 raspberrypi0 systemd[1]: Started LSB: Autogenerate and use a swap fi
Feb 28 14:44:27 raspberrypi0 systemd[1]: Stopping LSB: Autogenerate and use a swap f
Feb 28 14:44:27 raspberrypi0 dphys-swapfile[2244]: Stopping dphys-swapfile swapfile
Feb 28 14:44:27 raspberrypi0 systemd[1]: Stopped LSB: Autogenerate and use a swap fi


再起動して停止できているか確認します。
pi@raspberrypi0:~ $ sudo reboot
pi@raspberrypi0:~ $ free -h
              total        used        free      shared  buff/cache   available
Mem: 465M 37M 338M 6.6M 88M 373M
Swap: 0B 0B 0B

以上で完了です。

元に戻す場合は、サービスを再開します。

$ sudo systemctl enable dphys-swapfile
$ sudo systemctl start dphys-swapfile
$ systemctl status dphys-swapfile

(2)テンポラリ領域をtmpfs(Ramdisk上)に設定する
fstabを修正して、/tmp と /var/tmp をtmpfs(Ramdisk上)に設定する。

pi@raspberrypi0:~ $ sudo vi /etc/fstab
下記の2行を追加する。
tmpfs     /tmp      tmpfs  defaults,size=32m,noatime,mode=1777 0 0
tmpfs     /var/tmp    tmpfs  defaults,size=16m,noatime,mode=1777 0 0

後は、SDカード上の /tmp と /var/tmp を消して、再起動すればOK
pi@raspberrypi0:~ $ sudo rm -rf /tmp
pi@raspberrypi0:~ $ sudo rm -rf /var/tmp
pi@raspberrypi0:~ $ reboot

修正前のdf
pi@raspberrypi0:~ $ df
Filesystem   1K-blocks  Used Available Use% Mounted on
/dev/root    14875824 2213320 12025512 16% /
devtmpfs     233852    0  233852  0% /dev
tmpfs      238172    0   238172  0% /dev/shm
tmpfs      238172   6468  231704   3% /run
tmpfs       5120    4   5116   1% /run/lock
tmpfs      238172    0  238172   0% /sys/fs/cgroup
/dev/mmcblk0p1  43436  22541  20895   52% /boot
tmpfs       47632   0   47632   0% /run/user/1000

再起動後のdf
pi@raspberrypi0:~ $ df
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/root 14875824 2213256 12025576 16% /
devtmpfs 233852 0 233852 0% /dev
tmpfs 238172 0 238172 0% /dev/shm
tmpfs 238172 6484 231688 3% /run
tmpfs 5120 4 5116 1% /run/lock
tmpfs 238172 0 238172 0% /sys/fs/cgroup
tmpfs 32768 0 32768 0% /tmp
tmpfs 16384 0 16384 0% /var/tmp

/dev/mmcblk0p1 43436 22541 20895 52% /boot
tmpfs 47632 0 47632 0% /run/user/1000


(3)ログ出力を減らす
たくさんのログが出力されているが、使っていないサービスの出力を減らしてみる。
/etc/rsyslog.conf を編集して、RULES以下に記載されている設定の先頭に#をつけてコメントアウトする。
※ログファイルは動作がおかしい時に問題を解決する糸口を掴むためにとても大事なので、時々ONにして確認する必要はある。

pi@raspberrypi0:~ $ sudo vi /etc/rsyslog.conf

基本的なログの中で、使わないモノをコメントアウト。
###############
#### RULES ####
###############

#
# First some standard log files. Log by facility.
#
#auth,authpriv.*                 /var/log/auth.log
#*.*;auth,authpriv.none -/var/log/syslog
cron.* /var/log/cron.log
#daemon.* -/var/log/daemon.log
#kern.* -/var/log/kern.log
#lpr.* -/var/log/lpr.log
#mail.* -/var/log/mail.log
user.* -/var/log/user.log

#
# Logging for the mail system. Split it up so that
# it is easy to write scripts to parse these files.
#
#mail.info -/var/log/mail.info
#mail.warn -/var/log/mail.warn
#mail.err /var/log/mail.err

#
# Some "catch-all" log files.
#
#*.=debug;\
auth,authpriv.none;\
news.none;mail.none -/var/log/debug
*.=info;*.=notice;*.=warn;\
auth,authpriv.none;\
cron,daemon.none;\
mail,news.none -/var/log/messages

#
# Emergencies are sent to everybody logged in.
#
*.emerg :omusrmsg:*


修正が終わったら、サービスを再起動する。
pi@raspberrypi0:~ $ sudo systemctl restart rsyslog

(4)ログファイルの一次作成場所をtmpfs(Ramdisk上)にする
ログファイルの作成場所をtmpfsにすることで、SDカードへのアクセス回数を減らします。ただし、このままだと電源を落とすと消えてしまうので、定期的にバックアップする仕掛けも作っておく。

やることをざっくりまとめると
  (1)/var/log を tmpfs としてマウントする(fstab)
  (2)起動時に 前回終了時にセーブした/var/log の内容を上で作成したtmpfs上にコピー(init.d、rsync)
  (3)終了時にログのバックアップを取る(init.d、rsync)
  (4)定期的にログのバックアップを取る(crontab)

となる。
ネット上を見る限り立ち上げ時に/var/logの構造だけを作り、中身はゼロから書き込むような記述ばかりであったが、それではログの継続性が起動するたびに途切れてしまうため、ここでは停止時にセーブした/var/logそのものをtmpfs上に作成した/var/logにコピーする方法とした。

〇準備
作業前に/var/logのバックアップを取っておく。
pi@raspberrypi0:~ $ sudo mkdir -p /back/var-log
pi@raspberrypi0:~ $ sudo cp -r /var/log/* /back/var-log/

起動時に保存していた/var/logをtmpfsにコピーするスクリプトを作成する。
pi@raspberrypi-1f:~/scripts $ sudo vi setup-logs.sh

内容は
-----------------------------------------------
#!/bin/sh
sudo sh -c "rsync -av /back/logs/* /var/log/"

実行権を与える。
pi@raspberrypi-1f:~/scripts $ chmod +x setup-logs.sh

1) init.d にスクリプトを配置する
まず起動・終了時に呼び出されるスクリプトを作成する。

$ sudo touch /etc/init.d/setup-tempfs-logs
$ sudo chmod +x /etc/init.d/setup-tempfs-logs
$ sudo vi /etc/init.d/setup-tempfs-logs

setup-tempfs-logs の内容は:

---------------------------------------
#!/bin/sh
### BEGIN INIT INFO
# Provides: setup-tempfs-logs
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Setup tempfs log files
### END INIT INFO
case "$1" in
start|"")
sh /home/pi/scripts/setup-logs.sh 2>&1 >/dev/null
;;
stop)
sh /home/pi/scripts/backup-logs.sh 2>&1 >/dev/null
;;
*)
echo "Usage: /etc/init.d/setup-tempfs-logs [start|stop]" >&2 exit 3
;;
esac


起動時に実行するスクリプトは、先ほど生成したsetup-logs.sh をstartに指定して、前回までのログを復元するようになる。

終了時に実行するスクリプトとして、バックアップファイルを作成するスクリプトbackup-logs.shを作成して、stopに指定する。

まず、バックアップ先を作成する。
$ sudo mkdir -p /back/logs
スクリプトを作成する。中身はrsyncです。
$ vi /home/pi/backup-logs.sh
-------------------------------------------
#!/bin/sh
LOG="/back/backup-history.txt"
sudo sh -c "date > $LOG"
sudo sh -c "rsync -av /var/log/* /back/logs/ >> $LOG"

試しに実行する。
$ chmod +x backup-logs.sh
$ sudo ./backup-logs.sh

うまく実行されていれば、/back/logs/ にログファイルと/backにbackup-history.txtができていると思います。

2) boot時に実行されるよう登録
$ sudo insserv -d setup-tempfs-logs

確認
$ sudo ls -l /etc/rc*.d/*setup-tempfs-logs

ファイル(シンボリックリンク)が表示されていればOKである。

3) 定期的に実行されるようにcronに登録
毎時0分にバックアップするようcronを設定する。

$ sudo crontab -e
0 * * * * /home/pi/scripts/backup-logs.sh

4) tmpfsでマウントするように設定
/etc/fstab を編集して、tmpfsの1行を追加する。
$ sudo vi /etc/fstab
tmpfs     /var/log    tmpfs  defaults,size=32m,noatime,mode=0755 0   0

5) 既存の /var/log を削除して、再起動
$ sudo rm -rf /var/log
$ sudo shutdown -r now

起動後、/var/log がラムディスク上に作成されているとか、前回ログがコピーされているかなどを確認して、正常であれば終了である。

以上、ネットで拾ってきたSDカードへの書き込み回数低減対策を行ったが、どの程度SDカードの寿命が延びるかはこれから注視していかなければならない。