Rendrで作られたnode.jsアプリをminaでデプロイする

Rendrで新しくアプリを作ろうとしていて、rendr-cli
プロジェクトの雛形を作ったので、では実際にデプロイどうしようと考えた時にminaを使ってやってみたので
その際のメモ。

手順としては、

  • rendrアプリをデーモン化して稼働させるためにforeverで動く様にする
  • minaでデプロイするタスクを書く

minaのデプロイはここあたりに書いているので詳細は省略。

foreverでデーモン化する

rendr-cliで作られたアプリのベースには既にgruntを使って開発環境用のサーバを
稼働させたり、stylusをコンパイルするためのgruntタスクが既に用意されていて、開発環境であれば

$ grunt server

とすれば、一通りのコンパイルと手元でサーバ起動、そしてstyles等の更新を監視して再コンパイルをしてくれるのですが、
これをこのままproduction環境で動かす訳にはいかないので、foreverでデーモン起動、停止をする
タスクを書き足します。

まず、foreverが使える様にするためにアプリケーションプロジェクトのルートで、

$ npm install forever --save

としてforeverをインストール。 --save を付けて package.json も更新します。

次にrendr-cliが作った Gruntfile.js にforeverを使ったアプリケーション起動、停止をするタスクを足します。

// Gruntfile.js
grunt.registerTask('startProductionNode', function () {
grunt.util.spawn({
cmd: 'node',
args: ['./node_modules/forever/bin/forever', 'start', 'index.js'],
opts: {
stdio: 'inherit'
}
}, function () {
grunt.fail.fatal(new Error("forever quit"));
});
});
grunt.registerTask('stopProductionNode', function () {
grunt.util.spawn({
cmd: 'node',
args: ['./node_modules/forever/bin/forever', 'stop', 'index.js'],
opts: {
stdio: 'inherit'
}
}, function () {
grunt.fail.fatal(new Error("forever quit"));
});
});

ここまで設定すると、アプリケーションをデーモン化して起動、停止ができます。

起動は、*1

$ grunt startProductionNode
Running "startProductionNode" task
warn:    --minUptime not set. Defaulting to: 1000ms
warn:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info:    Forever processing file: index.js
Done, without errors.

一方停止は、

$ grunt stopProductionNode
Running "stopProductionNode" task
info:    Forever stopped process:
data:        uid  command                                            script   forever pid   logfile                             uptime
[0] sKj9 /Users/hideack/.nodebrew/node/v0.11.11/bin/node index.js 34008   34012 /Users/hideack/.forever/sKj9.log 0:0:1:15.836
Done, without errors.

これで、Rendrアプリをデーモン化して動かせる様になりました。

minaでのデプロイ

続けて実際にデプロイする際には、リポジトリから最新のコードを取ってきてstylus等をコンパイルして上で作ったgruntタスクを利用してアプリケーションを再起動させます。

gitリポジトリから引っ張ってくるところまではRubyのアプリケーションの場合と同様ですが、アプリケーションサーバの起動のタスクは独自で書く必要があるので以下の様なタスクを書き足します。

# rendr
# =============
namespace :rendr do
task :compile do
queue 'echo "-----> Start compile tasks."'
queue! %{
      cd #{deploy_to}/current
      grunt compile
    }
end
task :start do
queue 'echo "-----> Start server."'
queue! %{
      cd #{deploy_to}/current
      grunt startProductionNode
    }
end
task :stop do
queue 'echo "-----> Stop server."'
queue! %{
      cd #{deploy_to}/current
      grunt stopProductionNode
    }
end
end
desc "Deploys the current version to the server."
task :deploy => :environment do
deploy do
# Put things that will set up an empty directory into a fully set-up
# instance of your project.
invoke :'git:clone'
invoke :'deploy:link_shared_paths'
to :launch do
invoke :'rendr:compile'
invoke :'rendr:stop'
invoke :'rendr:start'
queue "touch #{deploy_to}/tmp/restart.txt"
end
end
end

ここまで準備できれば手元から、

$ bundle exec mina deploy

でRendrアプリのデプロイ&アプリの再起動*2が行えます。

*1:warning出ているのはアプリのプロセスの監視条件を設定していないためなので設定するべきですね...

*2:ただこのままではgracefulな再起動になってないので今後直していく予定

sochi.ruのこと

オリンピックのニュースでよく見る sochi.ru ドメインっぽいのでたどってみた。Akamai使っててIISなのかぁ。と。

$ http sochi.ru
HTTP/1.1 301 Moved Permanently
Connection: keep-alive
Content-Length: 0
Date: Sun, 16 Feb 2014 07:59:59 GMT
Location: http://www.sochi.ru/
Server: AkamaiGHost
$ http www.sochi.ru
HTTP/1.1 301 Moved Permanently
Connection: keep-alive
Content-Length: 0
Date: Sun, 16 Feb 2014 08:01:00 GMT
Location: http://www.sochi2014.com/
Server: AkamaiGHost
$ http www.sochi2014.com
HTTP/1.1 302 Moved Temporarily
Connection: keep-alive
Content-Length: 0
Date: Sun, 16 Feb 2014 08:02:03 GMT
Location: http://www.sochi2014.com/en
Server: AkamaiGHost
$ http --header http://www.sochi2014.com/en
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 14928
Content-Type: text/html; charset=utf-8
Date: Sun, 16 Feb 2014 08:03:02 GMT
Expires: Sun, 16 Feb 2014 08:03:02 GMT
Last-Modified: Sun, 16 Feb 2014 08:03:05 GMT
Server: Microsoft-IIS/8.0
Vary: Accept-Encoding
X-VDC: EA1

お昼ごはん

馬肉ステーキ丼を食べた。とても食べやすくて美味しかった。

MacOSにパッケージから入れたnode.jsをnodebrewに移行した

node.jsは公式サイトでMac用のインストーラパッケージが配布されていて非常にインストールが楽なのだけど、これをやめてnode.jsのバージョンマネージャ*1であるnodebrewを導入したのでその際のメモ。

パッケージでインストールされたnode.jsを削除する

各所で紹介されている以下の様なコマンドで削除。

lsbom -f -l -s -pf /var/db/receipts/org.nodejs.pkg.bom \
| while read i; do
sudo rm /usr/local/${i}
done
sudo rm -rf /usr/local/lib/node \
/usr/local/lib/node_modules \
/var/db/receipts/org.nodejs.*

nodebrewのインストール

これはgithubリポジトリのreadmeに従って完了するのですが一応メモとして残しておきます。

まずはインストール

$ curl -L git.io/nodebrew | perl - setup

完了したら環境変数(~/.zshrcや~/.bashrc)にパスを通す

export PATH=$HOME/.nodebrew/current/bin:$PATH

そして一度環境変数を更新

$ source ~/.zshrc

そしてここから実際にインストール。インストールできるnode.jsのバージョンを得るには

$ nodebrew ls-remote
v0.0.1    v0.0.2    v0.0.3    v0.0.4    v0.0.5    v0.0.6
(略)
v0.11.0   v0.11.1   v0.11.2   v0.11.3   v0.11.4   v0.11.5   v0.11.6   v0.11.7
v0.11.8   v0.11.9   v0.11.10  v0.11.11

こんな感じで一覧が把握できる。そして実際にインストールするには、

$ nodebrew install-binary v0.11.11
$ nodebrew use v0.11.11
use v0.11.11
$ node -v
v0.11.11

といった具合でインストールできます。*2

参照

Mac OS X から Node.js をアンインストールする方法 - SONICJAM Developerz Blog

*1:Rubyでのrbenv的な位置づけ

*2:install-binaryでバイナリからインストールするため数秒でインストール完了します。オプションをinstallとした場合はソースからコンパイルするので時間を要します。

JavaScriptのハッシュをマージする

  var _hashMerge = function(target) {
var sources = [].slice.call(arguments, 1);
sources.forEach(function (source) {
for (var prop in source) {
target[prop] = source[prop];
}
});
return target;
};

を定義しておけば、

var origin1 = {a: "foo"};
var origin2 = {b: "hoge"};
var merged = _hashMerge({}, origin1, origin2);
// => Object {a: "foo", b: "hoge"}

といった具合でマージできる

JavaScriptのハッシュのキーへ任意の変数名を指定する場合

JavaScriptのハッシュのキーへ任意の変数名を設定したいつもりで、以下の様に書くと意図した挙動にならない。

例えば下の場合、

var key = "foo";
var data = {key: "hoge"};
// data
// Object {key: "hoge"}

dataというハッシュに foo というキーを設定したかったができていない。

そこで、以下の様に関数を定義してあげると所望する処理を実現できる。

var hash = function(key, value) {
var h = {};
h[key] = value;
return h;
};
var key = "foo";
var data = hash(key, "hoge");
// data
// Object {foo: "hoge"}

dataのキーにfooを設定できている。

Mocha と should.js で例外が生じるケースのテストを書く

Mochashould.jsでテストを書いていて例外が起こるテストを書いていて、少し詰まったのでメモ。

例えばhogeというメソッドに空文字列を渡すと例外が生じるというケースのテストを書きたいときに、

foo.hoge('').should.throw();

という書き方をすると、これは誤りで

(function(){ foo.hoge('') }).should.throw();

と書くのが正解。*1

実際に書いてみると下の様な形になる。(確認用なのでテストの中にクラス入れているが、ここは本来requireして呼び出す形。)

これでmochaを実行すると、

$ node_modules/mocha/bin/mocha --reporter spec
Foo
#sample1
✓ equal test
#sample2
✓ exception test
2 passing (8ms)

といった形で例外を起こすテストができている。

*1:https://github.com/visionmedia/should.js/#throw-and-throwerror に書いてあるのだが....