はじめまして。今年の4月からアルバイトとして勤務している中山です。
実は入社前までフリーソフトウェアとほとんど無縁の生活を送っていたのですが、あるきっかけがあってクリアコードにお世話になることになりました。
さて、フリーソフトウェア開発未経験の私が入社して初めて担当したのがFluentdのメンテナンスです。
入社時のFluentdに対する私の知識は、「ログを集める有名なフリーソフトウェア」くらいしかなかったので、Fluentdの先輩メンテナーの方々にいろいろ教えていただきました。
そこでこの記事では、Fluentdの公式ドキュメントの内容とFluentdの先輩メンテナーの方々に教えていただいた内容をもとに、タイトルの通り「Fluentdは何ができるのか」についてまとめていきます。
「Fluentdって聞いたことはあるけど実際何をするの?」と思っている方や「Fluentdを導入したいけど、こういうことはできるのか?」と思っている方はぜひ参考にしていただければ幸いです。
Fluentdの機能
まずは、私がこの2ヶ月で教えていただいたFluentdの基礎的な機能について簡単にまとめていきます。
Fluentdの公式ドキュメントのOverviewを開くとまずこの画像が出てきます。
これを見ると、Fluentdは、様々なログ(Access logsやApp logsなど)を収集して、フィルタリングやバッファリング、ルーティングをした上でアラート(Alert)や分析(Analysis),アーカイブ(Archive)ができるものだということがわかります。
確かにそれで間違ってはないのですが、厳密にいうとFluentdは「あるデータを必要な場所に安全に転送・収集するために必要な、input(入力)・buffering(バッファリング)・output(出力)を行う機能に加えて、入出力データを加工する(parseやfilteringなどの)機能を持つもの」です。すなわち、収集するデータはログデータに限りません。ただFluentdはテキストデータを扱うことに長けているため、専らログを収集するのに使われているわけです。詳しい図解は、Fluentdの公式ドキュメントのBuffer Pluginsに載っている下の図を参照してください。
また、Fluentdのコア(Core)で全てのデータのinputやあらゆる場所へのoutputを行なっているわけではありません。Fluentdで特定のデータを収集し、特定の場所に出力するためには、そのデータや場所に対応するプラグイン(plugin)を使用します。例えば、Fluentdの公式ドキュメントにあるInput Pluginsのtailは、ファイルからログを収集するために使うプラグインです。(このin_tailプラグインについてもっと知りたい場合は、Fluentd: in_tailプラグインの基本的な使い方をメンテナーが解説も見てみてください。)
なお、Fluentdではinputやoutputの機能以外にも、以下の9つの機能についてこのプラグインと呼ばれる仕組みを使用します。
- Input : Fluentdにデータをインプットする
- Output : Fluentdからデータをアウトプットする
- Filter : Outputするまでのデータの加工を行う(フィルタリングなど)
- Parser : インプットされたデータを指定した形式とみなして解析(パース)する
- Formatter : アウトプットされるデータの形式をカスタマイズする
- Buffer : 入力されたデータをバッファリングする
- Storage : Input,Filter,Outputプラグインの内部状態を外部ストレージに保存する
- Service Discovery : Outputプラグインのサービスディスカバリーを設定する
- Metrics : Input,Filter,Outputプラグインの内部メトリクスを保持する
これにより、ユーザーごとのニーズに合わせてFluentdを使えるようになります。また、プラグインとしてFluentdのコアから分離されているので、特定のプラグインをFluentdのコアや他のプラグインとは独立してメンテナンスできます。
またFluentdでは、よく使われる機能に対するプラグインは標準機能として実装されています。Fluentdの公式ドキュメントには、標準実装されているプラグインの説明や使い方が細かく書いてあります。さらに有志が作成したプラグインは、公式ブログのPluginsにまとめられており誰でも使用することができます。
Fluentdの強み
私がFluentdのことを学んで感じたFluentdの強みは、「複数のインプットに対して異なる処理を、簡単な設定で同時に行えること」です。今回は以下のインプットの例で説明していきます。
<source>
@type sample
sample {"message":"hoge"}
tag test.hoge
</source>
<source>
@type sample
sample {"message":"hogehoge"}
tag test.hoge
</source>
<source>
@type sample
sample {"message":"fuga"}
tag test.fuga
</source>
説明に入る前にこの設定の意味を簡単に解説します。
まず一つ一つのインプットは<source>から</source>の間に書きます。すなわち、今回は3種類のデータをFluentdにインプットしています。次にそれぞれのインプットにある@type sampleは、inputプラグインの種類を示します。なので今回は3つのインプットは全てin_sampleプラグインです。ちなみにin_sampleプラグインは、単にサンプルデータを1秒おきにFluentdにインプットするだけのプラグインです。そして次のsample {"message":"..."}はそれぞれのin_sampleプラグインが流し込むサンプルデータです。つまり、今回の例では、「1秒おきに{"message":"hoge"}をインプットする」、「1秒おきに{"message":"hogehoge"}をインプットする」、「1秒おきに{"message":"fuga"}をインプットする」の3種類のインプットを同時に行うことになります。そして、tagは入力するデータの分類です。今回は{"message":"hoge"}と{"message":"hogehoge"}のサンプルデータにはtest.hogeという同じtagを、{"message":"fuga"}のサンプルデータにはtest.fugaという異なるtagをつけています。
では、まずこの3種類のデータを全て標準出力にアウトプットしてみます。アウトプットの設定は以下のとおりです。
<match test.**>
@type stdout
</match>
ちなみに、一つ一つのアウトプットは<match>から</match>の間に書きます。また、標準出力のプラグインはstdoutになります。
この設定でFluentdを実行すると、次のような結果になります。
2024-06-06 13:25:08.056381000 +0900 test.hoge: {"message":"hogehoge"}
2024-06-06 13:25:08.058900000 +0900 test.fuga: {"message":"fuga"}
2024-06-06 13:25:08.059012000 +0900 test.hoge: {"message":"hoge"}
2024-06-06 13:25:09.095427000 +0900 test.hoge: {"message":"hogehoge"}
2024-06-06 13:25:09.095759000 +0900 test.fuga: {"message":"fuga"}
2024-06-06 13:25:09.099108000 +0900 test.hoge: {"message":"hoge"}
2024-06-06 13:25:10.027508000 +0900 test.hoge: {"message":"hogehoge"}
2024-06-06 13:25:10.027766000 +0900 test.fuga: {"message":"fuga"}
2024-06-06 13:25:10.036786000 +0900 test.hoge: {"message":"hoge"}
...
確かに1秒おきに{"message":"hogehoge"}、{"message":"fuga"}、{"message":"hoge"}の3種類のデータがアウトプットされてますね!このように、Fluentdでは簡単な設定で複数のインプットを1つの出力先に同時にアウトプットすることができます。
今度はtest.hogeというtagをつけたサンプルデータである{"message":"hoge"}と{"message":"hogehoge"}のみを標準出力にアウトプットしてみます。これは先ほどのアウトプットの設定を下のように1行変えるだけでできます。
<match test.hoge>
@type stdout
</match>
<match test.**>を<match test.hoge>に変えただけです。これで出力は次のようになります。
2024-06-06 13:34:54 +0900 [warn]: #0 no patterns matched tag="test.fuga"
2024-06-06 13:34:54.029237000 +0900 test.hoge: {"message":"hogehoge"}
2024-06-06 13:34:54.031941000 +0900 test.hoge: {"message":"hoge"}
2024-06-06 13:34:55.066796000 +0900 test.hoge: {"message":"hoge"}
2024-06-06 13:34:55.078105000 +0900 test.hoge: {"message":"hogehoge"}
2024-06-06 13:34:55 +0900 [warn]: #0 no patterns matched tag="test.fuga"
2024-06-06 13:34:56.003585000 +0900 test.hoge: {"message":"hoge"}
2024-06-06 13:34:56.017083000 +0900 test.hoge: {"message":"hogehoge"}
...
今度は1秒おきに{"message":"hoge"}と{"message":"hogehoge"}の2種類のサンプルデータが表示されました!(ちなみに、[warn]から始まる警告ログも出ていますが、これは「tagがtest.fugaであるデータに対するアウトプットが存在しない」ことを表しています。すなわち「{"message":"fuga"}というサンプルデータは出力先がない」という意味の警告ログです。)
さらに今度は{"message":"hoge"}と{"message":"hogehoge"}の2種類のサンプルデータを同じファイルに、{"message":"fuga"}を別のファイルに1分おきにファイル出力してみます。アウトプットの設定は次のとおりです。
<match test.**>
@type file
path /tmp/sample/${tag}
append true
<buffer time,tag>
@type file
path /tmp/sample/buffer/tag
timekey 60
timekey_wait 3
</buffer>
</match>
今回はファイル出力なので、out_fileプラグインを使います。この設定でFluentdを実行し1分ほど待つとout_fileプラグインで指定したパスに次の2つのファイルができています。
test.hoge.202406071443.log
test.fuga.202406071443.log
そしてそれぞれの末尾をtailコマンドで確認してみると、以下のように、test.hogeのタグがついた{"message":"hoge"}と{"message":"hogehoge"}の2種類のサンプルデータはtest.hoge.202406071443.logに出力され、{"message":"fuga"}はtest.fuga.202406071443.logに出力されたことがわかります!
$ tail test.hoge.202406071443.log
2024-06-07T14:43:55+09:00 test.hoge {"message":"hoge"}
2024-06-07T14:43:55+09:00 test.hoge {"message":"hogehoge"}
2024-06-07T14:43:56+09:00 test.hoge {"message":"hogehoge"}
2024-06-07T14:43:56+09:00 test.hoge {"message":"hoge"}
2024-06-07T14:43:57+09:00 test.hoge {"message":"hogehoge"}
2024-06-07T14:43:57+09:00 test.hoge {"message":"hoge"}
2024-06-07T14:43:58+09:00 test.hoge {"message":"hoge"}
2024-06-07T14:43:58+09:00 test.hoge {"message":"hogehoge"}
2024-06-07T14:43:59+09:00 test.hoge {"message":"hoge"}
2024-06-07T14:43:59+09:00 test.hoge {"message":"hogehoge"}
$ tail test.fuga.202406071443.log
2024-06-07T14:43:50+09:00 test.fuga {"message":"fuga"}
2024-06-07T14:43:51+09:00 test.fuga {"message":"fuga"}
2024-06-07T14:43:52+09:00 test.fuga {"message":"fuga"}
2024-06-07T14:43:53+09:00 test.fuga {"message":"fuga"}
2024-06-07T14:43:54+09:00 test.fuga {"message":"fuga"}
2024-06-07T14:43:55+09:00 test.fuga {"message":"fuga"}
2024-06-07T14:43:56+09:00 test.fuga {"message":"fuga"}
2024-06-07T14:43:57+09:00 test.fuga {"message":"fuga"}
2024-06-07T14:43:58+09:00 test.fuga {"message":"fuga"}
2024-06-07T14:43:59+09:00 test.fuga {"message":"fuga"}
このように複数のインプットに対しての異なる処理を同時に行え、かつ設定がとても簡単であることがFluentdの長所です。ちなみに、今回の例ではin_sampleプラグインを用いて説明しましたが、実際に複数のログファイルからデータを収集する場合はin_tailプラグインを用いてtagの設定を今回と同様に行えば良いです。
また、今回はtagによってデータのアウトプットを分類することに焦点を当てましたが、もちろんtagによってフィルタリングやパースなどを行うこともできます。
まとめ
Fluentdは自分が収集したいデータやそのデータを転送したい場所に対応するプラグインを作成・使用することで、思い思いのデータを思い思いの場所に転送・収集できるフリーソフトウェアです。そのため、流動的なデータの収集を行いたい場合はFluentdを検討してみるとよいと思います。なおFluentdの動かし方については、Fluentdを動かしてみよう!に細かくまとめてあるので、触ってみたいという方はぜひ見てください。
なお、Fluentdコミュニティーでは、日本語用のQ&Aも用意しています。 何か疑問や困ったことがあれば、こちらを利用してみてください。
また、クリアコードはFluentdのサポートサービスを行っています。 Fluentdをつかったシステムの設計支援をはじめ、Fluent Packageへのアップデート支援や影響のあるバグ・脆弱性のレポートなどのサポートをします。詳しくはFluentdのサポートサービスをご覧いただき、お問い合わせフォームよりお気軽にお問い合わせください。

