電通総研 テックブログ

電通総研が運営する技術ブログ

Fluent Bitを利用したログルーティング - 入門編

こんにちは、電通国際情報サービス デジタルイノベーション1部の加世です。

今回は「FluentBitを利用したログルーティング」を進める際に、「FluentBitについて理解する」ことを目的とした記事となっております。 具体的なFluentBitの使い所や設計を考える前段階として、本記事を参考にしていただければと思います。

Fluent Bitについて

FluentBitは、データ(メトリクス、ログ、それ以外の生情報など)をInput情報として収集し、加工処理したうえで任意の転送先に転送します。 クラウドサービスプロバイダを利用するうえでは、既に「システム」から「サービス」に転送する仕組みを用意していることが多いため、ログルーティングについて考慮しなくても問題ないケースが多いです。

そのため、FluentBitの利用は「複数のシステム(サーバ/コンテナ)」から「様々なデータ」を「複数の宛先」に「加工」して「集約」する場合が有効であると考えています。

Fluent Bitの特徴

FluentBitは、大きく6種類「Input」->「Parse」->「Filter」->「Buffer」->「Router」->「Output」のPluginで構成されています。 また、「Output」プラグインは、主要なクラウドサービスプロバイダ(AWS, Azure, GCP, Datadog...etc)に対応しています。 そのため、データ転送先はアーキテクチャ・データ分析方法を踏まえて、プラグインの対応範囲はありますがさまざまなサービスを選択できます。

FluentBitは、次の点で優れていると考えています。

検証環境と処理フローについて

検証環境は、「AWS Fargate」で「テストアプリケーションコンテナ (Firelensログドライバ付き)」「FluentBitコンテナ」を稼働しています。 また、FluentBitは、ログルーティング先として複数の転送先「CloudWatch」「S3」を指定します。

※FluentBitコンテナを使用できる環境であれば、FluentBitの基礎的な機能は検証できます。 ※検証環境のホストOSカーネルパラメータ設定によっては、挙動が変わる場合はあります。 ※FireLensログドライバは、Fargate標準出力(stdout/stderr)をFluentBitにログルーティングするためだけに使用します。

  • AWS FargateでFirelensログドライバを利用して、標準出力をFluentBitに送信する方式について Firelensログドライバは、仕様を細かく確認していませんが「td-agent」の仕組みでFluentBitのInputプラグイン「Forward」を利用して標準出力を転送していると思われます。 そのため、タグは「<コンテナ名>-firelens」となるため、必要に応じて「rewrite_tags」などでタグを変更することで、Outputプラグインでのタグ条件指定がしやすくなります。

  • 処理フロー

    1. アプリケーション(stdout/stderr) -> Firelensログドライバ -> (forward:24224) -> サイドカーコンテナ(FluentBit) -> CloudWatch/S3
    2. アプリケーション(ローカルファイル) -> (tail:Volume共有マウント) -> サイドカーコンテナ(FluentBit) -> CloudWatch/S3

  • AWS専用のFluentBitコンテナ

    https://docs.fluentbit.io/manual/installation/getting-started-with-fluent-bit

    AWSに最適化されたFluentBitコンテナが提供されています。
    マニュアルにしたがって、サイドカーコンテナとして起動します。

    • aws-for-fluent-bit
      AWS Fargateの場合は、「Firelens」ログドライバと連携して「aws-for-fluent-bit」が一部設定を自動生成します。
      また、オプション設定をすることで追加のFluentBit設定ファイルを読み込むことも可能です。

    • IAMロール設定について 「aws-for-fluent-bit」コンテナは出力先となる「CloudWatch」「S3」などに対して操作権限が必要になります。 そのため、Fargateコンテナを前提とした場合は「ECSタスクロール」に設定しておく必要があります。

      なお、AWS以外から「CloudWatch」「S3」に転送する場合は、FluentBit向けのIAMロールを用意して「cloudwatch」「S3」プラグインでIAMロールを指定して使用できます。

Fluent Bit設計前の準備

データパイプラインの理解

FluentBitを設計するうえで、データパイプライン「Input」->「Parse」->「Filter」->「Buffer」->「Router」->「Output」を理解する必要があります。

  • Input

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

    「Forward(TCPリスナポート経由のメッセージ受信)」「tail(ローカルファイスシステム上のログファイルなどのtail)」などで「生データ」の入力を受け付けます。 他にもメトリクスデータを取得するプラグインも多数用意されています。

  • Parse

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

    Inputプラグインで受け取ったデータを「Parser」で処理することにより、「データ構造化」や「マルチライン処理」を行います。
    「Input」「Filter」プラグインで使用できます。

    • データ構造化 「生データ(log)」を「時間(time)」「レベル(level)」「メッセージ(message)」などのフィールドに分割します。

    • マルチライン処理
      複数行データ(たとえば、Javaスタックトレースの「at」など)を1つのデータとして扱います。
      ※なお、「\n」「\t」がエスケープされずにファイル出力されますが、回避方法は執筆時点で未調査です。

    • 処理前

      {
          "hostname": "ip-172-24-136-132.ap-northeast-1.compute.internal",
          "level": "EROROR",
          "message": "org.apache.catalina.startup.Catalina.start Server startup in 135176 ms",
          "thread": "main",
          "time": "02-Mar-2022 15:33:32.000"
      }
      {
          "hostname": "ip-172-24-136-132.ap-northeast-1.compute.internal",
          "level": "EROROR",
          "message": "\tat com.myproject.module.MyProject.badMethod(MyProject.java:22)",
          "thread": "main",
          "time": "02-Mar-2022 15:33:32.000"
      }
      {
          "hostname": "ip-172-24-136-132.ap-northeast-1.compute.internal",
          "level": "EROROR",
          "message": "\tat com.myproject.module.MyProject.oneMoreMethod(MyProject.java:18)",
          "thread": "main",
          "time": "02-Mar-2022 15:33:32.000"
      }
      
    • 処理後

      {
          "hostname": "ip-172-24-136-132.ap-northeast-1.compute.internal",
          "level": "EROROR",
          "message": "org.apache.catalina.startup.Catalina.start Server startup in 135176 ms\n\tat com.myproject.module.MyProject.badMethod(MyProject.java:22)\n\tat com.myproject.module.MyProject.oneMoreMethod(MyProject.java:18)",
          "thread": "main",
          "time": "02-Mar-2022 15:33:32.000"
      }
      
  • Filter

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

    入力された「生データ」を「Filter」で加工(追加・変更・整形・削除 etc)します。
    「Parser」「Multiline(Parser)」「Record Modifier」「Rewrite Tag」「Lua」「Kubernetes」などがあります。
    ※「AWS Metadata」はEC2向けであり、Fargate向けのものではありません。(Fargate自体は、メタデータを参照するURLは提供されています)

  • Buffer

    https://docs.fluentbit.io/manual/concepts/buffering

    生データを保管する領域として、「メモリ」または「ファイルシステム(永続領域)」を選択できます。
    「Service」「Input」「Output」プラグインなどで定義します。
    なお、「処理間隔(メモリリフレッシュ、データ出力などのタイミング)」「各種バッファサイズ(ファイル初期読み込みサイズなど)」はチューニング要素となります。

    • 補足
      通常は、「メモリ」のみを選択する方針で問題ありません。
      生データの過去分までの情報(オフセットなど)を持っておきたい場合は、「ファイルシステム」を選択します。

      Fargateなどを前提としたエフェメラルストレージを利用する場合は、コンテナ内のメトリクス・ログデータなどは更新時に初期化されるため「メモリ」を採用する点は問題ありません。
      一方で、データ分析などで永続領域にあるデータを漏れなく・重複なく読み込む必要がある場合は、EFSと連携するなどの永続領域のデータを参照する構成が必要になります。

  • Router

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

    出力対象となるデータは、Input時点でデータとひもづけられたTag(または、Filterで書き換えられたTag)をもとに「識別」できるようにします。
    そのうえで、正しく条件を指定することで適切な出力先に出力します。
    ルーティング条件は、Outputプラグインの「Match(ワイルドカード指定のみ)」と「Match_regex」により一致条件を設定します。

  • Output

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

    クラウドサービスプロバイダ向けのストレージサービス」「ローカルログファイル」「FluentBitコンテナの標準出力」などに対して「データ」を出力します。

データの構造化について

Inputデータは、「データフィールド名の追加・加工」「データ値の追加・加工」が可能です。 InputデータがJSON形式であり、すでに構造化されているデータの場合は、そのまま「フィールド名(キー名)」「値」を利用することが多いと考えられます。 ※ただし、どのようなデータでも「Filter」プラグインにより追加の加工処理は可能です。

  • フィールドの追加・加工
    Inputデータは、「Filter」プラグインでデータフィールド名を追加・加工できます。

    Firelensログドライバ経由でInputした標準出力・標準エラー出力は、「container_id」「container_name」「log」「source」フィールドをInputします。
    これは既に構造化されたInputデータですが、例えば「log」「source」に対して「キ名ー(フィールド名)」を統一する目的で「message」「file_path」に変換できます。

    また、追加の加工処理としてコンテナ内の環境変数「$HOSTNAME」の情報をもとに、「hostname」フィールドを追加できます。

    • Inputデータ

      {
          "container_id": "3885a532543547978adf8d6e9bdf729b-2449111020",
          "container_name": "test-container",
          "log": "test message.",
          "source": "stdout"
      }
      
      {
          "container_id": "625a41f4c1a241f49e7308b9g62911cb-1475094415",
          "container_name": "test-container",
          "log": "java.io.IOException: listener timeout after waiting for [60000] ms",
          "source": "stderr"
      }
      
    • 加工済みデータ

      {
          "container_id": "3885a532543547978adf8d6e9bdf729b-2449111020",
          "container_name": "test-container",
          "file_path": "stdout",
          "hostname": "ip-172-24-100-100.ap-northeast-1.compute.internal",
          "message": "test message.",
      }
      
      {
          "container_id": "625a41f4c1a241f49e7308b9g62911cb-1475094415",
          "container_name": "test-container",
          "file_path": "stderr"
          "hostname": "ip-172-24-100-100.ap-northeast-1.compute.internal",
          "message": "java.io.IOException: listener timeout after waiting for [60000] ms",
      }
      
  • データ値の追加・加工
    Inputデータは、「Filter」プラグインでデータ値を追加・加工できます。

    Docker標準出力などであれば、すでに「Parser」プラグインとして用意されているパーサ「docker」を利用できます。 事前に用意されているParserで構造化が難しい場合は、独自にParserを定義できます。

    たとえば、以下に示す独自の「Parser」プラグイン定義は、Tomcatの標準出力ログを加工します。 これは、InputデータのログをRegexに指定した正規表現でパースして、「time」「level」「thread」「message」などのフィールド名にマッピングします。

    • 「Parser」プラグイン定義

        [PARSER]
            Name        tomcat
            Format      regex
            Regex       /^(?<time>\d{1,2}-\D{3}-\d{4} \d{1,2}:\d{1,2}:\d{1,2}.\d{3}) (?<level>[^\s]+) \[(?<thread>[^\]]*)\] (?<message>.*)/
            Time_Key    time
            Time_Format %d-%b-%Y %H:%M:%S.%L
            Time_Offset +0900
            Time_Keep   On
            # Command       | Decoder      | Field   | Optional Action   |
            # ==============|==============|=========|===================|
            Decode_Field_As   escaped        message
      
    • インプットデータ

        04-Mar-2022 12:34:39.072 ERROR [main] org.apache.catalina.startup.Catalina.start Server startup in 123094 ms
      
    • 加工済みデータ

        {
            "file_path": "/usr/local/tomcat/logs/catalina.test.log",
            "hostname": "ip-172-24-136-164.ap-northeast-1.compute.internal",
            "level": "EROROR",
            "message": "org.apache.catalina.startup.Catalina.start Server startup in 135176 ms\n\tat com.myproject.module.MyProject.badMethod(MyProject.java:22)\n\tat com.myproject.module.MyProject.oneMoreMethod(MyProject.java:18)\n\tat com.myproject.module.MyProject.anotherMethod(MyProject.java:14)\n\tat com.myproject.module.MyProject.someMethod(MyProject.java:10)\n\tat com.myproject.module.MyProject.main(MyProject.java:6)",
            "thread": "main",
            "time": "04-Mar-2022 12:42:28.000"
        }
      

まとめ

本記事では、「FluentBitについて理解する」ことを目的として記事を作成しました。 次回の記事では、「FluentBitを実際に動かしたうえで確認した設計・設定ポイント」をお話できればと考えています。

執筆:@kase.teruyoshi、レビュー:@sato.taichiShodoで執筆されました