Smooth Devoperations: Deploying Rails 3 with Moonshine



Written By : Josh Nichols


February 17, 2010

Here at Rails Machine, we have written, on occasion, about moonshine, our open source configuration management and deployment system. But now, I’m happy to announce it works with the Rails 3.0.0.beta.

Lots of people have written about getting up and running on Rails 3, or about upgrading from Rails 2 to Rails 3. I’m not going to repeat that here, but here are some links that might interest you:

Instead, I will focus on getting an existing Rails 3 application deployed. For narrative purposes, I’m going to spin the tale in which I deploy a fictional application.

The background

Here’s a little background you should be aware of before I begin my tale

  • I have an application, zomgblog, which works with rails-3.0.0.beta
  • I’m hosting the code on GitHub at http://github.com/technicalpickles/zomgblog)
  • I have a freshly installed Ubuntu 8.10 server (the version supported by moonshine)
  • I have a domain name setup, zomgblog.technicalpickles.com, which points at my freshly setup server
  • I have a ‘rails’ user on the server, with my SSH public key setup in ~/.ssh/authorized_keys for password-less access
  • I setup sudo to allow ‘rails’ to allow sudo without a password by adding rails ALL=(ALL) NOPASSWD:ALL to /etc/sudoers on the server
  • I have installed capistrano locally: gem install capistrano

Adding moonshine to my application

I started in my local checkout. First thing I did was install moonshine itself:

rails plugin install git://github.com/railsmachine/moonshine.git

Next, I used the moonshine-provided generator to setup moonshine for my application. It has a few options, so I reviewed rails generate moonshine-- help. Ultimately, I decided on:

rails generate moonshine --domain zomgblog.technicalpickles.com --user=rails --repository [email protected]:technicalpickles/zomgblog.git

I don’t plan to deviate from the default moonshine stack, so there’s not much else to change. I do need to my config/database.yml for production though. Moonshine assumes MySQL will be used, and will handle installing it, creating the database, and running migrations, so I need a production stanza that looks like:

production:
  adapter: mysql
  encoding: utf8
  reconnect: false
  database: zomgblog_production
  pool: 5
  username: zomgblog
  password: zomgsosecretandsecure
  socket: /var/run/mysqld/mysqld.sock

I knew my application was working with the gem dependencies I had installed locally. I didn’t want any surprises though, so I should use the same versions on production. bundler made this easy:

bundle lock

Before proceeding, I needed to commit and push the changes:

git add .
git ci -m "Moonshined application and locked dependencies" 
git push origin master

The first deploy

At this point, I’m ready to bootstrap my server. This takes about 15 minutes, and will create the minimal environment that moonshine can run in.

cap deploy:setup

I’m ready to deploy when that’s done. Moonshine does a lot of heavy lifting during deployment which makes sure the server is in compliance with what’s defined in app/manifests/application_manifest.rb. Since I didn’t change anything, I end up with the default stack of Apache, MySQL, and Passenger. This usually takes about 10 minutes.

cap deploy

I may now rejoice and reap the rewards of a fully deployed Rails 3 application.

Subsequent deploys

Application development, like life, goes on. From now on, I can continue to develop and deploy just like any other Rails application out there

# insert development & testing
git push origin master
cap deploy

In cases of changing dependencies, I need to do a little extra work before deploying because of the way bundler works:

# make changes to Gemfile bundle unlock bundle install bundle lock git add Gemfile Gemfile.lock git ci -m “Updated dependencies” cap deploy

In the case of having to changing the configuration of my server, I don’t even need to ssh there. Moonshine (and the underlying shadow_puppet) provides an automated way to manage it. See the Shadow Puppet overview and examples for more details.

Some caveats with Rails 3 and moonshine

There are a two points worth noting, particularly if you are used to using moonshine with Rails 2.

First, before bundler was introduced, moonshine had it’s own mechanism for installing gems on the server:

  • Add dependencies to config/environment.rb
  • Run rake moonshine:gems to update config/gems.yml
  • Commit config/gems.yml.

When deploying, moonshine uses config/gems.yml to install the gems, in addition to system dependencies they might need (ie libxml2 for nokogiri).

But now Rails 3 has the bundler, so moonshine will try to use bundler if you have a Gemfile in your application. One side effect of this is that, because of a bug in rubygems-1.3.5, moonshine isn’t able to install system dependencies of gems managed by Gemfile. This is fixed in 1.3.6 when released

The second is that Rails 3 is Rack based, so Passenger’s Rack support is used. According to the 2.2.9 release notes, Passenger does not support it’s smart spawn mode for Rack applications yet though. Contrary to some reports, removing config.ru will not allow Passenger’s Rails support to be used instead of its Rack support because removing config.ru will render your application unbootable.

Future Moonshine developments

It’s been a long road to Rails 3, and we at Rails Machine fully intend to keep moonshine up and running with it. Don’t worry though: Rails 2 support is not being deprecated.

Supporting Rails 3 does open some interesting doors. In particular, by supporting bundler, any application with a Gemfile should benefit from the improvement. In addition, Rails 3 is a first class Rack citizen, so anytime there’s a config.ru file, Passenger will default to it’s Rack mode.

Between bundler support and detecting config.ru to turn on Passenger’s rack support, this moves moonshine in the right direction to support any Rack application in the future, not just Rails.