A core challenge of setting up a Django project is creating a production-worthy server environment and project setup. At Imaginary Landscape, we've put a lot of thought into how to accomplish this task in an intuitive and flexible way.
The goal of this article is to detail the default setup process that we use for a single-server site. We will cover the process of provisioning a server, configuring a virtual environment, and setting up a Django project. Each site that we create comes with very unique requirements, but this article will detail our default provisioning approach.
Please note that we are always iterating and improving this process and there are many approaches to setting up a Django server. However, we've found a basic server configuration that works well for us for now. Finally, when we actually provision a server, we utilize Saltstack to automate these setup tasks. However, for the purposes of this article, we will walk through the setup as though we were doing so without an automated provisioning tool.
Software Stack Overview
First, we will cover the major components of our software stack.
The infrastructures for our projects almost always involve a cloud service, such as Amazon AWS or Rackspace Cloud. Our default choice is Rackspace, since it has a few nice tools for helping manage sites. We prefer a cloud environment for a variety of reasons. Cloud servers provide an inexpensive means to isolate our various projects. They are easily scalable when the infrastructure needs to grow. Additionally, they are easy to backup, creating an exact duplicate development server is simple, and they more easily allow multi-server application architectures.
We use a carefully selected default stack as the foundation of our project. Currently, Ubuntu 14.04 (Trusty) Long Term Support serves as our operating system of choice. We find that it provides a good balance between stable and modern software, and we also love the ease-of-use that the Apt packaging system affords us. Additionally, Canonical maintains that it will provide 5 years of support for LTS releases. Of course, we will increment our OS version of choice when Canonical releases its next LTS release.
PostgreSQL serves as our database of choice. Though we are equally comfortable with MySQL, we feel that Postgres is slightly more desired and supported in the Django community. Additionally, Postgres provides some killer features that make it a pleasure to use in Django, such as PostGIS and replication. PostgresSQL 9.3 is the default version packaged with Ubuntu 14.04 and that works well for our needs.
Nginx is our webserver of choice, as it provides an extremely fast and memory-efficient means to serve static resources (images/css/js) and reverse proxy to the running Django process. Nginx is a proven technology and is used by some of the largest websites on the Internet. We typically install the latest stable version available through Apt, which is currently 1.6.
Python 2.7, the default python version bundled with Ubuntu, serves as the basis for our programming stack. As many do, we do not feel quite ready to adopt Python 3 as our default Python version, but we are making preparations to eventually make the switch. Python 2.7 positions us to eventually make the leap to Python 3, while still maintaining compatibility with many of the packages that haven't yet made the 3k leap. We prefer Python 2.7 over 2.6 because starting with Django 1.7, Python 2.7 will become a minimum requirement and we want to stay as forward-compatible as possible.
As version control is essential for a multi-programmer project, we have adopted Git as a core component of our stack. Typically, the "authoritative" Git repository for a given project of ours either resides at Github or in our own private Gitolite Git server. Though we occasionally use other version control systems, namely Mercurial and Subversion, Git remains our strong preference.
Redis is a good jack-of-all-trades tool that we often use throughout a web application. It's a great drop-in solution for Django's caching backend, using the django-redis Python module. Via the RQ Python module, Redis also offers a great queuing service for processing deferrable tasks like sending email, resizing images, or interacting with an API. Though it has been said that Memcached is faster at caching and that Celery/RabbitMQ is a more comprehensive queuing system, Redis is still extremely fast, simple, and flexible and allows us to consolidate these two external service dependencies into one service.
For actually running our Django application, we use Gunicorn. Unlike Apache's mod_wsgi and the deprecated mod_python, which run the Django process as a subprocess of the Apache process, Django/Gunicorn and Nginx run as two separate processes. This allows us to restart each process independently and allows each process to specialize in what it does best: Nginx can focus on serving static media and buffering requests to Django and Gunicorn/Django can handle all of the dynamic requests. We have considered using uwsgi, which is a popular, highly-performant alternative to Gunicorn, though Gunicorn has served our needs thus far. Gunicorn also comes with a handy "run_gunicorn" Django management command, which makes running the process in the Django context extremely simple.
On occasion, when database performance is of utmost importance, we will use the PGBouncer database connection pooler. This pre-establishes connections to the PostgreSQL database so Django doesn't have to do this on every request, offering a performance boost.
Now that we have covered the core components of our stack, let's discuss the basic server setup.
OS Package Requirements
We install most of the required Operating System packages through Apt.
We start by installing a few requisite, system-level Python modules (packages), though we try to limit the Python modules that we install at the system level. For the purposes of this article, I'm defining "system-level" Python modules as those installed via Apt versus those installed with the Python package manager, pip (when developing our application, most of our Python modules will be installed and isolated inside of a virtualenv, completely independent of the system-level packages; more on this later). Two of the system-level Python modules that we will need are python-pip and python-virtualenv.
sudo apt-get install python-pip python-virtualenv
In addition, we need the Python development sources package, python-dev. These are required for building several compiled Python modules, such as PIL/Pillow.
sudo apt-get install python python-dev python-setuptools
Second, we want to install tools needed to compile packages from source. These tools include gcc, g++, make, etc. and are needed to compile a variety of Python modules and for compiling Nginx. Debian-based systems, such as Ubuntu, provide a handy package alias to install all of the core compiler tools. This alias is called "build-essential".
sudo apt-get install build-essential
Third, we need to install a variety of media development source libraries. These libraries install the development sources for creating JPEGs, TIFFs, PNGs, etc. They are needed by the Python Imaging Library (PIL) to provide the application with support for these media types.
sudo apt-get install libjpeg62 libjpeg62-dev libtiff4-dev libwebp-dev libfreetype6 libfreetype6-dev zlib1g-dev
Fourth, we install a variety of version control client tools. Though we primarily use Git for version control, occasionally our project will require third-party Python requirements that need to be pulled directly from Subversion, Mercurial, or other types of repositories. Installing support for all of these version control systems prepares us for this possibility. You might notice the inclusion of "tig" below, which is a handy command-line-based Git repository browser.
sudo apt-get install git subversion mercurial bzr tig
Fifth, we install a few server tools that we consider staples. Postfix provides our application with the ability to send emails, NTP ensures that the system clock is always correct, and Supervisor manages our running Django and Nginx processes.
sudo apt-get install supervisor postfix ntp
To install PostgreSQL, we will need the following packages from Apt:
sudo apt-get install postgresql-9.3 postgresql-server-dev-9.3
To install Nginx, we will need the following package from Apt:
sudo apt-get install nginx
If we are to use Redis in our project, we will need to install the redis-server package.
sudo apt-get install redis-server
Additionally, we install a variety of tools that generally help make server maintenance easier.
sudo apt-get install pwgen htop curl rsync nmon dnsutils \ screen tmux ack-grep whois sqlite3 sshfs openssh-client \ openssh-server libssl-dev libreadline-dev aptitude fail2ban \ libxml2-dev libxslt-dev graphviz graphviz-dev csstidy \ emacs23-nox vim ncurses-dev iotop nload iptraf-ng nethogs
After these installations, our system will contain all of the system-level package dependencies and is ready for installing and configuring the Django application.
In Part 2 of this article series, we will cover user configuration, application setup, nginx configuration, supervisor configuration, and a variety of other topics.