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 aspen-mri-1.8.6 bamboo-mri-1.9.2 * 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-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: http://devcenter.heroku.com/articles/bamboo -----> 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 firstname.lastname@example.org: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.
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' end group :development, :test do gem 'heroku_san' gem 'rspec-rails' gem 'sqlite3-ruby', :require => 'sqlite3' end
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
alias bd="bundle --without production staging"
Note that I excluded both
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
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 http://0.0.0.0:5000 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 0.0.0.0:5000, 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
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 logging:basic pgbackups:auto-month shared-database:5mb 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:
Eos:production-hub jcarlson$ heroku run rake db:setup
Eos:production-hub jcarlson$ heroku rake db:setup
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.