CLOVER🍀

That was when it all began.

Fluent Bitを使って、DockerコンテナのログファイルをTailプラグインで読む

これは、なにをしたくて書いたもの?

前に、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