Django 1.5 deployment on Amazon EC2 Ubuntu

26 May 2013
Django AWS


Please Note:
This post appears to be over 6 months old.

Introduction

Six months ago I began my Django journey. Since then I have setup, installed and utilised many different deployment mechanisms for projects. This tutorial demonstrates the installation and deployment solution I found to be the most fitting for my own Django projects. So, I’d like to share… 

Why Git?

I’ve been using Git for deployment within our company for sometime. It was a natural fit since all our projects already used GIT for version control and our development team understands it. What’s best, it's completely free!

Prerequisites

One will assume you are already familiar, perhaps even using GIT and have a basic understanding of Apache, Ubuntu Server and Django.

I tend to use Cloud computing such as Amazon AWS. I have tested this tutorial on a EC2 instance running Ubuntu 12.

Installing our Server Software

So as mentioned above I’ll be using an EC2 instance for this writeup. So first ssh in and do any updates.

 apt-get update
 apt-get upgrade
 

Next,  you will need to install Apache2, Python, and PIP using the following commands...

 apt-get -y install apache2-mpm-worker apache2-dev
 apt-get -y install python python-dev python-setuptools 
 sudo apt-get install python-pip
 pip install --upgrade pip
 

We will wget the latest mod_wsgi, since the time I checked Ubuntu only offered outdated version. Last commands here will add the mod to Apache2 and disable Apaches default site.

 apt-get install make
 wget http://modwsgi.googlecode.com/files/mod_wsgi-3.3.tar.gz
 tar xvfz mod_wsgi-3.3.tar.gz
 cd mod_wsgi-3.3
 ./configure
 make
 make install
 echo "LoadModule wsgi_module /usr/lib/apache2/modules/mod_wsgi.so" > /etc/apache2/mods-available/wsgi.load
 a2enmod wsgi
 a2dissite default
 

Note: I have had emails about permissions issues to resolve use 'sudo' i.e.:

 sudo echo "LoadModule wsgi_module /usr/lib/apache2/modules/mod_wsgi.so" | sudo tee /etc/apache2/mods-available/wsgi.load
 

Database?

A lot of tutorials at this stage will show you how to setup mysql on the same instance, but I highly recommend against this in production! My philosophy is that each instance should have its own dedicated job, besides AWS is cheap!  Another consideration is to use Amazon’ RDS. There are several advantages over having a separate instance for your DB, but that’s beyond the scope of this tutorial.

I’ll assume you have setup and installed the database of your choosing and continue with the setup.

Install Python environment and Django

You probably already know the benefits of apt-get. Automatic update notifications, other apt-installed packages that need those tools know they're installed, etc.

With pip, you know you're getting the latest version at the time you install it, you can install to a non-default version of Python, and you can install to a virtualenv (if you want to).

 apt-get install python-pip python-dev build-essential 
 pip install setuptools --no-use-wheel --upgrade
 pip install django
 

So what about virtualenv? You may have just noticed I didn’t install virtualenv! This is where many other developers may not agree with me. In production, I don’t tend to use virtualenv!

In most cases I would agree it is best to setup with virtualenv, even if it doesn't seem like you need. That being said, if you are using cloud services such as AWS where computing power is very cheap and each instance is designed for a specific task, then I don't really see any the point of using a virtual environment.

Now we can make a new location for the project directory.

 cd /srv
 sudo mkdir project
 cd project
 

Within the new project folder start a new Django Project.

 django-admin.py startproject website

In our newly created website folder we will now crate another directory which we will use to store our *.wsgi file.

 cd website 
 mkdir apache 
 cd apache 

For this tutorial I use a linux command-line editor called 'nano' as it's pre-installed, but you can use any editor of your choosing of course.

 nano django.wsgi
 

Paste the following code into the editor then save it. Remeber to replace 'WEBSITE' with what ever you have called your new project.

 import os
 import sys
  
  path = '/srv/project/WEBSITE'
  if path not in sys.path:
      sys.path.insert(0, '/srv/project/WEBSITE')
  
  os.environ['DJANGO_SETTINGS_MODULE'] = 'WEBSITE.settings'
  
  import django.core.handlers.wsgi
  application = django.core.handlers.wsgi.WSGIHandler()

Now, lets create a  new directory to hold our logs later on.

 cd /srv/project/
 mkdir logs
 nano logs/error.log

Now, we will create a system to run Apache's mod_WSGI process. Make sure to edit USERNAME.

 useradd --system --no-create-home --home-dir /srv/my_project/ --user-group USERNAME
  chsh -s /bin/bash USERNAME

Apache Site Configuration

Below is an example site configuration which works on most environments. Open nano to the site file with the following command. Remember to change DOMAIN with your own domain name.

 nano /etc/apache2/sites-available/DOMAIN

