[初心者向け]シンプルなものを触りながら理解するFluent Bit

[初心者向け]シンプルなものを触りながら理解するFluent Bit

Clock Icon2025.01.16

こんにちは。まるとです。
今回、Fluent Bitを触る機会が得られたため、シンプルなものを構築しながらざっくり動きを理解できればと思い、備忘録として記事を執筆します。

Fluent Bitとは

https://fluentbit.io/

ログの収集やフィルタリングなどの加工、転送などを行える、テレメトリエージェントです。
Fluent Bitを使用することで様々なソフトウェアのログを収集しながら、ログ形式統一化をはじめとした加工を行えます。

image

Fluent Bitのパイプライン

Fluent Bitのパイプラインは以下で構成されます。

名称 説明
Input Fluent Bitにデータを入力(収集)するフェーズ
Parser ログをパース(構造化データに変換)するフェーズ
Filter データを加工、変換、フィルタリングするフェーズ
Buffer Filterで加工されたログを不変の状態で保持するフェーズ
Router ログの配信先をルーティング(宛先の設定)するフェーズ
Output データの送信先を定義、出力するフェーズ

図に示すと以下のフローとなります。

flow

順番に見ていきましょう。

Input

実際にログを収集するための入り口(収集方法の設定)を定義するフェーズです。
収集方法は複数の方法をサポートしており、標準入力、tailなどシンプルなものから、MQTTやSyslog、HTTPなど各種プロトコルが対応しています。

ここではどのような方式でログを収集するのかのみを定義します。
詳しい設定方法は後ほど「触ってみよう」の章でご説明します。

なお、公式ドキュメントでは以下のページで案内しています。

https://docs.fluentbit.io/manual/pipeline/inputs

Parser

収集したログを構造化データに変換するフェーズです。
例えば以下のログがあったとします。

[2025/1/15 15:00] FROM:Aさん TO:Bさん MESSAGE:こんにちは1
[2025/1/15 15:01] FROM:Aさん TO:Cさん MESSAGE:こんにちは2
[2025/1/15 15:02] FROM:Bさん TO:Cさん MESSAGE:こんにちは3

人間が見ると、日時送信者送信先メッセージという4つの要素があるように見えます。
しかし、コンピュータの世界では、ただの1行の文字列に過ぎません。
ただの文字列ではデータの構造が明示されていないため、例えば、「メッセージの部分だけ抜き出したい!」となっても、コンピュータどこを抜き出せばいいのかわかりません。

そこでパースを行うことで、データの構造を明示(構造化データに変換)してあげる必要があります。
例えば、以下のように変換することができます。
※わかりやすいようにプロパティ名を日本語で記載しています。

{
    "日時": "2025/1/15 15:00",
    "送信者": "Aさん",
    "受信者": "Bさん",
    "メッセージ": "こんにちは"
}

このように構造化データに変換してあげることによって、プログラムで特定の要素を取得や加工が容易となります。

Filter

Filterは収集したログデータを加工できるフェーズです。
例えば先ほどのログ

[2025/1/15 15:00] FROM:Aさん TO:Bさん MESSAGE:こんにちは1
[2025/1/15 15:01] FROM:Aさん TO:Cさん MESSAGE:こんにちは2
[2025/1/15 15:02] FROM:Bさん TO:Cさん MESSAGE:こんにちは3

から送信者Aさんのものだけ、ログとして出力したいとします。

# 出力例
[2025/1/15 15:00] FROM:Aさん TO:Bさん MESSAGE:こんにちは1
[2025/1/15 15:01] FROM:Aさん TO:Cさん MESSAGE:こんにちは2

先ほど、構造化データに変換したことにより、特定の要素を取得は容易になっています。

そのため、今回の場合では送信者Aさんというフィルタを作成することで実現できます。
実際にこの後の触ってみようでも触れていきたいと思います。

その他、ログごとにタグをつけることでき、付与されているタグによってログの配信先を振り分けるということも可能です。

Buffer

メモリまたはファイルシステム上に、ログをバッファリング(一時的に記録)するフェーズです。
ネットワークの問題などによりログを配信できないや瞬間的に配信処理が追いつかない場合、一時的にFluent Bit上に保持しておくことで、データの損失を防止します。

ログの読み書き速度から多くの場合は、メモリ上にバッファリングする方針が中心となりますが、例えば

  • 大量のログデータを扱うなどメモリが不足する可能性があるケース
  • セキュリティログなどシステム障害が発生した際に、データの消失が発生してはならないデータを扱うケース
    などユースケースによって、バッファリング先をファイルシステムにすることもできます。

