その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

Ukigumo入門 ― 2014年スタイル

とりあえずデモサイトを示しますので適当に見て下さい.
http://ukigumo.moznion.net/

さて今回はゆるふわCIシステムであるところのUkigumoのナウいスタイルについて説明しようと思います.
ここ最近ではUkigumo::AgentというAgentサーバが存在しており,これを使うとまあ便利なんですけれども,ドキュメントが少ない為か *1 あまり利用されている事例を見かけませんので,それらも踏まえて解説したい感じです.

まずUkigumoとは何か

CIシステム.Perl製.
Perl製だが,もちろん他言語のプロジェクトでも使える.

Travisのようにサービスとして提供されている感じではなく,自前でインストールして使う.
多分,感覚としてはJenkinsに近いと思うが,そこまで複雑ではなく,シンプル.
基本的に,「テストの実行及びその結果の取得」と「テスト結果の保存」が主な仕事.詳細は後述.

Ukigumoの登場人物

Ukigumo::Server

Clientから送られてくるテスト結果を保存する為のサーバ.
保存しているテスト結果をブラウザから閲覧するためのウェブアプリケーションもサポートしている.
端的にいうとCPAN Testers Reportsみたいな存在.

Ukigumo::Client

実際にテストを走らせて,Serverにその結果を送る存在.
例えば,Ukigumo::Clientのインスタンスを作って,$client->run()などと実行してやると,テスト対象のリポジトリをclone or pullしてきて,実際にテストを走らせる.そしてその結果をServerに対してPOSTしてその儚いライフサイクルを終える.

Ukigumo::Agent

Agentサーバ.テストランナーサーバとも言える.
何らかのイベントが発火して,Agentにタスクをenqueueする (AgentサーバのAPIにPOSTする) とAgentが適宜Clientのインスタンスを作って,そのClientのインスタンスにタスクを振って処理を行わせる,という事が出来る.
中にジョブキュー的な機構を持っているので,「次から次へとClientに処理を実行させた結果、ハングして処理を詰まらせる」みたいな悲劇を防ぐことが可能.
実際の例を挙げると,GitHubのPushのWebHooksにAgentサーバのAPIのアドレス (GitHub WebHooksだと/api/github_hook)を登録しておくと,リポジトリにpushされる度にイベントが発火してWebHooksでリポジトリの情報がAgentサーバのAPIにPOSTされる (つまりenqueueされる) ので,リポジトリにpushされる都度テストを走らせる,みたいなことが出来るようになる.

構成の今昔

かつての構成

かつてはAgentサーバを使わず,下図のようにServerとClientのみでまかなう構成が多かった.
Cron等で定期的に実行するという素朴な感じ.

f:id:moznion:20140503134026p:plain

ここ最近の構成

Agentサーバを使って,Agentが何らかのイベントを受け取ったらClientを立ち上げてジョブを走らせるようにする.
例えばリポジトリにpushされたらそのイベントをAgentに投げて,その都度テストを回してやるという感じ.

f:id:moznion:20140502180827p:plain

最近のインストールの方法

最近ではUkigumoシリーズはCPANにアップされているので,それを使うと楽.今回はこの方法を紹介する.
GitHubに上がっているもの *2 をcloneしてcarton等で実行するのでも良い.そういうことしたい人は説明しなくても出来ると思うので説明略!

なお今回の説明では,簡単化の為にUkigumo::ServerとUkigumo::Agentを同じマシン上で立ち上げるものとする.もちろんそれぞれが別のマシンに存在していて良い.

インストールは至って簡単,以下のコマンドを叩くだけで良い.

$ cpanm Ukigumo::Server Ukigumo::Client Ukigumo::Agent

最近の構成で実際に使ってみる

1. サーバ群を立ち上げる

なにはなくともServerとAgentを立ち上げる必要があるので立ち上げる.

Server
$ ukigumo-server --port=2828 --host=127.0.0.1 --max-workers=4 --config=/path/to/config.pl

--port--hostはそれぞれサーバを立ち上げるポートとホスト.
--max-workersは動かすワーカーの最大数.--configについては後述.
詳細はukigumo-server --helpを参照のこと.
とりあえずこうすると,localhost:2828にUkigumo::Serverが立ち上がる.

Agent
$ ukigumo-agent --port=2829 --server_url=http://127.0.0.1:2828 --work_dir=/tmp/

--portはAgentサーバを立ち上げるポート番号.
--server_urlはテスト結果をPostするサーバのアドレス (つまり上で立ち上げたUkigumo::Serverのアドレス) .
--work_dirはClientがリポジトリから実際にコードをcloneしてきてテストを走らせる為に割り当ててあげるワークスペースのディレクトリ.
こういう塩梅で実行するとUkigumo::Agentも立ち上がる.

