Problem:
You are a good little Python developer and you use virtualenv in your Python development. If you aren’t using virtualenv you’re doing it wrong. You have one of the follwoing problems/desires.
- You want/need to use a Python virtual environment in your production deployment to encapsulate your configuration
- You have multiple Apache virtual hosts on the same machine and/or you are running serveral Python/Django applications with different or conflicting versions of Python packages.
Solution:
I will demonstrate the solution by installing a Pinax application. Pinax is a website framework buit on top of Django. Pinax has many dependencies (including a specific version of Django). We will use Pinax version 0.7.3 whcih requires Django 1.0. I found the the official Pinax deployment documentation very weak and it did not work for me out of the box. Have no fear. The following steps should get you going. You can also use this blog post as a general tutorial on production deployment of Django applications. This solution was tested on Ubuntu 10.10 64-bit server edition.
Step 1: Install system pre-requisites:
$ sudo apt-get update
$ sudo apt-get install git-core build-essential python-setuptools python2.6-dev
Step 2: Install `pip`, `virtualenv`, and ‘virtualencwrapper’:
$ sudo easy_install pip
Step 3: Install ‘virtualenv’ and ‘virtualenvwrapper’ with pip:
$ sudo pip install virtualenv virtualenvwrapper
Step 5: Setup virtualenv wrapper to make dealing with Python virtual environments trivial.
This will let us use the command “workon” and say “workon ve” to switch to the “ve” virtual environment. All wee need to do is add a couple items in our .baschrc file. You can use any text editor to do this but I will give the commands using “nano”. Nano is a terminal based text editor pre-installed with Ubuntu. ”vi” and “emacs” are also options.
$ cd ~
$ nano .bashrc
Add the following lines to the bottom of the file and then save it by typing CTL-x and then hit ‘y’ to confirm the save.
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh
Now activate virtualenv wrapper by re-running the .bashrc
$ source .bashrc
Step 6: Download and install Pinax
Before we download Pinax, here is a little background. The Pinax installer will use pip to fetch other prerequisites and create the virtual environment. This is special to Pinax. Under a non-Pinax configuration, you would download your app, create a virgin virtalenv, activate it, and then run pip to fetch prerequisites and install them to the new virtualenv. For clarity and completeness I’m going to illustrate the these basic steps here. I’ll use an imaginary project name “foobar” (You don’t need to do this for Pinax):
$ wget http:/somewebsite/foobar.tar.gz
$ tar zxvf foobar.tar.gz
$ mkvirtualenv foobar-env --no-site-packages
$ workon foobar-env
(foobar-env)$ cd foobar
(foobar-env)$ pip install -r requirements.txt
This will install the prerequisites in the file “requirements.txt” to the virtual environment named “foobar-env”.
Okay back to the task at hand. Let’s install Pinax. Download and untar/unzip Pinax:
$ wget http://downloads.pinaxproject.com/Pinax-0.7.3-bundle.tar.gz
$ tar zxvf Pinax-0.7.3-bundle.tar.gz
$ cd Pinax-0.7.3-bundle/
Create the virtualenv and install the prerequisites. I’m calling my virualenv “pinax-env”. This step could take a while.
$ python scripts/pinax-boot.py $WORKON_HOME/pinax-env
Activate (i.e. switch to) the virtualenv.
$ workon pinax-env
Next let’s create a directory called projects to hold our actual project
(pinax-env)$ cd ~
(pinax-env)$ mkdir projects
(pinax-env)$ cd projects
Now lets clone a new Pinax project based on the Pinax intranet_project base. The first line will display a list of all project types.
(pinax-env)$ pinax-admin clone_project -l
(pinax-env)$ pinax-admin clone_project intranet_project myintranet
Install Python Imaging Library (PIL) which seems to be missing from Pinax installer
(pinax-env)$ pip install PIL
Step 7 : Fire Up the Pinax Project
(pinax-env)$ cd myintranet/
(pinax-env)$ python manage.py syncdb
(pinax-env)$ python manage.py runserver
Point your browser at http://localhost:8000/ and you should see the Pinax default homepage. If you are hosting remotely you may want to do the following:
(pinax-env)$ python manage.py runserver 0.0.0.0:8000
This will make Django serve over the Internet. Note this is only development server. We are just verifying things are working up to this point. Now here is where it gets insteresting. This blog post is about deployment on Apache! Now we want to make install Apache 2, mod_wsgi, and tell Apache to serve the Pinax/Django project located at “~/projects/myintranet” using our virtualenv called “pinax-env. I’m using “ubuntu” as the deault username. So our home directory is “/home/ubuntu/”, our project directory is “/home/ubuntu/projects/myintranet” and the root of our virtalenvs is “/home/ubuntu/.virtualenvs/”. Change this as needed to suit your system.
Step 8: Install Apache2 and mod_wsgi.
Hint don’t install mod_python too. This can mess things up.
$ sudo apt-get install apache2 libapache2-mod-wsgi
Step 9: Configure Apache to serve our project
We need to tell Apache to server our project. We do this by pointing Apache to a special python-based WSGI configuration file for our project. For Pinax, a sample file is located at “deploy/pinax.wsgi” . For Apache, the main file you need to edit is “/etc/apache2/sites-available/default”. Add the following lines in blue to “/etc/apache2/sites-available/default”:
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www
Deny from all
Allow from 127.0.0.0/255.0.0.0 ::1/128
</Directory>
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
WSGIScriptAlias / /home/ubuntu/projects/myintranet/deploy/pinax.wsgi
<Directory /home/ubuntu/projects/myintranet/deploy>
Order deny,allow
Allow from all
</Directory>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
Alias /doc/ "/usr/share/doc/"
<Directory "/usr/share/doc/">
Options Indexes MultiViews FollowSymLinks
AllowOverride None
Order deny,allow
Deny from all
Allow from 127.0.0.0/255.0.0.0 ::1/128
</Directory>
Now its time to complete our configuration by editing “home/ubuntu/projects/myintranet/deploy/pinax.wsgi”. Pinax.wsgi file didnt work for me but the configuration I gleamed from mod_wsgi’s wiki on Virtual Environments will work. Completely erase the default pinax.wsgi file and replace it with the following:
ALLDIRS = ['/home/ubuntu/.virtualenvs/pinax-env/lib/python2.6/site-packages',
'/home/ubuntu/projects',]
import os
import sys
import site
# redirect sys.stdout to sys.stderr for bad libraries like geopy that uses
# print statements for optional import exceptions.
sys.stdout = sys.stderr
prev_sys_path = list(sys.path)
for directory in ALLDIRS:
site.addsitedir(directory)
# Reorder sys.path so new directories at the front.
new_sys_path = []
for item in list(sys.path):
if item not in prev_sys_path:
new_sys_path.append(item)
sys.path.remove(item)
sys.path[:0] = new_sys_path
activate_this = '/home/ubuntu/.virtualenvs/pinax-env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
from os.path import abspath, dirname, join
from site import addsitedir
sys.path.insert(0, abspath(join(dirname(__file__), "../../")))
from django.conf import settings
os.environ["DJANGO_SETTINGS_MODULE"] = "myintranet.settings"
sys.path.insert(0, join(settings.PINAX_ROOT, "apps"))
sys.path.insert(0, join(settings.PROJECT_ROOT, "apps"))
from django.core.handlers.wsgi import WSGIHandler
application = WSGIHandler()
The key things that may change if the project path and/or virtual environment change are highlighted in blue. Note you want to remove the following lines if you are setting up a regular Django project (one that is not based on Pinax).
sys.path.insert(0, join(settings.PINAX_ROOT, "apps"))
sys.path.insert(0, join(settings.PROJECT_ROOT, "apps"))
Step 10: Restart Apache and Cross your fingers
$ sudo apache2ctl restart
If the god s are smiling on you today you should be in business. check by going to http://localhost/. If not, you might want to check Apache’s error log.
$ cat /var/log/apache2/error.log
There are many other Django and Pinax specific setting you will likely want to change. Please see the Django Project and the Pinax Project for more information. Good luck.