Django — Build an app with Docker and Nginx

Docker

Nginx

[projectname]/
├── [projectname]/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
[project_name]/
├── [project_name]/
| ├── development/
| ├── ├── __init__.py
| ├── ├── settings.py
| ├── staging/
| ├── ├── __init__.py
| ├── ├── settings.py
| ├── testing/
| ├── ├── __init__.py
| |── ├── settings.py
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ |── wsgi.py
│ |── databases.py
| ├── thrid_parties/
| └── ├── __init__.py
└── manage.py
from project_name.settings import *DEBUG = TrueDJANGOENV = 'development'MIDDLEWARE += [
'debug_toolbar.middleware.DebugToolbarMiddleware'
]
INSTALLED_APPS += [
'django_nose',
'django_extensions',
'debug_toolbar',
'template_repl',
]
import osBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))BD_NON_DEFAULT = u'db_non_default'DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
BD_NON_DEFAULT: {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': 'postgres',
'HOST': 'postgres',
'PORT': '5432',
}
}
from project_name.databases import *

Docker-compose

version: '2'
services:
db:
restart:
always
image: postgres:9.6.0
expose:
- "5432"
volumes:
- "{{ project_name }}_db_data:/var/lib/postgresql/data"
ports:
- "5432:5432"
redis:
restart:
always
image: redis:latest
expose:
- "6379"
{{ project_name }}:
restart: always
image: oratio/kale:1.0.0
depends_on:
- redis
environment:
DJANGOENV:
development
ENVIRONMENT: development
PYTHON: python
PROJECT_NAME: {{ project_name }}
TERM: xterm
ROLE: development
links:
- db:postgres
- redis:redis
ports:
- "80:80"
- "443:443"
- "8000:8000"
expose:
- "80"
- "443"
- "8000"
volumes:
- .:/var/www
working_dir: /var/www
command: bash -lc "pip install invoke jinja2 && invoke -r roles $${ROLE}"
volumes:
{{ project_name }}_db_data:
external:
true
volumes:
{{ project_name }}_db_data:
external:
true
db:
restart:
always
image: postgres:9.6.0
expose:
- "5432"
volumes:
- "{{ project_name }}_db_data:/var/lib/postgresql/data"
ports:
- "5432:5432"

The main project container

{{ project_name }}:
restart: always
image: oratio/kale:1.0.0
depends_on:
- redis
environment:
DJANGOENV:
development
ENVIRONMENT: development
PYTHON: python
PROJECT_NAME: {{ project_name }}
TERM: xterm
ROLE: development
links:
- db:postgres
- redis:redis
ports:
- "80:80"
- "443:443"
- "8000:8000"
expose:
- "80"
- "443"
- "8000"
volumes:
- .:/var/www
working_dir: /var/www
command: bash -lc "pip install invoke jinja2 && invoke -r roles $${ROLE}"

Environment Variables

environment:
DJANGOENV:
development
ENVIRONMENT: development
PYTHON: python3
PROJECT_NAME: {{ project_name }}
TERM: xterm
ROLE: development
command: bash -lc "pip install invoke jinja2 && invoke -r roles $${ROLE}"

tasks.py

import os
import sys
from jinja2 import Environment, FileSystemLoader
from invoke import run, task
def _template_file(template, destination, template_dir='/var/www/deploy/'):
environment = Environment(loader=FileSystemLoader(template_dir))
template = environment.get_template(template)
rendered_template = template.render(os.environ)
if os.path.isfile(destination):
os.unlink(destination)
with open(destination, 'w') as f:
f.write(rendered_template)
@task
def development():
run('pip3 install -U pip')
run('pip3 install -r requirements/development.txt')
_template_file('nginx/nginx.conf', '/etc/nginx/sites-enabled/default')
_template_file('supervisor.nginx.conf', '/etc/supervisor/conf.d/nginx.conf')
run('supervisord -n')

Makefile

clean_pyc:
find . -type f -name "*.pyc" -delete || true
migrate:
python3 $(PROJECT_NAME)/manage.py migrate --noinput
migrations:
python3 $(PROJECT_NAME)/manage.py makemigrations
run:
python3 $(PROJECT_NAME)/manage.py runserver_plus 0.0.0.0:8000 --settings=$(PROJECT_NAME).$(ENVIRONMENT).settings
shell:
python3 $(PROJECT_NAME)/manage.py shell_plus --settings=$(PROJECT_NAME).$(ENVIRONMENT).settings
show_urls:
python3 $(PROJECT_NAME)/manage.py show_urls --settings=$(PROJECT_NAME).$(ENVIRONMENT).settings
validate_templates:
python3 $(PROJECT_NAME)/manage.py validate_templates --settings=$(PROJECT_NAME).$(ENVIRONMENT).settings

Nginx configuration

