Herokuとnode.jsを使って簡単なWebアプリ

さて、Herokuを使って簡単なWebアプリを公開する手順をメモする。

言語としてnode.jsを使うので、現在の公式バージョン node.js 0.6を使える環境を探した。Windowsはそもそもnode.jsとherokuの開発に向いてなさそうなので考慮外。Linuxで探すとDebian 6は少し古いようだ(nodejsをパッケージとして入っていない。apt-getで取得できない)。Ubuntu 12.04ならOK。

Ubuntu12.04を(仮想OSに)インストールして、

apt-get install git nodejs npm

と実行する。herokuにはgitも必要らしいし、node.jsをherokuで使う場合npmも必要らしいからだ。
開発環境のために

apt-get install zsh lv emacs23-nox vsftpd

も実行。

$HOME/.ssh というディレクトリにSSH鍵ファイルを用意しておく。WindowsからWinSCP使ってFTPで転送した。

$HOME/.ssh/id_dsa
$HOME/.ssh/id_dsa.pub

Herokuにアクセスするツールを取得。
https://toolbelt.heroku.com/linux
に書いてあるとおり実行。

   wget https://toolbelt.heroku.com/install.sh
   sh ./install.sh

そのあとは
https://devcenter.heroku.com/articles/nodejs
に書いてあるとおり実行。

foo@ubuntu1204sv ~ % heroku login
Enter your Heroku credentials.
Email: itouhiro@example.com
Password (typing will be hidden):
Found existing public key: /home/foo/.ssh/id_dsa.pub
Uploading SSH public key /home/foo/.ssh/id_dsa.pub
Authentication successful.
foo@ubuntu1204sv ~ %

foo@ubuntu1204sv ~ % mkdir webaborn
foo@ubuntu1204sv ~ % cd webaborn
foo@ubuntu1204sv ~/webaborn % heroku create --stack cedar
Creating pure-stream-5725... done, stack is cedar
http://pure-stream-5725.herokuapp.com/ | git@heroku.com:pure-stream-5725.git

これで、名前がwebabornという、中身はまだカラのアプリができたようです。 (←名前は「pure-stream-5725」だぞ。この文書の後のほうで消します)



さてnode.jsでアプリを作るぞ。
https://devcenter.heroku.com/articles/nodejs
によると、npmも必要なのでそれはapt-getで入れた。
そのページに書いてあるweb.jsを実行してみると、expressというフレームワークがインストールされてないというエラー。

foo@ubuntu1204sv ~/webaborn % node web.js
node.js:201
        throw e; // process.nextTick error, or 'error' event on first tick
              ^
Error: Cannot find module 'express'

expressフレームワークの入れ方だが、
この https://devcenter.heroku.com/articles/nodejs ページとほぼ同じことをやっている
http://www.eiplab.com/2011/06/heroku-node-js-express-helloworld/
によると

$ npm install express

を実行してexpressのバージョンを覚えろ、とのこと。

foo@ubuntu1204sv ~/webaborn % npm install express
npm http GET https://registry.npmjs.org/express
...
npm http 200 https://registry.npmjs.org/formidable/-/formidable-1.0.9.tgz
express@2.5.9 ./node_modules/express
├── qs@0.4.2
├── mime@1.2.4
├── mkdirp@0.3.0
└── connect@1.8.7

2.5.9か。

ファイルpackage.jsonを作成する。

{
  "name": "webaborn",
  "version": "0.0.1",
  "dependencies": {
    "express": "2.5.9"
  }
}
$ npm install

を実行すると package.jsonを読み込んで何か設定してくれたらしい。


ちなみにpackage.json

    "express": "2.5.9",

と最後にコンマを書いたらnpm installがエラーになるので注意。

Procfileというファイルを作り、中にこう書く。

web: node web.js

そのファイルがherokuがファイルを実行するときの指令書のようだ。
そのファイルを読んで実行するコマンドが以下。

$ foreman start

ちゃんと実行できてるな。



さてそれでは開発したファイルをgitに登録する。
まず必要ないファイルは登録しないように設定する。

foo@ubuntu1204sv ~/webaborn % echo "node_modules" > .gitignore

foo@ubuntu1204sv ~/webaborn % git config --global user.name "itouhiro"
foo@ubuntu1204sv ~/webaborn % git config --global user.email "itou.hiroki@example.com"
foo@ubuntu1204sv ~/webaborn % git config --global color.ui "auto"
foo@ubuntu1204sv ~/webaborn % git config --global core.pager "lv -c"

それでは登録。

