VOYAGE GROUP エンジニアブログ

voyagegroup_techのブログ
VOYAGE GROUPエンジニアブログです。

programming

QUnit+PhantomJS+JenkinsでJavaScriptの品質を改善!

はじめまして!cosmiRelationshipSuiteの開発者であるマルィシェフ・ドミトリーと申します。
 
世の中で、Webベースシステムが増えており、管理画面の開発を担当している、エンジニアの視野から抜けがちであるJavaScriptのテストについてお話します。
最近の数年、TDD概念が非常に流行っており、Model(ビジネスロジック)をテストするJUnitやPHPUnitの利用は当たり前のようなことになりました。
それと同じく、JS(要するに、Front側のロジック)のテストがをしっかりできる環境として、QUnit+PhantomJS+Jenkinsの組み合わせを紹介したいと思います。

初めに
この度、localhostで開発・テスト・本番環境を設定し、開発→テスト→本番マシンへの展開を再現し、
JavaScriptのTDDを実践で試してみましょう。

利用するツールに関して 

Jenkins
CI(Continuous Integration→継続的インテグレーション)のツールです。
Jenkinsの詳しい資料は、http://jenkins-ci.org/にあります。

PhantomJS
PhantomJSはJavaScript APIで動くヘッドレスWebKitです。要するに、ブラウザーがなくてもJavaScriptが実行できる仕組みを提供していただいております。
PhantomJSについて、http://phantomjs.org/ をお読みください。

QUnit
QUnitはJavaScriptのUnit Testing Frameworkです。その大きなメリットは、
  • 非常にlightweightである
  • 学習コストが安い
  • 豊富なpluginが用意されている
  • synchronous/asynchronous callbackのテストが可能
  • モジュール定義により、htmlファイル毎にJSテストを分けることもできる
QUnitの利用に関して、詳しくはhttp://qunitjs.com/cookbook/#introductionをご覧ください。

早速、環境を用意しましょう。。。

前提条件
gitがマシンに既にインストールされていて、GitHubのアカウントを保有していること。

1. PhantomJSをインストール

ローカルフォルダーの下にgit cloneでPhantomJSを取得し、下記の手順に従い、ビルドしてください。
私の場合、/myprojects/phantomjsにgithubからcloneしました。
mkdir /myprojects
cd /myprojects
git clone git://github.com/ariya/phantomjs.git
そこからQUnitを実行するスクリプトを呼び出します。でも、それについて、後程。。。

そして、PhantomJSをインストールします。
sudo apt-get update 
sudo apt-get install phantomjs
ビルドに関して、http://phantomjs.org/build.html に書いてあります。
※ ビルドは数時間かかる場合もございます。 

2. Jenkinsをインストール

wget -q -O - https://jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins
今回は、Ubuntuでインストールしましたので、その他のシステムのJenkinsインストール手順に関して、
https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkinsをご覧ください。

デフォルトでJenkinsはhttp://127.0.0.1:8080で見れます。

3. JenkinsとGitHubを連携

必要となるpluginをインストールします。
「Jenkinsの管理」→「プラグイン管理」で、「利用可能」タブで、「Git Plugin」、「Parameterized trigger Plugin」、「GitHub pull request builder plugin」、「GitHub Plugin」を検索し、インストールしてください。

pluginインストールが完了したらJenkinsを再起動してください。
sudo service jenkins restart
JenkinsとGitHubを連携する際、必要となる鍵を生成しましょう。jenkinsユーザ(Jenkinsインストール時に自動的に作成されるユーザ)の権限で、鍵を生成しなければなりません。
sudo su -l jenkins
ssh-keygen -t rsa -C "jenkins@hogehoge.com"
鍵を生成する際、デフォルトのまま進めてください。
Generating public/private rsa key pair.
Enter file in which to save the key (/var/lib/jenkins/.ssh/id_rsa):
Created directory '/var/lib/jenkins/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /var/lib/jenkins/.ssh/id_rsa.
Your public key has been saved in /var/lib/jenkins/.ssh/id_rsa.pub.
The key fingerprint is:
09:b6:79:a6:b4:9d:5b:d0:ab:36:fb:93:ea:a4:62:7e jenkins@hogehoge.com
生成された鍵、Ubuntuの場合、デフォルトで/var/lib/jenkins/.sshに生成されます。
できた鍵をknown_hostsに追加します。
eval $(ssh-agent)
ssh-add /var/lib/jenkins/.ssh/id_rsa
その後、GitHubの「Settings」→「SSH keys」→「Add SSH Key」を開きます。
Titleに適当な名前を入力して、/var/lib/jenkins/.ssh/id_rsa.pub(生成されたJenkins公開鍵Jenkinsのデフォルト保存場所)の内容をコピーして、Keyに貼り付けます。

