How to deploy a Django app on AWS (with Elastic Beanstalk)

This tutorial is walk-through of how to deploy a Django application to AWS Elastic Beanstalk (with Postgres).

Pre-requisites:
  1. Python 2.x
  2. pip
  3. virtualenv (install with pip install virtualenv; read more here)
  4. awsebcli (install with pip install awsebcli)
  5. an AWS account. You can signup for their free tier if you don't already have an account.
1. Create a database and a database user

To install postgres on Ubuntu:

sudo apt-get update  
sudo apt-get install python-pip python-dev libpq-dev postgresql postgresql-contrib  

During the Postgres installation, an operating system user named postgres is created, and this is the super user account to connect to our database. We need to switch to this user to perform administrative tasks:

sudo su - postgres  

We're now in the postgres user account session, next we'll log into PostgreSQL using the psql CLI

psql  
CREATE DATABASE myproject;  
CREATE USER myprojectuser WITH PASSWORD 'password';  

Let's grant the user myprojectuser all the database privileges they will need.

GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;  
2. Setup a virtualenv and create a Django project

Let's create a virtual environment and activate it.

virtualenv ~/eb-venv  
source ~/eb-venv/bin/activate

Now let's install Django with:

pip install django==1.11  

To check if Django was installed correctly, open up a python console and type

import django  
django.VERSION  

If django is installed it will print the version you're on (eg. 1.11), otherwise the shell will print an error message.

Now we'll start a Django project with

~$ django-admin startproject ebdjango

This should create a project with following structure.

~/ebdjango
  |-- ebdjango
  |   |-- __init__.py
  |   |-- settings.py
  |   |-- urls.py
  |   `-- wsgi.py
  `-- manage.py

Let us check that everything we've set up so far works locally.

To run the Django server locally, you will need the add localhost or 127.0.0.1 to your ALLOWED_HOSTS setting in ~/ebdjango/ebdjango/settings.py. Edit the file and add this setting:

ALLOWED_HOSTS = ['localhost', '127.0.0.1', '.elasticbeanstalk.com']  
(eb-venv) ~$ cd ebdjango
(eb-venv) ~/ebdjango$ python manage.py runserver

Open http://127.0.0.1:8000/ in a web browser to view your application.

3. Configure database settings and migrate database
(eb-venv) ~/ebdjango$ pip install django psycopg2

Edit ~/ebdjango/ebdjango/settings.py. You will see a DATABASES section that looks like this:

...
DATABASES = {  
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
...

We'll change the engine so that it uses the postgresql_psycopg2 backend instead of the sqlite3 backend. We'll also add the database name and user login credentials. We'll add localhost as the host and leave port blank to select the default.

...
DATABASES = {  
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}
...

Now we run database migration to set up the initial database structure

(eb-venv) ~/ebdjango$ python manage.py makemigrations
(eb-venv) ~/ebdjango$ python manage.py migrate

After creating the database structure, we can create an administrative account by typing:

(eb-venv) python manage.py createsuperuser
4. Add a Database Migration Configuration File

Create a new configuration file named db-migrate.config with the following content

~/ebdjango/.ebextensions/db-migrate.config

container_commands:  
  01_migrate:
    command: "django-admin.py migrate"
    leader_only: true
option_settings:  
  aws:elasticbeanstalk:application:environment:
    DJANGO_SETTINGS_MODULE: ebdjango.settings

This configuration file runs the django-admin.py migrate command during the deployment process, prior to starting your application. Because it runs prior to the application starting, you must configure DJANGO_SETTINGS_MODULE environment variable explicitly (usually wsgi.py takes care of this during startup). Specifying leader_only: true ensures that it is run only once when you're deploying to multiple instances.

5. Configure your site for Elastic Beanstalk

Activate your virtual environment.

~/ebdjango$ source ~/eb-venv/bin/activate

Run pip freeze and save the output to a file named requirements.txt

(eb-venv) ~/ebdjango$ pip freeze > requirements.txt

Create a new directory, called .ebextensions

(eb-venv) ~/ebdjango$ mkdir .ebextensions

Within the .ebextensions directory, add a configuration file named django.config with the following text:

~/ebdjango/.ebextensions/django.config

option_settings:  
  aws:elasticbeanstalk:container:python:
    WSGIPath: ebdjango/wsgi.py
  aws:elasticbeanstalk:container:python:staticfiles:
    "/static": "static/"
container_commands:  
   01_collectstatic:
      command: "python manage.py collectstatic --noinput"
      leader_only: true

WSGIPath specifies the location of the WSGI script that Elastic Beanstalk uses to start the application.

6. Create an environment and deploy your Django application

If you haven't already, create an IAM user here. We will need the aws-access-id and aws-secret-key in the next steps.

Initialize your EB CLI repository with the eb init command:

~/ebdjango$ eb init -p python2.7 django-tutorial

Run eb init again to configure a default keypair so that you can connect to the EC2 instance running your application with SSH

~/ebdjango$ eb init

Create an environment and deploy your application to it with eb create

~/ebdjango$ eb create django-env

When the environment creation process completes, open your web site with eb open

~/ebdjango$ eb open

Ensure that your directory ~/ebdjango looks like this:

~/ebdjango
  |-- ebdjango
  |   |-- __init__.py
  |   |-- settings.py
  |   |-- urls.py
  |   `-- wsgi.py
  |-- manage.py
  |-- requirements.txt
  |-- .ebextensions
  |   |-- db-migrate.config
  |   `-- django.config
  `-- .elasticbeanstalk 

Whenever you need to make any changes to your code and deploy use:

~/ebdjango$ eb deploy

You might get this error:

django.db.utils.OperationalError: could not connect to server: Connection refused  
Is the server running on host "localhost" (127.0.0.1) and accepting  
TCP/IP connections on port 5432?  

This just means that you haven't set up a postgres db on your production server. Let's do that next.

7. Set up RDS Postgres Database

Open your Elastic Beanstalk configuration page with

~/ebdjango$ eb console

Go to the Configuration tab, scroll to the end and click on Create a new RDS instance. Setup your credentials and hit Apply. AWS will take a few minutes to spin up your RDS instance.

Edit your DATABASES configuration parameter to reflect the following in settings.py:

...
DATABASES = {  
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': os.environ['RDS_DB_NAME'],
        'USER': os.environ['RDS_USERNAME'],
        'PASSWORD': os.environ['RDS_PASSWORD'],
        'HOST': os.environ['RDS_HOSTNAME'],
        'PORT': os.environ['RDS_PORT'],
    }
}
...

Next add a local_settings.py file in the directory containing settings.py.

DATABASES = {  
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}

Append this to your settings.py

if os.environ.get('ENVIRONMENT') == "PROD":  
    print "Running on Production Environment settings"
else:  
    from local_settings import *
    print "Running on  Local Environment settings"

Finally, set environment flag with:

~/ebdjango$ eb setenv ENVIRONMENT=PROD

This will separate your production settings from local development settings.

Deploy the changes with

~/ebdjango$ eb deploy

If you're running into errors, here's how you can view logs for the EC2 instances in your Elastic Beanstalk environment.

Resources:

Demo: Check out a working demo here.
Code: Find the code we wrote in this tutorial in this repo.

Deepti

Read more posts by this author.

Subscribe to TrySudo

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!