Deploying Rails on Ubuntu Dapper

Posted by robl

Ubuntu is fast becoming the distribution of choice for many people, having the ease of deployment and solid package management system that you would expect from a Debian based distribution and also benefits from a fast release cycle that results in up to date versions of many popular packages. In particular, Ubuntu ‘Dapper’ is the first Ubuntu release to provide long term support (6 years from release) and has achieved a healthy level of security and stability (in no small part due to it’s Debian roots).

Currently, the most popular Rails deployment stack is Linux, Apache 2.2, Mongrel and MySQL. This can present a problem when attempting to install with Ubuntu Dapper as Apache 2.0 is the only available Apache package within the official Dapper repositories. There is always the option to compile a version of Apache from source (which certainly has some benefits) however all the benefits of using a package management system are lost when taking this approach.

The Mongrel based deployment stack

Mongrel is (currently) the preferred application server for deploying Rails in a commercial environment. There are other solutions e.g. mod_fcgid or lighttpd, however these have become less popular due to instability or lack of support.

The preferred configuration involves using Apache as a proxy server, that sends Rails requests to Mongrel whilst serving requests for static content itself e.g. images or CSS. Apache is very good at serving static content whereas Mongrel excels at serving Ruby, thus each server performs tasks it is suited to. The diagram below demonstrates this architecture :

Why so many Mongrels ?

Mongrel is a Web server (and HTTP library) for Ruby applications. It’s capable of running applications developed using the Ruby on Rails, Nitro and Camping frameworks. Unfortunately, the Rails framework isn’t thread-safe. This results in a Mongrel instance having to handle HTTP requests serially. This is obviously a problem for a site with a constant stream of traffic, as Mongrel wouldn’t be able to process concurrent requests.

The answer to this problem is to run many instances of Mongrel on different ports and proxy requests to them. This can be achieved in a number of ways, but the most popular is to use Apache 2.2 with mod_proxy_balancer (as shown in the diagram above). This does mean there is a configuration overhead in determining the optimum number of running Mongrel instances for your application (which will be examined later). Managing a large number of Mongrel instances is a time consuming exercise and is made easier by mongrel_cluster. mongrel_cluster is a set of scripts that allows easy management of Mongrel instances (across many Rails applications) via the init system.

Installing Ruby, Rails and MySQL

These are the meat and bones of our system, and are easily installed using a combination of the apt and Ruby gems package management systems :

NOTE: You will need to have enabled the Universe repository to obtain some of these packages.


sudo apt-get install ruby ri rdoc mysql-server libmysql-ruby 
sudo wget http://rubyforge.org/frs/download.php/17190/rubygems-0.9.2.tgz
tar -xvzf rubygems-0.9.2.tgz
cd rubygems-0.9.2
sudo ruby setup.rb
sudo gem install rails --include-dependencies

You can try creating a test Rails application by running :


rails railsapp

This should create a directory called ‘railsapp’ with the standard Rails application skeleton.

Installing Mongrel

You’ll need both Ruby and some build packages for Mongrel to be installed on your system :


sudo apt-get install build-essential ruby1.8-dev ruby1.8

You can then install Mongrel as a Ruby Gem (this should install all Ruby dependencies automagically) :


sudo gem install mongrel

Test your Mongrel install by running Mongrel in the root of a Rails application. If all is ok, you should be able to browse to your rails application at http://localhost:3000 :


cd rails_app/
mongrel_rails start -d
mongrel_rails stop

Installing mongrel_cluster

Once you have a successful Mongrel install, you can install mongrel cluster enabling you to manage your Mongrel instances.


sudo gem install mongrel_cluster

Installing Apache 2.2

Kodefoo have prepared a set of Apache 2.2 packages (for the i386 architecture). These packages have been backported from Ubuntu Feisty to Ubuntu Dapper. The packages are used for commercial hosting of Rails applications on Ubuntu Dapper by Kodefoo. The packages provided include mod_php and mod_perl for running LAMP stacks in addition to a Rails environment. The packages are provided in an apt repository to ease the install and update process. A package update process runs on a daily basis, checking to see if new packages have been released, if new packages have been made available for Feisty then these are backported for Dapper and added to the apt repository.

Before using this repository you must agree to the Terms Of Use . If you are unable to agree to this then do not use this repository.

Add the following line to your /etc/apt/sources.list

deb http://packages.kodefoo.com/ dapper main

