Rails, Capistrano, Apache 2.2 and Mongrel

Posted by OrganicVeggie Thu, 02 Aug 2007 09:29:00 GMT

The basic idea is to setup one or more instances of Mongrel to handle the Ruby/Rails content and have Apache handle static content. In addition, Apache will act as a proxy and load-balance the request to Mongrel. The best resource I found was a page on Mongrel's site titled Apache Best Practice Deployment.

Here's the basics of how I set it up:

Software:

  • Ruby 1.8.6
  • Rails 1.2.1
  • Apache httpd 2.2.4
  • Mongrel 1.0.1
  • Capistrano 2.0
In my case, I have one main development machine, one database server and a combined web/application server.

Steps

  1. Setup development machine
  2. Setup web/application server
  3. Create the Mongrel cluster configuration
  4. Configure the Apache virtual host
  5. Deploy

Setup Dev Machine

Pre-requisites: Ruby 1.8.5 or later, Ruby Gems and Capistrano

Install Mongrel

[localhost]$ gem install deamons gem_plugin mongrel mongrel_cluster --include-dependencies 

Create Mongrel Configuration

[localhost]$ mongrel_rails cluster::configure  -N 3 -e production -p 9000 -a 127.0.0.1 -c /path/to/current/on/application/server

This creates mongrel_cluster.yml in your config directory. The -N 3 option tells Mongrel to setup three instances. The -e option tells Mongrel that these instances will be running in production mode (aka RAILS_ENV='production'). The -p option specifies the base port number. Since we're creating three instances, they will be running on ports 9000, 9001 and 9002. The -a option specifies the address Mongrel should bind to, the localhost in this case. The final option, -c, tells Mongrel to change to the specified directory before starting up. Since this cluster configuration corresponds to our production server, the specified path should be the actual path to the Rails application on the server. Assuming you're using Capistrano, this should be the current symlink for the application.

You will also need to tell Capistrano to use the Mongrel recipes for deployment. Edit deploy.rb and add the following to the top:

require 'mongrel_cluster/recipes' 

You also need to specify the name of the Mongrel configuration file in deploy.rb, so add the following somewhere after setting deploy_to:

set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml 

As of this writing, Capistrano 2.0 is available. One of the new features in Capistrano 2 is the concept of namespaces for tasks. Unfortunately, namespaces change the technique used to override a task. Mongrel 1.0.1 has not been converted to Capistrano 2, so the default recipes that come with Mongrel need to be tweaked. Specifically, I copied the restart task from mongrel_cluster-1.0.2/lib/mongrel_cluster/recipes.rb into my and my deploy.rb and made one small modification (in red below):

desc <<-DESC
Restart the Mongrel processes on the app server by calling restart_mongrel_cluster.
DESC
depoloy.task :restart, :roles => :app do
restart_mongrel_cluster
end

Setup Web/Application Server

Pre-requisites: Ruby 1.8.5 or later, Ruby Gems and Capistrano

Install Mongrel

[localhost]$ gem install deamons gem_plugin mongrel mongrel_cluster --include-dependencies 

Setup Apache  

Make certain mod_proxy and mod_proxy_balancer are both setup.  mod_proxy_balancer requires Apache 2.1 or later. Create a load balancer configuration file for the Mongrel cluster and place this somewhere in the Apache configuration tree. Here's a sample, which I called myappname.proxy_cluster.conf:

<Proxy balancer://mongrel_cluster>
BalancerMember http://127.0.0.1:9000
BalancerMember http://127.0.0.1:9001
BalancerMember http://127.0.0.1:9002
</Proxy>

This tells apache to load-balance requests for mongrel_cluster to the three Mongrel instances we setup earlier. Include this in the main httpd.conf file. Then create a virtual host entry for the server as well:

<VirtualHost *>
ServerName myservername.com
DocumentRoot /var/webapps/myapp/current/public

<Directory "/var/webapps/myapp">
Options FollowSymLinks
</Directory>

<Directory "/var/webapps/myapp/curent/public">
Order deny, allow
Allow from all
Options FollowSymLinks
AllowOverride None
</Directory>

ErrorLog /var/log/apache/myapp-error.log
CustomLog /var/log/apache/myapp-access.log

RewriteEngine On

# Uncomment for rewrite debugging
#RewriteLog logs/myapp_rewrite.log
#RewriteLogLevel 9

# Check for maintenance file and redirect all requests
# ( this is for use with Capistrano's disable_web task )
# The -f option tests for the existence of the specified file.
# If it exists, forward all requests to the maintenance page
# and stop processing.
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ /system/maintenance.html [L]

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

# Redirect all non-static requests to cluster.
# Again, the -f tests for the existence of the specified file.
# If the file does NOT exist, forward the request the load-balanced
# Mongrel cluster and stop processing rules.
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L]

# Deflate
AddOutputFilterByType DEFLATE text/html text/plain text/css
# ... text/xml application/xml application/xhtml+xml text/javascript
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
</VirtualHost>

The one secret piece is the directory entry for "/var/webapps/myapp". In most of the standard Apache installs I have seen, the default configuration includes a Directory entry for "/" that disables everything. A typical Capistrano deployment creates current as a symlink to releases/XXX. In my situation, Apache was unable to navigate into current, because of the top-level rule for "/" that explicitly forbid following symbolic links. The entry for "/var/webapps/myapp" is the work-around.

 

 

Posted in ,  | Tags , , , , , ,  | no comments