Norikra 0.1を使ってみた

Norikra 0.1を使ってみた。

前回、Norikraで遊んでみたの記事を書いたときは0.0.4だったけど、Norikra 0.1リリースということで、記事を書き直すことにした。[Norikraで遊んでみた](http://qiita.com/harukasan/items/957012833e5a361f7aa1)は既に古くなっているので参考にしないようにしよう。

前に試したのが0.0.4だったので、変わったところをまとめるとつぎのような機能がついててすごい。

- クエリの追加/削除
- BootstrapなWeb UI
- UDF
- デーモン化できる
- [公式ドキュメントページ](http://norikra.github.io)

UDFとかも便利そうだけど、そこまで説明する余裕はない。ということで、今回はFluentdとNorikraを連携して使ってみよう。

## Norikraを使ってみよう

今回はFluentdとNorikraを連携させて簡単なStream Processingをやってみる。環境はこんなかんじで、この記事もすぐに古くなることであろう。

| **product** | **version** |
|:--|:--|
| MRI (CRuby)       | 2.0.0-p353 |
| JRuby             | 1.7.9 |
| Norikra           | v0.1.0 |
| Norikra::Client   | v0.1.0 |

なお、今回のサンプルコードは以下のリポジトリで公開している。

- [harukasan/hello-norikra](https://github.com/harukasan/hello-norikra)


## JRubyをインストールする

Norikraを動かすにはJRubyが必要である。いつも使っているrbenvでインストールする（RVMでも大丈夫だと思う）。

```
$ rbenv install jruby-1.7.9
Downloading jruby-1.7.9.tar.gz...
-> http://dqw8nmjcqpjn7.cloudfront.net/b2e44f1f44837c07068ee453a89f4b55
Installing jruby-1.7.9...
Installed jruby-1.7.9 to /Users/harukasan/.rbenv/versions/jruby-1.7.9

```

## 作業用スペースを作る

FluentdとNorikra、それぞれの作業用ディレクトリを作る。

```
$ mkdir -p hello-norikra/{norikra,fluentd}
```

FluentdはMRI、NorikraはJRubyで動かす。rbenvには`.ruby-version`ファイルを見てプラットフォームをスイッチする機能があるため、これを利用して切り替える。設定には`rbenv local`コマンドを使う。

```
$ cd ~/hello-norikra/fluentd
$ rbenv local 2.0.0-p353 # 使用するRubyをRuby 2.0にする
$ ruby -v 
ruby 2.0.0p353 (2013-11-22 revision 43784) [x86_64-darwin13.0.0]

$ cd ../norikra
$ rbenv local jruby-1.7.9 # 使用するRubyをJRuby 1.7.9にする
$ ruby -v
jruby 1.7.9 (1.9.3p392) 2013-12-06 87b108a on Java HotSpot(TM) 64-Bit Server VM 1.6.0_65-b14-462-11M4609 [darwin-x86_64]
```

## Norikra、Fluentdをインストールする

次にNorikra、Fluentdをインストールする。NorikraとFluentdの管理にはGemfileを使う。Norikra、Fluentdは活発に開発されているが、Gemfileを使えばバージョンを固定することができる。それぞれ動作するプラットフォームが違うが、`platforms`ブロックを使えば1つのGemfileに書くことができる。

```rb:~/hello-norikra/Gemfile
source "https://rubygems.org"

platforms :ruby do
  gem "fluentd"
  gem "fluent-plugin-norikra"
  gem "fluent-plugin-dummydata-producer" # テスト用に使っている。後述
end

platforms :jruby do
  gem "norikra"
end

```

それぞれの作業ディレクトリに移動して、`bundle install`を行う。まずは、Fluentdをインストールする。今回は依存ライブラリが他から入り込んだりしてしまうのを防ぐために`--path`を指定しているが、指定しなくても特に問題なく動作する。その場合はインストールが終わった後に`rbenv rehash`しておく。

```
$ cd ~/hello-norikra/fluentd
$ gem install bundler # 既にbundlerをインストールしてるなら不要
Successfully installed bundler-1.3.5
Parsing documentation for bundler-1.3.5
1 gem installed

$ bundle install --path=~/hello-norikra/bundle
Fetching gem metadata from https://rubygems.org/..........
(..snip..)
Installing fluentd (0.10.41)
Using bundler (1.3.5)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
```

> 今bundle installを実行するとfluent-plugin-norikraが依存しているmsgpack-rpc-over-httpが依存しているrackと、norikraが依存しているrackのバージョンが違うせいでエラーになるようです。
> そのうち修正されると思いますが、とりあえず、一旦norikraをgemfileからコメントアウトしてbundle installした後、コメントを戻すとうまくインストールできます。

続いてNorikraをインストールする。

```
$ cd ~/hello-norikra/norikra
$ gem install bundler  # 既にbundlerをインストールしてるなら不要
Fetching: bundler-1.3.5.gem (100%)
Successfully installed bundler-1.3.5
1 gem installed

$ bundle install --path=~/hello-norikra/bundle 
Fetching gem metadata from https://rubygems.org/..........
(..snip..)
Installing norikra (0.1.2)
Using bundler (1.3.5)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.

```

## Norikraを起動してみる

とりあえずそのまま起動してみよう。正常に起動することが確認できたら`Ctrl-c`でとりあえず終わる。

```
$ cd ~/hello-norikra/norikra
$ bundle exec norikra start
2013-12-09 23:18:12 +0900 [INFO] : thread configurations, engine:{:inbound=>{:threads=>0, :capacity=>0}, :outbound=>{:threads=>0, :capacity=>0}, :route_exec=>{:threads=>0, :capacity=>0}, :timer_exec=>{:threads=>0, :capacity=>0}}, rpc:{:threads=>2}, web:{:threads=>2}
2013-12-09 23:18:12 +0900 [INFO] : logging configurations, level:nil, dir:nil, filesize:nil, backups:nil
2013-12-09 23:18:12 +0900 [INFO] : Loading UDF plugins
2013-12-09 23:18:12 +0900 [INFO] : RPC server 0.0.0.0:26571, 2 threads
2013-12-09 23:18:12 +0900 [INFO] : WebUI server 0.0.0.0:26578, 2 threads
2013-12-09 23:18:12 +0900 [INFO] : Norikra server started.

^C
2013-12-09 23:18:31 +0900 [INFO] : Norikra server shutting down.
2013-12-09 23:18:31 +0900 [INFO] : Norikra server stopped.
2013-12-09 23:18:31 +0900 [INFO] : Norikra server shutdown complete.
```

## Norikraをデーモンとして起動する

起動できるのはわかったので、続いてデーモンとして起動する。

```
$ bundle exec norikra start -Xmx1g -Xms1g -Xmn256m -XX:+UseConcMarkSweepGC \
   -d --pidfile=norikra.pid --stats=norikra.json --logdir=./tmp/ --micro
```

なお、Gitリポジトリをcloneしてきた人は`./script/norikra start`で起動できるはずだ。

NorikraはJRubyで書かれたJavaアプリケーションで、javaのオプションも指定できるので、ヒープサイズとか指定しておこう。GCはとりあえずCMSにしておけば良さそう。G1GCでも一応動くようだ。

Norikraのオプションとしては次のオプションがある。ロングオプションを使うか短縮形を使うかは好み。

| **オプション[ロングオプション]** | **説明** |
|:--|:--|
| -d, [--daemonize]       | デーモンとして起動する |
| -p, [--pidfile=PIDFILE] | PIDファイルを指定する |
| -l, [--logdir=LOGDIR]   | ログディレクトリを指定する |
| [--outfile=OUTFILE]     | 標準出力のリダイレクト先を指定する |
| -s, [--stats=STATS]     | statsファイルを指定する |

statsファイルというのはNorikraの設定情報とかクエリ、ターゲット情報をJSONに出力してくれるもので、これを指定しておかないと再起動したタイミングで設定したクエリ等が消えてしまう。必ず指定しておくようにしよう。デーモンとして起動したらあとはmonitとかsupervisordとかお好きなプロダクトで監視させよう。

## Fluentdからデータを流してみる

ではNorikraにデータを流してみよう。Norikraにデータを流すには[fluent-plugin-norikra](http://github.com/norikra/fluent-plugin-norikra)を使う。Fluentdの設定はこんな感じになる。

```apache
# 外からログを受ける時向け
# <source>
#   type forward
# </source>

<source>
  type dummydata_producer
  tag input.dummy
  rate 100
  
  dummydata0 {"status":200, "request_time": 0.1, "request_uri":"/index", "remote_addr": "127.0.0.1"}
  dummydata0 {"status":200, "request_time": 0.4, "request_uri":"/entries/1", "remote_addr": "127.0.0.1"}
  dummydata1 {"status":400, "request_time": 0.2, "request_uri":"/entries/2", "remote_addr": "127.0.0.1"}
  dummydata1 {"status":500, "request_time": 0.3, "request_uri":"/entries/3", "remote_addr": "127.0.0.1"}
</source>

<match input.*>
  type norikra
  norikra localhost:26571
  
  target_map_tag true
  remote_tag_prefix input
</match>

```

今回はダミーデータを生成するために[fluent-plugin-dummydata-producer](http://github.com/tagomoris/fluent-plugin-dummydata-producer)を使っている。ずらずらと書いたが重要なのは下の5行だけで、この5行でNorikraに出力してくれる。それぞれのパラメータは次の様な意味がある。

| **ディレクティブ** | **説明** |
|:--|:--|
| target_map_tag | タグ名をターゲット名にする |
| remove_tag_prefix | タグのプレフィックスを除去する |

設定したら適当に起動しよう。Gitリポジトリをcloneしてきた人は`./script/fluentd start`で起動できるはずだ。

```
$ bundle exec fluentd -c fluent.conf -d fluentd.pid -o ./tmp/
```

起動したらログを確認しておこう。


## Web UIからクエリを登録する

ここまで説明してなかったが、Norikraには簡単なWeb UIがついていてWebからステータスを見ることができる。[http://localhost:26578](http://localhost:26578)を開いてみよう。

![Norikra-GUI.png](https://qiita-image-store.s3.amazonaws.com/0/5218/2fe5493e-6051-c017-f282-d988f53747f2.png)

こんな感じの画面が開いたはずだ。ヒープメモリの容量や今処理しているデータの量などが表示されている。Fluentdがうまく動いていれば、EVENTS INPUT、EVENTS PROCESSEDが増えているはずである。ターゲットの内容は一番下のところで指定できる。show fieldsをクリックすると、スキーマが確認できるはずである。

試しにクエリを追加してみよう。クエリの追加はAdd Queryから行うことができる。

![131214-0001.png](https://qiita-image-store.s3.amazonaws.com/0/5218/9ce05f06-4b8e-fca1-eae2-7075f312c970.png)

追加するクエリは次の様な感じである。

```
SELECT count(status) AS status_count FROM dummy_data.win:time_batch(1 min)
```

Nameはクエリ名を入力する。これは後でFluentdから結果を取得するときとかに使う。Groupは今のところ使い道はなさそうだけど、そのうちなんかできるはず。

ぱっと見SQLっぽいがFROMの部分が特徴的だ。FROMにはターゲット名を指定する。さっきからカジュアルにターゲットという用語を使っているが、これはRDBでいうテーブルのようなものである。`dummy_data`はターゲット名だがその後に`.win:time_batch(1 min)`というのがついている。これはウインドウを指定するもので、何分間のログに対してクエリを実行するかを指定する。ウインドウの指定には次のものがある。

| **ウインドウ** | **説明** |
|:--|:--|
|win:time(1 min) | 現在から1分前まで |
|win:time_batch(1 min) | 1分ごと |
|win:length(N) | 現在からN件前まで |
|win:length_batch(N) | N件ごと |

`time(1 min)`と`time_batch(1 min)`の違いはわかりづらいけど、`time_batch(1 min)`は1分ごとに区切って処理するのに対して、`time(1 min)`は1分間のウインドウを1秒ずつスライドしながら処理していく感じだ。ちなみにこれはSQLではなくて、EPLというもので、他にもいろんな書き方ができる。詳しくはNorikraが使っているEsperというエンジンのリファレンスを読もう。読みづらい。

- [Chapter 5. EPL Reference: Clauses](http://esper.codehaus.org/esper-4.10.0/doc/reference/en-US/html/epl_clauses.html#epl-select-syntax)

他にも次の様なクエリが書けて、fluent-plugin-numeric-monitorの代わりに使えたりする。

```
SELECT min(request_time), max(request_time), avg(request_time) FROM dummy_data.win:time_batch(1 min)
```

結果データはWeb UI上から確認できる。ちゃんと書いたクエリが実行されてるのがわかるはずだ。

## Fluentdで結果データを取得する

Web UIからちゃんと計算できていることはわかった。とはいえWeb UIから確認するのも面倒だし、NorikraのAPIをたたくのも面倒だ。Fluentdから投げてFluentdで取得したい。こういうときはfluentd.confに以下の設定を追加しよう。

```apache
<source>
  type    norikra
  norikra localhost:26571
  <fetch>
    method     sweep
    tag        query_name
    tag_prefix norikra.query
    interval 5s
  </fetch>
</source>

# とりあえずファイルに結果を出力する
<match norikra.query>
  type file
  path tmp/norikra-result.log
</match>

```

このsweepメソッドというのはすべてのnorikraの結果を取得するものである。他のオプションの意味はだいたい次の様な感じだ。

| **ディレクティブ** | **説明** |
|:--|:--|
| tag query_name | クエリ名をtagにする |
| tag_prefix | tagに指定したprefixをつける |
| interval 5s | Norikraから取得する時間間隔 |

設定を書き終わったらfluentdの設定を読み込みなおすか、再起動しよう。リポジトリを使ってる人は既に設定が入っている。

指定したファイルを見るとちゃんとログが流れているはずだ。今回はログに書いたけどGrowthForecastとかに投げるようにすると、すごく簡単にグラフで結果が見えたりして便利である。

## まとめ

Norikraかなり使えるレベルになってきたし、弊社でも動いていたりする。とはいえまだ、0.1だしこれからどんどん進化していくことだろう。

Fluentdの設定をしなくても簡単にクエリを追加したりできて、とても便利なのでどんどん使われて行けば良いと思う。まだまだ運用周りのノウハウは少ないので、これから蓄積していきたいところである。