deploy Django app using Docker

Docker is a program that makes managing application processes in containers much more accessible. Containers allow you to run your programs in separate processes with their resources. Containers are comparable to virtual machines, except they’re more portable, resource-friendly, and reliant on the host OS.

Containers enable developers to package apps with all the components they require and then distribute them as packages. It also allows the execution of several apps on the same server.

Docker provides a higher level of security since programs running inside containers are segregated from one another. Furthermore, Docker ensures that each container has its own set of resources, so an application will only use the resources that have been given to it.

The advantages of containerization include:

  • It reduces overall ownership costs.
  • Because containers are lightweight, they are simple to manage and scale.
  • Because processes run in various containers, containerization creates separation and hence reduces overdependence.
  • Effective use of RAM- Containers take up only the space required to conduct their functions.
  • Code is easy to ship–Docker provides a uniform environment for applications from development to production, removing the need for developers to worry about compatibility.
  • Faster integration – With only a few keystrokes, you can ship several instances of an application in the same container or distinct containers. You can, for example, run two separate Django apps in different containers, each operating on a different Python version.

Docker Compose

Docker Compose is a program that allows you to create and operate multi-container Docker applications. A YAML file is used to set up the application’s services, which makes this possible. Then, with just a single command from the configuration, these services are established and started.

Containers and Docker images

A container is a running instance of a docker image that contains instructions for software components required by the application.

Docker Commands

To get started, let’s look at some of the basic Docker commands listed below.

  • docker ps–this command displays a list of all currently running containers.
  • docker ps a–this command is used to display a list of all containers (including those not running).
  • docker images – This command displays a list of all images in Docker.
  • docker run [image-id]–This command launches the image supplied.
  • Docker pull [image]–This command retrieves a Docker image from the Docker registry. For example, to get the Ubuntu image, type:

docker pull ubuntu

The app’s behavior in development differs significantly from its behavior in production. Pushing to production, or making an app production-ready, is a critical, make-or-break stage in app development. Certain configurations must be completed to ensure no security breaches or sensitive configurations, such as secret keys, occur. It’s the same with Django web development.

The Django application must have the following features to be used in production:

For receiving HTTP requests, a stable and reliable web server gateway interface (WSGI) is required. If the Django app receives much HTTP traffic, the proxy server can also be a load balancer.

You’ll install and utilize Docker Community Edition (CE) on Ubuntu 20.04 in this tutorial. You’ll learn how to use Docker, how to work with containers and images, and how to pull an image from a Docker Repository.

In addition, this article will show you how to set up a Django app with NGINX as the proxy server and Gunicorn as the WSGI. Finally, the guide will package all of these using Docker for ease of setup. As a result, it is assumed that you have at least intermediate Docker and Docker Compose experience, as well as beginner Django skills.

Prerequisites

You’ll need the following items to complete this tutorial:

  • One Ubuntu 20.04 server, including a sudo non-root user and a firewall – you can set up using the Ubuntu 20.04 initial server setup tutorial.
  • Docker
  • Python 3.9 (Download from here)

If you want to generate your images and push them to Docker Hub, you’ll need a Docker Hub account. However, we will start by first installing Docker.

Installing Docker in Ubuntu 20.04

The Docker packages in the official Ubuntu repository may not be the most recent version. Therefore, we’ll install Docker from the official Docker repository to guarantee we obtain the most current version. To do so, we’ll create a new package source, add the Docker GPG key to verify the downloads, and then install the package.

The initial stage is to update the existing list of packages by running the following command.

sudo apt update

Subsequently, you need to do an installation of the prerequisite packages that will enable apt to use the packages over HTTPS

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

After that, we will be required to add the GPG key to the system. The latter is for the official Docker repository.

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

Then follow this by adding the Docker repository to the APT sources as follows.

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

Follow this by updating the database for packages with the latest Docker packages emanating from the recently added repo.

sudo apt update