GitHubの鍵生成の詳しくは、https://help.github.com/articles/generating-ssh-keysを参照できます。

4. GitHubのリポジトリーを作成
GitHubでtestrepoリポジトリーを作成します。

5. 開発環境の用意
testrepoをローカルの作業フォルダーにcloneします。
mkdir /workdir
cd workdir/
git clone --recursive https://github.com/d-malyshev/testrepo.git 
apacheのconfファイルに作業フォルダーパスを追加<VirtualHost *:80>
    DocumentRoot /workdir/testrepo/public_html
    ServerName work.myserver.com

    ErrorLog "/var/log/apache2/work_server-error_log"
    CustomLog "/var/log/apache2/work_server-access" common
</VirtualHost>
/etc/hostsに開発用のサーバを追加

127.0.0.1 work.myserver.com

6. プロジェクトのソースコードを実装
開発フォルダーのtestrepoの下で、加算・引き算をする簡単なファイルを実装しましょう。
それより、先にGitHubのtest_branchのブランチを作成します。
cd /workdir/testrepo
git checkout -b test_branch
プロジェクトは以下のファイル構造になります。
/public_html
    /index.html -> indexファイル
    /add.html -> 足し算の処理
    /subtract.html -> 引き算の処理
    /js -> JavaScriptフォルダー
        /test -> ここでJavaScript用のUnit Testの.jsファイルを保存します
            /unit_test.js -> 実際のUnit Test
まずは、htmlページを実装します。

index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Calculation Page</title>
</head>
<body>
    <p>Welcome to calculation site!</p>
    <p><a href="add.html">Addition</a></p>
    <p><a href="subtract.html">Subtraction</a></p>
</body>
</html>
add.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Calculation Page: addition</title>
</head>
<body>
    <script>
    function calculateAdd(a, b)
    {
        var result = a + b;
        return result;
    }
    </script>
    <script>
    var res = calculateAdd(1, 2);
    document.write("1 + 2 = " + res);
    </script>
</body>
</html>
subtract.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Calculation Page: subtraction</title>
</head>
<body>
    <script>
    function calculateSubtract(a, b)
    {
        var result = a - b;
        return result;
    }
    </script>
    <script>
        var res = calculateSubtract(2, 1);
        document.write("2 - 1 = " + res);
    </script>
</body>
</html>
 
今回、テストしたいJavaScriptのロジックはadd.htmlとsubtract.htmlのページに実装していきます。
QUnitでテストできるには、ソースコードに数行を追加しなければいけません。

QUnitのライブラリjsとその表示用のcssをhtmlのheadに追加します。

<link rel="stylesheet" type="text/css" href="//code.jquery.com/qunit/qunit-1.15.0.css">
<script src="//code.jquery.com/qunit/qunit-1.15.0.js"></script>

QUnitのテスト結果が表示されるqunitとqunit-fixtureのidを持っているdivもhtmlのbody追加します。
<div id="qunit"></div>
<div id="qunit-fixture"></div>
そして、QUnitのUnit Testも実装します。
QUnit.test("calculateAdd test", function(assert) {
    var value = calculateAdd(1, 2);
    var expected = 3;
    assert.equal(value, expected, "We expect addition result to be " + expected);
});
すべてを組み合わせると、以下のようになります。
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Calculation Page: addition</title>
  <link rel="stylesheet" type="text/css" href="//code.jquery.com/qunit/qunit-1.15.0.css">
  <script src="//code.jquery.com/qunit/qunit-1.15.0.js"></script>
</head>
<body>
    <script>
    function calculateAdd(a, b)
    {
        var result = a + b;
        return result;
    }
    </script>
    <script>
        var res = calculateAdd(2, 1);
        document.write("1 + 2 = " + res);
    </script>
   <script>
      QUnit.test("calculateAdd test", function(assert) {
        var value = calculateAdd(1, 2);
        var expected = 3;
        assert.equal(value, expected, "We expect addition result to be " + expected);
    });
    </script>
    <div id="qunit"></div>
    <div id="qunit-fixture"></div>
</body>
</html>
ブラウザで見ると、この感じになります。

jenkins35
CLIでもテストできます。
そのために、phantomjsから、QUnitを実行するrun-qunit.jsを呼び出します。
phantomjs /myprojects/phantomjs/examples/run-qunit.js http://work.myserver.com/add
.html
'waitFor()' finished in 220ms.
Tests completed in 20 milliseconds.
1 assertions of 1 passed, 0 failed.
※ run-qunit.jsのパスはPhantomJSのソースコードの保存先によって変わります。

