Jenkinsでnodeのアドオンをビルドして、それを使ったhttpサーバアプリケーションを起動する

突然のJenkinsネタですが ^^;

Jenkinsでnodeのアドオンを自動でビルドして、
そのビルドスクリプトからnodeアプリ(サーバ)をサービスとして立ち上げるということをしてみたのですが
そのときのメモです。

# Jenkinsのジョブのワークスペースから直接サービスを起動しているので
# 自動デプロイっていうのかどうかは微妙な感じです。
# あくまでも動作確認の一環ですね。

Jenkinsのインストール、設定

Jenkinsのインストール、apacheからのリバースプロキシ設定、git/githubとの連携等
Jenkins(+git)を始めるのに以下のブログがわかりやすくまとまっていました。

ITエンジニアmegadreamsの開発日記 GitHubとJenkins連動 自動デプロイ 開発環境設定編

あとうちはFedoraなのですが、インストールの時に公開鍵がないって言われて
http://pkg.jenkins-ci.org/redhat/
に書いてあるように

sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key

が必要でした。

Jenkinsでnode addonのビルドをする準備

node-gypをjenkinsユーザで走らせることになるのですが、
node-gypの初回動作時にユーザーのホームの.node-gyp以下に
必要なファイルのインストールをするようなのです。

このときネットアクセスが必要になるので、会社等でプロキシ設定等が必要な場合は少し注意が必要です。
手動でプロキシ設定をしなくてはならない場合は、
su jenkinsしてプロキシ設定してからnode-gyp installしておくというような手順が必要です。

こんな感じです

[penkoba@devil ~]$ sudo su jenkins -s /bin/bash
[jenkins@devil ~]$ export http_proxy=http://proxy.example.com:3128
[jenkins@devil ~]$ node-gyp install
gyp info it worked if it ends with ok
gyp info using node-gyp@0.9.6
gyp info using node@0.10.7 | linux | ia32
gyp http GET http://nodejs.org/dist/v0.10.7/node-v0.10.7.tar.gz
gyp http 200 http://nodejs.org/dist/v0.10.7/node-v0.10.7.tar.gz
0.10.7
gyp info ok 

nodeアプリをデーモンとして起動する準備

nodeアプリをデーモンとして起動するために、foreverを使用します。

インストール

sudo npm install -g forever

foreverを使用したnodeアプリの起動と停止

forever start app.js
forever stop app.js

Jenkinsのジョブ設定

以上で準備は整いましたので、Jenkinsのプロジェクトを作成してビルド&サーバー起動してみます。

nodeアプリのリポジトリ構成は以下のようだと思って下さい。
addon以下にmy_addonモジュールのソースがあり、
サーバスクリプトはserver/app.jsです。
shebangと実行パーミッションをつけて直接起動できるようにしてあります

