JubatusをCentOS5.8にインストールした奮闘記
JubatusをCentOS5.8にインストールしました。インストール中にはまりどこがあったので、個人的な備忘録代わりに記しておきます。
Jubatusとは?
Jubatusはオンラインで大規模・リアルタイムな機会学習が可能なOSSです。NTT研究所とPFIが開発を行っています。
Jubatus
現在のバージョンは0.3.0です。Jubatusはサーバ・クライアント構成になっており、文章の多値分類、レコメンデーション、回帰、特徴抽出、ネットワークグラフの分析などをリアルタイムに行うことができます。また、zoopkeeperを使って複数のサーバ側で分散して学習と分類・レコメンデーションを処理できます。
使用方法はJubatusサーバを立ち上げ、クライアントからRPCでmsgpack形式の設定や分類データを送付し、結果を受け取ります。クライアントはC++/Pythonなどが用意されています。
CentOS5.8へのインストール
Jubatusのインストールは複数の関連パッケージを事前にインストールしておかなければならず、yumやaptで一発という訳にはいかないようです。*1今回はJubatusのインストールシェルが公開されているのでこれを利用します。関連パッケージの入手とビルドの手順がシェルスクリプトにまとめられています。
そのまえに、CentOSの環境を整えます。
事前準備
CentOSに次のパッケージをインストールしておきます。
CentOS 5.8はPython2.4がデフォルトになっているため、Python2.7にバージョンアップしておきます。この時、PythonのSSLサポートをONにしておく必要があります。Googlecodeからre2ライブラリをmercurialでクローンするときに転けます。私はSSLをONにしてなかったので、install.shでre2ライブラリのクローン先をhttsからhttpにしました。またgccも4.1なので4.4以上にバージョンアップしておきます。
Jubatusインストーラのダウンロードとシェルスクリプトの修正
GithubからJubatusのインストールスクリプトをcloneして、ディレクトリに移動します。
$ git clone https://github.com/odasatoshi/jubatus-installer.git
$ cd jubatus-installer
このままではインストールに失敗してしまうので、installer.shの中身を修正します。修正内容はmsgpackのビルド部分で、CFLAGSとCPPFLAGSを追加することです。このオプションをつけずにビルドすることも可能ですが、msgpackのライブラリをリンクさせるときに失敗します。
cd ../msgpack-${MSG_VER} ./configure --prefix=${PREFIX} CFLAGS="-march=i686" CPPFLAGS="-march=i686" make make install
続いて、jubatus.profileの内容を.bashrcにコピーするか、source jubatus.profileを実行します。これで環境変数にインストールに必要なパスが追加されるはずです。.bashrcにコピーした場合もsource .bashrcを忘れないようにしてください。
Jubatus起動の確認
最後にJubatusサーバの起動確認をしましょう。以下のコマンドで多値分類器の起動を行います。
$ jubaclassifier --name=tutorial
またtutorialのコードが以下のGithubで公開されています。git でクローンしてください。
中のReadmeに従い以下のURLからテストデータをwgetして展開します。
$ wget http://people.csail.mit.edu/jrennie/20Newsgroups/20news-bydate.tar.gz
その後Jubatusのサーバ(jubaclassifier)を立ち上げた状態でtutorial.py を実行すると文章分類を開始します。
$ python tutorial.py
まとめ
とりあえず、Jubatus動いたので何か作ってみたいです。tutorial.pyもさほど難しくないので、文章分類なら簡単にできるのではないかと思いました。
mbedとDjangoでWebSocket!
mbedを買ったので、django-websocketで作ったアプリケーションにセンサーデータをWebSocketで投げ込んでみました。全体的な構成は次の図のような感じです。
各構成要素説明
Websocket Server
Mac OS X上にDjangoフレームワークとdjango-websocket 0.3.0を使ってechoサーバーを構成しています。ほぼ前々回のエントリーで紹介したアプリケーションと同じものです。django-weboskcetは前回のエントリーで紹介したRFC6455対応版です。
mbed
mbedに関しては細かい説明は省略します。知らない人はググって下さい。Arduinoとかそんな感じのラピッドプロトタイピング向けマイコンです。WebSocketのライブラリが提供(RFC6455対応)されているのでそれを使ってセンサー情報の値をサーバーに上げています。今回はセンサーとしてFRSの圧力センサーを用いています。
iPad
今回はクライアントとしてiPad*1を使いました。普通のWEBブラウザでも十分です。サーバから送られてくる値をもとにJavaScriptでグラフを描画しています。
そんなわけでYoutubeに実験動画をアップロードしてみました。
画面の一番奥がWebsocket ServerのMacBook proです。その手前にiPadを置いています。丸くぴょこっとしてるのが圧力センサになっています。この圧力センサーを指で握ると、それに合わせてiPadに表示されているグラフの表示が変わるのが解ると思います。グラフはSafari上にJavaScriptで描画しています。
最初はWebsocket通信を500ms周期でやっていたのですが、どうも途中でハングすることが多いので動画は900ms周期にしています。このあたりはDjangoの開発用サーバを使っていたり、作りが悪かったりいろいろあるのかなと。もう少し周期を短くできるといいかなと思います。
このようにWebSocketを使うと簡単にWEB上でリアルタイム通信を実現することができます。こういったマイコンからの情報をサーバに上げてWEBブラウザで可視化したいという用途や、逆にWEBブラウザからマイコンへ指示を送りたい場合にはかなり強力な仕掛けになると思います。AjaxやCommetでも良いのですが、いずれにしろタイマでの更新またはポーリング処理を行うためタイムラグが発生すると思います。(まあ、実用上問題なければ大丈夫でしょうが)またHTTPセッションを1リクエストごと(今回だと16bitのデータを一つ送るたびに)接続しなければ行けないので、多くの機器と通信したい場合はAjaxやCommetは不利になるでしょう。
とりあえず今週末はここまで!
今回は本質じゃないところで、BUFFALOのルータが無線LANと有線LAN間で通信出来ないというバグ持ちだったので、最初うまくいかずに一晩悩みました。最終的にルータのファームウェア更新で解決!
この仕組みを利用して、もうちょっとアプリケーション的なものを作ってみようかな。
django-websocket 0.3.0をRFC6455対応に改造しました。
表題の通り、前回の記事で紹介したdjango-websocket 0.3.0をRFC6455対応に改造しました。
crhome 16でもdjango-websocketのアプリケーションが動くようになります。これでSafari5.0ユーザー以外の方も試せます。WindowsやLinuxの方もどうぞ!実装した部分は基本的になところだけなので、細かいところの実装は目をつぶっています。また、ろくにテスト的なこともしてないので(簡単な動作確認程度はしています。)実際に使うには難ありです。
すでにdjango-websocket 0.3.0をインストールされている方はwebsocket.pyを上書きするだけでも動きます。
当分はちょくちょく機能追加やテストを書いていこうと思ってます。そもそも開発元にもコミットしたいなーとおもっていたり。せっかく書いたしね。いろんな人に使っていただければ幸いです。
フィジカルコンピューティングクラスタとかDjangoクラスタのみんなもWebSocketデビューするといいよ!
今回はPythonのWEBアプリケーションフレームワークであるDjangoでWebSocket通信を行う方法を紹介します。
はじめに
事の発端ですが、Arduinoなどのマイコンボードに接続したセンサの値をサーバーに集約してごにょごにょしたいな〜と以前から考えていました。
こういった場合、個人ユースでよく用いられるのが、CGIにPOSTやGETでデータを送りつける方法だと思います。しかし、わずか数バイトのデータを送るにヘッダや何やらくっつけたりしていたら、本来送りたい情報よりも付属のデータの方が多くなってしまいます。また、逆にサーバからマイコンボードなどにデータを送りたい場合、マイコン側からポーリングするといった方法を取らなくてはいけません。AjaxやCometの様な手法でも従来のWEBサーバの技術を応用して上記のようなことを実現しようとすると同様の問題にぶち当たると思います。もちろん一からソケットプログラムを書いてプロトコルを決めて・・・としても良いのですが手間だしあまり汎用性が無いように感じます。
そこで、今回思い立ったのが最近密かなブーム、、、、になってるどうかは判りませんが、従来のAjaxやCometに変わる技術として注目されているWebSocketを実現させようと思いました。
AjaxやCometとWebSocketとの違いや、そもそもWebSocketとの違いなどの話は他の詳しいサイトに譲りますが、簡単に言うと、AjaxやCometと違いWebSocketは最初にWebサーバに接続した際にセッションを接続させたままにしておき、TCPソケットのようにreadとwriteでサーバ/クライアントの双方向でデータのやりとりを行う事ができます。AjaxやCometは1リクエストごとにセッションを張り直し、さらにクライアント側が起因となる通信士かできませんでした(サーバ起因でクライアントに接続できない。)
問題のWebSocketですが、2012年の1月2011年の12月にRFCの最終草案がまとまりました。この草案が決まるまでに幾つかの仕様が存在しており、今回はRFCとはちょっと違った仕様のWebSocketとなります。*1
今回の構成
今回はMac OS X snow Lepperd上にPython2.6, Django, django-websocketをインストールしてDjangoフレームワークでWebSocketを実現します。
選定の理由は自分の主要言語がPythonで使えるWebフレームワークがDjangoだったからです。DjangoはそのままではWebSocketを扱うことができません。そこで、django-websocketという拡張モジュールをインストールします。この拡張モジュールですが、最終リリースが2010年の7月だったりバージョンが0.3.0だったりで怪しさが漂ってますがRFCの草案もまとまったことだしそのうちメンテされると信じてます。django-pythonのpipのページです。
それでは早速django-websocketが動くところまで設定しましょう。
環境構築
Mac PortでDjangoをインストール!
MacPortに関係する環境変数などは割愛させていただきます。最近はHomebrewなんかが人気ですが、そちらでもDjangoが入れば問題ないかと。
Python2.6とDjangoをさくっとインストールしてしまって下さい。あとpipが入っていない場合は併せてpipもインストールして下さい。
django-websocketのインストール。
django-websocketをMac環境にインストールする場合、PythonのCPAN的なpipを使ってインストールします。
django-websocketのインストール。
>||$pip install django-websocket|
permission denieとかで怒られるときはsudoでも先頭に付けときましょう。
ここまでで、WebSocketもしゃべれるDjangoな環境ができあがりました。
Djangoの設定
WebSocket用のプロジェクトをDjangoで作成しましょう。今回はプロジェクト名をwebsocketにします。Djangonoの設定がうまくいっていれば以下のコマンドでプロジェクトが作成できるはずです。
>||$django-admin.py startproject websocket|
permission deniedとか怒られるときはdjango-admin.pyのパーミッション確認して、実行権(x)が付いているか確認して下さい。
上記コマンドが正常に実行できたらwebsocketディレクトリに移動してsettings.pyを以下の様に編集します。
1.MIDDLEWARE_CLASSESにdjango_websocket.middleware.WebSocketMiddlewareを追加
MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django_websocket.middleware.WebSocketMiddleware' )
2.INSTALLED_APPSにdjango_websocketを追加
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', 'django_websocket' # Uncomment the next line to enable the admin: # 'django.contrib.admin', # Uncomment the next line to enable admin documentation: # 'django.contrib.admindocs', )
3.WEBSOCKET_ACCEPT_ALL= Trueを追加
適当なところに追加して下さい。
WEBSOCKET_ACCEPT_ALL= True
以上です。これでDjangoで作成したプロジェクトがWebSocketを受信できるようになります。
Djangoのアプリケーションを作成する。
websocketディレクトリ内で以下のコマンドを使いDjangoのアプリケーションを作成します。
>||$python manager.py startup echo|
そうするとアプリケーションechoのディレクトリが作成されるのですかさずechoディレクトリに移動して下さい。
適当なエディタでview.pyを開いて以下のコードを記述して下さい。
view.py
from django.http import HttpResponse from django_websocket import accept_websocket def modify_message(message): return message.lower() @accept_websocket def websocket(request): if not request.is_websocket(): message = request.GET['message'] message = modify_message(message) return HttpResponse(message) else: for message in request.websocket: message = modify_message(message) request.websocket.send(message)
django-websocketのページに載っているサンプルコードと同じものです。通常のGETリクエストが来た場合はHttpレスポンスを返しますが、websocketが来た場合は通信を確立してwebsocketでデータを返しています。django-websocketはこのようにDjangoのview.pyに記述されたメソッドをラップして、requestオブジェクトにwebsocketのメソッドを拡張させて利用します。非常に簡単にWebSocket対応サーバを構築することが可能です。
view.pyにURLをマッピングそしてサーバーを起動!
websocketディレクトリ内のurls.pyを開いて以下の様に記述しましょう。
urlpatterns = patterns('', # Examples: # url(r'^$', 'websocket.views.home', name='home'), # url(r'^websocket/', include('websocket.foo.urls')), url(r'^websocket/','websocket.echo.views.websocket') # Uncomment the admin/doc line below to enable admin documentation: # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: # url(r'^admin/', include(admin.site.urls)), )
上記設定を行い保存したら以下のコマンドを実行してServerを起動して下さい。
>||$python manage.py runserver --multithreaded|
こうすると開発用サーバーが起動します。
http://localhost:8000/websocket/?message=TESTにアクセスして下さい。
画面に"test"と表示されれば成功です。
あとはJavaScriptで適当なWebSocketクライアントを作りWebSocketを試してみて下さい。
実装方法は他のページに沢山載ってます。私は以下のページを参考にさせていただきました。
WebSocketによるクライアント=サーバー通信
注意点はSafari5.0.1を使うことです。
django-websocketが実装しているWebSocketの実装はdraft-hixie-thewebsocketprotocol-76またはdraft-ietf-hybi-thewebsocketprotocol-00と呼ばれるもののどちらかですおなじものらしいです。*2Wikipedia様のWebSocketのページにはこの2つのプロトコルに対応したブラウザとしてSafari5.0.1でした。他にもCrhome6とかありますが。。。ちなみに最新版のCrhome16とFireFox10で試しましたが動作しませんでした。*3
あと、DjangoのフロントとしてApacheを使う場合はmod_wisgiではなくmod_pythonを使わないと行けないようです。このあたりもそもそもWebSocketを扱えるWebサーバの構成が限られているようです。
最後に、今回自分の環境で作ったDjangoプロジェクトをbitbucketに公開しました。
bibucket
よかったら使って下さい。。。
追記:コードを修正して、2つのブラウザからアクセスした場合、ブラウザの更新がもう片方に反映されるようになりました。よりWebSocketの動きが判りやすくなったと思います。
Subversionのコミットログからバグを予測する!
元ネタは数日前にみつけたこれです。
http://www.publickey1.jp/blog/11/post_193.html
Googleではソースコードの修正履歴をもとに今後バグが発生しそうなソースを探しているという記事でした。基本的なアイディアは、「よくコミットされてるソースはたくさん修正してるから、バグもたくさん含まれているよね」ってとこでしょうか?
任意のソースコードに対して、コミットした日時を評価関数で計算して、コミット回数分の積をスコアとするというものです。面白そうだったのでPythonで簡単なスクリプトを作って普段使っているSubversionのリポジトリに対して計算してみました。
svn lookコマンドに「-q」「-v」オプションを付けた結果をファイルに保存しデータを作成しました。
$svn look -q -v [repository path] > svn_log_result.log
プログラムは主に以下の動作をします。
まず最初に、上記のログファイルからパスの部分を抽出し、計算対象となるパスを抽出しリスト化します。次に再度ログファイルを先頭から精査して、パスをキー値、コミット時間のリストをバリューとしたディクショナリを作成します。最後に、作成したリストとバリューを使い各パスごとのスコアを計算しました。計算式は上記のブログで紹介しているものと同じです。
実際に作成したPythonのスクリプトを以下に掲載します。1時間くらいで書いたものなので問題もありそうですが、とりあえず動きました。
以下にそのソースをのせてます。バグのコミットかどうか関係なく計算します。また、ソース以外のファイルが含まれていてもカウントしてしまいます。
prediction_bug.py
import time from math import exp import re import sys def main(): first_time = 0.0 file_path=sys.argv[1] file_p = open(file_path) file_line = file_p.readlines() file_p.close() #[path1,path2,path3,path4] # A: Add new file or directory # D: Delete file or directory # M: Modify file or directory # R: Replace file or directory # Make path list paths=set([line.strip()[2:] for line in file_line if line.strip()[0] in ['A','M','R','D']]) # Make dictionary key=path value=[commit_time1,commit_time2,commit_time3.....] data_reg = re.compile(u'[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}') revision_time={} time='' for line in file_line: if line.strip()[0] == 'r': match = data_reg.search(line.strip()) if not match is None: time = match.group(0) if line.strip()[0] in ['A','M','R','D']: if revision_time.has_key(line.strip()[2:]): #Update dictuonary revision_time[line.strip()[2:]].append((int(time[0:4]),int(time[5:7]),int(time[8:10]),int(time[11:13]),int(time[14:16]),int(time[17:19]))) else: #Append new path times=[(int(time[0:4]),int(time[5:7]),int(time[8:10]),int(time[11:13]),int(time[14:16]),int(time[17:19]))] revision_time.update({line.strip()[2:]:times}) # calc score for path in paths: score=0.0 commit_date = revision_time[path] if len(commit_date) > 1: first=commit_date[0] for date in commit_date: lt = lapse_time(first,date) score += bug_score(lt) print '%s,%f' % (path,score) def lapse_time((f_year, f_month, f_day, f_h, f_m, f_s),(b_year, b_month, b_day, b_h, b_m, b_s)): first_time = time.mktime((f_year,f_month,f_day,f_h,f_m,f_s)+(0,0,0)) bug_time = time.mktime((b_year,b_month,b_day,b_h,b_m,b_s)+(0,0,0)) now_time = time.time() delta_fix_time=float(now_time-bug_time) delta_alive_time=float(now_time-first_time) return float(delta_fix_time)/float(delta_alive_time) def bug_score(lt): return 1.000/(1.000+exp(-12.000*lt+12.000)) if __name__ == '__main__': main()
以下の様なコマンドで実行して下さい。
$python prediction_bug.py svn.log > result.csv
結果をexcelなどで開き、スコアの降順に並べたところ、確かに普段修正が多いファイルが上位に来ました。当たり前と言えば当たり前なんでしょうが、普段感覚的にしか感じない修正の回数などを可視化できて面白いと思いました。バグが落ち着いて来るとスコアが下がるはずなので、そのあたりも見てみると面白いかもしれません。
コードの複雑度などのメトリクスもありますが、実際に修正が多いファイルを見つけるということは、実際に使われている or バグが発見されやすいコードを発見するのに役立つと思います。また、簡単に計測ができてしまうのも魅力的かもしれません。また、この手法ならソースコードだけでなく設計書などのドキュメントも計測できそう!という感じもします。
リファクタリングの目安などに利用すると面白いと思います。もう少しコードをきれいにしてバグりそうなソースを発見するのに利用してみたいです。
せっかく構成管理のツールを利用しているなら、こういったリポジトリの情報を分析してみると言うのも良いかもしれません。プロジェクトの改善につながる情報が落ちているかも。定量的な分析ツールはSubversionではStatSVNというツールがあるのですが、GitとかMarcurialはそういうものがあるのかな。。。個人的には集合知的な手法でリポジトリを分析すると面白いのではと思いますがなにが?という疑問符も。
【FRISK HACKシリーズ】FRISKポインタ
最近はもうやってないのですが、夏頃、会社でFRISKを馬鹿食いしていた時期がありました。
その結果としてFRISKのケースが会社の机に山のように貯まっていったのです。ゆうに20個ほど
貯まっているのですが、捨てるのももったいなく、かといってこのまま残していてもな〜。。。
- 出版社/メーカー: クラシエフーズ
- メディア: 食品&飲料
- クリック: 34回
- この商品を含むブログ (8件) を見る
そこで、考えたのがFRISK HACKシリーズです。あのFRISKのケースを使っていろんなものに応用しよう
というこの企画。まあ、ネタとしてはいろんな方がやられてますし、最近ではFRISKケースピッタリの
基盤なんかも売られていたりして、FRISKケースに仕込みをするのはかなり2番煎じな感じもしますが、
気にせずやっていきます。
フリスク基板:http://www.sunhayato.co.jp/products/details.php?u=1503&id=07017
今回作成したのはこのFRISKポインタ
外見はただのFRISKケースですが、、、、
ふたを開けると、中からはFRISKではなくレーザーが発射されます。
中身はこうなっています。簡単な作りですね。蓋を開けると跳ね上がりスイッチが跳ね上がり、スイッチが
ONとなります。レーザーモジュールと電池とスイッチは直に接続してます。レーザーモジュールは秋月電子で
売ってます。平型である必要はないのですが、取り付けがしやすいです。3Vの電池を繋ぐだけで動きます。
3V以上だとモジュールが壊れるそうなので注意!電池であれば問題ないかと。。。
プレゼンのときにおもむろに胸ポケットからFRISKケースを取り出す。プレゼン前にFRISKを食べて心を
落ち着かせるのかと思いきやレーザーポインタだったという、うけること間違い無しですね!
お仕置き系eXtreamFeedbackDeviceの開発
Jenkinsネタが続いてますが、本来僕のフィールドはもっと別のレイヤーにあるはず!
はい、この記事は、Jenkins Advent Calendar 10日目の記事です。
思えば初日の川口さんに始まり、昨日のleather_soleさんの記事に至るまで皆さんレベル高いです。そんな中でこんな記事を投稿するのも気が引けるのですが、空気読まずに投稿しちゃいます。
こんなん作ってみました的な記事なのですが、箸休め程度にみていただければ幸いです。
それではいってみましょう、10日目の今日はお仕置き系eXtreamFeedbackDeviceの開発です。
事の発端は現在のチーム内で、XFDを導入したいという話になりました。XFDはJenkinsなどのCIツールがビルドに失敗すると、回転灯などを光らせて瞬時に知らせる物理的なデバイスの事を指します。こうすることで、失敗したビルドの修正を素早く行うことを目的としているのですが、単純にけたたましい音や光が点滅するだけでは面白くないよねということになりました。
そこで、今回はビルドが失敗すると可愛いペナルティがある「お仕置き系」XFDを試作したので紹介したいと思います。
以下の写真が今回作成したお仕置き系XFD試作1号、通称サンダーボルトです。ビルド失敗した場合に先端の電極から凶悪な電気が流れ、装着者の体を痺れさせてビルドの失敗を知らせてくれます。
この装置の構成は以下の図のようになっています。
電圧発生部分には市販の「電子びっくり箱2」のキットを流用しました。秋葉原や日本橋などの電気街や、通信販売でも簡単に手に入る代物です。
[rakuten:edenki:10017455:detail]
この電子びっくり箱の電池を繋ぐリード線の+側をリレーを介して接続します。リレーの駆動側はトランジスタを介してPepperと呼ばれるプロトタイピング向けのマイコンを接続しています。リレーを使用する理由は、Pepperを経由して供給されるUSBの電源と、電子びっくり箱で使用する電池を電気的に分離するためです。GNDが共通になってたりすると最悪USBがお亡くなりになります。Pepperの出力ではリレーを直接駆動できないので、トランジスタでPepperの出力を増幅してリレーを駆動します。マイコンの出力では一般的な回路構成です。
Pepperについてはmorecatさんのページをご覧下さい。PepperがあればXFD導入の敷居をかなり下げられるはずです。興味が出た方は是非購入してみて下さい。1000円程度と安いです。
Pepperの説明:http://web.mac.com/kuwatay/morecat_lab./Pepper.html
- メディア: エレクトロニクス
- 購入: 1人 クリック: 35回
- この商品を含むブログ (3件) を見る
PepperとパソコンをUSBで接続し、Jenkinsからのビルド失敗の指令をPepper側に伝えます。ここでJenkinsからのビルド失敗の指令をどうやってPepperに伝えるのかというところなのですが、今回はJenkinsがビルドに失敗した場合、チームのIRCサーバーにbuildbreakの文字列が流れるようになっています。そしてIRCを監視するプログラムがそのメッセージを読み取ると、Pepperに出力をHighにするように指令を出し、リレーが動作し、電気が流れる仕組みとなっています。
このあたり、うまくJenkinsからマイコンの出力を操れるプラグインなんかがあればXFD作りも流行るのでしょうか?
あるのかな?つくろうかな・・・。
あ、Jenkins のアドベントカレンダーなのにJenkinsのキャプチャが一つも出てこない!!
さて明日はwadatkaさんです!