MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


Bringing Ruby on Rails with FastCGI into Mac OS X Server

by Luke Burton
03/29/2006

Ruby on Rails (RoR) is a great web application development framework. Although I'm still coming up to speed with actually developing RoR apps, I also wanted to become familiar with the production deployment process. Most of the documentation out there sort of glosses over this plumbing. However, sooner or later you need to get your hands dirty.

Now as a Mac aficionado and OS X Server user, obviously I wanted my RoR installation to be squeaky clean. I wanted to leverage existing components that were already managed by Mac OS X Server, and keep the manually installed components to a minimum. Unfortunately I found a dearth of straightforward instructions in this matter.

What I desired as an outcome was to deploy a RoR application into a directory outside of the normal web document root. I wanted that directory under version control, and I wanted it hosted inside of Apache. I also wanted it to be quick, so FastCGI was absolutely necessary. I also wanted the option to use either MySQL or SQLite. Thankfully, all my wishes came true. Read on to discover how I did it.

Necessary Infrastructure

Let's itemize the components we require to effect our desired setup:

  1. Ruby--the language.
  2. Ruby Gems--the Ruby package manager.
  3. Rails--the development framework based on Ruby.
  4. MySQL--our relational database.
  5. Apache--our web server.
  6. FastCGI--a mechanism to speed up our Rails app inside of Apache.
  7. SQLite--as an alternative relational database.

We have a couple of no-brainers on this list. MySQL, SQLite, and Apache are all bundled into OS X Tiger Server. "Aha," you may say, "and so is Ruby." Yes, but we know that the default installation of Ruby has some problems.

So let's just sidestep the default installation of Ruby, but keep the default MySQL, SQLite, and Apache installations. Plus, we'd like to manage Apache as far as possible through the Server Admin tools. This rolls toward an overwhelming question: how do we most easily install Ruby, Ruby Gems, and everything else without becoming mired in GNU configure scripts?

The answer is DarwinPorts. If you're familiar with FreeBSD's "ports" distribution, or Debian Linux, or Gentoo, this kind of stuff will come as no surprise. For the rest of us, it's a way of easily downloading and compiling source code for open source projects, without having to worry about interdependencies or complex command-line options.

Visit DarwinPorts, grab the package installer, and fire it up. After installation, you should have the command port in your shell path. If not, add /opt/local/bin to your PATH environment variable.

Laying Down Some Ports

DarwinPorts makes life easy by installing everything into /opt/local. This means it pretty much lives in its own little directory tree, with few if any conflicts with existing system libraries. DarwinPorts also looks after its own list of installed packages, making a subsequent clean uninstall an option.

Let's kick off by installing the latest version of Ruby:

# port install ruby

Then, the Gems package manager:

# port install rb-rubygems

Next, we use the Gems package manager to install to the Rails framework:

# gem install rails --include-dependencies

Wow, this is too easy. Next we need the development framework for FastCGI:

# port install fcgi

And also the Ruby interface to FastCGI:

# port install rb-fcgi

And believe it or not, we're almost done. We have one final, manual piece to attend to, and this is the FastCGI module for Apache, aka mod_fastcgi. We first need to download the source code

You will want, at the time of this writing, mod_fastcgi-2.4.2.tar.gz. Extract the files and inside the mod_fastcgi directory, execute the following two lines as per the INSTALL file:

# apxs -o mod_fastcgi.so -c *.c

This compiles the module. Now we need to install it. The installation procedure via apxs is smart enough to modify our Apache's httpd.conf on our behalf:

# apxs -i -a -n fastcgi mod_fastcgi.so

You can go ahead and verify that by finding the LoadModule and AddModule lines in /etc/httpd/httpd.conf that refer to mod_fastcgi.so.

So apart from a bit of fiddling around with mod_fastcgi, our installation is now complete. We just need to complete some final configuration.

Essential Mac OS X Panther Server Administration

Related Reading

Essential Mac OS X Panther Server Administration
Integrating Mac OS X Server into Heterogeneous Networks
By Michael Bartosh, Ryan Faas

Configuration Cauldron

Before we go any further, you have got a Rails app to deploy, haven't you? If not, you had better read up on some tutorials first and create one. Be mindful that your Rails app's public/dispatch.fcgi file has the right Ruby in the path. I edited mine and hardcoded it to /opt/local/bin/ruby, which is the path of the DarwinPorts Ruby installation.

