ログの表示、管理及び転送

こんにちは。サイオステクノロジー OSS サポート担当何敏欽です。

RHEL7/CentOS7 におけるログファイルの表示と管理は rsyslogd デーモンと journald デーモンによって制御されています。デフォルトでは rsyslogd と journald はシステム上で共存しています。

journal ログを不揮発ログに

journald デーモンは、トラブルシューティング用の主要ツールです。デフォルトでは、/run/log/journal にログを保存するように systemd ジャーナルが設定されています。ジャーナルデータベースのログは持続せず、システムを再起動するとなくなります。しかし、この設定は変更可能です。/var/log/journal にジャーナルログを永続的に保存するには以下の 2つ方法があります。

# 方法①:/var/log/journal ディレクトリを作成する
# mkdir -p /var/log/journal

# 方法②:/etc/systemd/journald.conf に Storage=persistent を設定する
# sed -i 's/#Storage=auto/Storage=persistent/' /etc/systemd/journald.conf

# 方法①、方法② の後サービスを再起動する 
# systemctl restart systemd-journald.service

ソースコードからログ出力の仕様を確認

journal ログと messages の両方にシステムログを記録したい場合、上記の設定を行なった後に rsyslog を再起動する必要があります。

# systemctl restart rsyslog.service

systemd と rsyslog のソースコードを確認するとわかりますが、仕様上 rsyslog を再起動しない場合は messages のログ出力が途絶えて、rsyslog を再起動すると messages に再度ログが出力されます。

rsyslog では、imjournal モジュールを使用して journald から情報を受け取ってログを出力しています。方法①、方法② で /var/log/journal に journal ログを保存するようになりますが、rsyslog はまだ以前の保存先を参照しているため、新しいメッセージを取得できません。

rsyslog を再起動すると imjournal モジュールも再起動し、journal の新しい保存先を検索して参照します。journald から情報を取得し始めると、messages に再度ログが出力されるようになります。

systemd-219-19.el7/src/journal/sd-journal.c
…
1508 static int add_search_paths(sd_journal *j) {
1509         int r;
1510         const char search_paths[] =  ← /run/log/journal と /var/log/journal の保存先を検索する。
1511                 "/run/log/journal
systemd-219-19.el7/src/journal/sd-journal.c
…
1508 static int add_search_paths(sd_journal *j) {
1509         int r;
1510         const char search_paths[] =  ← /run/log/journal と /var/log/journal の保存先を検索する。
1511                 "/run/log/journal\0"
1512                 "/var/log/journal\0";
1513         const char *p;
1514
1515         assert(j);
1516
1517         /* We ignore most errors here, since the idea is to only open
1518          * what's actually accessible, and ignore the rest. */
1519
1520         NULSTR_FOREACH(p, search_paths) {
1521                 r = add_root_directory(j, p);
1522                 if (r < 0 && r != -ENOENT) {
1523                         r = set_put_error(j, r);
1524                         if (r < 0)
1525                                 return r;
1526                 }
1527         }
1528
1529         return 0;
1530 }
…
1611 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1612         sd_journal *j;
1613         int r;
1614
1615         assert_return(ret, -EINVAL);
1616         assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1617
1618         j = journal_new(flags, NULL);
1619         if (!j)
1620                 return -ENOMEM;
1621
1622         r = add_search_paths(j);  ← sd_journal_open() が保存先を検索処理する。
1623         if (r < 0)
1624                 goto fail;
1625
1626         *ret = j;
1627         return 0;
1628
1629 fail:
1630         sd_journal_close(j);
1631
1632         return r;
1633 }
…
" 1512 "/var/log/journal
systemd-219-19.el7/src/journal/sd-journal.c
…
1508 static int add_search_paths(sd_journal *j) {
1509         int r;
1510         const char search_paths[] =  ← /run/log/journal と /var/log/journal の保存先を検索する。
1511                 "/run/log/journal\0"
1512                 "/var/log/journal\0";
1513         const char *p;
1514
1515         assert(j);
1516
1517         /* We ignore most errors here, since the idea is to only open
1518          * what's actually accessible, and ignore the rest. */
1519
1520         NULSTR_FOREACH(p, search_paths) {
1521                 r = add_root_directory(j, p);
1522                 if (r < 0 && r != -ENOENT) {
1523                         r = set_put_error(j, r);
1524                         if (r < 0)
1525                                 return r;
1526                 }
1527         }
1528
1529         return 0;
1530 }
…
1611 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1612         sd_journal *j;
1613         int r;
1614
1615         assert_return(ret, -EINVAL);
1616         assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1617
1618         j = journal_new(flags, NULL);
1619         if (!j)
1620                 return -ENOMEM;
1621
1622         r = add_search_paths(j);  ← sd_journal_open() が保存先を検索処理する。
1623         if (r < 0)
1624                 goto fail;
1625
1626         *ret = j;
1627         return 0;
1628
1629 fail:
1630         sd_journal_close(j);
1631
1632         return r;
1633 }
…
"; 1513 const char *p; 1514 1515 assert(j); 1516 1517 /* We ignore most errors here, since the idea is to only open 1518 * what's actually accessible, and ignore the rest. */ 1519 1520 NULSTR_FOREACH(p, search_paths) { 1521 r = add_root_directory(j, p); 1522 if (r < 0 && r != -ENOENT) { 1523 r = set_put_error(j, r); 1524 if (r < 0) 1525 return r; 1526 } 1527 } 1528 1529 return 0; 1530 } … 1611 _public_ int sd_journal_open(sd_journal **ret, int flags) { 1612 sd_journal *j; 1613 int r; 1614 1615 assert_return(ret, -EINVAL); 1616 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL); 1617 1618 j = journal_new(flags, NULL); 1619 if (!j) 1620 return -ENOMEM; 1621 1622 r = add_search_paths(j);  ← sd_journal_open() が保存先を検索処理する。 1623 if (r < 0) 1624 goto fail; 1625 1626 *ret = j; 1627 return 0; 1628 1629 fail: 1630 sd_journal_close(j); 1631 1632 return r; 1633 } …

