Zero-Downtime Deployments with Capistrano
It’s 2007, and if you’re still SSH-ing into your production server to run svn update and restart Mongrel manually, you’re asking for trouble. Capistrano has become the de-facto standard for Rails deployments, and for good reason. It turns a multi-step, error-prone process into a single command: cap deploy.
The Architecture of a Capistrano Deploy
Capistrano works by creating a specific directory structure on your server:
releases/: Contains timestamped folders for each deployment.current/: A symlink pointing to the latest release in thereleases/directory.shared/: Files that persist across deployments (likedatabase.yml, logs, and system uploads).
When you run cap deploy, Capistrano checks out the code into a new releases/ subfolder, runs your migrations, and then atomically updates the current symlink. If anything goes wrong, cap deploy:rollback just points the symlink back to the previous release folder.
A Typical deploy.rb Configuration
Here is how we set up a multi-server Rails cluster in 2007:
set :application, "my_cool_app"
set :repository, "svn://svn.example.com/apps/my_cool_app/trunk"
# We're moving to Git, but many are still on Subversion!
# set :scm, :subversion
role :web, "web1.example.com", "web2.example.com"
role :app, "app1.example.com", "app2.example.com"
role :db, "db1.example.com", :primary => true
set :deploy_to, "/var/www/#{application}"
set :user, "deploy"
set :use_sudo, false
namespace :deploy do
desc "Restarting mod_rails with restart.txt"
task :restart, :roles => :app, :except => { :no_release => true } do
run "touch #{current_path}/tmp/restart.txt"
end
task :after_update_code, :roles => :app do
run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
end
end
Handling SSH Keys and Roles
Capistrano uses the Net::SSH Ruby gem to execute commands in parallel across all servers defined in your roles. By using SSH keys (Agent Forwarding), you don't have to embed passwords in your scripts.
The role system is brilliant. You can tell Capistrano to only run migrations on the :db role, while restarting the application on all :app roles. This level of granularity is what makes it possible to scale from a single VPS to a massive cluster of rack-mounted servers.