Go ahead!

Memoization for Everything

Fluentdとログ収集のパターン

| Comments

「ログを集めて保存する」と言うのは簡単だけど,ログ収集の構成にはいくつか方法があり,勉強会などでちょくちょく聞かれるので,いくつかのパターンについて書く.
「俺はもうバリバリログ収集やってるぜ!」という人は多分すでに知っていることが書かれているので,タブを閉じて良い.

ここではログコレクタにFluentdを想定しているが,他のログ収集プロダクトにも適用出来るはず. ただ,Fluentdはタグベースのルーティングを持ち,単体でもキューのように動作させることが可能で,既存のものより複雑な問題を解決しようとしているので,少し工夫が必要かもしれない.
Fluentdそのものについては公式ドキュメントや,Fluentdとはどのようなソフトウェアなのかを参考に.

クライアントから直接保存する

"Only client pattern"

いきなりFluentdを使わないパターン.JavaScript SDKを提供している解析サービスやモバイル端末などでよく使われている.ログがユーザサイドで発生する場合にはログコレクタを仕込むことは出来ないので,基本このアプローチになる.
これらはクライアントの出来によって精度が左右されるが,モバイルであれば繋がらなかったら一定量バッファリングをし,ネットワークが繋がった段階で非同期に転送などがよくある実装.

ログコレクタを用意出来るサーバサイドでこのアプローチを取っているシステムは最近だとあまり聞かない.もしやるならエラーハンドリングとして

  • バッファリング
  • リトライ
  • ロードバランシング

などの問題を解決する必要がある.Fluentdであればこれらの機能が最初からついているので,今から構築するシステムでやる必要はほとんど無いと思われる.

末端ノードのログコレクタから直接保存する

"Only leaf node pattern"

アプリケーションやノードからログを集め,ログコレクタから直接保存する.「クライアントから直接保存する」の例で,ローカルにログコレクタを置き,そこ経由でデータを投げつけるモデル.
ネットワーク周りのややこしい問題をログコレクタに任せられ,Fluentdであればプラグインで容易にストリームを操作できるのがメリット.また,ApacheやNginxから直接外部にデータを投げるのは難しいので,Fluentdでファイルにはき出されたログを収集するときにはこの構成となる.

この構成だと,収集対象のサーバが多くなるだけアクセスが増えて保存先への負荷があがるので

  • データのストリームがそれほど大きくない
  • 保存先が書き込みに対して耐性がある (クラウドサービスなど)

などの場合に有効になる.次で書く集約ノードが必要ないので,その分管理は楽になる.
Fluentdは転送役と集約役に違いはないので,この段階でフィルター系プラグインを使って,データを加工することもある.

Treasure Dataの場合には,Librato Metricsに投げる所などは集約ノードを経由せず,各Fluentd(Treasure Agent)から直接投げている.この場合は各ノードのメトリックスが取りたいので,集約ノードでデータストリームをまとめる必要もない.

これとは違いMongoDBの場合だと,細かいのをちまちま送るより大きめのバッチでガンと書き込んだ方が効率が良いので,このモデルよりも集約ノードを置いた方が良い.

集約ノードのログコレクタを経由して保存する

"Using aggregation node pattern"

ログコレクタでの多段構成モデル.集約ノードを置く利点は,データストリームをまとめてデータに対する処理をしやすくする点が大きい.また,各ノードから集めることでストリームが太くなり,マイクロバッチの効率も良くなる.

Fluentdでもアグリゲーションをしたり,Norikraと連携するプラグインがあったりするので,その手の処理はこの集約ノードでやるのがベター.

大抵のログコレクタと同じく,Fluentdのデフォルトの転送プロトコルはPush型になっていて,多段構成時には流れるように保存先にデータが集まるようになっている.
多段構成時の注意点は,ある場所のノードが落ちた時にストリームが途切れてしまう所.Fluentdの場合は障害に対応するために,転送先を複数指定可能で,その中で負荷分散とノードが落ちた時の処理を指定出来るようになっていて,よほど一気にノードが落ちない限りストリームが途切れることはない.もし落ちてもバッファリングが行われるので,ノードを再度立ち上げればデータはまた流れ出す.

PaaS上でのアプリケーション

アプリケーションをホストするような環境だとローカルにFluentdを置けなかったりするので(HerokuだとFluentdは起動できるが,ファイルバッファが使えなかったりする.ユースケース次第),その場合はいきなり集約ノードやキューにデータを投げることになる.

キューを置く

"Using queue pattern"

データストリームの中でキューを間に挟むことの主な理由は,ログのreliabilityの向上と,処理の間にワンクッション置くこと.

そもそもキューはPush型ではなくPull型であり,Producer・Queue・Consumerと登場人物も増えるので,キューを置くことでログ収集そのものが効率化されることはあまりない.異なる粒度のシステム間で連携をする時に,間にキューを置くことが多い.