QUnitテストのソースコードをテスト専用のjs/test/unit_test.jsに移行しましょう。または、各htmlファイルにQUnit用のcssやdiv.qunitとdiv.qunit-fixtureを追記しないように、それを動的に生成するようにしましょう。
さて、unit_test.jsは以下のようになります。
// Test環境のみで必要な結果表示用のdivやcssを追加
var d = document;
var qd = d.createElement('div');
qd.id = "qunit";
d.body.appendChild(qd);

var qfd = d.createElement('div');
qfd.id = "qunit-fixture";
d.body.appendChild(qfd);

//QUnitのstylesheetを追加
var head = d.getElementsByTagName('head')[0];
var css = d.createElement("link")
css.setAttribute("rel", "stylesheet")
css.setAttribute("type", "text/css")
css.setAttribute("href", "//code.jquery.com/qunit/qunit-1.15.0.css");
head.appendChild(css);

// 足し算Testを実行
QUnit.test("calculateAdd test", function(assert) {
    var value = calculateAdd(1, 2);
    var expected = 3;
    assert.equal(value, expected, "We expect addition result to be " + expected);
});
add.htmlも修正します。
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Calculation Page: addition</title>
    <script src="//code.jquery.com/qunit/qunit-1.15.0.js"></script>
</head>
<body>
    <script>
    function calculateAdd(a, b)
    {
        var result = a + b;
        return result;
    }
    </script>
    <script>
    var res = calculateAdd(1, 2);
    document.write("1 + 2 = " + res);
    </script>
    <script src="js/test/unit_test.js"></script>
</body>
</html>
ただし、subtract.htmlのQUnitのテストjs/test/unit_test.jsを追加すると...
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Calculation Page: subtraction</title>
    <script src="//code.jquery.com/qunit/qunit-1.15.0.js"></script>
</head>
<body>
    <script>
    function calculateSubtract(a, b)
    {
        var result = a - b;
        return result;
    }
    </script>
    <script>
        var res = calculateSubtract(2, 1);
        document.write("2 - 1 = " + res);
    </script>
    <script src="js/test/unit_test.js"></script>
</body>
</html>
...subtract.htmlのUnit Testは失敗します。
phantomjs /myprojects/phantomjs/examples/run-qunit.js http://work.myserver.com/subtract.html
'waitFor()' finished in 208ms.
Tests completed in 34 milliseconds.
0 assertions of 1 passed, 1 failed. 
失敗の理由は、subtract.htmlでテストしようとしているcalculateAddの関数がないからです。
もちろん、subtract.htmlの関数のみのUnit Testのjsを実装すれば、subtract.htmlのUnit Testは成功します。
しかし、各htmlファイル用のUnit Testのjsを用意するのが非常に面倒です。

そのために、QUnitでModuleという仕組みが設けられ、複数htmlページの関数テストを一つのjsファイルでまとめて、モジュール毎の呼び出しは可能です。

足し算処理のテストと引き算処理のテストをそれぞれ、add_testとsubtract_testに分けます。
// モジュールadd_testを作成(public_html/add.htmlページのJSテスト用)
QUnit.module("add_test");

// 足し算Testを実行
QUnit.test("calculateAdd test", function(assert) {
    var value = calculateAdd(1, 2);
    var expected = 3;
    assert.equal(value, expected, "We expect addition result to be " + expected);
});

// モジュールsubtract_testを作成(public_html/subtract.htmlページのJSテスト用)
QUnit.module("subtract_test");

// 引き算Testを実行
QUnit.test("subtract_test calculateSubtract test", function(assert) {
    var value = calculateSubtract(2, 1);
    var expected = 1;
    assert.equal(value, expected, "We expect subtraction result to be " + expected);
});
できたモジュールをもう一度、呼び出してみます。モジュール毎のUnit Testは、http://yourdomain/filename?module=moduleName形式で呼び出されます。
足し算のテスト
phantomjs /myprojects/phantomjs/examples/run-qunit.js http://work.myserver.com/add.html?module=add_test
'waitFor()' finished in 216ms.
Tests completed in 17 milliseconds.
1 assertions of 1 passed, 0 failed.
引き算のテスト
phantomjs /myprojects/phantomjs/examples/run-qunit.js http://work.myserver.com/subtract.html?module=subtract_test
'waitFor()' finished in 207ms.
Tests completed in 19 milliseconds.
1 assertions of 1 passed, 0 failed.
最後には、unit_test.jsにもう少し手を加えましょう。

実は、その開発環境でも本番環境でもQUnitのJavaScriptは毎回呼ばれるのが困ります。
結局、テスト用のマシンのみQUnitを実行したいわけです。そのため、QUnit.config.autostartの設定項目が存在しています。そもそも、QUnit.config.autostartはtrueになっているから、ページがロードされる度にUnit Testは走ります。
そこで、テスト用のtest.myserver.comでのみ実行できるようにQUnit.config.autostartを工夫します。