I had a specific end state in mind when I wrote this guide. I had a VirtualHost configured on this Apache installation, and I wanted to install my Rails app against a specific URL that didn't actually exist in the DocumentRoot.

Let's say for argument's sake the host was http://www.hagus.net. I wanted to access my Rails app at http://www.hagus.net/railstest. But obviously I don't want my whole Rails app living in the DocumentRoot; I want it somewhere like /Users/hagus/src/railstest. How to make this happen?

First, we dive into httpd.conf. We need to add a few extra configuration lines to make sure FastCGI is completely ready to rock & roll:

<IfModule mod_fastcgi.c> 
  FastCgiIpcDir /tmp/fcgi_ipc/ 
  AddHandler fastcgi-script .fcgi 
</IfModule>

For cleanliness, we wrap these files between an IfModule declaration, in case we disable FastCGI in the future for some reason. The two config lines are simple: we declare a temporary directory for the FastCGI process to write out various files; then we add a handler to tell Apache that files ending in .fcgi are FastCGI scripts.

Don' t forget to actually create the /tmp/fcgi_ipc directory if necessary! Give it wide open permissions too:

# mkdir /tmp/fcgi_ipc
# chmod 777 /tmp/fcgi_ipc

Before we go any further: be very aware that changes to the httpd.conf file and friends may be overwritten by Server Admin. Unfortunately this is a difficult situation, as Server Admin doesn't really let us edit the httpd.conf file in a granular enough fashion. So we're forced to hand-edit.

A workaround to protect your httpd.conf modifications is to set the "immutable" bit with the chflags command. In reality, if you're diving in and editing your httpd.conf file manually, you probably don't lean heavily on Server Admin anyway.

# chflags uchg /etc/httpd/httpd.conf 
# ls -lo /etc/httpd/httpd.conf
-rw-r--r--   1 root  wheel  uchg 33495 Mar  9 13:18 /etc/httpd/httpd.conf

Now, we must edit the VirtualHost directive for our host to finish the job. Add these lines inside the <VirtualHost> for your site (OS X Tiger stores its VirtualHosts in /etc/httpd/sites, in case you're having trouble locating them):

Alias /railstest "/Users/hagus/src/railstest/public" 
<Directory "/Users/hagus/src/railstest/public">
  AllowOverride all 
  Allow from all
  Order allow,deny 
</Directory>

We first declare an Alias. This gets me what I wanted: a URL on my website that actually terminates outside the DocumentRoot. We then use a standard Directory tag to set the AllowOverride preference. We need this set to allow .htaccess in the public directory to do its job. You could also add some HTTP authentication directives here too, if desired.

There's just one more bit that needs to be taken care of. We need to edit the public/.htaccess file of our Rails app. We must set the RewriteBase directive correctly so that requests are correctly routed to dispatch.fcgi. Locate the line that says RewriteBase, and uncomment it appropriately. In my case:

RewriteBase /railstest

We must also choose to use dispatch.fcgi rather than dispatch.cgi. This is a simple matter of changing the appropriate RewriteRule line from this:

RewriteRule ^(.*)$ dispatch.cgi [QSA,L]

to this:

RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]

Now give your web server a kick in the guts (pardon the Australian colloquialism):

# /usr/sbin/apachectl restart

And you should now be able to access your Rails app!

Final Thoughts

You should now have a Rails app living at http://www.yoursite.com/yourapp/. Your Rails app should be "teh snappy" thanks to the wonders of FastCGI.

Your Ruby, Rails, Gems, and FastCGI installations are being managed out of the DarwinPorts distribution system. Your Apache and MySQL are still managed by Mac OS X Server, but we have had to hand-edit httpd.conf. We also had to hand-compile the FastCGI Apache module.

On my own RoR installation, I have everything checked into Subversion, which is also available via DarwinPorts:

# port install subversion

So when I make a change in development (I use the excellent Locomotive all-in-one environment) and confirm that it works, I can just log into my production server and issue the checkout command to refresh my code. Simple and elegant, the way Ruby on Rails should be. :)

Luke Burton chipped his teeth on C++, and has lately sought refuge in the beautiful world of scripting languages like Ruby and Perl.


Return to the Mac DevCenter

Copyright © 2009 O'Reilly Media, Inc.