Raspberry PiのSDカードが壊れた!寿命を延ばす方法 5+1選!【運用編を追加】

2021年1月12日

ラズベリーパイ、完全に沈黙

ラズパイで音声合成を使って喋らせることができるようになったので、調子に乗って「時報女」さんのように数ヶ月ずっと喋らせていたら、つい先日から沈黙してしまいました。

キーボード入力も何も受け付けない、、、
そして、電源を切ったら最後、真っ暗画面から復帰せず。

ちーん。

これは、SDカードがお亡くなりなってしまったな。。。

同僚からは、長期運用するにはSDカードはヤバイヨ!と言われていたので、ついに来たかと。

ということで、今回はラズベリーパイ用のマイクロSDカードの寿命を延ばす方法、

名付けて、

Re: ゼロから始めるラズパイ生活

※あんたも好きね〜w と言われそうですが、実際に再インストールから始めたので方向性は合っているのです(`・ω・´)キリッ

実施環境

Raspberry Pi 3B Raspbian Jessie

$ uname -r
4.9.35-v7+

念のため更新。

$ sudo apt-get update
$ sudo apt-get upgrade

追記事項

  • 2021-01-12 改訂
    ログ出力をtmpfs上にする方法を改訂しました。
  • 2018-09-16 新章追加
    これまでのセットアップ時の設定に加え、運用時に気をつけるSDカードの延命方法をまとめた「寿命を延ばす方法(運用編)」を追加しました。
  • 2018-03-08 追記
    Raspbian stretchでinservが使えないようなので、代わりにsysytemctlを使ってください。該当箇所にsystemctlを用いた方法も記載しました。

寿命を延ばす方法5選!(+1番外編)

ネットで先駆者の情報を渉猟すること小1時間。

ざっくりまとめると、

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

という方法がありました。

では、具体的な方法を紹介します。

(1)スワップを無効にする

メモリが足りないときに一時的に作られるスワップファイル。ラズパイではそこまでメモリを食う作業をやらないので、スワップしている所はほとんど見たことありません!

試しにどのくらい使っているか見てみると、

$ free -h
             total       used       free     shared    buffers     cached
Mem:          923M       286M       636M        29M        23M       173M
-/+ buffers/cache:        89M       833M
Swap:          99M         0B        99M

全然使っていないことがわかります。(元々100MB程度しか確保さていない!)
ということで、スワップを無効化していきます。

$ sudo swapoff --all
$ free -h
             total       used       free     shared    buffers     cached
Mem:          923M       296M       627M        29M        24M       174M
-/+ buffers/cache:        96M       826M
Swap:           0B         0B         0B

これで一旦スワップ領域を無くすことができますが、再起動すると元に戻ってしまうためサービスを止めます。

ラズパイのスワップファイルは dphys-swapfile を使っていて自動起動になっています。

※systemctlコマンドで確認。

$ systemctl status dphys-swapfile
$ systemctl status dphys-swapfile
● dphys-swapfile.service - LSB: Autogenerate and use a swap file
   Loaded: loaded (/etc/init.d/dphys-swapfile)
   Active: inactive (dead)

サービスを停止してから自動起動も停止します。

$ sudo systemctl stop dphys-swapfile
$ sudo systemctl disable dphys-swapfile

再起動して停止できているか確認します。

$ sudo shutdown -r now
free -h

以上で完了です。

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

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

古いシステム(非推奨)の場合のためにメモを残しておきます。

$ insserv -s | grep dphys-swapfile
S:03:2 3 4 5:dphys-swapfile
 sudo insserv -r dphys-swapfile

ref. Raspberry Pi -RASPBIAN- のswapを無効化

(2)テンポラリ領域をtmpfs(Ramdisk上)に設定する

fstabを修正して、/tmp と /var/tmp をtmpfs(Ramdisk上)に設定します。

$ sudo vim /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です。

$ sudo rm -rf /tmp
$ sudo rm -rf /var/tmp
$ sudo shutdown -r now

(3)ログ出力を減らす

たくさんのログが出力されていますが、使っていないサービスの出力を減らしてみます。
/etc/rsyslog.conf を編集して、RULES以下に記載されている設定の先頭に#をつけてコメントアウトします。

※ログファイルは動作がおかしい時に問題を解決する糸口を掴むためにとても大事なので、時々ONにして確認することをオススメします。

$ sudo vim /etc/rsyslog.conf

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

###############
#### RULES ####
###############

#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

メール、InterNetNewsは使っていないので、

#mail.info     -/var/log/mail.info
#mail.warn     -/var/log/mail.warn
#mail.err      /var/log/mail.err

#news.crit     /var/log/news/news.crit
#news.err      /var/log/news/news.err
#news.notice     -/var/log/news/news.notice

デバッグも。

#*.=debug;\
#  auth,authpriv.none;\
#  news.none;mail.none -/var/log/debug

修正が終わったら、サービスを再起動します。

$ sudo systemctl restart rsyslog

(4-改訂版)ログファイルの一次作成場所をtmpfs(Ramdisk上)にする

【変更点】

以前書いていた方法が古くて複雑だったので、シンプルでわかりやすい方法に変更しました。

  • rsync を用いて/var/logをバックアップ・リストアする。
  • systemdを使って起動時にリストア、終了時にバックアップを行う。

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

やることをざっくりまとめると、

  1. /var/log を tmpfs としてマウントする(fstab)
  2. 起動時に /var/log の構造を作る(systemd、rsyncでバックアップから復元)
  3. 終了時にログのバックアップを取る(systemd、rsync)
  4. 定期的にログのバックアップを取る(crontab、rsync)

となります。

作業前のバックアップ

sudo mkdir -p /backup/var-log
sudo rsync -av /var/log/* /backup/var-log/

バックアップ&リストア用スクリプトの作成

バックアップ用スクリプトを作成します。

vim /home/pi/scripts/var-log-backup.sh

中身はこちら。

#!/bin/bash
FROM="/var/log/*"
TO="/backup/var-log/"
LOGDIR="/backup/logs"
LOG=${LOGDIR}/$(date "+%Y%m%d")_var-log-backup.log

## 出力先の確認。無ければ作る。
if [ ! -e ${TO} ]; then
        sudo sh -c "mkdir -p ${TO}"
fi

if [ ! -e ${LOGDIR} ]; then
        sudo sh -c "mkdir -p ${LOGDIR}"
fi

## バックアップ開始
sudo sh -c "echo \"\n[ begin - backup ]\n$(date)\" >> ${LOG}"
sudo sh -c "rsync -av ${FROM} ${TO} >> ${LOG}"

リストア(復元)用のスクリプトを作成します。

vim /home/pi/scripts/var-log-restore.sh

中身はこちら。バックアップとほぼ一緒で、FROMとTOが入れ替わっています。

#!/bin/bash
FROM="/backup/var-log/*"
TO="/var/log/"
LOGDIR="/backup/logs"
LOG=${LOGDIR}/$(date "+%Y%m%d")_var-log-backup.log

## 出力先の確認。無ければ作る。
if [ ! -e ${TO} ]; then
        sudo sh -c "mkdir -p ${TO}"
fi

if [ ! -e ${LOGDIR} ]; then
        sudo sh -c "mkdir -p ${LOGDIR}"
fi

## 復元開始
sudo sh -c "echo \"\n[ begin - restore ]\n$(date)\" >> ${LOG}"
sudo sh -c "rsync -av ${FROM} ${TO} >> ${LOG}"

実行権を付けておきます。

chmod +x /home/pi/scripts/var-log-*.sh

ファイルシステムの設定

/etc/fstab の末尾に以下の1行を追加します。sudo vim /etc/fstab
※32MBで作成しているので、大量のログで埋まらないように注意。

tmpfs           /var/log        tmpfs   defaults,size=32m,noatime,mode=0755  0       0

既存の /var/log を削除して、再起動

sudo rm -rf /var/log
sudo reboot

起動後に、/var/logがtmpfsとしてマウントされていることを確認します。

$ df -h | grep /var/log
tmpfs             32M  6.1M   26M   19% /var/log

systemdサービスファイルの作成と登録

サービスファイルを作成します。

sudo vim /etc/systemd/system/tempfs-var-log.service

中身はこちら。

[Unit]
Description = tempfs var-log

[Service]
Type = oneshot 
ExecStart = /home/pi/scripts/var-log-restore.sh
ExecStop = /home/pi/scripts/var-log-backup.sh
RemainAfterExit = yes

[Install]
WantedBy = multi-user.target

Type = oneshot、RemainAfterExit = yes として、ExecStartとExecStopに先ほど作成したスクリプトを指定します。
(参考)SystemdでOS起動/停止時にスクリプト実行させるサービスを作ってみた

サービスファイルを作成したら、デーモンをリロードしてenableにします。

sudo systemctl daemon-reload
sudo systemctl enable tempfs-var-log

登録され、enableになっていることを確認します。

systemctl list-unit-files --type=service |grep tempfs-var-log

以下のように表示されます。

tempfs-var-log.service                 enabled

登録したサービスの動作確認

起動時のリストア処理を実行します。

sudo systemctl start tempfs-var-log

終了時のバックアップ処理を実行します。

sudo systemctl stop tempfs-var-log

出力されたログを見て、backup/restoreが行われていることを確認します。

less /backup/logs/$(date "+%Y%m%d")_var-log-backup.log

正しく実行されていれば、以下のようなログが見えると思います。

[ begin - backup ]
2021年  1月 11日 月曜日 22:39:57 JST
sending incremental file list
Xorg.0.log
Xorg.0.log.old
lastlog
wtmp

sent 316,804 bytes  received 95 bytes  633,798.00 bytes/sec
total size is 5,934,589  speedup is 18.73

[ begin - restore ] 2021年  1月 11日 月曜日 22:40:03 JST
sending incremental file list
Xorg.0.log
Xorg.0.log.old
alternatives.log
...

crontabで定期的にバックアップ

停電などによる消失を防ぐため、定期的に/var/logをバックアップします。crontabに以下の1行を追加します。
※毎時1分にバックアップスクリプトを実行。

1 * * * * /home/pi/scripts/var-log-backup.sh > /dev/null

以上で設定は完了です。再起動して、ログファイルにbackup/restoreの記録が残っており、/var/log が /backup/var-log と同じ構造になっていれば成功です。

(4)古い方法なので、(4-改訂版)を参照してください。※後日整理します。

既存の/var/log 構造を復活させるためのスクリプトは、インストールされているサービスによって異なってくるため、汎用的に対処できるよう「復活の呪文(復元するためのスクリプト)を自動生成するスクリプト」も作成しました。

※作業しやすいようファイルを分割し、ユーザーディレクトリで作業しています。自前の環境で実行する際には、ユーザーを読み替えたり、/usr/local/bin にスクリプトを置くなど適宜調整してください。

準備

作業前にバックアップを取っておきます。

$ sudo mkdir -p /back/var-log
$ sudo cp -r /var/log/* /back/var-log/

/var/log/ にあるログファイルやサービス用ディレクトリを調べて復元するためのリストを作成します。

#ログファイル
stat --format='%a %U:%G %n' `find /var/log/ -maxdepth 1 -mindepth 1 -type f` | grep -v '.gz\|.log$\|1$\|.old' > ~/logFiles 
#ログディレクトリ 
stat --format='%a %U:%G %n' `find /var/log/ -maxdepth 1 -mindepth 1 -type d` > ~/logDirs 

このリストを使って、復活の呪文を生成するスクリプト(make-init-logfiles.sh) を作成します。

$ vim ~/scripts/make-init-logfiles.sh

ファイルの中身:

#!/bin/sh
 
# 出力するスクリプトファイル
fScr="${HOME}/scripts/setup-logs.sh"
 
# ログファイル
stat --format='%a %U:%G %n' `find /var/log/ -maxdepth 1 -mindepth 1 -type f` | grep -v '.gz\|.log$\|1$\|.old' > ~/logFiles
cp -i ~/logFiles ~/logFiles.bak
 
# ログディレクトリ
stat --format='%a %U:%G %n' `find /var/log/ -maxdepth 1 -mindepth 1 -type d` > ~/logDirs
cp -i ~/logFiles ~/logDirs.bak
 
echo "#!/bin/sh" > $fScr
echo "\n### Make directories ###" >> $fScr
 
cat ~/logDirs | while read line
do
	echo "##" >> $fScr
	echo $line | awk '{print "mkdir -p " $3}' >> $fScr
	echo $line | awk '{print "chmod " $1 " " $3}' >> $fScr
	echo $line | awk '{print "chown " $2 " " $3}' >> $fScr 
done

echo "\n### Make files ###" >> $fScr

cat ~/logFiles | while read line
do
	echo "##" >> $fScr
	echo $line | awk '{print "touch " $3}' >> $fScr
	echo $line | awk '{print "chmod " $1 " " $3}' >> $fScr
	echo $line | awk '{print "chown " $2 " " $3}' >> $fScr 
done 
 
# 実行権をつけておく 
chmod +x $fScr

このコマンドを実行すると、最新の/var/log に必要なファイルやフォルダを作成するスクリプトができあがります。

$ sh ~/scripts/make-init-logfiles.sh

次に、起動・終了時に実行するスクリプトを作成していきます。

1) init.d にスクリプトを配置する

起動・終了時に呼び出されるスクリプトを作成します。

$ sudo touch /etc/init.d/setup-tempfs-logs
$ sudo chmod +x /etc/init.d/setup-tempfs-logs
$ sudo vim /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.shstartに指定して、必要なログファイルの準備を行うようにしています。

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

まず、バックアップ先を作成します

$ sudo mkdir -p /back/logs

スクリプトを作成します。中身はrsyncです。
(追記)作成場所としてスクリプト用フォルダが抜けていたので追記しました。後述するcrontabの中身と一致していればOKです。

$ vim /home/pi/scripts/backup-logs.sh
#!/bin/sh
LOG="/back/logs/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/ にログファイルとbackup-history.txtができていると思います。

2) boot時に実行されるよう登録

$ sudo insserv -d setup-tempfs-logs

確認

$ sudo ls /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 vim /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

(5)容量の大きなSDカードを使う

容量が少ないと、同じ場所を何度も読み書きする機会が増えるので、なるべく容量の大きなSDカードを使うと良いそうです(※らしい。まだ実感無し)。

(番外編)fstabでcommitの時間を長く設定する(ちょっとリスク高め)

ラズパイの掲示板で発見しました。システム領域のマウント設定で、commit値を大きめに設定するというものです。

$ cat /etc/fstab |grep mmcblk | grep ext4
/dev/mmcblk0p7  /               ext4    defaults,noatime  0       1

これを、このように書き換えます。

/dev/mmcblk0p7  /               ext4    defaults,noatime,commit=290  0       1

290秒(4分50秒)毎にファイルを書き込むようになります。
man mount によるとdefaultは30秒なので、約10倍書き込む頻度が減ることになります。
なお、300秒以上はwarningが表示されるとのことなので290としています。

この間に電源が落ちると変更は失われてしまいますので、使い方によって適宜調整したほうが良いでしょう。

ref. Script to extend SD card life

寿命を延ばす方法(運用編)

セットアップ時より、もっと大切になってくるのが「普段の使い方」。

特に大量のデータ頻繁にデータを書き換えるプログラムを作り始めたら要注意です!

ということで、今日から使える方法を紹介します(^o^)

1)RAMディスクを活用せよ!

一番良く使う方法。メモリー(RAM)上に作成されたファイルシステムで、RAMディスクとして知られています。Linuxには共有メモリとして /dev/shm が用意されていますので、プログラムの一時データ置き場として/dev/shmを使用すると良いです。

よくある使い方

  • 高頻度(1秒毎とか)にデータを取得して、ログファイルに書き出す
  • 10分毎に消えない場所(SDカード、ホームディレクトリなど)にコピー(もしくはrsync)する。

1秒毎にデータを取って都度書き込むプログラムを24時間動かす」なんてことをすると、

24*3600 = 86400回 (゚Д゚)!!

SDカードはもの凄い書き換えを強いられますwww
(2週間動かしたら120万回。SDカードも辛いっす)

これを、10分(600秒)に1回にするだけで、書込回数は144回まで減らせます。

本サイトに記載しているプログラムはほとんどこの方法を活用しているのでぜひ気をつけて見てみてください。

注意点

  • 電源が落ちると消える!
    RAMなので、シャットダウン・リブートしたり、停電で電源が落ちたりすると一瞬でデータが消えます。
    定期的に不揮発領域(SDカード等)にバックアップ(copy、rsync)しましょう。

2)外付けディスクを活用せよ!

OSが動いているSDカードが壊れるともうどうしようもありません。なので、外部のディスクにデータを書き込むことで少しでもSDカードの寿命を長くします。

よくある使い方

注意点

  • 外部接続のSSDやSDカードも条件は同じ!
    フラッシュメモリを使っている場合はSDカードだろうとSSDだろうと条件は同じなので、高頻度に書き込むと壊れます。プログラムの書き方には同様に注意が必要で、RAMディスクを使う方法と組み合わせるのがベストだと思います。

まとめ

ラズベリーパイのシステム用マイクロSDカードへの書込を減らして寿命を延ばす方法を紹介しました。

何事も、備えあれば憂いなし。

寿命を延ばした上でさらにSDカードのバックアップを取っておけば安心です。
その辺りはまた次の機会にまとめたいと思います。

それでは、良いラズベリーパイ生活を!

参考にしたサイト

Raspberry Pi

Posted by まーく