Setting Up WSGI on an Ubuntu Rackspace Cloud Server
February 6th, 2010 by derekIn a previous post I wrote about how to set up mod_python on a Rackspace cloud server running a clean version of Ubuntu 9.04 Jaunty. This is fine for simple apps, but there are a number of downsides to using mod_python. Here we will install mod_wsgi, which is a much better solution and nearly as simple.
Warning: I am not an apache expert. This is the result of figuring out for the first time how to set up an apache server from scratch. Please drop a comment if you know of any way to ease the process or have any corrections.
Installing Apache2 and mod_wsgi
First we are going to install apache2. If you’ve already done this, you can skip this step.
sudo aptitude install apache2 apache2.2-common apache2-mpm-prefork apache2-utils libexpat1 ssl-cert
Next we’ll install WSGI. This should automatically install and enable WSGI on your server by putting wsgi.conf and wsgi.load in /etc/apache2/mods-available and symlinking them to /etc/apache2/mods-enabled.
sudo aptitude install libapache2-mod-wsgi
Configuring the virtual hosts
Next we’re going to set up an apache2 virtual host. This is the file that defines what folders to server up, with what mods, and on what port. We are going to edit the host that is already there, so if you have some configuration existing, you might want to back it up in case it screws around with it.
We’ll be making two folders: one for static files (like HTML) and another for your WSGI (python) scripts. This is important so that you never accidently serve your raw python files to users. Allowing the users to see your raw web server code is bad news.
Here’s a VirtualHost file to start you off; mine came from the default file located at /etc/apache2/mods-enabled/000-default. Inline are comments explaining each part of it.
<VirtualHost *:80>
# This is some standard config, not a big deal.
ServerName localhost
ServerAlias localhost
ServerAdmin webmaster@localhost
# Check the end of these files if your server is crashing,
# they usually have stack dumps of the python error.
ErrorLog /var/log/apache2/error.log
CustomLog /var/log/apache2/access.log combined
# Here is your static directory. We are saying the files in
# /var/www/documents/ can be accessed at the root directory,
# for example http://yoursite/. This directory is intended for
# files like HTML and images
DocumentRoot /var/www/documents
# Default options (both optional)
Options FollowSymLinks MultiViews
# Allow use of .htaccess files (optional)
AllowOverride None
# Allow viewing of all the subfolders
Order allow,deny
allow from all
# Here we are turning on WSGI. I'll
# explain this line in more detail in text below
WSGIDaemonProcess localhost processes=2 threads=15 display-name=%{GROUP} python-path=/var/www/wsgi-scripts/ python-eggs=/tmp
WSGIProcessGroup localhost
# This is saying http://yoursite/myapp should be be processed
# by the python script at /var/www/wsgi-scripts/myapp.py
WSGIScriptAlias /myapp /var/www/wsgi-scripts/myapp.py
</VirtualHost>
The line of interest here is the WSGIDaemonProcess directive. This says to start the WSGI module as multiple separate processes.
Not all the options I use all required, here’s why I use each one
- processes: You must use this if you want script handling to be multithreaded. The actual number can be whatever you like.
- threads: Optional. The number of threads for each process, default is 15
- display-name: Optional. This changes how each spawned process appears when listing processes with the linux command ps
- python-path: Optional. This adds the provided paths to the PYTHONPATH for spawned processes. I use the location of our scripts because it is not included by default, so this will allow the handler script we are about to write to import other scripts in the same directory.
- python-eggs: Optional. I added this after getting errors about python eggs not being able to be cached.
Testing it out
Now we create our actual handler file that we referenced in the VirtualHost file. Create the file /var/www/wsgi-scripts/myapp.py, creating the directory if necessary. In the file, paste this little test snippet and save.
def application(environ, start_response): status = '200 OK' output = 'Hello there World!' response_headers = [('Content-type', 'text/plain'), ('Content-Length', str(len(output)))] start_response(status, response_headers) return [output]
Whatever string this function returns will be displayed in your browser. Let’s restart the server and give it a whirl.
sudo /etc/init.d/apache2 restart
Now when you go to http://yourserver/myapp in your web browser, it should display “Hello there world!”.
Notes
- Whenever you change your script, the server will use the newer one without needing to restart the server. However if you change any files that your script imports, you will need to have to restart your server in order for it to use the updated version of the script.