以前に晒したFluentdからWebHDFSに対してログを流し込むフローの部分を、
少しキツ目の環境にブっこんで運用したら色々問題点がでてきたので記しておきます。
どちらかというとFluentdというよりはHDFSよりの話になります。
HDFSファイルのCREATEエラー
複数のFluentd CollectorからHDFSの1ファイルへ共通して書き込むというスタイルをとってみました。こんな感じに1分毎のHDFSファイルとして。
1 2 3 4 5 |
Collector-01 ─┐ Collector-02 ─┤ Collector-03 ─┼─> HDFS %Y%m%d-%H%M.log Collector-04 ─┤ ... |
これをやると、必ずファイルの新規作成時にそれなりの頻度でエラーが発生します。
正直言うと、こうなるだろうことは予想していましたが、簡素な負荷試験では見受けられませんでした。
なぜエラーが起きるかというと、WebHDFSの書き込み仕様によるところが強いのですが、ファイルの新規作成をCREATE、追記がAPPENDとなっていて、その辺はこちらに詳しく書いてあります。
どちらにせよ、NameNodeにHTTPでリクエストを投げて、返す刀でLocation先となるDataNodeにHTTPでデータを送信する流れになっています。
で、fluent-plugin-webhdfs はAPPENDにも対応していて、簡単に言うと APPEND してみて失敗したら CREATE するようになっています。これはSQLで言うところのUPDATE -> INSERT の技法で、参照クエリを省略できる方法です。
それ自体は問題ないのですが(若干の問題については次回)、2HTTPで1リクエストである点と、HDFSにはトランザクションとか排他制御の概念は無いことから、複数Collectorから同時にCREATEをするタイミングでエラーとなります。
1 2 3 |
Collector-01 : ☓ APPEND -> ○ CREATE Collector-02 : ☓ APPEND -> ☓ CREATE =retry=> ○ APPEND Collector-03 : ☓ APPEND -> ☓ CREATE =retry=> ○ APPEND |
このCREATEエラーはこんなのがDataNodeから返されます。
1 |
... because this file is already being created by ... |
対策としては
ということで、素直に collector : hdfs file = 1 : 1 にするのが良いと判断し、
前に、webhdfsプラグインの path に %{tag} を使えるようにしたと書きましたが、
%{hostname} も使えるようにして対応しました。
書き込み頻度やpath条件を調整してマシにはできるかもしれませんが、この部分は 100% の成功を目指す所なので、エラーの可能性を残すことはやりません。WebHDFSの仕様もそうですが、HDFSの構造上、この辺を高望みしていてもしょうがないと思われるので、並列CREATEやAPPENDは大人しくやめるのが吉でしょう。
HDFSへの高速APPENDエラー
CREATEとAPPENDの考察
CREATEと同じくAPPENDの信頼度も高いはずもなく、1HDFSファイルに大量にAPPENDをしているとこのようなエラーが出ます。
1 2 3 |
Failed to add a datanode. User may turn off this feature by setting dfs.client.block.write.replace-datanode-on-failure.policy in configuration, where the current policy is DEFAULT. とか Failed to close file /hdfs/to/path.log. Lease recovery is in progress. Try again later. |
このエラーは、1HDFSファイルに対して単発Collectorであろうと複数Collectorであろうと出ます。
なので 1Collector : 1HDFSファイル で1分毎pathのファイルの場合で話をしますが、
例えば、1秒毎にHDFSにFlushすると、1CREATE+59APPEND になり、
10秒毎だと1CREATE+6APPEND になるわけです。
APPENDが複数になるパターンは、エラー頻度を緩和できても絶対起きないとは言い切れません。
しかし、1分ファイルに対して60秒毎にFlushした場合、1CREATE+1APPENDになるため (1CREATEのみになることはまずないでしょう)、APPENDの衝突という線は無くなります。とはいえ、CREATEしたものの何かに時間がかかってAPPENDが失敗する、という線はゼロではないですが、pathを時間区切りにする以上は必須の処理ですし、それほど遅いとしたらそもそも大幅に改善すべき状態なので、そこは気にしないでよいかなと。
Fluentdの仕様による高速APPEND
APPENDがーAPPENDがーと言ってる間にふと気になったので仲間に見てもらったのですが、そもそも60秒間隔でHDFSに書き込んだとして、実際にCREATEとAPPENDは何回発生しているのかという話。
1:31 – 2:30 と 2:31 – 3:30 のデータをFlushするとして、2:00 – 2:59 のHDFSファイルはイメージ通り 1CREATE+1APPENDで収まっているのだろうかと。
で、まず flush_interval 60 で見たところ、HDFSへの書き込み間隔はだいたい60秒に1回なんだけど、どうやらその1回の中身が60chunk になっていて結果として 1CREATE+59APPEND になっていたっぽい罠。どうりで単発Collectorでもエラーが無くならないわけだ、と。
それはマズいと次に time_slice_wait 60 を試したところ、エラーは出なくなって、どうやら 1ファイル当たり1CREATE+1APPEND になって効率が良くなったようだ、という見解。この辺がfluentdの奥の方に隠蔽されているので追っていない。
間隔設定とかはもう少しちゃんと動作を知っておきたいところ。
60秒にしたら起動直後からキッカリ60秒刻みになるのか、少しずつズレていくのか、とか、pathに使われた最低単位の時間と間隔設定を合わせて最も効率の良いタイミングでFlushしてくれないかな、とか。
あとtimeout関係もまだ曖昧にしたままだな。
とはいえ、元気に動きそうな感じにはなってます。
次回は、今回と少し関係して log4j のことでも軽く。