QUnit.config.autostart = (window.location.href.indexOf("test.myserver.com") > -1) ? true : false;
 
最終的に、Unit Test用のjs/test/unit_test.jsファイルは下記の形をとります。
// テスト環境のみで実行
QUnit.config.autostart = (window.location.href.indexOf("test.myserver.com") > -1) ? true : false;

// Test環境のみで必要なdivやcssを追加
if (QUnit.config.autostart != false) {
    // 結果表示divを追加
    var d = document;
    var qd = d.createElement('div');
    qd.id = "qunit";
    d.body.appendChild(qd);

    var qfd = d.createElement('div');
    qfd.id = "qunit-fixture";
    d.body.appendChild(qfd);

    //QUnitのstylesheetを追加
    var head = d.getElementsByTagName('head')[0];
    var css = d.createElement("link")
    css.setAttribute("rel", "stylesheet")
    css.setAttribute("type", "text/css")
    css.setAttribute("href", "//code.jquery.com/qunit/qunit-1.15.0.css");
    head.appendChild(css);
}

// モジュールadd_testを作成(public_html/add.htmlページのJSテスト用)
QUnit.module("add_test");

// 足し算Testを実行
QUnit.test("calculateAdd test", function(assert) {
    var value = calculateAdd(1, 2);
    var expected = 3;
    assert.equal(value, expected, "We expect addition result to be " + expected);
});


// モジュールsubtract_testを作成(public_html/subtract.htmlページのJSテスト用)
QUnit.module("subtract_test");

// 引き算Testを実行
QUnit.test("subtract_test calculateSubtract test", function(assert) {
    var value = calculateSubtract(2, 1);
    var expected = 1;
    assert.equal(value, expected, "We expect subtraction result to be " + expected);
});
できたソースコードをGitHubのtest_branchにpushしましょう。
git add .
git commit -m "Initial commit"
git push origin test_branch
7. Jenkinsのjobを作成
こちらで、ソースコードをbuild/test/deployするjobを作成します。

status_update_job: build状態をGitHubに知らせるjobを作成

プロジェクトをビルドするjobはそれを参照するので、先に作ります。

「新規ジョブ作成」で
「フリースタイル・プロジェクトのビルド」にチェック
「プロジェクト名」→ status_update_job
「OK」で作成画面に入ります。

ジョブ作成画面で
「ビルドのパラメータ化」にチェック
「ビルドのパラメータ化」→「パラメータの追加」→「文字列」で3つのパラメータを作成し、それぞれの「名前」にCOMMIT_STATUS、UPDATE_COMMIT、REPOSITORY_NAMEを入力
「ソースコード管理」→「なし」にチェック
「ビルド」→「シェル実行」を選択します。
「ビルド」→「シェルスクリプト」にビルドされたcommitの状態(successまたは、failure)をGitHubに通知する処理
curl -i -H 'Authorization: token abcdef0123456789abcdef012345fc2b6142d8a7' \
    -d '{"state": "'${COMMIT_STATUS}'"}' \
    https://api.github.com/repos/${REPOSITORY_NAME}/statuses/${UPDATE_COMMIT}
tokenはOAuth用に生成してください。

GitHubの「Settings」→「Applications」→「Generate new token」でOAuthトークンを生成し、その文字列をどこかで保存してください。トークンを作成する際、repoにチェックが入っていることを、確認してください。

build_job: プロジェクトをビルドするjobを作成

「新規ジョブ作成」で
「フリースタイル・プロジェクトのビルド」にチェック
「プロジェクト名」→ build_job
「OK」で作成画面に入ります。

ジョブ作成画面で
「ソースコード管理」→「Git」にチェック
「Repositories」→「Repository URL」にgit@github.com:{githubのアカウント}/{あなたのリポジトリ―}(私の場合、git@github.com:d-malyshev/testrepo.gitです)
「Repositories」→「Credentials」で「なし」を選択
「Branches to build」に2つのbranchを「Add Branch」で追加してください。それぞれの「Branch Specifier」に「*/master」と「*/release」を入力してください。
「Additional Behaviors」で「Strategy for choosing what to build」を選択し、その中から「Inverse」にしてください。
「ビルド・トリガー」→「SCMをポーリング」にチェック
「ビルド・トリガー」→「スケジュール」に常に起動するように設定
 
* * * * *

「ビルド」→「シェル実行」を選択します。
「ビルド」→「シェルスクリプト」にPhantomJS+QUnitのモジュールテストを指定します
phantomjs /myprojects/phantomjs/examples/run-qunit.js http://test.myserver.com/add.html?module=add_test
phantomjs /myprojects/phantomjs/examples/run-qunit.js http://test.myserver.com/subtract.html?module=subtract_test
「ビルド後の処理」→「Trigger parametrized build on other projects」を選択
「Add trigger」で2つのBuild Triggersを追加
その二つは、以下のようになります

1番目
「Projects to build」→ status_update_job
「Trigger when build is」→「Stable or unstable but not failed」を選択
「Predefined parameters」に
COMMIT_STATUS=success
UPDATE_COMMIT=${GIT_COMMIT}
REPOSITORY_NAME=あなたのアカウント/あなたのリポジトリ―(私の場合、d-malyshev/testrepo)
2番目
「Projects to build」→ status_update_job
「Trigger when build is」→「Failed」を選択
「Predefined parameters」に
COMMIT_STATUS=failure
UPDATE_COMMIT=${GIT_COMMIT}
REPOSITORY_NAME=あなたのアカウント/あなたのリポジトリ―(私の場合、d-malyshev/testrepo)

mergeされたリポジトリ―を本番サーバにdeployするjobを作成

「新規ジョブ作成」で
「フリースタイル・プロジェクトのビルド」にチェック
「プロジェクト名」→ deploy_job
「OK」で作成画面に入ります。

ジョブ作成画面で
「ソースコード管理」→「なし」にチェック
「ビルド・トリガー」→「定期的に実行」にチェック
「ビルド・トリガー」→「スケジュール」に常に起動するように設定。5分毎にmergeされたプロジェクトを確認したい場合、以下のように記述
*/5 * * * *
「ビルド」→「シェル実行」を選択します。本番サーバに最新のソースコードをpullします
if [ -d /myserver/testrepo ]; then
    cd /myserver/testrepo
    git pull origin master
else
    cd /myserver
    git clone --recursive https://github.com/d-malyshev/testrepo.git
fi

8. 本番環境の用意
GitHubでbranchがmasterにmergeされた後、本番環境にソースコードをdeployします。
今回、開発環境や本番環境は同じマシンになっていますので、本番環境は別のマシンになる場合、rsyncなどをご利用ください。
/myserverフォルダーを作成してください。
apacheのconfファイルに作業フォルダーパスを追加
<VirtualHost *:80>
    DocumentRoot /myserver/testrepo/public_html
    ServerName prod.myserver.com

    ErrorLog "/var/log/apache2/prod_server-error_log"
    CustomLog "/var/log/apache2/prod_server-access" common
</VirtualHost>
/etc/hostsに開発用のサーバを追加
 
127.0.0.1 prod.myserver.com 

9. Jenkinsでのテスト環境の用意
PhantomJSはQUnitをテスト用のサーバで実行するので、先ほど作成されたJenkinsのbuild_jobはtest.myserver.comテストサーバのDocumentRootになるように、apacheのconfファイルに下記を追記します。
<VirtualHost *:80>
    DocumentRoot "/var/lib/jenkins/jobs/build_job/workspace/public_html"
    ServerName test.myserver.com

    ErrorLog "/var/log/apache2/test_server-error_log"
    CustomLog "/var/log/apache2/test_server-access" common
</VirtualHost>
/etc/hostsにも開発用のサーバを追加
 
127.0.0.1 test.myserver.com


10. PhantomJS+QUnit+Jenkinsの実践
これで、やっと開発・テスト・本番環境が揃いました。

本番環境にはまだ何もdeployされていないので、本番サーバはこの感じです。

とりあえず、add.htmlを修正し、bugを仕込みましょう。

bugを入れる前
function calculateAdd(a, b)
{
    var result = a + b;
    return result;
}
bugを入れた後
function calculateAdd(a, b)
{
    var result = a + b;
    // Unit Testを通らないようにbug
    return result+1;
}
そして、そのソースコードを、先作成してきたtest_branchに pushします。
git add .
git commit -m "Created bug"
git push origin test_branch
GitHubでtest_branchのPull Requestを作成してみます。
それをやってみると、最近の当ブランチにpushされたcommitのbuild_jobが失敗したことが分かります。

jenkins16

ブランチをクリックすると、ビルドが失敗したcommitを見れます。

jenkins17

Jenkinsのjob一覧画面を見ると、build_jobの#8が失敗したことが分かります。
そのbuild_jobをクリックし、job結果一覧画面に移動します。

jenkins22

失敗した#8の詳細ページに行って、Console Outputでbuildの失敗した原因を調べることもできます。
 
jenkins23
jenkins21

build_jobの次に、status_update_jobが起動し、GitHubでcommitの状態をfailureに設定しました。

jenkins39


では、仕込んできたbugを修正し、またtest_branchにpushしましょう。

bugを修正した後
function calculateAdd(a, b)
{
    var result = a + b;
    return result;
}
そして、test_branchにpushします。
git add .
git commit -m "Fixed bug"
git push origin test_branch
今度、build_jobは成功し、GitHub上の結果はこのとおりとなります。

jenkins19

