ログインユーザが100人を越えるときにはjournalの容量に注意

Red Hatの森若です。

今回はsystemd-journald と、systemd-journaldがログ情報を保存するjournalのローテートについての話です。

結論

ユーザが多い環境では /etc/systemd/journald.conf で SplitMode=noneに変更しよう。

systemd-journald とは?

systemd-journaldは、systemdと統合されたログ収集・保存サービスです。 従来から syslogで使われてきた /dev/log や systemd unitの標準出力・標準エラー出力など様々な入力をうけつけ、 専用の journal とよばれるバイナリログへ保存します。

従来のsyslogによるログと比較して、journalではデータが構造化されていてたとえばログ優先度や 「x月y日10:10から10:30まで」のような検索ができるほか、 マイクロ秒単位のタイムスタンプ群や関連するsystemd unitなど追加のメタデータを含んでいてアプリケーションからの拡張も可能で便利に利用できます。

RHEL 7,8,9のデフォルトでは、journalはtmpfs上に作成され、再起動により消えます。 rsyslogのimjournal pluginがjournalを読み、/var/log/messagesなどに保存します。 つまりjournalは主にrsyslogへの中継役として利用されています。

journalは中継役として使うだけではなく専用のコマンド journalctl を利用して検索・閲覧できます。 このときさきほどのメタデータによる検索が役に立ちます。 systemctl status chronyd.service のようなコマンドでサービスに関連した最近のログが表示されるのも journalを活用したものです。

journalはtmpfs上で利用するほかに、通常のファイルシステム上へ保存して利用することもできます。 利用開始は非常に簡単で、root権限で /var/log/journal ディレクトリを作成し、systemd-journaldを再起動するだけです。

# mkdir /var/log/journal
# systemctl restart systemd-journald

journalは便利ですが、長期間・大量のログに対応するログ管理ソリューションではありません。 たとえばjournalctlによる検索・表示はjournal全体を読み込みます。数GB〜10GB程度におさまる範囲で利用するのがよいでしょう。

journalファイル群

journal は、複数のファイルからなっています。通常のシステム管理で直接操作することはありませんが、 /run/log/journal または /var/log/journal ディレクトリの中身を見てみます。 journalを構成する各ファイルを「journalファイル」と呼んでjournalと区別します。

以下の例は私がデスクトップ用途に常用している環境のものです。ユーザ数や設定により細かな違いはありますが、見た目はおおむねこのようになります。

$ tree /var/log/journal/
/var/log/journal/
└── 0e5c275b71674a68a37d977e8902c7c7
(中略)
    ├── system@9946b0564b1841efba5ba6f72db3d490-00000000000f8e55-0005f02c9e33e417.journal
    ├── system@9946b0564b1841efba5ba6f72db3d490-00000000000fe0e8-0005f040c28107ac.journal
    ├── system.journal
(中略)
    ├── user-1000@e5f646cd6f8249748eb5d5759a730a90-00000000000f8e69-0005f02cbe48cdc9.journal
    ├── user-1000@e5f646cd6f8249748eb5d5759a730a90-00000000000fe1b3-0005f040c284aad9.journal
    └── user-1000.journal

ファイル名が長くて面倒そうですが、前述のとおりこれらのファイル群を直接操作することは基本的にありません。 以下簡単にまとめます。

  • /run/log/journal または /var/log/journal の下のディレクトリは、machine-id です。複数マシンのjournalを集約しても競合しないように、システム毎に一意なmachine-idでディレクトリが作成されています。
  • system.journal と user-<UID>.journal が、最新の journal ファイルです。新しいログはこのファイルに保存されます。journalファイルは内容がログ1行相当でも最低8MiBで作成され、必要に応じて8MiB単位で大きくなります。
    • 命名規則から想像がつくように、多数のユーザがログインする環境では user-<UID>.journal は各ユーザに対応します。
    • 1度でもログインしたことがある全ユーザのjournalファイルが作成されます。ssh接続やコンソールからのログイン、GUIセッションなどのユーザの対話的なセッション内で発生したログはこちらに保存されます。
    • journalファイルのuidはrootでgidはsystemd-journalです。ただし該当ユーザに読み込みを許可するACLが設定されるため、ユーザは特権やsystemd-journalグループへの参加なしで読み込めます。つまりjournalctl --userコマンドで自分のセッションのログを閲覧できます。
    • ログインをともなわない、単にUIDが設定されるだけのサービスのログはsystem.journalに保存されます。
  • system@9946b0564b1841efba5ba6f72db3d490-00000000000f8e55-0005f02c9e33e417.journal のような長いファイル名のファイルは、ローテートされた過去のjournalです。 興味がある方のために紹介すると (もとのファイル名のbase部分)@(namespaceで決まる長い数字)-(journalのカーソル位置)-(時刻).journal のような規則で命名されています。

