fluentbitのキホン2
キホンの2は、Parser。
Parserといっても、Parserを定義するわけじゃなく"Parserを使う"が主目的。
上記ページの図の通り、Inputに次いで2番目に実行されるPlugin。
Parser使うと何がいいの?
端的に書けば。
これを
fluentbit-1 | [0] tail.0: [[1720234089.718042716, {}], {"log"=>"172.22.0.1 - - [06/Jul/2024:02:48:09 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.81.0" "-""}]
こうすることができる
fluentbit-1 | [0] tail.0: [[1720234964.000000000, {}], {"remote"=>"172.25.0.1", "host"=>"-", "user"=>"-", "method"=>"GET", "path"=>"/", "code"=>"200", "size"=>"615", "referer"=>"-", "agent"=>"curl/7.81.0"}]
見にくいが、受信したメッセージをフィールドに分割することが可能。
で、分割すると何が嬉しいかというと。
- Filterする際に特定のフィールドを条件にすることが可能
- Elasticsearchに送ったときにフィールドに分けてIndexに格納してくれるから、メッセージの検索が楽になる
など、色々
Parserの定義
Parserは定義してあげないと使えない。
Fluentbitは、デフォルトでいくつかParserが定義されている。以下は、NginxのアクセスログのParserの定義。
[PARSER]
Name nginx
Format regex
Regex ^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
Apache, NginxなどのParserは/fluent-bit/etc/parsers.conf に定義されているので、自分でParserを定義する場合は参考にするとよいです。
実際にやってみる
今回は、冒頭に記載した、NginxのアクセスログをParserを使って分解してみる。
今回使うdocker-compose.ymlは以下。
services:
nginx:
image: nginx:1.27.0
ports:
- "8080:80"
volumes:
- "$PWD/nginx/default.conf:/etc/nginx/conf.d/default.conf"
- "$PWD/nginx/host.access.log:/var/log/nginx/host.access.log"
fluentbit:
image: fluent/fluent-bit:3.0.3-debug
volumes:
- "$PWD/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf"
- "$PWD/nginx/host.access.log:/tmp/host.access.log:ro"
Nginxの準備
Nginxコンテナはデフォルトでは標準出力にログを出力するので、ファイル(/var/log/nginx/host.access.log)に出力するように設定を変更する。
具体的には、default.confの以下設定のコメントを外してあげる。
access_log /var/log/nginx/host.access.log main;
コメントを外したdefault.confはホスト側においてマウントする。
Nginxのアクセスログを出力する、host.access.logはFluentbitからも参照するので、ホスト側で空ファイルを作って、やっぱりマウントする
Fluentbitの準備
docker-compose.ymlについては、fluentbint.confとNginxのアクセスログが出力されるhost.access.logをマウントするだけ。host.access.logは、山椒の実なのでroをつけて読み取り専用としておく。
今回使うfluentbit.confは以下。
[SERVICE]
flush 1
Parsers_File /fluent-bit/etc/parsers.conf
[INPUT]
Name tail
Path /tmp/host.access.log
[FILTER]
Name parser
Match *
Key_Name log
Parser nginx
[OUTPUT]
Name stdout
Match *
最初にParserを定義したファイルをインクルードするため、Parsers_File /fluent-bit/etc/parsers.conf
を定義。
[FILTER]で、受信したメッセージを分解。ParserでどのParserを適用するかを指定してあげる。Key_Nameは、メッセージが格納されているキーを指定する。キホンで記載した通り、メッセージを格納しているキーはデフォルトのlog。
実践
準備したら、docker compose up -dしてNginxとFluentbitを起動。
ホスト上で以下コマンドを実行して、Nginxにリクエストを投げてみる
curl localhost:8080
docker compose logs fluentbitでログを確認すると以下のように分解されたログが見えるはず。
> fluentbit-1 | [0] tail.0: [[1720234964.000000000, {}], {"remote"=>"172.25.0.1", "host"=>"-", "user"=>"-", "method"=>"GET", "path"=>"/", "code"=>"200", "size"=>"615", "referer"=>"-", "agent"=>"curl/7.81.0"}]
Parseした後のFilter
Parseすると、メッセージが分割され、分割された単位でFilterの条件にすることが可能。
例えば、レスポンスコードは、codeというキーで保持されるので
- レスポンスコードが 2xx,3xxの場合は、nginx.OKというタグをつける
- レスポンスコードが 4xx,5xxの場合は、nginx.OKというタグをつける
ということも可能。以下はその例。
[SERVICE]
flush 1
Parsers_File /fluent-bit/etc/parsers.conf
[INPUT]
Name tail
Path /tmp/host.access.log
[FILTER]
Name parser
Match tail.0
Key_Name log
Parser nginx
[FILTER]
Name rewrite_tag
Match tail.0
Rule $code [2|3][0-9][0-9] nginx.OK false
[FILTER]
Name rewrite_tag
Match tail.0
Rule $code [4|5][0-9][0-9] nginx.NG false
[OUTPUT]
Name stdout
Match *
上記例は実用的ではないが、アクセスしてきたFQDNごとにタグを書き換え、Outputで、タグごとにElasticsearchのインデックスを指定するなど、出力先を変えることもできる。
さいごに
今回はParseを使うだけの簡単な内容でしたが、一つのキーにログ全文が格納されていると使い勝手がよくない面が多いので、Parseして分解するようにしてみてください。
Discussion