Deployment with Capistrano 3

Capistrano is a ruby based deployment tool which executes commands in parallel on multiple remote machines, via the SSH protocol. With Capistrano you can deploy your Rails application on N servers with one single command from your dev machine. You even don’t need to login via SSH to your server. This command can rollout your application on N servers:

cap production deploy

And if something goes wrong you can easily rollback to the last stable deployment. Just like this.

cap production deploy:rollback

Screen Shot 2013-01-15 at 8.15.33 PM

Capistrano is pretty cool. I used already the previous version 2.X. The new version 3.X I’m using already since a couple months in production and it is super stable.

If you are deploying your Rails application to dedicated servers or instances on AWS, than Capistrano is the way to go!

Before you start with Capistrano, you have to implement SSH with authentification keys instead of password. In that way you can just login to your server with a simple “ssh user@server” without password. That is possible if your public ssh certificates are on the server. In that way the server “knows” you.

First of all you need to add the Gem to your Gemfile.

gem 'capistrano'

And if you are using Rails and Bundler you want to add this 2 lines as well.

gem 'capistrano-rails' , '~> 1.1.1'
gem 'capistrano-bundler', '~> 1.1.2'

Now you have to run bundler, to install the packages.

bundle install

As next step you have to capify your rails project. Just run:

capify .

That will create some files in your project.

[add] writing './Capfile'
[add] writing './config/deploy.rb'
[add] writing './config/deploy/production.rb'
[add] writing './config/deploy/staging.rb'
[add] writing './config/deploy/test.rb'
[done] capified!

In the Capfile you can require some capistrano packages. For a Rails App it will look like this.

require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/bundler'
require 'capistrano/rails'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'

# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }

In Capistrano 3 most of the magic happens in the deploy.rb file, which is the central configuration file for Capistrano. In general it fetches the current code from your Git server, runs bundler, rake db:migrate, precompiles your assets and starts/restarts the ruby app server.

Here is my deploy.rb with some additional comments.


# Force rake through bundle exec
SSHKit.config.command_map[:rake] = "bundle exec rake"

# Force rails through bundle exec
SSHKit.config.command_map[:rails] = "bundle exec rails"

set :migration_role, 'app' # Defaults to 'db'
set :assets_roles, [:app] # Defaults to [:web]

# The name of your application
set :application, 'myapp'

# Configuration for the source control management system
set :scm , :git
set :repo_url, 'git@github.com:myorga/myapp.git'
set :branch , "master"

# This forwards the user agents and uses the local
# user for the git authentification.
set :ssh_options, {:forward_agent => true}

# User on remote server
set :user , "ubuntu"

# Application root directory on remote server
set :deploy_to , '/var/www/myapp'

# Shared directories over different deployments
set :linked_dirs, %w(pids log)

# Configuring capistrano log output
set :format , :pretty
set :log_level, :info # :debug :error :info

# Keeps the last 5 deployments on the server for rollback scenarios
set :keep_releases, 5

namespace :deploy do

 desc 'Start application'
  task :start do
   on roles(:app), in: :sequence, wait: 5 do
   execute "/etc/init.d/unicorn.sh start"
  end
 end

 desc 'Stop application'
  task :stop do
   on roles(:app), in: :sequence, wait: 5 do
   execute "/etc/init.d/unicorn.sh stop"
  end
 end

 desc 'Restart application'
  task :restart do
   on roles(:app), in: :sequence, wait: 5 do
   execute "/etc/init.d/unicorn.sh restart"
  end
 end

 after :finishing, 'deploy:restart'
 after :finishing, 'deploy:cleanup'

end

The script for starting and stoping unicorn you can find here: https://robert-reiz.com/2012/02/29/running-unicorn-as-a-service-on-debian-linux/.

In Capistrano you have different environments. For example “test”, “staging” and “production”. You can define as much as you want. Each environment has his own configuration file under “config/deploy/”. For example “config/deploy/production.rb”. Which might look like this:

set :stage, :production

# Setting RAILS_ENV environment variable on server
set :rails_env, :production

set :normalize_asset_timestamps, %{public/images public/javascripts public/stylesheets}

role :app, %w{ubuntu@myapp_server}

set :ssh_options, {
   forward_agent: true # , auth_methods: %w(password)
}

The most important line is the one with the role. In capistrano you can define different roles and assign them to different servers. So that some deployment commands will be only executed on specific servers. You can read more to that in the official docu. For this article I keep it simple and go ahead with only 1 role and 1 server.

