Skip to content
This repository has been archived by the owner on Nov 17, 2020. It is now read-only.

Commit

Permalink
Merge pull request #17 from praekeltfoundation/feature/docker-setup
Browse files Browse the repository at this point in the history
Feature/docker setup
  • Loading branch information
nyashabryan committed Jul 30, 2018
2 parents d455150 + 2a1a8a2 commit e0d2fb8
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 0 deletions.
103 changes: 103 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Docker-specific things:
# Docker files
Dockerfile
*.dockerfile
.dockerignore

# Git files
.git/
**/.gitignore

# Adapted from the standard Python .gitignore
# https://github.com/github/gitignore/blob/master/Python.gitignore
# 455a69dd48ce041f6ac2aa7aeeb9560957311e2f

# Byte-compiled / optimized / DLL files
**/__pycache__/
**/*.py[cod]
**/*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
**/*.mo
**/*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# IPython Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM praekeltfoundation/django-bootstrap:py3.6

COPY . /app
COPY entrypoint.sh /scripts/django-entrypoint.sh
COPY nginx.conf /etc/nginx/conf.d/django.conf
RUN pip install -e .

ENV DJANGO_SETTINGS_MODULE "momkhulu.settings.production"
RUN ./manage.py collectstatic --noinput
CMD ["momkhulu.wsgi:application"]
74 changes: 74 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env sh
set -e

# No args or looks like options or the APP_MODULE for Gunicorn
if [ "$#" = 0 ] || \
[ "${1#-}" != "$1" ] || \
echo "$1" | grep -Eq '^([_A-Za-z]\w*\.)*[_A-Za-z]\w*:[_A-Za-z]\w*$'; then
set -- gunicorn "$@"
fi

# Looks like a Celery command, let's run that with Celery's entrypoint script
if [ "$1" = 'celery' ]; then
set -- celery-entrypoint.sh "$@"
fi

if [ "$1" = 'gunicorn' ] || [ "$1" = 'daphne' ]; then
# Do an extra chown of the /app directory at runtime in addition to the one in
# the build process in case any directories are mounted as root-owned volumes
# at runtime.
chown -R django:django /app

# Run the migration as the django user so that if it creates a local DB
# (e.g. when using sqlite in development), that DB is still writable.
# Ultimately, the user shouldn't really be using a local DB and it's difficult
# to offer support for all the cases in which a local DB might be created --
# but here we do the minimum.
if [ -z "$SKIP_MIGRATIONS" ]; then
su-exec django django-admin migrate --noinput
fi

if [ -n "$SUPERUSER_PASSWORD" ]; then
echo "from django.contrib.auth.models import User
if not User.objects.filter(username='admin').exists():
User.objects.create_superuser('admin', 'admin@example.com', '$SUPERUSER_PASSWORD')
" | su-exec django django-admin shell
echo "Created superuser with username 'admin' and password '$SUPERUSER_PASSWORD'"
fi

nginx -g 'daemon off;' &

# Celery
ensure_celery_app() {
[ -n "$CELERY_APP" ] || \
{ echo 'If $CELERY_WORKER or $CELERY_BEAT are set then $CELERY_APP must be provided'; exit 1; }
}

if [ -n "$CELERY_WORKER" ]; then
ensure_celery_app
celery-entrypoint.sh worker --pool=solo --pidfile worker.pid &
fi

if [ -n "$CELERY_BEAT" ]; then
ensure_celery_app
celery-entrypoint.sh beat --pidfile beat.pid &
fi

# Set some sensible Gunicorn and daphne options, needed for things to work with Nginx

# umask working files (worker tmp files & unix socket) as 0o117 (i.e. chmod as
# 0o660) so that they are only read/writable by django and nginx users.
if [ "$1" = 'gunicorn' ]; then
set -- su-exec django "$@" \
--pid /var/run/gunicorn/gunicorn.pid \
--bind unix:/var/run/gunicorn/gunicorn.sock \
--umask 0117 \
${GUNICORN_ACCESS_LOGS:+--access-logfile -}
fi
if [ "$1" = 'daphne' ]; then
set -- su-exec django "$@" \
-u /var/run/gunicorn/gunicorn.sock
fi
fi

exec "$@"
74 changes: 74 additions & 0 deletions nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
upstream gunicorn {
# Proxy to Gunicorn socket and always retry, as recommended by deployment
# guide: http://docs.gunicorn.org/en/stable/deploy.html
server unix:/var/run/gunicorn/gunicorn.sock max_fails=0;
}

# Detect filenames for static files that look like they contain MD5 hashes as
# these can be cached indefinitely.

# Nginx's 'expires max' directive sets the Cache-Control header to have a max-
# age of 10 years. It also sets the Expires header to a certain date in 2037:
# http://nginx.org/en/docs/http/ngx_http_headers_module.html#expires

# We also want to add the 'public' and 'immutable' values to the Cache-Control
# header to prevent clients from revalidating files that we know are immutable:
# https://bitsup.blogspot.co.za/2016/05/cache-control-immutable.html

# Using 'expires max' makes adding other Cache-Control values tricky and the
# The Expires header should be ignored when a max-age is set for Cache-Control:
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires

# So, avoid 'expires max' & just add the header manually with a 10-year max-age.
map $uri $static_cache_control {
# ManifestStaticFilesStorage files have a hash in the middle of the filename
"~^/static/.*/[^/\.]+\.[a-f0-9]{12}\.\w+$" "max-age=315360000, public, immutable";

# django-compressor cached files are js/css with a hash filename
"~^/static/CACHE/(js|css)/[a-f0-9]{12}\.(js|css)$" "max-age=315360000, public, immutable";

# For the default, copy what WhiteNoise does and set a short max-age
default "max-age=60, public";
}

server {
listen 8000;

root /app;

location ~ ^/static/?(.*)$ {
# Fallback for projects still using STATIC_ROOT = BASE_DIR/staticfiles
# as recommended by WhiteNoise
try_files /static/$1 /staticfiles/$1 =404;
add_header Cache-Control $static_cache_control;
}

location ~ ^/media/?(.*)$ {
# Fallback for projects still using MEDIA_ROOT = BASE_DIR/mediafiles
try_files /media/$1 /mediafiles/$1 =404;
}

location / {
client_max_body_size 20m;
proxy_pass http://gunicorn;

proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# For websockets, from https://www.nginx.com/blog/websocket-nginx/
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";

# We only use the 'X-Forwarded-Proto' header from our load-balancer to
# indicate the original connection used HTTPS, but Gunicorn by default
# accepts more headers than that:
# http://docs.gunicorn.org/en/19.7.1/settings.html#secure-scheme-headers
# Overriding that config in Gunicorn is a bit complicated, and could
# easily be overriden by accident by the user, so just delete those
# other headers here so that a client can't set them
# incorrectly/maliciously.
proxy_set_header X-Forwarded-Protocol "";
proxy_set_header X-Forwarded-Ssl "";
}
}

0 comments on commit e0d2fb8

Please sign in to comment.