Monday, November 7, 2011

Deploying Rails 3.1 applications on Heroku's Celadon Cedar stack

http://batsov.com/Ruby/Heroku/2011/10/30/deploying-rails31-apps-on-celadon-cedar.html

Deploying Rails 3.1 applications on Heroku's Celadon Cedar stack

Prelude

Heroku is an amazing cloud hosting solution. It's extremely well documented, very easy to start with, very stable and provides you with the option to use free hosting for some small applications.
While Heroku is generally known as a Ruby on Rails hosting company, since their acquisition by SalesForce last year, they've expanded their deployment options a lot. Currently Ruby, Java, Scala, Clojure, Python and Node.js are all officially supported (on the Cedar Celadon stack) and more will probably come very soon.
All of our company's applications are using Ruby on Rails 3.1.x and are targeting the MRI 1.9.2. Those two facts are the reason that we're using Heroku's Celadon Cedar deployment stack. Celadon Cedar is currently in beta, but it offer a lot of benefit over the old (stable) Bamboo stack (which also support Rails 3.1 apps and Ruby 1.9.2). For instance - Celadon is aware of the Rails 3.1 asset pipeline and can compile the assets automatically when you deploy your apps to Heroku. With Bamboo you have to precompile the resources, which is a very tedious task.
While our experience with Heroku has been very positive in general, we've hit some bumps along the road, so I've decided to share some of the problems and their solutions with everyone.

Background

There are some things to keep in mind before deploying to Heroku:
  • You can't upload files to Heroku. This means that if you're using a gem such as CarrierWave (or PaperClip) for file uploads it should be configured to use some cloud storage (e.g. Amazon S3). There is only one writeable folder on Heroku and this is the tmp folder that you should configure as a tmp folder for your uploads as well.
  • Your app should run on Ruby 1.9.2 (the Celadon Cedar stack, doesn't support Ruby 1.8.x).
  • Your rake assets:precompile task should (ideally) not invoke any database related operations.
  • You should use PostgreSQL as your local development database to avoid potential differences between the production and the development database.
  • You should add the heroku gem to your Gemfile.
  • You should install the taps gem if you'd like to use heroku db:pull or heroku db:push.

Preparations

Configure CarrierWave to use cloud storage

Here's a sample carrierwave.rb, that you can put in config/initializers/ folder:
if Rails.env.test? # Store the files locally for test environment
  CarrierWave.configure do |config|
    config.storage = :file
    config.enable_processing = false
  end
end

if Rails.env.development? or Rails.env.production? # Using Amazon S3 for Development and Production
  CarrierWave.configure do |config|
    config.root = Rails.root.join('tmp')
    config.cache_dir = 'uploads'

    config.storage = :fog
    config.fog_credentials = {
        :provider => 'AWS', # required
        :aws_access_key_id => 'key_id', # required
        :aws_secret_access_key => 'access_key', # required
    }
    config.fog_directory = 'empoweronrails' # required
  end
end

Disable db access during rake assets:precompile

Some gems (like Rails Admin) access the database during the initialization of the app. The app initialization is ran by default when you invoke:
rake assets:precompile
This is not a problem on your development machine, but it's problematic on Heroku, since the regular database.yml is discarded there. Add this:
config.assets.initialize_on_precompile = false
somewhere near the end of your application.rb file and it will suppress the initialization on precompile.

Enable assets compilation

It's absolutely required to have this line:
config.assets.compile = true
in your production.rb file if you want your assets to be compiled automatically by Heroku (which I highly recommend).

Use thin

By default, your app's web process runs rails server, which uses Webrick. This is fine for testing, but for production apps you`ll want to switch to a more robust webserver. I personally use Thin (recommended by Heroku). Add this to your Gemfile:
gem 'thin'
and this to your Procfile (create it if it doesn't already exist):
web: bundle exec rails server thin -p $PORT
The creation of the Procfile is very important! You can use the foreman gem to test the correctness of the Procfile locally.

Deployment

First you should create a Heroku application on the Cedar Celadon stack.
heroku create --stack cedar
This step will automatically add a git remote called heroku to your git repo's config. Afterwards the deployment is as simple as pushing a branch (e.g. master) to this remote:
git push heroku master
Keep in mind that one Heroku app corresponds to exactly one git branch. We keep a production branch for production deployments and amaster branch for development deployments.
The last step is to initialize your database. You have two options - you can either load the db schema or push an existing database:
heroku run rake db:schema:load
or
heroku db:push
db:push takes as an optional parameter the URL of the db to push to heroku in the following formatdb://username:password@host/dbname. For example the url for a local SomeApp db is probablypostgres://someapp:someapp@localhost/someapp. If you don't supply the URL it will be automatically conjured by inspecting thedatabase.yml file of the project you were in, while issuing the command.

Troubleshooting

If you're lucky your deployment will go without a hitch and you won't have to ever read this section of the manual. Most people won't be so lucky. :-)

Errors during deployment

If you get an error during the deployment process the cause of the problem will be in front of your eyes. Never-the-less here are some of the most common problems:
  • missing Gemfile.lock
  • Gemfile.lock that doesn't match the project's Gemfile (this happens if you have OS specific gems in your Gemfile)
  • db access on assets:precompile
  • you forgot the run all specs and cucumber scenarios before deployment ;-)

Other errors

Obviously you need to take a look at the stack trace to gain some insight about the nature of the problem. You can do this very easy:
heroku logs
Some of the most common errors you'll encounter:
  • you forgot to run some migration(s)
  • you forgot to turn on asset compilation
  • you forgot to run all specs and cucumber scenarios before the deployment

Misc

Dealing with the database on Heroku

You can easily apply migrations to the production database. Just run:
heroku run rake db:migrate
If you want to retrieve the production database locally use the following command:
heroku db:pull postgres://username:password@localhost/dbname
If you want to push your local db to production run the following command:
heroku db:push
Be very careful about the last command! It will wipe out and replace the current production database!

Running a console

Running the Rails console on Heroku is astonishingly easy. Just run:
heroku run rails console

Epilogue

Heroku and Rails are moving targets, which causes a bit of a headache from time to time. Hopefully this short article will save some of you some of that headache.
Posted on 30 Oct 2011 by Bozhidar. Filed under Ruby and Heroku.

No comments:

Post a Comment