Django FastCGI init.d script for Linux

One of the common deployments are FastCGI servers with a reverse proxy redirecting the requests to the appropriate server. This proxy can be Apache, Lighttpd, Nginx,...

The standard Django documentation give a simple way to start and stop the FastCGI servers, but sometimes more flexibility and integration with the system is needed. Here, an alternative script is presented.

If you are using Gentoo you will need Django FastCGI init.d script for Gentoo Linux

The initialisation script

Then init.d script will be called fastcgi, an admittedly unimaginative name. The main requirement is that it should integrate without problems with the init.d scripts, and that it should allow to transparently and uniformly start, stop and restart FastCGI processes for several Django sites with minimum configuration.

#! /bin/sh
### BEGIN INIT INFO
# Provides:          FastCGI servers for Django
# Required-Start:    networking
# Required-Stop:     networking
# Default-Start:     2 3 4 5
# Default-Stop:      S 0 1 6
# Short-Description: Start FastCGI servers with Django.
# Description:       Django, in order to operate with FastCGI, must be started
#                    in a very specific way with manage.py. This must be done
#                    for each DJango web server that has to run.
### END INIT INFO
#
# Author:  Guillermo Fernandez Castellanos
#          <guillermo.fernandez.castellanos AT gmail.com>.
#
# Version: @(#)fastcgi 0.1 11-Jan-2007 guillermo.fernandez.castellanos AT gmail.com
#

#### SERVER SPECIFIC CONFIGURATION
DJANGO_SITES="site1 site2 site3"
SITES_PATH=/path/to/sites
RUNFILES_PATH=$SITES_PATH/run
HOST=127.0.0.1
PORT_START=3000
RUN_AS=www-data
FCGI_METHOD=threaded
#### DO NOT CHANGE ANYTHING AFTER THIS LINE!

set -e

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="FastCGI servers"
NAME=$0
SCRIPTNAME=/etc/init.d/$NAME

#
#       Function that starts the daemon/service.
#
d_start()
{
    # Starting all Django FastCGI processes
    PORT=$PORT_START
    for SITE in $DJANGO_SITES
    do
        echo -n ", $SITE"
        if [ -f $RUNFILES_PATH/$SITE.pid ]; then
            echo -n " already running"
        else
            start-stop-daemon --start --quiet \
                       --pidfile $RUNFILES_PATH/$SITE.pid \
                       --chuid $RUN_AS --exec /usr/bin/env -- python \
                       $SITES_PATH/$SITE/manage.py runfcgi \
                       method=$FCGI_METHOD \
                       host=$HOST port=$PORT pidfile=$RUNFILES_PATH/$SITE.pid
            chmod 400 $RUNFILES_PATH/$SITE.pid
        fi
        let "PORT = $PORT + 1"
    done
}

#
#       Function that stops the daemon/service.
#
d_stop() {
    # Killing all Django FastCGI processes running
    for SITE in $DJANGO_SITES
    do
        echo -n ", $SITE"
        start-stop-daemon --stop --quiet --pidfile $RUNFILES_PATH/$SITE.pid \
                          || echo -n " not running"
        if [ -f $RUNFILES_PATH/$SITE.pid ]; then
           rm -f $RUNFILES_PATH/$SITE.pid
        fi
    done
}

ACTION="$1"
case "$ACTION" in
    start)
        echo -n "Starting $DESC: $NAME"
        d_start
        echo "."
        ;;

    stop)
        echo -n "Stopping $DESC: $NAME"
        d_stop
        echo "."
        ;;

    restart|force-reload)
        echo -n "Restarting $DESC: $NAME"
        d_stop
        sleep 1
        d_start
        echo "."
        ;;

    *)
        echo "Usage: $NAME {start|stop|restart|force-reload}" >&2
        exit 3
        ;;
esac

exit 0

The variables of the script are:

  • SITES_PATH: The folder where all the sites are stored. The folder structure i supposed to be a common folder ($SITES_PATH, in our case) where each site has a known folder ($SITES_PATH/site1/, $SITES_PATH/site2/, $SITES_PATH/site3/) where all configuration files are stored (manage.py, settings.py,...).
  • DJANGO_SITES: The names of the folders where the different sites are.
  • RUNFILES_PATH: The path to the folder where .pid and .socket files (if using Unix sockets) will be stored.
  • HOST: The IP address where we want the server to run.
  • PORT_START: Each server will be running in a different port. server1 will run on PORT_START, server2 on PORT_START+1, server3 on PORT_START+2,etc.
  • RUN_AS: We want the FastCGI servers to run under a different user than root. RUN_AS is this user. You can put the username or the UID of the user.

To automatically start the service at start-up, configure the initial variables, put the fastcgi file in the /etc/init.d/ directory and execute in a terminal:

update-rc.d fastcgi defaults

With UNIX sockets

This script uses TCP sockets for communication, but it is really simple to modify it in order to use Unix domain sockets:

  • The sockets will be stored in the same folder as the .pid files, and will have the same name but with the .socket extension.
  • Modify the init script so it uses runfcgi socket=$RUNFILES_PATH/$SITE.socket pidfile=$RUNFILES_PATH/$SITE.pid instead of runfcgi host=$HOST port=$PORT pidfile=$RUNFILES_PATH/$SITE.pid.
  • Remove the HOST and PORT variables from the fastcgi file.

Some comments

Make sure to change the ownership of the folders where the FastCGI server must write. This is important for the folder where the .pid files are written.

For Debian, some people reported to use those options:

start-stop-daemon --start --exec /usr/bin/python $SITES_PATH/$SITE/manage.py runfcgi method=threaded host=$HOST port=$PORT pidfile=$RUNFILES_PATH/$SITE.pid --pidfile $RUNFILES_PATH/$SITE.pid

For Debian with sockets, this worked:

start-stop-daemon --start --pidfile $RUNFILES_PATH/$SITE.pid --startas /usr/bin/python $SITES_PATH/$SITE/manage.py runfcgi method=threaded socket=$RUNFILES_PATH/$SITE.socket pidfile=$RUNFILES_PATH/$SITE.pid

The script could be improved:

  • It does not have a finely grained site management. All sites are treated alike.
  • The port management is done a bit ad-hoc.
  • The FastCGI server does not work under root, but still has some flaws. For example, the .pid files can be read by www-data and can not be written in /var/run/. This is due to the fact that Django's manage.py does not seem to have an option to downscale to a lower-permission user as www-data.
  • The script is known to be working in Debian and Ubuntu based systems, but will not work on Gentoo due to it's particular init.d system.
  • It should work at is in other RedHat based systems (Mandriva, Suse,...), although this has not been confirmed. (Note: At least on RedHat EL4, you must source /etc/rc.d/init.d/functions and change start-stop-daemon to daemon and adjust accordingly.

Some More Comments

The script above uses "let" which appears to be a ksh comment. If your rot user is not setup for ksh, the script might not work.

Replace:

let "PORT = $PORT + 1"

With:

PORT=`expr $PORT + 1`

OR:

PORT=$(($PORT + 1))

Some improvements

To make the script a bit less random, so it is easier to connect to the web-server use named sockets instead. Replace:

host=$HOST port=$PORT 

With:

socket=$SITES_PATH/$SITE/$SITE.sock

This will obvious not work if the django site(s) and the web server are on different machines.

See also: Simple Django FastCGI control script

Last modified 12 years ago Last modified on Jul 5, 2012, 3:07:56 AM

Attachments (1)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.
Back to Top