Staging and production release deployment with Git using Capistrano

by jagbir on June 25, 2012

There are many ways to deploy your code on staging and production environment. What I am describing here is just one of them. It’s working as expected for me but I recommend doing proper research before using it in your environment.

We assume here that there is one Server having Git along with Gitolite for authentication and authorization running RHEL/CentOS. We will call this as release server. I have already described how to setup this server with Git, Gitolite, Gitweb and SSH/HTTP authentication in earlier article.

Before proceeding, we need to have a plan as how Git is being used. Again, there are so many implementations and ways to do this. I am summarizing our approach below:
* We have 2 branches in every Git repository: Master and Prod.
* Developers commit on Master branch. When they want to push code to staging environment, they create a tag and request Ops to deploy that tag, which will be pushed using Capistrano.
* When Ops get request to push Tag to Production environment (after stag testing), a script get executed which will merge that Tag with Prod branch and create alias Tag with prod prefix for tracking purposes. That tag will then get pushed to Production environment.
* In case of incidents, issues when immediate hotfix is required, developers can directly commit code to Prod branch, create tag which will get pushed to Prod using Capistrano. Developers will then merge code with Master branch to keep both branches in Sync.

Here’s a pictorial representation of Git Flow:

This plan only covers Staging and Prod environment as other environments like Dev/QA etc are managed by Developers themselves but you can easily extend this to cover these env if required.

Now comes to Capistrano, which is a module/gem of Ruby, so you need to install Ruby first which should be quick:

$ yum install ruby ruby-devel ruby-libs ruby-irb ruby-rdoc

Download and install RubyGems now, which is needed to install other Gems/third-party modules and Capistrano is one. While writing this article, RubyGems version 1.8.24 was available:

$ wget http://rubyforge.org/frs/download.php/76073/rubygems-1.8.24.tgz
$ tag xzf rubygems-1.8.24.tgz
$ cd rubygems-1.8.24
$ ruby setup.rb

Let’s install Capistrano:

$ gem install capistrano --include-dependencies

We need to do initialization or “capify” our application which essentially a small process by which Capistrano creates some config files. You can go inside your application directory which in my case located at /var/www/deploy/myapp:

$ cd /var/www/deploy/myapp
$ capify . 
[add] writing './Capfile'
[add] making directory './config'
[add] writing './config/deploy.rb'
[done] capified!

You will find a config/deploy.rb which we have to update per our requirements. We have two stages: Stage and Prod, so you need to create two files for each stage having hostnames/IP address where code release have to be done:

$ cat config/deploy/staging.rb
role :web,  '192.168.10.50'
$ cat config/deploy/prod.rb
role :web, '192.168.10.60', '192.168.10.61', '192.168.10.62'

We are deploying using Git tags and accordingly our deploy.rb having some options, here is our deploy.rb:

require 'capistrano/ext/multistage'
 
set :application, "myapp"
set :stages, %w(prod staging)
 
set :use_sudo, false
set :port, 27000
set :user, "deploy"
set :normalize_asset_timestamps, false
 
set :deploy_to, "/var/www/html/#{application}"
set :shared_path, "#{deploy_to}/shared"
 
set :repository, "ssh://gitolite@our_git_server/repo_name"
set :scm, "git"
set :deploy_via, :remote_cache
set :mystage, ARGV[0]
set :branch do
  if mystage == "staging"
        default_tag = `cat /git/myapp_tags.txt | grep -v ^prod`.split("\n").last
  else
        default_tag = `cat /git/myapp_tags.txt`.split("\n").last
  end
  tag = Capistrano::CLI.ui.ask "Which tag to deploy: [#{default_tag}] "
  tag = default_tag if tag.empty?
  puts "Tag is: #{tag}"
  tag
end
 
set :copy_exclude, [ '.git' ]
set :runner, "deploy"
set :deploy_tag, "#{branch}"
 
namespace :deploy do
        desc 'Overrides default action to restart the server - not needed for Expression Engine site'
        task :restart, :roles => :app, :except => { :no_release => true } do
        end
end

Do not forget to update value in some variables as per your environment. When we initiate deploy using Capistrano, you issue command like “cap staging deploy”. In this case, we are identifying the stage with command line argument ARGV[0] which is “staging” here and then extract the latest tag (excluding tags whose names are prefixed by prod keyword). Confirm user which tag to deploy (display latest tag as default value) and then push that tag to stage environment. For production deployment, we issue command “cap prod deploy”. There’s empty declaration of restart task which is there to suppress warning. To avoid that, you can check railsless-deploy extension to override rails actions that don’t apply with our app. More info about Capistrano and deploy.rb setup be found here.

Make sure to add Servers SSH keys into your Gitolite-admin repository for access or supply proper http auth information to access using http.

What I have presented here is a quick method and of course, there can be lots of enhancements. Please provide your suggestions in comment section below.

Other related and helpful articles you may like:

Previous post:

Next post: