Capistrano 2.0, upgrading & fitting into a size 0 dress

Posted by Mathew Abonyi Fri, 27 Jul 2007 14:02:09 GMT

The improvements to Capistrano are much welcomed. My deployment recipe is now half the length it used to be and it is much easier to follow what is happening for my many types of deployment. I love the new features added, mostly dealing with manipulating scopes and enhancing the user’s ability to extend the core framework.

Review of new features

namespaces: Like Rake, you can namespace your tasks and group them together more sensibly. This feature alone is worth upgrading for just to make your scripts more sensible and easier to read.

events: Like Rails, you can now perform tasks before or after other ones rather than using the hacky ‘before_something’ and ‘after_something’. Much cleaner and much faster too.

strategies: In addition to checkout, you can now deploy via export and copy and use different strategies for deployment, such as using export for your copy_strategy rather than zips and tarballs.

scoping: All sorts of scoping has been introduced in Capistrano 2.0, from namespacing to single execution of “run” and “sudo”, allowing you to define specific roles or hosts in which your commands run.

help: Capistrano 2.0 now has a more verbose way of explaining tasks with cap -e task_name. You’ll realise how useful this is when you use it for the built-ins as well as your own.

All in all, Capistrano is pretty simple, but it is the way it is written that makes it appear so much simpler than it really is. Capistrano 2.0 takes that to a new level, not groundbreaking perhaps, but definitely a lot cleaner than its previous releases.

Upgrading from 1.4.1

There is no need to change config/deploy.rb out of the box. Capistrano 2.0 is nicely backwards compatible, unlike other things out there, and, at least for me, nothing broke because of the upgrade.

You can look at Capistrano’s instructions for upgrading, if you want to know what is being done, but for the impatient, here are the steps you have to follow before we can start drying up your deploy script.

1. Install the new version of capistrano:
sudo gem install capistrano
2. cd project_root & run capify
~# cd projroot
projroot# capify .
3. Upgrade previous deployments to use the new revision tracking system
projroot# cap -f upgrade -f Capfile upgrade:revisions

4. Rinse and repeat for each of your deployment targets

Getting your deploy.rb into its new size 0 dress

You may now have the very understandable urge to slim down your deployment recipes. With the introduction of Capistrano 2.0, I found my deploy.rb reduced to less than half the size. Below, I cover the areas which you should focus on to get that deploy script into its new size 0 dress.

Anatomy of my deploy.rb

  1. requires: capistrano-ext, mongrel_cluster, etc.
  2. global, stage and custom variables
  3. event chains
  4. rewriting built-ins: web:disable and web:enable
  5. extra tasks: fixing permissions, copying mongrel confs, etc.
  6. custom deploy tasks: long, normal, quick
  7. maintenance tasks: backup, restore

Variables

More than before, variables are the lynch-pin of slimming everything down. The first thing you should do is look over every task rewrite or custom task and see how it can be turned into a simple set :var, true/false/whatever. Capistrano 2.0 will make it very easy to do this.

With Capistrano 2.0, you should use the set command religiously, both for built-in and custom tasks.

I personally set the following at the top of my recipe.

  • Global variables: stages, deploy_via
  • Application specific: application, repository, user, scm_username
  • Deployment specific: deploy_to, rails_env
  • Custom variables: serving_via, suexec, suexec_user, suexec_group, disable_template

Deployment Strategy

I would personally suggest using xport for your deploy_via strategy unless you have a reason for using heckout or copy.

Using Namespaces

Namespaces make it dead simple to group common tasks, like different restart methodologies. I use a serving_via variable which translates into the reload:whatever task to run for restarting the application. For example:

namespace :reload do
  desc "Default reloading procedure"
  task :default do
    mongrels
  end
  desc "Reload an FCGI application"
  task :fcgi, :roles => :app do
    sudo "#{current_path}/script/process/reaper -a graceful -d #{current_path}/public/dispatch.fcgi"
  end
  desc "Reload an LSAPI application"
  task :lsapi, :roles => :app do
    sudo "/usr/local/litespeed/bin/lswsctrl restart"
  end
  desc "Give the mongrels a bath"
  task :mongrels, :roles => :app do
    restart_mongrel_cluster
  end