実際Fluentdとキューを連携させている例はいくつかあり,たとえば複雑なシステムを構築しているVikiの例ではKestrelでFluentdからのデータストリームを分岐させているし,AWSは本家がfluent-plugin-kinesisを書いているので,いずれこの事例も増えるはず.

KafkaやKinesisなど信頼性のあるキューを間に置けば,ログをそれなりの期間保持しつつ,Consumerを複数用意することでストリームを分岐できる.
Push型での分岐とは違い,キューからログを取る側の都合でデータを処理出来るので,各データストリームで処理粒度が違う時に有効. ただ,キューそのものに信頼性があってもProducer/Consumerが駄目だと効率が良くならずログが欠損/重複するので,自分たちのシステム要件に合わせてちゃんと構築する必要がある.

Pull型のログ収集

キューを置かずにPull型でログを転送する方法もある.Kafkaほどの信頼性を期待するのは難しいが,Pull型の利点を享受できる.まだ完成していないが,Fluentdだとモリス=タゴという人が,pullforwardというのを開発中らしい.

Fluentdを使わない方が良いパターン

プロダクションで使われているパターンをいくつか書いた.大抵のシステムは上のどれかの構成に当てはまる.FluentdはWebや広告のみならずIoTやPOS含め色々なところで使われていて,データのアーカイブ,簡単なストリーム処理,ノードやアプリケーションのモニタリング,などなど様々なユースケースの基盤になれる柔軟性がある.

また,LINE社で2年間問題なく動いているし,最近勉強会とかで話を聞くと「安定していて自分の仕事がない」と言われる位,よほど変なことしない限り安定性もある.ある会社で100億events/dayをFluentdクラスタで転送した実績もあるので,パフォーマンスもそれなりにある(フィルタ的な処理を重ねまくると,もちろん遅くなる).

が,Fluentdを使わない方が良いケースもある.以下主に二つ.

Exactly Onceが必要なケース

一切ログの欠損も重複も許せない,しかも確実に書き込む必要がある,というケース.
ストリーム処理でExactly Onceを保証するのはかなり難しく,俺が知っている限りOSSでは存在していない.商用でもほとんど知らない.

Exactly Onceを実現しようとすると,ログ発生時点から書き込むまでを完全に同期的にやるか,様々な箇所でトランザクションが必要になり,スケールアウトが難しくコストもものすごく掛かる.そして保存先にもそれなりの機能が求められる.

かつてOSSでマスターノードを使い到達保証を目指したプロダクトがあったが,結局パフォーマンスがボトルネックになり,そこまでしてもログの欠損が防げなかったため,新しいバージョンでは諦めることになった.

Fluentdのモデル

FluentdはデフォルトAt Most Onceであり,ドキュメントにもモデルの説明と対策が書いてある
これはログが欠損/重複しまくるということではなく,At Most OnceもAt Least Onceも通常は問題は起きない.トポロジーに問題がある場合に,システム的に欠損/重複が起きうるという話.そして大抵のユーザはExactly Onceでなくても上手く行く.

Fluentdでも欠損/重複を防ぐための対策はいくつかあり

  • ログにユニークIDを足して,HadoopなどのETLで重複を削除する
  • 直近の生ログは保持しておき,保存先とのレコード数を比較する.おかしい所は再送
  • データストリームを複数作って冗長性を持たせる

などなど.システム的にExactly Onceではなくても,それに低コストで近づけることは可能.

追記(2014/11/14)

v0.12からはAt Least Onceもサポートする.現在リリースされているv0.12.pre2から使えるが,v0.12の正式リリース後,ドキュメントにもちゃんと書く.

データストリームが一本で密結合したサービスがある

たとえばAWSのCloudWatch Logsがそう.Agentのセットアップが必要だったりして楽かと言われるとまだ微妙だが,今後最初からインストールされる可能性もある.
そのような状況で,Agentから取れるログをCloudWatch Logsで直接見るので十分であれば,Fluentdを入れるメリットは少ない(CloudWatch Logs用のプラグインがあるので,Fluentdから利用することは可能).

Fluentdの利点は簡単にロバストで柔軟性のあるログ収集基盤が作れて,一度構築してしまえば,環境非依存でそのログ収集基盤を再利用出来るところなので,そのような必要性がないのであれば,環境と密結合したサービスを使う方が運用コストそのものは直近は削減できるはず.

まとめ

Fluentdが向いているケースと向いてないケース,そのPros/Consみたいなのを書いた.細かく書けばもっと色々と書けるのだけど,記事でそんなに長々と書いても疲れるだけなので大まかに. 何か質問があればTwitterで@repeatedlyにmentionするなり,この記事にコメントするなりしてください :)

もうすぐFluentd + Elasticsearch + Kibanaという有名な構成のムック本が出るようなので,この本とか読むと,解析のノウハウ含め色々とここには書いていない情報が手に入るのではないかと思います.

Comments