Green Unicorn and Nginx on Debian

July 27, 2010

This post covers installing a configuring Green Unicorn, hosting a Django app in a virtual host behind Nginx (pronounced EngineX). We'll be doing this on Debian Lenny.

Install Nginx

The first thing we need to do is install the Nginx web server. We'll do this from lenny-backports to get a reasonable version.

Create or edit /etc/apt/preferences and add the following:

Package: nginx
Pin: release a=lenny-backports
Pin-Priority: 999

Next, install nginx package the normal way:

drumcoder:/etc/apt# apt-get install nginx
-snip-
Need to get 333kB of archives.
After this operation, 139kB of additional disk space will be used.
Get: 1 http://www.backports.org lenny-backports/main nginx 0.7.65-2~bpo50+1 [333kB]
Fetched 333kB in 1s (278kB/s) 
(Reading database ... 55758 files and directories currently installed.)
Preparing to replace nginx 0.6.32-3+lenny3 (using .../nginx_0.7.65-2~bpo50+1_i386.deb) ...
Unpacking replacement nginx ...
Processing triggers for man-db ...
Setting up nginx (0.7.65-2~bpo50+1) ...
Installing new version of config file /etc/nginx/nginx.conf ...
Installing new version of config file /etc/nginx/sites-available/default ...
Installing new version of config file /etc/init.d/nginx ...

As we're doing this on a machine that already has Apache installed, we're going to run on port 8224. Edit /etc/nginx/sites-enabled/default and change the port near the top of the file to be 8224 instead of 80:

listen   8224 default;

Bounce the server using /etc/init.d/nginx restart, and you should now be able to navigate to http://localhost:8224 in a browser and see a 404 page with nginx/0.7.65 at the bottom. This is good.

Install Gunicorn

We now need to install gunicorn using easy_install.

drumcoder:/home/drumcoder# easy_install gunicorn
Searching for gunicorn
Reading http://pypi.python.org/simple/gunicorn/
Reading http://gunicorn.org
Reading http://github.com/benoitc/gunicorn
Reading http://www.gunicorn.org
Best match: gunicorn 0.10.0
Downloading http://pypi.python.org/packages/source/g/gunicorn/gunicorn-0.10.0.tar.gz
Processing gunicorn-0.10.0.tar.gz
Running gunicorn-0.10.0/setup.py -q bdist_egg --dist-dir /tmp/easy_install-AXWc1u/gunicorn-0.10.0 /egg-dist-tmp-Q2wYeg
warning: no files found matching 'gunicorn/options.ini'
warning: no files found matching '*' under directory 'examples/pylonstest'
Adding gunicorn 0.10.0 to easy-install.pth file
Installing gunicorn_paster script to /usr/bin
Installing gunicorn script to /usr/bin
Installing gunicorn_django script to /usr/bin

Installed /usr/lib/python2.5/site-packages/gunicorn-0.10.0-py2.5.egg
Processing dependencies for gunicorn
Finished processing dependencies for gunicorn

Start Green Unicorn

To start Green Unicorn, we need to first set the PYTHONPATH. (I must look at VirtualEnv soon!). We then use the gunicorn_django executable and pass it the name of the settings file to use. We do this in the same directory as the settings file.

[drumcoder@drumcoder django_app]$ export PYTHONPATH=/home/user/django_app
[drumcoder@drumcoder django_app]$ gunicorn_django settingslive.py
2010-07-29 09:48:32 [23433] [INFO] Arbiter booted
2010-07-29 09:48:32 [23433] [INFO] Listening at: http://127.0.0.1:8000
2010-07-29 09:48:32 [23434] [INFO] Worker spawned (pid: 23434)

We have now started Green Unicorn on port 8000. For security reasons, you won't be able to access this URL from a different machine. You should, however, be able to access the django app if you run a browser on the same machine. Remember you won't have any static files (so no styling) - Green Unicorn only hosts the Django site, not the static media.

Configure nginx

We now need to configure nginx to proxy to the django app running on port 8000. We do this by changing a server block:

server {
    listen 8224;
    server_name dominname.co.uk;

    location / {
      proxy_pass  http://localhost:8000;
      proxy_set_header X-REAL-IP $remote_addr;
      proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;
    }

    location /site_media {
      root /home/drumcoder/django_app/;
    }

    location /media {
      root /home/drumcoder/django_app/django/contrib/admin/;
    }
}

Navigating to domainname.co.uk:8224 will now proxy to localhost:8000 for the django pieces, and use nginx to serve static media files at /site_media. You will now be able to access the django app from a different machine.

Django App Offset

If you want to host the django app at an offset, you can do that by rewriting the request within nginx:

location /offset {
  rewrite  ^/offset(.+)$ $1 break;
  proxy_pass  http://localhost:8000;
  proxy_redirect  http://localhost:8000/ http://domainname.co.uk:8224/offset/;
}

This ensures that any requests coming in have the offset removed (the rewrite line), and in the headers returned the urls are changed to add the offset (the proxy_redirect line).

Specifying the Green Unicorn Port

If you want Green Unicorn to listen on a different port (and you will if you want to deploy more than one Django app) then you'll need to use the -b command line option. This example listens on port 8001:

$ gunicorn_django -b localhost:8001 settingslive.py

Note that you must specify both host and port.

Changing the Process Name

To change the process name that gunicorn reports (which is again useful if you have more than one instance) you need to install py-setproctitle. This will require gcc and python-dev to be installed, so if you don't have either, install those first.

# apt-get install gcc
# apt-get install python-dev
# easy_install setproctitle

Once that is done, you can start up gunicorn and specify the name you want reported in top, ps etc.

$ gunicorn_django -b localhost:8001 -n drumcoder settingslive.py

Unfortunately the name comes out too long for top, I can only see gunicorn: worke. If you push c whilst in top, this will toggle the command paths so they are the same as ps and you'll be able to see the full name.

Output from ps is good:

12168 pts/0    S+     0:00 gunicorn: master [drumcoder]                                                           
12169 pts/0    S+     0:00 gunicorn: worker [drumcoder]

References