Fluent Bitを導入しました:ローカル実行・確認方法と、導入の過程でハマったこと

AlphaDrive、NewsPicks兼務でエンジニアしている大場です。 最近はNewsPicks Webの新基盤開発を行っています。

新基盤はNext.jsで開発していてAWSのFargateで構築しているのですが、このFargate上で取得したログをS3、New Relicに送るためにFluent Bitを導入しました。

今回はローカルでの実行・確認方法と、導入の過程で問題になったことを紹介します!

Fluent Bit とは

Fluent Bitは軽量高速なログ収集ツールで様々なサービスにログデータを転送できます。

C言語で実装されているのでFluentdよりも高速に動作します。

(公式サイト)https://fluentbit.io

github.com

ローカル実行・確認方法

ローカル実行、テストデータの投入、出力内容の確認を基本的な処理の出力結果を見ながら紹介します。

イメージの選択

Fluent BitのDockerイメージを利用します。

本運用ではAWS環境上に構築するため、AWS用にPluginsが更新されているAWS公式イメージを利用します。

ecr aws-for-fluent-bit

github.com

設定ファイルの準備

AWS公式イメージではfluent-bit.conf は既に用意されているため、別ファイル名で設定ファイルを作成します。

今回は application.conf で作成してみます。

application.conf

[SERVICE]
    Flush 5
    Log_Level info

[SERVICE] はFluent Bitのグローバル設定を行っています。 どんな設定ができるかは以下のページを参照ください。

https://docs.fluentbit.io/manual/administration/configuring-fluent-bit/configuration-file#config_section

このファイルを以下のようにDockerfileで配置します。

Dockerfile.fluentbit

FROM public.ecr.aws/aws-observability/aws-for-fluent-bit:2.21.3

COPY application.conf /fluent-bit/etc/application.conf

デバッグ用の設定を追加する

application.conf にテストデータ投入用の[INPUT]、出力確認用の[OUTPUT]を追加します。

application.conf

[SERVICE]
    Flush 5
    Log_Level info

[INPUT] # ← 追加
    Name dummy
    Tag  *-firelens-*
    Dummy {"date":"2022-01-23T03:10:33.317817Z","source":"stdout","log":"time:2022-01-23T03:10:33+00:00\tprotocol:HTTP/1.1\tstatus:200\tsize:1450\treqsize:150\treferer:-\treqtime:0.176\tcache:-\truntime:-\t"}

[OUTPUT] # ← 追加
    Name stdout
    Match *

動作確認

Dockerを起動してターミナルを開き、以下でFluent Bitを起動します。

/fluent-bit/bin/fluent-bit -c /fluent-bit/etc/application.conf

すると、[INPUT]に記載したDummyデータを受け取り[OUTPUT]により以下のように出力されます。

出力結果

[0] *-firelens-*: [1645367995.432183928, {"date"=>"2022-01-23T03:10:33.317817Z", "source"=>"stdout", "log"=>"time:2022-01-23T03:10:33+00:00 protocol:HTTP/1.1   status:200  size:1450   reqsize:150 referer:-   reqtime:0.176   cache:- runtime:-   "}]

簡単ですね!

ltsv形式のログを展開する

次にltsv形式のログを展開してみます。

[INPUT]のDummyデータのlogはltsv形式のデータです。

このlogデータを展開するためにはparser.confを作成して以下のように設定します。

Time_*の設定はlog内のキー項目のどのKeyのどのようなフォーマットの時間を[OUTPUT]の時間にするかを指定できます。

parser.conf

[PARSER]
    Name        parser_ltsv
    Format      ltsv
    Time_Key    time    
    Time_Format %Y-%m-%dT%H:%M:%S %z
    Time_Keep   On

このファイルを以下のようにapplication.confに設定します。

application.conf

[SERVICE]
    Flush 5
    Log_Level info
    Parsers_File parser.conf # ←ファイルの指定

[INPUT]
    Name dummy
    Tag  *-firelens-*
    Dummy {"date":"2022-01-23T03:10:33.317817Z","source":"stdout","log":"time:2022-01-23T03:10:33+00:00\tprotocol:HTTP/1.1\tstatus:200\tsize:1450\treqsize:150\treferer:-\tvhost:10.10.18.102\treqtime:0.176\tcache:-\truntime:-\t"}

[FILTER] # ← parserのための設定
    Match *-firelens-*
    Key_Name log  # ← logデータを利用
    Parser parser_ltsv # ← parser.conf内のparser_ltsvを利用して処理する指定
    Preserve_Key false
    Reserve_Data true

[OUTPUT]
    Name stdout
    Match *

Dockerfile.fluentbit

FROM public.ecr.aws/aws-observability/aws-for-fluent-bit:2.21.3

COPY application.conf /fluent-bit/etc/application.conf
COPY parser.conf /fluent-bit/etc/parser.conf # ← 追加

これを実行すると以下のようにltsvデータが展開されます。

出力結果

[1642907433.000000000, {"time"=>"2022-01-23T03:10:33+00:00", "protocol"=>"HTTP/1.1", "status"=>"200", "size"=>"1450", "reqsize"=>"150", "referer"=>"-", "vhost"=>"10.10.18.102", "reqtime"=>"0.176", "cache"=>"-", "runtime"=>"-", "date"=>"2022-01-23T03:10:33.317817Z", "source"=>"stdout"}]