journalのローテートと削除

systemd-journaldはログをjournalに保存するタイミングで、時間・サイズなどの条件を判定してjournalを自動的にローテートします。デフォルトでは記録をはじめて30日経過する、全体で利用できる容量(デフォルトは 4GiBまたはパーティションの10%のどちらか小さなサイズ )の1/8のサイズになる等の契機でローテートします。

systemd-journaldによるローテートはlogrotateと異なり定期的に実行するものではなく、ログ出力タイミングで行われる点に違和感があるかもしれません。system.journal はシステムが単純に動作しているだけでもログが出力されますが、user-<UID>.journal は該当するユーザのログインがなければ全く出力されませんから、ローテートもずっと行われません。 一部journalファイルが古すぎて気になるような場合は、journalctl --rotate コマンドで強制的にローテートを発生させることができます。

ローテートされた過去のjournalは、やはりログ出力のタイミングで設定に従い削除されます。最新のjournalファイルが削除されることはありません。

1000人のユーザがいるシステム

さて、1000人のユーザが散発的にログインして利用するシステムを考えてみます。共用されるHPCクラスタや大学の演習環境のようなイメージです。

  1. user-<UID>.journal が1000個(!)できます。1つ最低8MiBですから、最低で8GiB弱になります。
  2. 8GiB弱のサイズは、systemd-journaldがデフォルトで設定する「4GiBまたはパーティションの10%のどちらか小さなサイズ」を越えています。
  3. 最新のjournalファイルは削除されませんが、ローテートにより作成された過去のjournalは、サイズ上限に影響されるので作成後すぐに削除されます。

この状況でsystem.journalにローテートが発生すると、journalからサービスなどの過去のログがほとんど消えてしまいます。その後徐々にログが蓄積されていき、次回のローテートでまた消えるという動作の繰り返しになります。これは用途によらずまずい動作です。

SplitMode設定

このようなケースでは、各ユーザセッションのログも1つのjournalファイルにまとめて記録する方が期待に近い動作になります。 /etc/systemd/journald.conf で以下の設定を行います。デフォルトでは "uid" となっていますが、これを "none" とすると全体が systemd.journalに記録されます。

SplitMode=none 

ファイルをユーザ毎に分割しないことで、ローテーション時に参照できる過去のログが極端に少なくなる現象を避けられます。 1つのファイルにまとめた結果として、各ユーザがデフォルトでは自分のセッションのログを読めません。システム全体のログを読むのと同じ権限が必要になります。

結論(再)

ユーザが多い環境では /etc/systemd/journald.conf で SplitMode=noneに変更しよう。

まとめ

journal 利用上のポイント

  • journalは複数のファイルに分けられた形で存在する
  • journalの操作はjournalctlコマンドと systemd-journald の設定でおこなう
  • logrotateではなく systemd-journald自身がローテートをおこなう
  • 古いjournalはsystemd-journaldがローテートのタイミングで削除する
  • 削除条件は複数あるがサイズ上限の典型的な値は4GiB
  • 最新のjournalは条件によらず削除されない

ユーザが多いときのjournalの注意点

  • 最新のjournalファイルは内容によらず1つ 8MiB以上
  • SplitMode=uid で 容量4GiBのデフォルトの上限設定では500ユーザで確実にサイズの上限を越える
  • サイズ上限を越えた場合実用上の問題がある動作になる
  • サイズ上限を越えないよう SplitMode=none の設定を行う

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。