21 November 2005

Rails with Apache MPM worker

The existing instructions for Typo on FreeBSD presume that you’re running lighttpd and Apache httpd with mod_proxy. This is unnecessary and, generally speaking, more complicated than what most people need. It turns out you can serve your Rails app, legacy PHP pages and static content just fine with just one webserver installed. This can also help save memory (my own motivation), by running PHP as a FastCGI process rather than embedding it in every Apache MPM prefork process.

It seems like many users aren’t aware that there is any option beside MPM prefork for Apache httpd. Starting with Apache httpd 2 you can choose different multi-processing models. One of these is MPM worker, which keeps not only multiple processes, but also uses several threads to handle requests.

Threads complicate matters with things that are not typically thread-safe like mod_php, mod_perl, and many other programming language Apache modules. Luckily FastCGI can move this work outside of the httpd process where it can be handled outside of these threading concerns.

Before I wander into the exact details of configuring this setup, a word about performance: Super. MPM worker does a fabulous job serving static and dynamic content quickly. In addition, memory consumption is much easier to control since the FastCGI processes associated with each language or Rails application can be independently controlled and tuned to the traffic they’re seeing.

Installing the Pieces

I put the following into my /etc/make.conf to make sure the components are built correctly:


Next, the correct ports have to be built. The packages are listed below along with their origin (that is, where to find them in the FreeBSD ports collection). Building the things below with the options above will be different on other systems.

Package Origin
apache-worker-2.0.54_4 www/apache2
rubygem-rails-0.13.1 www/rubygems-rails
mod_fastcgi-2.4.2 www/mod_fastcgi
ruby18-fcgi-0.8.6 www/ruby-fcgi
ruby18-sqlite-2.2.3 databases/ruby-sqlite
ruby18-postgres-0.7.1_2 databases/ruby-postgres
ruby18-mysql-2.6 databases/ruby-mysql

You only need the database package relevant to the database you want to be using; which one you should not use is a rant for another day, today I’m going to use sqlite. There may be newer versions of these packages available by the time you read this.

For this example, we’ll unzip/untar the latest typo release into /usr/local/www/data.

Configuring the components

First, I’m going to get the Typo install configured. This is simply building the database with sudo -u www sqlite db/typo.sqlite.db < db/schema.sqlite.sql and then changing my config/database.yml to contain:

login: &login
adapter: sqlite
dbfile: db/typo.sqlite.db

<<: *login

<<: *login

<<: *login

A quick run with script/server will indicate if things are working correctly with the Rails application. I had to comment out a line in config/environment.rb to get it to run (and have filed a bug about it).

I also check at this point if public/dispatch.fcgi will run correctly from within Apache httpd. Generally the PATH used by Apache httpd will not include the ruby executable, so I change the /usr/bin/env ruby at the top of public/dispatch.fcgi to /usr/local/bin/ruby.

Now I’ll configure Apache httpd. The default values for MPM worker are quite reasonable, so I’ll leave them alone. You’ll find them near the text <IfModule worker.c> along with a comment about what the settings do.

Setting ServerName is usually a good idea right about now. Remember that this needs to be a real name that points at this server.

In order for Apache to use the FastCGI module, it’ll need to know that I want it to be loaded. At the end of all the lines that start with LoadModule we’ll want to add the FastCGI module with:

LoadModule fastcgi_module libexec/apache2/mod_fastcgi.so

Next, move the DocumentRoot into Rails application by changing it to "/usr/local/www/data/typo/public"

Lastly, after the line with DocumentRoot I add lines specific to the FastCGI configuration and the permissions we want:

<IfModule mod_fastcgi.c>
FastCgiIpcDir /tmp/fcgi_ipc/
FastCgiServer /usr/local/www/data/typo/public/dispatch.fcgi -idle-timeout 120 -initial-env RAILS_ENV=production -processes 2
FastCgiConfig -idle-timeout 120 -initial-env RAILS_ENV=production -restart
AddHandler fastcgi-script fcgi fcgi fpl

<Directory "/usr/local/www/data/typo/public">
AllowOverride All
Options ExecCGI

The critical line in there is the FastCgiServer line that Scott Laird has written about in some detail on his blog.

And we’re off…

Now I just run apachectl start as root and I’m up and running.

No comments: