Hatena::ブログ(Diary)

watawata日記

2012-02-07

fluentdを試してみた

Fluentd meetup in Japan on Zusaar

僕は行ってないんですがTwitterUstream、スライド、ブログなどを見る限りだいぶ盛り上がったようですねー。僕自身が仕事で使う予定は今のところ無いんですがログ解析関連の仕事をしていることもあるので素振りしてみようと思います。

環境はVirtualBox上のCenOS 5.7(x86_64)を使いました。

fluentdはRuby 1.9上で動くんですがCentOS 5.7に入っているのはRuby 1.8.5です。Ruby 1.9インストールから始めるとはまりそうなのでyumインストールできるtd-agentを使います。td-agentはfluentdの安定版パッケージという位置付けのようです。

試したのは下記3つです。


最初のfluent-catでログを送るで参考にしたサイトはこちらです。

2012-02-06 - ぽにくすじゃないだいありー

Yumリポジトリの設定をしてインストールします。

[root@localhost ~]# vim /etc/yum.repos.d/td.repo
[root@localhost ~]# cat /etc/yum.repos.d/td.repo 
[treasuredata]
name=TreasureData
baseurl=http://packages.treasure-data.com/redhat/$basearch
gpgcheck=0
[root@localhost ~]# yum install td-agent

設定ファイル/etc/td-agent/td-agent.confは下記のようにします。

<source>
  type tcp
</source>

<match debug.**>
  type file
  path /var/log/fluent/debug
</match>

あとは事前準備です。

[root@localhost ~]# mkdir /var/log/fluent
[root@localhost ~]# chown td-agent:td-agent /var/log/fluent

そして起動します。

[root@localhost ~]# service td-agent start

td-agentのログが /var/log/td-agent/td-agent.logに出ます。下記のような感じ。エラーがでないことを確認します。ありがちなのはAddress already in use とかPermission deniedかな。

2012-02-07 22:31:24 +0900: reading config file path="/etc/td-agent/td-agent.conf"
2012-02-07 22:31:24 +0900: adding source type="tcp"
2012-02-07 22:31:24 +0900: adding match pattern="debug.**" type="file"
2012-02-07 22:31:24 +0900: running fluent-0.10.6
2012-02-07 22:31:25 +0900: listening fluent socket on 0.0.0.0:24224

fluent-catを使ってtd-agentにログを送ります。

[root@localhost ~]# echo '{"hoge":"fuga"}' | /usr/lib64/fluent/ruby/bin/fluent-cat debug.test

結果を確認するとちゃんと時間、タグ、JSONが出てますね。

[root@localhost ~]# cat /var/log/fluent/debug.20120207.b4b85fcecacf316bc 
2012-02-07T22:32:17+09:00       debug.test      {"hoge":"fuga"}

では次にApacheアクセスログを収集してみましょう。

参考にしたのはこのサイトです。

イベントログ収集ツール fluent を試しに使ってみる - 文字 - はてな自習室

td-agent.confを下記のように設定します。

<source>
  type tail
  format apache
  path /var/log/httpd/access_log
  tag apache.access
</source>

<match apache.access>
  type file
  path /var/log/fluent/access_log
</match>

このまま起動すると/var/log/httpdのパーミッションが700でroot:rootという所有権なのでtd-agentだと読み込めずに下記のようにPermission deniedになります。

2012-02-07 22:47:49 +0900: unexpected error error="Permission denied - /var/log/httpd/
access_log"
  2012-02-07 22:47:49 +0900: /usr/lib64/fluent/ruby/lib/ruby/gems/1.9.1/gems/fluentd-0
