librelist archives

« back to archive

Hosting multiple Flask applications in a single Apache instance (on WebFaction)

Hosting multiple Flask applications in a single Apache instance (on WebFaction)

From:
Michael Fogleman
Date:
2011-09-08 @ 23:59
I was previously running virtual hosts for each Flask application. But
I figured I could save even more memory if I had a single host and
some kind of WSGI dispatcher middleware to route the requests to the
appropriate Flask app depending on the host and path. Below is what I
came up with, but I'm wondering if anyone has any comments or
suggestions. Or maybe this will be helpful to others.

I'm particularly looking for feedback on how I wrote dispatcher.py

I'm running two Flask apps currently, using just over 50 MB of the
allowed 80 MB with the base WebFaction plan. I should be able to run
one or two more apps using this setup.

All of the WebFaction domains point to the same WSGI application. The
dispatcher does the routing.



Directory Structure
===================
apache2/
dispatcher.py
dispatcher.wsgi
env/
sample1/
    static/
    templates/
    __init__.py
sample2/
    static/
    templates/
    __init__.py

env/ is a virtualenv environment (note that all apps get the same
environment with this setup)
sample1/ and sample2/ are two sample flask applications



dispatcher.py - just import your flask apps and specify a domain/path for each
=============
from flask import Flask

# === Begin Dispatcher Configuration ===
from sample1 import app as sample1
from sample2 import app as sample2

SITES = [
    # (host, path, app)
    ('www.sample1.com', '/', sample1),
    ('username.webfactional.com', '/sample2', sample2),
]
# ===  End Dispatcher Configuration  ===

class Dispatcher(object):
    def __call__(self, environ, start_response):
        host = environ['HTTP_HOST']
        path = environ['PATH_INFO']
        for _host, _script, _app in SITES:
            if host != _host:
                continue
            if not path.startswith(_script):
                continue
            _script = _script.rstrip('/')
            new_path = path[len(_script):] or '/'
            if not new_path.startswith('/'):
                continue
            environ['SCRIPT_NAME'] = _script
            environ['PATH_INFO'] = new_path
            return _app(environ, start_response)
        raise Exception('No matching site found for the request.')

app = Flask(__name__)
app.wsgi_app = Dispatcher()



dispatcher.wsgi
===============
import sys

# activate virtualenv
activate_this = '/home/username/webapps/wsgi/env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))

# setup python path
sys.path.insert(0, '/home/username/webapps/wsgi')

# pull wsgi app
from dispatcher import app as application



httpd.conf
==========
ServerRoot "/home/username/webapps/wsgi/apache2"
ServerAdmin joe@gmail.com
Listen 33370

LoadModule authz_host_module modules/mod_authz_host.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule wsgi_module modules/mod_wsgi.so
LoadModule rewrite_module modules/mod_rewrite.so

LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\"
\"%{User-Agent}i\"" combined
ErrorLog /home/username/logs/user/error_wsgi.log
CustomLog /home/username/logs/user/access_wsgi.log combined

<Directory /home/username/webapps/wsgi>
    Order allow,deny
    Allow from all
</Directory>

WSGIDaemonProcess wsgi display-name=wsgi
WSGIProcessGroup wsgi
WSGIScriptAlias / /home/username/webapps/wsgi/dispatcher.wsgi

KeepAlive Off