foo@ubuntu1204sv ~/webaborn % git init
Initialized empty Git repository in /home/foo/webaborn/.git/
foo@ubuntu1204sv ~/webaborn % git add .
foo@ubuntu1204sv ~/webaborn % git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   .gitignore
#       new file:   Procfile
#       new file:   package.json
#       new file:   web.js
#
foo@ubuntu1204sv ~/webaborn % git commit -m "hello world"
[master (root-commit) c068179] hello world
 4 files changed, 20 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Procfile
 create mode 100644 package.json
 create mode 100644 web.js
foo@ubuntu1204sv ~/webaborn %

さてそれではdeploy(Webアプリとして設置)。

foo@ubuntu1204sv ~/webaborn % heroku create --stack cedar
Creating stark-day-7906... done, stack is cedar
http://stark-day-7906.herokuapp.com/ | git@heroku.com:stark-day-7906.git
Git remote heroku added


foo@ubuntu1204sv ~/webaborn % git push heroku master
The authenticity of host 'heroku.com (50.19.85.132)' can't be established.
RSA key fingerprint is 8b:48:5e:67:0e:c9:16:47:32:f2:87:0c:1f:c8:60:ad.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'heroku.com,50.19.85.132' (RSA) to the list of known hosts.
Counting objects: 6, done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 615 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)

-----> Heroku receiving push
-----> Node.js app detected
-----> Resolving engine versions

       WARNING: No version of Node.js specified in package.json, see:
       https://devcenter.heroku.com/articles/nodejs-versions

       Using Node.js version: 0.4.7
       Using npm version: 1.0.106
-----> Fetching Node.js binaries
-----> Vendoring node into slug
-----> Installing dependencies with npm
       express@2.5.9 ./node_modules/express
       ├── mkdirp@0.3.0
       ├── qs@0.4.2
       ├── mime@1.2.4
       └── connect@1.8.7
       express@2.5.9 /tmp/build_3csnlz01f4x7y/node_modules/express
       connect@1.8.7 /tmp/build_3csnlz01f4x7y/node_modules/express/node_modules/connect
       qs@0.4.2 /tmp/build_3csnlz01f4x7y/node_modules/express/node_modules/qs
       mime@1.2.4 /tmp/build_3csnlz01f4x7y/node_modules/express/node_modules/mime
       formidable@1.0.9 /tmp/build_3csnlz01f4x7y/node_modules/express/node_modules/connect/node_modules/formidable
       mkdirp@0.3.0 /tmp/build_3csnlz01f4x7y/node_modules/express/node_modules/mkdirp
       Dependencies installed
-----> Discovering process types
       Procfile declares types -> web
-----> Compiled slug size is 3.3MB
-----> Launching... done, v3
       http://stark-day-7906.herokuapp.com deployed to Heroku

To git@heroku.com:stark-day-7906.git
 * [new branch]      master -> master
foo@ubuntu1204sv ~/webaborn %

あれれ、node.jsのバージョン0.4で動いてるじゃないか。package.jsonの書き方に不備があったらしい。
まあ動作はするだろう。

プロセス数を指定?する必要があるようだ。

foo@ubuntu1204sv ~/webaborn % heroku ps:scale web=1
Scaling web processes... done, now running 1
foo@ubuntu1204sv ~/webaborn % heroku ps
Process  State      Command
-------  ---------  -----------
web.1    up for 2m  node web.js

foo@ubuntu1204sv ~/webaborn % heroku ps:scale web=0

とするとプロセスなくなるので停止ということかな。

foo@ubuntu1204sv ~/webaborn % heroku logs
2012-05-16T09:07:26+00:00 heroku[slugc]: Slug compilation started
2012-05-16T09:07:37+00:00 heroku[api]: Config add PATH by itou.hiroki@example.com
2012-05-16T09:07:37+00:00 heroku[api]: Release v2 created by itou.hiroki@example.com
2012-05-16T09:07:37+00:00 heroku[api]: Deploy c068179 by itou.hiroki@example.com
2012-05-16T09:07:37+00:00 heroku[api]: Release v3 created by itou.hiroki@example.com
2012-05-16T09:07:38+00:00 heroku[web.1]: State changed from created to starting
2012-05-16T09:07:38+00:00 heroku[slugc]: Slug compilation finished
2012-05-16T09:07:39+00:00 heroku[web.1]: Starting process with command `node web.js`
2012-05-16T09:07:40+00:00 app[web.1]: listening on 18011
2012-05-16T09:07:40+00:00 heroku[web.1]: State changed from starting to up
2012-05-16T09:10:20+00:00 heroku[api]: Scale to web=1 by itou.hiroki@example.com
foo@ubuntu1204sv ~/webaborn %

Expressフレームワーク使ってる場合、以下を実行らしい‥‥。

foo@ubuntu1204sv ~/webaborn % heroku config:add NODE_ENV=production
Adding config vars and restarting app... done, v4
  NODE_ENV => production