First, ascertain that you are installing from the Docker repo other than the default repo from Ubuntu.

apt-cache policy docker-ce

As a result, you will receive an output similar to the one in the image below. However, the Docker version number may vary.

The candidate for installation is from the Docker repository for Ubuntu 20.04, and docker-ce is not installed (focal).

Finally, run the following commands to install Docker:

sudo apt install docker-ce

Docker should now be installed, the daemon running, and the process automatically starts when the computer boots up. Make sure it’s up and running:

sudo systemctl status docker
check docker status
check docker status

The output should look something like this, indicating that the service is up and running:

When you install Docker, you get the Docker service (daemon) and the docker command-line program, commonly known as the Docker client. Later in this lesson, we’ll look at how to use the docker command.

When can test that Docker is installed correctly by running the following command:

$ sudo docker run hello-world
pull hello-world image from docker
pull hello-world image from docker

If the installation is done correctly, running the above command will cause the instance to download the test hello-world image. Subsequently, it will be able to run in a container.

Alternatively, run the following command.

docker -v
check docker version via terminal
check docker version via terminal

Docker Containers and Images

You can use images to construct ECS clusters with identical configurations on an AWS instance. Docker containers, too, have images. They are conceptually very similar. According to Docker’s official documentation:

A container image is a small, standalone executable package of software that contains everything needed to run it, including code, runtime, system tools, system libraries, and settings.

You can see running containers by typing “containers” in the search box.

$ sudo docker ps
sudo docker ps
sudo docker ps

On the other hand, an image is a file that is both inert and immutable and is simply a snapshot of a container. Images are built with the build command, and when started with the run command, they produce a container.

To view images in docker, run the following command:

$ sudo docker images
how to view images in docker
how to view images in docker

Building a Codeunderscored Django Application

First, create a directly called cloud by running the following command on the terminal.

Sudo mkdir cloud

Then change the directory (cd) to the new directory.

After that, create a virtual environment called cloud_env. You can do so by running the following command.

virtualenv cloud_env

If you do not have virtualenv already installed on your system, you can get it by running the following commands.

python3 -m pip install virtualenv

After completing the installation of the virtualenv, revisit the previous step to ensure that you have a virtual environment for our project. Then, subsequently activate the virtual environment we created in the last step by running the following command.

source cloud_env/bin/activate
activate cloud virtual environment
activate cloud virtual environment

Now, we can install Django by running the command,

python3 -m pip install django==3.2

Follow this by creating our test Django application as follows.

django-admin startproject codeunderscoredapp

Let’s now create a requirements.txt file inside our application directory. The requirements.txt file will be essential in defining and managing the dependencies needed by the application.

sudo vim requirements.txt

We can now add our dependencies to the requirements.txt file, for instance,

requirements.txt

Django==3.2
gunicorn==20.1.0

contents of requirements.txt
contents of requirements.txt

Let’s make the following changes to our Django Application.

manage.py

#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


def main():
    """Run administrative tasks."""
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'codeunderscoredapp.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()

views.py

from django.shortcuts import render, redirect

def home(request):
 
    return render(request, 'home.html', {})

apps.py

from django.apps import AppConfig


class UnderscoredConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'Underscored'

home.html

<!DOCTYPE html>
<html>
  <head>
    <title>Codeunderscored APP</title>
  </head>
  <body>
    
     <h1>Successfully deployed Codeunderscored App on Docker</h1>
    
  </body>
</html>

urls.py

"""codeunderscoredapp URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from Underscored.views import home

urlpatterns = [
    path('', home),
    path('admin/', admin.site.urls),
]

wsgi.py

"""
WSGI config for codeunderscoredapp project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'codeunderscoredapp.settings')

application = get_wsgi_application()

asgi.py

"""
ASGI config for codeunderscoredapp project.

It exposes the ASGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
"""

import os

from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'codeunderscoredapp.settings')

application = get_asgi_application()

settings.py

