Monthly Archives: August 2012

Deploying Django with Fabric

Fabric is a Python library and command-line tool for streamlining the use of SSH to deployment and sys-admin tasks. In the Django community Fabric has been picked up as the de facto standard for deploying apps, which is a place that it deserves. One thing I noticed though was that there wasn’t a lot of good examples for getting it to place nice with VirtualEnv.

Getting Started

The first thing you need is to make sure that you have Fabric installed.

pip install fabric

Once that’s done, open a file called fabfile.py and add the following imports:

from __future__ import with_statement
from fabric.api import *

Role Defs

Fabric has two functions that get used very frequently: local & run. The local() command will execute whatever you pass into in your local environment. The run() command will execute whatever you put into it in a remote environment defined in your roledefs.

A roledef defines a what a server is. For instance, if you have a nice fleshed out dev-staging-live type of environment:

evn.roledefs = {
	'dev' : ['dev.re-cycledair.com'],
	'staging' : ['staging.re-cycledair.com'],
	'live' : ['re-cycledair.com']
}

Once your roledefs are defined, you can run code on those specific servers by using the @roles decorator.

@roles("dev")
def ls_on_dev:
	run("ls") # Runs 'ls' on dev.re-cycledair.com
 
@roles("staging")
def ls_on_staging:
	run("ls") # Runs 'ls' on staging.re-cycledair.com

At this point, you want to run one of your new Fabric functions, from the directory where your fabfile is run:

fab ls_on_staging

Deployment

Now that you’ve got a handle on the trivial examples, lets make things a little more useful. We’re going to deploy to our ‘staging’ server.

@roles("staging")
def deploy_staging(tag=False):
	code_dir = '/path/to/remote/directory'
	with cd(code_dir):
		run("git fetch")
		if tag:
			run("git checkout %s" % tag)
		else:
			run("git checkout master")
			run("git pull origin master")
		with prefix('source /path/to/virtual/environment/bin/activate'):
			run("pip install -r requirements.txt")
			run("python manage.py syncdb")
			run("python manage.py migrate")
			run("python manage.py collectstatic --noinput")
			run("touch path/to/wsgi.py"

All this code is pretty self-explanatory. The @roles decorator has run execute in the context of “staging”. The run commands are just regular commands you would execute at the terminal if you were manually deploying. The only potential “gotch-ya” is the with prefix call. That basically says “execute the following statements in the context of my virtual environment”. To make everthing go:

fab deploy_staging

will deploy the head of the master branch and

fab deploy_staging:tag=1.0.0

will deploy the 1.0.0 tag.

Done

If all goes well, you can now deploy your app in seconds! If it takes longer then that, you might need to set up your ssh keys you so can login to your server without getting prompted for usernames and password.