我、ラズパイにおけるログディレクトリRAM DISK化の新興勢力とならむ
~無駄なディレクトリをrc.local
で量産するのはもうやめよう。~
Raspberry Pi
でSDカードの延命が必要なのは周知の事実です。
Web上で見当たる主な手法は以下四つ。
-
SWAP
の無効化 - ログ削減
-
一時ディレクトリの
RAM DISK化
-
ログディレクトリの
RAM DISK化
上三つは簡単に行えますが、ログのRAM DISK化だけ難しそうなイメージがあり避けていました。
SDカードなんて大容量で安いし別にいいかぁ
と日和っていたのですが、ラズパイなんて趣味の世界、妥協したらそこで終わりと思い直してトライしてみました。
ところが、イメージ通りどのサイトを見ても面倒くさそうなことが書いてあります。
やめて!怠惰星からやって来た僕のライフはもうゼロよ!
僕はクーデターを起こそうと思います。
前提条件
まず、言うまでもないですが自己責任でお願いいたします。
ラズパイが動かなくなろうが壊れようが爆発しようが一切の責任を負いません。
爆発はちょっと気になりますが。
今回のスタンスはエラーが出なければOK
です。
相容れない方にはお勧めいたしません。
環境
Device => Raspberry Pi 4 2GB
OS => Raspberry Pi OS Lite
$ uname -a
Linux raspberrypi 5.4.51-v7+ #1333 SMP Mon Aug 10 16:45:19 BST 2020 armv7l GNU/Linux
主流の手法(読み飛ばしていただいても結構)
だいたい以下のような感じかと思います。
-
/etc/fstab
を編集して/var/log
をマウントするように設定 -
/etc/rc.local
を編集して起動時にディレクトリを作成するように設定 - 再起動
- マウントされていることをチェック
- 古い
/var/log
を削除
とりあえず一つツッコんでおきたいのが、マウント後にrm -rf /var/log/*
を実行してもSDカード上のログディレクトリは消えないということ。
そしてもうひとつ/etc/rc.local
でディレクトリを作成するのは誤りであるということ。
先生!`rc.local`君がまだ来てません!
`/etc/rc.local`の実行タイミングはとても遅いです。 試しに`cron@reboot`と`rc.local`の実行タイミングを計ってみました。`デーモン名:秒_ミリ秒` `crontab :38s_954ms` `rc.local:41s_869ms #実にcron@rebootの3秒後!`
今日の`rc.local`がどのように実行されているかというと、**`systemd`の`rc-local.service`というデーモンとしてキック**されています。 このデーモンのユニットファイルを見ると起動順序は`After=network.target`となっていて、これは`nginx`や`Apache`と同じです。 つまり、**`nginx`等より後に実行される**(nginx起動時に必要なファイルを用意できない)可能性が十分にありますから、`rc.local`に記述するのは誤りということになります。~~((多分。))~~
(1) RAM上にディレクトリをマウント
まず、素晴らしいコマンドを用いてログディレクトリを把握しておきます。
ホームディレクトリで以下のコマンドを実行しましょう。
$ sudo find /var/log | sort | sed '1d;s/^\.//;s/\/\([^/]*\)$/|--\1/;s/\/[^/|]*/| /g' > logtree.txt
$ cat logtree.txt
| | |--alternatives.log
| | |--apt
| | | |--eipp.log.xz
| | | |--history.log
| | | |--term.log
| | |--auth.log
| | |--auth.log.1
| | |--btmp
| | |--daemon.log
| | |--daemon.log.1
| | |--debug
(以下略)
それでは**sudo nano /etc/fstab
で以下を追記しましょう。
ちなみに/var/tmp
はRAMDISK
化するな**とArch wikiに書かれています。
各容量(size=XXm
)はデバイスに応じて調整してください。
/tmp
は少なすぎるとエラーが発生し、/var/log
は毎日再起動すれば16m
でも十分である点を考慮してください。
tmpfs /tmp tmpfs defaults,size=128m,noatime,mode=1777 0 0 #一時フォルダのRAM DISK化
tmpfs /var/log tmpfs defaults,size=32m,noatime,mode=0755 0 0
fstabのオプションについて
**`defaults`:** [基本オプションを一括指定](https://wiki.archlinux.jp/index.php/Fstab#.E3.83.95.E3.82.A3.E3.83.BC.E3.83.AB.E3.83.89.E3.81.AE.E5.AE.9A.E7.BE.A9) **`size`:** 最大サイズを指定 **`noatime`:** [パフォーマンス向上](https://wiki.archlinux.jp/index.php/Fstab#atime_.E3.82.AA.E3.83.97.E3.82.B7.E3.83.A7.E3.83.B3) **`mode`:** 権限 **`1 7 6 5`** . **0**:無 **rwx** **rw** **rx** . **1**:配下でファイル所有者のみが操作可(一時フォルダ向け設定)再起動すればこれらがマウントされているはずです。
追記(2021/09/03)
以下に記載の★追記(エラー対策)
をしないと起動できない可能性があります。必ず対策を行ってから再起動してください。強く推奨します。
コマンドdf -h
で、Filesystem
が**tmpfs
となっているディレクトリ/var/log
**を確認できれば無事完了です。
ここで、ls -l /var/log
と打ちログディレクトリを確認すると、いくつかのログファイルが自動的に作成されているのが確認できます。
試しにsudo apt update
を実行してみると、/var/log/apt
が自動で作成されるはずです。(アップデートがあれば)
このようにディレクトリを用意してあげなくてもシステムの起動自体に支障はありませんし、多くの場合は自動でディレクトリ/ファイル
が作成されます。
ただ、nginx
など一部のデーモンはエラーとなって起動できないのでcat /var/log/syslog | grep /var/log
などでログディレクトリに関するエラーを確認してみましょう。
No such file
などの文言が含まれるエラーがあれば、次のステップに進んで該当するファイルを作成しなければなりません。
特にそのようなエラーが見当たらなければログディレクトリのRAMDISK
化は以上です。
ちなみに、次のエラーDirectory /var/log to mount over is not empty, mounting anyway
は**SDカード側の/var/logは空じゃないよ!**という意味ですので(3)でSD上の/var/log
を空にすれば消えます。
★追記(エラー対策)
デバイスによって上記手法でエラーが出なくても極稀に正常に動作しない事がありましたので、念の為対策です。
実行しておくことをおすすめ致します。
追記(2021/09/03)
実行しない場合、OSが起動しない可能性があります。
必ず行うようにしてください。
sudo nano /lib/systemd/system/rsyslog.service
で以下のようにAfter=local-fs.target
を追記。
[Unit]
Description=System Logging Service
Requires=syslog.socket
After=local-fs.target # ここを追記!
Documentation=man:rsyslogd(8)
Documentation=https://www.rsyslog.com/doc/
[Service]
Type=notify
ExecStart=/usr/sbin/rsyslogd -n -iNONE
StandardOutput=null
Restart=on-failure
# Increase the default a bit in order to allow many simultaneous
# files to be monitored, we might need a lot of fds.
LimitNOFILE=16384
[Install]
WantedBy=multi-user.target
Alias=syslog.service
要するに、マウントが完了してからログサービスを起動しましょうということです。
(2) エラー対策
不幸にも自分でログディレクトリを作成してくれないデーモンに行き会った方々へ。
例としてnginx
を用いて、エラーへの対処法を記します。
怠惰な人向けと、まめな人向けの二つ手法を用意しました。
全人類向けに統一しました。(2021/3/31)
全人類向け
systemd
にExecStartPre
なんて便利な機能があります。
sudo nano /opt/arrange.sh
で以下のシェルスクリプトを作成します。
syslog
上のエラーを確認して、保存しておいたlogtree.txt
を参考に不足しているディレクトリとファイルに適宜書き換えてください。
Nginx
では以下のようにします。
#!/bin/bash
mkdir /var/log/nginx # ディレクトリを作成
touch /var/log/nginx/error.log # 空ファイルを作成
touch /var/log/nginx/access.log # 空ファイルを作成
実行権を付与します。
sudo chmod a+x /opt/arrange.sh
ユニットファイルを編集します。
sudo nano /lib/systemd/system/rsyslog.service
以下のようにExecStartPre
を追記してください。
[Unit]
Description=System Logging Service
Requires=syslog.socket
After=local-fs.target # エラー対策
Documentation=man:rsyslogd(8)
Documentation=https://www.rsyslog.com/doc/
[Service]
Type=notify
ExecStartPre=/opt/arrange.sh # ここを追記!
ExecStart=/usr/sbin/rsyslogd -n -iNONE
StandardOutput=null
Restart=on-failure
# Increase the default a bit in order to allow many simultaneous
# files to be monitored, we might need a lot of fds.
LimitNOFILE=16384
[Install]
WantedBy=multi-user.target
Alias=syslog.service
ExecStartPre
は、その名の通りExecStart
の前に実行されます。
一行で完結するので全人類にぴったりだと思います。
再起動してエラーが出なければRAMDISK化
完了です。
お疲れ様でした。
**(3)**の清掃に進んでください。
エラーが出てしまった人は再度不足しているファイルを確認し、正しいディレクトリを作成できるように試行錯誤してください。
追記(2021/07/19)
@farmertaki 様よりmysql
の対策についてコメントいただきました。
ありがとうございます。
導入されている方はコメント欄をご参照ください。
怠惰な人向け
本項目は非推奨です。(2021/3/30)
cronでお手軽にディレクトリを作成します。
まず、sudo nano /opt/arrange.sh
で以下のシェルスクリプトを作成します。
syslog
上のエラーを確認し、不足しているディレクトリとファイルを設定してください。
階層構造は保存しておいたlogtree.txt
を参考にすると良いでしょう。
#!/bin/bash
mkdir /var/log/nginx # ディレクトリを作成
touch /var/log/nginx/error.log # 空ファイルを作成
touch /var/log/nginx/access.log # 空ファイルを作成
-
sudo chmod a+x /opt/arrange.sh
(実行権付与) -
sudo crontab -e
(crontabを編集) -
@reboot /opt/arrange.sh
左を追記 (再起動時に/opt/arrange.sh
を実行)
以上で再起動時に必要なディレクトリが作成されます。
再起動してエラーが出ないことを確認できればRAMDISK化完了です。
まめな人向け
本項目は非推奨です。(2021/3/30)
crontab
は起動順序と手間のバランスから最適解ですが、最善の策とは言い難いです。
ここではsystemd
を用いて正しい実装を行いたいと思います。
まず、実行するファイルを作成するところまでは上の怠惰な人向け
と変わりませんので、同様に/opt/arrange.sh
を作成して実行権付与まで行います。
作成した/opt/arrange.sh
の実行タイミングは、tmpfsマウント
後、かつrsyslog.service
起動前が正しいと思われますので、以下のようにユニットファイルを記述します。
[Unit]
Description=Arrange log directory
After=local-fs.target
Before=rsyslog.service
[Service]
ExecStart=/opt/arrange.sh
Type=oneshot
RemainAfterExit=True
[Install]
WantedBy=multi-user.target
local-fs.target
は**/etc/fstab
の記述に従ってファイルシステムをマウント**してくれるサービスです。
多くの場合crontab @reboot
も同様のタイミングで実行され、結果的に上の条件を満たしますが、systemd
経由の実行で明示するのが最善でしょう。
-
sudo systemctl daemon-reload
(ユニットファイル更新) -
sudo systemctl enable arrange.service
(自動起動するように設定)
再起動してエラーが出ないことを確認できれば、ログディレクトリのRAMDISK
化完了です。
(3) 旧ディレクトリの清掃
正しく移動できることが確認できたのでSDカード上のログディレクトリは削除します。
-
sudo nano /etc/fstab
=>/var/log
の行をコメントアウト - 再起動
-
sudo systemctl stop syslog.socket rsyslog
でログを一時停止 -
sudo nano /etc/fstab
=>/var/log
のコメントアウトを外す -
sudo rm -rf /var/log/*
でログディレクトリをクリア - 再起動
/var/log
に直で書き込みに来るデーモン(rbfeeder
など)があればそれも止めてからクリアしてください。
Directory /var/log to mount over is not empty, mounting anyway
がエラーとして出ている間はSDのログディレクトリがクリアな状態でシステム終了できていません。
どのデーモンが書き込みに来てるか分からない場合は、自分でインストールしたデーモンをとにかく止めてみましょう。
備考
RAM DISK化後は、定期的に再起動してあげないとRAM上に用意した領域を使い切ってしまいますので、僕はcron
で毎晩深夜に再起動させてます。
sudo crontab -e
を実行して以下のように追記します。
16 3 * * * /sbin/reboot # 毎日3時16分に再起動
ログの保存
やはり再起動で消えてしまうのはログとしてよろしくありません。
以下のようにして、シャットダウン時にSDへ移動するようにしました。
まずスクリプトを用意して、実行権を付与します。
#!/bin/bash
tar Jcf /var/log_history/`date "+%y%m%d_%H%M%S"`.tar.xz /var/log
find /var/log_history -name '*' -mtime +7 -delete
これを毎シャットダウン時に実行すると、/var/log
配下のディレクトリとファイルが全て/var/log_history/YYMMDD_HHMMSS.tar.xz
という形式で一週間保存されます。
-
sudo mkdir /var/log_history
でディレクトリを用意します。 -
sudo nano /lib/systemd/system/rsyslog.service
で**ExecStop
を追記**します。
[Unit]
Description=System Logging Service
Requires=syslog.socket
After=local-fs.target # エラー対策
Documentation=man:rsyslogd(8)
Documentation=https://www.rsyslog.com/doc/
[Service]
Type=notify
ExecStartPre=/opt/arrange.sh # エラー対策
ExecStart=/usr/sbin/rsyslogd -n -iNONE
ExecStop=/opt/log.sh # !!ここを追記!!他を書き換えるとログが止まってしまうかもしれないので注意!!
StandardOutput=null
Restart=on-failure
# Increase the default a bit in order to allow many simultaneous
# files to be monitored, we might need a lot of fds.
LimitNOFILE=16384
[Install]
WantedBy=multi-user.target
Alias=syslog.service
僕はrsyslog.service
終了時のコマンドとして実行するように設定しました。
最後にsudo systemctl daemon-reload
でユニットファイルをリロードしたら再起動してみてください。
/var/log_history
配下にYYMMDD_HHMMSS.tar.xz
というファイルが存在していれば成功です。
ちなみに.xz
は高能率な圧縮形式で解凍はsudo tar xvf hogehoge.tar.xz
です。
Congratulations!!
クーデターといった割には変わらずに煩雑な手法になってしまいました。
rc-local.service
のユニットファイルに**/etc/rc.local Compatibility
とあるように、/etc/rc.local
は、Linux
がsystemd**に移行してから振る舞いが変わっています。
古い記事を参照するときにrc.local
があれば実行タイミングに気を使う必要がありそうですね。
なにはともあれ、このRAM DISK化でそれなりに延命が図られるはずです。
末永く素敵な木苺ライフをお楽しみ下さい。