読者です 読者をやめる 読者になる 読者になる

ALBログのためのInput Plugin

ALB (https://aws.amazon.com/jp/blogs/aws/new-aws-application-load-balancer/) がリリースされてから半年以上経っているけれど、 明示的にサポートしているfluendプラグインがあまりないようなので書くことにした。

先人には、id:yomon8 さんのここのような記事があったけど、 自分の環境ではこれではなく以下のプラグインをずっと使っているため、こちらを修正することになる。

GitHub - winebarrel/fluent-plugin-elb-access-log: Fluentd input plugin for AWS ELB Access Logs.

とにかくテストが書けていないのであれだけど、一応動くものとしては以下のforkにpushしている。

github.com

ポイントとしては、id:yomon8 さんのを参考に、multiple_files_gzip_reader を使っている点だ。 あと、CLBとALBではログのフォーマットが変わっているのでそこの細かい対応をしている。

テストまで書ければ完璧なのだけど、pluginディレクトリに置いて起動し問題なく稼働しているしとりあえずOK。

簡単な修正で済んだ。

Elasticsearch + Kibana 5.0のDockerImage

www.elastic.co

ここ最近、会社のログ収集基盤周りばかりと向き合っているので、 上記エントリを参考に手元のKitematicで5.0-betaを起動するところまでの記録です。

環境: MacOS El Capitan 10.11.6, VirtualBox 5.0.16, Docker Kitematic 0.10.0, boot2docker 1.10.3

エントリ通り、以下のdocker-compose.ymlを用意。

---
version: '2'
services:
  kibana:
    image: docker.elastic.co/kibana/kibana
    links:
      - elasticsearch
    ports:
      - 5601:5601

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch
    cap_add:
      - IPC_LOCK
    volumes:
      - esdata1:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    environment:
      - -Xms2g
      - -Xmx2g

volumes:
  esdata1:
    driver: local  

できたら、コマンド一発で起動するはずが、Elasticsearch側コンテナでエラー

 % docker-compose up
・・・
elasticsearch_1 | max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144]
elasticsearch_1 | [2016-09-24T13:34:47,265][INFO ][o.e.n.Node               ] [RaJ4T2F] stopping ...
elasticsearch_1 | [2016-09-24T13:34:47,343][INFO ][o.e.n.Node               ] [RaJ4T2F] stopped
elasticsearch_1 | [2016-09-24T13:34:47,344][INFO ][o.e.n.Node               ] [RaJ4T2F] closing ...
elasticsearch_1 | [2016-09-24T13:34:47,383][INFO ][o.e.n.Node               ] [RaJ4T2F] closed
・・・

docker-machineのカーネルパラメータを変更する必要があります。(https://github.com/elastic/elasticsearch-docker)

docker-machine ssh
sudo sysctl -w vm.max_map_count=262144

デフォルト設定としたいなら、/etc/sysctl.confに書いときます。

再度起動コマンドを実行。

% docker-compose up
・・・
elasticsearch_1 | [2016-09-24T14:05:07,637][WARN ][o.e.d.s.g.GroovyScriptEngineService] [groovy] scripts are deprecated, use [painless] scripts instead
・・・
kibana_1        | {"type":"log","@timestamp":"2016-09-24T14:05:21Z","tags":["status","plugin:elasticsearch@5.0.0-beta1","info"],"pid":6,"state":"green","message":"Status changed from red to green - Kibana index ready","prevState":"red","prevMsg":"Elasticsearch is still initializing the kibana index."}
kibana_1        | {"type":"log","@timestamp":"2016-09-24T14:05:21Z","tags":["status","ui settings","info"],"pid":6,"state":"green","message":"Status changed from red to green - Ready","prevState":"red","prevMsg":"Elasticsearch plugin is red"}
kibana_1        | {"type":"log","@timestamp":"2016-09-24T14:05:21Z","tags":["license","info","xpack"],"pid":6,"message":"Imported license information from Elasticsearch: mode: trial | status: active | expiry date: 2016-10-23T17:22:33+00:00"}
kibana_1        | {"type":"log","@timestamp":"2016-09-24T14:05:21Z","tags":["status","plugin:xpack_main@5.0.0-beta1","info"],"pid":6,"state":"green","message":"Status changed from red to green - Ready","prevState":"red","prevMsg":"Elasticsearch is still initializing the kibana index."}
kibana_1        | {"type":"log","@timestamp":"2016-09-24T14:05:21Z","tags":["status","plugin:graph@5.0.0-beta1","info"],"pid":6,"state":"green","message":"Status changed from red to green - Ready","prevState":"red","prevMsg":"Elasticsearch is still initializing the kibana index."}
kibana_1        | {"type":"log","@timestamp":"2016-09-24T14:05:21Z","tags":["status","plugin:reporting@5.0.0-beta1","info"],"pid":6,"state":"green","message":"Status changed from red to green - Ready","prevState":"red","prevMsg":"Elasticsearch is still initializing the kibana index."}
kibana_1        | {"type":"log","@timestamp":"2016-09-24T14:05:21Z","tags":["status","plugin:security@5.0.0-beta1","info"],"pid":6,"state":"green","message":"Status changed from red to green - Ready","prevState":"red","prevMsg":"Elasticsearch is still initializing the kibana index."}
kibana_1        | {"type":"log","@timestamp":"2016-09-24T14:05:21Z","tags":["status","plugin:monitoring@5.0.0-beta1","info"],"pid":6,"state":"green","message":"Status changed from red to green - Ready","prevState":"red","prevMsg":"Elasticsearch is still initializing the Monitoring indices"}
・・・

groovyはdeprecatedで、painlessを使っていきましょうよというメッセージが。

先日のElastic社のブログで触れられていたPainlessというElasticsearch組み込みスクリプトのこと。

www.elastic.co

あとは、上記起動メッセージから、5.0から標準内臓のX-Packプラギンがロードされていることが確認できます。

ともかく正常に起動できてそうなので、http://192.168.99.100:5601にアクセス(192.168.99.100はdocker-machineのipアドレス)

すると、Shieldの認証フォームが。デフォルトの認証情報でログインします。

f:id:ujun:20160924232704p:plain

おお、なんかKibana4までしか触ったことがないとちょっと感動するくらい様変わりしたUIだ。。

せっかくなので、Painless書いてみるかと思っているところ。

mruby-hibariとmruby-rack-r3でWeb API フレームワークを書いた

まだ表現力は乏しいですが、一応mrubyのWeb APIフレームワークのfirst commitを書きました。

github.com

書き味は、CRubyのGrapeやその他のRESTライクなAPIを書くフレームワークのような感じになっています。mruby-hibarimuby-rack-r3など巨人の肩の上に乗ってる感が満載ですが。

試しにmod_mrubyで使ってみます。 まず、インストールは通常のようにbuild_config.rbに依存mrbgemを追記します。

MRuby::Build.new do |conf|

    # ... (snip) ...

    conf.gem :github => 'ujun/mruby-webapi'
end

そして、mod_mrubyをビルド => インストール。

cd /path/to/mod_mruby
sh build.sh
make && make install

例えば、以下のようなフックの設定を書き、mrubyスクリプトを参照します。

そして、参照先スクリプトは以下のように書きます。 Rack::WebAPIを継承して、応答する内容をmruby-rack-r3のDSLで書きます。

Apacheを起動して、定義したエンドポイントにアクセスしてみます。

curl http://localhost/fuga
#=> "Fuga World!"
curl http://localhost/hoge/22
#=> your id is 22

以上のように、直感的にmrubyでWebAPIを書けるようになりました。

mrubyでがっつりWebアプリケーションを書くことはあまりないかも知れませんが、WebAPIサーバならアリなんじゃないかと思えてきました。これと同じようなことが、以下のエントリの最後のほうにも書いてありました。

mod_mruby を使った Web アプリ - Qiita

mrubyでWebAPI、楽しそうなネタです。

NorikraのAPIを叩くmrbgemを書いて、mod_mrubyで使う

FluentdからNorikraにサーバの各種ログを流し込んで解析する、というのは、 割とよくあるNorikraのユースケースかと思います。

Norikra+FluentdでDoS攻撃をブロックする仕組みを作ってみた | Developers.IO

Fluentd + Elasticsearch + Kibana + Norikra+ Zabbixを使ってOpenStackのログ解析してみた | テクノロジーコラム | コラム・ブログ | NTTソフトウェア株式会社

ログ解析にNorikraを使ってみた - hase log

FluentdとNorikraで異常アクセス検知を行う | ピコもん開発ブログ

これらは、テキストとして出力されているログファイルをFluentdで取り込んで送信となりますが、 ApacheやNginxのアクセスログをNorikraに溜め込むのなら、 以下のmrbgemをmod_mrubyやnginx_mrubyに組み込んでおけばできますよ!

READMEにも書いていますが、CRuby版からのポートなので詳しいAPI情報はそちらをご参照ください。

github.com

インストール

インストールは、通常通りbuild_config.rbに依存gemを追記します。

git clone https://github.com/matsumoto-r/mod_mruby
cd mod_mruby
vim build_config.rb

sh test.sh
sh build.sh
make && make install

使ってみる

まずは、Norikaraサーバのほうに、アクセス時間メソッドURIを受け取る簡単なTargetおよびそれらをselectするQueryをていぎします。

f:id:ujun:20151008064126p:plain

次に、以下のような感じのmod_mrubyの設定を書いて、Apacheを立ち上げます。 情報を投げる先のNorikraサーバのホスト名とポート番号を指定して、アクセス時間/メソッド/URIをNorikraに投げています。

このように、イベントを投げる際にmod_mrubyで取れる種々の情報を利用することができるので、 様々な側面から分析する際に役立つように思っています。

あとは、なんらかの手段でApacheにアクセスしてみます。

curl http://localhost/

このあと、Norikraのコンソールからイベント数を確認!!

アクセス数分増加していました!

(以下のスクリーンショットでは、50。)

f:id:ujun:20151008071237p:plain

mod_mrubyは本当に多くのサーバ情報が手軽に取れますので、 mruby-norikra-clientと組み合わせて、工夫次第で興味深い分析ができるかも。

mruby-hibari on mod_mrubyが動いた。

mruby-hibariというmrbgemがあり、主要なWebサーバならなんとM/WレベルでRackテイストなWebアプリを書けるようです。

kysnm.hatenablog.com

kysnm.hatenablog.com

Apacheでやってみたところ、少し修正して、なんとか動きました!!

まず、通常通り以下のようにmod_mrubyをビルド。

$ git clone https://github.com/matsumoto-r/mod_mruby
$ cd mod_mruby
$ vim build_config.rb  # 依存gemにkentaro/mruby-hibariを追加します。
$ sh test.sh
$ sh build.sh

で、こうして生成されたmod_mruby.soモジュールをApacheにて利用します。

サンプルにならえば、以下のようにLocationとmrubyHandlerMiddleCodeでhello, world!!ができます。

curl!!

$ curl http://lcoalhost/rack_base?hoge=fuga
$ 

と思いましたが、なにも返ってきません。

でもH2OでもNginxでもうまくいってるっぽいんだけどと思いつつ、mruby.confで何をやってもうまくいってくれない。。

さんざん見直した末、以下が種明かしだと気付きました。

github.com

'nginx' || 'apache''nginx'と評価されるようですね。なので、

case engine
when 'nginx' || 'apache' # => when 'nginx'

となるため、caseの中に'apache'がマッチする条件がなくなってしまうということのよう。

複数条件に正しくマッチさせるには、

case engine
when 'nginx', 'apache'

とする必要があるようです。

このPR、送ったら即座にmergeしていただけました!

curl!!

$ curl http://localhost/rack_base?hoge=fuga
Hello, World!hoge: fuga

ちゃんと出ました。

自分が普段お世話になっているmrubyとそのmrbgems達のコントリビュータの末席を汚すことになり、鼻血がとまりません。

続 mod_mrubyでmrubyのビルドサーバを書いた

この記事で、mod_mrubyで数行書くとmrubyのビルドサーバが書けるということをやっていたのですが、@matsumotoryさんが以下のようなコメントをされていたので、対応をしました。

libmruby.flags.makはmrubyビルド時にlibmruby.aと同パスに生成されますが、以下のようなフォーマットになっていて、libmruby.aをスタティックリンクする際にこのファイルからオプションを得るみたいな用途で必要になります(参考:人間とウェブの未来 - マルチプラットフォームでmrubyを使ってHTTP通信する方法)。

MRUBY_CFLAGS = -g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings -I\"/path/to/mruby/include\"
MRUBY_LDFLAGS =  -L/path/to/mruby/lib
MRUBY_LDFLAGS_BEFORE_LIBS = 
MRUBY_LIBS = -lmruby -lm -lreadline -lncurses

この情報をサーバから返すにはどうしたらいいか。。。

現状、libmruby.aをレスポンスボディで返しているので、さらにボディに追加するのは難しいってことで、 無理やりレスポンスヘッダに設定することで乗り切ることにしました!!

生成されたlibmruby.flags.makを読み込み、(左辺)=(右辺)のような形でマッチした部分をキャプチャし、 (key, value) = (左辺, 右辺) としてレスポンスヘッダに突っ込んでいます。

@@ -8,6 +8,16 @@
 # build mruby 
 `cd /var/www/html/mruby; rake`
 
+# set values in libmruby.flags.mak to response headers
+File.open("/var/www/html/mruby/build/host/lib/libmruby.flags.mak") do |file|
+  reg = Regexp.compile("([^=]+)\s=(.*)\n")
+  file.each do |line|
+    if reg =~ line
+      r.headers_out[$1] = $2
+    end
+  end
+end
+
 # return the generated static library
 r.filename = "/var/www/html/mruby/build/host/lib/libmruby.a"

ということで、このサーバに対してcurlすると、レスポンスヘッダの中に欲しい情報が入るようになりました。 (以下の、MRUBY_CFLAGS, MRUBY_LDFLAGS, MRUBY_LDFLAGS_BEFORE_LIBS, MRUBY_LIBS)

$ curl -v -d "`cat /path/to/build_confg.rb`" -o libmruby.a http://hostname:8080/mruby-build

・・・

> POST /mruby-build HTTP/1.1
> User-Agent: curl/7.37.1
> Host: hostname:8080
> Accept: */*
> Content-Length: 848
> Content-Type: application/x-www-form-urlencoded
> 
} [data not shown]
* upload completely sent off: 848 out of 848 bytes
100   848    0     0  100   848      0     13  0:01:05  0:01:04  0:00:01     0< HTTP/1.1 200 OK
< Date: Mon, 21 Sep 2015 13:04:40 GMT
* Server Apache/2.4.7 (Ubuntu) is not blacklisted
< Server: Apache/2.4.7 (Ubuntu)
< MRUBY_CFLAGS:  -g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings -I\"/var/www/html/mruby/include\"
< MRUBY_LDFLAGS:   -L/var/www/html/mruby/build/host/lib
< MRUBY_LDFLAGS_BEFORE_LIBS:
< MRUBY_LIBS:  -lmruby -lm -lcrypto
< Last-Modified: Mon, 21 Sep 2015 13:05:44 GMT
< ETag: W/"6fbc80-520418a0948d2"
< Accept-Ranges: bytes
< Content-Length: 7322752
< 

・・・

@matsumotoryさん有難うございます!!

mod_mrubyでmrubyのビルドサーバを書いた

build_config.rbをそのままPOSTすると、mrubyの静的ライブラリを返してくれるサーバを書きました。

なにが嬉しいかといえば、ローカルにCRuby等を用意しなくても、以下を実行してlibmruby.aをコロッと作れるので、 開発環境に1台このビルドサーバを置いておけば、任意の場所からおもむろにmrubyを生成することができるのです。 あとは、これをGoに組み込んだりして遊ぶんです。

$ curl -d "`cat /path/to/build_config.rb`" -o libmruby.a http://hostname:8080/mruby-build

github.com

インストール&コンテナ起動

これは、setup.shを実行するだけです。

#!/bin/bash

git clone https://github.com/matsumoto-r/mod_mruby
cp -p ./Dockerfile mod_mruby
cp -p ./mruby-build.rb mod_mruby/docker/hook
cp -p ./mruby.conf mod_mruby/docker/conf

cd mod_mruby

docker build -t local/mruby-build-server .
docker run -d -p 8080:80 local/mruby-build-server 

まずmod_mruby本体をcloneしてきています。

このままmod_mrubyの手順にしたがってdocker buildすると、サンプルのapache設定ファイルとフックに渡すmrubyスクリプトを使ったDockerイメージが出来上がります。

ですが、ここでは、この2つをそれぞれ用意したmruby.conf, mruby-build.rbで置き換えしています。また、 Dockerfileも自前のもので置換しています。

置換したDockerfileについては、mod_mrubyオリジナルのものに、以下を追記したものになります。

# clone mruby 
RUN cd /var/www/html; git clone https://github.com/mruby/mruby
RUN chown -R www-data:www-data /var/www/html/mruby

# create the module directory and add files 
RUN mkdir /etc/apache2/mruby-build
ADD docker/conf/mruby.conf /etc/apache2/mruby-build/mods-available/mruby.conf
ADD docker/hook/mruby-build.rb /etc/apache2/mruby-build/mruby-build.rb

乱暴ですが、ドキュメントルートにmrubyのリポジトリをcloneしてきて、POSTされたbuild_config.rbを使ってここでmrubyをビルドすることを考えています。
※ もともとmod_mrubyの生成時に使っている/usr/local/src/mod_mruby/mrubyがあるはずですが、なんとなくそれはそれで手をつけたくはないのでそっとしときます。

次に、置換したmruby.confは以下です。

<IfModule mod_mruby.c>
  <Location /mruby-build>
    mrubyTranslateNameFirst /etc/apache2/mruby-build/mruby-build.rb
  </Location> 
</IfModule>

mrubyTranslateNameFirstフックにmruby-build.rbを渡しているだけです。

そして、/mruby-buildルートにアクセスが来た時実行されるmruby-build.rbが以下です。

r = Apache::Request.new()                                                                                

# store the sent mruby_config.rb to /var/www/html/mruby  
File.open("/var/www/html/mruby/build_config.rb", "w") do |file|
  file.write(r.body.to_s)
end

# build mruby 
`cd /var/www/html/mruby; rake`

# return the generated static library
r.filename = "/var/www/html/mruby/build/host/lib/libmruby.a"

Apache.return(Apache::OK)

POSTされたbuild_configを/var/www/html/mruby/build_config.rbに保存して、これを使ってmrubyをビルドしています。 最後に、r.filenameにlibmruby.aを指定することで、クライアントに静的ライブラリを返しています。

setup.shが最後まで動けば、コンテナが起動しているはずです。 あとはcurlで、libmrubyを生成してください。

$ curl -d "`cat /path/to/build_config.rb`" -o libmruby.a http://hostname:8080/mruby-build

このコンテナ、このままHerokuデプロイすることも考えたのですが、 POSTされたスクリプトをそのまま実行部に渡すというのがちょっと怖かったので、 やっぱり見送りました。。

ローカルでちょこちょこ遊ぶにはちょうどよい作りなんじゃないかと思います。

この調子で、mod_mrubyでどんどんオレオレサーバを量産していきたいです。