.10.6/lib/fluent/plugin/in_tail.rb:97:in `stat'

なので

[root@localhost ~]# chmod g+rx /var/log/httpd

とするとうまくいきます。o+rxでうまくいかないのはよくわからんです。。。td-agentユーザってrootグループじゃないのに。rootLinux弱者だ。。。

ともあれ、service httpd startして下記のようにHTTPアクセスします。

[root@localhost ~]# curl http://localhost
|<<

そうすると下記のように構造化されたログが出力されます。
>||
[root@localhost ~]# cat /var/log/fluent/access_log.20120207.b4b860095492522d2 
2012-02-07T22:48:39+09:00       apache.access   {"host":"127.0.0.1","user":"-","method":"GET","path":"/","code":"403","size":"5043","referer":"-","agent":"curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5"}

最後にMongoDBApacheアクセスログを出力してみましょう。

参考にしたのはこちら

Treasure Data Blog | Real-Time Log Collection with Fluentd and MongoDB

まずはMongoDBインストールします。

[root@localhost ~]# vim /etc/yum.repos.d/10gen.repo
[root@localhost ~]# cat /etc/yum.repos.d/10gen.repo
[10gen]
name=10gen Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64
gpgcheck=0
[root@localhost ~]# yum install mongo-10gen-server
[root@localhost ~]# service mongod start

この時点ではデータは入っていません。

[wyukawa@localhost ~]$ mongo
MongoDB shell version: 2.0.2
connecting to: test
> show dbs;
local   (empty)

次にfluent-plugin-mongoをインストールします。

[root@localhost ~]# /usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-mongo

td-agent.confを下記のようにします。

<source>
  type tail
  format apache
  path /var/log/httpd/access_log
  tag mongo.apache
</source>

<match mongo.**>
  # plugin type
  type mongo

  # mongodb db + collection
  database apache
  collection access

  # mongodb host + port
  host localhost
  port 27017

  # interval
  flush_interval 10s
</match>

この状態でtd-agentを起動すると下記のメッセージができます。

Starting td-agent: Able to load bson_ext version 1.4.0, but >= 1.5.2 is required.

**Notice: C extension not loaded. This is required for optimum MongoDB Ruby driver performance.
  You can install the extension as follows:
  gem install bson_ext

  If you continue to receive this message after installing, make sure that the
  bson_ext gem is in your load path and that the bson_ext and mongo gems are of the same version.

bson_extをインストールしろといっているのでそうします。

[root@localhost ~]# /usr/lib64/fluent/ruby/bin/gem install bson_ext

td-agent起動後にHTTPアクセスします。

[root@localhost ~]# curl http://localhost/

その後MongoDBの中を見ているとデータが入ってます。

> show dbs;
apache  0.015625GB
local   (empty)
> use apache;
switched to db apache
> show collections;
access
system.indexes
> db.access.find();
{ "_id" : ObjectId("4f313256e138231825000001"), "host" : "127.0.0.1", "user" : "-", "method" : "GET", "path" : "/", "code" : "403", "size" : "5043", "referer" : "-", "agent" : "curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5", "time" : ISODate("2012-02-07T14:16:42Z") }

以下のように100アクセスするとdb.access.count();の値が100づつ増えます。

[root@localhost ~]# ab -n 100 -c 10 http://localhost/

td-agent使えばかなり手軽にログ解析できそうですね。wktkしてきました。

fluentdについて調査する場合は下記からいろいろとたどれそうですね。

fluentdのブログ記事などまとめメモ - oranieの底辺日記

いじょ。

2012-02-06

OLAP関数

テレコム業界のCDR(Call Detail Record)の解析をOracleからHiveにマイグレーションした資料を見つけたのでめもっておく。

この資料を見つけたきっかけはp22にあるOracleのrank() over (partition by ... order by ...)みたいなOLAP関数をHiveでどのように書き変えるかの部分を見たからだ。

そもそもオレはDB弱者でOLAP関数なぞ使った事も無いし知らなかったのだがWeb+DBのミックさんの連載を読んで便利というか直感的だなあと思ったものだ。

MySQLにはOLAP関数が無いらしいので同じ事を実現しようとすると相関サブクエリになったりして可読性はあんま良くないと思う。

具体例はこの辺。

Hiveで処理する場合はOracleと違ってまだOLAP関数がないのでうごうごとやらないといけない。

ただHiveも0.7になってRank関数は実装されたようだ。

[#HIVE-1304] add row_sequence UDF - ASF JIRA

しかしここで指摘されているようにアンドキュメントなようだw

この辺の集計処理をHiveでやるのかそれとも例えばSqoopでPostgreSQLにデータを取り込んでRDBMSに任せるのかとかいろいろとやり方はありそうだ。

2012-01-28

シェルスクリプトのパスとパイプ

考えてみるとここ1年はJavaのコードはほとんど書いていなくてそれよりはシェルスクリプトを書いている機会の方が多かった。

なのでここら辺でシェルスクリプトを書いていてハマったところというかちょっとしたTipsをメモっておこうと思う。前提としてBashである。


まずはパスについての話。

シェルスクリプトでパスをべた書きしているとポータビリティが失われてしまい別のマシンに移行する場合に困るケースが多い。

なのでこれをスマートに解決したいわけである。

かといって単純に相対パスを使っていると困るケースがある。

例えばある基準となるディレクトリ${BASE_DIR}があってその下にシェルスクリプトをおくディレクトリscriptsとログをおくディレクトリlogという構成にしたいとしよう。

 scripts/
 log/

この場合にシェルスクリプトの中で

echo ... > ../log/log.txt

とかやるとシェルスクリプトを実行する場所がscriptsに固定されてしまう。

それで良いケースもあるだろうが、もうちょっとスマートにやる場合は基準となるディレクトリ${BASE_DIR}の絶対パスを求めてそれを使うことである。

どう求めるかというとこんな感じ。

BASE_DIR=$(cd $(dirname $0);pwd)

説明はまるっと借用させていただくw

$0は実行中のシェルスクリプトのファイル名。

dirnameを使うことで、シェルスクリプトディレクトリーが取得できる。

ただしこれは相対パスかもしれないので、cdでそのディレクトリーに移動し、pwdでその場所(絶対パス)を取得している。

UNIXシェルスクリプトメモ(Hishidama’s UNIX shell script Memo)

このように設定した${BASE_DIR}を使ってパスを作ればよい。

echo ... > ${BASE_DIR}/log/log.txt

この手の共通的な設定は別ファイル、例えばconfig.shにくくりだして読み込むのがよろしい。

こんな感じにね。

cwd=`dirname $0`

. ${cwd}/config.sh

つづいてはパイプ周りの話。

例えばあるディレクトリ${WORK_DIR}にあるデータファイル全てを1つ1つ処理したい。

ただし仕様としてある1つのファイルでエラーが起きた場合も処理を続行したい。

しかし1ファイルでも異常があったら最終的には異常終了にしたい。

全てのファイルを正常に処理できたときのみ正常終了とする。

言葉だと伝わりづらいと思うが、シェルスクリプトで書くとこんな感じになるだろう。

#!/bin/sh

cwd=`dirname $0`

. ${cwd}/config.sh

exit_code=0

ls ${WORK_DIR} | while read datafile
do
    ${cwd}/process.sh ${WORK_DIR}/${datafile}
    if [ $? -ne 0 ]; then
      exit_code=1
    fi
done

exit ${exit_code}

しかしこの方法では仕様を満たさない。

ループの外側のexit_codeが1になることは無いから。

つまり1つのファイルでエラーが起きた場合でも全体としては正常終了する。

何故かというと | (パイプ)処理から先は別プロセスで起動していて、ループの外側と内側のexit_code変数は別物だから。

解決策はリダイレクトを使う。

#!/bin/sh

cwd=`dirname $0`

. ${cwd}/config.sh

exit_code=0

ls ${WORK_DIR} > datafile.list

while read datafile
do
    ${cwd}/process.sh ${WORK_DIR}/${datafile}
    if [ $? -ne 0 ]; then
      exit_code=1
    fi
done < datafile.list

exit ${exit_code}

これならOK。

リダイレクトするとそのファイルの処理を考えないといけないのが南天のど飴なのであるがこれは仕方ない。

似たような話だが、下記のようにすると多重ループをexitで抜けられない。

#!/bin/sh

cwd=`dirname $0`

. ${cwd}/config.sh

ls ${WORK_DIR} | while read dir
do
  ls ${WORK_DIR}/${dir} | while read datafile
  do
    ${cwd}/process.sh ${WORK_DIR}/${dir}/${datafile}
    if [ $? -ne 0 ]; then
      exit 1
    fi
  done
done

これはさっきと違って1ファイルでも異常があったら処理を続行せずに終了したいパターン。

これもリダイレクトすればOK。

#!/bin/sh

cwd=`dirname $0`

. ${cwd}/config.sh

ls ${WORK_DIR} > dirs.txt
while read dir
do
  ls ${WORK_DIR}/${dir} > dir.txt
  while read datafile
  do
    ${cwd}/process.sh ${WORK_DIR}/${dir}/${datafile}
    if [ $? -ne 0 ]; then
      exit 1
    fi
  done < dir.txt
done < dirs.txt

もしくはこんな感じでdoneの直後でexitする。

#!/bin/sh

cwd=`dirname $0`

. ${cwd}/config.sh

ls ${WORK_DIR} | while read dir
do
  ls ${WORK_DIR}/${dir} | while read datafile
  do
    ${cwd}/process.sh ${WORK_DIR}/${dir}/${datafile}
    if [ $? -ne 0 ]; then
      exit 1
    fi
  done
  if [ $? -ne 0 ]; then
    exit 1
  fi
done

いじょ。

2012-01-22

ログ解析についてつらつらと考えていること

ログ解析についてつらつらと考えていることを書いてみたいと思います。

Hadoopを用いたログ解析によってマーケティングを変革し売り上げを向上させようという話はよくあります。

この手の話はたいていBtoCで例としてはメールでレコメンドして商品を買ってもらうとかですね。

ログ解析がどういうフローかというと、ログを埋め込んでログを収集して蓄積して解析してそのレポートを見て何らかの施策を打つ、という感じになります。

図にするとこんな感じ

f:id:wyukawa:20120122185538p:image

今話題沸騰中の「Fluentd」はログ収集を担当します。といいつつ僕自身はFluentd使ったことないです。記事を読んだくらいです。

ちなみにどれぐらい話題沸騰中かというとこれぐらい定員オーバーしてます。すごすぎ。

Fluentd meetup in Japan on Zusaar

ログ埋め込みはJavaならLog4j使って埋め込んだりするでしょう。

Apacheアクセスログ使うのならログ埋め込みは必要無いですが業務的なログは取れません。URLに業務的な情報を付加すれば別ですが。

Fluentdやアクセス解析ソフトを使っている場合は生ログをパースする必要はないかもしれないですが、使っていないならパースして必要な情報を取得する必要があります。

JavaScriptや画像へのアクセスログを除く必要もありますね。僕はやったことないですがなんだか凄く大変そうですね。。。

ログ埋め込む人とパースする人(解析する人)が同じならいいですが、そうでないことが多いでしょう。

同じでない場合ログ埋め込んだ人が何をもって正しくログを埋め込んだかを確認するんでしょう。。。

仕様書なのかなあという気がしますが、そんなちゃんとした仕様書を書く工数があるのかという話と、もっと根本的にはログというプログラム本来の動きに直接関係しないことにどれだけ工数をかけられるかというのがあります。

てか、例外でさえ無視されやすいのに例外でもないログを考慮できるかというと難しい気がします。

でもこれは能力の話ではなくマネジメントの話ですね。ログ解析で何をしたいのか?ってところがスタートでそこからかけられる工数の中から最低限のことをやっていくという話でしょうね。

サービスレベルの話もあります。収集プロセスは毎日だったりストリーミング的にまわっているかもしれないですがこれが1日ダウンしてもOKなのかとか。さすがに24時間365日動いている必要もない気がしますが。

どのような手法でやるにせよまあHadoopに蓄積します。そして解析します。HiveやPigを使うのが一般的かもしれないですが、直MapReduceHadoop Streamingでやる手もあるでしょう。

この辺で問題になってくるのが解析結果の信頼性です。解析結果が正しいことをどう担保するのか。これは悩ましいです。プログラムスタックトレース吐いて落ちるならすぐわかりますが、数値が違うというバグはそう簡単には見つかりません。検算するにしても何を正しいと仮定するのでしょうか。場合によっては2人で同じ仕様の解析プログラム書かせて結果を比べるという手もあるかも。

最後はビジュアライズしてレポーティングですね。商用のBIツール使うのかExcelのピボットテーブル使うのかはおいといて解析結果を見やすい形にして施策を打つ人に見せる必要があります。


こう見ていくとログ解析というのは大変です。データ量が増えるとなおさらです。こうなると費用対効果がどうなんだという話になりそうです。

費用対効果はA/Bテストで調べるんだと思いますが、それやるのも一手間かかるでしょう。

そういうこともあるのでログ解析のゴールとして売り上げ向上というよりは不正データの検出のほうが向いているのかなあという気がします。

なんでそう思ったかというと最近でたこの記事を読んだからですがw

――そうなると企業としてはビッグデータ活用の投資対効果をどう測るかが重要になると思うのですが。

佐藤氏:そのとおりです。その意味でも、ビッグデータ活用の目的をいきなり収益の向上やマーケティングの変革に置くよりも、カード会社が行っているカード不正利用の洗い出しのように、リスク管理リスク対策に置くほうが現実的ではないでしょうか。つまり、そうしたほうが会社の投資は引き出しやすいということです。

 また、「ビッグデータの分析によって違法行為はすぐに見つけられる」と宣言できるようにしておくことは、それだけで犯罪の抑止につながります。ビッグデータの分析で違法行為を発見するのは簡単なことではないかもしれませんが、たとえ実効性が低くても、犯罪の抑止になればそれで十分という割り切った考え方もできるはずです。

ビッグデータの間違いを正す(前編)|ストレージ|トピックス|Computerworld

他にはソーシャルゲームでクリアが難しく脱落者が多い箇所をチューニングして退会を防ぐとかはログ解析の価値がありそうです。要はマイナスを減らすディフェンスの話ですね。売り上げを向上させるとかそういうオフェンスの話ではなく。


というようなことを今晩のプレミアリーグのビッグマッチ前につらつらと考えていました。いじょ。

2012-01-14

プレミアリーグに関する情報源

今シーズンからプレミアリーグをTV(JSPORTS)で見るようになったんですが関連して面白そうな情報源があったのでメモっておく。英語の勉強にもいいかも。

プレミアリーグを紹介する書籍としては下記があります。2009年出版なので選手の所属が変わってたりしてますが(例えば表紙にはアーセナルセスクが写っている)参考になります。

プレミア最強ガイド

プレミア最強ガイド

フットボールと英語という切り口で書かれた本としては下記があります。CD付き。ベンゲルの英語がフランス語訛りなのがよくわかりますw

Footballイングリッシュ

Footballイングリッシュ

Footballイングリッシュでは英語の情報源をいくつか紹介しています。それが下記なのですが、この辺のサイトを見るのも面白いかも。

テレビ局のスポーツサイト

タブロイド紙

全国紙

公式ウェブサイト


日本語での情報源としては下記があげられると思います。

イングランドサッカー,プレアミリーグ,クラブ,代表チーム,チーム,移籍情報,動画,ビデオ - Goal.com

スポーツナビ|欧州サッカー|イングランド[プレミアリーグ]

電子雑誌としては

theWORLDがあります。iPadで購入してみましたが操作性はイマイチな気がします。コンテンツは良いと思いますが。

コラムは下記がなかなか面白いです。

平床 大輔のコラム一覧 : コラム | J SPORTS

ブログだと下記ですね。

プレミアリーグ日誌:So-netブログ

またJSPORTSではサッカーネット プレスパス:プレミアリーグ日誌:So-netブログに書かれているようにサッカーに関する関連番組をやっています。

月曜日〜金曜日まで「デイリーサッカーニュース Foot! 」というサッカー番組があります。毎回見ているわけではないですが、月〜水が欧州サッカーのネタで、木曜日がJリーグ、金曜日が南米サッカーという感じのようです。

月曜日〜木曜日が西岡さんMCによる22:00-22:30枠で金曜日が倉敷さんMCによる22:00-22:50枠のようです。

また「デイリーサッカーニュース Foot! 」の後に「サッカーネット プレスパス」という20分番組(月曜日〜木曜日)があって、これも毎回見ているわけではないですが、コメンテーターがすげえ言いたい放題で面白いですw 外国の番組を吹き替えているんですが日本だとあそこまで言いたい放題な番組は無いでしょうね。関係者が見たら怒りそうですが部外者が見ている分には面白いです。ただ再放送は無いようですね。

ここまで書いて思い出しましたが、解説は粕谷さんが面白いです。これは好みが分かれるかもしれませんが、批判する場合も理由をちゃんと言うしその理由に結構納得できるので個人的には好きですね。むしろ実況の人が偉そうにプレーをdisってる方が違うかなーと思いました。