On the remote server(s) you have to create the application root directory. If your application has the name “myapp” it would look like this:

  /var/www/myapp
  /var/www/myapp/releases
  /var/www/myapp/shared

Make sure that the user you defined in the deploy.rb file has full read and write access to this directories. Capistrano will create for each deployment a separate directory in the “release” directory, named with the timestamp of the deployment. The last deployment will be linked to “/var/www/myapp/current”. The “current” directory is a symbolic link to the latest deployment in “/var/www/myapp/releases”.

Now you can deploy with:

cap production deploy

If you have done everything right the deployment will run through and deploy your application.

This command shows you all possible Capistrano tasks:

cap -T

If you don’t deploy on Heroku or CloudControl, than Capistrano is a big help. It makes life much easier 🙂

Let me know if you have questions. Either in the comments or on Twitter.

Capistrano + Rails + Unicorn + NGinx + RVM

Capistrano is a ruby based deployment tool which executes commands in parallel on multiple remote machines, via the SSH protocol. With Capistrano you can deploy your application on N server with one single command from your dev machine. You don’t need to login via SSH to your server.

Screen Shot 2013-01-15 at 8.15.33 PM

If you don’t deploy your app on Heroku or CloudControl, if you have dedicated servers or you are using AWS, than Capistrano is the way to go.

Before you start with Capistrano, you have to implement SSH with authentification keys instead of password. In that way you can just login to your server with a simple “ssh user@server” without password. That is possible if your public ssh certificates are on the server. In that way the server “knows” you.

First you need to add the Gem to your Gemfile.

gem 'capistrano'
gem 'rvm-capistrano'

I am adding here “rvm-capistraon”, too. Because I am working with RVM on my dev machine and on the server. Now you have to run bundler.

bundle install

As next step you have to capify your rails project. Just run:

capify .

That will create some files in your project.

[add] writing './Capfile'
[add] writing './config/deploy.rb'
[done] capified!

Your Capfile is loading the deploy.rb script. The Capfile should look like this:

load 'deploy'
# Uncomment if you are using Rails' asset pipeline
# load 'deploy/assets'
load 'config/deploy'

The magic happens in the deploy.rb file. The full documentation to this script you will find here: https://github.com/capistrano/capistrano. It basically executes a couple of commands on your server. It fetches the current code from your GIT server, runs bundler, rake db:migrate, precompiles your assets and runs the ruby app server.

Here is my deploy.rb with some additional comments.


# Automatically precompile assets
load "deploy/assets"

# Execute "bundle install" after deploy, but only when really needed
require "bundler/capistrano"

# RVM integration
require "rvm/capistrano"

# Name of the application in scm (GIT)
set :application, "your_app"
set :repository, "git@github.com:your_company/your_project/project.git"

# Source Control Management
set :scm, :git

set :deploy_to, "/var/www/#{application}"

# server there the web server is running (nginx)
role :web, "192.168.0.12"

# server there the app server is running (unicorn)
role :app, "192.168.0.12"

# server there the db is running
# This is where Rails migrations will run
role :db, "192.168.0.12", :primary => true

set :rails_env, :production

# user on the server
set :user, "project_user"
set :use_sudo, false

# Target ruby version
set :rvm_ruby_string, '1.9.3'

# System-wide RVM installation
set :rvm_type, :system

# Apply default RVM version for the current account
after "deploy:setup", "deploy:set_rvm_version"

namespace :deploy do

  task :set_rvm_version, :roles => :app, :except => { :no_release => true } do
    run "source /etc/profile.d/rvm.sh && rvm use #{rvm_ruby_string} --default"
  end

  task :start, :roles => :app, :except => { :no_release => true } do
    run "/etc/init.d/unicorn start"
  end

  task :stop, :roles => :app, :except => { :no_release => true } do
    run "/etc/init.d/unicorn stop"
  end

  task :restart, :roles => :app, :except => { :no_release => true } do
    run "/etc/init.d/unicorn restart"
  end

  # Precompile assets
  namespace :assets do
    task :precompile, :roles => :web, :except => { :no_release => true } do
      run %Q{cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:precompile}
    end
  end

end

The script for starting and stoping unicorn you can find here: https://robert-reiz.com/2012/02/29/running-unicorn-as-a-service-on-debian-linux/.

On the Linux server you should install a couple things before you deploy:

apt-get install make
apt-get install g++
gem update --system
gem install bundler

Now you can deploy with:

cap deploy

And this command shows you all possible Capistrano tasks:

cap -T

Let me know if you have questions.