Search code examples
djangodeploymentvirtualenvfabricbuildout

django + virtualenv = atomic upgrade - is it possible?


I have a Django project running within a virtualenv with no site-packages. When it comes to pushing my new changes to the server, I would like to create a new virtualenv directory, install my project and all its dependancies, then do a quick renaming of the two virtualenv directories ONLY if the new code deployed successfully.

All is great on paper, till the point you rename the virtualevn directory. Relocate option on virtualenv is not reliable as per its documentation.

How do you suggest upgrading my project ONLY if the new code is proven to be deployable.

Here are the steps:

# fab update_server to run the following:
cd /srv/myvenv # existing instance
cd ../
virtualenv myenv-1
source myenv-1/bin/activate
git co http://my.com/project
pip install -r project/req.txt
# all worked
mv myenv myenv-2; mv myenv-1 myenv
touch /path/proj.wsgi # have apache to reload us

The above is perfect, but renaming or relocating virtualenv is not reliable.

Upgrading the live site within myvenv takes time and may break the site too.

How would you do it? Buildout?


Solution

  • I do it with symlinks and completely separate release directories. Ie, a deployment involves cloning the entire project into a new directory, building the virtualenv inside that, then switching the "production" symlink to point at that directory.

    My layout is basically

    /var/www/myapp/
                   uploads/
                   tmp/
                   releases/
                            001/myapp/
                            002/myapp/
                            003/myapp/
                                     ve/
                                     ...etc in each release directory...
                   myapp # symlink to releases/003/myapp/
    

    So, when I deploy to production, my deployment scripts rsync a completely fresh copy to /var/www/myapp/releases/004/myapp/, build a virtualenv in there, install all the packages into it, then

    rm -f /var/www/myapp/myapp
    ln -s /var/www/myapp/releases/004/myapp/ /var/www/myapp/myapp
    

    My actual deployment script is a little more complicated as I also make sure to keep the previous release around and marked so if I notice that something is really broken, rolling back is just a matter of switching the symlink to point back at the previous one. (some extra work is also necessary to clean up old, unused releases if you are worried about the disk space).

    Every external reference (in apache config files or wsgi files or whatever) point at libraries and binaries in the virtualenv as /var/www/myapp/myapp/ve/. I also shun the use of source ve/bin/activate and instead point at the full path in config files and I edit manage.py to use #!ve/bin/python so I can run commands with ./manage.py whatever and it will always work without me having to remember if I've activated the right virtualenv.