こんにちは、Яeiです。
今回は現役エンジニアである私がFluent Bitって何だという話についてまとめたいと思います。
最近巷で良く聞くFluent Bitと呼ばれるものがありますが、そもそもこれって何なのか調査しましたのでシェア致します。
最後はdocker-composeを用いたサンプルも用意しましたので見てみて下さい。
目次
Fluent Bit
概要は公式サイトに分かりやすく書かれておりますので興味のある方は一読しておくとよいでしょう。
また、各種ドキュメントは以下公式資料に詳しく書かれておりますのでこちらを参考にしてもらえればと思います。
概要
Fluent Bitとは、
アプリケーションから出力されたログファイルや標準出力ログなどのデータを収集し、フィルタリングして複数の宛先に送信できるツールとなります。
例えば昨今流行りのコンテナ化において、コンテナログを標準出力する機会も多いと思います。
その場合、logging driverにFluent Bitを指定しておくことで、ログを簡単にElasticSearchやSlack、Amazon CloudWatchなどに転送することができるのです。
つまり、コンテナ化やリアルタイム監視が話題になる現在において、ログの転送ツールとして大いに活躍してくれそうです。
特徴
公式サイトから拝借しますと、
C言語で記述されており、パフォーマンスを念頭に置いて設計されているため、CPU/メモリの使用量が少なく高スループット
とのことです。
Fluent bit VS Fluentd
違いについては以下見てもらえれば記載があります。
違いはいくつかありますが、Fluent Bitを採用する場合、大きな理由としてはリソース使用量の少なさでしょう。
例えば、Memoryに関してみますと、Fluentdについては~40MBに対し、Fluent Bitについては~650KBと大幅に節約されております。
もちろん、Fluentdの方が機能(プラグイン)が圧倒的に多かったりとメリットも多いのですが、Fluent Bitで実現できる内容であればFluent Bitを使った方が良いと考えるのも自然な気がします。
なお、AWSのECSサービスにおいて利用可能なFirelensでもFluent Bitを採用しており、その理由もまた以下のように明記されておりました。
Fluent Bit が AWS の推奨ですが、それは Fluentd に比べてリソース使用量が大幅に少ないからです
https://aws.amazon.com/jp/blogs/news/under-the-hood-firelens-for-amazon-ecs-tasks/
重要なコンセプト
以下は読んでおいてください、とのことですので一読しておきます。
ここでは基本的に以下の公式サイトを抜粋およびざっくり記載になりますので以下読んで頂く形で良いと思います。
Event or Record
Fluent Bit によって取得されるログまたはメトリクスに属する受信データはすべて、イベントまたはレコードと見なされます。
例えば以下のSyslogが渡されたとすると、これらは4 つの独立したイベントとして処理されます。
Jan 18 12:52:16 flb systemd[2222]: Starting GNOME Terminal Server
Jan 18 12:52:16 flb dbus-daemon[2243]: [session uid=1000 pid=2243] Successfully activated service 'org.gnome.Terminal'
Jan 18 12:52:16 flb systemd[2222]: Started GNOME Terminal Server.
Jan 18 12:52:16 flb gsd-media-keys[2640]: # watch_fast: "/org/gnome/terminal/legacy/" (establishing: 0, active: 0)
Filtering
各イベント(上記例ではログ1行)に対してフィルタリングすることが可能となります。
例えば、特定のパターンに引っかかったログは取り込み対象外とするなど。
あるいは、特定のIPの場合にこうする、など制御することも可能となります。
Tag
各イベント(上記例ではログ1行)にはタグが割り当てられます(入力プラグインはForwardの場合は割り当てされない)。
タグが指定されていない場合、そのイベントが生成された場所から入力プラグインインスタンスの名前を割り当てます。
このタグは、フィルターや出力フェーズでイベント(ログ)を指定するために使用される内部文字列となります。
ざっくりとしたイメージになりますが、このタグが付いていたらSlackに通知、このタグがついていたらElastic Searchに送る、などの切り分けに使うようなものになります。
データパイプライン
Fluent Bitのデータの流れは以下のようなパイプラインになっております。
単純に、Fluent Bitに送られてきたログを転送するだけではなく、間にParser、Filterなどの機能があるのです。
Buffer、Routingは概念的な部分になりますので、以下の説明では割愛致します。
Input
Fluent Bit では、様々なリソースからの入力を受け付けられるように様々な入力プラグインが提供されております。
例えばログファイルからの読み取りや、システムログや標準出力など用途に応じて様々です。
Fluent Bitで読み取りたいデータに応じてInputプラグイン設定をする必要があります。
Parser
ログを取り込んだ後、Fluent Bitではログを適切な構成にパースできます。
例えば、以下のようなApacheログをInputした場合、Parserでは以下のようにパースしてくれます。
192.168.2.20 - - [28/Jul/2006:10:27:10 -0300] "GET /cgi-bin/try/ HTTP/1.0" 200 3395
↓ Parse
{
"host": "192.168.2.20",
"user": "-",
"method": "GET",
"path": "/cgi-bin/try/",
"code": "200",
"size": "3395",
"referer": "",
"agent": ""
}
Filter
データを宛先に配信する前にデータを変更できる重要な機能とのことです。
入力されたログを変更したり、除外したりと制御することができます。
Output
出力先を定義します。
Filterを用いてTagやMatchを指定することで、複数の出力先に分けることも可能となります。
設定ファイル
設定ファイルについても公式サイトに丁寧に記載されております。
参考
Configuring Fluent BitFluent Bit: Official Manual
Fluent Bitの設定ファイルは以下の2種類の形式をサポートしているとのことです。
・Classic Mode
or
・Yaml
Classic Modeはよく見るような以下の形式です(スペース4推奨とのことです)。
[SERVICE]
Flush 5
Daemon off
Log_Level debug
[INPUT]
Name cpu
Tag my_cpu
[OUTPUT]
Name stdout
Match my*cpu
一方Yaml形式での記載もサポートしているとのことです。
service:
flush: 5
daemon: off
log_level: debug
pipeline:
inputs:
- name: cpu
tag: my_cpu
outputs:
- name: stdout
match: 'my*cpu'
次に設定ファイルの設定要領を見ていきます(よく見かけるClassic Modeになりますが、Yamlでも似たような感じになりますので公式資料参照下さい)。
設定ファイルの構成は以下の4つになります。
・Service
・Input
・Filter
・Output
これらの概念はすでに説明済みですね。
また、後述しますが、以下のような便利な機能もありますので利用幅が広がることでしょう。
・ファイルのインクルード機能(ファイル分割が可能になる)
・環境変数の読み込み機能
ちなみに、以下のサイトを使うことで可視化可能となります(Yaml形式には対応していなさそうでした)。
https://cloud.calyptia.com/visualizer
Service
Serviceセクションでは設定可能な項目が多いので、ここでは抜粋して記載致します。
他にも設定可能項目が多くありますので、一読してから利用要否を決めると良いでしょう。
・flush
・grace
・daemon
・log_level
flush
出力プラグインにフラッシュ(流す)する間隔を設定します。
デフォルトでは5秒間隔でフラッシュします。
grace
Fluent BitがSIGTERMを受け取ってからシャットダウンするまでの時間(待機時間)となります。
デフォルトは5秒になってます。
daemon
Fluent Bitをデーモン(バックグラウンド)として実行するかどうかを設定します。
(設定値:yes、no、on、off)
デフォルトはoffになっています(dockerのサイドカーとして使う場合はoffで良さそうな気がします)
log_level
エラーレベルを指定します。
error、warning、info、debug
を指定でき、info指定した場合はerror、warningも対象。
デフォルトはinfoとなっています。
なお、この項目は以降のセクションでも指定可能となります。
例:
[SERVICE]
Flush 1
Grace 30
Daemon off
Log_Level debug
Input
ここでは入力プラグインの指定をします。
設定できるのは以下の三つになります。
・Name
・Tag
Name
入力プラグインの名前を記載します。
適宜以下を参照して何を設定するか確認してください。
参考
Input PluginsFluent Bit: Official Manual
Tag
タグ名を任意名称で指定します。
この入力プラグインを通ったログに対してタグ付けすることで、後続のFilterやOutputでログを制御することが可能となります。
例:
[INPUT]
Name cpu
Tag my_cpu
Filter
ここではフィルタープラグインを指定します。
詳細はフィルタープラグインに準拠しますが、以下は必須となります。
※ 下二つはどちらか片方設定する。
・Name
・Match
・Match_Regex
※ このセクションは複数設定することで、転送先を複数にすることもできます。
Name
フィルタープラグインを記載します。
フィルタープラグインの種類は以下参照下さい。
(サンプルも付いているので英語でも直感的に分かると思います)
参考
Filter PluginsFluent Bit: Official Manual
Match
入力レコードのタグに対して、パターンマッチングを行い出力対象とします。
大文字と小文字が区別されワイルドカード(*)指定が可能となります。
Match_Regex
Matchではなく、正規表現で出力対象を指定したい場合に設定します。
Matchと両方指定されている場合は、こちらのMatch_Regexが優先されます。
例:
[FILTER]
Name grep
Match *
Regex log aa
※ RegexはGrepプラグインの設定項目
Output
ここでは出力プラグインの設定を行います。
主に設定できるのは以下の3つです。
・Name
・Match
・Match_Regex
※ このセクションは複数設定することで、転送先を複数にすることもできます。
Name
出力プラグインを記載します。2023年1月現在では256個の出力プラグインをルーティングできるようです。
適宜以下を参照して何を設定するか確認してください。
参考
Output PluginsFluent Bit: Official Manual
Match
入力レコードのタグに対して、パターンマッチングを行い出力対象とします。
大文字と小文字が区別されワイルドカード(*)指定が可能となります。
Match_Regex
Matchではなく、正規表現で出力対象を指定したい場合に設定します。
例:
[OUTPUT]
Name stdout
Match my*cpu
その他便利機能
ファイル分割
Fluent Bit 0.12 以降では「@INCLUDE」によって、ファイル読み込みが可能となりました。
例:
@INCLUDE somefile.conf
ワイルドカード(*)を指定することも可能となりますが、順序関係が必要な場合は順序性は保証してくれないため、ファイル名を明示する必要があります。
なお、セクション内にこの定義をすることができないので注意が必要です。
例:
[SERVICE]
Flush 5
Daemon off
Log_Level debug
[INPUT]
Name cpu
Tag my_cpu
@INCLUDE somefile.conf
[OUTPUT]
Name stdout
Match my*cpu
変数
設定ファイルでは「@SET 」にて、変数の使用も可能となります。
例:
@SET my_input=cpu
@SET my_output=stdout
[SERVICE]
Flush 1
[INPUT]
Name ${my_input}
[OUTPUT]
Name ${my_output}
環境変数
設定ファイルでは環境変数の使用も可能となります。
参考
VariablesFluent Bit: Official Manual
変数は大文字と小文字が区別され、次の形式で使用できます。
${MY_VARIABLE}
例:
$ export MY_OUTPUT=stdout
[SERVICE]
Flush 1
Daemon Off
Log_Level info
[INPUT]
Name cpu
Tag cpu.local
[OUTPUT]
Name ${MY_OUTPUT}
Match *
Record Accessor
Fluent Bitでは、構造化されたログに対してレコードの特定の部分を選択する方法が備わっております。
参考
Record AccessorFluent Bit: Official Manual
以下のようなログがあったとします。
{
"log": "some message",
"stream": "stdout",
"labels": {
"color": "blue",
"unset": null,
"project": {
"env": "production"
}
}
}
$log
|
“some message”
|
$labels[‘color’]
|
“blue”
|
$labels[‘project’][‘env’]
|
“production”
|
$labels[‘unset’]
|
null
|
$labels[‘undefined’]
|
|
公式資料通りですが、一例見れば一目瞭然ですので以下サンプルになります。
test.logが以下の通りだったとします。
{"log": "message 1", "labels": {"color": "blue"}}
{"log": "message 2", "labels": {"color": "red"}}
{"log": "message 3", "labels": {"color": "green"}}
{"log": "message 4", "labels": {"color": "blue"}}
これをTailプラグインを使ってFluent Bitで取り込みます。
[SERVICE]
flush 1
log_level info
parsers_file parsers.conf
[INPUT]
name tail
path test.log
parser json
[FILTER]
name grep
match *
regex $labels['color'] ^blue$
[OUTPUT]
name stdout
match *
format json_lines
すると結果は以下のようになります。
{"date":1599862267.483684,"log":"message 1","labels":{"color":"blue"}}
{"date":1599862267.483692,"log":"message 4","labels":{"color":"blue"}}
labelsのcolorがblueのもののみが出力されているのが分かります。
Parser(執筆中)
執筆中
参考
ParsersFluent Bit: Official Manual
超簡単なお試し(別コンテナのechoを標準出力)
参考
Fluent Bit READMEFluent Bit
まずはFluent BitのDocker Hub公式イメージのREADME記載内容を試してみます。
echoで標準出力したログを、Dockerのロギングドライバとして設定したFluent Bitコンテナで読み込み、標準出力する構成になります。
まず、ロギングドライバを先に起動する必要がありますので、以下のコマンドにてFluent Bitコンテナを起動します。
(設定ファイルではなくCLIオプションでの指定になります。https://docs.fluentbit.io/manual/administration/configuring-fluent-bit)
docker run -p 127.0.0.1:24224:24224 fluent/fluent-bit /fluent-bit/bin/fluent-bit -i forward -o stdout -p format=json_lines -f 1
そして、次にechoするためのコンテナを起動します(ロギングドライバの指定必須)。
docker run --log-driver=fluentd -t ubuntu echo "Testing a log message"
すると、Fluent Bitコンテナのターミナルの方に以下のように出力されることが確認できます。
Status: Downloaded newer image for fluent/fluent-bit:latest
Fluent Bit v2.0.8
* Copyright (C) 2015-2022 The Fluent Bit Authors
* Fluent Bit is a CNCF sub-project under the umbrella of Fluentd
* https://fluentbit.io
[2023/01/09 00:30:16] [ info] [fluent bit] version=2.0.8, commit=9444fdc5ee, pid=1
[2023/01/09 00:30:16] [ info] [storage] ver=1.4.0, type=memory, sync=normal, checksum=off, max_chunks_up=128
[2023/01/09 00:30:16] [ info] [cmetrics] version=0.5.8
[2023/01/09 00:30:16] [ info] [ctraces ] version=0.2.7
[2023/01/09 00:30:16] [ info] [input:forward:forward.0] initializing
[2023/01/09 00:30:16] [ info] [input:forward:forward.0] storage_strategy='memory' (memory only)
[2023/01/09 00:30:16] [ info] [input:forward:forward.0] listening on 0.0.0.0:24224
[2023/01/09 00:30:16] [ info] [sp] stream processor started
[2023/01/09 00:30:16] [ info] [output:stdout:stdout.0] worker #0 started
{"date":1673224261.0,"source":"stdout","log":"Testing a log message\r","container_id":"d02554da73083c99dbb45ba88e778c2bc28096a4a9c6ca8625de9b5a9fbf12ae","container_name":"/flamboyant_cannon"}
超簡単なお試し
お試しの構成は以下のようなイメージです。
ブラウザからhttp通信を行うことでServer側ではログを出力します。
そのログはFluent Bitコンテナに転送され、標準出力されるという構成です。
(標準出力されたログはdocker logsコマンドでコンテナを見はることで確認できます)
Composeファイルは次の通り。
version: '3.7'
services:
sample_service1:
image: gihyodocker/echo:latest
ports:
- "8080:8080"
depends_on:
- fluent-bit
logging:
driver: fluentd
options:
fluentd-address: "localhost:24224"
tag: "docker1.{{.Name}}"
sample_service2:
image: gihyodocker/echo:latest
ports:
- "8090:8080"
depends_on:
- fluent-bit
logging:
driver: fluentd
options:
fluentd-address: "localhost:24224"
tag: "docker2.{{.Name}}"
fluent-bit:
image: fluent/fluent-bit
volumes:
- ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf
ports:
- "24224:24224"
fluent-bit.confは次の通り。
[INPUT]
Name forward
Listen 0.0.0.0
Port 24224
[OUTPUT]
Name stdout
Match *
この二つのファイルをカレントディレクトリに配置して以下のコマンドを実行。
docker-compose up -d
これにて、3つのコンテナが起動したことを確認出来ると思います。
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------------------------
fluent_sample_fluent-bit_1 /fluent-bit/bin/fluent-bit ... Up 2020/tcp, 0.0.0.0:24224->24224/tcp
fluent_sample_sample_service1_1 go run /echo/main.go Up 0.0.0.0:8080->8080/tcp
fluent_sample_sample_service2_1 go run /echo/main.go Up 0.0.0.0:8090->8080/tcp
次のコマンドでログを監視してみましょう。
docker logs -f [container_name]
時折、ポートを変えたりして以下のコマンドを打ってみるとログ内容がService1用と2用で出力されることが分かります。
http://localhost:8080/
http://localhost:8090/
次に、fluent-bit.confを以下のように変更して同じことを実施してみます。
[INPUT]
Name forward
Listen 0.0.0.0
Port 24224
[OUTPUT]
Name stdout
Match docker1*
結果はdocker1のタグが追加方のサービスのみ標準出力されるようになります。
[0] docker1.test_sample_service1_1: [1673225672.000000000, {"container_id"=>"b7a1f1a7f2ec5697c7d109934879c57bde4be63b934d49d1fbf416cf90633bf0", "container_name"=>"/test_sample_service1_1", "source"=>"stderr", "log"=>"2023/01/09 00:54:32 start server"}]
[0] docker1.test_sample_service1_1: [1673225697.000000000, {"source"=>"stderr", "log"=>"2023/01/09 00:54:57 received request", "container_id"=>"b7a1f1a7f2ec5697c7d109934879c57bde4be63b934d49d1fbf416cf90633bf0", "container_name"=>"/test_sample_service1_1"}]
[1] docker1.test_sample_service1_1: [1673225697.000000000, {"container_id"=>"b7a1f1a7f2ec5697c7d109934879c57bde4be63b934d49d1fbf416cf90633bf0", "container_name"=>"/test_sample_service1_1", "source"=>"stderr", "log"=>"2023/01/09 00:54:57 received request"}]
当記事は以上となります。
今回はFluent Bitとは何なのかを確認してみました。
最後に実例を用いたことでなんとなくイメージはつかめたのではないでしょうか。
これからコンテナ化が進むにつれて、もしかしたら必須ツールになるかもしれませんね。
長々とお疲れさまでした。
以下にもFluent Bitを利用したElastic Search、Kibana構成のサンプルを記載してますので気になる方は参照下さい。