rsyslog では、起動後 imjournal プラグインを読み込む時のみ、sd_journal_open() が実行されます。

rsyslog-7.4.7-12.el7/plugins/imjournal/imjournal.c
675 /* open journal */
676 BEGINwillRun
677 CODESTARTwillRun
678         int ret;
679         ret = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
680         if (ret < 0) {
681                 iRet = RS_RET_IO_ERROR;
682         }
683 ENDwillRun

ログを永続的に保存する設定を行なった後、rsyslog を再起動したら、複数日分 (数ヶ月前とか) の古いメッセージがログファイルに再度書き込まれますので、ビックリするかもしれませんが、これも仕様通りの動作です。

現在 journald が保持する過去のメッセージを取得するかどうかは ImjournalIgnorePreviousMessages の設定で決まります。デフォルトでは、ImjournalIgnorePreviousMessages の設定が off であり、現在 journald が保持する過去のメッセージも含めてすべてのメッセー ジを取得する設定になっています。例えば、新しいメッセージのみをインポートしたい場合は ImjournalIgnorePreviousMessages を on に設定すれば、現在 journald が保持する過去のメッセージを無視して、新しいメッセージのみを取得します。

rsyslog を用いて信頼できるログ転送を実現

rsyslog は、reliable (信頼できる) シスログデーモンという意味で、高い信頼性を実現する次世代の syslog です。信頼性のある通信というのは、ログメッセージを確実に転送され、取りこぼしがないことです。

rsyslog は信頼性のあるログ転送を実現できる理由は、主に下記の 2点です。

  • TCP でログメッセージを転送できる。
  • 転送に失敗したログメッセージは転送先が再び到達可能になるまでローカルに保存される。

上記 2点目ですが、これは転送先が利用できない (ダウンしたなど) 場合にログメッセージが失われないように、転送アクションに対するアクションキュー (ActionQueue) を設定することで、転送先が復旧後に転送に失敗したログメッセージが再送されます。

仮に下記のような構成があるとします。

                   [転送元(10.1.100.100)]
                      ↓           ↓
                      ↓           ↓
                      ↓           ↓
                 [転送先_1]      [転送先_2]
               (10.1.100.110)   (10.1.100.120) 

rsyslog で転送先_1(10.1.100.110)、転送先_2(10.1.100.120) へログを転送する場合、以下のように転送先毎に ActionQueue 等の設定を行う必要があります。それぞれの転送は独立して行われるため、1つの転送先がダウンした場合でも他方への転送はブロックされず、転送先が復旧したらログメッセージが転送されるようになります。

転送元と転送先のログ転送設定について、インターネット上にたくさん情報があるため、ここでは記載しません。ActionQueue の設定は /etc/rsyslog.conf ファイルに直接記載するか、または /etc/rsyslog.d/ 配下に ActionQueue の設定内容のファイルを作成します。

# 転送ルール_1
$ ActionQueueType LinkedList #LinkedList インメモリーキューを有効にする。
$ ActionQueueFileName srvrfwd1 #ファイル名の設定、ディスクストレージを定義する。
$ ActionResumeRetryCount -1 #サーバが応答しない場合に接続を再試行する際 rsyslog がメッセージを破棄しないようにする。
$ ActionQueueSaveOnShutdown on #rsyslog がシャットダウンした場合、インメモリデータが保存される
*.* @@10.1.100.110:514 #受信されたすべてのメッセージをサーバに転送する。

# 転送ルール_2
$ActionQueueType LinkedList
$ActionQueueFileName example_fwd2
$ActionResumeRetryCount -1
$ActionQueueSaveOnShutdown on
*.* @@10.1.100.120:514

では、設定通りになるかを確認してみます。

転送先_1 がダウンしたとします。

[root@destination_1]# systemctl stop rsyslog

転送元からメッセージが出力されたとします。

[root@source]# logger "転送先_1がダウンしました。"

転送先_1 がダウンしたから、当然そのログメッセージが転送されません。

[root@destination_1]# tail -f /var/log/messages
(出力なし)

転送先_2 にログが出力されたことを確認できます。転送先_1 と転送先_2 はそれぞれの転送は独立して行われており、転送先_1 がダウンしても転送先_2 は影響されず、ログメッセージが転送されます。

[root@destination_2]# tail -f /var/log/messages
Jun  8 12:06:39 rhel7 root: 転送先_1がダウンしました。

転送先_1 が復旧したとします

[root@destination_1]# systemctl start rsyslog

転送先_1 に復旧前のログが出力されたことを確認できます。

[root@destination_1]# tail -f /var/log/messages
Jun  8 12:06:39 rhel7 root: 転送先_1がダウンしました。

設定通りの動作になっていますね。これこそ、確実な転送ですね。

以上で systemd ジャーナルでログの永続化を有効にする方法、ログ出力の仕様および rsyslog で複数のサーバへの確実な転送方法を説明しました。

ご覧いただきありがとうございます! この投稿は役に立ちましたか? 役に立った 役に立たなかった

Be the first to comment

コメント投稿

Your email address will not be published.


*