.
|-- addon
|   |-- binding.gyp
|   `-- my_addon.cc
`-- server
    |-- node_modules
    |   |-- ... 
    |   |-- ... 
    |   `-- ... 
    |-- public
    |   |-- ... 
    |   |-- ... 
    |   `-- ... 
    `-- app.js

Jenkinsのビルドスクリプトを以下のように書きます。

#!/bin/sh
export PATH=/usr/local/bin:$PATH
forever stop server/app.js
( \
cd addon; \
node-gyp configure build; \
)
BUILD_ID=dontKillMe forever start server/app.js

ここで一つ注意点があり、
forever start server/app.js
の起動時にBUILD_IDという変数を指定しています。

これは https://wiki.jenkins-ci.org/display/JENKINS/ProcessTreeKiller に書いてある通り、
この変数をなにか適当な値で上書きしないとJenkinsのProcessTreeKillerという仕組みによって
ビルドのジョブが完了した時点でその中で起動したプロセスはすべて殺されてしまいます。
ここでは例の通りdontKillMeという値をセットしていますが、たぶん何でもいいはずです。

実行

では実行してみます。ジョブのコンソール出力はこんな感じになりました。
(2回目以降のログです。初回起動時はforever stopのところのログが少し違うと思います)

Started by user Punch Neko
Building in workspace /var/lib/jenkins/jobs/my_app/workspace
Checkout:workspace / /var/lib/jenkins/jobs/my_app/workspace - hudson.remoting.LocalChannel@1db1e7f
Using strategy: Default
Last Built Revision: Revision e518b266b1d011ad0a2835e36bbf15ee8478c5a4 (origin/master)
Fetching changes from 1 remote Git repository
Fetching upstream changes from origin
Commencing build of Revision e518b266b1d011ad0a2835e36bbf15ee8478c5a4 (origin/master)
Checking out Revision e518b266b1d011ad0a2835e36bbf15ee8478c5a4 (origin/master)
[workspace] $ /bin/sh /tmp/hudson1277186369459731033.sh
e[32minfoe[39m:    Forever stopped process:
e[90mdatae[39m:    e[37m   e[39m e[37muide[39m  e[90mcommande[39m                              e[90mscripte[39m                 e[37mforevere[39m e[37mpide[39m   e[35mlogfilee[39m                            e[33muptimee[39m       
[0] gL9T e[90m/opt/node-v0.10.7-linux-x86/bin/nodee[39m e[90mserver/app.jse[39m 12847   12849 e[35m/var/lib/jenkins/.forever/gL9T.loge[39m e[33m0:0:2:39.454e[39m 
gyp info it worked if it ends with ok
gyp info using node-gyp@0.9.6
gyp info using node@0.10.7 | linux | ia32
gyp info spawn python
gyp info spawn args [ '/opt/node-v0.10.7-linux-x86/lib/node_modules/node-gyp/gyp/gyp',
gyp info spawn args   'binding.gyp',
gyp info spawn args   '-f',
gyp info spawn args   'make',
gyp info spawn args   '-I',
gyp info spawn args   '/var/lib/jenkins/jobs/my_app/workspace/addon/build/config.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/opt/node-v0.10.7-linux-x86/lib/node_modules/node-gyp/addon.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/var/lib/jenkins/.node-gyp/0.10.7/common.gypi',
gyp info spawn args   '-Dlibrary=shared_library',
gyp info spawn args   '-Dvisibility=default',
gyp info spawn args   '-Dnode_root_dir=/var/lib/jenkins/.node-gyp/0.10.7',
gyp info spawn args   '-Dmodule_root_dir=/var/lib/jenkins/jobs/my_app/workspace/addon',
gyp info spawn args   '--depth=.',
gyp info spawn args   '--generator-output',
gyp info spawn args   'build',
gyp info spawn args   '-Goutput_dir=.' ]
gyp info spawn make
gyp info spawn args [ 'BUILDTYPE=Release', '-C', 'build' ]
make: Entering directory `/var/lib/jenkins/jobs/my_app/workspace/addon/build'
make: Nothing to be done for `all'.
make: Leaving directory `/var/lib/jenkins/jobs/my_app/workspace/addon/build'
gyp info ok 
e[33mwarne[39m:    --minUptime not set. Defaulting to: 1000ms
e[33mwarne[39m:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
e[32minfoe[39m:    Forever processing file: e[90mserver/app.jse[39m
Finished: SUCCESS

forever stopをしたのち、アドオンのビルド実行と、foreverでserver/app.jsを起動しています。

psで見てみます。

[penkoba@devil ~]$ ps -U jenkins uwww
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
jenkins   6955  0.2 12.1 805860 250564 ?       Ssl  01:38   2:32 /etc/alternatives/java -Dcom.sun.akuma.Daemon=daemonized -Djava.awt.headless=true -DJENKINS_HOME=/var/lib/jenkins -jar /usr/lib/jenkins/jenkins.war --logfile=/var/log/jenkins/jenkins.log --webroot=/var/cache/jenkins/war --daemon --httpPort=8080 --ajp13Port=8009 --debug=5 --handlerCountMax=100 --handlerCountMaxIdle=20
jenkins  13074  0.0  0.0  39412  1436 ?        Ssl  02:46   0:00 /opt/node-v0.10.7-linux-x86/bin/node /opt/node-v0.10.7-linux-x86/lib/node_modules/forever/bin/monitor server/app.js
jenkins  13076  0.0  0.5  96084 12152 ?        Sl   02:46   0:05 /opt/node-v0.10.7-linux-x86/bin/node /var/lib/jenkins/jobs/my_app/workspace/server/app.js

foreverの監視プロセスと、app.jsのプロセスが動いています。
またjenkinsでビルドを実行する度にこのプロセスIDが入れ替わりますので、
アプリケーションが起動しなおされている様子も確認できます。

以上をgitのコミットのフックで自動的に動作させれば、
常に最新の状態でnodeアプリがサービスされている環境ができあがります。
便利ですね ^^