Router

配信先を振り分けるコア機能です。
通常は配信先Aにログを送信しつつ、特定のログが来たら配信先Bにログを送信したり、同じログを複数の配信先に送信するなど宛先を決定することができます。

特に

通常は配信先Aにログを送信しつつ、特定のログが来たら配信先Bにログを送信

の実現のため、「Filter」のフェーズでも説明しましたが、ログにタグをつけることができます。
タグの内容などによって配信先を切り替える役割を持つのが、Routerです。

例えばParseにより構造化された次のログがあったとします。

{
    "datetime": "2025/1/15 15:00",
    "method": "GET",
    "ResponseCode": 200,
    "Path": "/index.html",
    "client": "192.168.0.2"
},
{
    "datetime": "2025/1/15 15:02",
    "method": "GET",
    "ResponseCode": 404,
    "Path": "/not_found.html",
    "client": "192.168.0.3"
},
{
    "datetime": "2025/1/15 15:05",
    "method": "GET",
    "ResponseCode": 200,
    "Path": "/hello_world.html",
    "client": "192.168.0.5"
}

その後、FilterフェーズでResponseCode404の場合、errorというタグを付与します。

{
    "datetime": "2025/1/15 15:00",
    "method": "GET",
    "ResponseCode": 200,
    "Path": "/index.html",
    "client": "192.168.0.2"
},
{
    "tag": "error",
    "datetime": "2025/1/15 15:02",
    "method": "GET",
    "ResponseCode": 404,
    "Path": "/not_found.html",
    "client": "192.168.0.3"
},
{
    "datetime": "2025/1/15 15:05",
    "method": "GET",
    "ResponseCode": 200,
    "Path": "/hello_world.html",
    "client": "192.168.0.5"
}

あとRouterでタグの内容に沿って、配信先を振り分けることができます。
これにより

通常は配信先Aにログを送信しつつ、特定のログが来たら配信先Bにログを送信

という要件を実現することができます。

Output

ログの出力部分となります。
標準出力のほか、各種アプリケーションやプロトコルとして出力、Amazon S3やAmazon CloudWatchなどクラウドサービスにも出力することができます。

実際に触ってみよう

では実際に触って、動きを掴んでいければと思います。

前提条件

今回の検証にあたり、以下の環境で実施しています。
OSやターミナルが違う場合、一部ソフトウェアのインストール方法が異なる場合がございます。
また、ソフトウェアのインストール方法については各種公式ドキュメントをご確認ください。

  • macOS
  • Homebrew 4.4.16
  • Nginx 1.27.3
  • Fluent Bit v3.2.4

要件は次のとおりです。

  • Webサーバー(Nginx)のアクセスログを振り分け
  • シンプルかつフローを簡単に理解できるよう、Webサーバー側の設定は変更しない
  • 基本的に全てのログをFluent Bitの標準出力
  • レスポンスコードが404の場合はファイルに書き出す

はじめに

Fluent Bitを利用するために各フローの定義を行う必要があります。
今回はsandbox.confという名前で作成していきます。

Serviceの準備

まずはじめにFluent Bit自体の動作を設定します。
今回はWebサーバー(Nginx)のログを収集していきます。

Nginxのログパーサーは標準で用意されているため、以下のように設定を記載します。

[SERVICE]
    parser_file /opt/homebrew/etc/fluent-bit/parsers.conf

なお、Fluent-Bit標準のParserはmacOS + HomebrewでFluent Bitをインストールした場合、通常、以下に定義されていますが、OSなどによって異なる場合がございます。

/opt/homebrew/etc/fluent-bit/parsers.conf 

標準のNginxのパーサーについて、以下のように記載があります。

