Skip to content

필사 모드: Linux ログ運用完全ガイド: journald、rsyslog、logrotate をひとつにつなぐ

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

はじめに

ログが原因の障害は二つの顔を持ちます。必要なときに存在しないか、必要ないときにディスクを埋めます。どちらもログの配管を設計せず、既定値のまま回した結果です。

このブログには 構造化ロギングの設計 があり、その記事はアプリケーションが何をどのフィールドで残すべきかを扱います。この記事はその下の層です。アプリケーションが吐いたログがホスト上でどこへ流れ、どれだけ残り、いつ消えるのかがテーマです。ログの内容ではなく、ログの配管の話です。

扱う範囲は三つの層です。systemd-journald が収集し、rsyslog がファイルとリモートへ送り、logrotate がファイルをローテーションします。基準は systemd 250 以上、rsyslog 8.x、logrotate 3.18 以上であり、ディストリビューションごとのパスの違いはその都度表記します。


1. ログが流れる経路

現代の Linux においてログの出発点はほとんどが journald です。入力は四つに分かれます。

  • カーネルリングバッファ(/dev/kmsg)
  • syslog ソケット(/dev/log)
  • ネイティブジャーナル API
  • サービスの標準出力と標準エラー

最後の項目が重要です。systemd ユニットとして実行されるプロセスが画面に出力するものはすべて自動的にジャーナルへ入ります。アプリケーションがファイルに直接書かなくてもログが残るという意味であり、コンテナ時代の標準的な慣行とも一致します。

ジャーナルから次の段階へ出ていく経路は二つです。

systemctl status systemd-journald.service
systemctl status rsyslog.service
ls -l /run/systemd/journal/

rsyslog は通常 imjournal モジュールでジャーナルを読むか、imuxsock でソケットを直接受け取ります。二つの方式を同時に有効にすると同じメッセージが二度記録されるため、どちらか一方だけを使わなければなりません。ディストリビューションの既定値が何かを確認してから始めてください。

grep -rn 'imjournal\|imuxsock' /etc/rsyslog.conf /etc/rsyslog.d/ 2>/dev/null

配管を設計するときにまず決めるべきなのは どこが単一の真実のソースなのか です。選択肢は三つあります。ジャーナルを原本としてファイルは補助にするか、ファイルを原本としてジャーナルは最小限だけ残すか、両方を持ったうえで中央収集器へ送る構成です。三つめが最も一般的であり、同時に最も重複が多い構成です。

判断基準はツールです。チームが使う調査ツールが journalctl 中心ならジャーナルを原本に、ファイルを読むエージェントや既存スクリプトが多いならファイルを原本にするのが自然です。重要なのは、どちらを選んだのかをドキュメントに書いておくことです。これが決まっていないと、後になってログが二か所に別々の形式でたまり、調査のたびにどちらが正しいかを確認する時間が上乗せされます。


2. journalctl — まず照会を身につける

設定を変える前に、まず照会に習熟しているべきです。照会が速ければ障害時間が短くなります。

journalctl -u nginx.service -n 200 --no-pager
journalctl -u nginx.service -f
journalctl -p err -b
journalctl -k -b -1
journalctl --since '2026-08-15 14:00' --until '2026-08-15 14:30'
journalctl -g 'timeout|refused' --since today
journalctl -o json-pretty -n 1
journalctl --list-boots
journalctl --user-unit myapp.service

オプションの正確な意味は次のとおりです。

  • -u はユニットまたはパターンでフィルタします。--user-unit はユーザーセッションのユニット用です。
  • -p は優先度フィルタで、名前と数字の両方を受け付けます。emerg(0)、alert(1)、crit(2)、err(3)、warning(4)、notice(5)、info(6)、debug(7) の順です。
  • -b はブート単位の照会です。-b -1 は直前のブートです。
  • -k はカーネルメッセージだけを見ます。
  • -g は MESSAGE フィールドに正規表現を適用します。
  • -o は出力形式であり、short、short-iso、verbose、json、json-pretty、cat などをサポートします。
  • -x はメッセージカタログの説明を付け加えます。見慣れないエラーに出会ったときに有用です。

