log2ramの仕組みを読み解きました!

log2ram

↓にlog2ramというパッケージがありました。今回はこれを読み解いてみます。

Just a moment...

log2ramとはなんぞや

読んで字の如し、ログをRAM上に保存します。
例えばsyslogは通常/var/log/以下に保存、HDDやRaspberry PiならmicroSDに保存します。
log2ramを使うと保存場所をRAM(=メモリ)に移すことができます。

何がメリットなのか

Raspberry Piを想定していて、ストレージの寿命を延ばすことができます。
Raspberry Piは/var/log/がmicroSD上にあります。
従ってsyslogなどでログを書き込むたびにmicroSDへの書き込みが発生します。
 実際はsync等でkernelバッファからmicroSDに物理的な書込が発生するときです。

そのため、ログの頻度にもよりますが、microSDの寿命に悪影響を与えます。
TLCであるmicroSDでの書込限度は1000回とも。
ログを書き込みすぎると、ある日microSD内のデータが読めなくなり、
Raspberry Piが起動しなくなることもあり得ます。

microSDの種類書込限度(目安)
SLC100000回
MLC10000回
TLC1000回
参考元: https://tokusengai.com/_ct/17259668

ですので、ログ等頻繁に書き込みが発生するようなデータはRAM上で取り扱いたいものです。
log2ramを使うとログを保存するプロセスは何も変更せず実現できます!

ちなみにlog2ram自体はdebian向けにも提供されているようです。
ソースコードはgithubで公開されています。

GitHub - azlux/log2ram: ramlog like for systemd (Put log into a ram folder)
ramlog like for systemd (Put log into a ram folder) - GitHub - azlux/log2ram: ramlog like for systemd (Put log into a ra...

ファイル構成

インストールすると、以下の構成で展開されます。

名前で何をしているかは推測できますね。

パス役割
/etc/log2ram.conf設定ファイル
/etc/cron.daily/log2ramcronで毎日実行用スクリプト
/etc/logrotate.d/log2ramログローテート用スクリプト
/etc/systemd/system/log2ram.serviceサービス登録用ファイル
/usr/local/bin/log2ramメインの実行ファイル
/usr/local/bin/uninstall-log2ram.shアンインストール用スクリプト

動作 – 起動時 –

起動時は
 /usr/local/bin/log2ram start
が動作します。
動きは以下のような感じです。
対象ディレクトリは/var/log/がデフォルトです。
 log2ram.confで変更可能。

start時に動くコードは以下の部分です。

start)
        IFS=';'
        for i in $PATH_DISK; do
            PATH_FIRST_PART=$( echo ${i%/*} )
            PATH_LAST_PART=$( echo ${i##/*/} )
            RAM_LOG=$i
            HDD_LOG=$PATH_FIRST_PART/hdd.$PATH_LAST_PART
            LOG2RAM_LOG="${RAM_LOG}/${LOG_NAME}"
            
            make_log_dir
            
            mount --bind $RAM_LOG/ $HDD_LOG/
            mount --make-private $HDD_LOG/
            wait_for $HDD_LOG

            if [ "$ZL2R" = true ]; then
                createZramLogDrive
                mount -t ext4 -o nosuid,noexec,noatime,nodev,user=log2ram /dev/zram${RAM_DEV} ${RAM_LOG}/
            else
                mount -t tmpfs -o nosuid,noexec,noatime,nodev,mode=0755,size=${SIZE} log2ram $RAM_LOG/
            fi
            wait_for $RAM_LOG
            syncFromDisk
        done
        ;;

PATH_DISK=”/var/log”です。従って、
 HDD_LOG=/var/hdd.log/
となります。

/var/log/を/var/hdd.log/にbindさせた後、ZL2Rの値によって分岐します。

ZL2R動作
true/dev/zram0をext4でフォーマットし、/var/log/にマウント
false/var/log/をtmpfsとしてマウント

zramはkernelの機能で自動圧縮展開機能付RAMディスクです。
(後述)

最後にsyncFromDiskで/var/hdd.log/の中身を/var/log/に全コピーしlog2ram起動は終了です。
以降は/var/log/以下にファイル書込すると、作成したext4もしくはtmpfsに書き込まれます。
 もともとの/var/log/は/var/hdd.log/に退避されています。

注意点 – 起動時 –

log2ramの起動処理と並行して/var/log/に書込みをするプロセスが存在する場合、タイミングによってはログ内容に不整合が発生する場合があります。
↓の表で黒字がsyslog、青字がlog2ram起動処理とします。

Bではsyslogが/var/log/に書き込み、その後/var/hdd.log/の中身を/var/log/にコピーします。
もしsyslog/log2ramが取り扱うファイル名が同じだった場合、期待したファイル内容にならない可能性があります。

