これは、なにをしたくて書いたもの?
前に、Fluent BitをDockerのlogging driverとして使ってみました。
Fluent BitをDocker logging driverとして使う - CLOVER🍀
今度は、DockerコンテナのログをTailプラグインで読み込んでみようと思います。
Tail - Fluent Bit: Official Manual
その時に、どれくらい情報が取れるのかを調べてみようかなと。まあ、この条件だと大したことができないことが確認できたの
ですが。
環境
今回の環境は、こちら。
$ uname -srvmpio Linux 4.15.0-99-generic #100-Ubuntu SMP Wed Apr 22 20:32:56 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.4 LTS Release: 18.04 Codename: bionic $ docker -v Docker version 19.03.8, build afacb8b7f0 $ /opt/td-agent-bit/bin/td-agent-bit --version Fluent Bit v1.4.4
お題
ふつうにnginxをDockerコンテナとして起動します。
$ docker container run -d -p 80:80 --name nginx nginx:1.17.10
この状態でDockerコンテナ内で標準出力に書き込まれたログは、デフォルトのJSON File logging driverを使ってストレージに
書き込まれることになります。
JSON File logging driver | Docker Documentation
About storage drivers | Docker Documentation
これをTailプラグインで読みましょう、と。
Tail - Fluent Bit: Official Manual
たとえば、ここでお題として起動したnginxコンテナのログは、以下にあることになります。
$ docker inspect nginx | grep LogPath "LogPath": "/var/lib/docker/containers/52c01ce2c45b60cb2718ca42a948393733b13b6d02514e543a61812890292471/52c01ce2c45b60cb2718ca42a948393733b13b6d02514e543a61812890292471-json.log",
Fluent Bitを、Dockerコンテナのログを読むように設定する
まずは、簡単に設定してみましょう。
$ grep -v '^ *#' /etc/td-agent-bit/td-agent-bit.conf [SERVICE] Flush 5 Daemon Off Log_Level info Parsers_File parsers.conf Plugins_File plugins.conf HTTP_Server Off HTTP_Listen 0.0.0.0 HTTP_Port 2020 [INPUT] Name tail Path /var/lib/docker/containers/*/*-json.log Tag container.log [OUTPUT] Name stdout Match * Format json_lines
なんとなく、Tagは付けておきました…。
結果は標準出力に書いているので、journalctlで確認することにします。
$ sudo journalctl -u td-agent-bit.service -f
コンテナにアクセスしてみます。
$ curl localhost
こんな感じに出力されます。
May 17 13:06:56 ubuntu1804.localdomain td-agent-bit[5401]: {"date":1589720815.612513,"log":"{\"log\":\"172.17.0.1 - - [17/May/2020:13:06:55 +0000] \\\"GET / HTTP/1.1\\\" 200 612 \\\"-\\\" \\\"curl/7.58.0\\\" \\\"-\\\"\\n\",\"stream\":\"stdout\",\"time\":\"2020-05-17T13:06:55.612432829Z\"}"}
もともとのログとしては、こんな感じに入っているわけですね。
$ sudo cat /var/lib/docker/containers/52c01ce2c45b60cb2718ca42a948393733b13b6d02514e543a61812890292471/52c01ce2c45b60cb2718ca42a948393733b13b6d02514e543a61812890292471-json.log {"log":"172.17.0.1 - - [17/May/2020:13:06:55 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.58.0\" \"-\"\n","stream":"stdout","time":"2020-05-17T13:06:55.612432829Z"}
Parserを使ってパースする
出力結果を見ると、「log」の中にログメッセージが全部入っていて、ちょっと扱いづらそうです。
May 17 13:06:56 ubuntu1804.localdomain td-agent-bit[5401]: {"date":1589720815.612513,"log":"{\"log\":\"172.17.0.1 - - [17/May/2020:13:06:55 +0000] \\\"GET / HTTP/1.1\\\" 200 612 \\\"-\\\" \\\"curl/7.58.0\\\" \\\"-\\\"\\n\",\"stream\":\"stdout\",\"time\":\"2020-05-17T13:06:55.612432829Z\"}"}
Dockerコンテナログとしてパースする
ここで、Parserの定義を見てみます。
/etc/td-agent-bit/parsers.conf
[PARSER] Name docker Format json Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%L Time_Keep On # -- # Since Fluent Bit v1.2, if you are parsing Docker logs and using # the Kubernetes filter, it's not longer required to decode the # 'log' key. # # Command | Decoder | Field | Optional Action # =============|==================|================= #Decode_Field_As json log
Docker用のものがあるので、こちらを使用しましょう。
このParserは、JSON Parserを使って定義されています。
JSON - Fluent Bit: Official Manual
Parserの指定場所ですが、TailプラグインではParserが指定できるようなので、こちらに直接設定してみます。
[INPUT] Name tail Path /var/lib/docker/containers/*/*-json.log Parser docker Tag container.log
Fluent Bitを再起動してから、nginxコンテナにアクセスしてログを確認してみましょう。
May 17 13:12:38 ubuntu1804.localdomain td-agent-bit[5465]: {"date":1589721153.619957,"log":"172.17.0.1 - - [17/May/2020:13:12:33 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.58.0\" \"-\"\n","stream":"stdout","time":"2020-05-17T13:12:33.61995679Z"}
コンテナログの、「log」、「stream」、「time」が独立したJSONの要素になったことが確認できます。
コンテナIDを入れる
ところで、今回のログからは、コンテナであることがまったくわかりません。
なにか情報はないかな?ということで、コンテナのIDがファイルパスに含まれることを利用して、「Path_Key」を使うことに
しました。
Tail - Fluent Bit: Official Manual
以下のように、「Path_Key」を使って「container_log_file_path」というキーでログファイルパスを保存するように変更。
[INPUT] Name tail Path /var/lib/docker/containers/*/*-json.log Path_Key container_log_file_path Parser docker Tag container.log
このファイルパスから、コンテナのIDを抽出できるように、Parserを書きましょう。 /etc/td-agent-bit/parsers.conf
[PARSER] Name container-log-file Format regex Regex /var/lib/docker/containers/(?<container_id>[^/]+)/.+\.log
結果、こんな感じになりました。
[INPUT] Name tail Path /var/lib/docker/containers/*/*-json.log Path_Key container_log_file_path Parser docker Tag container.log [FILTER] Name parser Match container.* Key_Name container_log_file_path Parser container-log-file Preserve_Key Off Reserve_Data On
「Reserve_Data」をOnにしておかないと、Parserが解析したキー以外がなくなってしまうので、要注意。
適用結果としてはこんな感じになり、「container_id」というキーでコンテナIDが追加されました。
May 17 13:36:27 ubuntu1804.localdomain td-agent-bit[5529]: {"date":1589722583.842574,"container_id":"52c01ce2c45b60cb2718ca42a948393733b13b6d02514e543a61812890292471","log":"172.17.0.1 - - [17/May/2020:13:36:23 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.58.0\" \"-\"\n","stream":"stdout","time":"2020-05-17T13:36:23.842574434Z"}
nginxのログとしてパースする
最後に、nginxのログとしてパースしておきましょう。
以下のFilterを加えます。nginxのParserは、事前定義されたものを使用しています。
[FILTER] Name parser Match container.* Key_Name log Parser nginx Preserve_Key Off Reserve_Data On Tag docker.nginx.log
全体は、こんな感じですね。
[INPUT] Name tail Path /var/lib/docker/containers/*/*-json.log Path_Key container_log_file_path Parser docker Tag container.log [FILTER] Name parser Match container.* Key_Name container_log_file_path Parser container-log-file Preserve_Key Off Reserve_Data On [FILTER] Name parser Match container.* Key_Name log Parser nginx Preserve_Key Off Reserve_Data On Tag docker.nginx.log
パースした結果は、こちら。
May 17 13:38:47 ubuntu1804.localdomain td-agent-bit[5567]: {"date":1589722727,"remote":"172.17.0.1","host":"-","user":"-","method":"GET","path":"/","code":"200","size":"612","referer":"-","agent":"curl/7.58.0","container_id":"52c01ce2c45b60cb2718ca42a948393733b13b6d02514e543a61812890292471","stream":"stdout","time":"2020-05-17T13:38:47.382578641Z"}
だいぶ整ったのではないでしょうか?
Kubernetes Filter
ところで、今回はコンテナIDを含めるのが精一杯でしたが、コンテナ名なども含められたらいいのにな?と思うわけですが。
こういうのには、Kubernetes Filterを使うのが正解みたいですね。
Kubernetes - Fluent Bit: Official Manual
Kubernetes内で、DaemonSetとして動かす例がよく挙がると思いますが、こちらで使われるのがこのKubernetes Filterのようです。
製品系統は違いますが、FilebeatsだとProcessorでできそうな感じですね。Filebeatsの場合は、Docker用のProcessorがあれば
コンテナ名やイメージ名まで取得できそうな模様。
Add Docker metadata | Filebeat Reference [7.7] | Elastic
Add Kubernetes metadata | Filebeat Reference [7.7] | Elastic