One problem that seems to face people when they’re attempting to move their applications into production is the best way to manage deployment of their application. This is where tools like capistrano comes in.
Capistrano was written by Jamis Buck of 37signals. In a lot of ways it has become the defacto way to deploy Ruby on Rails applications. It has also had tools like webistrano build on top of it to provide a graphical interface to the command line tool.
To get started, you need to install the capistrano gem:
gem install capistrano
Once you have the gem installed go to your application’s directory and capify the app.
cd /path/to/app capify .
This will create two new files in your application’s folder: a Capfile in the application’s root folder and a deploy.rb in the application’s config folder. The deploy.rb file is where all of the magic happens. Here’s the default deploy.rb:
set :application, "set your application name here" set :repository, "set your repository location here" set :scm, :subversion # Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none` role :web, "your web-server here" # Your HTTP server, Apache/etc role :app, "your app-server here" # This may be the same as your `Web` server role :db, "your primary db-server here", :primary => true # This is where Rails migrations will run role :db, "your slave db-server here" # If you are using Passenger mod_rails uncomment this: # if you're still using the script/reapear helper you will need # these http://github.com/rails/irs_process_scripts # namespace :deploy do # task :start {} # task :stop {} # task :restart, :roles => :app, :except => { :no_release => true } do # run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}" # end # end
Let’s start at the top and work through this deployment recipe one line at a time.
First, set the application name to whatever you call your application. In my recipes, I set it to the url where you can access the application. This tells me where the application is deploying to.
set :application, "testapp.charlesmaxwood.com"
Now I need to tell the recipe where I store my code. I store mine at github, which obviously means I use git as my source control manager.
set :repository, "git@github.com:charlesmaxwood/testapp.git" set :scm, :git
One thing that drives me crazy with the default git setup is that it checks out your github repository, tars it up, copies it to your server via sftp, untars it, and then restarts your webserver. I’d much rather have my server just check the source code out from my github account. Lucky you and I, there’s an option to do that.
set :deploy_via, :remote_cache
This option just works for public repositories, but private repositories require authentication. Most git repositories use rsa public/private keys for this. Github has a guide for this.
Now that we can get the code, now we need to tell capistrano how to get onto the server we want our application deployed to.
set :user, "root" set :use_sudo, false
We also need to define server roles. In my case, the database, app, and web servers are all the same server.
role :web, "#{application}" # Your HTTP server, Apache/etc role :app, "#{application}" # This may be the same as your `Web` server role :db, "#{application}", :primary => true # This is where Rails migrations will run
Remember, application is the actual domain that I’m deploying to, which will resolve to the ip address of the server I want my application on.
By default capistrano will use sudo to deploy, which means you can use a user other than root. I use root and turn sudo off because most sudo configurations give the user sudo access to all commands by default. In this case, I don’t see much difference between that and just using root. This is primarily due to the fact that I don’t care to spend the time to make sudo more restrictive for my deployment user.
Now, let’s tell capistrano where to put the application on your server:
set :deploy_to, "/var/www/testapp"
The final thing we need to do is define any tasks we want capistrano to perform when it deploys. A few of these things may be installing gems, stopping/starting daemons, seeding data, or restarting your webserver.
The sample deploy stop and start tasks didn’t work for me out of the box. As soon as I uncommented them, it complained about the {} following each one. So, here’s what I changed them to:
namespace :deploy do task :start do run "/etc/init.d/apache2 start" end task :stop do run "/etc/init.d/apache2 stop" end task :restart, :roles => :app, :except => { :no_release => true } do run "touch #{File.join(current_path,'tmp','restart.txt')}" end end
The restart task in this case works for me because I’m running Phusion Passenger for my rails applications. You can restart passenger for your application by modifying or touching the tmp/restart.txt file.
Another task I usually need as I develop my applications is to install all of the gems in the config/environment.rb file.
namespace :gems do task :install do run "cd #{deploy_to}/current && RAILS_ENV=production rake gems:install" end end
Of course, having the task there doesn’t actually run it. You have to tell capistrano to run your task.
after :deploy, "gems:install"
You also need to run migrations. The task is built in, but I usually call it explicitly.
after "gems:install", "deploy:migrate"
Overall, I’ve been extremely impressed with Jamis’ work. I love what he’s provided for us to use in deploying our applications.