Saturday, March 03, 2007

Mongrel-based Rails Deployment

So, the CGI-based deployment did not perform that well at all. So, I wanted to try out Mongrel. Mongrel is a web-server written in Ruby, but not targeted alone at serving Rails applications. When hosting Rails applications though, it can do so very well. Because Rails is not thread-safe, there needs to be some synchronization on simultanous requests in Mongrel. And so there is. But Mongrel only synchronizes the Rails part. All the other parts about handling the HTTP protocol and streaming the result back to the client is done in parallel.

There is good documentation on getting this running. Here is what I did, to get it running on my new ubuntu installation. I will assume you have read the previous post on the CGI-based approach.

Installation and Configuration

sudo aptitude install build-essential ruby1.8-dev
sudo gem install mongrel mongrel_cluster --include-dependencies

I then jumpted into the application dir "/var/opt/rails/app" and configured my mongrel cluster:

sudo mongrel_rails cluster::configure \
-e production \
-p 4000 \
-N 5 \
-c /var/opt/rails/app \
-a 127.0.0.1 \
--user www-data \
--group www-data

This created a YAML configuration file for the "cluster" of mongrel instances. Now don't get too excited. This is not a cluster as we know it in Java terminology. Is is simply a bunch of mongrel instances that can be managed (started and stopped) together. This "cluster" is five instances, running on the ports 4000, 4001, ... 4003 in production mode.

The "cluster" can be started and stopped using:

sudo mongrel_rails cluster::start
sudo mongrel_rails cluster::stop

What was really nice was the the mongrel_cluster gem comes with a SYSV init script, ready to copy into "/etc/init.d". It reads all YAML configuration files from the directory "/etc/mongrel_cluster", so I simply linked my new cluster configuration file into such a directory and did a "sudo /usr/sbin/update-rc.d -f mongrel_cluster defaults" to get it auto started and stopped on boots.

I then followed this guide to install Apache 2.2.4. I needed Apache >=2.2 to get access to "mod_proxy_balancer", and I could not find such a new Apache as a .deb package :-( So, I had to compile it from scratch.

After compiling and installing a brand new Apache, I made this virtual host configuration:

NameVirtualHost appcluster
<virtualhost appcluster>
ServerName appcluster
DocumentRoot /var/opt/rails/app/public/
ErrorLog /var/opt/rails/app/log/apachecluster.log

<directory /var/opt/rails/app/public>
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
</directory>

RewriteEngine On
RewriteRule ^/$ /index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ balancer://app_cluster%{REQUEST_URI} [P,QSA,L]
</virtualhost>

<Proxy balancer://app_cluster>
BalancerMember http://127.0.0.1:4000
BalancerMember http://127.0.0.1:4001
BalancerMember http://127.0.0.1:4002
BalancerMember http://127.0.0.1:4003
BalancerMember http://127.0.0.1:4004
</Proxy>

And I was up and running.

Performance was much better
Again, the same small performance test was made, and it was much better:
  • 1 request, with no db-access took around 0,0-0,1 second
  • 5 simultanous requests, to same page, < 1 second, with an average of ~0,1 second/request
  • 15 simultanous requests, to same page, < 2 seconds, with an average of ~0,1 second/request