In this blog entry I would like to describe our deployment strategies we use at the different stages of our development process. The stages are the following:
- local development
- staging (+ QA)
- production (+ QA)
For local development everyone is welcome to use his preferred way, but most of us bet on Virtualenv. Especially given the fact that we maintain a large number of different projects, it makes our lives a lot easier to have a separate environment for each of those. We also have to make sure that we align with the production environment, which is in some cases still Python 2.5-based, but we’re currently in transition to 2.6.
If a feature or bugfix is ready to be QA’d, we deploy the application to an Amazon EC2 instance. Our team mate, Łukasz Czyżykowski, wrote a collection of extensions to Fabric, that provides a few useful functions (e.g. using private PPAs very easily). With a few dozens of lines of simple Python code, we can deploy the whole application to a running EC2 instance. We also use EC2 to QA all the features and bugfixes targeted at a release together before deploying to staging, so that if there is an issue, we can re-deploy very quickly (during the next two stages, QA is mainly about testing regressions).
The staging and production environments are identical from the deployment process perspective. We simply create a binary Debian package from our application: Launchpad’s PPA feature makes the build process a breeze. The main reason we decided to go with Debian packages is that we can also specify system level dependencies, not only Python packages (and of course there’s some dogfooding involved since the company supports Ubuntu). This also requires that all of the team members have packaging skills, so we had several training sessions, and a two-day online sprint where we packaged lazr.restful and all of its dependencies which were not available in Ubuntu 8.04 (around 30 packages, half of them backports, half of them new packages – thanks to our hard-working team members, these are available for Ubuntu 10.04 as well).
For configuration we don’t use Django’s built-in settings mechanism, but a custom solution that will be open sourced in the near future (one more reason to keep an eye on our blog). It consists of two components: schemaconfig is responsible for parsing the config files (which are INI-style, but have some extra features, like layering, typing, and support for data structures like lists and dictionaries – basically we looked around for solutions, and stole a little bit from everywhere to put together one that fits us most), and there’s django-settings which is a glue between schemaconfig and Django’s settings (so in the end we still use django.conf.settings). One of the biggest problems we had with our previous setup that it was very prone to human error, and that caused us unexpected deployment issues between staging and production. This is solved by the layering and the non-Python style of the config files, so they are easily manageable by both us and IS (our operations team).
Watch this blog for more about schemaconfig and other exciting projects and articles!