Project Structure:
A. Setup Python Virtual Environment
create virtualenv: virtualenv venv
start virtualenv: venv/Scripts/activate
install Django and Gunicorn in virtualenv: pip install django gunicorn
B. Django Project with Gunicorn
Step 1: Create Django Project, Inital Migration
Create Django: django-admin startproject myproject
Go to myproject folder: cd myproject
Initial Migration: python manage.py migrate
Step 2: Create requirements.txt
create requirements.txt: pip freeze> requirements.
Step 3: Create Dockerfile in myproject
# syntax=docker/dockerfile:1
FROM python:3.8
# set environment variables
ENV APP_HOME=/app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# set work directory
WORKDIR $APP_HOME
# update pip, install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt $APP_HOME
RUN pip install -r requirements.txt
# copy app folder
COPY . $APP_HOME
# run python command
RUN python manage.py makemigrations
RUN python manage.py migrate
RUN python manage.py collectstatic --noinput --clear
C. Nginx Docker setup
Step 1: Preparation
Create nginx folder in main folder
Step 2: Create nginx.conf file
client_max_body_size 8M;
upstream django_app {
server myproject:8000;
}
server {
listen 80;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
client_max_body_size 50M;
location / {
proxy_pass http://django_app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
Step 3: Create Dockerfile in nginx folder
# Fetching the latest nginx image
FROM nginx
# Removing default nginx.conf
RUN rm /etc/nginx/conf.d/default.conf
# Copying new conf.d into conf.d nginx image
COPY nginx.conf /etc/nginx/conf.d
D. Docker Compose Setup
Create docker-compose.yml in main folder
version: '1.0'
# Defining the compose version
services:
nginx:
build: ./nginx
ports:
- 8687:80
depends_on:
- myproject
restart: "always"
myproject:
build: ./myproject
command: sh -c "gunicorn myproject.wsgi:application --bind 0.0.0.0:8000"
expose:
- 8000
restart: "always"
Docker-compose command:
docker-compose down && docker-compose build && docker-compose up -d
down for remove existing apps (if exist), build for building into image, up for start the services
open in browser 127.0.0.1:8687
OTHER CONCERN: Debug=False, STATIC and MEDIA
Debug False is crucial for production env, STATIC and MEDIA should be served by nginx
Step 1: Django Myproject > Update myproject/myproject/settings.py
Set Debug, Allowed Host, Templates, Static and Media in settings.py
...
Debug = False #update
ALLOWED_HOSTS = ['127.0.0.1', 'localhost'] # update
...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
Path(BASE_DIR, 'templates') # update
],
'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',
],
},
},
]
...
STATIC_URL = '/static/' #new
STATIC_ROOT = Path(BASE_DIR, 'staticfiles') #new
STATICFILES_DIRS = [Path(BASE_DIR,'static'),] #new
MEDIA_URL = "/media/" #new
MEDIA_ROOT = Path(BASE_DIR, 'mediafiles') #new
...
Step 2: Django Myproject > Set static, media and templates folder
Create static, media and templates folder
Add image in static file, example: cat.png
Create index.html in templates folder
{%load static%}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World</title>
</head>
<body>
<h1>Hello World!</h1>
<img src="{%static 'cat.png' %}" alt="not found">
</body>
</html>
Step 3: Django Myproject> Update myproject/urls for serving static file simulation
from django.urls import path
from django.shortcuts import render
def home(request):
return render(request, template_name='index.html')
urlpatterns = [
path('', home ),
]
Step 4: Nginx > Update nginx.conf Add staticfiles and mediafiles
client_max_body_size 8M;
upstream django_app {
server myproject:8000;
}
server {
listen 80;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
client_max_body_size 50M;
location / {
proxy_pass http://django_app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
# updated
location /static/ {
alias /app/staticfiles/;
}
# updated
location /media/ {
alias /app/mediafiles/;
}
}
Step 5: Docker Compose> Update docker-compose.yml add volumes
version: '1.0'
# Defining the compose version
services:
nginx:
build: ./nginx
ports:
- 8687:80
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/mediafiles
depends_on:
- myproject
restart: "always"
myproject:
build: ./myproject
command: sh -c "gunicorn myproject.wsgi:application --bind 0.0.0.0:8000"
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/mediafiles
expose:
- 8000
restart: "always"
volumes:
static_volume:
media_volume:
Final Folder Structure:
Run with Docker-compose command:
docker-compose down && docker-compose build --no-cache && docker-compose up -d
open in browser 127.0.0.1:8687