😸

fluentbitのキホン2

2024/07/06に公開

キホンの2は、Parser。

Parserといっても、Parserを定義するわけじゃなく"Parserを使う"が主目的。

https://docs.fluentbit.io/manual/concepts/data-pipeline/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