test_branchを「Merge pull request」→「Confirm merge」でmasterにmergeし、最後に「Delete branch」でtest_branchを削除します。その後、deploy_jobは起動し、masterから最新版のソースコードを本番サーバにpullします。
 
そして、先ほど設定した本番サーバのhttp://prod.myserver.comにアクセスし、ソースコードは問題なく表示されていることをご確認ください。

まとめ
「使いやすさと良き見映えは成功の8割」と言われるWeb画面ですが、
そのJavaScriptのバグは、ユーザーエクスペリエンスを低下させ、お客様からの多くのクレームの原因にもなります。 
そこで、QUnit+PhantomJS+Jenkinsを利用したら、Front側JSの品質向上に充分貢献できるかと思います。
 


あなたにおすすめするたった一つの最高のキーボード

みなさまこんにちは!

VOYAGE MARKETINGにてエンジニアをしている@gomachan46です。
社内の非公式サークルとして音ゲー部なるものを立ち上げて日々熱心に活動しております。


さて、みなさまキーボードはどのようなものをお使いでしょうか?

エンジニアな読者のみなさまは、キーボードには特段の思い入れがあるのではないでしょうか。

そこで、VOYAGE GROUPのエンジニア達がどんなキーボードを使っているか見てきました。 

realforce

高級キーボードの代名詞的なREALFORCEや、

hhkb

矢印キーのない非常にコンパクトなHHKB

kinesis

エルゴノミクスデザインが斬新なKinesis

truly

エルゴノミクスキーボードスレで定番と噂のTruly Ergonomic

打鍵音が良い、打鍵時の感触が良い、コンパクトでどこでも持ち運べる、変荷重で各指に合った程よい重さ、、、などなど色々あることと思います。

僕も例に漏れずキーボードは人一倍こだわって選んでいます。

そこで今日は僕のキーボードを紹介したいと思います!

otoge


どうでしょうかこの重厚感!他とは一線を画すデザイン!高級感あふれるメタリックボディ!
そしてなんといっても左側に燦然と輝くturn table!!

素晴らしいの一言に尽きますね。

■ What is this?

音ゲー用のコントローラーです。

■■ What is Otoge?

簡単にいうとリズムに合せて作成された譜面の通りにボタンを押して遊ぶゲームです。
 
otoge_info

 
一般的に失敗(Failure)、成功(Clear)に結果が分かれ、更にClearの先にはミスのないフルコンボ(FullCombo)という結果もあります。

こんな音ゲー用の代物なので、僕のキーボードは打鍵感、打鍵音ともに折り紙付きなキーボードなのです。

■ Let's Programming!

せっかくなので素晴らしさを伝えるべくこのキーボードでプログラミングしていこうと思います。

otoge2

これらのボタンにキーバインドをしていき、キーボードとして機能するようにします。

■■ Hello World!

プログラミング言語はRubyです。 

puts 'hello world'

これをRubyを対話的に実行できるirbを起動して実行するところまでをやってみます。

実際にプログラミングしている様子がこちらになります。



いかがでしょうか。実に楽しそうですね!

■ From now on...

このように、これからの時代はまず譜面を作り、それをプレイすることで開発を行う譜面駆動開発(Fumen Driven Development)が主流となっていくことでしょう。
Failed->Clear->FullComboの黄金サイクルを回すことで、開発にリズムが生まれ、進捗最高になること間違いなし!

いかがでしょうか?
みなさんすぐにでも今お使いのキーボードから乗り換えたくてウズウズしているのではないでしょうか。

さぁ、今すぐFDDを始めましょう! 

GitHubにあげたコードをブログに貼り付ける方法

こんにちわわわ。ECナビの中の人 @tadasy です。

ブログにコードを貼り付けるとき、みなさんどうしてますか?
貼り付けたコードを見やすくするために、ブログが用意しているシンタックスハイライトの機能を使ったり、Gistで貼っつけたりしますよね。

ところで、僕、今回このブログを書くにあたって、まったく別のネタで書こうとしていたのです。
で、多分に漏れず、ちょっとしたものを作って、コードを貼っ付けようとしたのですが、このブログをホスティングしているライブドアブログにはシンタックスハイライトの機能が提供されていないんです。
なので当然Gistを使うって選択肢になるのですが、ちょっとしたものを作るにしても、Gistで書いたりせずにGitHubにpushするじゃないですか。でもGitHubに上がっているコードってGistみたいにブログに貼っつけることが出来ないんですよね。かと言って、GitHubに上げたものをワザワザGistにコピるのものなんだかなぁと思うじゃないですか。

うだうだ書きましたが、GitHubにあげているコードをブログに貼っつける方法を見つけたので、それをネタにブログを書くことにしました。見つけたのはコレ。


使い方は簡単で、

