How to Containerize and Deploy Django Web App on Azure - Single Host

Docker, Django, Azure.... we hear about these terms in our daily day to day job. I would assume we all have a little understanding as to what these terms are. If not that is ok. Check the links below to read about them.
This is a follow up from one of our previous tutorial, How to Create and Deploy Django Web App on Azure . In the previous tutorial, we created a django app and deployed it on a full VM on azure. However, in this tutorial we will be packaging the django app using docker and deploying it onto azure. Later on, I will be creating a blog-post showing the benefits and benchmark test between using a full VM or container to deploy a webapp.

Let's get started

Tools Needed

# Components Link Necessary
1 Azure Account Free $200 Azure Credit on Sign Up. Yes
2 Azure VM Create VM Yes
3 Docker Download Docker Yes
4 Your Time Roughly 20 mins Yes. So you can learn to build web app.

Please follow this tutorial to create a VM on Azure if you haven't created one already.

Once you are done creating your Azure Account and creating your VM, you are golden to follow through the tutorial.

Installing Docker and Docker-Compose

Install packages

$ sudo apt install apt-transport-https ca-certificates curl software-properties-common

Add Docker GPG key

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

Add Docker repository

$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"

Install Docker

$ sudo apt install docker-ce

Add current user to run Docker without sudo

$ sudo usermod -aG docker ${USER}

Log user out/in of system

$ su - ${USER}

Install Docker-Compose

$ sudo curl -L \
https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-linux-x86_64\
-o /usr/local/bin/docker-compose

Change Docker-Compose permission

$ sudo chmod +x /usr/local/bin/docker-compose

Congratulations you now have docker and docker-compose installed

Setting Up Django Project

Create django directory

$ mkdir django && cd django

Create a python virtual environment

$ virtualenv .

Setup DB

$ mkdir db && cd db

Create a docker-compose file to create postgres db running in a container

$ vi docker-compose.yml

Paste the following lines into the file

version: '3.5'

services:

django-db:
image: postgres
restart: always
container_name: django_db
environment:
POSTGRES_PASSWORD: root
POSTGRES_USER: root
PGDATA: /var/lib/postgresql/data
ports:
- 5432:5432

This will create a postgres db ruining in container

Bring the postgres container up

docker-compose up -d
We will be using the default postgres db and root user. Please don't do this in production environment.

Change back to django directory

cd ..

Activate the virtual environment

$ source bin/activate 

Install django and and postgres binary

$ pip install django psycopg2-binary

Start django Project

$ django-admin startproject djangoapp
You should have a directory structure like this terminal

Change directory into Project

$ cd djangoapp
You directory structure should look similar to this
~/django/db/: should contain docker-compose file for postgres db
~/django/djangoapp/: should contain djangoapp (project) and manage.py (dango project management script).
~/django/djangoapp/djangoapp: should contain __init__.py, asgi.py, settings.py, urls.py, wsgi.py.

Adjusting the project settings

First change the ALLOWED_HOSTS section to allow the your Host and other hosts you want

$ vi settings.py 
terminal

Next change the DATABASES section to look like this

terminal Use you Vm's ip as the HOST

Lastly, create a STATIC_ROOT variable to store all static files from your projects and apps

terminal

Change to the directory where we have the manage script

$ cd ..

Migrate the changes to the database

$ python manage.py makemigrations 
$ python manage.py migrate

Create a super user in the database

$ python manage.py createsuperuser

Collect all the static files in your project

$ python manage.py collectstatic

Packaging the project into Docker

Change directory to where the project root folder

terminal

Create a requirements.txt file using pip

$ pip freeze > requirements.txt

This will create a requirement.txt file that will contain all the needed dependencies to run the app int the contanier.

Due to an issue with backports.zoneinfo library in python images from docker. We will have to remove it from our requirements.txt file.
$ vi requirements.txt

Your file should look something like this

asgiref==3.5.0
backports.zoneinfo==0.2.1
Django==4.0.2
psycopg2-binary==2.9.3
sqlparse==0.4.2

If your requirements.txt doesn't have the backports.zoneinfo ignore the next step.

delete the backports.zoneinfo==0.2.1 line and save the file

Your requirements.txt should look something like this, unless you added other libraires for your app

asgiref==3.5.0
Django==4.0.2
psycopg2-binary==2.9.3
sqlparse==0.4.2

Create a Dockerfile

$ vi Dockerfile

Paste the following lines into the file

# syntax=docker/dockerfile:1
FROM python:3.9.6-alpine
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /code/
COPY requirements.txt /code/
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
COPY . /code/

This dockerfile will use the python image as the base image on which to build the django app. It will also use the requirement.txt file to install the necessary dependencies on the python image. It will also copy all the django project file into the python image.

Create a docker-compose.yml file

$ vi docker-compose.yml

Paste the following lines into the file

version: "3.9"

services:
web:
build: .
container_name: django_app
command: python djangoapp/manage.py runserver 0.0.0.0:80
volumes:
- .:/code
ports:
- "80:80"

This docker-compose.yml file will create a container based on the Dockerfile and will start the django app. It will also map the port 80 from the container onto the port 80 on the vm.

Make sure your port 80 on the VM isn't mapped to any application

Bring the django-web-app container up

docker-compose up -d

Confirm all container is up an running

docker-compose ps -a
terminal

Open up a browser to check if django is up

Use the VM's ip without any IP

http://vm-IP/
terminal

Congratulations, we now have the Django running in a container on our vm

Conclusion

We just learnt how to package our django-app and it's db into a container. This scenario would help you deploy your app onto any environment without any worries about infrastructure, O.S or other problems faced with deploying traditional apps. There will be a follow up tutorial on using minikube - a single host kubernetes cluster, to demonstrate scalability with our django-app. Also, we will do a benchmark test on a django-app running in a container and a vm to weigh out our options.