"""
Django settings for codeunderscoredapp project.

Generated by 'django-admin startproject' using Django 3.2.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-1x462+34_sh+viljsgu3zf_1v&56-f6$kg%@45ttmd6txxag9c'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
  
  # our apps
    'Underscored'
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'codeunderscoredapp.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'codeunderscoredapp.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

Make a Docker file

The following entities will be found in a typical Dockefile:

  • basic image
  • maintainer
  • command instructions on how to save the project in the container
  • Default instructions for running within the container

Docker can read instructions from a Dockerfile and build images for you automatically. A docker file provides all of Docker’s commands and instructions for creating images.

Let’s go over some of the most common commands in a Dockerfile.

  • FROM – Sets the Base Image for subsequent instructions and starts a new build stage. As a result, a legitimate Dockerfile must begin with a FROM statement.
  • RUN – executes the provided command.
  • ADD – Paste one or more files into the container.
  • EXPOSE – tells Docker that at runtime, the container listens on the given network ports.
  • CMD – Set defaults for a container that is currently running.

Let’s create a Dockerfile file now.

Sudo gedit Dockerfile

The starting point is defining the necessary properties needed in the Dockerfile. First, we will begin by defining the base image and the maintainer name.

# base image
FROM python:3.9
  
# File Author / Maintainer
MAINTAINER Codeunderscored

We will then follow the latter by copying the folder for the application inside the container. We will also define the directory where CMD will execute.

# Copy the application folder inside the container
ADD . /usr/src/app

# set the default directory where CMD will execute

WORKDIR /usr/src/app

At last, we can make settings for the default command to execute.

CMD exec gunicorn djangoapp.wsgi:application --bind 0.0.0.0:8000 --workers 3


The completed Dockerfile should resemble the following:

set the base image

FROM python:3.9

# File Author / Maintainer
MAINTAINER Codeunderscored


# add project files to the usr/src/app folder
ADD . /usr/src/app

# set directoty where CMD will execute

WORKDIR /usr/src/app

COPY requirements.txt ./

# Get pip to download and install requirements:
RUN pip install --no-cache-dir -r requirements.txt

# Expose ports
EXPOSE 8000

# default command to execute
CMD exec gunicorn djangoapp.wsgi:application --bind 0.0.0.0:8000 --workers 3

How to build the Docker Image

Now is the time to build our docker image by running the following command on the terminal.

$ sudo docker build -t django_application_image .
How to build docker image
How to build a docker image

The resultant build image is available locally in your machine’s Docker image registry. To view your image, you can do so by running the following command.

$ sudo docker images.
check if django_application_image image has been added to Docker image registry
check if the django_application_image image has been added to the Docker image registry

Running the codeunderscored Django Application

$ sudo docker run -p 8000:8000 -i -t django_application_image
Running the codeunderscored Django Application on Docker
Running the codeunderscored Django Application on Docker

At the URL http://0.0.0.0:8000, you should see a notice stating that gunicorn is providing your app. You should see the Django welcome page if you go to your server’s IP (ip address:8080).

Codeunderscored App successfully deployed on Docker
Codeunderscored App successfully deployed on Docker

To see containers that are now running, run the following command on the terminal:

$ sudo docker ps -a
containers running currently
containers running currently

Conclusion

When utilizing Docker, you may encounter various difficulties. You should first look at the Docker log files when you get an issue since they will tell you what went wrong.

For application development, Docker and other containers offer a solid alternative to traditional virtual machines.

Making a Django app production-ready entirely from declarative Docker scripts for team leads and project managers is a huge benefit. It enables them to do setups ahead of time, allowing developers to concentrate on app development. This strategy ensures that deployment methods are consistent and saves time by focusing on app development and business logic rather than setup and deployment.

Take on the task of deploying the packed application to a live server to hone your abilities. First, select an appropriate service provider. Linode, GCP, Digital Ocean, and AWS are just a few cloud service providers available.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *