[Django]-How to run nginx, gevent, virtualenv and django

4👍

It can be complicated for someone like me who never tried nginx, gunicorn and gevent. I’m using Debian Squeeze and prefer pip in virtualenv over deb packages because some packages in stable are obsolete but of course they are simply stable. Sometimes debs can help with managing (vide gunicorn solution). I don’t want chroot, but it’s easy – I just believe it’s unnecessary for me.

First of all you must create an user called webd for better isolating. I assume that webd group exists. You can check its existence with:

root$ cat /etc/group | cut -d: -f1 | grep -E '^webd'

This prints webd if group exists. If not, replace -g webd with -U in the below command. Read the useradd --help first.

root$ useradd --home-dir /var/web --create-home --shell /bin/bash -g webd webd

Install pip and virtualenv from official repo.

root$ aptitude install python-pip python-virtualenv

As virtualenv helper you can use virtualenvwrapper, but its deb version is very old, so I prefer to use pip instead:

root$ pip install virtualenvwrapper

Add following line to end of the /var/web/.bashrc:

source /usr/local/bin/virtualenvwrapper.sh

Let’s prepare virtualenv for production with gevent and django 1.5c1. You may need to install some dev packages from debian repo to pip some of the below – if you have problems please tell me about.

root$ su - webd
webd$ mkvirtualenv --no-site-packages production
(production)webd$ pip install -U distribute
(production)webd$ pip install gunicorn
(production)webd$ pip install gevent
(production)webd$ pip install https://www.djangoproject.com/download/1.5c1/tarball/
(production)webd$ pip install django-gevent-deploy
(production)webd$ cd .virtualenvs/production/bin/
(production)webd$ ln -s django-admin.py django-admin
(production)webd$ exit
root$ su - webd
webd$ workon production
(production)webd$ python
Python 2.6.6 (r266:84292, Dec 27 2010, 00:02:40)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information
>>> import django
>>> django.VERSION
(1, 5, 0, 'rc', 1)

Additionally get the pytz and postgres support:

(production)webd$ pip install pytz
(production)webd$ pip install psycopg2
(production)webd$ pip install gevent-psycopg2

If you want leave production venv use deactivate. If you want activate it use workon production. Let’s create django lifespace (I prefer single master django project):

(production)webd$ django-admin startproject myproject
(production)webd$ mv myproject/manage.py ./
(production)webd$ chmod u+x manage.py
(production)webd$ mv myproject/myproject/* myproject/
(production)webd$ rm -rf myproject/myproject/
(production)webd$ vim manage.py

Change to something like this:

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    if not '/var/web' in sys.path:
        sys.path.append('/var/web')

    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

You’ll need to customize myproject/settings.py file propably.

I want to use latest gunicorn 0.17.2 so I installed it in production virtualenv by pip. But I want to manage gunicorn daemon like other daemons by /etc/init.d/gunicorn too, so let’s install latest possible but obsolete 0.14.5 version from backports repo.

As mentioned above you’ll need few debs – some from official repo, and some from squeeze-backports. If you don’t used backports yet, add this line to your /etc/apt/sources.list:

deb http://backports.debian.org/debian-backports squeeze-backports main

Update your sources and install gunicorn:

root$ aptitude update
root$ aptitude -t squeeze-backports install gunicorn

Gunicorn supports basics for virtualenv but there is no standard way to use dedicated gunicorn from other daemon’s start-stop script. I decided to hard code this feature in file /usr/sbin/gunicorn-debian. Locate Config.start(self) method and replace its first lines with:

def start(self):
    gunicornpath = self['gunicornpath'] if 'gunicornpath' in self else '/usr/bin'

    daemon = {
        'wsgi': gunicornpath + '/gunicorn',
        'django': gunicornpath + '/gunicorn_django',
        'paster': gunicornpath + '/gunicorn_paster',
    }[self['mode']]

Let’s configure gunicorn server. Create /etc/gunicorn.d/web.py file and put (I don’t tryed django mode yet):

CONFIG = {
    'mode': 'wsgi',
    'user': 'webd',
    'group': 'webd',
    'working_dir': '/var/web',
    'python': '/var/web/.virtualenvs/production/bin/python',
    'pythonpath': '/var/web/.virtualenvs/production/lib/python2.6',
    'gunicornpath': '/var/web/.virtualenvs/production/bin',
    'args': (
        '--bind=127.0.0.1:9090',
        '--workers=4',
        '--worker-class=egg:gunicorn#gevent',
        '--daemon',
        'myproject.wsgi:application',
    ),
}

Next install nginx that’s newer version come from official repo (1.2.6-1~dotdeb.0 instead of 1.2.1-2.2~bpo60+1):

root$ aptitude install nginx

Create and edit /etc/nginx/sites-available/myproject:

server {
    listen *:80;

    server_name mydomain.com;

    root /var/web/;

    location / {
        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_connect_timeout 10;
        proxy_read_timeout 10;
        proxy_pass http://localhost:9090/;
    }

    error_page 500 502 503 504 /err50x.html;
}

Please create /var/web/err50x.html too. Remember that in real server you must setting up static url and some other details. Add below as first line in /etc/nginx/nginx.conf:

user webd webd

And chmod for log directories:

root$ chown -R webd:webd /var/log/gunicorn/ && chmod g+s /var/log/gunicorn/
root$ chown -R webd:webd /var/log/nginx/ && chmod g+s /var/log/nginx/

Apply changes and start up your machinery:

root$ cd /etc/nginx/sites-enabled/
root$ ln -s ../sites-available/myproject 001-myproject
root$ /etc/init.d/gunicorn restart
root$ /etc/init.d/nginx restart

Use this for django’s project shell:

(production)webd$ ~/manage.py shell

To create a new app use:

(production)webd$ cd ~/myproject/
(production)webd$ ~/manage.py startapp myapp

Did you have any problems? What things can be done better?

👤kbec

1👍

uWSGI supports gevent from version 0.9.9, you can follow the tutorial adapting it for the gevent plugin:

http://projects.unbit.it/uwsgi/wiki/Gevent

Leave a comment