🐙

fluentbitのキホン

2024/05/01に公開

fluentbitがどうメッセージを保持しているのかとか、わからなかったのでお勉強してみた。

Input, Filter, Outputが何とか、confファイルの書き方などは説明していないので、その辺は理解している前提。

お勉強の構成

fluentbitはコンテナでたてる。

今回使うfluentbitを起動するdocker-compose.ymlは以下の通り。
いろいろ見たい可能性があるので、debug付きのイメージを利用。

services:
  fluentbit:
    image: fluent/fluent-bit:3.0.3-debug
    volumes:
      - "$PWD/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf"
      - "$PWD/log:/tmp/log"

以下のfluent-bit.confを作ってdocker-compose.ymlと同じディレクトリに置く

[INPUT]
  Name tail
  Path /tmp/log

[OUTPUT]
  Name stdout
  Match *

/tmp/logファイルに書き込まれた内容をstdoutに出力するだけの簡単なコンフィグ

同じくlogファイルを作ってdocker-compose.ymlと同じディレクトリに置く。

flubent-bit.confとログファイル(logファイル)はホスト側において、bind mountする。

fluentbit起動

docker compose upで起動。メッセージの様子を見るためにコンソール画面が見るので、

-dオプションはつけない(大事)

[+] Running 1/2
 ✔ Network fluentbit_default        Created                                                      0.1s 
 ⠋ Container fluentbit-fluentbit-1  Created                                                      0.0s 
