beanstalkでNode.jsを動かしてみた (1) AMI作成編

某イベントでもこっそり使っていましたが、Node.jsをBeanstalkで動かしてみました。
Beanstalkが東京上陸したので、東京にもAMIを作成してみます。
AMIの作り方はいいから早く動かしたいぜ、という人は次のエントリをどうぞ!

準備

PHPのAMI(ami-32358433 PHPBeanstalk64-2012.03.30)を元に作業をします。
SecurityGroupでssh, httpと、3000を開けておいてください。

nvm導入

nvmを導入します (参考: Node.jsとnvmを初めてインストールするときのハマりポイントと対策 - ess sup)

[ec2-user@ip-10-153-59-170: ~] $ sudo yum install -y openssl-devel
[ec2-user@ip-10-153-59-170: ~] $ sudo su - elasticbeanstalk
[elasticbeanstalk@ip-10-153-59-170 ~]$ git clone git://github.com/creationix/nvm.git ~/.nvm
[elasticbeanstalk@ip-10-153-59-170 ~]$ . ~/.nvm/nvm.sh
[elasticbeanstalk@ip-10-153-59-170 ~]$ nvm install v0.6.15
                   :
'install' finished successfully (0.525s)
Now using node v0.6.15
~/.nvm/v0.6.15/bin/npm
[elasticbeanstalk@ip-10-153-59-170 ~]$ echo . ~/.nvm/nvm.sh >> .bashrc 
[elasticbeanstalk@ip-10-153-59-170 ~]$ echo nvm use 0 >> .bashrc 

npmを使い、node-devとexpressを入れておきます。

[elasticbeanstalk@ip-10-153-59-170 ~]$ npm install node-dev -g
[elasticbeanstalk@ip-10-153-59-170 ~]$ npm install express -g

/var/www/htmlにexpressでアプリのひな形を作ります。

[elasticbeanstalk@ip-10-153-59-170 ~]$ cd /var/www/html
[elasticbeanstalk@ip-10-153-59-170 html]$ echo launching... > index.html  (注:deploy前にhealthyと判断されるために必要です)
[elasticbeanstalk@ip-10-153-59-170 html]$ express .
[elasticbeanstalk@ip-10-153-59-170 html]$ npm install
[elasticbeanstalk@ip-10-153-59-170 html]$ node-dev app.js 
Express server listening on port 3000 in development mode

サーバにアクセスしてみます。

ちゃんと起動しています。Node.jsのインストールはこれで一応完了です。

apache設定

sldeshare:AWSマイスターシリーズReloaded(AWS Beanstalk)にあるように、基本的にBeanstalkアプリはApache経由でアクセスされます。そこで、/に来たアクセスをlocalhost:3000にreverse proxyを行い、コンテンツにアクセス出来るように設定します。1つ注意が必要なのが、先ほどのスライドにあるような/_hostmanagerへのアクセスの設定までもproxyしてしまうと、hostがダウンしていると判定されてしまいます。
この設定は/etc/httpd/sites/hostmanager に書かれています。

## Setup proxy to redirect to HostManager for ELB Health Check
<VirtualHost *:80>
        <Proxy *>
                Order deny,allow
                Allow from all
        </Proxy>

        ProxyPass /_hostmanager http://localhost:8999 retry=0
        ProxyPassReverse /_hostmanager http://localhost:8999

        ## Add an exclusion for the host manager communication so that it doesn't
        ##  get globbed into the / proxypass
        ProxyPass /_hostmanager !
</VirtualHost>

ProxyPassの最後の行の後に

        ProxyPass / http://localhost:3000/
        ProxyPassReverse / http://localhost:3000/

と追加してapacheを再起動(sudo service httpd restart)すると、httpでつないでもnode.jsのアプリケーションにアクセスできるようになります。

deploy時スクリプト修正

実際にアプリケーションをdeployした後には、/etc/httpd/sites/application というファイルが生成され、下記のような内容となります。

NameVirtualHost *:80

<VirtualHost *:80>
    DocumentRoot /var/www/html
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" proxy
    SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded
    CustomLog "/var/log/httpd/application-access_log" combined env=!forwarded
    CustomLog "/var/log/httpd/application-access_log" proxy env=forwarded
    ErrorLog /var/log/httpd/application-error_log

    <Directory "/var/www/html">
        Options Indexes FollowSymLinks
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>

    ## Setup proxy to redirect to HostManager for ELB Health Check
    <Proxy *>
        Order deny,allow
        Allow from all
    </Proxy>

    ProxyPass /_hostmanager http://localhost:8999 retry=0
    ProxyPassReverse /_hostmanager http://localhost:8999
 
    ## Add an exclusion for the host manager communication so that it doesn't get globbed into the / proxypass
    ProxyPass /_hostmanager !
</VirtualHost>

このファイルは/opt/elasticbeanstalk/srv/hostmanager/lib/elasticbeanstalk/hostmanager/utils/apacheutil.rb というファイルで設定に応じて自動生成されます。先ほど行ったような修正と同じ事をするように、このスクリプトを修正します。

[elasticbeanstalk@ip-10-153-59-170 utils]$ diff apacheutil.rb.org apacheutil.rb
103a104,106
> 
>     ProxyPass / http://localhost:3000/
>     ProxyPassReverse / http://localhost:3000/

これにより、

    ProxyPass /_hostmanager !

    ProxyPass / http://localhost:3000/
    ProxyPassReverse / http://localhost:3000/

と、先ほどのProxyPassの最後の行の後に追加されます。
/_hostmanager以下へのアクセスは優先されて処理され、それ以外のアクセスは / へのアクセスとする設定となります。

起動時スクリプト作成

インスタンス起動時に以下のようなスクリプトを自動で実行するようにします。

[elasticbeanstalk@ip-10-153-59-170 ~]$ cat > ~elasticbeanstalk/bin/startup.sh 
#!/bin/sh
. ~/.nvm/nvm.sh
nvm use 0 # use the latest installed
while [ 1 ] 
do
        cd /var/www/html
        node-dev app.js
        sleep 5
done
[elasticbeanstalk@ip-10-153-59-170 ~]$ echo 'sudo su - elasticbeanstalk startup.sh &> /opt/elasticbeanstalk/var/log/node.log' | sudo tee -a /etc/rc.local 
sudo su - elasticbeanstalk startup.sh &> /opt/elasticbeanstalk/var/log/node.log

これで起動時に自動でNode.jsが起動します。また、deploy時にはnode-devがカレントディレクトリを失って落ちるため、自動で再起動するようにしています。
もう1つ、下記のようなコマンドを先ほどのNode.js起動前に入れておくと何かの役に立つかもしれません。
(publicのhostnameやIPアドレス環境変数から参照できるようにするものです)

[elasticbeanstalk@ip-10-153-59-170 ~]$ /opt/aws/bin/ec2-metadata -i -h -o -p -v | perl -pe 's/^/export /;s/-/_/;s/: /=/' > /etc/profile.d/ec2-meta-data.sh
(ログインし直す)
[elasticbeanstalk@ip-10-153-59-170 ~]$ echo $public_ipv4
175.41.200.42
[elasticbeanstalk@ip-10-153-59-170 ~]$ echo $public_hostname
ec2-175-41-200-42.ap-northeast-1.compute.amazonaws.com

これにより、/etc/rc.localは

[elasticbeanstalk@ip-10-153-59-170 ~]$ cat /etc/rc.local 
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local

/opt/aws/bin/ec2-metadata -i -h -o -p -v | perl -pe 's/^/export /;s/-/_/;s/: /=/' > /etc/profile.d/ec2-meta-data.sh
sudo su - elasticbeanstalk startup.sh &> /opt/elasticbeanstalk/var/log/node.log

のようになりました。

AMI作成

さて、この状態で、AMIを作りましょう。

$ ec2-create-image i-133fad13 -n "NodeBeanstalk64-2012.04.25" -d "Elastic Beanstalk Node.js v0.6.15 64-bit"
IMAGE   ami-14c87815

AMIが出来ました!
(デプロイ編に続きます)