2. テストしたいリポジトリのルートに.ukigumo.ymlを置く

.ukigumo.ymlというのは,.travis.ymlのようなもので,Ukigumo::Clientはプロジェクトルートに置かれているこのファイルを見てよしなにもろもろ設定をしてくれる.
以下が例:

before_install:
  - "cpanm -L $HOME/.ukigumo/ukigumo-server/extlib --installdeps --with-develop -n ."
install: "ln -s $HOME/.ukigumo/ukigumo-server/extlib ./extlib"
script: prove -lrv -Iextlib/lib/perl5 t

before_installと言うのはinstallの前段階で走るコマンドで,installはその名の通りインストール時に走るコマンド.scriptでは実際にテストを実行するコマンドを指定する.
あけすけに言うと,だいたい.travis.ymlでできることが出来る.現状セット出来るオプションは以下のとおり.実行される時系列順に示している.

  • before_install
  • install
  • before_script
  • script
  • after_script


<追記>
すっかり忘れていた.
.ukigumo.ymlではこれ以外にもnotificationsという項目が設定できて,これが便利.
テストの結果を任意のNotifierに渡して,通知を飛ばすということができる.
例えば,以下のように書いてやると,GitHubのStatuses API *3 を使ってテスト結果をGitHubのcommitに反映させる,みたいなことが可能.

notifications:
  guthub_statuses:
    - api_endpoint: https://api.github.com
      access_token: __ACCESS_TOKEN__

他にも色々な通知 (とは言え限定的なのだけれど……) に対応している.Ukigumo::Client::Notify::*というモジュールがそうした通知に対応している *4.<追記ここまで>


ここらへん,現状公式のドキュメントが無いので書く必要を感じている.

とにかく,こういうものを置くと,Ukigumo::Clientは上手く処理してくれる.

3. (例えば) GitHubのWebHooksに登録してやる

リポジトリにpushされたらその都度テストが走る,みたいな事をさせたいのでGitHubのWebHooksを設定する.

リポジトリの設定のWebHooksのところに,Ukigumo::AgentのサーバのAPIに対するアドレスを突っ込んでやる.
例: http://your.ukigumo-agent.net/api/github_hook

push以外のPayloadが来てもしょうがないので,Push Onlyで問題ない.

(今回はGitHubを例に採っているが,別のものでも構わない.その場合,登録するアドレスは`http://your.ukigumo-agent.net//api/v0/enqueue`のようになると思う *5.しかしここ最近のUkigumoはGitHubに寄せていて,「GitHubを使っていると更に便利!」という風になっているという旨を記す)

4. リポジトリにpushする

こうすると,GitHubリポジトリにpushされるや否やAgentサーバがそのHookを受け取り,Clientを立ち上げてテストを実行し,その結果をServerに投げるという一連の処理を実現できる.

Ukigumo関連の設定の勘所

Ukigumo::Server

サーバを立ち上げる時のオプションである--config=/path/to/config.plに食わせるコンフィグファイルの話.
コンフィグファイルは以下のような感じになる.

+{
    'DBI' => [
        'dbi:SQLite:dbname=deployment.db',
        '',
        '',
        +{
            sqlite_unicode => 1,
        }
    ],

    max_num_of_reports => 5000,
    max_num_of_reports_by_branch => 1000,
    enable_compression => 1,
};

DBIはもちろんDBの設定で,Ukigumo::ServerはMySQLSQLiteに対応している.たいていの場合はSQLiteで問題がない (と思う).
max_num_of_reportsmax_num_of_reports_by_branchはそれぞれ「保存するレポートの総個数の上限」と「ブランチごとの保存するレポートの個数の上限」を表している.これを超えると,古いものから順に自動的に消去される.
enable_compressionは,レポートの内容をgzip圧縮するかどうかの設定で,たいていの場合はenable (つまり1) にすると良いと思う.

Ukigumo::Client

特になし..ukigumo.ymlを使えば良いという位.

Ukigumo::Agent

特になし.強いて言えばukigumo-agentコマンドには--timeoutというコマンドがあって,テストの実行に指定した以上の時間がかかると強制的に処理を終了させる,ということが出来る (その場合はTIMEOUTという風になり,失敗扱いになる).

そんな感じです

かなり駆け足でしたが,こんなふうにすると使えますよ,という感じでした.
割と簡単に導入できることがお分かりいただけたと思います!!
「単純にテストを走らせて,その結果だけとれればいいワ」みたいな用途だとハマるのではないでしょうか.

ああ,次は公式のドキュメントだ……