正常ケース(A)不整合が発生するケース(B)
syslogが/var/log/に何か書き込む
/var/log/を/var/hdd.log/にbind
/var/hdd.log/の中身を/var/log/にコピー
/var/log/を/var/hdd.log/にbind
syslogが/var/log/に何か書き込む
/var/hdd.log/の中身を/var/log/にコピー

これを回避するには、log2ramを起動後一番最初に動作させることです。
実機では確認できていませんので、使う際はご注意ください。

動作 – シャットダウン時 –

終了時の動作は以下のコードです。

stop)
        IFS=';'
        for i in $PATH_DISK; do
            PATH_FIRST_PART=$( echo ${i%/*} )
            PATH_LAST_PART=$( echo ${i##/*/} )
            RAM_LOG=$i
            HDD_LOG=$PATH_FIRST_PART/hdd.$PATH_LAST_PART
            LOG2RAM_LOG="${RAM_LOG}/${LOG_NAME}"
            
            syncToDisk
            #ZRAM_LOG=$(awk '$2 == "/var/log" {print $1}' /proc/mounts)
            #ZRAM_LOG=$(echo ${ZRAM_LOG} | grep -o -E '[0-9]+')
            umount -l $RAM_LOG/
            umount -l $HDD_LOG/
            # Unsure as even with Root permision denied
            #echo ${ZRAM_LOG} > /sys/class/zram-control/hot_remove
        done
        ;;

syncToDiskでは以下のコードが動作します。
/var/log/の中身を/var/hdd.log/にコピーしています。

syncToDisk () {
    isSafe
    #INITIAL_STATE=$(remountRW)

    if [ "$USE_RSYNC" = true ]; then
        rsync -aXv --inplace --no-whole-file --delete-after $RAM_LOG/ $HDD_LOG/ 2>&1 | tee -a $LOG2RAM_LOG
    else
        cp -rfup $RAM_LOG/ -T $HDD_LOG/ 2>&1 | tee -a $LOG2RAM_LOG
    fi
    #remountOriginal ${INITIAL_STATE}
}

これをやっている目的は、HDD/microSDにログを残すためです。
起動時に/var/log/はzram or tmpfs、つまりRAMディスクにマウントします。
従ってHDD/microSDに書き戻さないと電源OFF時にログが消えてしまいます。

/var/hdd.log/は電源ON直後に/var/log/の全コピーをしていましたね。
/var/自体はHDD/microSDですので、/var/hdd.log/もHDD/microSDです。
ですので、/var/hdd.log/にコピーすればシャットダウン後も残ります。

注意点 – シャットダウン時 –

↑はシャットダウン処理を行うときに動作します。
電源供給が遮断された場合は動作しません。
 Raspberry Piの電源コードをひっこ抜く、停電、等

シャットダウン時も起動時と同様ログ整合性の問題があります。
回避するにはlog2ramの終了処理を一番最後に実行させることです。

zramとは

zram, formerly called compcache, is a Linux kernel module for creating a compressed block device in RAM, i.e. a RAM disk, , but with on-the-fly “disk” compression.

https://en.wikipedia.org/wiki/Zram

↑の通り説明がありました。
compcacheというkernelモジュール後継でオンザフライ圧縮/展開機能付RAMディスクのこと。
今回は、/var/log/以下に何か書き込むとRAM上では圧縮された状態で保持するということですね。
逆も然りで、/var/log/以下からの読出データは自動的に展開された状態で読み出せます。

圧縮して保持するのでtmpfsに比べてRAM量の節約になりますし、
アプリケーションプロセスは圧縮/展開を意識することなく取り扱えます。

圧縮方式など、いくつかのパラメーターはアプリケーションで設定が可能です。
log2ramでも↓の通りcreateZramLogDrive()で設定をしています。

echo ${COMP_ALG} > /sys/block/zram${RAM_DEV}/comp_algorithm
echo ${LOG_DISK_SIZE} > /sys/block/zram${RAM_DEV}/disksize
echo ${SIZE} > /sys/block/zram${RAM_DEV}/mem_limit

普段使いするなら?

wikipediaにも書いてありますが、主な用途は以下。
 A. tmp/など一時的なデータ置き場
 B.スワップ領域
compcacheではB.だけを想定していたようです。

A.としてはブラウザーのキャッシュデータ置き場などに使えそうですね。

終わりに

いかがでしたか。
個人的には前職で似たような設計をしたことがあったので
 「もっと早く知っていれば…」
と頭をよぎってしまいました。

万能というわけではありませんが、書込回数が気になる、RAM容量に余裕がない、といったシステムでは導入を検討してみてはいかがでしょうか?

この記事を書いた人

組込ソフト歴15年の外資系エンジニア。
前職で組込Linuxを使った商品設計/品質確保の業務に従事。
Raspberry Piが好き。
株式投資で年50万円を稼ぐ。
妻は香港人(国際結婚)。

Please follow me
log2ram
Please follow me

Comments

タイトルとURLをコピーしました