Jigar Patel Driving fast in a slow lane!

A Guide to Deploying Rails Apps

December 04, 2011 / Jigar Patel

I am writing an ebook on basic DevOps for Ruby on Rails.

Do check it out for more detailed and up to date guide for deploying Ruby on Rails applications.

Devops 101 for Ruby on Rails

Note: This is not a definitive guide or a tutorial. I have simply collected all my notes related to deployment and compiled them into a blog post. Please check the documentation of the tools/apps mentioned here.

There are three major steps in the deployment process.

###A) Choosing the host / server

There are different types of hosting options that suit different people. You should choose an option depending on your requirements, budget and skills.

These are the popular hosting options in rails community

  • Shared Hosts e.g Dreamhost
  • VPS providers e.g Linode, Slicehost
  • Cloud servers e.g EC2, Rackspace Cloud (More or less similar to VPS boxes)
  • Dedicated Servers e.g Softlayer, Rackspace
  • PaaS Providers e.g Heroku, EngineYard

Choose shared hosts for extremely small personal projects only. These are cheaper than the rest. However, these are extremely low on resources and the host may terminate your account if your scripts are consuming resources more than what your account is allotted. Additionally, you do not get root access to your server.

Dreamhost is a popular shared host for rails.

I tend to put VPS boxes and cloud servers in the same basket. Although their underlying technologies are different, as an end user ,I get more or less the same functionalities and same performance. Simply put these are virtual machines hosted on large hardware boxes and behave almost like a typical UNIX box. You can get memory configurations ranging from 64 MB to 68.4 GB (on EC2). The CPU power usually is directly proportional to your memory size. Only exception to this case is EC2 where you get to choose the CPU configuration as well.

These are the popular VPS and cloud server hosts -

Host Notes
Prgmr Cheap, Good for small projects and side projects. Difficult to manage as it comes with a management console.
Linode Cheap, good for most projects. Nice service and consistent performance. High Memory / $
Rackspace Cloud Per hour billing. Easy to setup. Costlier than Linode. Good performance.
Amazon EC2 Per hour billing. Expensive. Performance depends on configuration. CPU on micro instance sucks. Variety of configuration options. Choose Medium or Large CPU instance for CPU intensive apps.

There are few other providers as well. The only negative point about VPS / cloud servers is poor disk I/O. Get a dedicated server if I/O causes to be a bottleneck.

Dedicated servers are expensive than the above mentioned options. They are full fledged hardware boxes. They usually come with a fully managed contract or self managed contract. Choose fully managed servers if you do not have in-house server admin skills. Some providers also let you choose an SSD (albeit at a premium). SSD’s provide a remarkable performance improvement in disk I/O.

Softlayer and Rackspace are popular options for dedicated boxes.

PaaS providers are the most expensive among the above mentioned hosts. They provide a fully managed ruby/rails stack built on top of services like Amazon EC2. Their performance is similar to the underlying cloud service. Choose them if you want to focus only on coding and forget about server admin stuff. Engine Yard and Heroku are the most popular options.

Engine Yard provides you a platform along with an EC2 instance. You can ssh into that instance and install anything.

Heroku is a bit more restrictive. You get a read only file system, you can only use postgres as your db and you do not get ssh access to the server. You can, however, install addons to overcome these restrictions.

A1) Choosing the server config

Determine the number of users and the max number of requests/s that you want to handle.

Determine the amount of memory consumed by your services and application server instances.

Number of application server instances required = (Max requests/s) / (Time taken by the longest request)

Minimum memory required = Memory consumed by the services + number of application server instances required * memory consumed by each application server instance

A2) Free servers

If you are running short on cash, or you simply want a server to learn stuff or demonstrate a prototype, buying a server does not make sense. There are few providers that let you test the water before you dive.

EC2 Micro - Free for first year. Around 600 MB of RAM / Low CPU

Heroku - One free dyno (presumably forever). Good for a small site. No background jobs.

Engine Yard - 500 hours free with a new account. One medium CPU EC2 instance. Extremely powerful.


B) Setting up the server

The hosts typically let you choose the operating system and mail you the root access details. In case of EC2, you have to download the .pem files.

The next steps are based for Ubuntu Server 10.04. Refer to the documentation of the OS in case you choose another OS. The steps, however, remain the same.

B1) Setup user accounts

