Rails Production Environment Using Mongrel Cluster, Apache 2.2 mod_proxy, and Capistrano on Ubuntu Gutsy

on Wednesday 13th February, 2008 Gabe speculated thusly…

Recently, I had to setup a production environment for Rails applications at work. I encountered many difficulties, including problems with permissions on client/server computers, and not least because the documentation I had was out of date (Agile web dev and The Rails Way). Online tutorials were helpful but incomplete. I have compiled all that I discovered here.

Install required software on the production server

To install a mongrel_cluster behind an Apache proxy on a server install of Ubuntu (Gutsy) you need to install serveral items.

Make sure Apache 2.2 is installed:
# aptitude install apache2

Install Ruby Gems and necessary libraries:
# aptitude install rubygems ruby1.8-dev build-essential

Update gems to the latest version:
# gem update --system

Install Rails:
# gem install rails

Install Mongrel Rails application server:
# gem install mongrel

Install Mongrel_Cluster, scripts for managing multiple Mongrel instances:
# gem install mongrel_cluster

Mongrel is the name of the server that will server your Rails application. It is a good idea to create a dedicated user and group on your system for this process:
# adduser --system --no-create-home --disabled-password --group mongrel

Next you should plan where your applications will be stored on the server. I chose /var/www/apps so I created that directory:
# mkdir /var/www/apps

Then ensure it is owned by the user I will be deploying with:
# chown gabriel:gabriel /var/www/apps

Configuring Apache to cope with a Mongrel cluster

Enable several apache modules that allow us to perform URI rewriting and reverse proxying to our mongrels:

# a2enmod rewrite
# a2enmod proxy
# a2enmod proxy_balancer
# a2enmod proxy_http

Create a configuration file for a virtual host in Apache, you can do this as follow:
# touch /etc/apache2/sites-available/railsapp.conf

Then edit that file you would any other. Essentially you need a configuration as follows (more or less):

<VirtualHost *>
   	ServerAdmin admin@dev.server.com
	ServerName railsapp.dev.server.com
	ServerAlias www.railsapp.dev.server.com
	ServerAlias railsapp
	DocumentRoot /var/www/railsapp/current/public
	ErrorLog /var/log/apache2/railsapp.error.log

	# Enable proxying.
	<Proxy *>
		Order allow,deny
		Allow from all
	</Proxy>

	# Define the members of the cluster to load balance with.
	<Proxy balancer://railsapp_cluster>
		BalancerMember http://127.0.0.1:8000
		BalancerMember http://127.0.0.1:8001
	</Proxy>

	<IfModule mod_rewrite.c>
		RewriteEngine On

		# If capistrano has left a maintenance file then we should display that.
		RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
		RewriteCond %{SCRIPT_FILENAME} !maintenance.html
		RewriteRule ^.*$ /system/maintenance.html [L]

		# Rewrite index to check for static
		RewriteRule ^/$ /index.html [QSA]

		# Rewrite to check for Rails cached page
		RewriteRule ^([^.]+)$ $1.html [QSA]

		# Redirect all non-static requests to cluster
		RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
		RewriteRule ^/(.*)$ balancer://railsapp_cluster%{REQUEST_URI} [P,QSA,L]
	</IfModule>

	<Directory "/var/www/railsapp/current/public">
		Options FollowSymLinks
		AllowOverride None
		Order allow,deny
		Allow from all
	</Directory>

	# Possible values include: debug, info, notice, warn, error, crit,
	# alert, emerg.
	LogLevel notice
</VirtualHost>

Now enable that site:
# a2ensite railsapp

Configuring your development machine/desktop to make deployments to the production server

It is a good idea to have Mongrel and Mongrel Cluster installed on your local machine along with Capistrano. This is accomplished in different ways depending on your operating system. The following part of the guide assumes that you are deploying from your desktop to a production server via SSH. I personally use the Bazaar revision control system for all my projects, and so we will make the configuration to suit that. The next commands are to be carried out on your desktop.

Change to the directory containing your Rails application. For me that is stored at ~/Desktop/railsapp:
$ cd ~/Desktop/railsapp

Instruct Capistrano to make needed scripts:
$ capify .

This will create a file called deploy.rb in the config folder of your Rails application. Go to edit that file now and make it look similar to the following, I will discuss what each option does after:

require 'mongrel_cluster/recipes'

set :application, "railsapp"
set :repository,  "/home/bazaar/rails/railsapp"

# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
set :deploy_to, "/var/www/apps/#{application}"

# If you aren't using Subversion to manage your source code, specify
# your SCM below:
set :scm, :bzr
set :revision, 'last:1'

set :password, "mypassword"
set :user, "gabriel"

role :app, "railsapp.dev.server.com"
role :web, "railsapp.dev.server.com"
role :db,  "railsapp.dev.server.com", :primary => true

task :after_cold, :roles => [:app,:web,:db] do
  sudo "chown -R mongrel:mongrel /var/www/apps/#{application}/"
end

task :after_deploy, :roles => [:app, :web, :db] do
  sudo "chown -R mongrel:mongrel /var/www/apps/#{application}/"