end

Note: I warn against using restart as a namespace because it clashes with the built-in task and, in certain instances, results in infinite recursion.

Maintenance Splash

The biggest change in Capistrano you may need to worry about is the removal of delete and render. Don’t despair, though, because creating a maintenance splash is still easy. This is my rewrite:

desc "Generate a maintenance.html to disable requests to the application."
deploy.web.task :disable, :roles => :web do
  remote_path = "#{shared_path}/system/maintenance.html"
  on_rollback { run "rm #{remote_path}" }
  template = File.read(disable_template)
  deadline, reason = ENV["UNTIL"], ENV["REASON"]
  maintenance = ERB.new(template).result(binding)
  put maintenance, "#{remote_path}", :mode => 0644
end

desc "Re-enable the web server by deleting any maintenance file."
deploy.web.task :enable, :roles => :web do
  run "rm #{shared_path}/system/maintenance.html"
end

Using events

Like the before and after filters in Rails, you can now cleanly chain together tasks. I’m a sucker for one-line solutions and these are really so simple that it makes my heart bleed:

before "deploy:restart", "fix:permissions"
before "deploy:migrate", "db:backup"
after "deploy:symlink", "deploy:cleanup"
after "deploy:update_code", "deploy:web:disable"
after "deploy:restart", "deploy:web:enable"

capistrano-ext & multistage

I highly recommend the use of multistage. It comes with the capistrano-ext gem (which has been upgraded to Capistrano 2.0, of course).

Basically, it separates the concerns of different deployments. If, like me, you like having a few other versions of your application out there, like a staging area, a testing area for bleeding edge features, and, of course, the production site, separating these in Capistrano before 2.0 was very irritating. Multistage sorts that out very nicely.

By default, you must specify the stage you wish to deploy. This behaviour can be overridden by setting the default_stage variable, but I like being explicit. This is what using stages looks like:

# cap production deploy

If you don’t provide ‘production’, it’ll complain and abort.

Using multistage is dead easy. Put this at the top of your deploy.rb:

  require 'capistrano/ext/multistage'
  set :stages, %w(staging production testing)

Run the task for generating your stage deploy files:

projroot# cap multistage:prepare

This will create a recipe file for each stage in a new config/deploy directory (exactly like Rails environments). Now, in each stage recipe, add all of your stage-specific tasks and variables. For example:

set :rails_env, "stage"
set :application, "staging.example.com"
set :deploy_to, "/var/www/#{application}"

Now switching between different deployments is a breeze. Just make a new recipe file for it with the necessary variables and you’re set.

Posted in , ,  | 1 comment | no trackbacks

LiteSpeed, LSAPI & Capistrano

Posted by Mathew Abonyi Sat, 28 Oct 2006 13:03:00 GMT

Capistrano is a highly flexible deployment system (among other things) written in Ruby. It is used primarily by Rails applications, but can be suitable for any kind of web/database/application combination (Ruby or otherwise).

For more information about Capistrano, please read the Capistrano Manual for Rails.

Requirements

  • a web application to deploy
  • a working LSAPI setup
  • a working Capistrano installation on your client machine(s)
  • Capistrano set-up for your application on your server machine(s)

If any of this is bedazzling, please see the Capistrano Manual above.

Note: This wiki is based on Capistrano 1.2.0.

Quick Summary

Deploying an application to a LiteSpeed server requires you to check out your application to a new release directory, change the link for the RAILS_ROOT/current directory, and then restart the LiteSpeed server.

Before restarting Apache-style gives you shivers, know that LiteSpeed’s restart is instant, with no downtime and negligible effect on the server.

Step 1: Restricted SUDO

