mongrel snippets

Jump start a Rails project with Rails Edge, Capistrano, Mongrel and Mercurial

Tagged rails, capistrano, mongrel, mercurial  Languages bash
# Create a Rails project
rails project -d sqlite3
cd project
# Delete index file
rm public/index.html
# Use Rails edge. Use  rake rails:freeze:edge TAG=rel_1-2-3 to get a specific version.
rake rails:freeze:edge
# Add Capistrano configuration file
capify .
# Add Mongrel cluster configuration file
sudo mongrel_rails cluster::configure -e production \
    --user mongrel --group mongrel \
    -c /var/www/project-xxx/current \
    -a 127.0.0.1 \
    -p 8000  \
    -N 3
# Create a Mercurial repository
hg init
# Add project to repository
hg commit -A --message "Project started"
# Push changes to a remote repository
hg push ssh://user@ip:port//var/mercurial/xxx

Cloning is done with hg clone:

hg clone ssh://user@ip:port//var/mercurial/xxx

Simple Mongrel HTTP server and custom Mongrel handler example

Tagged mongrel, http, server, handler  Languages ruby
# http://mongrel.rubyforge.org/rdoc/index.html
# gem install -y mongrel
require 'rubygems'
require 'mongrel'

# Usage: ruby mongrel_http_server.rb <host> <port> <docroot>
host    = ARGV[0] || "127.0.0.1"
port    = ARGV[1] || 80
docroot = ARGV[2] || "html/"

# Simple Mongrel handler that prints the current date and time
class HandlerExample < Mongrel::HttpHandler
   def process(request, response)
      response.start(200) do |head, out|
         head["Content-Type"] = "text/html"
         out.write Time.now
      end
   end
end

# Configure Mongrel and handlers
config = Mongrel::Configurator.new :host => host, :port => port do
  listener do
    uri "/",              :handler => Mongrel::DirHandler.new(docroot)
    uri "/handler_example", :handler => HandlerExample.new, :in_front => true
  end

  # CTRL+C to stop server
  trap("INT") { stop }
  run
end

# Start Mongrel
puts "Mongrel listening on '#{host}:#{port}', serving documents from '#{docroot}'."
config.join

Fixing "warning: already initialized constant OPTIONS"

Tagged rails, edge, mongrel, mephisto  Languages bash

If you're using Edge Rails and mongrel you might have seen this error:

warning: already initialized constant OPTIONS

There's a bug report here that explains this problem in detail.

The solution for me was to rerun the freeze command:

rake rails:freeze:edge

You might also get "warning: already initialized constant OPTIONS" error if you haven't installed a gem your software relies on. In the following example openssl should be installed:

/usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in gem_original_require’: no such file to load—openssl (MissingSourceFile)

ActiveResource might also be the problem:

gem install activeresource --source http://gems.rubyonrails.org

Error when installing Mongrel from gem

Tagged ruby, gem, mongrel, mkmf (loaderror)  Languages bash

I received the following error when installing Mongrel from the gem repository:

marko@x61s:$ sudo gem install mongrel
Updating metadata for 281 gems from http://gems.rubyforge.org
complete
Building native extensions.  This could take a while...
ERROR:  Error installing mongrel:
    ERROR: Failed to build gem native extension.

/usr/bin/ruby1.8 extconf.rb install mongrel
extconf.rb:1:in require': no such file to load -- mkmf (LoadError)
    from extconf.rb:1

The fix is to install the ruby development package:

sudo apt-get install ruby1.8-dev

Installing Rails, mongrel and mongrel_cluster on Debian

Tagged rails, ruby, debian, install, sqlite3, mongrel, mongrel_cluster  Languages bash

DRAFT...

Install RubyGems

http://rubyforge.org/frs/download.php/29548/rubygems-1.0.1.tgz

tar zxvf rubygems-1.0.1.tgz

cd rubygems-1.0.1

ruby setup.rb

Install Rails

gem install rails

Install sqlite3 (optional)

apt-get install sqlite3 libsqlite3-dev
gem install sqlite3-ruby

Install mongrel and mongrel_cluster

$ gem install mongrel mongrel_cluster

$ mongrel_rails cluster::configure -e production \
  -p 8000 \
  -a 127.0.0.1 \
  -N 3 \
  -c /var/www/xyz/current


$ mongrel_rails cluster::start


$ useradd -g www-data -d /var/www mongrel

Surviving reboots

sudo mkdir /etc/mongrel_cluster

sudo ln -s /var/www/xyz/config/mongrel_cluster.yml /etc/mongrel_cluster/xyz.yml

sudo cp /usr/local/lib/ruby/gems/1.8/gems/mongrel_cluster-1.0.5/resources/mongrel_cluster /etc/init.d/

sudo chmod +x /etc/init.d/mongrel_cluster

sudo /usr/sbin/update-rc.d -f mongrel_cluster defaults

mongrel_cluster_ctl status

Stale pids

If your mongrels crash or if you kill them, mongrel_cluster won't start your mongrels because mongrel_cluster believes the processes are still running, instead mongrel_cluster complains and does nothing:

** !!! PID file tmp/pids/mongrel.8000.pid already exists.  Mongrel could be running already.  Check your log/mongrel.8000.log for errors.
** !!! Exiting with error.  You must stop mongrel and clear the .pid before I'll attempt a start.

To fix this simply add the --clean switch to the /usr/local/lib/ruby/gems/1.8/gems/mongrel_cluster-1.0.5/resources/mongrel_cluster startup script:

mongrel_cluster_ctl start -c $CONF_DIR --clean

How to use Vlad the Deployer with git, nginx, mongrel, mongrel_cluster and Rails

Tagged vlad, deployer, deploy, capistrano, nginx, mongrel, mongrel_cluster  Languages ruby

This is a draft...

Installing Vlad the Deployer

gem install vlad

Configuring Vlad the Deployer

Add this to the end of RakeFile:

begin
  require 'rubygems'
  require 'vlad'
  Vlad.load :scm => :git
rescue LoadError => e
  puts "Unable to load Vlad #{e}."
end

Note that we're telling Vlad to use git. This snippet gives you a quick introduction on how to use git with Rails.

Creating the deployment recipe

If you're uncertain what these variables mean, have a look at the docs. This folder is also worth a look, and don't forget to take a peek at the vlad source code.

#
# General configuration
#
set :ssh_flags,             '-p 666'
set :application,           'xxx.com'
set :domain,                '127.0.01'
set :deploy_to,             '/var/www/xxx.com'
set :repository,            '/var/lib/git/repositories/xxx.com/.git/'


#
# Mongrel configuration
#
set :mongrel_clean,         true
set :mongrel_command,       'sudo mongrel_rails'
set :mongrel_group,         'www-data'
set :mongrel_port,          9000
set :mongrel_servers,       3

#set :mongrel_address,       '127.0.0.1'
#set(:mongrel_conf)          { '#{shared_path}/mongrel_cluster.conf' }
#set :mongrel_config_script, nil
#set :mongrel_environment,   'production'
#set :mongrel_log_file,      nil
#set :mongrel_pid_file,      nil
#set :mongrel_prefix,        nil
#set :mongrel_user,          'mongrel'

#
# Customize Vlad to our needs
#
namespace :vlad do
  #
  # Add an after_update hook
  #
  remote_task :update do
    Rake::Task['vlad:after_update'].invoke
  end

  #
  # The after_update hook, which is run after vlad:update
  #
  remote_task :after_update do
  # Link to shared resources, if you have them in .gitignore
  #  run "ln -s #{deploy_to}/shared/system/database.yml #{deploy_to}/current/config/database.yml"
  end

  #
  # Deploys a new version of your application
  #
  remote_task :deploy => [:update, :migrate, :start_app]
end

Setup the server

$ rake vlad:setup

This will create the necessary folders and mongrel_cluster configuration file.

Deploy the application

Now deploy the application with vlad:deploy, which is a custom rake task that we added to the deployment recipe:

$ rake vlad:deploy

Copying your SSH public key to the remote server

Vlad uses ssh for executing commands on the remotely, and rsync for copying the build to your server, which means you'll quickly grow tired of typing your password each time a command is run.

This problem is solved by copying your public SSH keys to the remote server, this snippet explains how to do exactly that.

How to use god to monitor a pack of mongrels

Tagged god, mongrel, monit, monitor, recipe, monitoring  Languages ruby

God is a monitoring framework written in Ruby that can be used for monitoring, for example, mongrel processes.

Installing god

Install god with the following command:

sudo gem install god

Configuring god

To configure god, first create a master configuration script by saving the following in /etc/god/god.rb:

# load in all god configs
God.load "/etc/god/conf/*.rb"

Now, save this configuration in /etc/god/conf/site.com.rb:

#
# Test this configuration file by executing:  
#   god -c /path_to_this_file -D
# 
require 'yaml'


#
# Change these to match your project setup
#
APPLICATION  = "xxx.com"
ROOT         = "/var/www/#{APPLICATION}" # deployment directory
RAILS_ROOT   = ROOT + '/current'         # current release directory
MONGREL_CONF = ROOT + '/shared/mongrel_cluster.conf' # mongrel_cluster.conf file

# Read in mongrel_conf
OPTIONS      = YAML.load_file(MONGREL_CONF)   # Read mongrel configuration

#
# TODO This can be simplified
#
def ports(port, servers)
  ports = []
  
  start_port = port
  end_port   = start_port + servers - 1
  
  for port in start_port..end_port do
    ports << port
  end
  
  ports
end

PORTS        = ports(OPTIONS['port'].to_i, OPTIONS['servers'].to_i)

#
# Returns path of mongrel pid or log file:
#
#   mongrel_path "/tmp/mongrel.pid", 9000 => "/tmp/mongrel.9000.pid"
#
def mongrel_path(file_path, port)
    file_ext = File.extname(file_path)
    file_base = File.basename(file_path, file_ext)
    file_dir = File.dirname(file_path)
    file = [file_base, port].join(".") +  file_ext
    
    File.join(file_dir, file)
end

#
# Returns the mongrel_rails start, stop or restart command depending on command parameter
#
def mongrel_rails(command, port)
  raise "Unsupported command '#{command}'" if !['start', 'stop', 'restart'].include?(command)

  argv = [ "mongrel_rails" ]
  argv << command
  argv << "-d" if command != 'stop'
  argv << "-e #{OPTIONS['environment']}" if OPTIONS['environment'] && command != 'stop'
  argv << "-a #{OPTIONS['address']}"  if OPTIONS['address'] && command != 'stop'
  argv << "-c #{OPTIONS['cwd']}" if OPTIONS['cwd']
  argv << "-f #{OPTIONS['force']}" if OPTIONS['force'] && command == 'stop'
  argv << "-o #{OPTIONS['timeout']}" if OPTIONS['timeout'] && command != 'stop'
  argv << "-t #{OPTIONS['throttle']}" if OPTIONS['throttle'] && command != 'stop'
  argv << "-m #{OPTIONS['mime_map']}" if OPTIONS['mime_map'] && command != 'stop'
  argv << "-r #{OPTIONS['docroot']}" if OPTIONS['docroot'] && command != 'stop'
  argv << "-n #{OPTIONS['num_procs']}" if OPTIONS['num_procs'] && command != 'stop'
  argv << "-B" if OPTIONS['debug'] && command != 'stop'
  argv << "-S #{OPTIONS['config_script']}" if OPTIONS['config_script'] && command != 'stop'
  argv << "--user #{OPTIONS['user']}" if OPTIONS['user'] && command != 'stop'
  argv << "--group #{OPTIONS['group']}" if OPTIONS['group'] && command != 'stop'
  argv << "--prefix #{OPTIONS['prefix']}" if OPTIONS['prefix'] && command != 'stop'
  argv << "-p #{port}" if command != 'stop'
  argv << '-P ' + mongrel_path(OPTIONS['pid_file'], port)
  argv << '-l ' + mongrel_path(OPTIONS['log_file'], port) if command != 'stop'

  cmd = argv.join " "

  return cmd
end

PORTS.each do |port|
  God.watch do |w|
    w.name          = "#{APPLICATION}-#{port}"
    w.group         = "mongrels"
    w.interval      = 30.seconds
    w.start         = mongrel_rails('start', port)
    w.stop          = mongrel_rails('stop', port)
    w.restart       = mongrel_rails('restart', port)
    w.start_grace   = 10.seconds
    w.restart_grace = 10.seconds
    w.pid_file      = File.join(RAILS_ROOT, "/tmp/pids/mongrel.#{port}.pid")
        
    w.behavior(:clean_pid_file)

    w.start_if do |start|
      start.condition(:process_running) do |c|
        c.interval = 5.seconds
        c.running  = false
      end
    end
    
    w.restart_if do |restart|
      restart.condition(:memory_usage) do |c|
        c.above = 150.megabytes
        c.times = [3, 5] # 3 out of 5 intervals
      end
    
      restart.condition(:cpu_usage) do |c|
        c.above = 50.percent
        c.times = 5
      end
    end
    
    # lifecycle
    w.lifecycle do |on|
      on.condition(:flapping) do |c|
        c.to_state     = [:start, :restart]
        c.times        = 5
        c.within       = 5.minute
        c.transition   = :unmonitored
        c.retry_in     = 10.minutes
        c.retry_times  = 5
        c.retry_within = 2.hours
      end
    end
  end
end

Add a script for each site you want to monitor.

Starting god

To start god execute:

god -c /etc/god/god.rb

For a list of available commands run god with the help switch:

$ god --help
  Usage:
    Starting:
      god [-c <config file>] [-p <port> | -b] [-P <file>] [-l <file>] [-D]
      
    Querying:
      god <command> <argument> [-p <port>]
      god <command> [-p <port>]
      god -v
      god -V (must be run as root to be accurate on Linux)
      
    Commands:
      start <task or group name>         start task or group
      restart <task or group name>       restart task or group
      stop <task or group name>          stop task or group
      monitor <task or group name>       monitor task or group
      unmonitor <task or group name>     unmonitor task or group
      remove <task or group name>        remove task or group from god
      load <file>                        load a config into a running god
      log <task name>                    show realtime log for given task
      status                             show status of each task
      quit                               stop god
      terminate                          stop god and all tasks
      check                              run self diagnostic
      
    Options:
    -c, --config-file CONFIG         Configuration file
    -p, --port PORT                  Communications port (default 17165)
    -b, --auto-bind                  Auto-bind to an unused port number
    -P, --pid FILE                   Where to write the PID file
    -l, --log FILE                   Where to write the log file
    -D, --no-daemonize               Don't daemonize
    -v, --version                    Print the version number and exit
    -V                               Print extended version and build information
        --log-level LEVEL            Log level [debug|info|warn|error|fatal]
        --no-syslog                  Disable output to syslog
        --attach PID                 Quit god when the attached process dies
        --no-events                  Disable the event system
        --bleakhouse                 Enable bleakhouse profiling

Surviving reboots

Save the following in /etc/init.d/god:

#!/bin/bash
#
# God
#

RETVAL=0

case "$1" in
    start)
      god -c /etc/god/god.rb -P /var/run/god.pid -l /var/log/god.log
      RETVAL=$?
      echo "God started"
  ;;
    stop)
      kill cat /var/run/god.pid
      RETVAL=$?
      echo "God stopped"
  ;;
    restart)
      kill cat /var/run/god.pid
      god -c /etc/god/god.rb -P /var/run/god.pid -l /var/log/god.log
      RETVAL=$?
      echo "God restarted"
  ;;
    status)
      RETVAL=$?
  ;;
    *)
      echo "Usage: god {start|stop|restart|status}"
      exit 1
  ;;
esac

exit $RETVAL

Make the file executable with chmod:

chmod +x /etc/init.d/god

Tell Debian to run the script at startup:

sudo /usr/sbin/update-rc.d -f god defaults

Rails+Mongrel+Apache 2 on Mac OSX Leopard

Tagged apache, mongrel, osx, rails  Languages apacheconf

I use this configuration on my development machine when I need mod_rewrite; it's not meant for production:

<VirtualHost *:80>
ServerName dev.xxx.com

# Enable URL rewriting
RewriteEngine On

# Rewrite index to check for static pages
RewriteRule ^/$ /index.html [QSA]

# Rewrite to check for Rails cached page
RewriteRule ^([^.]+)$ $1.html [QSA]

# Redirect all non-static requests to cluster
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L]

DocumentRoot  "/Users/christian/Documents/Projects/xxx/public"
<Directory "/Users/christian/Documents/Projects/xxx/public">
     Options Indexes FollowSymLinks

     AllowOverride None
     Order allow,deny
     Allow from all
 </Directory>

</VirtualHost>

<Proxy balancer://mongrel_cluster>
  BalancerMember http://127.0.0.1:3000
</Proxy>