Deployment of a SANE App (Part 1)

You’ve created a web app using the SANE stack (using an Ember.js client and a Sails.js API), and now you’re ready to get it out there for the world to see. There are many ways to handle deployments, with varying complexity and tradeoffs. This series of posts will present a few of those methods, with increasing levels of complexity, for deploying your app to AWS. There are many options of where to deploy your app, including Heroku, Firebase, DigitalOcean, AWS, and others. I’ve chosen to use AWS as it is currently the largest and most well-known.

If you don't already have an account with AWS, you can get started with their free tier for 12 months, which gives you access to everything we will be using here. But you will be billed if you create a more high-power box or add some types of special features, so make sure you understand the cost implications of everything you launch.

Setting up a single EC2 Instance

When your app is young and you have between 0 and 100 users, it makes sense to keep the architecture as simple as possible. This means running all the pieces of your app (web server, database, API server, etc.) on a single EC2 instance.

Step 1: Choose and Create EC2 Instance

The first thing to do is pick out a base image to use for your instance. As of July, 2015, the image I’ve chosen to use is “Ubuntu Trusty 14.04 LTS hvm (x64)” with SSD disk storage. You can use http://cloud-images.ubuntu.com/locator/ec2/ to find the image you want to use, which will depend on your region. I’m in us-east-1, so I will use "ami-c135f3aa". This isn’t intended to be an in-depth guide on AWS itself, so I won’t get into the step-by-step of how to create the instance, but there are plenty of resources out there to take you through that process.

Step 2: Configure

Once your EC2 instance is up and running, you will need to SSH in so that you can make some changes. In more advanced methods, you will never need to manually log in to an individual instance, but that will come later. After you are logged in, perform the following actions:

Update apt-get repository and installed packages

sudo apt-get update  
sudo apt-get -y upgrade  

Install node

There are many ways to install node.js on a system. Feel free to use whatever way you are comfortable with. The simplest and quickest way I have found is with using Nave from Isaac Schlueter. The method below will take ownership of some directories in /usr/local, which is fine as long as you don't plan to run the EC2 instance with a user other than the default ubuntu. It will install the latest stable version of node.js, but you may also specify a version in place of stable on the last line.

mkdir ~/.nave  
cd ~/.nave  
wget http://github.com/isaacs/nave/raw/master/nave.sh  
chmod u+x nave.sh  
sudo ln -s $PWD/nave.sh /usr/local/bin/nave  
sudo mkdir -p /usr/local/{share/man,bin,lib/node,lib/node_modules,include/node}  
sudo chown -R $USER /usr/local/{share/man,bin,lib/node,lib/node_modules,include/node}  
nave usemain stable  

To verify node installed correctly, use node --version

Install NPM

wget -O - http://npmjs.com/install.sh | sh  

Install Ember, Sails, and other dependencies and tools

npm i -g sails  
npm i -g ember-cli  
npm i -g bower  
npm i -g pm2

sudo apt-get install -y git  

Additionally, you will need to install the database you are using (MySQL, PostgreSQL, MongoDB, etc.), as well as any other packages you may need like make and g++.

Step 3: Deploy App Code

Now that your EC2 instance is set up with the necessary environment, you need some way to get your application code to the server. There are many ways to do this, and I will be exploring other methods in future posts, but as a simple first step you can just clone it from your git repository. In this section, that's what we will do, and we will get the application to the point where it is ready to start.

Clone project into a directory

cd ~  
git clone <git repo url>  

Install project dependencies

If you've gotten to the point of deploying your app, you likely know how all this goes, but I'm including it here for the sake of completeness. Assuming you use the default SANE structure of using client and server directories, it will look like this:

cd <project-dir>  
npm I  
cd server  
npm i  
cd ../client  
bower install  
npm i  

Build your ember app for production

You now need to run your Ember app through a production build to concatenate, minify, etc. From the client directory, run:

ember build --environment=production --output-path=../server/assets/  

This will replace the default index.html which Sails comes with, so that users will get the Ember app instead.

Step 4: Start the App

The EC2 box is built, the application code is deployed, and now we are ready to fire it up. As always, there is a little yak shaving to get out of the way first.

Allow Node to listen on low ports

In Linux systems, root permissions are usually required in order for a process to listen on a port below 1024. We want our web app to listen on port 80 (HTTP) and possibly 443 (SSL), so we will need to make some changes. There are two main ways to handle this. The most robust is to add a web server like Nginx as a reverse proxy which forwards requests to the Node.js app. You can read about that in the post Deployment of a SANE App (Part 2) - Adding an Nginx Reverse Proxy, but in the interest of keeping things simple for now, we will just give the sails app permissions to listen on low ports.

sudo apt-get install -y libcap2-bin  
sudo setcap cap_net_bind_service=+ep /usr/local/bin/node  

See http://technosophos.com/2012/12/17/run-nodejs-apps-low-ports-without-running-root.html for more details on what we're doing here.

Start App

You could just start your Sails app in production mode at this point, and you would have a working app. However, if it ever died for some reason, your site would go down and you would manually have to start it back up. Ain't nobody got time for that, so we are going to use PM2 instead. From within your server directory:

pm2 start app.js -- --prod  

Notice the two sets of double dashes. The sails option for production is --prod, but in order for pm2 to pass arguments to the app, you need another set of --.

Start App on server boot

Congratulations, you just deployed your app. You should be able to visit the public IP given to your EC2 instance and see the app. Technically this is all you need to do. But wouldn't it be nice if the app would start up automatically if the server reboots? Maybe you're still testing and you want to start and stop the EC2 instance to save cost. If so, this is what you want:

sudo env PATH=$PATH:/usr/local/bin pm2 startup -u $USER  

Summary

You have a running application with a little bit of fanciness like autorestart. But once you start getting users you're going to want to improve the architecture to make it easier to deploy, scale, and manage. In future posts we will discuss adding a reverse proxy and load balancer, as well as configuration management and continuous deployment techniques.

Further Reading

To take the next step in making your deployment more flexible and robust, read Deployment of a SANE App (Part 2) - Adding an Nginx Reverse Proxy.