Friday, May 2, 2008

Mod_rails is AWESOME

Last week, as I was about to deploy a small application, I got a case of "ughh". Well, if there is such a case, I had it. Normally, I deploy with mod_fcgid, Apache, and suexec. It's an awesome solution that's difficult to set up but easier to maintain.

mod_fcgid Deployment

Roughly, here's the steps to such a deployment:

  • Download FastCGI. Compile and install it.
  • Install fastcgi ruby gem.
  • Download, compile, and install fcgid for Apache 2. Edit apache config to load the module.
  • Deploy the source code / database. Make sure it's in the directory that suexec is set to "allow".
  • Add a virtual host. Make sure you allow the .htaccess file to override. Set a user and group to run the app as.
  • Check your .htaccess file. Comment out the "cgi" line and uncomment the "fcgi" line.
  • Make dispatch.fcgi executable. Make the shebang point to a valid ruby path.
  • Make dispatch.fcgi not publicly writable.
  • Make public not publicly writable.
  • Make your app not publicly writable.
  • Add these steps to your capistrano deploy script to make sure this is always the case.
  • apachectl graceful.
  • Cross your fingers - didn't work.
  • Check your apache error log.
  • Oh crud... I forgot to do such and such.
  • Fix it.
  • apachectl graceful.
  • Try again.
  • See another error screen.
  • Check your apache error log.
  • Ugh, forgot that too.
  • Fix that.
  • apachectl graceful.
  • Repeat several times until application starts.
  • Enjoy!

You can imagine why I dreaded doing that for one tiny non-mission-critical application with one controller.

Mongrel Cluster with HTTP Proxy Deployment

The Mongrel Cluster / HTTP proxy is a great solution, perhaps the most popular now. I've never been a huge fan. Why? Well... I'll tell you (he's going to tell, he's going to tell... @ 7:30)

  • It's one more thing to make sure is running.
  • When you restart your cluster and don't redirect all the requests to a "we're down" page, Apache gets backlogged and all your users get a nice "Service not available" screen.
  • Sometimes mongrels don't restart.
  • Sometimes one doesn't come back up.
  • Mongrels never cycle through in their life span, so if your app has a memory leak, you're memory is going to creep.
So, it's a great option, but more work than I'd like to do, and I'd still prefer to set up mod_fcgid because I'm much better at it than mongrel, and I just really like having Apache start my application servers.

Passenger (mod_rails) to the rescue

So I heard some buzz recently about Passenger (aka mod_rails): something about rails deployment as easy as uploading your application. That sounded really good. I didn't care about benchmarks for this app, but as it turns out, passenger is about on par with "thin", and is faster than mongrel! Sweet lovin'.

After 2 minutes of going through the setup instructions and deploying my code, I was incredulous. "Watch, I'm going to start the application up, and everything will just fall apart." I was pleasantly proven wrong!

Here's a summary of the install steps for mod_rails (Passenger)

  • Gem install passenger.
  • Passenger-install-apache-module.
  • Copy and paste the 3 lines of apache config.
  • Deploy your app source code / set-up db.
  • Set up your virtual host (in 4 lines of code).
  • apachectl graceful.
  • That's it! Really!

And, I was pleasantly shocked to discover that it intelligently ran the rails processes as the user I deployed the application as. It checks config/environment.rb, and runs as the owner of that file. Hot stuff! And to reboot the server, just type "touch tmp/restart" - You could reboot your app with scp, rsync, ssh, ftp, samba, magnetic needles, anything! No need to have sudo access. No pid files. No "killall". Hot hot hot!

How's stability? I've converted our mongrel and fcgid deployments over to mod_rails now. Things are running smoothly. There seems to be a bit of a lag if the app isn't in use for a while (I don't think it keeps a minimum pool available, which would be a nice feature). I had a few issues with slowness on version 1.0.1, but 1.0.2 and onward has been rock solid. Oh - 1.0.4 doesn't work on the default install of Apache server for OS X Server - use 1.0.3 instead.

In Conclusion...

Here's a table of deployment scenarios with strengths and weaknesses, according to me and my experience.
mod_rails fcgid Mongrel Cluster
Speed Great (enterprise edition rumoured to be much faster) Great Great
Memory usage Slightly better (enterprise edition rumoured to reduce memory consumption by 33%) Slightly better Average
Reliability Great Great Pretty good
Ease of setup Easy Difficult Moderate
Graceful restarts?
(restart app w/out dropping a single request)
Yes Yes Possible (send kill -USR2 all mongrel pids)
App restarted on apache restart Yes Yes No
Automatic process cycling Yes Yes No
Process management Allocates on demand Allocates on demand Fixed regardless of load
Ability to set minimum process count per App No Yes Yes
Set max process count per App No (... actually, yes!) Yes Yes
I'm completely converted. I'm never going to go through that deployment nightmare again! Kudos to the Phusion folks for putting such a great solution together. If you've got a small app that you need to deploy, give it a spin. You won't regret it.

1 comment:

Hongli said...

Glad you like it. :)

About '"Zombie" process recovery' - Passenger doesn't create zombie processes, ever. We use a technique called "double forking" in order to prevent zombies from being created.

As for OS X compatibility: we're really sorry that 1.0.4 broke it. Before we released 1.0.4, a tester confirmed that 1.0.4 works fine on OS X, but apparently he was wrong or there's something else going on. In order to fix the problem, we're looking for as many OS X users as possible, and we'd really like to have your feedback. Please join #passenger on if you're interested.

With kind regards,
Hongli Lai