For those of you who do not have sudo set up to allow lswsctrl, you must change your /etc/sudoers file using visudo.

To restrict a user to only restarting the server, add the following with visudo:

Cmnd_Alias  LSWS = /opt/lsws/bin/lswsctrl restart
depuser     ALL = (root) LSWS

The LSWS command alias should be set to the path of your lswsctrl command.

Note: If you do not have root access yourself, you need to ask your system administrator to add you with the above sudoer entry. It only allows you to restart the server to get your application refreshed.

Step 2: Custom Deployment

The standard deployment script for Capistrano 1.2.0 assumes you are using SVN, deploying to one server, and restarting using the reaper script. To overwrite the default task, add the following to the end of your project’s config/deploy.rb:

set :lsws_cmd, "/opt/lsws/bin/lswsctrl"

task :restart, :roles => :app do
  sudo "#{lsws_cmd} restart"
end

Change the lsws_cmd to wherever your lswsctrl resides.

Note: You may wish, for cleanliness, place the ‘set’ command with your deploy_to, rails_env and user ‘set’s. A full deploy.rb can be seen at the end of this wiki for single and multiple application server deployments.

Step 3: Unprivileged User (Optional)

If you want your application to run in suEXEC mode, you need to create an unprivileged user for it and add whichever user will be deploying on the server to its group. It’s a little more tricky than most things and requires you to have at least this in your /etc/sudoers file:

depuser ALL = (root) LSWS, /bin/chown, /bin/chmod

The irony of using suEXEC is that you practically need root privileges to do it. Of course, your deployment user should not have such a frail password if it has this much power through sudo.

On most Linux systems, creating an application user would look like this:

sudo adduser --system --group --no-create-home --disabled-login --gecos "My Web App" mywebapp
sudo usermod -G mywebapp deploymentuser

Once you have your deployment user in the application user’s group, we set up Capistrano to deploy and fix permissions in config/deploy.rb. We do this after the code is checked out, like so:

set :jail_user, "mywebapp"
set :jail_group, "mywebapp"

task :after_update_code, :roles => :app do
  sudo <<-CMD
  sh -c "chown -R #{jail_user}:#{jail_group} #{release_path} &&
         chmod -R g+w #{release_path}"
  CMD
end

Now when you deploy, your entire application directory will be owned by the suEXEC user. If there are only specific directories (such as ‘public’ and ‘tmp’) you want to allow access to your application user, change the chown commands accordingly.

Step 4: Test It Out

Assuming you have your application, deploy_to and other parts of your Capistrano script configured properly, try it out:

shell /home/myproject$ cap deploy

The two most important things you should see are the password prompt for sudo and the lswsctrl command being run. It will probably not give any output, but you’ll notice your application is the latest deployment.

Step 5: Mulitple Application Servers

This is where Capistrano really shines. To make your application scale, just change this:

set :app, "www.example.com"

To this:

set :app, "www.example.com", "www2.example.com", "www3.example.com"

All of the deployment steps will be replicated across the application servers. It is important that they all have the same setup (i.e. same users, directory structure and the presence of LiteSpeed).

Example Deployment Script


set :user, "depuser"
set :svn_username, "depuser"

set :application, "www.example.com"
set :repository, "svn+ssh://#{user}@www.example.com/svn/trunk"

set :web, "www.example.com"
set :app, "www.example.com", "www2.example.com", "www3.example.com"
set :db, "db.example.com", :primary => true

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

set :lsws_cmd, "/opt/lsws/bin/lswsctrl"

set :jail_user, "mywebapp"
set :jail_group, "mywebapp"

task :after_update_code, :roles => :app do
  sudo <<-CMD
  sh -c "chown -R #{jail_user}:#{jail_group} #{release_path} &&
         chmod -R g+w #{release_path}"
  CMD
end

task :restart, :roles => :app do
  sudo "#{lsws_cmd} restart"
end

Good Luck!

Posted in ,  | no comments | no trackbacks