Archives For May

Deployment is a hard problem. A working environment might rely on a bunch of configuration to be in sync across init scripts, conf files, install paths, environment variables, all in different types of codebases involving ruby, C, java and all with their own breed of dependency management. Imagine getting your database yaml in sync with your sphinx.conf and your nginx virtual host configuration pointing to your ports set up in mongrel or thin, and everything should be under something like monit which is checking ports and pid files in case something blows chunks along the way, and not to mention making sure all your logs go somewhere sane and configured to use syslog or logrotate. And trying to remember common setup tasks for the system itself, like setting up the database, setting grants, adding user(s), even building and installing packages from scratch. My brain can’t handle all that.

So I setup a project, called capitate which provides 3 things (and you can take or leave any of them at will): Plugins, Recipes and Templates.

Plugins

Capistrano has a plugin architecture. For example, the capitate prompt plugin gives you a beefier password prompt:

This will prompt for a password, ask you to re-type to verify, up to 3 times and will not be lazy initialized.

set :db_pass, prompt.password("DB password: ", :verify => true,
  :max_attempts => 3, :lazy => false)

This one prompts for a password and verifies it against an md5 hash and prompt happens when variable is first accessed.

set :db_pass, prompt.password("DB password: ", :verify => false,
  :check_hash => "3e5e1c82fbed35282cf53608b80503a7")

More plugins are at: lib/capitate/plugins

Adding plugins into capistrano is done by Capistrano.plugin [namespace] [module]. For example,

module Capitate::Plugins::Prompt
  def password(label, options = {})
    ...
  end
end
Capistrano.plugin :prompt, Capitate::Plugins::Prompt

Templates

Most of the configuration files in capitate are erb templates, which allows you to dry up and share your configuration across templates. Here are some examples of templates we use:

Your templates maybe vary, but you get the idea.

Recipes

Recipes combine your templates, configuration and plugins into something more useful.

set :application, "capitate"

# Merb config
set :merb_nodes, 5
set :merb_port, 9000
set :merb_pid_dir, "#{shared_path}/pids"
set :merb_pid_path, "#{fetch(:merb_pid_dir)}/merb.pid"

# Nginx config
set :nginx_upstream_size, fetch(:merb_nodes)
set :nginx_upstream_port, fetch(:merb_port)
set :nginx_pid_path, "/var/run/nginx.pid"

task :setup do
  merb.centos.setup
  merb.monit.setup
  nginx.host.setup
end

The setup task would generate /etc/init.d/merb_capitate init script, /etc/monit/merb_capitate.monitrc, and /etc/nginx/vhosts/capitate.conf files all with matching ports and pid files.

To use it

Throw this in your Capfile

require 'capitate'
require 'capitate/recipes'
set :project_root, File.dirname(__FILE__)

Documenting recipes

Part of creating recipes is documenting them, you can view capitate’s recipe documentation at:
http://capitate.rubyforge.org/recipes/index.html

Contribute

Capitate is located at github.

Deployment and configuration varies a lot, so your mileage will vary, but capitate can be a resource for storing all different types of recipes and templates, so feel free to fork and pull request. Forking is the new friending.