PK
パーネルカニック研究所
今日もどこかでkernel panic。
root@pk-lab:~#

journalctlを理解する

連載ナビ

この連載は「Linuxを使えるようになる」ではなく、「Linuxがどう動いているのか」を理解するための実践シリーズです。

今回は Linuxを理解する 〜 LFSで学ぶLinux内部構造 〜 の Part 5 / 第5回として、仕組みのつながりを意識しながら学びます。

—

journalctlは「journaldが一元収集したログを検索するツール」

前回の宿題、「端末を持たないdaemonの出力はどこへ行くのか」から始めます。

結論から言うと、systemd環境ではsystemd-journaldというdaemonが、全サービスの標準出力・標準エラー出力、syslog経由のログ、カーネルのログまでを一元的に回収し、構造化された形式で保存しています。その保存先を検索・閲覧するコマンドが journalctl です。


各サービスのstdout/stderr ─┐
syslog経由のメッセージ    ─┼─> systemd-journald ─> ジャーナルファイル ─> journalctl で閲覧
カーネルログ(dmesg相当)   ─┘

保存先はジャーナル専用のバイナリ形式で、場所は /run/log/journal/(メモリ上・再起動で消える)または /var/log/journal/(ディスク上・永続)です。バイナリなので cat では読めませんが、その代わり各レコードにunit名・PID・優先度・ブートIDといったメタデータが付いており、これがjournalctlの強力な絞り込みを支えています。

「サービスごとにバラバラのテキストファイル」では調査が遅いから

なぜ従来の /var/log/ 以下のテキストログではなく、journaldという仕組みが作られたのか。理由は、従来方式には調査を遅くする構造的な問題があったからです。

  • 置き場所とフォーマットがバラバラ: nginxは /var/log/nginx/error.log、認証は /var/log/auth.log、それ以外は /var/log/syslog ……とサービスごとに場所が違い、タイムスタンプの書式すら揃っていません。複数サービスにまたがる障害では、ファイルを行き来しながら時刻を突き合わせる必要がありました
  • メタデータがない: テキスト行には「どのunitが出したか」「そのときのブートは何回目か」という情報がなく、grep の工夫でカバーするしかありませんでした
  • 標準出力が捨てられる: 前回見たとおりdaemonは端末を持たないため、素朴に起動すると標準出力への出力は行き場を失います。サービスごとにログファイル出力の実装が必要でした

journaldはこれらを一挙に解決します。サービスは標準出力に書くだけでよく(12-factor appの「ログはstdoutへ」と同じ思想です)、回収・メタデータ付与・保存はjournaldが担い、閲覧はjournalctlの統一されたインターフェースで行えます。

絞り込みオプションを5つ覚えれば実戦で使える

journalctlは引数なしで実行すると全ログが古い順に出て圧倒されます。実用上は「絞り込んで見る」が基本で、次の5つを覚えれば日常調査の大半をカバーできます。


# 1. -u : unit(サービス)で絞る
journalctl -u nginx

# 2. -b : ブート単位で絞る(-b は今回起動分、-b -1 は前回起動分)
journalctl -b
journalctl -b -1

# 3. --since / --until : 時刻で絞る
journalctl --since "2026-08-22 09:00" --until "2026-08-22 10:00"
journalctl --since "1 hour ago"

# 4. -p : 優先度で絞る(err で「エラー以上」= err, crit, alert, emerg)
journalctl -p err -b

# 5. -f : 流れてくるログを追いかける(tail -f 相当)
journalctl -u nginx -f

オプションは組み合わせられます。「今回の起動で、nginxが出したエラー以上のログ」なら journalctl -b -u nginx -p err です。ほかに、カーネルメッセージだけを見る -k(dmesg 相当)、末尾から表示する -e、行数を絞る -n 50 も頻出です。

特に -b -1(前回起動分)は覚える価値があります。「サーバーが突然再起動した。直前に何が起きたのか」という調査では、今回のログではなく前回のブートの末尾を見る必要があるからです。


journalctl -b -1 -e

サービス起動失敗を調査する、実戦の流れ

Part 5の総仕上げとして、これまでの回をつなげた調査フローを見ます。たとえば systemctl start nginx が失敗したとします。


$ sudo systemctl start nginx
Job for nginx.service failed because the control process exited with error code.
See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.

エラーメッセージ自体が次の一手を教えてくれています。指示どおりに進めます。


# 手順1: statusで状態と直近ログの要約を見る
systemctl status nginx

# 手順2: journalctlで該当unitのログを詳しく見る
#        -x は説明文の追加、-e は末尾へジャンプ、-u はunit指定
journalctl -xeu nginx.service

ログにたとえば nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) とあれば、ポート80が既に使われていることが原因だと特定できます。「systemctlで状態確認 → journalctlで該当unitのログ確認」という2段階は、systemd環境の障害初動の定石です。

もう1つ、実務で必ず確認したいのがログの永続化設定です。/var/log/journal/ ディレクトリが存在しない環境では、ジャーナルは /run/log/journal/(tmpfs上)にしか書かれず、再起動するとログが消えます。その場合 journalctl -b -1 は「Specifying boot ID or boot offset has no effect, no persistent journal was found」という趣旨のメッセージを出して前回分を表示できません。永続化するには次のようにします(設定は /etc/systemd/journald.conf の Storage= でも制御できます)。


sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald

ビルドから起動、常駐、ログまでが1本につながった

journalctlは、journaldが一元収集した構造化ログを、unit・ブート・時刻・優先度で絞り込んで読むツールである——これが今回のポイントです。前回の自作daemonの echo の出力も、journalctl -u hello で確認できます。

これでPart 5は完結です。カーネルを自分でビルドし(第1回)、initramfsが本物のルートへ橋渡しし(第2回)、systemdがPID 1としてサービスを起動・監視し(第3回)、その実体であるdaemonが常駐し(第4回)、その出力をjournalctlで追跡する(今回)——起動からログ調査までが1本の線になりました。次回からのPart 6では、動き出したシステムの外側、ネットワークの世界に進みます。

図解テキスト


service logs (stdout/stderr) ─┐
syslog                        ─┼─> journald ─> /var/log/journal/ (永続)
kernel log                    ─┘              /run/log/journal/  (揮発)
                                                └─> journalctl -u/-b/--since/-p/-f

この回で実行するコマンド

上から順番に実行すれば、この回の内容を体験できます。

まずはそのままコピペして実行し、次に1行ずつ意味を確認してください。


# 今回の起動分のログを先頭から確認
journalctl -b | head -n 80

# sshdのログをunit指定で確認(環境によりunit名は ssh の場合あり)
journalctl -u sshd -b 2>/dev/null | tail -n 80 || journalctl -u ssh -b 2>/dev/null | tail -n 80 || true

# 今回の起動での「エラー以上」だけを確認
journalctl -p err -b --no-pager | tail -n 40

# ログが永続化されているか確認(ディレクトリがなければ揮発運用)
ls /var/log/journal/ 2>/dev/null || echo "persistent journal なし(/run/log/journal のみ)"

ハンズオン手順

1. ターミナルを開いて、上のコマンドを1行ずつ実行する

2. journalctl -p err -b で見つけたエラーを1つ選び、-u でそのunitに絞って前後の文脈を読む

3. /var/log/journal/ の有無を確認し、自分の環境のログが再起動後も残るか判断する

4. エラーが出たら、そのエラーメッセージをそのまま残す

チェックポイント

  • コマンドが最後まで実行できた
  • -u -b --since -p -f の5オプションの意味を言える
  • サービス起動失敗時の初動2手順(systemctl status → journalctl -xeu)を説明できる
  • 次の回に進む前に、分からない単語を1つ調べた

今回理解できたこと

  • journalctlは起動単位・サービス単位で追跡できる(-b と -u の組み合わせが基本)
  • ログ調査の再現性が上がる(メタデータ付きの一元管理で、grep頼みの調査から卒業できる)
  • 永続化には /var/log/journal/ が必要で、なければ再起動でログが消える