分享

Django on uWSGI and Nginx

 python_diango 2012-10-10

I recently moved Pegasus News from Perlbal, Lighttpd, and Apache to Nginx and uWSGI. We balance the traffic between 3 physical servers, and the systems were struggling under the load even after weeks of Apache conf tweaking. We began having issues with excessively slow page loads, request timeouts, and intermittent errors with OGR transformations.

I decided to move us to a lighter application server so that we could get the most out of our system resources, and after a lot of research and testing I chose uWSGI. While I was at it, I decided to replace Perlbal and Lighttpd with Nginx because of its great configuration syntax and excellent performance. I also upgraded us to Ubuntu 10.04 and Postgres 8.4.

The result was a resounding success! Memory usage and CPU load on each of the web nodes dropped dramatically. Swap usage dropped to almost nothing. The overall responsiveness of the site improved noticeably, and the timeout errors and OGR failures disappeared entirely.

If you'd like to give this stack a try, read on for an overview of the setup on Ubuntu 10.04. I'm using the standard Ubuntu source package for Nginx, but modifying it slightly and them installing it withdpkg-buildpackage.

This post is just about the setup relevant to Nginx and uWSGI. If you need a more complete server setup guide, try my Provisioning a new server post.

uWSGI

Before we build Nginx, uWSGI needs to be compiled so that its module can be included in the Nginx build.

$ sudo apt-get install build-essential python-dev libxml2-dev
$ wget http://projects./downloads/uwsgi-0.9.5.4.tar.gz
$ tar -xzf uwsgi-0.9.5.4.tar.gz
$ cd uwsgi-0.9.5.4
$ make -f Makefile.Py26

Copy the executable to the local sbin directory.

$ sudo cp uwsgi /usr/local/sbin

Also, copy the default uwsgi settings to the /etc/nginx directory.

$ sudo mkdir /etc/nginx
$ sudo cp nginx/uwsgi_params /etc/nginx
$ cd ..

Nginx

We need to slightly modify the nginx package from Ubuntu to add uWSGI (and, optionally, SSL).

$ sudo apt-get install libssl-dev
$ sudo apt-get build-dep nginx
$ apt-get source nginx
$ cd nginx-0.7*
$ emacs debian/rules

Add the following lines to the end of the configure options. Make sure to include backslashes so that all the options are interpreted as being on one line. If you don't need SSL, ignore that line.

    --with-http_ssl_module     --add-module=$(CURDIR)/../uwsgi-0.9.5.4/nginx

Build, install, and hold the packages.

$ dpkg-buildpackage
$ cd ..
$ sudo dpkg -i nginx*.deb
$ echo "nginx hold" | sudo dpkg --set-selections
$ echo "nginx-dbg hold" | sudo dpkg --set-selections

Starting with nginx-0.8.41, you can add something like --http-uwsgi-temp-path=/var/lib/nginx/uwsgi to the debian/rules so that the temp files are kept in the right place. Until then (probably a later version of Ubuntu), you'll need to create a /usr/local/nginx/uwsgi_tempfolder to use for the temp files.

$ sudo mkdir -p /usr/local/nginx/uwsgi_temp

Supervisor

To manage the uWSGI processes, I use Supervisor. In Ubuntu 10.04, you can simply install it with apt-get.

$ sudo apt-get install supervisor

Configuration

Supervisor

To configure uWSGI, I use command-line options in my Supervisor config. The example below is similar to what I use in production for Pegasus, but you'll want to take a look at the uWSGI docs and tweak for your situation.

[program:myapp]
command=/usr/local/sbin/uwsgi
  --home /home/myuser/.virtualenvs/myapp/
  --module myapp.deploy.wsgi
  --socket 10.1.2.3:10000
  --pythonpath /sites/myapp.com/code/myapp
  --processes 5
  --master
  --harakiri 120
  --max-requests 5000
directory=/sites/myapp.com/code/myapp
environment=DJANGO_SETTINGS_MODULE='myapp.settings'
user=www-data
autostart=true
autorestart=true
stdout_logfile=/sites/myapp.com/logs/uwsgi.log
redirect_stderr=true
stopsignal=QUIT

The module I specify in the --module option simply contains:

import django.core.handlers.wsgi

application = django.core.handlers.wsgi.WSGIHandler()

The IP address in the --socket option is the server's IP address on the interface reserved for local network traffic. On Rackspace, eth1 is your local interface. Use the ifconfig command to find your IP address on the local interface. If you're just using uWSGI on localhost, you can use something like --socket /sites/myapp.com/var/run/myapp.sock instead to avoid the overhead of the full TCP stack.

My value for --harakiri is rather high, and my value for --max-requests is rather low. You may not even need either of these options, but I'm using them to solve some problems specific to Pegasus.

Nginx

For the site-specific Nginx config, I'm using something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
upstream myapp {
    server 10.1.2.3:10000;
    server 10.1.2.4:10000;
    server 10.1.2.5:10000;
}

server {
    listen 80;
    listen 443;
    server_name myapp.com www.myapp.com;

    access_log /sites/myapp.com/logs/nginx-access.log;
    error_log /sites/myapp.com/logs/nginx-error.log;

    root /sites/myapp.com/public;

    ssl_certificate /sites/myapp.com/ssl/myapp.crt;
    ssl_certificate_key /sites/myapp.com/ssl/myapp.key;

    location / {
        # This checks for a file called simply "downtime" in the public
        # directory, and puts up the downtime page if it exists.
        if (-f /sites/myapp.com/public/downtime) {
            return 503;
        }

        uwsgi_pass myapp;
        include uwsgi_params;
    }

    location /media {
        # This makes static media available at the /media/ url.  The
        # media will continue to be available during site downtime,
        # allowing you to use styles and images in your maintenance page.
        alias /sites/myapp.com/public/media;
    }

    error_page 502 503 504 @maintenance;
    location @maintenance {
        # In this example, there's a directory in the site media files
        # called "downtime" that contains a "maintenance.html" file and
        # any styles and images needed for the maintenance page.
        root /sites/myapp.com/public/media/downtime;
        rewrite ^(.*)$ /maintenance.html break;    
    }
}

If you're just using uWSGI on localhost, then skip the upstream section and use something likeuwsgi_pass unix:///sites/myapp.com/var/run/myapp.sock; in the root location definition instead.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多