↑これはやはり完成版に使うべきだった様子。未完成なら使わなくてよいと思う。

foo@ubuntu1204sv ~/webaborn % heroku open
Opening http://stark-day-7906.herokuapp.com/
Failure in opening http://stark-day-7906.herokuapp.com/ with options {}: Unable to find a browser command. If this is unexpected, Please rerun with environment variable LAUNCHY_DEBUG=true or the '-d' commandline option and file a bug at https://github.com/copiousfreetime/launchy/issues/new
foo@ubuntu1204sv ~/webaborn %

あれー。確かに http://stark-day-7906.herokuapp.com/ は動いてる。
http://webaborn.herokuapp.com/ は動いてないじゃないか。


http://kray.jp/blog/twitter_service_in_1hours/

Herokuはコマンドラインからheroku create コマンドで、プロジェクトを登録できます。
% heroku create twitter-helloworld
twitter-helloworldは適宜変更してください。すでに利用されているプロジェクト名は使えません。

そういうことなのか。
確かにログにも

foo@ubuntu1204sv ~/webaborn % heroku create --stack cedar
Creating stark-day-7906... done, stack is cedar
http://stark-day-7906.herokuapp.com/ | git@heroku.com:stark-day-7906.git

と残ってるな。
これでいろいろ試してみよう。


とりあえず Node.jsとnpmのバージョンを指定した。

package.json:

{
  "name": "webaborn",
  "version": "0.0.1",
  "dependencies": {
    "node": "0.6.12",
    "npm": "1.1.4",
    "express": "2.5.9"
  }
}
foo@ubuntu1204sv ~/webaborn % git add .
foo@ubuntu1204sv ~/webaborn % git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   package.json
#
foo@ubuntu1204sv ~/webaborn % git commit -m "specify version of node.js and npm"
[master 00b5df8] specify version of node.js and npm
 1 file changed, 2 insertions(+)
foo@ubuntu1204sv ~/webaborn % git log

↑こうすると、npm install が通らなくなる。まずいかも
←文法まちがってただけ。

{
  "name": "webaborn",
  "version": "0.0.1",
  "engines": {
    "node": "0.6.12",
    "npm": "1.1.4"
  },
  "dependencies": {
    "express": "2.5.9"
  }
}

だとnpm install通る。

foo@ubuntu1204sv ~/webaborn % git push heroku master

これで動作した。




作成したプロジェクトを消す方法

foo@ubuntu1204sv ~/webaborn % heroku apps --help
  apps:create [NAME]   #  apps:create [NAME]
  apps:destroy         #  apps:destroy
  apps:info            #  apps:info
  apps:open            #  apps:open
  apps:rename NEWNAME  #  apps:rename NEWNAME

foo@ubuntu1204sv ~/webaborn % heroku apps:destroy --app pure-stream-5725

 !    WARNING: Potentially Destructive Action
 !    This command will destroy pure-stream-5725 (including all add-ons).
 !    To proceed, type "pure-stream-5725" or re-run this command with --confirm pure-stream-5725

> pure-stream-5725
Destroying pure-stream-5725 (including all add-ons)... done


さて新しくWebApp作るよ。

foo@ub1204 ~ % heroku login
Enter your Heroku credentials.
Email: itou.hiroki@example.com
Password (typing will be hidden):
Authentication successful.

foo@ub1204 ~ % heroku apps:create webaborn -s cedar

 !    Notice: on Wed, 20 June, our default stack will change to Cedar. http://bit.ly/Lh0rM5
Creating webaborn... done, stack is cedar
http://webaborn.herokuapp.com/ | git@heroku.com:webaborn.git
foo@ub1204 ~ %
-s cedar

とはどういう意味か?

--stack

でも同じだけど、stackというのはOSと言語バージョンのこと。
https://devcenter.heroku.com/articles/stack
'cedar'というのはUbuntu10.04にnode.jsとか載ってるstackの名前。node.js使えるのはこれしかないし、今では
デフォルトもこれ。

スクリプトをコピーして、確認。