end
  • This first line beginning with require includes extra mongrel scripts that Capistrano will need. You specify the name on the application with set :application.
  • Give it the location to the Bazaar repository where your project is stored. In my case it was on the same machine, and I did it with
    set :repository, “/home/bazaar/rails/railsapp”.
  • Next, using set :deploy_to tell Capistrano where on the production server your application will be served from. Remember we used /var/www/apps so the line will read as set :deploy_to, “/var/www/apps/#{application}”.
  • We notify Capistrano that we’re using the Bazaar revision control system with set :scm, :bzr.
  • Due to a bug in the version of Capistrano I was using it would attempt to make a bzr branch with a certain revision. However, it would neglect to provide a revision number which would cause bzr to error. This can be overcome by specifying the latest revision to use, in bzr terms this is last:1. So we set the revision with set :revision, ‘last:1′.
  • Provide your username and password for logging in to the production server via SSH with set :password and set :user. This user should be the same that owns the /var/www/apps directory that we chowned earlier.
  • Setup the roles with role :app, role :web. I haven’t paid much attention to the use of roles yet.
  • Finally, I had to add a couple of tasks to the end of the deploy file. After deploying your application to the live server all the files will be owned by you and the Mongrel server will not be allowed to write to any of them. This is a problem because the mongrel process will need to write session and cache files etc. These tasks started with task :after_. If you setup your server like mine your Mongrel processes will run as the mongrel user so you will need to chown mongrel:mongrel the files.

Now on your desktop machine you need to create a config file for mongrel_cluster. This simplifies management of a cluster of Mongrel servers. You can create the required file by running the command mongrel_rails cluster::configure along with several arguments. However, I prefer to create a file called mongrel_cluster.yml in the config directory:
$ touch config/mongrel_cluster.yml

Now pop the following data in to it using your favourite editor:

---
environment: production
address: 127.0.0.1
user: mongrel
group: mongrel
port: "8000"
servers: 2
cwd: /var/www/apps/railsapp/current
pid_file: tmp/pids/mongrel.pid
log_file: log/mongrel.log
  • environment: production specifies the environment e.g. testing, development, production.
  • address: 127.0.0.1 tells the mongrel instances on what address to listen. 127.0.0.1 means that will only accept requests from the localhost. Since we are proxying requests to them via Apache on the same machine this works.
  • Specify the username and group the Mongrel instances should run as with group: mongrel, and user: mongrel.
  • What port number the Mongrel instances should start at port: “8000″.
  • How many instances of Mongrel you want attached to the particluar application. In development you can just use one or two, going up to about 10 for very busy sites: servers: 2.
  • Specify the path to your application on the production server, appending current to the end. Using Capistrano means that your application will always end up under /current cwd: /var/www/apps/studiomaker/current.
  • We tell Mongrel where to store the lock files for it’s processes. This directory is relevant to the one set in CWD: pid_file: tmp/pids/mongrel.pid.
  • Lastly, where Mongrel should keep it’s logs log_file: log/mongrel.log.

Deploying from your desktop

We are now ready for Capistrano to create the required directory structure on the production server. Issue the following command from your application’s directory:
cap setup

Then get it to do a cold-deployment. Note: this step will probably error out as we have not finished configuring the production server. However, it is convenient for us to let Capistrano create the necessary files on the production server first:
cap deploy:cold

Finishing of configuration of the production server

Create a mongrel_cluster directory in /etc directory
# mkdir /etc/mongrel_cluster

Create a link to the mongrel_cluster.yml in the /etc/mongrel_cluster folder (name the link as your application, so many ruby application’s mongrel cluster file will be linked there and they will be booted at startup:
# ln -s /var/www/apps/railsapp/current/config/mongrel_cluster.yml /etc/mongrel_cluster/railsapp.yml

Copy (or link) the mongrel_cluster init script, this allows your clusters to restart after a reboot:
# cp /usr/lib/ruby/gems/1.8/gems/mongrel_cluster-1.0.5/resources/mongrel_cluster /etc/init.d

Make the script executable
# chmod +x /etc/init.d/mongrel_cluster

Install the script as a service

# cd /etc/init.d/
# /usr/sbin/update-rc.d -f mongrel_cluster defaults

Stop any running Mongrel clusters (just incase):
# /etc/init.d/mongrel_cluster stop

Delete your application and start again, just to minimise the risk of an problems from the last failure:
# rm -r /var/www/apps/railsapp

Going back to your desktop machine. Run the final commands

Setup the application again:
cap setup

Do another cold deployment:
cap deploy:cold

With a little bit of luck you should have a production system running!

4 Responses to “Rails Production Environment Using Mongrel Cluster, Apache 2.2 mod_proxy, and Capistrano on Ubuntu Gutsy”

  1. Daniel Shelby Says:

    Great tutorial. I am experiencing so much grief trying to rails to run on a production server. I am new to all of this so I am hitting walls left and right. Since I am such a noob I do have a few questions.

    In your railsapp.conf file, I assume anywhere that says “railsapp” should be replaced with the name of actual rails app?

    I am deploying from my employer provided windows dev box. Do I just dump the contents of my projects folder into the directory of the production? Or is there something special that capistrano does? Thanks!

  2. jayashree Says:

    Explained very well.Excellent for beginners to write deploy.rb file

  3. Gabe Says:

    @ Daniel
    I’m glad you find it useful. You are correct in assuming that ‘railsapp’ should be replaced by whatever the name of your particular application is.

    @Jayashree
    :) thanks very much for the positive feedback.

    I’ve personally got a chip on my shoulder about rails deployment. It is something of a dark art. I think the problem lies in Ruby not being thread-safe. Otherwise you could serve multiple rails applications just by using mod_ruby.

  4. Mukund Says:

    Rails 2.2 will be thread safe. But why exactly is threading an issue for deployment on production? There is no dark art with Rails production deployment. Capistrano has an issue with documentation and there isn’t anyone interested in fixing the problem.

Leave a Reply