ジャーナルは構造化フィールドを持っているため、フィールド単位の照会が可能です。これが一般的なテキストログとの決定的な違いです。

journalctl _PID=1234
journalctl _UID=1000 --since today
journalctl _SYSTEMD_UNIT=nginx.service _TRANSPORT=stdout
journalctl -F _SYSTEMD_UNIT | head -30

-F は特定のフィールドに存在するすべての値を列挙します。どのユニットがログを残しているかを一目で把握するときに使います。


3. ジャーナルの保存ポリシー — ディスクを守る設定

journald の既定動作はディストリビューションごとに異なります。最も大きな違いは ジャーナルがディスクに永続保存されるか です。

journalctl --disk-usage
ls -ld /var/log/journal /run/log/journal 2>/dev/null

/var/log/journal ディレクトリが存在すれば永続保存(persistent)、存在せず /run/log/journal だけがあれば揮発性(volatile)です。揮発性の場合は 再起動時に以前のログがすべて消えます。サーバーが突然再起動したときに原因を見つけられない、よくある理由です。

設定は /etc/systemd/journald.conf または /etc/systemd/journald.conf.d/ 以下の断片ファイルに置きます。

[Journal]
Storage=persistent
Compress=yes
SystemMaxUse=2G
SystemKeepFree=1G
SystemMaxFileSize=128M
MaxRetentionSec=30day
MaxFileSec=1day
RateLimitIntervalSec=30s
RateLimitBurst=10000
ForwardToSyslog=yes

各項目の意味を押さえます。

  • Storage=persistent/var/log/journal を作成して永続保存します。
  • SystemMaxUse はジャーナル全体が使う最大容量、SystemKeepFree は残しておく空き容量です。二つの条件のうち厳しいほうが適用されます
  • MaxRetentionSec は保存期間です。容量条件と一緒に設定しておくほうが安全です。
  • RateLimitIntervalSecRateLimitBurst は暴走するサービスがディスクを埋めるのを防ぎます。一つのサービスがログを流し込んで他のサービスのログを押し出す事故を防止する核心的な設定です。

適用は再起動で行います。

sudo systemctl restart systemd-journald
journalctl --disk-usage

すでに大きくなったジャーナルをすぐ縮めたい場合は vacuum 系のコマンドを使います。

sudo journalctl --vacuum-size=500M
sudo journalctl --vacuum-time=7d
sudo journalctl --vacuum-files=5

破壊的コマンドの警告: vacuum コマンドは保管されたジャーナルファイルを削除します。調査中の事件の記録がその範囲に入っていれば一緒に消えるため、ディスクが逼迫している状況でも必要な区間を先にファイルへ抜き出しておいてください。

journalctl --since '2026-08-15 00:00' -o export > /backup/journal-20260815.export

ジャーナルの整合性検査も可能です。

journalctl --verify

4. rsyslog — ファイルとリモートへ書き出す

ジャーナルだけで十分な環境もありますが、ファイルベースのツールと連携したりリモートの収集器へ送ったりするには rsyslog が必要です。

設定の構造は三つの部分です。モジュールのロード、ルール、テンプレートです。最新の文法(RainerScript)を使うほうが読みやすくなります。

module(load="imuxsock")
module(load="imklog")

template(name="DetailedFormat" type="string"
  string="%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n")

if $programname == 'myapp' then {
  action(type="omfile" file="/var/log/myapp/app.log" template="DetailedFormat")
  stop
}

*.info;mail.none;authpriv.none;cron.none  /var/log/messages
authpriv.*                                /var/log/secure

既定のログファイルのパスはディストリビューションごとに異なります。RHEL 系は /var/log/messages/var/log/secure、Debian/Ubuntu 系は /var/log/syslog/var/log/auth.log です。ドキュメントやスクリプトを移すときに最も頻繁に壊れる部分です。

リモート転送は TCP を使い、キューを挟むべきです。

action(type="omfwd"
  target="logs.example.com" port="514" protocol="tcp"
  queue.type="linkedlist"
  queue.filename="fwdRule1"
  queue.maxdiskspace="1g"
  queue.saveonshutdown="on"
  action.resumeRetryCount="-1")