foo@ub1204 ~/webaborn % cp -p ~/share/wbab/* .
foo@ub1204 ~/webaborn % lv webaborn.js
foo@ub1204 ~/webaborn % lv webaborn.html
foo@ub1204 ~/webaborn % lv template-webaborn-jssearch.js
foo@ub1204 ~/webaborn % lv template-webaborn-xpathsearch.js
foo@ub1204 ~/webaborn % emacs webaborn.js

package.json
{
  "name": "webaborn",
  "version": "13.0.0",
  "dependencies": {
  },
  "engines": {
    "node": "0.6.x"
  }
}

Procfile
web: node webaborn.js
foo@ub1204 ~/webaborn % npm install

ふむ。問題なし。

foo@ub1204 ~/webaborn % echo node_modules > .gitignore

動作確認。

foo@ub1204 ~/webaborn % foreman start
18:55:00 web.1     | started with pid 1512
18:55:00 web.1     | Listening on 5000
^CSIGINT received
18:55:57 system    | sending SIGTERM to all processes
18:55:57 system    | sending SIGTERM to pid 1512
18:55:57 web.1     | process terminated

動くよ。

foo@ub1204 ~/webaborn % git config -l
foo@ub1204 ~/webaborn % git config --global user.name "itouhiro"
foo@ub1204 ~/webaborn % git config --global user.email "itou.hiroki@example.com"
foo@ub1204 ~/webaborn % git config --global color.ui "auto"
foo@ub1204 ~/webaborn % git config --global core.pager "lv -c"
foo@ub1204 ~/webaborn % git config -l
user.name=itouhiro
user.email=itou.hiroki@example.com
color.ui=auto
core.pager=lv -c
foo@ub1204 ~/webaborn % git init
Initialized empty Git repository in /home/foo/webaborn/.git/
foo@ub1204 ~/webaborn % git add .
foo@ub1204 ~/webaborn % git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   .gitignore
#       new file:   Procfile
#       new file:   package.json
#       new file:   template-webaborn-jssearch.js
#       new file:   template-webaborn-xpathsearch.js
#       new file:   webaborn.html
#       new file:   webaborn.js
#


foo@ub1204 ~/webaborn % git commit -m "version 13 RC"
[master (root-commit) 3201e2d] version 13 RC
 7 files changed, 627 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Procfile
 create mode 100644 package.json
 create mode 100644 template-webaborn-jssearch.js
 create mode 100644 template-webaborn-xpathsearch.js
 create mode 100644 webaborn.html
 create mode 100644 webaborn.js
foo@ub1204 ~/webaborn % git push heroku master
fatal: 'heroku' does not appear to be a git repository
fatal: The remote end hung up unexpectedly

なんですかこのエラーは。どうもgit repository構築前にheroku apps:createしてしまったのがまずかったようだ。
http://stackoverflow.com/questions/2294024/cloned-project-from-github-heroku-does-not-work

foo@ub1204 ~/webaborn % git remote add heroku git@heroku.com:webaborn.git

とくに何もいわないけどこれでつながったようだ。

foo@ub1204 ~/webaborn % git push heroku master
The authenticity of host 'heroku.com (50.19.85.132)' can't be established.
RSA key fingerprint is 8b:48:5e:67:0e:c9:16:47:32:f2:87:0c:1f:c8:60:ad.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'heroku.com,50.19.85.132' (RSA) to the list of known hosts.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0666 for '/home/foo/.ssh/id_dsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
bad permissions: ignore key: /home/foo/.ssh/id_dsa
Permission denied (publickey).
fatal: The remote end hung up unexpectedly

ファイルのpermissionを修正。

foo@ub1204 ~/webaborn % ls -l ~/.ssh/
total 16
-rw-rw-rw- 1 foo foo  668 Feb  5  2011 id_dsa
-rw-rw-rw- 1 foo foo 1313 Feb  5  2011 id_dsa.OpenSSH.txt
-rw-rw-rw- 1 foo foo  600 Feb  5  2011 id_dsa.pub
-rw-r--r-- 1 foo foo  884 Jun 16 19:15 known_hosts
foo@ub1204 ~/webaborn % chmod 600 ~/.ssh/id_dsa

もう一度deploy

foo@ub1204 ~/webaborn % git push heroku master
Warning: Permanently added the RSA host key for IP address '50.19.85.156' to the list of known hosts.
Counting objects: 9, done.
Compressing objects: 100% (7/7), done.
Writing objects: 100% (9/9), 13.55 KiB, done.
Total 9 (delta 0), reused 0 (delta 0)

-----> Heroku receiving push
-----> Node.js app detected
-----> Resolving engine versions
       Using Node.js version: 0.6.18
       Using npm version: 1.0.106
-----> Fetching Node.js binaries
-----> Vendoring node into slug
-----> Installing dependencies with npm

       Dependencies installed
-----> Discovering process types
       Procfile declares types -> web
-----> Compiled slug size is 3.2MB
-----> Launching... done, v3
       http://webaborn.herokuapp.com deployed to Heroku

To git@heroku.com:webaborn.git
 * [new branch]      master -> master
foo@ub1204 ~/webaborn %


起動させるぞ。

foo@ub1204 ~/webaborn % heroku ps:scale web=1
Scaling web processes... done, now running 1

確認。

foo@ub1204 ~/webaborn % heroku ps
=== web: `node webaborn.js`
web.1: up for 1m

http://webaborn.herokuapp.com をブラウザで確認。