Stream Processorを使う

Stream Processorは以下の図がわかりやすいです。

InputをParser、Filterで処理したものをSQLクエリを利用し、新しくStreamを作成してそれをInputとして再処理します。

https://4018815634-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LKKSx-3LBTCtaHbg0gl-887967055%2Fuploads%2Fgit-blob-67e4a80805c98f7ee77c3c40ac5e32365022ebf1%2Fflb_pipeline_sp.png?alt=media

引用:https://docs.fluentbit.io/manual/stream-processing/overview#stream-processor

例えば以下のようにすると*-firelens-*というタグの付いたStreamから新規でstdout_logというタグの付いたStreamの作成を行うことが可能です。

stream-processor.conf

[STREAM_TASK]
    Name stdout_log
    Exec   CREATE STREAM stdout_log WITH (tag='stdout_log') AS SELECT * from TAG:'*-firelens-*' WHERE source = 'stdout';

この設定ファイルを利用する設定を以下のように行います。 標準出力にはstdout_logタグのデータのみの出力を行っています。

application.conf

[SERVICE]
    Flush 5
    Log_Level info
    Parsers_File parser.conf 
    Streams_File stream-processor.conf # ←ファイルの指定

[INPUT]
    Name dummy
    Tag  *-firelens-*
    Dummy {"date":"2022-01-23T03:10:33.317817Z","source":"stdout","log":"time:2022-01-23T03:10:33+00:00\tprotocol:HTTP/1.1\tstatus:200\tsize:1450\treqsize:150\treferer:-\tvhost:10.10.18.102\treqtime:0.176\tcache:-\truntime:-\t"}

[FILTER]
    Match *-firelens-*
    Key_Name log
    Parser parser_ltsv
    Preserve_Key false
    Reserve_Data true

[OUTPUT]
    Name stdout
    Match stdout_log

Dockerfile.fluentbit

FROM public.ecr.aws/aws-observability/aws-for-fluent-bit:2.21.3

COPY application.conf /fluent-bit/etc/application.conf
COPY parser.conf /fluent-bit/etc/parser.conf
COPY stream-processor.conf /fluent-bit/etc/stream-processor.conf # ←追加

出力結果

[0] stdout_log: [1642907433.000000000, {"time"=>"2022-01-23T03:10:33+00:00", "protocol"=>"HTTP/1.1", "status"=>"200", "size"=>"1450", "reqsize"=>"150", "referer"=>"-", "vhost"=>"10.10.18.102", "reqtime"=>"0.176", "cache"=>"-", "runtime"=>"-", "date"=>"2022-01-23T03:10:33.317817Z", "source"=>"stdout"}]

[OUTPUT]のMatchで出力を絞らない場合は、*-firelens-*stdout_log のそれぞれが出力されます。

その他の設定について

ここまでの理解ができれば後はFluent Bitの公式ページを見て設定・確認すればそれほど難しくなく導入していけると思うので割愛させていただきます。

Fluent Bitで導入の過程でハマったこと

ここからは実際にAWS上で運用する時にハマったことを紹介していきます。

S3 プラグインでgzip圧縮時に Content-Encoding: gzip が固定

以下のISSUEでgzip圧縮が動いてないと議論されていますが、自分が確認した限りだとgzip圧縮は動いてます。 github.com

メタデータに Content-Encoding: gzip が付いているとブラウザのダウンロード時など自動解凍されます。 Content-Encoding: gzipをS3上で削除してからダウンロードするとgzip圧縮されていることが確認できます。

この挙動で問題になったのがS3オブジェクトを利用したembulkでの処理でした。

Fluentdで集めたデータと一緒に処理をしようとするとFluent Bitで集めたデータは自動解凍されてしまい、処理の途中で解凍されたデータをさらに解凍しようとしてエラーになるという問題にぶつかりました。

Fluent BitのS3プラグインのソースを確認しましたが、gzip圧縮時はContent-Encoding: gzipが固定で付くようになってしまっています。

この問題の回避のために以下のように前処理を行い解決しました。

aws s3 sync s3://log-bucket1 s3://log-bucket2 --content-type 'application/x-gzip' --metadata-directive REPLACE

S3オブジェクト内のデータを正確に時間区切りで作成できない

以下のISSUEにあるように、/y=%Y/m=%m/d=%d/h=%H/$UUID.gz といった形でログ出力しても2022/02/28 10時のログに9時や11時のログが含まれる可能性があるので、この挙動を考慮した処理が必要です。

S3 output plugin complete upload with interval

nrlogs(New Relic)の推奨キーはlicense_keyっぽい

以下のドキュメントではapi_keyを推奨していると記載がありますが、New Relicの中の人に確認を取ったらlicense_keyを推奨とのことでした。

使い方によるかもしれませんがapi_keyは個人に紐づくのでできればlicense_key使いましょう。

docs.fluentbit.io

おわりに

Fluent Bit導入時、最初からECS上でやろうとしてしまい難しいなと思いましたが、ローカルでdockerを立ち上げて挙動の確認を行いながらやったら意外とさくっと導入できました。 どんな技術も簡単にデバッグできるようにして仮説・検証サイクルをいかに高速で回すかがポイントですね。

Page top