タグ「fluentd」が付けられているもの

Fluentd Casual Talks #3 へ参加しました

Fluentd Casual Talks #3 に参加してきました。

各発表の内容は資料を見るなり、誰かがまとめていたりするので、そちらを見れば良いと思います。今回参加した理由は、Windows版のFluentdについての発表があったからです。というわけで、発表者の方と休憩時間に話した内容をまとめます。

  • NTFS限定、かつネットワークドライブは挙動を保証しない事をドキュメントに書いた方が良いと思う
  • 監視対象がローテーションしていて、かつ別ボリュームへ移動している場合は保証外である事を明示した方が良いと思う
  • NTカーネルのバージョンをどこからサポートするか明示した方が良いと思う
    • 特にXPのバージョン(Windows Server 2003の1つ前のカーネル)まで遡ると、NTFSの挙動が違います
  • GetFileInformationByHandleで判断する時は、FileIndexの他にdwVolumeSerialNumberも合わせて比較しないとダメです
  • Win32Fileの実装がどこかで見たことあるコードのような。あのコードはPOSIXのインターフェースに合わせるように設計されているが、WinならWin32File, そうでないならKernel(or File)のインスタンスを使うというようなコードではなく、所謂ifdef win32ならWin32Fileを直接使うような記述をしているので、忠実にPOSIXのインターフェースに準拠する必要はないのでは。むしろデッドコードが多すぎてよろしくない気がします。
  • FileBufferが動かないのはFile操作がPOSIX準拠のままなので、そっちもWin32APIを使えば動くはずです(同一ボリュームであればMoveしてもOpenしているファイルハンドルは追従してくれます)
  • File操作に古いAPIを使っていて64ビット値の扱いが煩雑になっている。Ex系のAPIを使った方がスマートに書けます
  • 本体とin_tail, out_forwawrdがだいたい動くようになったら、次はパフォーマンスカウンタとWindowsのイベントログを吸い上げたいって要求は出てくるよね

このあたりを話した気がします。

1つ忘れた事として以下があります。何か意図があるならごめんなさい、スルーしてください。

  • Win32API.new() を各メソッド中で行うのではなく、initialize中で行った方がよいのでは?

※読み返したら、なんか書き方がDisっぽいけど、箇条書きの関係でそんな風に見えるだけです(;´x`) そんなつもりは全然ないですよ!

 

あと、以下の事も話しました。

  • そもそもログを吸い上げるところではCPUバウンドな処理はあまりしたくないし、それはつまり、ログを吸い上げてフィルタ処理などは他所のノードで行うことになる。
  • どうせ中間ノード(アグリゲーション)や最終的な出力先(MongoとかDBとかHadoopとか)はWindowsじゃないんでしょ
  • という事はWinで動くagent-liteのようなものだけで十分なのでは

パスワードの先頭4,5文字を公開してしまった某社CTO様から。

  • 世の中には全部Windowsのお客さんとかいるんやで
  • Azureとかもあるんやで

世の中は厳しい。

 

それはそれとして、TDにMonitorに機能が追加されるそうです(無料は嬉しいね!)。でもでも最終的にアラートがあげられないと厳しそう。(後々アラートも対応するそうです)。
Cloudera Managerって何かおかしな事があるとヒントをくれるんですが、TDもそういうの出してくれると嬉しいかもですね。例えば、あるログを検知したら「バッファが溢れておるぞ! このあたりの設定を見直すのじゃ!」みたいな。

 

というわけで、楽しかったでざる。
主催者、および発表者、会場提供のDeNAさんに感謝!

用事があったので参加できなかったけど、飲み会行きたかった(´・ω・)

なんかタイトルがラノベっぽい。

Fluentd Casual Talks #2 に参加してきたので、その感想でも。

-- 感想 --

JRの駅からヒカリエに行く方法がわからず、20分ほどうろうろして最終的に駅員さんに聞きました。くやしくなんかないもん。(震え声)

just_do_neet さんの運用の話は、とても良かったです。とりあえず入れてみて、それっぽく動いている。ハッピー。 というわけでもなく、きちんと検証したんだなーという感じが特に。駆け足だったので、ちゃんと追えていないけど・・(´・ω・`)

そしてangostura11さんがStreamに対してクエリを発行できるものを発表されていて、それってかなり便利なんじゃ。。でもJavaかぁーとか思ったり、Spring_MTさんの話でFluentdがテスト用のドライバを用意しているという事を知り、じゃぁテスト書くの楽なのかもとか思って、会場の後ろの方でpluginをこそこそと書き始めていました。

他の発表者の方も、短い時間でテンポ良くしゃべっていて、一部の内容は理解できなかったけど、タメになったなぁという感じです。

それはそうと、oza_x86さんのデスクトップ(?)にHADOOPのパッチが沢山置いてあったのが一番気になりました(;゜ロ゜)

-- 感想終わり --

--  次は Fluentd について思っていること  --

もうあちこちで言われているので、わざわざ書く必要もないかもですが、個人的にFluentdの良いところというのは、再送処理を現実的な範囲でそれなりに担保してくれるという事と、in_tailプラグインの存在だと思っています。

Webの例なんかはあちこちに出ているので、他の・・例えば400台くらいのゲームサーバがあったとして(昔CEDECで発表されていたの、これくらいでしたよね)、それのログはローカルDISKに書き込むか、専用のログサーバに転送するか、もしくはその両方を行うとかやると思います。ここまでかっちりと作ってあるものなら、まぁ別にそれなりに動きますが、時としてローカルDiskにのみログを出力しているものがあります。

ちなみに、(自分が知っている範囲で言うと)オンラインゲーム(ソーシャルゲームじゃないよ)のログというのは、ユーザー比率的にWebのログよりも圧倒的に多く、ちょっと追加しようものなら、ものすごい勢いでDiskを食いつぶしていきます。

話はそれましたが、まぁとにかくローカルDiskに書き込むケースが多いわけです。で、これを何かしらの方法で集約するか、もしくはその各ホスト上で直接分散検索をしたりするわけです。これで困る点はホストが壊れて代替機に振り替えた場合、そのホストにあるログが・・・ という問題があるわけです。集約するにしても、バッチ処理的に吸い上げるとスパイクな負荷がかかります。まぁこれもあちこちで言われている話なので言うまでもないですね。

で、in_tailの何がすばらしいかというと、既に作ってあるアプリケーションに対して何も変更を行うことなく、外部プロセスでStreamで他所の場所に集約できるという点です。 これ、かなり嬉しかったです。昨年のCEDECで国民的ゲームのデータベースの発表をした人も、in_tail イイね!と話した記憶があります。

