cooking with
... and deploying your Rails apps
Coimbra.rb 10/2013
Capistrano
What is it?
Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH.
on rubygems.org
Capistrano is written in Ruby, but it can easily be used to deploy any language.on capistranorb.com
Disclaimer: This presentation is based on Capistrano 2.x
Features
Deploys web applications directly from
source control to one or multiple servers
Support for multiple version control systems
(git, svn, mercurial, cvs, ...)
Rollback code to earlier releases, with ease
Support for multiple environments
(on 2.x via capistrano-multistage, by default on 3.x)
Getting started
cd my_awesome_app/ gem install capistrano
gem install capistrano-multistage
# (or just include these on your Gemfile) capify .
deploy.rb W/ Multistage
Generic details about your app, independent
from the target environment
require 'capistrano/ext/multistage'
set :stages, %w(production staging)
set :default_stage, "staging"
set :application, "set your application name here"
set :scm, :git
set :repository, "ssh://ourserver/#{application}.git"
set :deploy_to, "/var/www/#{application}"
set :deploy_via, :remote_cache
set :rails_env, 'production'
Environment Definitions
User, hosts, branches
e.g.: On a staging environment, we would like
to deploy from a development branch or feature branch
set :user, "your username here"
set :branch, "desired branch to deploy from"
role :web, "your web-server here"
role :app, "your app-server here"
role :db, "your primary db-server here", :primary => true
role :db, "your slave db-server here"
Deploying
To deploy our latest code to the target server (already provisioned) we just type
But if this is our first deploy on the target server
Will create our folder structure on the server and deploys and starts a "cold" application
cap <enviroment> deploy:setup
cap <environment>
deploy:cold
cap <environment> deploy
NOT ONLY FOR DEPLOY
We can also provision servers with it.
But how?
first, some basics
Capistrano DSL
The syntax is similar to the Rake syntax:
desc "Task description"
task :<task name>, roles: [<desired role(s) which should run the task >] do
run "apt-get -y update" # Bash commands can be used like this
end
after "deploy:<task>", "<namespace>:<task name>"
MOre BASICS
Built-in tasks
deploy:setup
deploy:start
deploy:stop
deploy:symlink
deploy:update
deploy:update_code
deploy:updload
deploy
deploy:check
deploy:cleanup
deploy:cold
deploy:finalize_update
deploy:migrate
deploy:migrations
deploy:restart
deploy:rollback
deploy:rollback_code
Provisioning
1. Install dependencies
2. Setup configuration files
3. Create service lifecycle policies
4. Monitoring your stack
Installing dependencies
Triggered by the install task
Run necessary installation commands
Mind the "-y" to use non-interactive mode
desc "Install latest stable release of nginx"
task :install, roles: :web do
run "#{sudo} add-apt-repository -y ppa:nginx/stable"
run "#{sudo} apt-get -y update"
run "#{sudo} apt-get -y install nginx"
end
after "deploy:install", "nginx:install"
cap <environment> deploy:install
Setup Configuration files
Triggered by the setup task
Set up the requires variables for configuration
So they can be used on the configuration file templates
set :domain, "myawesomeapp.com"
cap <environment> deploy:setup
SETUP CONFIGURATION FILES
Main Task and auxiliary methods
Usage of templates to generate our configuration files
desc "Setup nginx configuration for this application"
task :setup, roles: :web do
template "nginx_unicorn.erb", "/tmp/nginx_conf"
run "#{sudo} mv /tmp/nginx_conf /etc/nginx/sites-enabled/#{application}"
run "#{sudo} rm -f /etc/nginx/sites-enabled/default"
restart
end
after "deploy:setup", "nginx:setup"
def template(from, to)
erb = File.read(File.expand_path("../templates/#{from}", __FILE__))
put ERB.new(erb).result(binding), to
end
Setup COnfiguration FILES
ERB Templates
upstream unicorn { server unix:/tmp/unicorn.<%= application %>.sock fail_timeout=0; } server { listen 80; server_name <%= domain %>; root <%= current_path %>/public;
location ^~ /assets/ { gzip_static on; expires max; add_header Cache-Control public; }
... }
Monitoring
Using Monit (start/stop/restart scripts)
Keep track of infrastructure failures
check process nginx with pidfile /var/run/nginx.pid
start program = "/etc/init.d/nginx start"
stop program = "/etc/init.d/nginx stop"
if children > 250 then restart
if 5 restarts within 5 cycles then timeout
On this particular case, nginx already has a startup script, but for most of the recipes I've written, I had to create my own
Piece of advice: Mind the pid!
other utilities
Real-time server logs
Clone production database to development
Access Rails production console from your shell
Run Rake tasks on production
desc "Tail all application log files"
task :tail, :roles => :app do
run "tail -f #{shared_path}/log/*.log" do |channel, stream, data|
puts "#{channel[:host]}: #{data}"
break if stream == :err
end
end
Dry-run Deployments
To run the whole provisioning/deployment process
Making sure that everything is running nice and clean
Capistrano 3 now has a "dry-run" mode,
through which we can visually confirm every
command to be run without ever touching a server
Pros
Provisioning and deployment processes
are done in similar ways
Learning curve is quite accessible
Fast deployments for prototyping
(You can deploy a new app in 5 minutes or less)
Cons
OS/Distribution centric
Currently I just work with Debian/Ubuntu,
but with some work we can support multiple distros
Is it a good practice to keep our
provisioning/deployment processes coupled?
Maintenance can be tricky on complex infrastructures
Alternatives
Recipes so far
Not yet published but... It will be soon. Suggestions?
Ongoing work
Port current recipes to Capistrano v3
(slightly different DSL, but could save us
some lines of code)
Standardize recipes to fit multiple distros
and package managers (yum, pacman, ...)
Distribute as a gem?
Thanks guys!
Jorge Santos
Capistrano
By jsantos
Capistrano
- 3,014