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-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:
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 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'
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 ~/.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 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 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
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:
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.