Do not deploy using the root account or your personal account. It is recommended that you create a separate account for deploying apps.

1) ssh into the system using root credentials
2) Change the root password
passwd
3) Create new group for deployers
/usr/sbin/groupadd deployers
4) Give sudo privileges to users of deployers group

Issue the visudo command

/usr/sbin/visudo

Scroll down to the bottom and add the following two lines

## Allows people in group deployers to run all commands
%deployers  ALL=(ALL)       ALL

Save and Exit.

5) Create new user/s
/usr/sbin/adduser deployer
6) Add deployer user to deployers group
/usr/sbin/usermod -a -G deployers deployer
7) Logout and login as deployer

B2) Preparing the stack

You can now install the tools/software necessary for your application. Replace the commands for tools like Apache/MySQL with the commands for your tools.

1) Update the system
sudo apt-get update
2) Install MySQL and libmysqlclient16-dev [dependency for mysql2 gem]
sudo apt-get install mysql-server mysql-client libmysqlclient16-dev
3) Install Apache
sudo apt-get install apache2
4) Install Git, Curl and build-essential [to compile ruby]
sudo apt-get install build-essential git-core curl
5) Install rvm
bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
6) Add the following line to ~/.bashrc
'[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"'
7) Install the necessary packages
sudo apt-get install build-essential bison openssl libreadline6 
libreadline6-dev curl git-core zlib1gzlib1g-dev libssl-dev 
libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf
8) Install ruby 1.9.2
rvm install 1.9.2
9) Make 1.9.2 the default interpreter
rvm --default use 1.9.2
10) Install bundler
gem install bundler
11) Install passenger
gem install passenger
12) Install Apache2 module
passenger-install-apache2-module

passenger-install will notify about the missing dependencies. Install them.

13) Passenger install will complete with instructions about adding adding some lines to apache config file. Add them to apache config file.
sudo nano /etc/apache2/apache2.conf
14) Restart Apache
sudo /etc/init.d/apache2 restart

Now your server is ready to serve rails apps.

C) Deploying application

This part requires changes both to your application and to the server. I am also assuming that the app is to be deployed using capistrano.

1) Add capistrano gem to the application and capify the app

Add to the Gemfile

gem 'capistrano'

Run bundle install and then capify

bundle install

capify .

This will create a deploy.rb in the config folder. You can modify this file according to your config. The vanilla version is sufficient for normal apps.

2) Setup the database on the server

Change DATABASE_NAME and DB_USERNAME according to your settings.

ssh into the sever

Login to the MySQL database using the root password (blank by default)

mysql -u root -p

Create database for the rails application

create database DATABASE_NAME;

Create user for database

grant all on DATABASE_NAME.* to 'DB_USERNAME'@'localhost' identified by 'DB_PASSWORD';

Flush privileges

flush priviliges;	
4) Create folder for app
sudo mkdir /var/www/APPLICATION_NAME	
5) Change ownership of the folder
cd /var/www
sudo chown deployer APPLICATION_NAME
6) Update deploy.rb file

Update the server addresses, repository details and APPLICATION_NAME in your deploy.rb file.

7) Update ssh keys on repository

Add server’s ssh keys to your repository (Github)

8) Setup folder for deploy
cap deploy:setup
9) Create database.yml

Create database.yml in shared folder with proper credentials.

10) Make a cold deploy
cap:deploy:cold
11) Deploy with migrations
cap deploy:long

Your app is now deployed. You now need to configure Apache (or Nginx) so that it can serve your rails app.

12) Create a virtual host
sudo nano /etc/apache2/sites-available/app-name
13) Add the following to the virtual host

Change the IP address, domain name and the application name.

<VirtualHost *:80>  
  ServerName  XXX.XXX.XXX.XXX  
  ServerAlias subdomain.abc.com  
  DocumentRoot /var/www/APPLICATION_NAME/current/public
  RackEnv production
</VirtualHost>
14) Enable the new site
sudo a2ensite demo-app
15) Restart Apache
sudo /etc/init.d/apache2 restart

Your application is now deployed and live. :-)


P.S Let me know if you find any errors. I will fix ‘em.

P.S If you get stuck with any of the steps or need some extra guidance hit me on twitter. (@jagira). I will try to help if time permits.

P.S I am not a professional server admin and the above mentioned steps may violate some best practices. Share such instances with me.