RkBlog

Hardware, programming and astronomy tutorials and reviews.

Django and Cherokee server

Cherokee is a very fast, flexible, and lightweight Web server. It is implemented entirely in C, and has no dependencies beyond a standard C library :grin:. It is quite popular among debian and gnome users.

Django with Cherokee and SCGI

We will configure cherokee to server django project using SCGI protocol. Cherokee will also serve static files (media files)

- Install flup module:
easy_install flup
- Download django-scgi.py from this site and place it in the project folder.
- Install cherokee if you haven't done that yet. Note that when compiling from source you can explicitly enable SCGI and FastCGI support:
./configure --enable-scgi --enable-fcgi
- In gentoo use:
USE="fastcgi scgi" emerge cherokee
- In /etc/cherokee you will find all cherokee config files. Edit /etc/cherokee/sites-enabled/default (if not present symlink default from sites-available).
- Find:
DocumentRoot /var/www/localhost/htdocs
- Change to a path to your project, for example: /home/user/djangoProject/
- Find:
UserDir public_html {
    Directory / {
       Handler common
    }
- Change to:
UserDir public_html {
    Directory / {
       Handler scgi
        {
        Server localhost:8080
        }
    }
- Where localhost:8080 is host and port of WSGI server that cherokee will try to use (django-scgi.py server).
- Find:
Directory / {
    Handler common
}
- Change to:
Directory /site_media/ {
    Handler common
}
Directory /media/ {
    Handler common
}
Where site_media is a name of our folder with static files (media folder), you may change it to reflect your media folder name. /media/ is for admin panel media files (a symlink).
- To make a symlink execute from the project folder:
ln -sf /usr/lib/python2.4/site-packages/django/contrib/admin/media media
- Check if django is in that path!
- Start WSGI server:
python django-scgi.py --projects=/home/user/projectDjango/ --settings=settings --host=localhost --port=8080
Where -projects=/home/user/projectDjango/ is a path to your django project. -settings=settings defines settings file (in urls.py you should have: ROOT_URLCONF = 'urls'), --host and --port are WSGI server host and port to use.
- Start cherokee server:
/etc/SOMETHING/cherokee start
/etc/init.d/cherokee start    (gentoo)
/etc/rc.d/cherokee start     (arch)
- Open http://localhost/ in your browser, you should see your django powered site up and running..

Cleaning cherokee

You may want to remove PHP, /cgi-bin and other config entries that aren't needed.

django-scgi.py

If you can't download it, here is the code:
import os
import sys

if os.name == 'posix':

    def become_daemon(ourHomeDir='.',outLog='/dev/null',errLog='/dev/null'):
        """
        Robustly turn us into a UNIX daemon, running in ourHomeDir.
        Modelled after the original code of this module and some
        sample code from the net.
        """

        # first fork
        try:
            if os.fork() > 0:
                sys.exit(0)     # kill off parent
        except OSError, e:
            sys.stderr.write("fork #1 failed: (%d) %s
" % (e.errno, e.strerror))
            sys.exit(1)
        os.setsid()
        os.chdir(ourHomeDir)
        os.umask(0)
        # second fork
        try:
            if os.fork() > 0:
                sys.exit(0)
        except OSError, e:
            sys.stderr.write("fork #2 failed: (%d) %s
" % (e.errno, e.strerror))
            sys.exit(1)

        si = open('/dev/null', 'r')
        so = open(outLog, 'a+', 0)
        se = open(errLog, 'a+', 0)
        os.dup2(si.fileno(), sys.stdin.fileno())
        os.dup2(so.fileno(), sys.stdout.fileno())
        os.dup2(se.fileno(), sys.stderr.fileno())

else:

    def become_daemon(ourHomeDir='.',outLog=None,errLog=None):
        """
        If we are not running under a POSIX system, just simulate
        the daemon mode by doing redirections and directory changeing
        """

        os.chdir(ourHomeDir)
        os.umask(0)
        sys.stdin.close()
        sys.stdout.close()
        sys.stderr.close()
        if errLog and outLog:
            sys.stderr=open (errLog, 'a', 0)
            sys.stdout=open (outLog, 'a', 0)
        elif errLog:
            sys.stderr=open (errLog, 'a', 0)
            sys.stdout=NullDevice ()
        elif outLog:
            sys.stdout=open (outLog, 'a', 0)
            sys.stderr=NullDevice ()
        else:
            sys.stdout = NullDevice()
            sys.stderr = NullDevice()

    class NullDevice:
        """
        A substitute for stdout and stderr that writes to nowhere.
        This is a substitute for /dev/null
        """
    
        def write(self, s):
            pass

def main():
    from flup.server.scgi_fork import WSGIServer
    from django.core.handlers.wsgi import WSGIHandler

    import getopt

    (opts, args) = getopt.getopt(sys.argv[1:], 'f:s:h:p:',
        ['settings=','socket=','host=','port=',
         'minspare=', 'maxspare=', 'maxchildren=',
         'daemon', 'etclog=', 'errorlog=', 'workdir=',
         'projects='])
    
    socket = None
    host = None
    port = None

    minspare = 1
    maxspare = 5
    maxchildren = 50

    daemon = None
    workdir = '.'
    etclog = '/dev/null'
    errorlog = '/dev/null'

    projects = []

    for (o, v) in opts:
        if o in ('-s', '--socket'):
            socket = v
        elif o in ('-h', '--host'):
            host = v
        elif o in ('-p', '--port'):
            port = int(v)
        elif o in ('-f', '--settings'):
            os.environ['DJANGO_SETTINGS_MODULE'] = v
        elif o in ('--minspare',):
            minspare = int(v)
        elif o in ('--maxspare',):
            maxspare = int(v)
        elif o in ('--maxchildren',):
            maxchildren = int(v)
        elif o in ('--daemon',):
            daemon = 1
        elif o in ('--errorlog',):
            errorlog = v
        elif o in ('--etclog',):
            etclog = v
        elif o in ('--workdir',):
            workdir = v
        elif o in ('--projects',):
            projects.append(v)
    
    # add projects to path
    for p in projects:
        if p not in sys.path:
            sys.path.insert(0, p)

    # if we should run as a daemon, use the above function to turn us
    # into one reliably. This should correctly detach from the tty.
    if daemon:
        become_daemon(ourHomeDir=workdir,
            outLog=etclog, errLog=errorlog)

    if socket and not host and not port:
        WSGIServer(WSGIHandler(), minSpare=minspare, maxSpare=maxspare, maxChildren=maxchildren, bindAddress=socket).run()
    elif not socket and host and port:
        WSGIServer(WSGIHandler(), minSpare=minspare, maxSpare=maxspare, maxChildren=maxchildren, bindAddress=(host, port)).run()
    else:
        print "usage: django-scgi.py [--settings=<settingsmodule>] --socket=<socketname>"
        print "   or: django-scgi.py [--settings=<settingsmodule>] --host==<hostname> --port=<portnumber>"
        print
        print "   additional options are:"
        print "      --minspare=<minimum number of spare processes, default 1>"
        print "      --maxspare=<maximum number of spare processes, default 5>"
        print "      --maxchildren=<maximum number of child processes, default 50>"
        print
        print "      --daemon"
        print "      --etclog=<path for stdout log, default /dev/null>"
        print "      --errorlog=<path for stderr log, default /dev/null>"
        print "      --workdir=<path for working directory, default .>"
        print
        print "      --projects=<path to add to sys.path>"

if __name__ == '__main__':

    # first patch our own version of socketpair into the sockets module
    # if we don't have it already (comes with Python 2.4)

    import socket
    if not hasattr(socket, 'socketpair'):
        import eunuchs.socketpair

        def socketpair():
            (p,c) = eunuchs.socketpair.socketpair()
            (pp,cc) = (socket.fromfd(p,1,1), socket.fromfd(c,1,1))
            os.close(c)
            os.close(p)
            return (pp,cc)

        socket.socketpair = socketpair

    main()
RkBlog

Python programming, 14 July 2008, Piotr Maliński

Comment article