queue.filename を指定するとメモリキューが満杯になったときにディスクへ退避します。action.resumeRetryCount="-1" は無限リトライです。収集サーバーが一時的に落ちてもログを失わないための最小構成であり、その代わりディスクの空きを必ず確認しなければなりません。キューファイルが無制限に大きくなれば、それ自体が障害になります。

設定の検証と適用は次のとおりです。

sudo rsyslogd -N1
sudo systemctl reload rsyslog
logger -p local0.info 'test message from operator'
sudo tail -5 /var/log/messages

rsyslogd -N1 は設定の文法だけを検査します。適用前に必ず実行してください。文法エラーがあると rsyslog が起動せず、その間のログが丸ごと消えます。


5. logrotate — ファイルが無限に育たないように

logrotate は定期的に実行され、条件に合うファイルをローテーションします。設定は /etc/logrotate.conf/etc/logrotate.d/ 以下に置きます。

/var/log/myapp/*.log {
    daily
    rotate 14
    maxsize 100M
    missingok
    notifempty
    compress
    delaycompress
    create 0640 myapp myapp
    su myapp myapp
    sharedscripts
    postrotate
        /bin/kill -USR1 $(cat /run/myapp/myapp.pid 2>/dev/null) 2>/dev/null || true
    endscript
}

各ディレクティブのドキュメント上の意味です。

  • rotate 14: 14回ローテーションするまで保管し、それ以降は削除します。
  • daily: 一日に一度ローテーションします。weeklymonthly もあります。
  • size: 指定サイズより大きくなったときにのみローテーションします。時間条件を無視します
  • maxsize: 時間条件と無関係にサイズを超えればローテーションします。
  • minsize: サイズを超える必要があり、かつ時間条件も満たす必要があります。
  • compress: 既定では gzip で圧縮します。
  • delaycompress: 圧縮を一周期遅らせます。まだファイルを掴んでいるプロセスがあるときに必要です
  • missingok: ファイルがなくてもエラーを出さずに進みます。
  • notifempty: 空であればローテーションしません。
  • create モード 所有者 グループ: ローテーション直後に同じ名前の新しいファイルを指定した権限で作ります。
  • su ユーザー グループ: 指定したユーザー・グループの権限でローテーションを実行します。
  • sharedscripts: ワイルドカードで複数のファイルがマッチしてもスクリプトを一度だけ実行します。
  • dateext: ローテーションしたファイル名に日付を付けます。
  • olddir: ローテーションしたファイルを別のディレクトリへ移します。

最も重要な選択は createcopytruncate のどちらを使うか です。

方式動作リスク
create(既定)原本の名前だけ変えて新しいファイルを作るプロセスが再オープンしなければ旧ファイルに書き続ける
copytruncateコピーしたあと原本を0に切り詰めるコピーと切り詰めの間のログが失われる可能性がある

create 方式では postrotate フックでプロセスに再オープンを知らせる必要があります。nginx は USR1、多くのデーモンは HUP を慣例として使います。この信号を送らないとプロセスは名前の変わった旧ファイルに書き続け、そのファイルは df から消えない幽霊容量になります。

copytruncate は再オープンさせられないプログラムに対する次善策です。ログ消失の可能性を受け入れる選択であることをドキュメント化しておいてください。

検証は必ず dry run で行います。

sudo logrotate --debug /etc/logrotate.d/myapp
sudo logrotate -d /etc/logrotate.conf
sudo logrotate -f /etc/logrotate.d/myapp
cat /var/lib/logrotate.status

--debug(-d)はドキュメントによれば 何も変更せず、状態ファイルも更新しません。安全にシミュレーションできます。-f は条件と無関係の強制ローテーションであり、実際にファイルを変更するので慎重に使ってください。

状態ファイルの既定の位置は /var/lib/logrotate.status です。ディストリビューションによってパスが異なることがあるので、実際のパスはユニットファイルや cron スクリプトで確認してください。


6. 何をどれだけ残すか

技術的な設定より難しいのはポリシーです。判断基準を三つの軸に分けて整理します。

軸1 — 調査可能性。障害原因を突き止めるには最低何日必要か。週末に発生して月曜に認知される問題を考慮すると、最低7日、実務的には14日が下限になります。

軸2 — 規制と監査。認証ログや権限変更ログは、保存期間が法律や社内規定で定められている場合が多いです。このログは他のログと分離して別のポリシーを適用してください。

軸3 — コスト。ディスクは有限です。原本を長く置くよりも 原本は短く、中央収集は長く 持つ構成が一般的です。

この三つの軸を反映した階層構成が実務の標準に近いものです。

階層保存目的
ホストのジャーナル7-14日即時調査、再起動原因の追跡
ホストのファイル14-30日ファイルベースのツール連携
中央収集90日横断調査、ダッシュボード
監査アーカイブ1年以上規制対応

容量の見積もりは実測が答えです。数日回してみてから計算してください。

journalctl --disk-usage
du -sh /var/log
du -x -h --max-depth=1 /var/log | sort -h | tail -10

計算は単純です。一日のログ量に保存日数を掛け、圧縮率を勘案したうえで、そこに余裕分を足します。圧縮はテキストログではおおむね大きく効きますが、すでに圧縮されたデータやバイナリのペイロードが混ざると効果が落ちるので、推定値より実測値を優先してください。そして 平常時ではなく障害時のログ量で算定しなければなりません。障害が起きるとエラーログが普段の数十倍に増え、そのときディスクが埋まると調査に必要なログまで失います。

ログがディスクを埋めてサービスが落ちる事故は、いまも最も多い障害類型のひとつです。防衛線を二重に置いてください。一つめは journald の SystemMaxUse と logrotate の maxsize、二つめは /var/log を別のファイルシステムに分離してルートファイルシステムまで埋めないようにすることです。ディスク容量問題の診断順序は Linux 障害対応コマンドガイド を参照してください。


7. よく遭遇する五つの罠

罠1 — ログを消したのに容量が減りません。プロセスがファイルを掴んでいます。

sudo lsof -nP +L1 | head

解決は削除ではなく、再オープン信号か再起動です。原理は ファイルディスクリプタと inode ガイド で扱います。

罠2 — 同じメッセージが二度記録されます。rsyslog がジャーナルとソケットを同時に読んでいるか、ForwardToSyslog が有効な状態で rsyslog も imjournal を使っている場合です。入力経路を一つに整理してください。

罠3 — 時刻が合いません。ジャーナルは既定でローカル時刻を表示しますが、ファイルログは別の形式かもしれません。調査するときは UTC に統一するほうが混乱が少なくなります。

journalctl --utc --since '2026-08-15 05:00' --until '2026-08-15 06:00'
timedatectl status

罠4 — コンテナのログがノードを埋めます。コンテナの標準出力はノードのファイルにたまります。コンテナランタイム側のローテーション設定が別途必要であり、この設定は logrotate と無関係に動作します。ノードのディスク警告が繰り返されるなら、ここから確認してください。

罠5 — 暴走するサービスが他のログを押し出します。journald の速度制限はサービス単位で調整できます。

[Service]
LogRateLimitIntervalSec=10s
LogRateLimitBurst=500

このディレクティブが使えるかどうかは systemd のバージョンによって異なるため、動作しない場合はインストール済みバージョンの systemd.exec のドキュメントで確認してください。


クイズ: 理解度を確認しましょう

クイズ1: サーバーが夜間に再起動しましたが、その時点のログが一つもありません。原因は?

答え: ジャーナルが揮発性モードだった可能性が高いです

解説: /var/log/journal ディレクトリがなければ、ジャーナルは /run/log/journal にだけ書かれ、再起動時に消えます。

ls -ld /var/log/journal /run/log/journal
journalctl --list-boots

--list-boots に以前のブートが見えなければ確定です。対処は永続保存の有効化です。

[Journal]
Storage=persistent
SystemMaxUse=2G

設定後に journald を再起動すれば、次のブートから記録が残ります。今回の事件のログは復元できません。

クイズ2: logrotate の設定を変更しました。実際のファイルに触れずに確認するには?

答え: --debug(-d) オプションでシミュレーションします

解説: ドキュメントによれば --debug はログに何の変更も加えず、状態ファイルも更新しません。

sudo logrotate --debug /etc/logrotate.d/myapp

出力から、どのファイルがどの条件でローテーション対象になるのか、postrotate スクリプトがいつ実行されるのかを確認できます。-f は強制ローテーションで実際の変更が起きるため、検証目的には不適切です。

クイズ3: ログファイルをローテーションしたのにアプリケーションが旧ファイルに書き続けます。何が抜けていますか?

答え: ローテーション後にプロセスへファイルの再オープンを知らせる postrotate フックがありません

解説: create 方式はファイル名だけを変えます。プロセスが掴んでいるファイルディスクリプタは名前ではなく inode を指すため、再オープンしなければ名前の変わった旧ファイルに書き続けます。

postrotate
    /bin/kill -USR1 $(cat /run/myapp/myapp.pid 2>/dev/null) 2>/dev/null || true
endscript

再オープンさせられないプログラムであれば copytruncate が代替になりますが、コピーと切り詰めの間のログが失われうる点を受け入れる必要があります。

クイズ4: ディスクが満杯になりました。ジャーナルを直ちに縮めつつ、調査に必要な区間は守るには?

答え: 必要な区間を先にファイルへ書き出してから vacuum を実行します

解説: vacuum コマンドは保管されたジャーナルファイルを削除するため、順序が重要です。

journalctl --since '2026-08-15 00:00' --until '2026-08-15 12:00' -o export > /backup/journal-incident.export
sudo journalctl --vacuum-size=500M
journalctl --disk-usage

根本的な対策は事後の削除ではなく上限の設定です。

[Journal]
SystemMaxUse=2G
SystemKeepFree=1G
MaxRetentionSec=14day
クイズ5: リモートのログ収集サーバーが30分間ダウンしました。その間のログを失わないためには何が設定されている必要がありますか?

答え: rsyslog のアクションにディスク支援キューとリトライ設定が必要です

解説: 既定のメモリキューは容量が小さくすぐ溢れます。

action(type="omfwd"
  target="logs.example.com" port="514" protocol="tcp"
  queue.type="linkedlist"
  queue.filename="fwdRule1"
  queue.maxdiskspace="1g"
  queue.saveonshutdown="on"
  action.resumeRetryCount="-1")

queue.saveonshutdown は再起動時にもキューを保存します。ただしキューファイルが大きくなればそれ自体がディスク問題になるため、queue.maxdiskspace を必ず一緒に指定してください。

クイズ6: 特定のユニットのログだけ、しかも標準出力から出たものだけを見たいです。どう照会しますか?

答え: ジャーナルの構造化フィールドを組み合わせてフィルタします

解説: ジャーナルはテキストではなくフィールドを保存するため、精密なフィルタが可能です。

journalctl _SYSTEMD_UNIT=nginx.service _TRANSPORT=stdout
journalctl -u nginx.service -o json-pretty -n 1
journalctl -F _TRANSPORT

-F で該当フィールドに存在する値の一覧を先に確認すれば、どの値を入れればよいかが分かります。こうした照会は一般的なテキストログでは真似しにくく、ジャーナルを使う実質的な理由のひとつです。


おわりに

ログの配管は一度設計しておけば数年もちます。そして設計しなければ、数年のあいだ静かに問題を積み上げます。新しいサーバーを作るときに次の五つだけ確認すれば、ほとんどの事故を予防できます。

ジャーナルは永続保存か。ジャーナルに容量上限があるか。ログファイルにローテーション方針があるか。ローテーション後に再オープン信号が届くか。/var/log がルートファイルシステムを埋めうる構造か

この五行をサーバー構築のチェックリストに入れてください。一度入れておけば、二度と考える必要がなくなります。残った時間はログの内容を良くすることに使うほうが、はるかに価値があります。


参考資料


関連記事

현재 단락 (1/234)

ログが原因の障害は二つの顔を持ちます。**必要なときに存在しないか、必要ないときにディスクを埋めます**。どちらもログの配管を設計せず、既定値のまま回した結果です。

작성 글자: 0원문 글자: 12,056작성 단락: 0/234