そんなわけで、 in_tail を標準のpluginで入れているのは実は凄く素晴らしい事なんだと思っているわけです。
(当たり前すぎるのか言及している人を見かけない・・(´・ω・`))

-- Pluginを作ってみた話 --

話を聞いて、まぁ私もPluginを書いてみるかと思うわけですが、実際に書いてみると、コピペをしない場合は最終的にソースを見る羽目になるわけです。

例えば、テストを書くにしてもドキュメントを見ると、out_fileのテストを参考にしなって書いてあるし、何を継承するかとか、親クラスは何のパラメータを持っているのかとか、ソースを見ないと分からない(分かりづらい)わけで。こんなパラメータ合ったんだとか (例えば num_threads なんかは一部のTwitterと一部のBlogにちょろっと出てくるくらいです)、新しい発見があって、fluentdに限って言えば、pluginを1回書いた方が理解が深まるなと思いました。

その肝心のpluginの作り方ですが、pluginを○時間で書いたとかいう話をPlugin製作者が言ってますが、実際そーいうレベルの時間で書くことができました。outputプラグインに関しては、驚くほど抽象化されているので、やりたい事だけをペイッと書くだけです。めんどくさい処理はMixinで提供されいるので、楽ちんです。ただ、存在を見つけるのに時間がかかりました・・(´・ω・`) このドキュメントに載っていないので、複数のPluginのソースを参考にして、fluentdやpluginのソースを検索しまくりでした。

テストもドライバが用意されているので、TDDで書くことができます。

-- fluent-plugin-arango について --

そんなわけで、ArangoDBに書き込むためのpluginを書いてみました。ソースはこちらに、Gemはこちらにあります。 ArangoDBは最新版ではbulk_importを持っているのですが、rubyのclientであるashikawa-coreがまだサポートしていないので、1件ずつ書いています。
(そういえば、Collection勝手に作るから、wait_syncのパラメータが必要かも)

使い方はこんな感じです。

<match access.**>
  type arango
  collection ut_col
  scheme http
  host localhost
  port 8529
</match>

BufferedOutputを継承し、SetTimeKeyMixin, SetTagKeyMixinをincludeしているので、それらのパラメータも指定する事ができます。 詳しくはREADMEに記載しています。

実際にArangoDBのWebUIから確認をすると、データが入っています。

何かまずい点をみつけたら、ご指摘いただけると嬉しいです。

Fluentdに負荷をかけたいなと思いまして・・。
前回作ったapache-loggenを使っても良いのですが、あれだと一度diskに出力する事になるし、Rubyなので複数コアがあるホストだと何プロセスも起動するのは嫌だなーと。

というわけで、今回はマルチプロセスで直接Fluentdにmsgpack形式のデータを投げるものを作ってみました。
in_forwardで受け付ける形式は何個かあるみたいですが、今回は以下の形式のものを。

[ tag, [time, rec], [time, rec], [time, rec], ... ]
rec = { ... }

実際の実行画面のキャプチャです。

 

なんか、ほとんどマスクしちゃってますね(´・ω・)
parallelでforkするので複数コアを活用します。情報はUNIXSocketで親プロセスへ集めて表示はCursesを使っています。それぞれの子プロセスの情報とそれぞれを集計した情報を出力しています。

灰色でマスクしているところには設定が表示されています。上記の画面では、毎秒10リクエスト、1リクエストで50レコード、つまり毎秒500レコード。それを4プロセスで送りつけているので、毎秒2000レコードの負荷をかけているものになります。全開でまわせば、L社の初期の流量くらいは出せると思います。

使い方はこんな感じ。

$ ruby load.rb --help
Usage: load [options]
        --host         負荷をかけるFluentdのホスト名。  デフォルトはlocalhost。
        --port         負荷をかけるFluentdのポート番号。デフォルトは24224。
        --rate=RATE    1秒間に送信するリクエスト数。デフォルトは制限無し。
        --num=NUM      1回のリクエストで送信するレコード数。デフォルトは1。
                       いわゆるArrayの要素数。
        --thnum=THNUM  負荷をかけるプロセスの数。デフォルトは1。
        --mode=MODE    ログの形式を指定する。これは会社依存のオプションなので詳細は無し。
        --fixed-time   ダミーログの時間を元ファイルの時間のまま送る場合に指定する。
                       指定しなければ現在の時間に直して送信される。
                       (これも会社依存のオプション)

ログを生成する部分は apache-loggen を使って、apacheのログではなく アプリの実際のログを元に生成しています。なので、このままでは公開はできないのですが、ここを除去すれば問題ないので気が向いたらやろうかなと・・。

気が向いたら・・。

 

こちらの記事で書いた、Apacheのログを生成し続けるスクリプトをRubyGemsに登録してみました。

なので、今後は

gem install apache-loggen

とインストールするだけで、

apache-loggen

というコマンドが使えるようになります。使い方は今までと同じです。(以下のような感じです) 

apache-loggen [--rate=0] [--limit=0] [--rotate=0] [--progress] [--json] [outfile]
  --rate     毎秒何レコード生成するかの指定。0以下は制限無し。
  --limit    最大何件出力するか。0以下は延々と出力し続ける。
  --rotate   ローテーションの間隔。単位は秒指定。0以下はローテーションしない。
  --progress STDERRに経過情報を出力する
  --json     apacheのログ形式ではなくJSON形式で出力する

  outfile    指定するとファイルに出力します。省略するとSTDOUTに出力します。

毎秒10件をaccess.logに出力。60秒毎にローテーション。経過をSTDERRに表示。

apache-loggen --rate=10 --rotate=60 --progress access.log

また、この機構を使って独自のログ形式を出力できるようにしました。 以下のように使います。

require 'apache-loggen/base'
LogGenerator.generate(conf=nil, gen_obj=nil, &block)

confはnilを渡すとARGVの内容をパースします。プログラムで直接指定する場合は、Hashを渡す事でデフォルトの設定を上書きする事ができます。実際にログを出力する処理は、gen_objにLogGenerator::Baseを継承しgenerate(context, config)メソッドを定義したクラスのインスタンスを渡すか、ブロックを与えます。両方指定した場合は、gen_objが先に実行され、blockが評価されます。(blockではgen_objで出力したrecordを受け取る事ができます)

いくつかサンプルを置いておきます。

独自のログ形式を出力したい場合。

require 'apache-loggen/base'
class MyGen < LogGenerator::Base
  def generate(context, config)
    return "#{Time.now.to_s} #{context.inspect}\n"
  end
end
LogGenerator.generate(nil, MyGen.new)

もしくは

require 'apache-loggen/base'
LogGenerator.generate do | context, config, record |
  "#{Time.now.to_s} #{context.inspect}\n"
end

上記の2つは同じ処理です。

Apacheログに新しく情報を追加したい場合。

require 'apache-loggen/base'
class MyGen < LogGenerator::Apache
  # オリジナル実装はhashをJSONか1行の文字列にしているが
  # 今回はそれに情報を追加する
  def format(record, config)
    record["process_time"] = grand(1000000) + 1000
    if config[:json] then
      return record.to_json + "\n"
    else
      return %[#{record['host']} - #{record['user']} [#{Time.now.strftime('%d/%b/%Y:%H:%M:%S %z')}] "#{record['method']} #{record['path']} HTTP/1.1" #{record['code']} #{record['size']} "#{record['referer']}" "#{record['agent']}" #{record['process_time']}\n]
    end
  end
end
LogGenerator.generate(nil, MyGen.new)

LogGenerator::ApacheはLogGenerator::Baseを継承しており、generateの中でformatを呼び出しています。このformatの実装が、1行分のレコードをhashとしてうけとりJSONか1行の文字列に変換するという処理をしているので、これをOverrideするとApacheログの生成ロジックを使いつつ書式を変更する事ができます。

いくつかのサンプルはREADMEにも書いてあります。

(∩´∀`)∩

 

最新の記事はこちらです。

Fluentdのテストをする過程で欲しかったので、Apacheのログを生成し続けるスクリプトを書きました。

と言っても生成部分はTreasureDataのスクリプトを丸ぱくりで流量制限をするオプションを追加して延々と出力するようにしただけです。

frsyuki++。

手元でサクッとproductionで確認できる人はうらやましす。

使い方は以下のような感じです。詳細はREADMEを見てください。

ruby sample_apache_gen.rb [--rate=0] [--limit=0] [--rotate=0] [--progress] [--json] [outfile]
  --rate     毎秒何レコード生成するかの指定。0以下は制限無し。
  --limit    最大何件出力するか。0以下は延々と出力し続ける。
  --rotate   ローテーションの間隔。単位は秒指定。0以下はローテーションしない。
  --progress STDERRに経過情報を出力する
  --json     apacheのログ形式ではなくJSON形式で出力する

  outfile    指定するとファイルに出力します。省略するとSTDOUTに出力します。

ローテーションについては以下のルールでローテーションします。

  hoge.log --> hoge.[yyyy-MM-dd_HHmmss].log

例えば、こんな感じで使います。
毎秒10件をaccess.logに出力。60秒毎にローテーション。経過をSTDERRに表示。

ruby sample_apache_gen.rb --rate=10 --rotate=60 --progress access.log

普通はクラス化して、出力内容を決める部分をBlockで渡すようにして再利用性を高めると思うのですが、ごにょごにょ(´・ω・`)

ソースはここに置いておきます。
https://github.com/tamtam180/apache_log_gen 

これでWeb屋じゃなくてもそれっぽいログが出来るのでFluentdで遊べますね(∩´∀`)∩

WindowsでFluentdを動かそうとして調べていった記録のメモです。
きちんとまとめていないので起こった順に書いています。

同じような事を試みて挫折した人が居たので、この意思を勝手に引き継いで3秒以上頑張ってみました。

概要としては、fluentdのRuby実装Python実装(fluenpy)、fluent-agent-liteのPerl実装F#実装を確認した事と、Windowsでのファイル処理の排他制御について分かったことをまとめています。Windowsのファイルについては別の記事でまとめる事にして、この記事では簡単に触れる程度にします。