WebFaction
Community site: login faq
1
6

What are the steps to convert a Django application from Apache/mod_wsgi to Gunicorn?

Is it possible to test gunicorn without taking my sites offline?

What are the Pros and Cons?

asked 29 Mar '11, 06:27

johns ♦♦
5.2k211
accept rate: 22%

edited 29 Mar '11, 06:56


What are the steps to convert a Django application from Apache/mod_wsgi to Gunicorn?

The gunicorn_django application bundled wtih gunicorn makes this process rather easy. First we must install gunicorn.

This is best done with easy_install:

easy_install-2.6 gunicorn

The tricky part for the average gunicorn deployment is setting up a front-end webserver, we have nginx in place to manage ports already so all that is needed is to configure the gunicorn process to be executed on the server on the port assigned. I have provided a shell script that makes this process easy and provides {start | stop | restart } functions.

#!/bin/sh

# app_admin 
# Usage: ./app_admin { start | stop | restart }
# app_admin is a management script for gunicorn_django.
# It is designed to work on the WebFaction platform with minimal effort.
# The script requires gunicorn installed and enabled within your apps INSTALLED_APPS setting. 
# See http://gunicorn.org/ for instructions on gunicorn_django's use and installtion.

# Activate virtual environment. These 2 lines may be disabled/deleted if you do not use virtualenv.
WORKON_HOME=/home/<user name>/path/to/env
. $WORKON_HOME/bin/activate

# The servers IP adderss, this should be 127.0.0.1. 
ADDRESS='127.0.0.1'

# The port of your Django app. This will be located within the control panel in the applications details. 
SERVER_PORT='0000'

# Set PYTHON to '/usr/local/bin/python2.X' for a regular deployment and the path of the binary in the virtualenv if you are using one.
PYTHON="/usr/local/bin/python2.6"

# The path to gunicorn_django
GUNICORN="/home/<user name>/bin/gunicorn_django"

# The project location, settings.py, urls.py etc....
PROJECTLOC="/home/<user name>/webapps/<app name>/myproject"

# The default args for gunicorn_django see http://gunicorn.org/configure.html#contents
DEFAULT_ARGS="--workers=3 "

# Do not edit below this line
BASE_CMD="$PYTHON $PROJECTLOC/manage.py run_gunicorn $DEFAULT_ARGS"
SERVER_PID="$PROJECTLOC/$SERVER1_PORT.pid"

start_server () {
  if [ -f $1 ]; then
    if [ "$(ps -p `cat $1` | wc -l)" -gt 1 ]; then
       echo "A server is already running on ${ADDRESS}:${2}"
       return
    fi
  fi
  cd $PROJECTLOC
  echo "starting ${ADDRESS}:${2}"
  $BASE_CMD --daemon --bind=$ADDRESS:$SERVER_PORT --pid=$1
}

stop_server (){
  if [ -f $1 ] && [ "$(ps -p `cat $1` | wc -l)" -gt 1 ]; then
    echo "stopping server ${ADDRESS}:${2}"
    kill -9 `cat $1`
    rm $1
  else 
    if [ -f $1 ]; then
      echo "server ${ADDRESS}:${2} not running"
    else
      echo "No pid file found for server ${ADDRESS}:${2}"
    fi
  fi
}

case "$1" in
'start')
  start_server $SERVER_PID $SERVER_PORT 
  ;;
'stop')
  stop_server $SERVER_PID $SERVER_PORT
  ;;
'restart')
  stop_server $SERVER_PID $SERVER_PORT
  sleep 2
  start_server $SERVER_PID $SERVER_PORT
  ;;
*)
  echo "Usage: $0 { start | stop | restart }"
  ;;
esac

exit 0

The script must be configured for your applications, copy it from here into a text ediror and follow the configuration instructions in the comments. Once the script is configured you should name it [appname]_admin (replacing [appname] with the name of your app) and upload it to the server in the ~/bin/ directory and add execute permissions:

chmod +x ~/bin/[appname]_admin

If everything is set up correctly you can stop your apache server and then start the gunicorn server with this command:

[appname]_admin start

Is it possible to test gunicorn without taking my sites offline?

Yes. If you would like to test gunicorn without taking your site's apache server offline you may by creating a "Custom app listening on port" and using that port number in the provided script. Both should be able to run without effecting each other as long as you have them running on different ports. (You'll need to create a site record in the control panel with the new application mounted at / and a domain of your choice.)

What are the Pros and Cons?

Not everyone can just switch their apps to gunicorn without changing some code. Testing before switching a live site over is vital. Reading their official documentation is highly recommended.

http://gunicorn.org/install.html http://gunicorn.org/configure.html

I have ran some tests for request handling, and the gunicorn stack did outperform the apache stack.

The speed test was 'ab' and I used these parameters:

$ ab -n 10000 -c 25 http://

Apache,

Document Path:          /
Document Length:        2792 bytes

