Up until lately I was happy using Capistrano for my deployments. But then I noticed the following:
Capistrano was utilizing 70 % of a physical CPU core when deploying on the go.
I’m on a dual core system so this impacts my Mac Book Pros overall performance, battery life and it kept me waiting for a long long time. My iPhone was at edge speed so I figured Capistrano spent most of the time waiting for network I\O - busy waiting that is.
So I profiled Capistrano using perftools.rb. Capistrano spent about 50% on Compat.io_select
over each deployment run. That’s 50 % of a total of 40 seconds it took to deploy & restart my application server. The percentage spent waiting on Compact.io_select
decreases to about 10% as the network speed increases to regular DSL levels.
The interesting part is that this is just CPU time. When profiling wall time Compact.io_select
is at 9%, while Object#process_iteration
is at 84%. Object#process_iteration
is a wrapper Capistrano introduces around Kernel#select
which ‘should’ not do busy waiting. But Capistrano kept my CPU busy anyways.
I converted my Capistrano configuration to Mina to see if it was any better.
Contrary to Capistrano Mina creates a bash script based on your deployment configuration and executes the script on the server side using a single SSH connection.
In theory this should be way faster than Capistrano which creates multiple SSH connections. It should have a far smaller CPU footprint as well given that Ruby is only used to assemble a bash script.
Timing both setups led to the following results:
$ time mina deploy
real 0m23.999s
user 0m0.188s
sys 0m0.054s
$ time cap deploy
real 0m40.561s
user 0m0.897s
sys 0m0.191s
As you can see Mina deployments take about half the time it takes Capistrano to deploy.
For comparison only, here’s a stripped-down version of my Capistrano configuration
require "rvm/capistrano"
require 'bundler/capistrano'
set :application, "my-app"
namespace :deploy do
task :start, :roles => :app, :except => { :no_release => true } do
run "sudo supervisorctl start #{application}:*"
end
task :stop, :roles => :app, :except => { :no_release => true } do
run "sudo supervisorctl stop #{application}:*"
end
task :restart, :roles => :app, :except => { :no_release => true } do
run "sudo supervisorctl restart #{application}:*"
end
end
as well as Minas:
require 'mina/bundler'
require 'mina/rails'
require 'mina/git'
require 'mina/rvm'
task :environment do
invoke :'rvm:use[ruby-1.9.3-p125@default]'
end
desc "Deploys the current version to the server."
task :deploy => :environment do
deploy do
invoke :'git:clone'
invoke :'bundle:install'
to :launch do
queue 'sudo supervisorctl restart my-app:*'
end
end
end
Conclusion
Mina is faster than capistrano, uses less CPU and less network bandwidth. Since it generates a bash script for deployment with appropriate exit statements it’s also much more transparent and easier to debug than capistrano will ever be.
Verdict:
Use Mina already!