第17章 システムログと時刻管理
LPIC-1 108.1 / 108.2 相当
システムで何が起きたかを記録するログと、複数のサーバー間で時刻を正確に合わせる仕組みを学びます。障害調査の第一歩はログの確認からです。
システムログの仕組み
従来型のLinuxでは rsyslog などが /var/log/ 以下にテキストのログファイルを出力していました。systemdを採用する現代的なディストリビューションでは、journald がバイナリ形式でログを一元管理しています。
| ログの場所・コマンド | 内容 |
|---|---|
/var/log/syslog(Debian系)/ /var/log/messages(RHEL系) | システム全般のログ(テキスト形式) |
/var/log/auth.log(Debian系)/ /var/log/secure(RHEL系) | 認証関連のログ(ログイン試行など) |
journalctl | systemdのジャーナルログを閲覧するコマンド |
rsyslogの設定構文
journaldが登場する以前から使われてきたrsyslogは、今でも「バイナリのjournaldログを、従来どおりのテキストファイルにも書き出す」役割として、多くのディストリビューションで併用されています。設定は/etc/rsyslog.confや/etc/rsyslog.d/以下に置かれ、基本の書式はfacility.priority(どの種類のログを、どの重大度以上で選ぶか)と、その出力先の組み合わせです。
/etc/rsyslog.d/50-default.conf(書式の例)
auth,authpriv.* /var/log/auth.log
mail.* -/var/log/mail.log
*.=crit /var/log/critical.log
*.!=info /var/log/not-info.log
cron.* @logserver.example.com
kern.* @@logserver.example.com:514
facility(ログの種類)には、auth・authpriv(認証関連)、cron(cron関連)、daemon(各種デーモン)、kern(カーネル)、mail(メール関連)、syslog(syslog自身)、user(ユーザープロセス)、local0〜local7(管理者が自由に使える枠)などがあります。priority(重大度)は、journalctlの-pと同じemerg〜debugの8段階です。
| priorityの前の記号 | 意味 |
|---|---|
| (記号なし、既定) | 指定した優先度以上すべて(例:*.critはcrit以上) |
= | 指定した優先度と完全に一致するものだけ(例:*.=critはcritのみ) |
! | 指定した優先度を除外する(例:*.!=infoはinfo以外すべて) |
none | そのfacilityを対象外にする |
出力先も柔軟に指定できます。通常のファイルパスのほか、先頭に-を付けると書き込みのたびにディスクへ同期しない(性能優先)指定になり、@hostはUDPでのリモート転送、@@hostはTCPでのリモート転送、|コマンドは結果をパイプで別のプログラムに渡す指定になります。複数サーバーのログを1台のログサーバーに集約したい場合、この@/@@によるリモート転送がよく使われます。
rsyslogは機能ごとにモジュールという単位で構成されており、代表的なものに、ローカルのUNIXソケット経由でログを受け取るimuxsock、journaldからログを取り込むimjournal、任意のログファイルを監視して取り込むimfile、リモートサーバーへ転送するomfwdなどがあります(imはinput module、omはoutput moduleの略)。設定ファイルの先頭付近でmodule(load="imuxsock")のように読み込みを宣言してから使います。
出力する行の形式そのものを変えたい場合はテンプレートを定義します。
template(name="CustomFormat" type="string" string="%timegenerated% %HOSTNAME% %syslogtag% %msg%\n")
*.* /var/log/custom.log;CustomFormat
既定のテンプレートでは物足りない場合(JSON形式で出力してログ収集ツールに渡したいなど)に、この仕組みで出力フォーマットをカスタマイズします。
logger と systemd-cat — スクリプトからログに書き込む
自作のスクリプトから、システムのログに記録を残したい場合はloggerコマンドを使います。
$ logger -p local0.info -t mybackup "バックアップ処理を開始しました"
$ journalctl -t mybackup
Aug 24 03:00:01 web01 mybackup[4821]: バックアップ処理を開始しました
-pでfacility.priorityを指定し、-tでタグ(ログ上での識別名。上の例のmybackup)を指定します。似た目的で、あるコマンドの標準出力・標準エラー出力をまるごとジャーナルに送りたい場合はsystemd-catが使えます。
$ systemd-cat -t mybackup /home/user/backup.sh # backup.shの出力をそのままjournalへ記録する
journalctl の使い方
$ journalctl # すべてのログを表示(古い順)
Aug 21 08:02:11 web01 systemd[1]: Started Session 3 of user alice.
Aug 21 08:15:44 web01 sshd[2210]: Accepted publickey for alice from 203.0.113.10 port 51344 ssh2
...(この後も現在に近づくにつれて新しいログが続く。全件が1画面に収まらないため、既定でlessによるページング表示になる)
$ journalctl -f # リアルタイムで新着ログを表示(follow)
Aug 21 09:15:03 web01 CRON[3381]: (root) CMD (/usr/local/bin/check.sh)
(新しいログが記録されるたびに末尾に追記され続ける。Ctrl+Cで終了)
$ journalctl -u nginx # 特定のサービス(unit)のログのみ
Aug 21 09:00:01 web01 systemd[1]: Started nginx - high performance web server.
Aug 21 09:00:01 web01 nginx[1023]: start worker process
$ journalctl --since "1 hour ago" # 直近1時間のログ
Aug 21 08:30:12 web01 sshd[2255]: Failed password for invalid user admin from 198.51.100.7 port 40011 ssh2
...(省略。直近1時間分のログのみに絞り込まれる)
$ journalctl --since "2026-08-01" --until "2026-08-10" # 期間を指定して絞り込み
-- Logs begin at Fri 2026-08-01 00:00:12 JST, end at Sun 2026-08-09 23:58:40 JST. --
Aug 01 00:05:33 web01 systemd[1]: Startup finished in 4.812s.
...(省略)
$ journalctl -p err # エラー以上のレベルのみ
Aug 21 07:12:09 web01 kernel: nvme 0000:00:04.0: I/O 592 QID 3 timeout, aborting
$ journalctl -b # 直近の起動(boot)以降のログのみ
-- Boot 8f6a2e1c9e6a4b1c9c3d2e1f0a9b8c7d --
Aug 21 07:58:02 web01 kernel: Linux version 6.8.0-49-generic (Ubuntu 24.04 LTS)
...(省略)
$ journalctl -k # カーネルメッセージのみ(dmesgに相当)
Aug 21 07:58:02 web01 kernel: Command line: BOOT_IMAGE=/vmlinuz-6.8.0-49-generic root=UUID=1a2b3c4d-... ro quiet splash
...(省略)
この行のこの値に注目: 各行の先頭にある日時・ホスト名・プロセス名[PID]の並びは共通の形式で、-uや-pなどの絞り込みオプションを付けてもこの形式自体は変わりません。journalctl -bの先頭に出る-- Boot xxxx --行は起動ごとに異なるIDで、どの起動時のログかを見分ける目印になります。Rocky Linux 9でも出力形式は同じですが、カーネルバージョンやパッケージのビルド番号がディストリビューションによって異なります(例: Rocky Linux 9では5.14.0-427.13.1.el9_4.x86_64のような表記になります)。
-pで指定するログレベルは、緊急度の高い順に emerg(0)→ alert(1)→ crit(2)→ err(3)→ warning(4)→ notice(5)→ info(6)→ debug(7)という8段階になっており、これはsyslogの標準的な重大度(severity)レベルに準拠しています。-p errのように指定すると、そのレベルとそれより緊急度の高いログ(この例ではerr・crit・alert・emerg)がまとめて表示されます。
journaldのログは既定では再起動のたびに消えてしまう揮発性の設定になっていることがあります。ログを永続化したい場合は/etc/systemd/journald.confでStorage=persistentを設定するか、/var/log/journalディレクトリを作成しておくことで、再起動後もログが保持されるようになります。
/etc/systemd/journald.confでは、永続化の有無以外にもディスク使用量を管理する項目があります。
| 設定項目 | 意味 |
|---|---|
Storage= | volatile(メモリ上のみ)/persistent(ディスクに保存)/auto(/var/log/journalがあれば保存)/none(記録しない) |
SystemMaxUse= | ジャーナルlog全体が使ってよいディスク容量の上限(例:500M)。超えると古いログから自動削除される |
SystemKeepFree= | ジャーナルのために最低限空けておくディスク容量 |
MaxRetentionSec= | ログを保持する最長期間(例:1month)。これを過ぎたログは削除される |
設定を変更した後はsystemctl restart systemd-journaldで反映します。ディスク使用状況の確認や、設定変更を待たずにその場でログを整理したい場合は、journalctl自身の管理用オプションが使えます。
$ journalctl --disk-usage # ジャーナルログが現在使用しているディスク容量を表示
Archived and active journals take up 512.0M in the file system.
$ journalctl --vacuum-size=200M # 使用量が200MBを超えないよう、古いログから削除
$ journalctl --vacuum-time=2weeks # 2週間より古いログを削除
SystemMaxUse=/MaxRetentionSec=は設定ファイル側の恒常的な上限設定、--vacuum-size/--vacuum-timeはjournalctlコマンドでその場限りの整理を行う操作、という役割の違いを区別しておきましょう。
システム時刻の管理
$ date # 現在時刻を表示
Fri Aug 21 09:15:03 JST 2026
$ date "+%Y-%m-%d %H:%M:%S" # 書式を指定して表示
2026-08-21 09:15:03
$ timedatectl # システムの時刻・タイムゾーン設定を表示
Local time: Fri 2026-08-21 09:15:03 JST
Universal time: Fri 2026-08-21 00:15:03 UTC
RTC time: Fri 2026-08-21 00:15:03
Time zone: Asia/Tokyo (JST, +0900)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
$ sudo timedatectl set-timezone Asia/Tokyo # 正常時は無出力
$ sudo timedatectl set-time "2026-08-15 10:00:00" # 時刻を手動で設定(NTP同期が無効な場合のみ)
Failed to set time: Automatic time synchronization is enabled
$ timedatectl list-timezones | grep Tokyo # 設定可能なタイムゾーン名を検索
Asia/Tokyo
この行のこの値に注目: timedatectlのSystem clock synchronizedとNTP serviceがともにyes/activeであれば、NTPによる自動同期が正しく機能しています。この状態のままset-timeで手動設定しようとすると、上の例のようにAutomatic time synchronization is enabledというエラーになります。手動で時刻を設定したい場合は、先にsudo timedatectl set-ntp falseでNTP同期を無効化する必要があります。
NTPによる時刻同期
サーバーの時刻がずれていると、ログの時系列が信用できなくなったり、認証エラーが発生したりします。特にKerberos認証や一部のTLS通信では、サーバー間の時刻差が一定以上あると正規のアクセスであっても失敗扱いになることがあり、時刻同期は単なる「時計合わせ」以上に重要な意味を持ちます。NTP(Network Time Protocol)は、インターネット上の時刻サーバーと自動的に同期する仕組みで、現代の多くのディストリビューションでは chrony というサービスが使われています(older systemsではntpdが使われていました)。
$ chronyc tracking # 同期状況を確認
Reference ID : CB0009D3 (ntp1.example.ne.jp)
Stratum : 3
Ref time (UTC) : Fri Aug 21 00:14:52 2026
System time : 0.000023450 seconds slow of NTP time
Last offset : +0.000012300 seconds
RMS offset : 0.000045600 seconds
Leap status : Normal
$ chronyc sources # 同期先の時刻サーバー一覧と品質を確認
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* ntp1.example.ne.jp 2 6 377 42 +12us[ +8us] +/- 8ms
^+ ntp2.example.ne.jp 2 6 377 45 +34us[ +30us] +/- 11ms
$ sudo systemctl status chronyd # Ubuntu/Debian系ではunit名がchronyのため、chronyd.serviceではなくchrony.serviceになる
● chronyd.service - NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled)
Active: active (running) since Fri 2026-08-21 07:58:05 JST; 1h 17min ago
$ timedatectl show # NTP同期が有効かどうかも含めて設定を確認
Timezone=Asia/Tokyo
NTP=yes
NTPSynchronized=yes
...(省略)
| chronyc sources の列 | 意味 |
|---|---|
| M(先頭の記号) | ^はサーバー、*は現在同期に使っている最良のソースを示す |
| Stratum | 基準時刻源(GPSなど)から何ホップ離れているかの階層数 |
| Poll | 問い合わせ間隔(2の指数、6なら64秒ごと) |
| Reach | 直近8回の応答成否を8進数で表したビットマスク(377は全て成功) |
| Last sample | 直近の同期でのオフセットと誤差範囲 |
この行のこの値に注目: chronyc sourcesの先頭が^*になっている行が、現在実際に同期の基準として使われているサーバーです。chronyc trackingのSystem timeの値が数十ミリ秒を超えて大きい場合は、NTPサーバーへの到達性や名前解決に問題がないか確認します。
chrony(近年の標準)とntpd(伝統的な実装)の両方が出題対象となることがあります。timedatectl set-ntp trueでNTP同期を有効化できることもあわせて押さえておきましょう。
ログローテーション(logrotate)
ログファイルは放っておくと肥大化し続け、ディスクを圧迫します。logrotateは、設定した周期やサイズを基準にログファイルを切り替え(ローテーション)、古いものを圧縮・削除してくれる仕組みです。
$ cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
size 100M
create 0640 www-data adm
sharedscripts
postrotate
systemctl reload nginx > /dev/null 2>/dev/null || true
endscript
}
| 設定項目 | 意味 |
|---|---|
daily / weekly / monthly | ローテーションの周期(日次/週次/月次) |
rotate 14 | 過去何世代分のログを残すか |
size 100M | 周期に関わらず、指定サイズを超えた時点でローテーションする |
compress | ローテーション後のログをgzip圧縮 |
delaycompress | 直前世代(1つ前にローテーションしたファイル)の圧縮を1周期遅らせる。ログを開いたままのプロセスが直前ファイルへ書き込み続けている場合に備える |
missingok | 対象ログが存在しなくてもエラーにしない |
notifempty | ログが空の場合はローテーションを行わない |
create 0640 www-data adm | ローテーション後、指定したパーミッション・所有者・グループで新しい空のログファイルを作成する |
copytruncate | ファイルをコピーしてから元ファイルを空にする方式。ログを出力し続けるプロセスの再起動が不要になる一方、コピーと空にする操作の間に書き込まれた分のログが失われる可能性がある |
sharedscripts | 複数ファイルが1つの設定ブロックにマッチする場合、postrotateをファイルごとにではなく1回だけ実行する |
postrotate 〜 endscript | ローテーション後に実行するコマンドを記述するブロック。ログを開き直させるためのサービスへのシグナル送信・reloadなどに使う |
$ sudo logrotate -f /etc/logrotate.conf # -f: 設定を無視して強制的にローテーションを実行(動作確認用。正常時は無出力)
$ sudo logrotate -d /etc/logrotate.conf # -d: 実際には実行せず、動作をデバッグ表示だけする
reading config file /etc/logrotate.conf
reading config info for /var/log/nginx/*.log
Handling 1 logs
rotating pattern: /var/log/nginx/*.log after 1 days (14 rotations)
empty log files are not rotated, old logs are removed
considering log /var/log/nginx/access.log
log does not need rotating (log has already been rotated)
...(省略)
この行のこの値に注目: -d(dry-run)は実際にはローテーションを行わず、どの設定ファイルがどのログを対象にしているか、まだローテーションの必要がないかを事前に確認できます。設定変更後の動作確認は、まず-dで内容を確認してから-fで実行するのが安全です。
個別のアプリケーション向け設定は/etc/logrotate.d/以下に1ファイルずつ配置するのが一般的です。パッケージをインストールすると、そのパッケージ用のログローテーション設定が自動的にここへ追加されることも多く、多くの場合は特に手を加えなくても適切にログが管理されます。全体の既定設定は/etc/logrotate.confにあり、通常は/etc/logrotate.d/*を読み込む記述(include)が含まれています。
mvやrmしただけでは、プロセスは(見えなくなった)古いファイルへ出力を続けてしまい、新しいファイル名では何も記録されません。logrotateの多くの設定では、ローテーション後に対象サービスへシグナル(多くはSIGHUP)を送って、ログファイルを開き直させるpostrotateスクリプトが併用されます。copytruncateはこの問題を回避する手段ですが、コピーと空打ちの間に書かれたログが失われる可能性がある点に注意が必要です。
ハードウェアクロックとUTC
サーバーには、OSが動いていなくても時刻を刻み続ける「ハードウェアクロック(RTC)」があります。OSが管理する「システムクロック」とは別物で、起動時にシステムクロックへ反映されます。
$ sudo hwclock --show # ハードウェアクロックの時刻を表示
2026-08-21 09:15:04.123456+09:00
$ sudo hwclock --systohc # システムクロックの時刻をハードウェアクロックへ書き込む(正常時は無出力)
この行のこの値に注目: hwclock --showの表示はtimedatectlのRTC time行と一致するはずの値です。両者がずれている場合は、hwclock --systohc(システム→RTC)やhwclock --hctosys(RTC→システム)でどちらか一方に合わせます。
サーバーでは、ハードウェアクロックを地域時間ではなくUTC(協定世界時)で保持しておくのが一般的です。タイムゾーンの変換はOS側(timedatectlで設定した値)が行うため、サマータイムの切り替えなどに振り回されにくくなります。
複数のサーバーが関わるシステムでは、この「ハードウェアクロックはUTCで統一し、表示だけをタイムゾーンで変換する」という設計方針が特に重要になります。もしサーバーごとにハードウェアクロックを別々の地域時間で保持していると、ログを突き合わせて障害調査をする際に時刻の変換ミスが起きやすくなります。日本国内だけで完結するシステムであっても、将来的に海外リージョンのサーバーと連携する可能性を考えると、最初からUTC基準で統一しておくことには大きなメリットがあります。
journalctlの出力を絞り込む実践的なオプション
journalctlは記録量が多いため、目的のログにたどり着くには絞り込みが欠かせません。-u サービス名で特定サービスのログだけに絞り、--sinceと--untilで期間を指定し、-p errのように優先度(priority)を指定すればエラー以上のログだけに絞り込めます。これらは組み合わせて使うことができ、たとえばjournalctl -u nginx --since "1 hour ago" -p errのようにすると「直近1時間のnginxのエラーだけ」を素早く確認できます。障害調査の初動では、まずこうした絞り込みで対象を狭めてから詳細を読むと、大量のログに埋もれずに済みます。