I’m involved with a project where our ruby/rails developer dropped out, so I decided to take on the job using node.js (rather than learn rails). We initially were using services from dotCloud, but it was too flakey from day to day and our demo was coming up. For hosting, Amazon’s EC2 was the obvious candidate, but I’d have to setup and provision the entire server from scratch. This is that story
Here’s what we’ll do
- choose a Linux image
- create a HelloWorld node.js server
- use git to push code changes to the server
- automatically restart node after pushing with git
- set up node to run long term using supervisor
Setup a New EC2 Instance
launch a new Ubuntu instance
First things first, login to your AWS console and launch a new Ubuntu Linux image for your new EC2 server. Select the Community AMIs tab and search for this one:
099720109477/ebs/ubuntu-images/ubuntu-maverick-10.10-i386-server-20101225
I choose Ubuntu over other Linux distributions because more of what I needed was already available via the standard package manager (redis, couchdb, etc…). At this point, I usually assign an elastic IP address to my new instances before proceeding with ssh.
update your new system
Once your new instance is up and running, login and update the system. The upgrade might take some time.
$ sudo apt-get update
$ sudo apt-get -y upgrade
install the rcconf service utility
This will make it easy to manage services
$ sudo apt-get install rcconf
install some build tools including git
$ sudo apt-get install build-essential
$ sudo apt-get install libssl-dev
$ sudo apt-get install git-core
libssl-dev is needed to use the crypt node.js package
build node
You can view the [node.js installation instructions](https://github.com/joyent/node/wiki/Installation “Node.js installation”) or just follow what I did.
$ wget http://nodejs.org/dist/node-latest.tar.gz
$ tar xzf node-latest.tar.gz
$ cd node-v0.4.7
$ ./configure --prefix=/usr
$ make
$ sudo make install
I used –prefix=/usr to install node on the existing PATH. make install can take quite a while… go brew some espresso.
install the node package manager npm:
Get the latest npm from github and install.
$ cd ~
$ git clone http://github.com/isaacs/npm.git
$ cd npm
$ sudo make install
get some really good node packages
$ cd ~
$ npm install connect redis connect-redis jade express express-resource futures emailjs
install a web server
$ sudo apt-get install nginx
Edit the nginx default configuration file replacing the root (/) location section
$ sudo vi /etc/nginx/sites-enabled/default
Use a proxy passengry entry. This will forward your requests to your node server
location / {
proxy_pass http://127.0.0.1:8124/;
}
Restart ngnix
$ sudo /etc/init.d/nginx restart
Hello New Server!
create a directory for your node server
$ mkdir ~/www
$ cd ~/www
create a HelloWorld server
$ cat > server.js
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');
Start the server
$ node server.js
test your new server
Test in a browser by navigating to http://your.static.ip/
Shutdown your new server before continuing…
Bring in Source Control!
Now we’ll get to the good part where we can leverage git to deploy new server code
create a remote repository for our node project
Create a bare repository outside the www folder
$ mkdir ~/repo
$ cd ~/repo
$ git init --bare
create a git hook
Create a post-recieve hook that will copy over new code after it’s been pushed to the repository
$ cat > hooks/post-receive
#!/bin/sh
GIT_WORK_TREE=/home/ubuntu/www
export GIT_WORK_TREE
git checkout -f
$ chmod +x hooks/post-receive
add this remote repository to your LOCAL repository:
On your local development machine, setup a repository for your new server
$ mkdir helloworld
$ cd helloworld
$ git init
$ git remote add ec2 ssh://ubuntu@your.static.ip/home/ubuntu/repo
create a local HelloWorld node server
$ cat > server.js
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');
add and commit it to your local repository
$ git add server.js
$ git commit -m 'first'
push our local code changes to the remote repository:
$ git push ec2 +master:refs/heads/master
you only need to specify +master:refs/heads/mast the first time
test your new server
Test in a browser by navigating to http://your.static.ip/
Have Your Node Server Run Forever
dotCloud uses supervisor to keep node servers up after reboot and crashing. We’ll do the same. So, back on your EC2 instance:
$ sudo apt-get install python-setuptools
$ sudo easy_install supervisor
install it as a service
$ curl https://raw.github.com/gist/176149/88d0d68c4af22a7474ad1d011659ea2d27e35b8d/supervisord.sh > supervisord
$ chmod +x supervisord
$ sudo mv supervisord /etc/init.d/supervisord
set the service to start on boot
check the supervisord service in the services list using rcconf
$ sudo rcconf
create a configuration file
$ sudo echo_supervisord_conf > supervisord.conf
$ sudo mv supervisord.conf /etc/supervisord.conf
edit the new configuration file
$ sudo vi /etc/supervisord.conf
change the permissions of the [unix_http_server]
chmod=0777 ; sockef file mode (default 0700)
set the user that supervisord runs as to be ‘ubuntu’
This is under the [supervisord] section
user=ubuntu
add a section that describes your node server
Note that we can set the NODE_ENV variable here.
[program:node]
command=node server.js
directory=/home/ubuntu/www
environment=NODE_ENV=production
reload supervisord
$ supervisorctl reload
You can also restart the service
$ /etc/init.d/supervisord restart
restart supervisord when changes are pushed
In the post-recieve git hook we previously created, append a final command that restarts supervisord
$ vi ~/repo/hooks/post-receive
#!/bin/sh
GIT_WORK_TREE=/home/ubuntu/www
export GIT_WORK_TREE
git checkout -f
sudo supervisorctl restart node
That’s It
Now you can work on a local development machine, push your changes to the server using git, sit back and relax while the post-recieve hook tells supervisor to restart your node server. Refresh the browser and throw the confetti!