Paste the following to the editor and edit DOMAIN with your own domain name and USER with the username we just created a while ago.

 <VirtualHost *:80>
 ServerName www.DOMAIN.com
 ServerAlias   DOMAIN.com
 ServerAlias *.DOMAIN.com
 
 DocumentRoot /srv/project/WEBSITE
 
 Alias /robots.txt "/srv/project/WEBSITE/public/robots.txt"
 Alias /static "/srv/project/WEBSITE/static"
     <Location "/static/">
         SetHandler None
     </Location>
 
 <Directory /srv/project/WEBSITE/apache>
 Order deny,allow
 Allow from all
 </Directory>
 
 WSGIDaemonProcess DOMAIN.com processes=2 threads=15 display-name=%{GROUP}
 WSGIProcessGroup DOMAIN.com
 
 WSGIScriptAlias / /srv/project/WEBSITE/apache/django.wsgi
 ErrorLog /srv/project/logs/error.log
 </VirtualHost>
  
  
  
  

If you are using Apache 2.4 configuration the directory permission is a little different.

 <Directory /srv/project/WEBSITE/apache>
 Require all granted
 </Directory>
  

You will have noticed a few directories/files in the above configuration we don't have yet. I'm assuming these files such robots.txt will be deployed in your first commit.

Now we just need to activate that configuration we just created with Apache. Remeber again to edit DOMAIN again to your domain name or whatever you called you file.

 a2ensite DOMAIN

MySQL dependency to Python Next we need to install MySQL-module for our Python environment

 apt-get build-dep python-mysqldb

Now we have to set permissions for the user we made while ago. Edit USERNAME to your own.

 chown -R USERNAME:USERNAME /srv/project/

We have done everything to deploy Django so, all we need to do in order to get Django working is to restart Apache webserver.

 service apache2 restart

If the restart fails, check your logs. It could be because you don't yet have some of the directories we mentioned in our configuration.

Setup Deployment using GIT

The basic principle for this to work is that we create two repositories on the server. The actual live production folder in your example that’s /srv/project/ and we will have a bare “hub” repository that will act as the master copy. Local changes will be pushed to the “hub” and will then be pulled into the live repo by a post-update hook.  If this not clear hopefully the setup will help explain. 

On the server we will create a bare repository this is where what we will use to push the live repo code into. We will never push directly to our project folder.

 sudo sudo apt-get install git-core
  cd /home/ubuntu
  sudo mkdir repo
  cd repo git init --bare

Hooks

Next we need to setup a hook. First will make sure that any time anything is pushed to the hub repository it will be pulled into the live repository located at /srv/project/website.

GIT repository hooks are stored in the hooks directory of the bare repository we just created.

 cd /home/ubuntu/repo/hooks

Now create the hook post-receive...

 nano post-receive

Paste the following code, again remember to replace with your own WEBSITE if different.

 #!/bin/sh
  GIT_WORK_TREE=/srv/project/WEBSITE
  export GIT_WORK_TREE
  git checkout –f

We need to make sure this file we have just created is now executable.

 chmod +x hooks/post-receive

We are almost ready to push our local repository to the server, however before we can do this we have to edit the server authorized_keys to allow use to ssh from our local machine.

Make sure you are in your home directory i.e. /home/ubuntu. You won't see the directory called '.ssh' if you were to list 'ls' the folder out, don't worry if its not there as it's hidden!

 cd .ssh
  nano authorized_keys

Copy your local machine (Mac, PC, etc) rsa.pub key to the servers authorized_keys file then save the file. Without doing this you will not be able to push your project later!

And that’s your server setup completed!

Push your Local Website

Now the fun bit. On your local machine, setup a remote GIT repository then we create a remote which points to the repository on your server we just created in the home directory...

 git remote add deploy ssh://ubuntu@xx.xxx.xx.xxx/home/ubuntu/repo
  git push deploy +master:refs/heads/master

If all is setup correctly your local repo has just been pushed to the server and when it finished the site was automatically checked out into /srv/project/WEBSITE

To push in future, you will only need...

 git push deploy

If you now restart the server you should not have any errors. All Done!

NOTES:

I have included an example of my own git ignore file below....

 *.pyc
  *.pyo
  .installed.cfg
  bin
  develop-eggs
  dist
  downloads
  eggs
  parts
  src/*.egg-info
  lib
  lib64
  .idea/
  /static/
  workspace.xml
  /django-env/
  /south/
  core/migrations/
  *migrations/
  *migration/
  *initial.py
  /*.ini
  

That's all Folks!

Thanks for reading. Let's keep in Touch:
Follow me on GitHub or Twitter @glynjackson


Glyn Jackson is a Python Nerd, consultant and developer.


Find out more