TODO:
- 必要なら図を足す
- 他に書いた方が良いPros/Consのリクエストがあったら追記
内部のイベントストリームの扱い
- Pros: Inputがスケーラブルに実装しやすく,データストリームを正常時/エラー時で切り替えやすい
- Cons: エラーハンドリングがブロッキングモデルよりも複雑になりやすい
以下長々と理由書きます.
Fluentdはイベントストリームを効率良く,またロバストに扱うことを目的に設計されています.そのため,独自の転送プロトコル(forwardプラグイン)を実装していますし,内部のイベントのハンドリングもそれに沿うようになっています.ただ,それによって相性の悪い操作とかもあります.
Fluentdはバッファ機能を提供しており,これによって転送の効率化とエラー時のデータロスを防ぐ設計になっています.が,あまりにも書き込み先が遅いなどの問題があると,バッファの制限を超えてしまうことがあります.
この時,FluentdはInputプラグインにエラーを返します.これによりInputプラグインはさらにエラーを外部にも伝搬させることが出来,forwardプラグインなら再送や,エラーが続くようならセカンダリに流すなどが出来るようになっています.これはデータストリームを流し続けるため,またInputプラグインがブロックせずに大量の入力を扱えるようにするため,こうなっています.
このモデルは,データがストリームではない過去ログの一括転送などと少し相性が悪いです.例えばtailを使って過去ログをElasticsearchに一気に流す時によく問題に当たるのですが,Elasticsearchは1台だと受け付けられるデータ量はそれほど多くなく(ここはチューニングに左右されます),大量の過去ログを一気に送ろうとするとOutputプラグインのフラッシュ処理が追いつかなくなり,結果バッファが溜まりリミットを越え,Input側にエラーが返り続けることになります(tailの場合はひたすらリトライ).
一方,Elasticsearchと一緒によく使われるLogstashはFluentdとは違うアプローチをとっています.内部には長さ固定のブロッキングキュー(Outputのバッファではない)がInputとOutputの間に存在しており,Inputはそのキューがあくまで処理がブロックします.
そのため,上記のような過去ログの転送などでは,キューが空くまで処理は行われないので,Output側が詰まってもエラーなどにはなりません.その変わりブロックしているため,外部からデータが送られてくるようなケースにおいては,このブロックがどんどん伝搬していくので,大量・多数の入力を捌くのが難しくなります.
この辺に関しては最近のイベントでも言及されてました(ここで言われているログが抜ける云々はInput側の問題ではなくて,ESが高負荷になりすぎてES側でrejectされてるんだと思います.この記事参照).
fluentdは日本では流行ってるけど、キューがあふれるとキューに入りきらなかったログが抜けることがある、logstashなら大丈夫なので、大量の過去ログ投入するといはlogstashがおすすめ。 #jdt54 #JavaDayTokyo
— mogami (@smogami) April 8, 2015
これらのモデルの差は
- Fluentdはロバストなログ転送をするために
- Logstashはログの管理をするために(昔はそれほどES/Kibanaと密結合していなかったはず)
という出生の違いによって生まれているのかなぁと思っています.
実のところFluentdで過去ログを転送している人はいて大きな問題にはなってないのですが,どうしてもキャパシティが足りないところに無理にやろうとすると,上記のElasticsearchのような問題が起きたりするので気をつけましょうという感じです.(一括転送専用にバッファをチューニングするのは面倒だったりしますし…).
実際1レコードずつSQL発行してinsertを行うmysqlプラグインでも,最近同じ問題が起きてました.これはバルクロード版で解決しました.
今だとEmbulkがあるので,大量データの一括転送はそちらを使ってくださいという流れになりますが,ブロックする挙動があっても損はないとは思うので,うまくバッファのAPIが整備出来れば,back-pressureっぽい機能は入れようかとは思ったりはしてます(fluent/fluentd #569).
今回はFluentdとLogstashしか言及してませんが,他にもいくつかログ収集のプロダクトは存在しているので,上記のイベントのライフサイクルや,転送プロトコルでどういうセマンティクスがあるのかなどは,使う場合には一通りチェックした方が良いと思います.