For a long time I was content with the SSH into the server and do stuff
approach to deployment (I'm not managing any large deployments, and am
usually dealing with small projects without the resources for staging
servers and such), but I've been intoxicated by Fabric's sweet poison, and I think it's a
noticable improvement--even for the smallest of deployments.
Briefly, I think using Fabric (or something similar) is a real win
You never have to leave your system. You get to use your tools,
your files, your setup.
You only have to type one command to perform the deployment.
Even for simple deployments you're still typing one or two
dozen shell commands (changing directories, pulling from version
control, rebooting the server), and you just don't have to type
those commands anymore.
Even if that seems like a small gain, I think it's more than
that, because you can actually deploy without breaking your
With a good deployment solution, you type once and then run it many times, so you're more
likely to do things the right way instead of the quick way.
(Repetition leads to boredom, boredom to horrifying mistakes, horrifying mistakes to God-I-wish-I-was-still-bored, and
it goes downhill from there.)
Other developers, even those who wake up with a cold sweat when
dreaming about linux administration, can deploy and rollback as
well. If you throw a five minute GUI on top, then even your
grandmother can be out there deploying and rolling back your
By abstracting the deployment process into a simple deployment
script you're making it easiser for other people to do your
job for you. Which is a good thing. Unless you're worried that
you seem unproductive at work. Then you might not like Fabric.
It scales well to large environments.
Now let's take a brief stroll through my Fabric setup.
You can install Fabric using easy_install, but I'm using
the current Git checkout, and the project is still young enough
that code written for the latest packaged release will not work
for the current development head1. So I recommend you install it
from its Git repository.
git clone git://git.savannah.nongnu.org/fab.git
sudo python setup.py build install
Now go into your Django project directory and create a
file named fabfile.py. Let's start out by adding a few
utility functions I use for managing Git repositories.
defgit_pull():"Updates the repository."run("cd ~/git/$(repo)/; git pull $(parent) $(branch)")
defgit_reset():"Resets the repository to specified version."run("cd ~/git/$(repo)/; git reset –hard $(hash)")
Note a few important things:
The connection between
Fabric and your server is not a persistent connection.
That means you cannot use commands like cd in multiple
commands and expect them to work.
# this works as expectedrun("cd ~/git/$(repo)/; git pull $(parent) $(branch)")
# this will run the command in the wrong directoryrun("cd ~/git/$(repo)/")run("git pull $(parent) $(branch)")
Fabric uses its own system for string interpolation.
Usage is like this:
# standard python string interpolationrun("git pull %s%s"%("origin","master"))
Fabric's approach makes it easier to
pass state around between various commands.
Well, that's their story anyway. I'm not
going to defend the approach beyond saying
that I suspect they're working around some
pain point in their architecture.
Now that we have those Git utilities, lets fill
in the rest of the fabfile.py so that it can
take advantage of the utilities.
The first thing you should do is create one or more
functions, one for each set of servers you want to
handle in a different way. For example, if you
have a cluster of production servers and a cluster
of staging servers, then you'd write something
The fab_hosts value is a special value that Fabric uses
to determine the servers to run commands on, but the repos
value is something that I added in myself for my scripts.
You can add as many arbitrary values to config as your heart desires.
Now to put together the three functions I use for managing
defreset(repo,hash):""" Reset all git repositories to specified hash. Usage: fab reset:repo=my_repo,hash=etcetc123 """require("fab_hosts",provided_by=[staging,production])config.hash=hashconfig.repo=repoinvoke(git_reset)
To understand this code, it helps to know that there are
a few magic functions that Fabric uses for deployment.
run runs a command on another machine.
local runs a command on your local machine.
sudo runs a command on another machine as root.
put sends a file to another machine from your local machine.
invoke runs another function with the current function's configuration context.
require creates dependencies among various functions, to prevent functions from being run without their prerequisites.
Now deployment is as easy as:
# reboot the server
fab production reboot
# you can do all this on staging too
fab staging reboot
# update the git repositories
fab production pull
# update repositories then reboot
fab production pull reboot
# reset repositories then reboot
fab production reset:repo=my_app_repo,hash=13klafeomaeio23 reboot
This makes administration painless and quick as hell. But, hey,
lets add just a little more spice. Let's add a deployment option
that will only deploy your project if all its tests pass:
defreset(repo,hash):""" Reset all git repositories to specified hash. Usage: fab reset:repo=my_repo,hash=etcetc123 """require("fab_hosts",provided_by=[production])config.hash=hashconfig.repo=repoinvoke(git_reset)