時代はsystemd
…だそうです。巷ではinit v. systemd論争が続いていますが、Fluentdのモットーは「世の中の全ての開発者がデータを手軽に正しく集められる」ことですので、systemdだろうが頭文字Dだろうが、拘りはありません。
td-agentもCentos7版が昨日リリースされましたので、満を持してsystemdで管理されているサービスのログをどうやってFluentdで集められるのか、現状をシェアしたいと思います。
journalctl -o
ご存知の方も多いでしょうが、systemd環境では、journalctlがデフォルトのログのビューワとなります。例えばauditd
のログをtailしたい場合は
$ journalctl -u auditd -f
-- Logs begin at Mon 2014-12-22 13:36:30 EST. --
Dec 22 13:36:31 systemd-fluentd auditd[264]: Started dispatcher: /sbin/audispd p...3
Dec 22 13:36:31 systemd-fluentd audispd[273]: priority_boost_parser called with: 4
Dec 22 13:36:31 systemd-fluentd audispd[273]: max_restarts_parser called with: 10
Dec 22 13:36:31 systemd-fluentd audispd[273]: No plugins found, exiting
Dec 22 13:36:31 systemd-fluentd augenrules[265]: /sbin/augenrules: No change
Dec 22 13:36:31 systemd-fluentd auditd[264]: Init complete, auditd 2.3.3 listeni...)
Dec 22 13:36:31 systemd-fluentd augenrules[265]: No rules
Dec 22 13:36:31 systemd-fluentd augenrules[265]: AUDIT_STATUS: enabled=1 flag=1 ...1
Dec 22 13:36:31 systemd-fluentd systemd[1]: Started Security Auditing Service.
Dec 22 15:09:10 systemd-fluentd auditd[264]: Audit daemon rotating log files
みたいな感じになります。-f
はtail
のオプションと同じで、-u
でサービス名のフィルタリングができます。他にも--since
と--until
で時間を区切れたりして、結構万能です。
機能が豊富なjournalctl
ですが、Fluentd的にもっともオイシイのは-o
オプションです。これはアウトプットのフォーマッターオプションであり、-o json
と指定することで、一行一JSONの形式で出力してくれます。
$ journalctl -u auditd -f -o json
{ "__CURSOR" : "s=09186fb5df0c4f1ba9a259995faefc2b;i=26b;b=3b8f00a5c3c7428aabec902ecdbce64e;m=1b6eef;t=50ad256595cd2;x=9cb8e91012839fc6", "__REALTIME_TIMESTAMP" : "1419273391660242", "__MONOTONIC_TIMESTAMP" : "1797871", "_BOOT_ID" : "3b8f00a5c3c7428aabec902ecdbce64e", "PRIORITY" : "6", "_UID" : "0", "_GID" : "0", "_MACHINE_ID" : "fd8cf26e06e411e4a9d004010897bd01", "SYSLOG_IDENTIFIER" : "systemd", "SYSLOG_FACILITY" : "3", "_TRANSPORT" : "journal", "_PID" : "1", "_COMM" : "systemd", "_EXE" : "/usr/lib/systemd/systemd", "_CAP_EFFECTIVE" : "1fffffffff", "_SYSTEMD_CGROUP" : "/", "CODE_FILE" : "src/core/job.c", "CODE_LINE" : "732", "CODE_FUNCTION" : "job_log_status_message", "MESSAGE_ID" : "39f53479d3a045ac8e11786248231fbf", "RESULT" : "done", "_HOSTNAME" : "systemd-fluentd", "_CMDLINE" : "/usr/lib/systemd/systemd --switched-root --system --deserialize 23", "UNIT" : "auditd.service", "MESSAGE" : "Started Security Auditing Service.", "_SOURCE_REALTIME_TIMESTAMP" : "1419273391658951" }
...
うわ、なんかいろいろ出てきましたね!Fluentd的にはin_tcp
とformat json
が使えるので、これは非常にありがたいですが、人間的にはツラいので、一先ず-o json-pretty
で中身を確認してみましょう。
$ journalctl -u auditd -f -o json-pretty
{
"__CURSOR" : "s=09186fb5df0c4f1ba9a259995faefc2b;i=a3b;b=3b8f00a5c3c7428aabec902ecdbce64e;m=14b71339a;t=50ad3a1af217d;x=e37f04158d5689d0",
"__REALTIME_TIMESTAMP" : "1419278950539645",
"__MONOTONIC_TIMESTAMP" : "5560677274",
"_BOOT_ID" : "3b8f00a5c3c7428aabec902ecdbce64e",
"_UID" : "0",
"_GID" : "0",
"_SYSTEMD_SLICE" : "system.slice",
"_MACHINE_ID" : "fd8cf26e06e411e4a9d004010897bd01",
"PRIORITY" : "5",
"SYSLOG_FACILITY" : "3",
"_CAP_EFFECTIVE" : "1fffffffff",
"_HOSTNAME" : "systemd-fluentd",
"_TRANSPORT" : "syslog",
"SYSLOG_IDENTIFIER" : "auditd",
"SYSLOG_PID" : "264",
"_PID" : "264",
"_COMM" : "auditd",
"_EXE" : "/usr/sbin/auditd",
"_CMDLINE" : "/sbin/auditd -n",
"_SYSTEMD_CGROUP" : "/system.slice/auditd.service",
"_SYSTEMD_UNIT" : "auditd.service",
"MESSAGE" : "Audit daemon rotating log files",
"_SOURCE_REALTIME_TIMESTAMP" : "1419278950538648"
}
...
"PRIORITY", "SYSLOG_FACILITY", "MESSAGE"といった従来のsyslog的なフィールドに加え、Hostnameが変わっても永続的にサーバーを特定できる"_MACHINE_ID"など、役に立ちそうなフィールドがたくさんあります。
Fluentd的にはどのフィールドをtimestampにするのかが重要ですが、ここでは"__SOURCE_REALTIME_TIMESTAMP"を使います(注:単位がマイクロ秒です)。
systemd側としては、journalctl -f -o json | nc localhost <FLUENTD_PORT>
でデータを流すだけです。
Fluentd側の設定
Fluentd側の設定はこんな感じになります。
<source>
type tcp
port 24225
format json
time_key _SOURCE_REALTIME_TIMESTAMP
time_format %S
tag systemd
</source>
<match systemd>
type stdout
</match>
この設定でFluentdを起動します。
$ fluentd -c fluent.conf
そしてjournatlctlからnc経由でデータを送りつけると…
$ journalctl -f -o json | nc localhost 24225
2014-12-22 18:34:14 -0500 systemd: {"__CURSOR":"s=09186fb5df0c4f1ba9a259995faefc2b;i=4a0c;b=3b8f00a5c3c7428aabec902ecdbce64e;m=42b5c334d;t=50ad6819a2130;x=3e03baaff28f6db5","__REALTIME_TIMESTAMP":"1419291297194288","__MONOTONIC_TIMESTAMP":"17907331917","_BOOT_ID":"3b8f00a5c3c7428aabec902ecdbce64e","_TRANSPORT":"syslog","PRIORITY":"6","SYSLOG_FACILITY":"10","SYSLOG_IDENTIFIER":"sshd","_UID":"0","_GID":"0","_COMM":"sshd","_EXE":"/usr/sbin/sshd","_CMDLINE":"sshd: root [priv] ","_CAP_EFFECTIVE":"1fffffffff","_SYSTEMD_CGROUP":"/system.slice/sshd.service","_SYSTEMD_UNIT":"sshd.service","_SYSTEMD_SLICE":"system.slice","_MACHINE_ID":"fd8cf26e06e411e4a9d004010897bd01","_HOSTNAME":"systemd-fluentd","MESSAGE":"pam_succeed_if(sshd:auth): requirement \"uid >= 1000\" not met by user \"root\"","SYSLOG_PID":"8331","_PID":"8331"}
ちゃんとデータが流れてきました!
これから先の話
将来的には外部プログラムに頼らずに、Fluentdから直接sd_journal APIを叩きたいところです。今のところ一応Rubyのバインディングはあるのですが、少しまだ挙動が把握しきれてなかったので、今回はjournalctl
を使うアプローチを紹介しました>_<
それではMerry systemd + Happy Fluentd!