December 5, 2008

Day 5 - Capistrano

Do you store and deploy configuration files from a revision control system? You should. If you don't, yet, this article aims to show you how to make that happen with very little effort using Capistrano.

Capistrano is a ruby-powered tool that acts like make (or rake, or any build tool), but it is designed with deploying data and running commands on remote machines. You can write tasks (like make targets) and even nest them in namespaces. Hosts can be grouped together in roles and you can have a task affect any number of hosts and/or roles. Capistrano, like Rake, uses only Ruby for configuration. Capistrano files are named 'Capfile'.

Much of the documentation and buzz about Capistrano deals with deployment of Ruby on Rails, but it's not at all limited to Rails.

For a simple example, lets ask a few servers what kernel version they are running:

# in 'Capfile'
role :linux, "jls", "mywebserver"

namespace :query do
  task :kernelversion, :roles => "linux" do
    run "uname -r"
% cap query:kernelversion
  * executing `query:kernelversion'
  * executing "uname -r"
    servers: ["jls", "mywebserver"]
    [jls] executing command
 ** [out :: jls]
    [mywebserver] executing command
 ** [out :: mywebserver] 2.6.18-53.el5
    command finished
Back at the original problem being solved, we want to download configuration files for any service on any host we care about and store it revision control. For now, let's just grab apache configs from one server.

Learning how to do this in Capistrano proved to be a great exercise in learning a boatload of Capistrano's features. The Capfile is short, but too long to paste here, so click here to view.

If I run "cap pull:apache", Capistrano dutifully downloads my apache configs from 'mywebserver' and pushes them into a local svn repository. Here's what it looks like (I removed some output):

% cap pull:apache
    triggering start callbacks for `pull:apache'
  * executing `ensure:workdir'
At revision 8.
  * executing `pull:apache'
    triggering after callbacks for `pull:apache'
  * executing `pull:sync'
  * executing "echo -n $CAPISTRANO:HOST$"
    servers: ["mywebserver"]
    [mywebserver] executing command
    servers: ["mywebserver"]
 ** sftp download /etc/httpd/conf -> /home/configs/work/mywebserver
    [mywebserver] /etc/httpd/conf/httpd.conf
    [mywebserver] /etc/httpd/conf/magic
    [mywebserver] done
  * sftp download complete
    servers: ["mywebserver"]
 ** sftp download /etc/httpd/conf.d -> /home/configs/work/mywebserver
    [mywebserver] /etc/httpd/conf.d/README
    [mywebserver] /etc/httpd/conf.d/welcome.conf
    [mywebserver] /etc/httpd/conf.d/proxy_ajp.conf
    [mywebserver] done
  * sftp download complete
A         /home/configs/work/mywebserver/README
A         /home/configs/work/mywebserver/httpd.conf
A         /home/configs/work/mywebserver/magic
A         /home/configs/work/mywebserver/welcome.conf
A         /home/configs/work/mywebserver/proxy_ajp.conf
    command finished
Adding         configs/work/mywebserver/README
Adding         configs/work/mywebserver/httpd.conf
Adding         configs/work/mywebserver/magic
Adding         configs/work/mywebserver/proxy_ajp.conf
Adding         configs/work/mywebserver/welcome.conf
Transmitting file data .....
Committed revision 9.
If I then modify 'httpd.conf' on the webserver, and rerun 'cap pull:apache':
<output edited for content>
% cap pull:apache
 ** sftp download /etc/httpd/conf -> /home/configs/work/mywebserver
    [mywebserver] /etc/httpd/conf/httpd.conf
    [mywebserver] /etc/httpd/conf/magic
    [mywebserver] done
  * sftp download complete
Sending        configs/work/mywebserver/httpd.conf
Transmitting file data .
Committed revision 10.
Now if I want to see the diff against the latest two revisions, to see what we changed on the server:
% svn diff -r9:10 file:///home/configs/svn/mywebserver/httpd.conf
Index: httpd.conf
--- httpd.conf  (revision 9)
+++ httpd.conf  (revision 10)
@@ -1,3 +1,4 @@
+# Hurray for revision control!
 # This is the main Apache server configuration file.  It contains the
 # configuration directives that give the server its instructions.
This kind of solution is not necessarily ideal, it's a good and simple way to get history tracking on your config files right now until you have the time, energy and need to improve the way you do config management.

Capistrano might just help you with deployment and other common, remote access tasks.

Further reading:

Capistrano homepage
Capistrano cheat-sheet
A similar idea presented here (download config files and put them in revision control) but for network gear.
Coverage for this was suggested by Jon Heise, who helpfully provided me with a intro to Capistrano. <3


Joe Topjian said...

How does Capistrano stack up against Puppet? I'm used to using Make and ssh to remotely deploy configs so Capistrano might seem more natural to me.

Lester Cheung said...

Seems puppet is more suited for config management - as it uses its own transport and can trigger service restart on the clients.