Ruby 1.9.2, Rails 3.x and Passenger with Chef

We’re migrating some of our apps to Ruby 1.9.2 and Rails 3.1.3. On Lucid, there are debs for Ruby 1.9.1 and a rather generic-looking Rails, but we really need finer-grained control of versions.

I attempted to build our own custom debs but this led to a lot of crufty-looking dependencies and felt rather brittle, so I turned to RVM. I started with this rather splendid howto (which I’d used before on my Lucid laptop), and massaged it into a Chef cookbook.

Cheffing it up

We start with RVM, which we install from a source tarball. Then we install some supporting debs, followed by an RVM-powered install of Ruby 1.8.7 (as far as I can tell we need this in order to bootstrap Ruby 1.9, but to be honest I’m cargo-culting a lot of this). It builds it from source, and it takes a while.

Then it’s more supporting debs, and RVM-install Ruby 1.9.2 (more build-from-source, you should probably go and make a cup of tea). When it comes to installing the Rails gem, you’ll notice that I’ve done it as a bit of bash:

code "gem install rails --version #{node[:rails][:version]}"

rather than the more usual Chef-style gem_package "rails". This is because (I presume) gem_package uses the stock Ruby under which Chef is running, whereas we want to nail Rails into our new Ruby 1.9.2 setup.

Then I was forced to resort to a light bit of cruft, I’m afraid: the howto is for installing a development Rails environment, so it installs Rails only for the user, not system-wide, and puts it on the ${PATH}. The way I’m doing I’m doing it here, it doesn’t get on the path. I tried to manage this with update-alternatives, but that got messy very quickly. So, symlinks!

And now, on the target machine:

sam@amee:~$ ruby -v
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-linux]
sam@amee:~$ rails -v
Rails 3.1.3
sam@amee:~$ bundle -v
Bundler version 1.0.21

Looks good so far.

Passenger

Passenger is a zero-config Rails proxy, considered by many to be indistinguishable from magic. Now there is a deb for the apache module, which is a win, but for our requirements, there’s more to be done.

We need apache2-dev and libcurl for reasons that will become clear soon (and apache2 will be installed as a dependency, if it’s not already on the target node), then we install the gem via a bit of bash, as above and for the same reasons.

And once again we’re building from source! Just like we used to in 1999! Hence apache2-dev and libcurl. This step takes a while.

Finally, we construct the mod_passenger conf files for apache, inserting our various versions of things as appropriate, and:

root@amee:~# apache2ctl -t -D DUMP_MODULES | grep passenger
Syntax OK
 passenger_module (shared)

root@amee:~# cat /etc/apache2/mods-enabled/passenger.conf
PassengerRoot /usr/local/rvm/gems/ruby-1.9.2-p0/gems/passenger-3.0.11
PassengerRuby /usr/local/bin/ruby

root@amee:~# cat /etc/apache2/mods-enabled/passenger.load
LoadModule passenger_module /usr/local/rvm/gems/ruby-1.9.2-p0/gems/passenger-3.0.11/ext/apache2/mod_passenger.so

From zero to full Ruby-1.9.2/Rails-3.1.3/Passenger/Apache awesomeness in about 20 minutes!

Caveats

I developed this on a Lucid VirtualBox VM using Chef 0.10.4, and then deployed onto a pair of box-fresh Lucid nodes at our colo, and it worked just fine. YMMV.

I’m still fairly new to Chef, so there are definitely things in here that could be done better. Some of it feels very hard-coded and far too tightly-coupled – for example, it’s very much hitched to Apache, and I’m keen to see how this whole stack plays with nginx. It works for our purposes, but I’m sure somebody reading this can see where improvements could be made.

I’m also no Ruby expert (I’m definitely more Ops than Dev), so maybe the whole RVM thing could be handled better too. Please, have at it in the comments or on Github. I hope somebody finds this useful!

Get the cookbooks here.

Back to AMEE Blog