Fluentd & TreasureDataで
 こっそり始めるログ集計

      Fluentd Meetup #2
           @mikeda



                          1
自己紹介


• @mikeda
  – インフラエンジニア
  – Fluentd歴3ヶ月

• 会社:CROOZ
  – モバイルメインのWEBサービス
  – ブログとかソーシャルゲームとか
  – サーバ450台くらい


• 最近個人的に作ったもの
  – 『ざびたんウィジェット』
  – あんまり反響が(´・ω・`)


                      2
今日は


「Fluentd流行ってますよ。御社でも使ってみませんか?」
「でも導入たいへんなんじゃ・・・」
「だいじょうぶ、そんなことありません!!!」

という話と、
実際にどういうふうに使っているかについてです。




                                 3
FluentdとTreasureDataについて


• Fluentd
   – 構造化されたデータ
   – プラグインによる高い自由度
   – リアルタイム性
  →データストリームを統合管理する基盤

• TreasureData
   – TreasureData .inc
   – クラウドなHadoop(Hive)
   – Fluentdから簡単にデータを投入できる



                             4
今やってること グラフ&保存(検索/集計)


• アクセスログ:2億/day
  – グラフ:PV、レスポンスタイム、遅延件数(アラート設定)
  – 保存:TreasureData
• アプリケーションのエラーログ
  – グラフ:Warning、Errorの件数
  – 保存:とりあえず無し
• メールログ(試験中)
  – グラフ:キャリアごとのメール受信数
  – 保存:MongoDB


 傾向分析と詳細調査の両方が簡単にできるように

                                   5
構成概要

各サーバにtd-agentをインストール
  APPサーバ
  APPサーバ         メールサーバ                  その他
    WEBサーバ
    WEBサーバ           WEBサーバ               WEBサーバ
                                          WEBサーバ
                     WEBサーバ                WEBサーバ
                                           WEBサーバ
      WEBサーバ
      WEBサーバ           WEBサーバ
                       WEBサーバ


  td-agent
  td-              td-agent
                   td-                  td-agent
                                        td-




                集約サーバ
               Active/Standby
                    集約サーバ       新規サーバ
                 td-agent
                 td-




                                                    6
構成概要

集約サーバにログを集約、各種処理とアウトプット
    APPサーバ
    APPサーバ                     メールサーバ                           その他
      WEBサーバ
      WEBサーバ                      WEBサーバ                    WEBサーバ
                                                            WEBサーバ
                                  WEBサーバ                     WEBサーバ
                                                             WEBサーバ
アクセスログ WEBサーバ
          アプリログ
        WEBサーバ              メールログ                              ???
                                    WEBサーバ
                                    WEBサーバ               ???



     td-agent
     td-                         td-agent
                                 td-                        td-agent
                                                            td-




                              集約サーバ
                             Active/Standby
                                  集約サーバ       新規サーバ
                               td-agent
                               td-




    Zabbixサーバ
    Zabbixサーバ                     ファイル           TreasureData
                  MongoDB




                                                                       7
グラフのイメージ


サービス単位でグラフ作成(Zabbix)




    レスポンスコードごとのPV
    レスポンスコードごとのPV      レスポンスタイム




      レスポンス遅延          アプリケーションエラー件数




                                       8
導入に至った経緯


• 『おもしろそう』からスタート
 – 渋谷の居酒屋でC社の@oraXXXさん、D社の@ryiXXさん
   Fluentdおもしろいよー
   syslog-ngでいいじゃん
   syslog-ngオワコンじゃん
   (´・ω・`)ショボーン
   。。。(話を聞く)
   おもしろそうだからとりあえず使ってみるわー


※もちろんその後に事前検証、メリット検討しました!




                                     9
普通に導入しようとすると




「えー、ログとかそんながんばんなくていいでしょ」
 となるかも
(どうしても裏側は後回しになりがち)




                           10
でもこのあたりを抑えれば


•   人と工数
•   費用(サーバ OR お金)
•   既存システムへの影響

    ちょっとやってみようか、という雰囲気を作れる

    実際どうだったか?




                             11
人と工数