Import the kodefoo repository GPG key into your apt repository keyring :


wget http://packages.kodefoo.com/release.asc
sudo apt-key add release.asc

Update the package indexes and install Apache 2.2 :


sudo apt-get update
sudo apt-get install apache2 apache2.2-common apache2-mpm-prefork apache2-utils

Configuring Apache 2.2, Mongrel and mongrel_cluster

There is a very good mongrel_cluster configuration guide available at the Mongrel site. It’s recommended you follow the guide there to configure your mongrel_cluster instances.

For the purposes of configuring Apache 2.2, it is assumed the mongrel_cluster has been configured to run three Mongrel instances starting at port 8000 (so there will be Mongrel instances listening for HTTP requests on ports 8000, 8001 and 8002). The example below should be adjusted appropriately if your configuration differs.

The following virtual host file proxies Rails requests to your local Mongrel instances (invoked via mongrel_cluster) but allows Apache to serve the static content.


<VirtualHost *>
   ServerName www.yourserver.com

   DocumentRoot /var/www/railsapp/public/

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

   ErrorLog logs/railsapp_errors_log
   CustomLog logs/railsapp_log combined

   # this not only blocks access to .svn directories, but makes it appear
   # as though they aren't even there, not just that they are forbidden
   <DirectoryMatch "^/.*/\.svn/">
     ErrorDocument 403 /404.html
     Order allow,deny
     Deny from all
     Satisfy All
   </DirectoryMatch>

   <Proxy *>
    Order allow,deny
    Allow from all
   </Proxy>

   <Proxy balancer://mongrel_cluster>
     BalancerMember http://127.0.0.1:8000
     BalancerMember http://127.0.0.1:8001
     BalancerMember http://127.0.0.1:8002
   </Proxy>

   # This passes through remote_user to mongrel
   RewriteEngine On
   RewriteCond %{LA-U:REMOTE_USER} (.+)
   RewriteRule . - [E=RU:%1]
   RequestHeader add X-Forwarded-User %{RU}e

   # Now standard rewrite rules
   RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} -d
   RewriteRule ^(.+[^/])$ $1/ [R]

   RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} \.php
   RewriteRule ^(.*)$ $1 [QSA,L]

   RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
   RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME}/index.html -f
   RewriteRule ^(.*)$ $1/index.html [QSA,L]

   RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
   RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME}/index.php -f
   RewriteRule ^(.*)$ $1/index.php [QSA,L]

   RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} -d
   RewriteRule ^(.*)[^/]$ $1/ [QSA,L]

   RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
   RewriteRule ^/(.*)$ balancer://mongrelcluster%{REQUEST_URI} [P,QSA,L]

</VirtualHost>

This file can be saved in /etc/apache2/sites-enabled/yoursite and enabled with :


sudo a2ensite

The configuration can be tested using :


sudo apache2ctl -t

Providing there were no errors, the installation and configuration should now be complete. When Apache is restarted, the Rails application will be served by Apache and Mongrel. If you have configured mongrel_cluster as an init script then Apache and Mongrel can be restarted using :


sudo /etc/init.d/mongrel_cluster restart && sudo /etc/init.d/apache2 restart

Performance tuning hints and tips

There are a number of areas that can be tuned for performance using this architecture. Typical considerations include :

  • The number of Mongrel instances, configured per Rails application served. There is no ‘magic’ number, however Kodefoo typically find a minimum of four instances is required. To perform individual application tuning look at How Many Mongrel Instances Should I Run ?
  • The default Apache 2.2 configuration can be improved (some suggestions here and here) and using one of the newer Apache MPM models can certainly help
  • The use of memcached can also help in moving load away from the database layer to a memory cached distributed layer. This can be useful for scaling large sites with high traffic levels, some pointers can be found here

We’ll be publishing a performance tuning article in the near future, so check back soon.

Attribution

Articles consulted whilst writing this article :

Comments

Leave a response

  1. rustyMarch 06, 2007 @ 07:29 PM
    Hi there, Thanks for the article! I used it to get my Ubuntu updated to the latest Apache from your repository. Very much appreciated! Two things I found in there, instead of: sudo apt-key install release.asc You want: sudo apt-key add release.asc And instead of: sudo apt-get install apache2 apache2-common apache2-mpm-prefork apache2-utils I used: sudo apt-get install apache2 apache2.2-common apache2-mpm-prefork apache2-utils That worked for me, YMMV ;) Thanks again! rusty