{% if ENVIRONMENT == 'development' -%}
{% set subdomain = 'dev.' -%}
{% elif ENVIRONMENT == 'testing' -%}
{% set subdomain = 'testing.' -%}
{% elif ENVIRONMENT == 'staging' -%}
{% set subdomain = 'staging.' -%}
{% elif ENVIRONMENT == 'live' -%}
{% set subdomain = '' -%}
{% endif -%}
server {
server_name {{ subdomain }}project.name.com;
error_log /dev/stderr;
{% if ENVIRONMENT not in ('live', 'staging') -%}
access_log /dev/stdout;
{% else -%}
access_log /dev/stdout json_format;
{% endif -%}
location /crossdomain.xml {
return 200;
}
location /healthcheck/ {
include uwsgi_params;
uwsgi_param HTTP_HOST www.{{ subdomain }}project.name.com;
uwsgi_param HTTP_X_FORWARDED_PROTO 'https';
uwsgi_pass unix:/var/www/uwsgi.sock;
}
location / {
return 301 https://www.{{ subdomain }}project.name.com$request_uri;
}
}
server {
server_name www.{{ subdomain }}project.name.com ;
large_client_header_buffers 4 512k; error_log /dev/stderr;
{% if ENVIRONMENT not in ('live', 'staging') -%}
access_log /dev/stdout;
{% else -%}
access_log /dev/stdout json_format;
{% endif -%}
client_max_body_size 100M; {% if ENVIRONMENT != 'development' -%}
real_ip_header X-Forwarded-For;
set_real_ip_from 0.0.0.0/0;
{% else -%}
add_header Host $host;
add_header X-Forwarded-For $http_x_forwarded_for;
add_header X-Forwarded-Proto $scheme;
add_header X-CSS-Protection "1; mode=block";
{% endif -%}
sendfile off; location /favicon.ico {
alias /var/www/projet_name/static/foundation6/img/favicon.png;
}
location /error.html {
alias /var/www/projet_name/templates/errors/500.html;
}
location /fonts {
alias /var/www/projet_name/static/foundation6/fonts;
}
{% if ENVIRONMENT != 'development' -%}
error_page 500 501 502 503 504 =200 /err.or.html;
{% endif -%}
{% if ENVIRONMENT == 'development' -%}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_read_timeout 300s;
proxy_set_header Host $host;
}
{% else -%} location / {
include uwsgi_params;
uwsgi_pass unix:/var/www/uwsgi.sock;
uwsgi_read_timeout 300s;
{% if ENVIRONMENT != 'live' -%}
satisfy any;
deny all;
auth_basic "Password Protected Access";
auth_basic_user_file /var/www/deploy/htpasswd;
{% endif -%}
}
{% endif -%}
}
[uwsgi]
# Django-related settings
# Enable threads
enable-threads = true
single-interpreter=true
wsgi-file = /var/www/app.wsgi
touch-reload = /var/www/app.wsgi
touch-logreopen = /var/www/app.wsgi
chown-socket = www-data:www-data
master = true
workers = 4
;processes = 10
socket = /var/www/uwsgi.sock
chmod-socket = 664
harakiri = 300
harakiri-verbose
max-requests = 300
log-zero
log-slow
log-500
log-x-forwarded-for
drop-after-apps
buffer-size = 32768
vacuum = true
#!/usr/bin/env pythonimport os
import site
import sys
import _strptimeprev_sys_path = list(sys.path)site.addsitedir(os.path.abspath('/var/www'))
site.addsitedir(os.path.abspath('/var/www/project_name'))
new_sys_path = []
for item in list(sys.path):
if item not in prev_sys_path:
new_sys_path.append(item)
sys.path.remove(item)
sys.path[:0] = new_sys_path
os.environ["DJANGO_SETTINGS_MODULE"] = "project_name.{{ ENVIRONMENT }}.settings"from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
@task
def staging():
run('pip3 install -U pip')
run('pip3 install -r requirements/common.txt')
_template_file('nginx/nginx.conf', '/etc/nginx/sites-enabled/default')
_template_file('uwsgi/app.wsgi', '/var/www/app.wsgi')
_template_file('supervisor.nginx.conf', '/etc/supervisor/conf.d/nginx.conf')
_template_file('supervisor.uwsgi.conf', '/etc/supervisor/conf.d/uwsgi.conf')
_template_file('uwsgi/uwsgi.ini', '/var/www/uwsgi.ini')
run('supervisord -n')

Entrepreneur, comic book fan and a tech lover :-)

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

From understanding to creating Progressive Web Apps

How I’m teaching the kids coding for the web

Introduction To GraphQL(REST alternative)

Singularity — Microsoft’s Experimental OS

Install Kali Linux GUI win-KeX in windows WSL 2

Populating a PostgreSQL Table with Pandas is Slow

Medium Tester Post

Hangfire and .NET Core

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Tiago Silva

Tiago Silva

Entrepreneur, comic book fan and a tech lover :-)

More from Medium

Deploy Django using Postgres, Nginx in digital ocean ubuntu droplet

Deploy Django using Postgres, Nginx in digital ocean

Deploy Django with Zappa + AWS

Deploy Django App with NGINX, Gunicorn, and Supervisor on Ubuntu Server

Deploy Django app with Nginx, and Gunicorn