• 人→ほぼ自分だけ
 – 中堅どころのLinux力
 – 初心者に毛が生えたくらいのRuby力
 – 調整力&政治力・・・しがない1エンジニアです(´・ω・`)


• 工数→うろ覚えだけどきっとそんなにいらない・・・
 – 導入、展開は簡単→td-agent(全部込みパッケージ)
   1. yum install td-agent
   2. コンフィグ編集 OR rsync
   3. /etc/init.d/td-agent start
 – 基本は設定ファイルベース
   • ググってコピペ


                                   12
費用(サーバ OR お金)


• Fluentd
   –   最初は追加サーバ1台 →2億/dayのアクセスログ+α
   –   障害発生 →冗長化して2台(片方はスタンバイ)
   –   マルチプロセス化などの負荷対策は特に無し
   –   もちろん規模によりけり
       • 50億/Day、100,000msg/secな人は@tagomorisさんに


• TreasureData
   – 500GBまで無料(圧縮サイズで)
   – それ以上の容量、CPU予約したい場合は中の人に相談




                                                  13
費用(サーバ OR お金) 集約サーバ負荷:CPU


•   古めのサーバ(HP DL360G5)1台             →けっこうつらめ
    –   CPU:Xeon E5430 2.66GHz 4コア

    –
        →user 20%(1プロセスなのでけっこうギリギリ)

    –
        メモリ:8G(平常運転で使用量1G)
        HDD:SAS 15krpm、ライトキャッシュ無し
        →I/O wait 8%
•   もうちょっと新しめのサーバにリプレース画策中




                                                14
費用(サーバ OR お金) 集約サーバ負荷:トラフィック


•   気にするほどじゃない
    – IN(各サーバからの転送):ピークで20Mbps
    – OUT(TreasureDataへの転送):ピークで3Mbps




                                        15
既存システムへの影響


• 既存サーバの負荷増加
 – 無視できるレベル     →APPサーバに導入(2000万/day)
• パッケージの依存関係
 – ほぼなし   →td-agent(専用のruby、gem、fluentd同梱)
• 既存環境の設定変更&アプリケーションの修正
 – いったんいじらない     →既存ログからデータ取得


 ほぼ影響なし
 →ダマで入れても絶対バレない!!!



                                             16
というわけで


• ある程度までの規模であれば
• そんなに難しいことをしなくても簡単に導入できました

 みなさんも気軽に導入してみましょう!




                              17
ここから・・・
実際の構成、設定について




               18
その1-1 アクセスログ
    Fluentd




               19
使ってるプラグイン


• WEBサーバ側
  – tail:アクセスログをtail
  – forward:集約サーバに転送


• 集約サーバ側
  –   forward:WEBサーバからログ受け取り
  –   exec_filter:フィルタリング(また後で)
  –   copy:出力を分岐
  –   tdlog:TreasureDataにデータ送信
  –   datacounter:条件付きカウント(@tagomorisさんThx)
  –   zabbix:カウント結果をZabbixに送信(@fujiwaraさんThx)


                                                20
プラグイン構成




          21
グラフのイメージ


サービス単位でグラフ作成(Zabbix)




    レスポンスコードごとのPV
    レスポンスコードごとのPV      レスポンスタイム




      レスポンス遅延




                                  22
APPサーバ側設定


• 設定ファイルの構成
  /etc/td-agent/
  |-- conf.d
  | |-- site1.conf
  | `-- site2.conf
  `-- td-agent.conf


• 1サービス1コンフィグ(ちょっと管理めんどう)




                            23
APPサーバ側設定 共通設定


•   td-agent.conf
     # サイトごとの設定をinclude
     include conf.d/*.conf

     # forward.**は集約サーバに転送
                                                     conf.d/*をinclude
     <match forward.**>
      type forward

      <server>
       host 192.168.1.101
       port 24224
       standby
      </server>

      <server>
       host 192.168.1.102
       port 24224
                                                     集約サーバにforward
      </server>

      buffer_type file
      buffer_path /var/log/td-agent/buffer/forward

      flush_interval 60s
     </match>

     # Fluentdのログ
     <match fluent.**>
      type file
                                                     Fluentd本体のログ、
      path /var/log/td-agent/fluent.log
     </match>

     # マッチしなかったログ
                                                     マッチしなかったログはファイルに
     <match **>
      type file
      path /var/log/td-agent/no_match.log
     </match>




                                                                        24
APPサーバ側設定 サービスごとの設定


• conf.d/site01.conf
    – アクセスログのtail
<source>
 type tail
 format format /^(?<host>[^ ]*) [^ ]* [^ ]* ¥[(?<time>[^¥]]*)¥]
   "(?<method>¥S+) +(?<path>[^ ]+) +¥S*" (?<code>[^ ]*) (?<size>[^ ]*)
   "(?<referer>[^¥"]*)" "(?<agent>[^¥"]*)" (?<restime>[^ ]*)/
 time_format %d/%b/%Y:%H:%M:%S %z
 path /var/log/httpd/site01-access_log.link
 tag forward.access.site01
 pos_file /var/log/td-agent/site01-access_log.pos
</source>




                                                                         25
集約サーバ側設定


• 設定ファイルの構成
 /etc/td-agent
 |-- conf.d
 | |-- site01.conf
 | `-- site02.conf
 |-- filter
 | |-- mobile_access_log_filter.rb
 | `-- social_access_log_filter.rb
 `-- td-agent.conf

• 1サービス1コンフィグ(ちょっと管理めんどう)
• ログの種別ごとにフィルタプログラム


                                     26
集約サーバ側設定 共通設定


•   td-agent.conf
     <source>
      type forward
      port 24224
                                            転送を受け付ける
      bind 0.0.0.0
     </source>

     include conf.d/*.conf                  conf.d/*をinclude
     <match fluent.**>
      type file
      path /var/log/td-agent/fluent.log
     </match>                               Fluentd本体のログ、
                                            マッチしなかったログはファイルに
     <match *.**>
      type file
      path /var/log/td-agent/no_match.log
     </match>


                                                               27
集約サーバ側設定 サービスごとの設定1段目


• conf.d/game01.conf
• 1段目:exec_filterでフィルタリング

  <match forward.access.game01>
    type    exec_filter
    command /etc/td-agent/filter/mobile_access_log_filter.rb
    in_keys host,method,path,code,size,referer,agent,restime,dcmuid,auuid,sbuid,spuid
    out_keys host,method,path,code,size,referer,agent,restime,uid
    tag     filtered.access.game01
  </match>




                                                                                        28
集約サーバ側設定 サービスごとの設定2段目


•   2段目:カウンティングとTreasureDataへのデータ送信
    <match filtered.access.game01>
     type copy

     <store>
      type       tdlog
      apikey      XXXXXXXXXXXXXXXXXXXXXXX
      buffer_type file
      buffer_path /var/log/td-agent/buffer/game01/td
      use_ssl     true
      auto_create_table
      flush_interval 300s
                                                       TreasureDataに送信
     </store>

     <store>
      type         datacounter
      count_key      code
      aggregate      all
      tag         count.access.game01.code
      count_interval 300

      pattern1   2xx   ^2¥d¥d$
      pattern2
      pattern3
                 3xx
                 4xx
                       ^3¥d¥d$
                       ^4¥d¥d$
                                                       レスポンスコードごとにカウント
      pattern4   5xx   ^5¥d¥d$
     </store>

     <store>
      type         datacounter
      count_key      restime
      aggregate      all
      tag         count.access.game01.restime
      count_interval 300

      pattern1   0to100ms   ^¥d{1,5}$
      pattern2   100to200ms ^1¥d{5}$
      pattern3
      pattern4
                 200to500ms ^[2-4]¥d{5}$
                 500to1000ms ^[5-9]¥d{5}$
                                                       レスポンスタイムごとにカウント
      pattern5   1000to4000ms ^[0-3]¥d{6}$
      pattern6   over4000ms ^([4-9]¥d{6}|¥d{8,})$
     </store>

    </match>




                                                                         29
集約サーバ側設定 サービスごとの設定3段目


•   3段目:カウンティングした値をZabbixへ送信

    <match count.access.game01.*>
     type copy

     <store>
      type file
      path /var/log/fluent/count.access.game01.log
     </store>

     <store>
      type         zabbix
      zabbix_server 192.168.1.10
      port         10051
      host         service_game01
      name_key_pattern (xx_count|ms_percentage|4000ms_count)$
     </store>
    </match>


                                                                30
exec_filterについて


 • 外部プログラムで自由にフィルタを作れるプラグイン
   – データ構造をちょっといじる
     • 各携帯キャリアごとのUID → 共通のUIDカラムに統合
     • クエリストリングから情報を取得
   – 個人情報の暗号化
   – 不必要なデータを除外(静的ファイルのアクセスログなど)
   –

 • 乱用するとカオス化しそう
   – ログ自体をいじれるならそっちで解決しよう!

 ※TSVよりmsgpackのほうがいいかも


                                      31
exec_filterの例


 •   モバイルサイトのアクセスログフィルタ
     – キャリアごとのUID→統合UID、静的ファイルの除外

     #!/usr/lib64/fluent/ruby/bin/ruby
     require 'digest/sha1‘
     path_filter = Regexp.new "^/(img|swf|css|js|healthcheck)/"

     while line = STDIN.gets
      line.chomp!
      host,method,path,code,size,referer,agent,dcmuid,auuid,sbuid,spuid = line.split("¥t")
      next if path_filter =~ path
      uid = ""
      [dcmuid, auuid, sbuid,spuid].each do |id|
        if id != "-"
          uid = Digest::SHA1.hexdigest id
          break
        end
      end
      puts [host,method,path,code,size,referer,agent,uid].join("¥t")
     end
                                                                                             32
その1-2 アクセスログ
  TreasureData




                 33
TreasureData


 • クラウドなHadoop
   – Fluentdでデータを投入
   – Hive:HiveQL(SQLっぽい言語)でデータ参照
 • td-agentをインストールするだけでひと通り使える
   – データの投入
      • Fluentdプラグイン:tdlog
      • CLI:td
   – 各種操作(テーブル操作、クエリ発行、結果参照)
      • CLI:td
      • Rubyのライブラリ
     ※各種言語のライブラリもあります:Java、PHP



                                   34
構成概要

集約サーバからのアウトプット先の1つ
    APPサーバ
    APPサーバ                     メールサーバ                        その他
      WEBサーバ
      WEBサーバ                      WEBサーバ                 WEBサーバ
                                                         WEBサーバ
                                  WEBサーバ                  WEBサーバ
                                                          WEBサーバ
アクセスログ WEBサーバ
          アプリログ
        WEBサーバ              メールログ                           ???
                                    WEBサーバ
                                    WEBサーバ            ???



     fluentd                      fluentd                fluentd




                              集約サーバ
                             Active/Standby
                                  集約サーバ
                                fluentd




    Zabbixサーバ
    Zabbixサーバ                      ファイル       TreasureData
                  MongoDB




                                                                   35
データの投入


• Fluentdに設定を追記するだけ
  <match filtered.access.game01>
   type       tdlog
   apikey      XXXXXXXXXXXXXX
   buffer_type file
   buffer_path /var/log/td-agent/buffer/game01/td
   use_ssl     true
   auto_create_table
   flush_interval 300s
  </match>




                                                    36
CLIでデータ参照


•   データ確認
    $ td table:tail access game01

     “size”:“234880”,“uid”:“XXXX”,“host”:“xx.xx.xx.xx”,“restime”:“95961”,
     {“referer”:“-”,“time”:1345590016,“method”:“GET”,“code”:“200”,

     “agent”:“Mozilla/5.0 ","path":"/mypage/"}
•   集計
    $ td query -w -d access
       "SELECT COUNT(distinct uid), COUNT(*) ¥
           FROM blog ¥
           WHERE unix_timestamp()-60*60*24 < time“

         +---------+----------+
         | _c0     | _c1      |
         +---------+----------+
         | 1927724 | 70917556 |
         +---------+----------+
    たいてい数分から十数分で完了(クエリと契約CPU数に依存)
    ※ td schema:setを実行するとv[‘uid’]じゃなくuidと書けるようになる

                                                                            37
どういうふうに使ってるか


いったんは定期集計ツールではなく、
『かゆいところに手が届く調査ツール』
として使ってます

•   簡単な管理画面を作成
    – Ruby & Sinatra
    – Ruby部分96行、テンプレート部分は合計99行
    – プロトタイプ版の一部はブログにアップしてます




                                 38
管理画面 Home




            39
管理画面 Database/Table一覧




                        40
管理画面 クエリ(ジョブ)発行




                  41
管理画面 ジョブ一覧




             42
管理画面 ジョブ詳細、結果確認




                  43
実際の集計例




         44
TreasureDataの集計例


レスポンス遅延が発生してるのはどのページ!?

クエリ

SELECT split(path, '¥¥?')[0],
    COUNT(1) AS numreq
 FROM game01
 WHERE
  unix_timestamp()-1*60*60 <= time
  AND restime > 3000000
 GROUP BY split(path, '¥¥?')[0]
 ORDER BY numreq DESC

※直近1時間で3秒以上かかっている件数
  をパスごとに出力




                                     45
TreasureDataの集計例


問合せ調査 「タイムアウトばっかりでアクセスできません」


クエリ

SELECT time, restime, path
 FROM game01
 WHERE
  unix_timestamp()-24*60*60 < time
  AND uid = XXXXXXX
 ORDER BY time

                                     マイページアクセスが5秒以上かかっている
                                     マイページアクセスが5
※指定ユーザのアクセスパスと処理                     →特定ユーザの場合のみ
  時間を時系列で出力                           タイムアウトが発生するバグがあった
                                                            46
TreasureDataの集計例


リリース直後に5分ごとのPVとUUを確認したい!

クエリ

SELECT
 from_unixtime(CAST(time/(5*60) AS INT)*5*60) AS day,
    COUNT(distinct uid),
    COUNT(1)
 FROM game01
 WHERE
   code = 200
   AND unix_timestamp()-60*60 <= time
 GROUP BY CAST(time/(5*60) AS INT) ORDER BY day



※5分ごとのUUとPVを出力
   ごとのUUとPVを
      UU
 特定ページのみといった りこみも簡単
   ページのみといった絞
※特定ページのみといった絞りこみも簡単




                                                        47
TreasureDataの集計例


チュートリアルの離脱ポイントはどこ?

クエリ

SELECT split(path, '¥¥?')[0],
    COUNT(1) AS numreq
 FROM game01
 WHERE
  unix_timestamp()-15*60 <= time
  AND path LIKE '/tutorial/%'
 GROUP BY split(path, '¥¥?')[0]
 ORDER BY numreq DESC

※/tutorial/から始まるパスをアクセス数
  順に出力




                                   48
TreasureDataの集計例


サイトトップからの遷移状況が知りたい!

クエリ

SELECT path COUNT(1) AS numreq
 FROM mikeda
 WHERE
   unix_timestamp()-60*60*24 <= time
   AND referer = 'http://mikeda.jp/'
 GROUP BY path
 SORT BY numreq DESC LIMIT 10

※リファラーで絞り込んで遷移状況調査




                                       49
もっと高機能なのが欲しかったら


• TreasureDataが提供するダッシュボードもある(有料)




                                    50
導入して


• 今まで
  – 集計システムは集計チームが作りこみ
   • 細かいことは対応しづらい
  – 特別に知りたいことがあったら
   • 集計チームに依頼する
   • 自分のPerlワンライナーが炸裂


• 導入して
  – 「知りたいと思った人」か「その隣の人」がすぐ集計できる
  – 情報取得のコスト↓、意味のある情報を知るチャンス↑
  – 草の根啓蒙中



                                  51
アクセスログまわりの今後


• サイト単位の設定がめんどう
  →forestプラグインで統合を検討中
• ログのフォーマットを整理したい
  – 整理できればexec_filterいらないかな




                              52
その2 Postfixのmaillog
    ※試験中




                      53
メールログ解析


• モバイル系のサイトはメールが多い
 – ブログ投稿、新規登録
 – 不正も多い・・・
• グラフ化&検索できるようにしたい

※試験導入なので参考程度に。遅延時の挙動など未確認




                            54
Maillogのパース


•   すごくたいへん
    – 複数行で1セット&混ざりまくり
    – 複数サービスで共有(to=<xx@yyy>で振り分けが必要)




              モザイクもの



•   単純にカウントするだけならto=だけ取得すればいいけど・・・
•   もっとFluentdっぽくやりたい!


                                       55
in_tailを拡張する


• 必要な情報を独自プラグインで組み立ててから転送

• 標準プラグインのin_tailを拡張
   – ローテート対応、デーモン停止時の取りこぼし防止などはin_tail
     にまかせる
   – やっていいか迷ったけどすでにやってるのがあった
     ※追記)@frsyki『in_tailはそういう設計で作ってるのでどんどん継承
      しちゃってください』
   – 自作プラグインは/etc/td-agent/plugin/に置くだけで動く
   – in_tailのコードは最初の100行くらい読めばいい

   ※コードのプロトタイプは@mikedaのGistにアップしてます


                                               56
プラグイン構成




          57
使っているプラグイン


• メールサーバ側
  – maillog_tail:(in_tailの独自拡張)
  – forward


• 集約サーバ側
  –   forward
  –   rewrite_tag_filter:サイト単位に振り分け
  –   forest:タグを見て動的にプラグインを複製
  –   mongo:mongodbにデータを出力
  –   datacounter:特定キーで件数カウント
  –   zabbix:カウント結果をzabbixに送信


                                      58
メールサーバ側の設定、出力データ


• コンフィグ(基本的にin_tailと同じ)
<source>
 type maillog_tail
 path /var/log/maillog
 tag maillog.test
 pos_file /var/log/td-agent/maillog.pos
</source>

• 出力データ
2012-08-05T04:02:27+09:00 maillog.test {
  "qid":"AF06E10339D7",
  "from":"mikeda@mikeda.jp",
  "to":"oranie@oranie.com",
  "orig_to":nill,
  "size":"114364", "relay":"local", "delay":"1.5", "status":"sent"
}


                                                                     59
集約サーバ側の設定 1段目


•   1段目:rewrite_tag_filterでサービス単位にバラす
      →送信先ドメインでタグを付け替え

<match forward.mail.rcv>
 type rewrite_tag_filter
 rewriterule1 orig_to @blog¥.mikeda¥.jp$ filtered.mail.rcv.blog
 rewriterule2 orig_to @wiki¥.mikeda¥.jp$ filtered.mail.rcv.wiki

</match>




                                                                  60
集約サーバ側の設定 2段目


•   2段目:各種キーでカウンティング&mongodbにデータ投入

<match filtered.mail.rcv.*>
 type copy
 <store>
   type forest
   subtype datacounter
   remove_prefix filtered.mail.rcv
   <template>
     count_key      from
     aggregate      all
     tag         count.mail.rcv.__TAG__
     count_interval 300
     pattern1 docomo [@.]docomo¥.ne¥.jp$
     pattern2 au        [@.]ezweb¥.ne¥.jp$
     pattern3 softbank [@.](softbank¥.ne¥.jp|vodafone¥.ne¥.jp)$
   </template>
 </store>
 <store>
   type mongo



                                                                  61
集約サーバ側の設定 3段目


• 3段目:カウントした結果をZabbixに送信

<match count.mail.rcv.*>
 type forest
 subtype zabbix
 remove_prefix count.mail.rcv

 <template>
  type          zabbix
  zabbix_server 192.168.1.10
  port          10051
  host          service___TAG__
  add_key_prefix mail.rcv
  name_key_pattern count$
 </template>
</match>

                                  62
グラフできた!




          63
気になったら集計



• 気になるところがあったらmongodbで集計




『業者さんや・・・』




                           64
その3 アプリケーションエラーログ




                    65
省略!




      66
まとめ その1 Fluentd


• けっこう簡単に導入できる!
• 現状:アクセスログ、メールログ、アプリエラーログ
  – まだまだ試験中な感じですみません
  – 使わないとできないわけではないけど、使うとすごく楽
  – in_tailはこっそり集計のキモ
    • ちょっと手を加えたい時はexec_filterかin_tailいじるか?


• 検討、要望
  – アプリチームを巻き込んで次の段階を検討中
    →自由にログ出してもらってGrowthForecastで自動グラフ化
  – プラグインの設定、内部状態ダンプ機能が欲しい


                                             67
まとめ その2 TreasureData


• とにかく簡単にHadoopが使える
   – Fluentdでデータ投入
   – HiveでSQLっぽく集計
• 1エンジニアが勢いで最初の一歩を突破できる!


                       最初の一歩が大きな壁だった
                        コスト:HW、技術的コスト
                        コスト:HW、技術的コスト
                        リスク:コストとメリットが不明確

    (´・ω・`)ショボーン




                                       68
終わり!!!




         69

Fluentd meetup #2