LoginSignup
7
7

More than 1 year has passed since last update.

ラズパイでログディレクトリをRAM DISKに移行する手順の簡略化

Last updated at Posted at 2020-10-06

我、ラズパイにおけるログディレクトリRAM DISK化の新興勢力とならむ
~無駄なディレクトリをrc.localで量産するのはもうやめよう。~


Raspberry PiSDカードの延命が必要なのは周知の事実です。
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

主流の手法(読み飛ばしていただいても結構)

だいたい以下のような感じかと思います。

  1. /etc/fstabを編集して/var/logをマウントするように設定
  2. /etc/rc.localを編集して起動時にディレクトリを作成するように設定
  3. 再起動
  4. マウントされていることをチェック
  5. 古い/var/logを削除

とりあえず一つツッコんでおきたいのが、マウント後にrm -rf /var/log/*を実行してもSDカード上のログディレクトリは消えないということ。

そしてもうひとつ/etc/rc.localでディレクトリを作成するのは誤りであるということ。

先生!rc.local君がまだ来てません!
/etc/rc.localの実行タイミングはとても遅いです。
試しにcron@rebootrc.localの実行タイミングを計ってみました。

デーモン名:秒_ミリ秒
crontab :38s_954ms
rc.local:41s_869ms #実にcron@rebootの3秒後!

今日のrc.localがどのように実行されているかというと、systemdrc-local.serviceというデーモンとしてキックされています。
このデーモンのユニットファイルを見ると起動順序はAfter=network.targetとなっていて、これはnginxApacheと同じです。
つまり、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/tmpRAMDISK化するなArch wikiに書かれています。
各容量(size=XXm)はデバイスに応じて調整してください。
/tmpは少なすぎるとエラーが発生し、/var/logは毎日再起動すれば16mでも十分である点を考慮してください。

/etc/fstab
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: 基本オプションを一括指定
size: 最大サイズを指定
noatime: パフォーマンス向上
mode: 権限 1   7   6  5
.         0:無  rwx rw rx
.         1:配下でファイル所有者のみが操作可(一時フォルダ向け設定)

再起動すればこれらがマウントされているはずです。
追記(2021/09/03)
以下に記載の★追記(エラー対策)をしないと起動できない可能性があります。必ず対策を行ってから再起動してください。強く推奨します。

コマンドdf -hで、Filesystemtmpfsとなっているディレクトリ/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 anywaySDカード側の/var/logは空じゃないよ!という意味ですので(3)でSD上の/var/logを空にすれば消えます。

★追記(エラー対策)

デバイスによって上記手法でエラーが出なくても極稀に正常に動作しない事がありましたので、念の為対策です。
実行しておくことをおすすめ致します。

追記(2021/09/03)
実行しない場合、OSが起動しない可能性があります。
必ず行うようにしてください。

sudo nano /lib/systemd/system/rsyslog.serviceで以下のようにAfter=local-fs.targetを追記。

/lib/systemd/system/rsyslog.service
[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)

全人類向け

systemdExecStartPreなんて便利な機能があります。

sudo nano /opt/arrange.shで以下のシェルスクリプトを作成します。
syslog上のエラーを確認して、保存しておいたlogtree.txtを参考に不足しているディレクトリとファイルに適宜書き換えてください。
Nginxでは以下のようにします。

/opt/arrange.sh
#!/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を追記してください。

/lib/systemd/system/rsyslog.service
[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を参考にすると良いでしょう。

/opt/arrange.sh
#!/bin/bash

mkdir /var/log/nginx # ディレクトリを作成
touch /var/log/nginx/error.log # 空ファイルを作成
touch /var/log/nginx/access.log # 空ファイルを作成
  1. sudo chmod a+x /opt/arrange.sh (実行権付与)
  2. sudo crontab -e (crontabを編集)
  3. @reboot /opt/arrange.sh左を追記 (再起動時に/opt/arrange.shを実行)

以上で再起動時に必要なディレクトリが作成されます。
再起動してエラーが出ないことを確認できればRAMDISK化完了です。

まめな人向け

本項目は非推奨です。(2021/3/30)

crontabは起動順序と手間のバランスから最適解ですが、最善の策とは言い難いです。
ここではsystemdを用いて正しい実装を行いたいと思います。

まず、実行するファイルを作成するところまでは上の怠惰な人向けと変わりませんので、同様に/opt/arrange.shを作成して実行権付与まで行います。

作成した/opt/arrange.shの実行タイミングは、tmpfsマウント後、かつrsyslog.service起動前が正しいと思われますので、以下のようにユニットファイルを記述します。

/etc/systemd/system/arrange.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経由の実行で明示するのが最善でしょう。

  1. sudo systemctl daemon-reload (ユニットファイル更新)
  2. sudo systemctl enable arrange.service (自動起動するように設定)

再起動してエラーが出ないことを確認できれば、ログディレクトリのRAMDISK化完了です。

(3) 旧ディレクトリの清掃

正しく移動できることが確認できたのでSDカード上のログディレクトリは削除します。

  1. sudo nano /etc/fstab => /var/logの行をコメントアウト
  2. 再起動
  3. sudo systemctl stop syslog.socket rsyslogでログを一時停止
  4. sudo nano /etc/fstab => /var/logのコメントアウトを外す
  5. sudo rm -rf /var/log/*でログディレクトリをクリア
  6. 再起動

/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へ移動するようにしました。

まずスクリプトを用意して、実行権を付与します。

/opt/log.sh
#!/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という形式で一週間保存されます。

  1. sudo mkdir /var/log_historyでディレクトリを用意します。
  2. sudo nano /lib/systemd/system/rsyslog.serviceExecStopを追記します。
/lib/systemd/system/rsyslog.service
[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は、Linuxsystemdに移行してから振る舞いが変わっています。
古い記事を参照するときにrc.localがあれば実行タイミングに気を使う必要がありそうですね。

なにはともあれ、このRAM DISK化でそれなりに延命が図られるはずです。
末永く素敵な木苺ライフをお楽しみ下さい。

7
7
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7