インフラに携わったことのない新米エンジニアが自作Kibanaプラグインを作らせていただきました.
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
初めまして、シャンタコンと申します。軽く自己紹介させていただきます。
名前は外国人ですが、生まれも育ちも日本です。都内の大学で学部2年生やってます。プログラミングをちょうど1年前に始めて、今はクリエーションラインのメンバーとして勉強と仕事に充実している日々を送らせていただいてます。Pythonが一番好きな言語です。
今回の記事は「Kibana?なんじゃそりゃ?」と言った新米未熟エンジニアの私が上長である木内さんに「Kibanaの自作プラグインで つ◯やきビ◯グデータを実装して欲しい! Twitterをバブルチャートで可視化してほしい!」と言われて実装を終えるまでをまとめたものです。
色々調べました
とりあえずKibanaで検索をしたり、色々調べていくとKibanaとはElasticsearchというNoSQL内のデータを可視化する際に最もパフォーマンスを発揮する手段やツールの一つのようですね。
ここで一つの疑問が湧きます、「Elasticsearchってなんだろう?」といった感じにものすごい手探りで色々調べました。
その結果、要求されていた案件である「Kibanaの自作プラグインでTwitterを可視化してほしい!」こういった流れで実装できるんじゃないかという結果に至りました。
さぁ,後は実装するだけ!
実装するだけ!と一言で済ませましたがもちろん実装するまでに結構な期間がかかりました。
具体的にどう実装したか
$ npm install系についての解説は省きますが、generator-kibana-plugin について解説いたします。
こちらのGitHubで挙げられているものなのですが、簡単に説明させていただくとYeomanで生成できるKibanaプラグインのテンプレートです。
まず、Yeomanをインストールするために、$ npm install -g yo を実行します。
次に、generator-kibana-plugin のNodeモジュールをインストールするために$ npm install -g generator-kibana-plugin を実行します。
これで、Kibanaプラグインテンプレートを生成する準備ができたので、以下のコマンドでテンプレートを生成します。
$ mkdir my-new-plugin $ cd my-new-plugin $ yo kibana-plugin
$ yo kibana-plugin を実行すると、プラグインの名前と解説を聞かれますので、入力してください。
入力できると、自動的にプラグインのテンプレートが生成されます。
生成直後の構造は以下のようになっています。
生成直後の内容は以下のようになっています。
index.js
import exampleRoute from './server/routes/example'; export default function (kibana) { return new kibana.Plugin({ require: ['elasticsearch'], uiExports: { app: { title: 'My New Plugin', description: 'An awesome Kibana plugin', main: 'plugins/my_new_plugin/app' }, hacks: [ 'plugins/my_new_plugin/hack' ] }, config(Joi) { return Joi.object({ enabled: Joi.boolean().default(true), }).default(); }, init(server, options) { // Add server routes and initalize the plugin here exampleRoute(server); } }); };
基本的には、上のindex.jsのinit()の中身を変更していくのですが、私はすでにMeCabを用いた形態素解析や、Elasticsearchへのクエリを、Pythonで実装していたためすべてJavaScriptで書き直すことはせずにinit()からすでに作成済みのPythonスクリプトを呼び出すようにしました。変更したinit()の内容は以下のようになっています。
init(server, options) { exampleRoute(server); var exec = require('child_process').exec; exec('gunicorn bubbleapi:app', function (error, stdout, stderr) { if(stdout){ console.log('stdout: ' + stdout); } if(stderr){ console.log('stderr: ' + stderr); } if (error !== null) { console.log('Exec error: ' + error);} }); }
gunicornとは
Gunicorn (Green Unicorn) は UNIX 向けのピュア Python 製 WSGI サーバです。pipコマンドでインストールすることができます。
$ pip install gunicorn
これで$ gunicorn <モジュール名>:<関数名>(モジュール名はソースファイルの.pyを抜いたものです。)と実行すればWebAPIサーバーとして動作させることができます。デフォルトではポート8000番でRESTコマンドを待ち受けるようになります。
私が作成したモジュール(bubbleapi.py)の最終系はbitbucketを参照してください。
Kibanaと連携させるためにソースファイルは kibana/ の中に保存しておく必要があります。
表示系のカスタマイズ
Kibanaプラグインの表示をカスタマイズするには、以下の3つのファイルを編集します。app.jsはAPIServerの呼び出しロジックを含む様々なロジックを記述し、index.htmlにAngularJSの作法に習ってapp.jsの中のコントローラを呼び出すように設定します。(この辺りはAngularJS, jQuery, ES6の知識が必要だったためかなり苦労しました。)
・ public/app.js ・ public/hack.js ・ public/templates/index.html
詳細な記述はここでは行いませんが、以下のようなことを行っています。
・ gunicornのサーバーにRESTコマンドで問い合わせをする。
・ 戻り値のjsonを受け取る。
・ d3.jsにjsonを与えてsvgエレメントを生成する。
・ 生成されたsvgエレメントをAngularJSのコントローラ経由でindex.htmlに埋め込む。
完成した各ソースコードはbitbucketを参照してください。
プラグインを含めたKibanaを起動する。
プラグインを含めたKibanaを起動するには、以下の2種類の方法があり、それぞれメリット・デメリットがあります。
1.一時的にプラグインを有効にした起動方法
この起動方法では、一時的にプラグインを有効にした状態でKibanaが起動します。Kibanaを終了すると、pluginも消えてしまいます。開発時のトライ&エラーをするときにはこちらの方法が良いでしょう。
起動方法は以下の通りです。
$ cd kibana $ npm start -- --no-ssl --plugin-path ../great_bubble
-- --no-sslの部分について解説させていただきます。
今回書いたプラグインはローカルホストの8000番にGETされるとJSONを返すWebサーバを立てたのですが、 Kibanaが通信するときは普段はhttpsのためエラーが起きます。
よって、今回のプラグインはこのようにオプションをつける必要があります。
2.プラグインを完全にKibanaに組み込んで起動する方法
インストール方法は以下の通りです。
$ git clone https://m-kiuchi@bitbucket.org/creationline/great_bubble.git $ cd great_bubble $ sh init.sh $ kibana/bin/kibana-plugin install file://<great_bubble.zipのpath>
ホームディレクトリ以下に「 /User/hoge 」のようなディレクトリ構造を作成した場合の起動例です。
/User/hoge | +- great_bubble/ <- $ git clone https://m-kiuchi@bitbucket.org/creationline/great_bubble.git したディレクトリ | +- kibana/ <- $ git clone https://github.com/elastic/kibana.git したディレクトリ | +- package/ <- $ sh init.sh をすると生成されるディレクトリ
コマンド実行例
この起動方法では、プラグインを完全にKibanaに組み込んだ状態で起動します。Kibanaを終了してもプラグインは消えません。プラグインを組み込んだ状態でKibanaを配布したい場合にはこちらの方が良いでしょう。
$ cd $HOME $ git clone https://m-kiuchi@bitbucket.org/creationline/great_bubble.git $ cd great_bubble $ sh init.sh $ cd .. $ ls great_bubble/ kibana/ package/ $ cd kibana $ ./bin/kibana-plugin install file://$HOME/package/great_bubble.zip $ npm start -- --no-ssl
とりあえず実装した結果です。
Pythonはほんのちょっとだけ書いたことがあるので形態素解析のライブラリを使ったり、APIサーバーを立てるのはそう難しくはなかったのですが、KibanaのメインとなっているJavaScriptについての知識は「Google Mapなどが出る前のとりあえずオフにしておく!」みたいなかなり昔の知識しかなかったのでものすごい大変でした。
初めてNode.jsに触るので$ npm install -g yoとか$ npm install -g generator-kibana-pluginとか$ yo kibana-pluginとか訳がわからないままに打っていたと思います...。(猛省中)
先ほど述べた実装も最終的な結果論としてこうすると良さそう!と今振り返ってできたものでもあります。
例えば最初の実装は
・ 自作KibanaプラグインページにアクセスされるたびにElasticsearchから格納されたツイートをPythonスクリプトを呼び出す。
・ 形態素解析してこの単語が何回出たかを記述している静的なJSONをローカルファイルとして出力する。
・ ローカルにあるJSONを元にページ内にグラフを描画する。
といった感じだったのですが、ローカルにあるJSONファイルが変更されるたびにKibanaがローカルファイルの中身が変わったことを検知して丁寧にサーバーを再起動させるので上の方法はダメだったりものすごい行き当たりばったりな実装だったと思います。
Elasticsearch内に格納されているツイートに対して形態素解析をかけて出てきた回数が多い名詞になればなるほどバブルが大きくなるグラフとなっています。日本のみに絞ったのですが,kokkaiや民進党がランクインしているあたりに今日は政治関係で何かあったのかなぁとかなんとなくつかめますね。
参考に、木内さんが以前のCL LabでネタにあげていたポケモンGoが出た日も取ったのでグラフを見てみましょう。
格納されているツイート数よりもポケモンって単語が出てきた回数が多くて、ものすごい印象に残った記憶があります。また、ポケモンの文字がバブルからはみ出ているのもこのインデックスだけだったり...
最後に
今回は比較的ゆる〜い記事を書きました。
Pythonを使いましたが、Node.jsで形態素解析を済ませたらもっと簡単な実装方法で済んでいたとか色々思っているところはあります。
しかし新米エンジニアにとって何かを完成させるというのは嬉しいことであり、Kibana内でグラフが描画できた時は今でも「勝った!!(?)」とか心の中でずっと喜んでいたのを覚えています。
簡単な自作Kibanaプラグインの例を挙げましたが、より理解を進めることによってElasticsearchやLogstashをより有効に活用した便利なプラグインができるかもしれません。
読んでいただいてうれしく思います、また筆を執ることがあればよろしくお願いいたします。