Running OSQA with Nginx and uWSGI: VirtualHosting Mode with DynamicApps

Following on the previous post about OSQA installation, this post discusses the co-existence of OSQA with other WSGI-compliant web applications supported by uWSGI.

As has been shown in the post linked above, installing OSQA with Nginx, uWSGI and SQLite3 on Ubuntu Lucid is easy enough that it can be automatically done through a bash script. However, we were only considering an ideal and simplified case: one server, one (sub)domain, one application. Unless your OSQA site attracts so much traffic that it deserves its own VPS or even a dedicated server, you probably want to host other sites and web applications along with your OSQA installation. One common scenario would be running two or more OSQA installations on the same topic using different languages. For example, en.example.com catering for English-speaking visitors and fr.example.com targeting French-speaking visitors. There are more ways to achieve this than this post could possibly cover. We will focus on solutions with minimal deviation from our starting setup: Nginx as the only web server, uWSGI as the only application server and no coding beyond shell scripts.

In the following discussion, we will assume that OSQA has already been installed for en.example.com using the installation script and try to incorporate another OSQA installation for fr.example.com (see section 2 of previous post for installation guide).

Multiple uWSGI Master Processes

A direct solution would be running a separate uWSGI master process for each OSQA installation (or other uWSGI-supported application for that matter), with each uWSGI master process listening on a different port number and Nginx forwarding requests on different (sub)domains accordingly, e.g. 127.0.0.1:9090 used for en.example.com and 127.0.0.1:9091 used for fr.example.com.

The Nginx part of this solution is easy: just add a ‘server’ block changing the ‘alias’ to match the installation path and the port number in ‘uwsgi_pass’. The uWSGI part is a little bit tricky. First, an extra configuration file for the new OSQA installation is needed. For en.example.com, we use the one generated by the installation script. For fr.example.com, just do a ‘cp /etc/uwsgi.xml /etc/uwsgi_fr.xml’ and modify the ‘socket’, ‘python-path’ and ‘wsgi-file’ nodes in ‘/etc/uwsgi_fr.xml’. Second, the init script of uWSGI, or that of most other daemon-style applications, is not meant to start multiple processes of the same executable. In fact, it is specifically designed to ensure that there will only be one (master) uWSGI process running. To get round this issue, we start uWSGI with /etc/uwsgi_fr.xml manually:

uwsgi -x /etc/uwsgi_fr.xml -d /var/log/uwsgi.log

To get executed automatically every time the OS boots, this line should be added to /etc/rc.local before the ‘exit 0′ line.

Generally speaking, this is a secure solution, in the sense that it isolates different web applications from each other. Problems or even crashes of one application will not affect other applications, because they are served by separate and independent uWSGI processes. The main drawback of this solution, however, is its scalability. Imagine instead of 2, you want to deploy 5 or 10 applications. The number of uWSGI configuration files and rc.local changes needed will increase linearly. Another obvious issue is heavier resource usage caused by multiple processes. Efficiency will suffer the most if many or the majority of the applications are low-traffic.

Note: This example on uWSGI homepage provides a way of combining multiple configuration files into one. However, each app still needs to be started separately.

uWSGI in VirtualHosting Mode with DynamicApps

Another solution makes use of the VirtualHosting mode of uWSGI and adds applications dynamically (DynamicApps) to a single uWSGI master process by modifying Nginx configurations to pass required application parameters.

The uWSGI part is easy and done once for all. Modify ‘/etc/uwsgi.xml’, remove the application-specific ‘python-path’ and ‘wsgi-file’ directives, remove the ‘single-interpreter’ directive and add a ‘vhost’ directive:

 

www-data
www-data


127.0.0.1:9090
1

20
128



The Nginx part is also easy, but each type of application may require different settings. For our example, since there is only one type of application, the configurations are virtually the same, except the different paths. For en.example.com, ‘server’ block should look like this:

server {
  listen          80;
  # Replace this with your domain name
  server_name     en.example.com;

  location /m/ {
    alias /home/osqa/forum/skins/;
  }

  location /upfiles/ {
    alias /home/osqa/forum/upfiles/;
  }

  location /admin_media/ {
    alias /home/osqa/admin_media/;
  }

  location / {
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:9090;
    uwsgi_param UWSGI_CHDIR /home/osqa;
    uwsgi_param UWSGI_PYHOME /home;
    uwsgi_param UWSGI_SCRIPT osqa_wsgi;
  }
}

You will notice that the only real changes are the three ‘uwsgi_param’ lines. These tells Nginx what parameters it should pass to uWSGI, which will be used to find and launch the corresponding application. UWSGI_CHDIR and UWSGI_PYHOME are path-related parameters. UWSGI_SCRIPT indicates the file wherein the application is defined. For our en.example.com OSQA installation, this file is ‘/home/osqa/osqa.wsgi’. But uWSGI requires a python module, i.e. a python file with ‘.py’ extension, so we rename osqa.wsgi:

mv /home/osqa/osqa.wsgi /home/osqa/osqa_wsgi.py

Make similar modifications to fr.example.com Nginx server configuration, restart Nginx and uWSGI and you will have the two OSQA installations running together from a single uWSGI application server process.

Compared to the previous solution, this one requires far less resources and results in a virtual host environment much easier to maintain. The major drawback of it is that since all applications share a single uWSGI master process, should any of them malfunction and slow down or even crash uWSGI, all other application will suffer. Another issue with this solution is that what application is run in response to an http request entirely depends on the parameters set in Nginx configuration files. Mistakes there may cause uWSGI to serve the wrong application, resulting in the so-called ‘cross-site’ phenomenon.


For those patient enough to read to the end of this post, here is a updated installation script of OSQA with Nginx/uWSGI/SQLite3 on Ubuntu Lucid that incorporates the second solution based on uWSGI VirtualHosting mode and DynamicApps. In other words, if you want to have multiple installations of OSQA on your system, just run this script multiple times after making appropriate changes to the settings and everything will be done for you automatically.

OSQA installation script (VirtualHost Mode)

To serve other uWSGI-compatible applications, create Nginx configurations with corresponding ‘uwsgi_param’ entries for them and restart Nginx. Consult uWSGI wiki for documentation and examples.

This entry was posted in Linux, Web and tagged , , , , , , , , , . Bookmark the permalink.

3 Responses to Running OSQA with Nginx and uWSGI: VirtualHosting Mode with DynamicApps

  1. Martin says:

    The link to the scrips doesn’t work, but i found the file by googling for the file name at http://files.zayblog.com/

    I made a copy of it on pastebin to be more future proof.

    http://pastebin.com/MzwUNvrE

  2. Pingback: Installing OSQA with Nginx, uWSGI and SQLite3 on Ubuntu Lucid (10.04) Minimal | Farter's Mess