<script  src="http://gist-it.appspot.com/github/<user_name>/<repository_name>/blob/<branch_name>/</path/to/file_name>"></script>

みたいに書くだけ。
たとえば、
というファイルを貼り付ける場合は↓のように書きます。
 
<script src="http://gist-it.appspot.com/github/tadasy/sample_travis_memcached/blob/master/test/phpunit.xml"></script>

と書くだけ。

では実際に貼り付けたイメージを見てみましょう


こんな感じになります。
それっぽく表示されましたね!

Gistでも貼り付けてみましょう



なんかイケてる感じする...

見た目は、個人的にはGistを貼り付けたほうが好みなのですが、わざわざGistに書き直す面倒臭さを考えれば全然楽ちんです。

というわけでブログ業が捗りますね!

Route53をbind形式のzoneファイルで管理するツール, bind2route53を作成して公開しました。

こんにちは。
VOYAGE GROUPのシステム本部でインフラエンジニアとして働いている @s_tajima です。


DNSサーバーとしてRoute53を利用しようとした場合、
登録されたゾーン(HostedZone)やレコードをどのように管理するかというのに
悩んでいる方々も多いと思います。


特にVOYAGE GROUPでは、
現在 約100ゾーン,  約2500レコード
というとても多いレコードを管理しているのでとても重要な問題です。

一般的にすぐに思いつく管理の手段としては,,

  1. Management Consoleを操作してゾーン/レコードを管理する。
  2. AWSが提供するSDK等を使い、Route53のAPI(ChangeResourceRecordSets等)を叩く。
  3. 既存のRoute53の管理ツール(RoadWorker等)を使う。

などがあると思います。

これらの手段を選択する前に、Route53を管理する上で、
どのような用件が満たされていると良さそうかを考えてみましょう。

  1. ゾーンの編集(作成, 削除)/基本的なレコードの編集(作成, 削除, 変更)ができる
  2. 本番環境の設定変更の前に、適切な検証環境でレコードの変更をテストできる
  3. 2で実施した変更と完全に同一な変更を本番環境に適用できる 
  4. 適用した変更を(出来る限り短期間で)切り戻せる
  5. 任意の時点での変更内容を後から確認できる
  6. Route53から別のDNSサーバーに移設したいとなった時に、低いコストで対応できる
  7. Route53独自の機能(ALIASレコード等)を利用できる

と、このあたりでしょうか。

具体的な説明は割愛しますが、
検証の結果先に挙げた3つの手段では
これらの用件を満たすことは不可能/工夫が必要だということがわかりました。