Attaching to fluentbit-1
fluentbit-1  | Fluent Bit v3.0.3
fluentbit-1  | * Copyright (C) 2015-2024 The Fluent Bit Authors
fluentbit-1  | * Fluent Bit is a CNCF sub-project under the umbrella of Fluentd
fluentbit-1  | * https://fluentbit.io
fluentbit-1  | 
fluentbit-1  | ___________.__                        __    __________.__  __          ________  
fluentbit-1  | \_   _____/|  |  __ __   ____   _____/  |_  \______   \__|/  |_  ___  _\_____  \ 
fluentbit-1  |  |    __)  |  | |  |  \_/ __ \ /    \   __\  |    |  _/  \   __\ \  \/ / _(__  < 
fluentbit-1  |  |     \   |  |_|  |  /\  ___/|   |  \  |    |    |   \  ||  |    \   / /       \
fluentbit-1  |  \___  /   |____/____/  \___  >___|  /__|    |______  /__||__|     \_/ /______  /
fluentbit-1  |      \/                     \/     \/               \/                        \/ 
fluentbit-1  | 
fluentbit-1  | [2024/04/28 23:45:21] [ info] [fluent bit] version=3.0.3, commit=3529bbb132, pid=1
fluentbit-1  | [2024/04/28 23:45:21] [ info] [storage] ver=1.5.2, type=memory, sync=normal, checksum=off, max_chunks_up=128
fluentbit-1  | [2024/04/28 23:45:21] [ info] [cmetrics] version=0.9.0
fluentbit-1  | [2024/04/28 23:45:21] [ info] [ctraces ] version=0.5.1
fluentbit-1  | [2024/04/28 23:45:21] [ info] [input:tail:tail.0] initializing
fluentbit-1  | [2024/04/28 23:45:21] [ info] [input:tail:tail.0] storage_strategy='memory' (memory only)
fluentbit-1  | [2024/04/28 23:45:21] [ info] [sp] stream processor started
fluentbit-1  | [2024/04/28 23:45:21] [ info] [output:stdout:stdout.0] worker #0 started

起動に成功すると上記のような画面が出力されるはず。

その1 メッセージの構造を知る

https://docs.fluentbit.io/manual/concepts/key-concepts#event-format

上記に書いてある通り、fluentbitはメッセージ(イベント)は以下のような形で保持しているらしい。

[[TIMESTAMP, METADATA], MESSAGE]

ということで実際どうなのか、見てみる。

ホスト側に置いたlogファイルに何か書き込んで、コンテナのコンソール画面に何が表示されるか見てみる

echo "test message" >> log

そうするとコンテナのコンソールに以下のようなメッセージが表示されるはず

fluentbit-1  | [0] tail.0: [[1714351339.829795944, {}], {"log"=>"test message"}]

tail.0:の後ろがfluentbitが受信したイベント(ログ)。

全体のフォーマットは、Key conceptsに記載された通り

[[TIMESTAMP, METADATA], MESSAGE]

の形で保持している。METADATAは空。
同じくKey conceptsの起債によれば、TIMESTAMPはナノ秒まで保持しているらしい。。。

肝心のMESSAGEの部分。

{"log"=>"test message"}

単純に受信したメッセージを保持しているのではなく、Key-Value形式で保持しているらしい。

Keyは"log"なので、メッセージを参照する場合は、$logで参照可能

その2 デフォルトタグを知る

デフォルトの状態で、メッセージは、Key conceptsに記載の通りに保持されることが分かった。
次に、fluentbitの重要要素の一つであるタグ、さらに言えばデフォルトタグが何かを知る。

https://docs.fluentbit.io/manual/concepts/key-concepts#tag

同じく、Key conceptsのTAGによると

Most of the tags are assigned manually in the configuration. If a tag is not specified, Fluent Bit will assign the name of the Input plugin instance from where that Event was generated from.

大抵タグは、定義ファイルでアサインされる。もしタグが指定されていない場合は、イベントが生成されたInputプラグインのインスタンス名をfluentbitが割り当てる。

Inputプラグインのインスタンス名が具体的に何かを調べてみる。
違いが分かるようにInputにタグをつけない場合とつけた場合の出力が以下。

タグをつけない場合

fluentbit-1 | [0] tail.0: [[1714377370.103175583, {}], {"log"=>"aa"}]

タグ(test_tagというタグ)をつけた場合(fluent-bit.confのINPUTに Tag tag_testを追加した場合)

fluentbit-1 | [0] tag_test: [[1714434396.743894644, {}], {"log"=>"aa"}]

ということで、つけない場合は、tail.0(起動時に出力されている以下)がつくということが分かった。

fluentbit-1  | [2024/04/29 23:50:04] [ info] [input:tail:tail.0] initializing
fluentbit-1  | [2024/04/29 23:50:04] [ info] [input:tail:tail.0] storage_strategy='memory' (memory only)

ちなみにINPUTを複数設定した場合は以下のようになる。

fluentbit-1  | [2024/04/29 23:50:04] [ info] [input:tail:tail.0] initializing
fluentbit-1  | [2024/04/29 23:50:04] [ info] [input:tail:tail.0] storage_strategy='memory' (memory only)
fluentbit-1  | [2024/04/29 23:50:04] [ info] [input:tail:tail.1] initializing
fluentbit-1  | [2024/04/29 23:50:04] [ info] [input:tail:tail.1] storage_strategy='memory' (memory only)
fluentbit-1  | [2024/04/29 23:50:04] [ info] [input:tail:tail.2] initializing
fluentbit-1  | [2024/04/29 23:50:04] [ info] [input:tail:tail.2] storage_strategy='memory' (memory only)

input:tail:~~ の~~がデフォルトタグになるはず。

まぁ、ログの出力元が分からなくはないが、直感的じゃない。

その3 タグの書き換え

基本(デフォルト)のメッセージとタグが理解できたところでタグによる出力先の振り分けをやってみる。

やってみること

  • メッセージに err という文言が含まれている場合は/tmp/errorファイルにログを出力
  • メッセージに warn という文言が含まれている場合は/tmp/warningファイルにログを出力

やりかた

FILTER

  • Rewrite Tagを使ってメッセージ本文を精査
  • errという文言が含まれている場合はタグをerror.$TAG($TAGは元のタグ)に書き換える
  • warnという文言が含まれている場合はタグをwarning.$TAG($TAGは元のタグ)に書き換える

OUTPUT

こちらは、Matchを使って

  • タグがerror.*の場合は/tmp/errorファイルに出力
  • タグがwarning.*の場合は/tmp/warningファイルに出力

ということをやってみる

fluent-bit.conf変更

以下のように書き換え

[INPUT]
  Name tail
  Path /tmp/log

[FILTER]
  Name rewrite_tag
  Match *
  Rule $log .*err .* error.$TAG false

[FILTER]
  Name rewrite_tag
  Match *
  Rule $log .*warn .* warning.$TAG false

[OUTPUT]
  Name file
  Match error.*
  Path /tmp/error

[OUTPUT]
  Name file
  Match warning.*
  Path /tmp/warning

[OUTPUT]
  Name stdout
  Match *

最後のOUTPUTはデバッグ用に標準出力に出してるだけなので、必須ではないです。

先に書いておくと、この例失敗します

起動

docker compose upで起動してみる。
起動はしますが、起動時に以下のようなメッセージが出力され、rewrite_tagは無効な状態で起動してくる。

fluentbit-1 | [2024/04/30 10:07:52] [ warn] [filter:rewrite_tag:rewrite_tag.0] 'Match' may cause infinite loop.

上記メッセージの通りなのですが、Match * となっていて、どんなメッセージでもひっかけてしまう。

  1. err という文言が含まれているメッセージを検知してタグを error.~~~ に書き換える
  2. 書き換えたタグも同じくひかかって error.error.~~ というタグに書き換える

上記処理が延々と繰り返されてしまい、無限ループに陥ります。
別のバージョンで同じく無限ループに入った場合は、rewrite_tagの無限ループがチェックされず有効かされた状態で起動され、いざログを流してみるとfluentbitが落ちるという事象が発生しました。。。。(チェックできる範囲があるのか、バージョン差異なのか、、)

おそらくなのですが、FILTERで変更を加えると、再度すべてのFILTERを評価し、適合するFILTERがなくなると、次のStageに移行するみたい。
なので、FILTERはそこそこ厳密に書かないといけない模様。。。まぁ、Match * とかしないようにすると覚えておけばよいかと。

定義の作り直し

上記を踏まえ、再度fluent-bit.confを作り直し。
Matchのところを書き換えます。Matchについては、Routing Sectionに記載があるのでそこを参考に書き換え。

https://docs.fluentbit.io/manual/concepts/data-pipeline/router

[INPUT]
  Name tail
  Path /tmp/log

[FILTER]
  Name rewrite_tag
  Match_regex [^(error|warning)].*
  Rule $log .*err.* error.$TAG false

[FILTER]
  Name rewrite_tag
  Match_regex [^(error|warning)].*
  Rule $log .*warn.* warning.$TAG false

[OUTPUT]
  Name file
  Match error.*
  File /tmp/error

[OUTPUT]
  Name file
  Match warning.*
  File /tmp/warning

[OUTPUT]
  Name stdout
  Match *

Matchの個所を正規表現を使うようにMatch_regexに変更。
タグがerrorもしくはwarningで始まらない場合という風に書き換えます。

再度起動

docker compose upで起動してみる。
今回は正常に起動するはず

動作確認

ホスト側で echo "aa err error" >> log を実行。

docker compose exec -it fluentbit cat /tmp/error を実行して以下のように表示されればOK

$ docker compose exec -it fluentbit cat /tmp/error
error.tail.0: [1714528459.975296387, {"log":"aa err error"}]

echoを実行したタイミングでコンテナを実行したターミナルにも以下が表示されているはず。

fluentbit-1  | [0] error.tail.0: [[1714528459.975296387, {}], {"log"=>"aa err error"}]

タグがtail.0からerror.tail.0に書き換わっているのも確認できる。

warningもやってみてください。

FILTERの評価順序について

FILTERの評価順序は、fluent-bin.confに記載した順序で評価されます。
試しに以下を実行してみてください。

echo "err warn" >> log
echo "warn err" >> log 

どちらも error.tail.0のタグが付与され、/tmp/errorファイルにログが出力されていると思います。

fluent-bin.confのFILTERの順序を変えてwarnを上に持ってきて再度やってみてください。
今度は、どちらもタグが warning.tail.0 になっていると思います。

今回は簡単な例だったので完璧に振り分けることは出来ませんが、実際に運用するとなると

  • 一意に識別できるようなログフォーマット
  • 一意に識別できるような正規表現

が必要になるので、ログを設計する際に留意してください。(あとからどうこうしようとすると大変なので。。。)

以上が、fluentbitのキホンになります。

Parserを使って、ログを構造化したり、色々できることはあるので、試してみてください。

Discussion