Webpy deployment through Nginx, Fastcgi, Spawn-fcgi and Flup.


[ Note: this guide was written in 20 minutes and maybe there are some errors in it: if you have any corrections/suggestions, please let me know in comments. ]

 

This (very basic) guide will explain how to install Webpy framework with Nginx web server via Fastcgi.

Requirements:

nb1: This guide have been tested on Linode with Nginx 0.7.65, Webpy 0.33, Spawn-fcgi 1.6.3 and Flup 1.0.2 but it should work also with later versions. Please check each software's documentation for changes.

Let's suppose that your Python application is called myapp.py and that /path/to/myapplication is the path to the directory where your application is located. Let's start with Nginx configuration:

Nginx configuration

Let's suppose that you have a working Nginx installation with it's own nginx.conf. This is an extract of my conf file related to the location / of my server  mysite.com (that is where my application will respond):

location ~* \.(js|css|jpg|jpeg|gif|png|ico|html|txt)$ {
    if (-f $request_filename) {
        expires max;
        break;
    }    
}

location / {
    fastcgi_pass 127.0.0.1:8100;
    fastcgi_index myapp.py;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param CONTENT_TYPE $content_type;
    fastcgi_param CONTENT_LENGTH $content_length;
    fastcgi_param SCRIPT_NAME $fastcgi_script_name;
    fastcgi_param REQUEST_URI $request_uri;
    fastcgi_param DOCUMENT_URI $document_uri;
    fastcgi_param DOCUMENT_ROOT $document_root;
    fastcgi_param SERVER_PROTOCOL $server_protocol;
    fastcgi_param GATEWAY_INTERFACE CGI/1.1;
    fastcgi_param SERVER_SOFTWARE nginx;
    fastcgi_param REMOTE_ADDR $remote_addr;
    fastcgi_param REMOTE_PORT $remote_port;
    fastcgi_param SERVER_ADDR $server_addr;
    fastcgi_param SERVER_PORT $server_port;
    fastcgi_param SERVER_NAME $server_name;
}

The row 'fastcgi_pass 127.0.0.1:8100' tells Nginx to "pass" all the requests for my mysite.com to a local port on the same server, in this case port 8100. Note that you can use any free port number instead of 8100 but take note of it because you'll need it later. The section 'location ~* \.(js|css|jpg|jpeg|gif|png|ico|html|txt)' (that is processed first by Nginx) is needed to serve static files directly (without passing by your application). Now you can reload your Nginx to read new modifications but be aware that at this point they are still useless since you don't have anything that listens on port 8100 yet.

Spawn-fcgi configuration

This is the quick&dirty bash script (run.sh) that I use to start my application. It basically stops (kills) a previous running copy of my application and then it starts a new one. Do not run it now, wait.

#!/bin/bash
kill `cat /path/to/myapplication/spawnfcgi.pid`;
chmod +x /path/to/myapplication/myapp.py;
sleep 1
spawn-fcgi -f /path/to/myapplication/myapp.py -a 127.0.0.1 -p 8100 -P /path/to/myapplication/spawnfcgi.pid

Note that spawn-fcgi must listen to the same address and port that you used in your fastcgi_pass directive in your nginx.conf (in this case 127.0.0.1 8100).

Flup installation

Extract Flup archive into your /path/to/myapplication so that you have a dir called flup in it.

Webpy installation

Extract Webpy archive into your /path/to/myapplication so that you have a dir called web in it. Now you have to edit a file called wsgi.py (you can find it in the web directory): find a function called runfcgi and change the parameter multiplexed=True to False. It should look like this:

def runfcgi(func, addr=('localhost', 8500)):
    """Runs a WSGI function as a FastCGI server."""
    import flup.server.fcgi as flups
    return flups.WSGIServer(func, multiplexed=False, bindAddress=addr).run()

Again port 8500 can be any free port number.

myapp.py

This is the source of myapp.py located in /path/to/myapplication:

#!/usr/local/bin/python2.6
# -*- coding: utf-8 -*-

import web

urls = ('/', 'index')

app = web.application(urls, globals())

class index:
    def GET(self):
        return 'Hello, world!'

if __name__ == "__main__":
    web.wsgi.runwsgi = lambda func, addr = None: web.wsgi.runfcgi(func, addr)
    app.run()

Run it :) !

Now follow this steps:

  1. Be sure that your Nginx web server is up and running with it's changed nginx.conf.
  2. Run run.sh

Now, if you point your browser to mysite.com you should see an 'Hello, world!' message.