このような背景から上記の用件を実現できるツールとして,
bind2route53(https://github.com/voyagegroup/bind2route53)を作成しました。


bind2route53_fig1
このツールがどのような機能をもっているかを簡単に説明すると、

・bind形式のzoneファイルをCloudFormationのTemplateに変換する。(convert_zonefile)
・Route53上に、ゾーンを作成する。(create_hostedzone)
・CloudFormationに、Route53を管理するためのStackを作成する。(create_hostedzone_stack)
・CloudFormationのStackを更新し、Route53のゾーン/レコードの編集をする。(update_hostedzone_stack)

というシンプルなツールとなっています。
このツールがどのように上記の用件を満たしているのかを説明すると、

1. ゾーンの編集(作成, 削除)/基本的なレコードの編集(作成, 削除, 変更)ができる
 上記の機能を見ればこの用件を満たしていることはわかると思います。

2. 本番環境の設定変更の前に、適切な検証環境でレコードの変更をテストできる
 上記の機能を実施する際, 設定ファイルにIAMの鍵を複数設定し、どの鍵を利用するかを指定します。
 検証環境用の鍵と本番環境の鍵を登録することで、事前に検証環境でテストをすることができます。
  ※尚、検証環境と本番環境は、別のAWSのアカウントを用意することを前提としています。

bind2route53_fig4


3. 2で実施した変更と完全に同一な変更を本番環境に適用できる 
 検証環境に対して実行したコマンドを、
 環境の部分だけ本番環境とすることで同じ内容が本番環境に適用されることが保証されます。

4. 適用した変更を(出来る限り短期間で)切り戻せる
 bind形式のzoneファイルをバージョン管理(gitやsvn)しておき、
 設定に不備があった場合にはzoneファイルを切り戻し(git checkoutやsvn revert)して
 再度CloudFormationに適用することで、切り戻しが行えます。

5. 任意の時点での変更内容を後から確認できる
 4に記載した通り、zoneファイルをversion管理下に置くことで変更の履歴を閲覧できます。

6. Route53から別のDNSサーバーに移設したいとなった時に、低いコストで対応できる
 bind形式のzoneファイルを管理するので、一般的な別のDNS管理サービスへの移設コストは低いと言えます。
 (Route53もそうだが、bind形式のzoneファイルをimportする機能を提供するサービスが多い。)

7. Route53独自の機能(ALIASレコード等)を利用できる
 例外的に、zoneファイルに独自のシンタックスを記述することで
 Route53独自の機能を利用できるような機能を提供しています。

使い方についての詳細は こちら に記載してあるので参考にしてぜひお使いください。
以上、bind2route53の紹介でした。

#====================================

※尚, VOYAGE GROUPでの現在の運用環境では
bind2route53をラップするスクリプト 
(convert_zonefile -> update_hostedzone_stackをコマンド1つで行う等)や、
DNSの設定変更をした際に、その変更が正しくできているかを確認するツール
(内部的にはdigを実行して意図した結果が返ってくるかの確認)を
を作成して、合わせて利用しています。

【JavaScript】 New Relicのなかなか凄い新機能を試してみた

こんにちは、こんばんは、おはようございます。株式会社adingo 高橋です。
弊社adingoは月間200億impを捌くSSP「Fluct」を提供しており、100台近いサーバでリクエストを捌いたり、大規模データの分析をしております。

さて、そんな仕事とは直接の関係は無いですが、最近New Relicがなかなか凄い新機能をβリリースしていたので簡単な紹介をしたいと思います。
New Relicとはアプリケーションとサーバのモニタリングを行うサービスです。yumでagentをインストールするだけで手軽に導入できます。今回紹介する新機能は「JSのエラーレポート機能」と「AJAXの実行時間レポート」で、リリース文は以下となります。
http://blog.newrelic.com/2014/03/13/javascript-error-reporting-ajax-timing-new-relic/

(1)インストールとアプリケーション設定
新機能を使う前にまずアプリケーション設定を行う必要があります。
アカウントを持っていない場合、New Relicページの右上の「Create Free Account」ボタンから新規にアカウントを作成します。
ログインして「Application」のメニューを選択するとメニュー画面が表示されますので、案内に従ってアプリケーションの設定を行ってください。
regist_app2

自分(CentOS)の場合は以下のコマンドで登録しました。
$ sudo rpm -Uvh http://yum.newrelic.com/pub/newrelic/el5/x86_64/newrelic-repo-5-3.noarch.rpm
$ sudo yum install newrelic-php5
# ↓ライセンスキーの登録
$ sudo newrelic-install install
# ↓apacheであればhttpdを再起動
$ sudo /etc/init.d/php-fpm restart
phpinfoで以下のようにnewrelicが表示されればOKです。
phpinfo

(2)JSのエラーレポート機能を試す
アプリケーションを登録してしばらくすると、Browserメニューの中に「PHP Application」のようにアプリ名が表示されます。
選択して「Settings」タブに移動後、「Enable Beta features」にチェックを入れて保存します。
setting

以下のように「newrelic/render_js.php」を作成後、ブラウザで開いてみてください。
(「alert」をスペルミスしているためスクリプトエラーになるはずです。)
$ cd [アプリケーションのドキュメントルート]
$ mkdir newrelic
$ vi newrelic/render_js.php
<?phpecho <<<EOT
<html>
<head>
<title>test</title>
<script type="text/javascript">
alet('hoge');
</script>
</head>
<body>test</body>
</html>
EOT;
しばらくして、「JS errors」のタブを選択
js_error
「Show all JavaScript errors」を選択 
js_error1
更に詳細を見てみると
js_error2
すげええええ!!!JavaScriptのエラーをハンドリングして表示してくれました。 

(3)AJAXの実行時間レポート機能を試す
次に以下のように「newrelic/render_ajax_js.php」を作成して、ajaxのリクエストを発生させます。
$ vi newrelic/render_ajax_js.php
<?php
echo <<<EOT
<html>
<head>
<title>test</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$('#test').click(function(){
$.get('/', function(){ });
});
});
</script>
</head>
<body>
<input type="button" id="test" value="test">
</body>
</html>
 しばらく待ってレポートを見てみると以下のようにレポートに反映されました。
ajax_timing

今のところ、 下記の値がグラフされるようです。
・Total time percentage:処理時間に占めるAJAXリクエストの割合
・Throughput:1分あたりのリクエスト数
・Status codes of all requests:200以外のステータスコードの割合
・Average data transfer per request:リクエストあたりの平均送信バイト数

以上、New Relicに追加された新機能の紹介でした。
βリリースかつ htmlにJavaScriptのハンドリング処理を差し込むので本番環境に入れるのは少し怖いですが、テスト環境に導入してみるのは良いかもしれないと思いました。

[PR]
VOYAGE GROUPではアドテクエンジニアを絶賛募集中です!
Cosmi データマイニングエンジニア
広告配信システムエンジニア
Zucksフロントエンジニア
記事検索
QRコード
QRコード