Concurrency Level:      25
Time taken for tests:   145.590 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      29220000 bytes
HTML transferred:       27920000 bytes
Requests per second:    68.69 [#/sec] (mean)
Time per request:       363.976 [ms] (mean)
Time per request:       14.559 [ms] (mean, across all concurrent requests)
Transfer rate:          196.00 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       3
Processing:    19  363 113.1    399    1890
Waiting:       19  363 113.0    399    1890
Total:         19  363 113.1    399    1890

Percentage of the requests served within a certain time (ms)
  50%    399
  66%    402
  75%    404
  80%    406
  90%    411
  95%    425
  98%    433
  99%    453
 100%   1890 (longest request)

Gunicorn,

Document Path:          /
Document Length:        2792 bytes

Concurrency Level:      25
Time taken for tests:   53.540 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      29220000 bytes
HTML transferred:       27920000 bytes
Requests per second:    186.78 [#/sec] (mean)
Time per request:       133.851 [ms] (mean)
Time per request:       5.354 [ms] (mean, across all concurrent requests)
Transfer rate:          532.97 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       4
Processing:    21  133  18.0    130     304
Waiting:       21  133  17.9    130     304
Total:         21  134  18.0    130     304

Percentage of the requests served within a certain time (ms)
  50%    130
  66%    136
  75%    141
  80%    143
  90%    152
  95%    162
  98%    181
  99%    207
 100%    304 (longest request)
permanent link

answered 29 Mar '11, 06:57

johns ♦♦
5.2k211
accept rate: 22%

edited 29 Mar '11, 17:22

I just tried this myself with a slightly different approach, and got different results.

The tl;dr version: serving a basic "hello world" view, Gunicorn saved me ~11MB of RAM, but didn't provide any improvement over Apache+mod_wsgi in terms of requests per second.

Here are the steps I took:

  1. I created a Django 1.3.1/mod_wsgi 3.3 app in the control panel, wrote a bare-bones "hello world" view and corresponding urlconf, turned DEBUG off, and added the app to a site.
  2. I measured my initial idle Apache memory usage: 18.3594MB
  3. I benchmarked the site running on Apache with "ab -n 1000 -c 25": 333.73 req/sec
  4. I measured Apache memory usage after the benchmark: 48.4219MB
  5. I stopped Apache.
  6. I installed gunicorn:
    cd ~/webapps/django
    PYTHONPATH=$PWD/lib/python2.7 easy_install-2.7 -s $PWD/bin -d $PWD/lib/python2.7 gunicorn
  7. I added gunicorn to my INSTALLED_APPS in settings.py.
  8. I started gunicorn via manage.py, giving it the port assigned to my Django app:
    cd ~/webapps/django
    python2.7 myproject/manage.py run_gunicorn 127.0.0.1:54321
    
  9. I measured my idle Gunicorn memory usage: 36.4688MB (2x what Apache used when initially idle!)
  10. I benchmarked the site running on Apache with "ab -n 1000 -c 25": 331.75 req/sec (~2 less than Apache, not really enough to complain about)
  11. I measured Gunicorn memory usage after the benchmark: 37.5703MB (~11MB less than Apache, and only ~1MB higher than initial usage. not bad!)

John, any idea why your method produced a ~300% improvement over Apache in terms of requests per second while mine did not?

(24 Nov '11, 18:56) seanf

I just tested again with 3 gunicorn workers as you did, eg python2.7 myproject/manage.py run_gunicorn 127.0.0.1:54321 --workers=3, and upped the benchmark to 10k requests. The requests per second were the same (~335), and gunicorn's final idle memory usage was 75MB, whereas Apache's final memory usage stayed around 48MB. Ouch!

Is there any advantage at all to using Gunicorn instead of mod_wsgi?

(24 Nov '11, 19:10) seanf

I just did a post about a setup like this in http://armonge.info/blog/1/django-gunicorn-y-supervisord-en-webfaction/

However it's in spanish :P

permanent link

answered 12 Jun '12, 10:23

armonge
1
accept rate: 0%

Thanks for sharing!

(12 Jun '12, 12:03) seanf
Your answer
toggle preview

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • link:[text](http://url.com/ "title")
  • image?![alt text](/path/img.jpg "title")
  • numbered list: 1. Foo 2. Bar
  • to add a line break simply add two spaces to where you would like the new line to be.
  • basic HTML tags are also supported

Question tags:

×901
×54
×12

question asked: 29 Mar '11, 06:27

question was seen: 7,143 times

last updated: 12 Jun '12, 12:03

WEBFACTION
REACH US
SUPPORT
AFFILIATE PROGRAM
LEGAL
© COPYRIGHT 2003-2016 SWARMA LIMITED - WEBFACTION IS A SERVICE OF SWARMA LIMITED
REGISTERED IN ENGLAND AND WALES 5729350 - VAT REGISTRATION NUMBER 877397162
5TH FLOOR, THE OLD VINYL FACTORY, HAYES, UB3 1HA, UNITED KINGDOM