Upgrading to Rails 3.1 on Heroku — Part II: Stack Migration

In the last post, I started upgrading to Rails 3.1 on Heroku by first satisfying some of the prerequisites, such as ensuring that my application would run on Ruby 1.9.2.

In this post, I’m going to continue that process by actually migrating my Heroku application from a Bamboo stack to the new Cedar stack.

Since this is a real-world, live application, it’s important that everything be tested in a staging environment before I start mucking about with the live application. Since Heroku apps are (almost) free and take (almost) no time to setup, I’ll be relying heavily on that feature during this process.

If you don’t have a staging environment for your live application, you really should. I would encourage you to look at some of these tools to help make the process of migrating code from one system to another easier:

  • heroku_san: Gem to assist in deployment of Heroku apps, especially when dealing with multiple environments
  • git-flow: Git extension to help add flow to the development/release cycle

From bamboo-ree-1.8.7 to bamboo-mri-1.9.2

At the time I created Production Hub and deployed it to Heroku, bamboo-ree-1.8.7 was the default stack, and thus that is what it still runs on. Since the last post focused on upgrading Ruby from 1.8.7 to the Cedar stack’s 1.9.2, we should do the same with our existing stack to make sure the application still runs on Heroku under Ruby 1.9.2. In fact, even Heroku recommends doing this.

If you’re following along and your app is already on bamboo-mri-1.9.2, you can skip ahead. If you’re not sure, you can see what stack you’re on by running heroku stack --app <app name>.

Eos:production-hub jcarlson$ heroku stack --app production-hub
* bamboo-ree-1.8.7
  cedar (beta)
Eos:production-hub jcarlson$ 

Fortunately, this step is pretty easy. We just need to tell Heroku to go ahead and migrate the stack on next deploy, and then make a deploy. Since I started a support/ruby-1.9.2 branch back in the previous post, I’ve gone ahead and merged that into pipeline with a little git checkout pipleline; git merge support/ruby-1.9.2 action.

I’ve also started a release branch using git flow release start cedar. Setting up a release branch will allow us to track any changes we have to make to the code related to this deployment, and we can even push directly from the release branch to our staging server to test the deployment without finalizing the release branch!

Migrate the stack

First we tell Heroku to initiate the migration from bamboo-ree-1.8.7 to bamboo-mri-1.9.2 as follows:

Eos:production-hub jcarlson$ heroku stack:migrate bamboo-mri-1.9.2 --app production-hub-staging
-----> Preparing to migrate production-hub-staging
       bamboo-ree-1.8.7 -> bamboo-mri-1.9.2

       NOTE: You must specify ALL gems (including Rails) in manifest

       Please read the migration guide:

-----> Migration prepared.
       Run 'git push heroku master' to execute migration.
Eos:production-hub jcarlson$

Next, we’ll push our release branch to the staging server to finalize the migration. You may need to ‘touch’ some code if your Heroku app is already up to date in order to kick-off a git push.

Eos:production-hub jcarlson$ git push staging release/cedar:master
Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 328 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)

-----> Heroku receiving push
-----> Migrating from bamboo-ree-1.8.7 to bamboo-mri-1.9.2

-----> _some details were omitted_

-----> Compiled slug size is 4.5MB
-----> Launching... done, v10
       http://production-hub-staging.heroku.com deployed to Heroku

-----> Migration complete, your app is now running on bamboo-mri-1.9.2

To git@heroku.com:production-hub-staging.git
   1a4a9dd..bb80f20  release/cedar -> master
Eos:production-hub jcarlson$

That’s it! Now I just have to go test the app and we should be good.

Moving onto Cedar

Heroku does not support stack migration between Bamboo and Cedar stacks, so to complete the next step of the process, I’ll simply create a new application on the cedar stack and push to it. When I’m satisfied that the new stack is running as expected, I’ll just update the domain names and (eventually) delete the old app.

Prepare for Cedar stack

Following the getting started guide from Heroku for Rails 3 applications on Cedar, there’s a few prerequisite changes to my codebase.

Database changes

For one, I need to use the PostgreSQL database in production, but I’d like to keep SQLite3 in development. Heroku recommends running PostgreSQL locally for development, but since I have a dedicated staging environment on Heroku, that’s more better than enough to satisfy my testing requirements.

group :production, :staging do
  gem 'pg'

group :development, :test do
  gem 'heroku_san'
  gem 'rspec-rails'
  gem 'sqlite3-ruby', :require => 'sqlite3'

Then I just bundle install; but there’s a catch. I don’t have the PostgreSQL binaries and libraries on my system, so Bundler can’t compile the native extensions for the pg gem! Crap. To resolve this for now, I’m going to cheat and add this to my ~/.bash_profile:

alias bd="bundle --without production staging"

Note that I excluded both production and staging. After re-running bd install, everything worked fine.

Web server changes

The Cedar stack offers quite a bit more control over the way my application will be served, right down to a choice of web servers. It’s something I’ll need to investigate in more detail later, but for now, I’ll follow Heroku’s recommendation to use Thin, which will require a procfile and the foreman gem. I added the following to my Gemfile and then ran bd install:

gem 'foreman'
gem 'thin'

Then, I created a Procfile for Foreman.

web: bundle exec rails server thin -p $PORT

Let’s test that the Thin web server is up and running by starting things up with. So far so good.

Eos:production-hub jcarlson$ foreman start
10:40:16 web.1     | started with pid 17676
10:40:19 web.1     | => Booting Thin
10:40:19 web.1     | => Rails 3.0.3 application starting in development on
10:40:19 web.1     | => Call with -d to detach
10:40:19 web.1     | => Ctrl-C to shutdown server
10:40:21 web.1     | >> Thin web server (v1.2.11 codename Bat-Shit Crazy)
10:40:21 web.1     | >> Maximum connections set to 1024
10:40:21 web.1     | >> Listening on, CTRL+C to stop

Create a new application

I’m now ready to push my application to the Cedar stack. First, I need to create an application on the Cedar stack with heroku create --stack cedar.

The command will automatically add a Git repository to your remotes called heroku if you don’t already have one. Mine were named production and staging accordingly, so I’ll rename the new heroku remote to cedar for now with git remote rename heroku cedar.

Configure the new application

Before I push any code to the new application, I need to tweak some of the settings and add-ons so it aligns with how my staging environment is setup.

First, let me see what the current settings are for my staging environment:

Eos:production-hub jcarlson$ heroku config --app production-hub-staging
DATABASE_URL        => <sorry, secret!>
LANG                => en_US.UTF-8
PGBACKUPS_URL       => <sorry, secret!>
RACK_ENV            => production
SHARED_DATABASE_URL => <sorry, secret!>
Eos:production-hub jcarlson$

The database and RACK_ENV configurations will be handled by Heroku automatically, so I won’t worry about those. I don’t really have any other custom configurations, so I’ll move on to add-ons.

Eos:production-hub jcarlson$ heroku addons --app production-hub-staging
Eos:production-hub jcarlson$

Each of these add-ons can be added easily to the new application with heroku addons:add <addon:name>.

Deploying the code

Now that I’ve finished preparing my new Cedar stack environment, I’m ready to push my code. A quick gt status reports that the working directory is clean and I am on the releases/cedar branch. Good. Time to push:

Eos:production-hub jcarlson$ git push cedar release/cedar:master

Don’t forget, of course, to migrate the database! Note that the syntax for this Heroku command is a little different for Cedar-stack applications than Bamboo:

On Cedar:

Eos:production-hub jcarlson$ heroku run rake db:setup

On Bamboo:

Eos:production-hub jcarlson$ heroku rake db:setup

Quality test

Nothing left to do now but make sure the application is working as expected! Importing a database dump from the production site for testing wouldn’t be a bad idea.

One caveat that needs attention is that the new Cedar stack does not include an HTTP cache, like Varnish, as Bamboo did. In Rails 3.1 applications, Rack::Cache is included by default for production, but since I haven’t yet upgraded to Rails 3.1, my application might suffer some performance issues until I complete the upgrade.

heroku pgbackups:restore DATABASE heroku pgbackups:url --app production-hub –app severe-dawn-3900

Finishing the release

I’ll go ahead and tag my release code at this point. Since I’m using git-flow, it’s as simple as git flow release finish cedar. The release branch gets merged into master and a tag is created. Don’t forget to push master (and the tag!) to any remote repositories, like GitHub, if you’re using them.

I’ll also want to sunset the old staging environment so I can rename the new one appropriately.

Leave a Reply

Your email address will not be published. Required fields are marked *