[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

正規表現によるパースを行うようになっています。
これにより例えば次のアクセスログがあったとします。

127.0.0.1 - - [16/Jan/2025:11:35:27 +0900] "GET / HTTP/1.1" 200 615 "-" "curl/8.7.1"

このログをパースすると

{
    "remote": "127.0.0.1",
    "host": "-",
    "user": "-",
    "time": "16/Jan/2025:11:35:27 +0900",
    "method": "GET",
    "path": "/",
    "code": "200",
    "size": "615",
    "referer": "-",
    "agent": "curl/8.7.1"
}

のように構造化データとなります。
これにより、後続のFilterなどで加工などがしやすくなります。

Inputの定義

続いて、ログの入り口/収集方法の定義をしていきます。

本検証ではよりシンプルかつフローを簡単に理解できるよう、Webサーバー側の設定は変更しない、という要件があります。
そのため、Webサーバーが標準で出力されるアクセスログをTailを利用して継続的に監視します。

そこで、ログなどを継続的に取得できるTailを使用していきます。
なお、コマンドラインtail -fと同様の挙動となります。

[INPUT]
    name tail
    path <Webサーバーログファイルへのパス>
    Parser nginx
    Tag nginx.logs

上記の設定ファイルを詳しく説明すると

[INPUT]
    name tail           # 取得方式としてtailを指定
    path <ログへのパス>   # 指定したパスをtailで取得
    Parser nginx        # nginxという名前のパーサーを使用
    Tag nginx.logs      # nginx.logsというタグを付与

今回はよりシンプルに実装する手段としてtaiiを使用していますが、他にも複数の取得方式がございます。
詳しくは公式ドキュメントをご確認ください。

https://docs.fluentbit.io/manual/pipeline/inputs

Filterの定義

続いてFilterを定義していきます。
今回の要件は次のとおりです。

  • 基本的に全てのログをFluent Bitの標準出力
  • レスポンスコードが404の場合はファイルに書き出す

先ほど、ログ自体は構造化データに変換できています。
ログの振り分けはRouterの処理(Fluent Bitのパイプライン参照)で実施します。振り分けにはタグを利用するかつ今回の要件では、レスポンスコードが404場合のみファイルに書き出すため、

  • レスポンスコードが404の時に、特定のタグに書き換え

という加工をやっていきます。
それでは実際に設定を書いていきましょう。

同じく、先ほどINPUTで書いた設定ファイル内に以下を追加します。

[FILTER]
    Name rewrite_tag
    Match nginx.logs
    Rule $code ^404$ nginx.logs.not_found false

すごく直感的なのではないでしょうか。
タグの書き換えというフィルタリングでcodeが特定の値の時、適切なタグに変更するという設定です。

[FILTER]
    Name rewrite_tag    # タグの書き換え操作
    Match nginx.logs    # nginx.logsタグがついているログを対象
    Rule $code ^404$ nginx.logs.not_found false # codeが404の場合、タグをnginx.logs.not_foundに書き換えて、元のデータを破棄する

ログの振り分け・ログの出力

最後にログの配信先振り分け、出力を行なっていきます。
では実際に書いていきましょう。

[OUTPUT]
    Name file
    Match nginx.logs.not_found
    Path <ログ出力先ディレクトリへのパス>

[OUTPUT]
    Name stdout
    Match *

まず、一つ目の[OUTPUT]

  • 書き出し先の形式を指定
  • Matchを利用してタグとの一致を条件に設定
  • Pathに書き出し先ディレクトリへのパスを設定

そして、二つ目の[OUTPUT]

  • 出力先を標準出力
  • *により全てのログを対象

という二つのフローを指定しています。
複数[OUTPUT]を記載することで、条件によってログの振り分けを行いながら、ログの出力ができるため、Fluent Bit上でのフローがわかりやすくなっています。

完成した設定ファイル

本過程で完成した設定ファイルは次のとおりです。

[SERVICE]
    parsers_file /opt/homebrew/etc/fluent-bit/parsers.conf

[INPUT]
    name tail
    path <ログへのパス>
    Parser nginx
    Tag nginx.logs

[FILTER]
    Name rewrite_tag
    Match nginx.logs
    Rule $code ^404$ nginx.logs.not_found false

[OUTPUT]
    Name file
    Match nginx.logs.not_found
    Path <ログ出力先ディレクトリへのパス>

[OUTPUT]
    Name stdout
    Match *

上から順番に処理を記載する設定ファイルのため、どのように入力、加工をし、出力する、というフローが非常にわかりやすくなっています。
Fluent Bitではやりたい操作をよりシンプルに書くことができます。

実際に動作させてみる

それでは実際に試してみましょう。設定ファイルを指定してFluent Bitを起動します。

fluent-bit -c <設定ファイル>
Fluent Bit v3.2.4
* Copyright (C) 2015-2024 The Fluent Bit Authors
* Fluent Bit is a CNCF sub-project under the umbrella of Fluentd
* https://fluentbit.io

______ _                  _    ______ _ _           _____  _____ 
|  ___| |                | |   | ___ (_) |         |____ |/ __  \
| |_  | |_   _  ___ _ __ | |_  | |_/ /_| |_  __   __   / /`' / /'
|  _| | | | | |/ _ \ '_ \| __| | ___ \ | __| \ \ / /   \ \  / /  
| |   | | |_| |  __/ | | | |_  | |_/ / | |_   \ V /.___/ /./ /___
\_|   |_|\__,_|\___|_| |_|\__| \____/|_|\__|   \_/ \____(_)_____/

[2025/01/16 14:59:54] [ info] [fluent bit] version=3.2.4, commit=, pid=77226
[2025/01/16 14:59:54] [ info] [storage] ver=1.5.2, type=memory, sync=normal, checksum=off, max_chunks_up=128
[2025/01/16 14:59:54] [ info] [simd    ] disabled
[2025/01/16 14:59:54] [ info] [cmetrics] version=0.9.9
[2025/01/16 14:59:54] [ info] [ctraces ] version=0.5.7
[2025/01/16 14:59:54] [ info] [input:tail:tail.0] initializing
[2025/01/16 14:59:54] [ info] [input:tail:tail.0] storage_strategy='memory' (memory only)
[2025/01/16 14:59:54] [ info] [input:emitter:emitter_for_rewrite_tag.0] initializing
[2025/01/16 14:59:54] [ info] [input:emitter:emitter_for_rewrite_tag.0] storage_strategy='memory' (memory only)
[2025/01/16 14:59:54] [ info] [output:file:file.0] worker #0 started
[2025/01/16 14:59:54] [ info] [sp] stream processor started
[2025/01/16 14:59:54] [ info] [output:stdout:stdout.1] worker #0 started

上記のような文字列が出力されたら起動完了です。
実際にWebサーバーにアクセスしてみます。

curl http://localhost:8080

するとFluent Bit側でログが取得・出力されます。

[0] nginx.logs: [[1737007276.000000000, {}], {"remote"=>"127.0.0.1", "host"=>"-", "user"=>"-", "method"=>"GET", "path"=>"/", "code"=>"200", "size"=>"615", "referer"=>"-", "agent"=>"curl/8.7.1"}]

続いて、404を発生させます。適当なパスを入れてWebサーバーにリクエストします。

curl http://localhost:8080/notfound
[0] nginx.logs.not_found: [[1737007362.000000000, {}], {"remote"=>"127.0.0.1", "host"=>"-", "user"=>"-", "method"=>"GET", "path"=>"/notfound", "code"=>"404", "size"=>"153", "referer"=>"-", "agent"=>"curl/8.7.1"}]

タグが変わりましたね。
また、ファイルにもエラーが出力されている確認するため、ログの出力先ディレクトリを確認します。

xxxxxxxxxxxxxx@xxxxxxxx errors % ls -l 
total 8
-rw-r--r--  1 xxxxxxxxxxxxxx  staff  186  1 16 15:03 nginx.logs.not_found
xxxxxxxxxxxxxx@xxxxxxxx errors % cat nginx.logs.not_found 
nginx.logs.not_found: [1737007362.000000000, {"remote":"127.0.0.1","host":"-","user":"-","method":"GET","path":"/notfound","code":"404","size":"153","referer":"-","agent":"curl/8.7.1"}]

正しく出力されています。

終わりに

今回はシンプルなものを構築しましたが、設定ファイルが非常に読みやすくシンプルだと感じました。
複数のログを集配信したい、加工や振り分けを行いたいというユースケースにはすごく導入しやすいツールだと思います。
ぜひ皆様も触ってみてください。

おまけ

標準のパーサーが用意されていない場合、どうすればいいのか?

自身でパーサーを定義する必要があります。正規表現を例にします。

まず、パーサー用の設定ファイルを作成します。(sample-parser.conf
[PARSER]という定義を作成し、パーサーの名前、マッチング方式、正規表現を書いていきます。

ログ例

[2025/1/15 15:00] FROM:Aさん TO:Bさん MESSAGE:こんにちは1

記述例

[PARSER]
    Name sample-parser
    Format regex
    Regex ^\[(?<datetime>[^\]]*)\] FROM\:(?<sender>[^ ]*) TO\:(?<destination>[^ ]*) MESSAGE\:(?<message>[^ ]*)

設定ファイルを作成後、Fluent Bitの設定ファイル内[SERVICE]parsers_fileとしてファイルをパスを指定します。

[SERVICE]
    parsers_file <ファイルパス>

その後、Parserにパーサーの名前を設定することで利用可能です。

[INPUT]
    name tail
    path <ログへのパス>
    Parser sample-parser

https://docs.fluentbit.io/manual/pipeline/parsers/configuring-parser

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.