Showing posts with label Postgresql. Show all posts
Showing posts with label Postgresql. Show all posts

Friday, July 7, 2017

Dockerize your Django Web Application


Docker platform is becoming more and more popular thanks to it’s ability to create, deploy, and run applications easily by using containers.  

In this guide we will talk about how to dockerize our django web application using nginx, gunicorn and postgresql.
If you are not deeply familiarized with docker, check out this Docker Basic Guide.

Prerequisites

  • Python 2.7 or 3.x
  • Install docker-compose (we can do that by running pip install docker-compose).

Create an empty directory

On this directory (we will name it "myproject") we will put all the required files to set up our docker configuration properly.

Create a subdirectory named web

This folder will contain our django project, let's name it mydjangoproject.

Place the requirements.txt file inside the Web folder

If you don't have it already, create it. It should be placed at the same level as mydjangoproject and contain all your project dependencies, including at least these three:

Django==1.11.2
gunicorn==19.7.1
psycopg2==2.6
Also inside "web", make a file named DockerFile and add the following lines:
FROM alpine

# Install required packages
RUN apk update
RUN apk upgrade
RUN apk add --update python python-dev py-pip postgresql-client postgresql-dev build-base gettext

# Initialize
RUN mkdir -p /data/web
COPY . /data/web/
WORKDIR /data/web/

#Setup
RUN pip install --upgrade pip
RUN pip install -r requirements.txt

#Prepare
RUN mkdir -p mydjangoproject/static/admin
By adding this lines we are setting up our container by installing the necessary packages such as pip and postgresql, adding our project to the container and installing the required dependencies.

Create a file called run_web.sh and place it inside the "web" folder

This file contains a script that will be executed when the container starts. We will add the following lines to it:

#!/bin/sh

python manage.py migrate                  # Apply database migrations
python manage.py collectstatic --noinput  # Collect static files

# Start Gunicorn
exec gunicorn myproject.wsgi:application \
  --bind 0.0.0.0:8008 \
  "$@"

Here we can see how we apply the migrations, collect the static files and start our gunicorn server on the localhost at the port 8008. Don’t forget to add the host '0.0.0.0' to your allowed hosts in your Django settings.

Go back to myproject directory and create a file named docker-compose.yml

This file is the one that will contain the configuration of all the services and how they interact with each other. Let’s add the following code:

version: '2'
services:
  # Postgres database
  postgres:
    restart: always
    image: postgres:latest
    volumes:
      - ./postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
    env_file: ./env
    expose:
      - "5432"
  # Django web service
  web:
    build: ./web
    ports:
      - "3000:8008"
    env_file: ./env
    links:
      - postgres
    depends_on:
      - postgres
    volumes:
      - ./web/mydjangoproject/static:/static
    working_dir: /data/web/mydjangoproject/
    command: /data/web/run_web.sh
  nginx:
    restart: always
    build: ./nginx/
    ports:
    - "8001:8001"
    volumes_from:
    - web
    links:
    - web
    We can see here three services: “nginx”, “web” and “postgresql”. “nginx” is linked to “web” and “web” to “postgresql” in order to make them reachable between each other. Also it’s specified that “postgres” service will use the latest version of postgres image from dockerhub and both web and nginx are built using their dockerfiles. Finally, we can see that the web service will run the script written in run_web.sh when the container gets started.

    All commands can be found on the docker official page.

    Nginx configuration:

    To configure nginx we are going to create a directory inside myproject called nginx and create a configuration file called default.conf inside it. Then we will write the following lines:

    server {
       listen 8001;
       charset utf-8;
       location /static/ {
           root /;
       }
       location / {
           proxy_pass http://web:8008;
           proxy_set_header Host $host;
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       }
    }
    On this configuration we specify the path of our static files (we have to write the same one that is specified on our settings.py file) and also we will set up the reverse proxy to redirect nginx to our gunicorn server. If you can’t see your static folder with your static files, run the command 'python manage.py collectstatic' and it will automatically collect the statics for you on the root of the project.
    Also, as nginx is built and not taken from a dockerhub image, we will create a file named Dockerfile inside our nginx directory and write the following code.
    FROM nginx:alpine
    RUN apk update
    RUN apk upgrade
    RUN apk add --update curl
    ADD default.conf /etc/nginx/conf.d/default.conf

    Finally, set up the database configuration

    This configuration will be done on the settings file from our Django project. The database section should look like this:
    
    
    DATABASES = {  
        'default': {
           'ENGINE': 'django.db.backends.postgresql_psycopg2',
           'NAME': 'postgres',
           'USER': 'postgres',
           'HOST': 'postgres',
           'PORT': 5432,
        }
    }

    We have linked the service "web" and "postgres" on the docker-compose.yml file already. That means that the "web" service will be able to establish connection with "postgres". To do that, take into account that database settings have to use the host name "postgres" to reach the container.
    The last step is to create a file named 'env' inside 'myproject' folder with the database configuration. This file is referenced in our “env_file” variable on the docker-compose file.
    We should write the following lines on it:

    
    
    POSTGRES_DB=socialNetwork
    POSTGRES_USER=root
    DB_SERVICE=postgres
    DB_PORT=5432
    POSTGRES_PASSWORD=password
    Environment variables can change depending on the database configuration. All the options can be found on the docker’s environment variables section for postgres.

    It’s done!

    Everyting should be set up now. We only need to go into 'myproject' folder (folder where docker-compose.yml is located) and run 'docker-compose build' to build the containers and 'docker-compose up' to start the containers.

    Then if you go to localhost:8001 you should see the django project working.