AV-Looks
Watching and Wondering

Managing dispatch.fcgi processes

Posted by Andrew Thu, 15 Jun 2006 23:55:00 GMT

Rails committer Jamis Buck recently explained in this post why it is important not to use static FastCGI processes on shared webservers. AVLUX took Jamis’ helpful guidance and used it as a starting point to enable all its Rails hosting customers to manage their own FastCGI processes

At the end of this post Jamis writes:

“Note, however, that a reboot of the machine hosting your account will require you to run the spawn scripts for your applications again… I’m sure there’s a handy solution for that floating around somewhere, but I don’t know it offhand.”

At AVLUX this is how we facilitate our Rails developers managing their own dispatch.fcgi processes, and ensuring that they all run at startup:

1) Create a startup script for FastCGI in /etc/rc.d/rc.fcgi
#!/bin/sh

curruser=$1
currdir=`pwd`
homeuser=`stat -c %U $currdir`
domdir=/var/www/vhosts

case $2 in
  -d)
      env=development
      specific=true
      ;;
  -t)
      env=test
      specific=true
      ;;
  -p)
      env=production
      ;;
   *)
      env=production
      ;;
esac

if [ "$curruser" = "root" ]; then
  env=production
  specific=false
fi

# Read through all domains
ls -1 $domdir | while read domain
do
  # Check to for Ruby on Rails domains
  if [ -d $domdir/$domain/rails/public ]; then
    cd $domdir/$domain/rails
    # Change to fully resolved directory (no symlinks) to keep Lighttpd happy
    railsdir=`pwd -P`
    cd $railsdir
    # Get owner of current directory, in order to start fcgi processes as that user
    owner=`stat -c %U .`
    if [ "$curruser" = "root" -o "$curruser" = "$owner" ]; then
      # Ensure that ownership is set correctly
      echo Updating owner on $domain/rails dir contents to $owner
      echo Rails dir is $railsdir
      # chown -R $owner $railsdir/*
      echo Starting dispatch.fcgi in processes in RAILS_ENV $env for $domain as user $owner...
      # Create tmp/sockets dirs if they don't exist
      if [ ! -d $railsdir/tmp ]; then
        su $owner -c "mkdir tmp" 
      fi
      if [ ! -d $railsdir/tmp/sockets ]; then
        su $owner -c "mkdir tmp/sockets" 
      fi
      sockdir=$railsdir/tmp/sockets
      # Start FastCGI processes for domain
      if [ "$curruser" = "$owner" ]; then
        RAILS_ENV=$env spawn-fcgi -f $railsdir/public/dispatch.fcgi \
-s $sockdir/$domain-fcgi-0.socket -P $sockdir/$domain-fcgi-0.pid
        RAILS_ENV=$env spawn-fcgi -f $railsdir/public/dispatch.fcgi \
-s $sockdir/$domain-fcgi-1.socket -P $sockdir/$domain-fcgi-1.pid
      else
        su $owner -c "RAILS_ENV=$env spawn-fcgi -f $railsdir/public/dispatch.fcgi \
-s $sockdir/$domain-fcgi-0.socket -P $sockdir/$domain-fcgi-0.pid" 
        su $owner -c "RAILS_ENV=$env spawn-fcgi -f $railsdir/public/dispatch.fcgi \
-s $sockdir/$domain-fcgi-1.socket -P $sockdir/$domain-fcgi-1.pid" 
      fi
      echo ------------------------------------------------------
      sleep 3

      # Now check to for Ruby on Rails subdomains
      ls -1 $domdir/$domain/subdomains | while read sub
      do
        if [ -d $domdir/$domain/subdomains/$sub/rails/public ]; then
          cd $domdir/$domain/subdomains/$sub/rails
          # Change to fully resolved directory (no symlinks) to keep Lighttpd happy
          railsdir=`pwd -P`
          cd $railsdir
          # Get owner of current directory, in order to start fcgi processes as that user
          owner=`stat -c %U .`
          # Ensure that ownership is set correctly
          echo Updating owner on $sub.$domain/rails contents to $owner
          echo Rails subdir is $railsdir
          # chown -R $owner $railsdir/*
          echo Starting dispatch.fcgi processes in RAILS_ENV $env for $sub.$domain as user $owner...
          # Create tmp/sockets dirs if they don't exist
          if [ ! -d $railsdir/tmp ]; then
            su $owner -c "mkdir tmp" 
          fi
          if [ ! -d $railsdir/tmp/sockets ]; then
            su $owner -c "mkdir tmp/sockets" 
          fi
          sockdir=$railsdir/tmp/sockets
          # Start FastCGI processes for subdomain
          if [ "$curruser" = "$owner" ]; then
            RAILS_ENV=$env spawn-fcgi -f $railsdir/public/dispatch.fcgi \
-s $sockdir/$sub.$domain-fcgi-0.socket -P $sockdir/$sub.$domain-fcgi-0.pid
            RAILS_ENV=$env spawn-fcgi -f $railsdir/public/dispatch.fcgi \
-s $sockdir/$sub.$domain-fcgi-1.socket -P $sockdir/$sub.$domain-fcgi-1.pid
          else
            su $owner -c "RAILS_ENV=$env spawn-fcgi -f $railsdir/public/dispatch.fcgi \
-s $sockdir/$sub.$domain-fcgi-0.socket -P $sockdir/$sub.$domain-fcgi-0.pid" 
            su $owner -c "RAILS_ENV=$env spawn-fcgi -f $railsdir/public/dispatch.fcgi \
-s $sockdir/$sub.$domain-fcgi-1.socket -P $sockdir/$sub.$domain-fcgi-1.pid" 
          fi
          echo ------------------------------------------------------
          sleep 3
        fi
      done
      # Start Lighttpd server by user
      /usr/local/bin/lighttpd_server start $owner
    fi
  fi
done
2) Set up FastCGI to run as a service, by creating /etc/init.d/fcgi
#!/bin/bash
#. /etc/rc.d/init.d/functions
PATH=/sbin://bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin:/usr/local/bin
prog=dispatch.fcgi
proc=dispatch.fcgi
startup=/etc/rc.d/rc.fcgi
startarg=$2
owner=`whoami`
currdir=`pwd`

start() {
      if [ "$owner" = "root" ]; then
        echo "Attempting to start $prog for all users..." 
        bash $startup $owner; echo -e "OK";
      else
        if [ "$startarg" = ""  -o "$startarg" = "-d" -o "$startarg" = "-t" ]; then
          echo "Starting $prog for user '$owner'... " 
          if `pgrep -u $owner -f $proc 1>/dev/null 2>/dev/null`; then
            bash $startup $owner $startarg; echo -e "OK";
            echo "$prog for user '$owner' is already running..." 
          else
            bash $startup $owner $startarg; echo -e "OK";
          fi
        else
          echo $"Usage: $prog {start [-d, -t] |stop|restart|status}" 
          exit 1
        fi
      fi
}
stop() {
      if [ "$owner" = "root" ]; then
        echo "Stopping $prog for all users..." 
        pkill -9 -f $proc
        echo -e "OK";
      else
        echo -n $"Stopping $prog for user '$owner'..." 
        if ! `pgrep -u $owner -f $proc 1>/dev/null 2>/dev/null`
        then
          echo "$prog for user '$owner' is not running";
        else pkill -9 -u $owner -f $proc
        echo -e "OK";
        fi
      fi
}
status() {
      if [ "$owner" = "root" ]; then
        if `pgrep -f $proc 1>/dev/null 2>/dev/null`;
        then
          echo "$proc processes running on `pgrep -d , -f $proc`";
          pgrep -f $proc -l
        else echo "$prog is not running...";
        fi
      else
        if `pgrep -u $owner -f $proc 1>/dev/null 2>/dev/null`;
        then
          echo "$proc processes for user '$owner' running on `pgrep -u $owner -d , -f $proc`";
          pgrep -u $owner -f $proc -l
        else echo "$prog for user '$owner' is not running...";
        fi
      fi
}
case "$1" in
  start)
        start
        status
        ;;
  stop)
        stop
        status
        ;;
 restart)
        stop
        start
        status
        ;;
 status)
        status
        ;;
*)
        echo $"Usage: $prog {start|stop|restart|status}" 
        exit 1
esac
3) Add this line to /etc/rc.d/rc.local
/etc/init.d/fcgi start
Of course your lighttpd.conf files must be written accordingly, e.g.
server.modules = ("mod_rewrite","mod_redirect","mod_access" 
,"mod_status" 
,"mod_fastcgi" 
,"mod_proxy" 
,"mod_accesslog" 
)
var.vhosts = "/home/httpd/vhosts/" 
var.domain = "yourdomain.com" 

server.bind = domain
server.port = 4004
server.pid-file = vhosts + domain + "/var/run/lighttpd.pid" 
server.username = "username" 
server.groupname = "groupname" 
status.status-url = "/server-status" 
server.indexfiles = ( "index.html", "dispatch.fcgi" )

server.document-root = vhosts + domain + "/rails/public" 
server.error-handler-404 = "/dispatch.fcgi" 
fastcgi.server= (".fcgi" => ( domain  =>
    ( "socket" => vhosts + domain + "/rails/tmp/sockets/" + domain + "-fcgi-0.socket" ),
    ( "socket" => vhosts + domain + "/rails/tmp/sockets/" + domain + "-fcgi-1.socket" )
  ) )

Posted in , ,  | Tags , , , ,  | no comments | no trackbacks

Comments

Trackbacks

Use the following link to trackback from your own site:
http://avlooks.net/articles/trackback/53

(leave url/email »)

   Comment Markup Help Preview comment