When developing (settings.DEBUG = True
), django serves up static content. If we are only running
our API service, there are no static files to serve as the only content is JSON, however, with the
addition of the openapi documentation and
Swagger-UI
we will need to collect and serve up the static files with a web server.
Following are some examples of how to do this with a Docker setup and with Apache httpd.
We'll make a Docker image for myapp
with:
docker build -t myapp:latest .
But there are a number of extra steps, so I've added them to the tox.ini. Just do a tox -e docker
and the right
thing will happen. Here's what I've added which:
- bundles openapi.json
- builds a new wheel.
- builds the docker image.
- saves the image as a tar file so it can be
docker image load
ed elsewhere if needed.
+[testenv:docker]
+deps =
+whitelist_externals =
+ docker
+ npm
+ swagger-ui-watcher
+setenv =
+ DJANGO_SETTINGS_MODULE = training.settings
+commands =
+ npm install swagger-ui-watcher
+ /bin/cp docs/schemas/openapi.json .
+ /bin/rm -rf dist
+ python setup.py bdist_wheel
+ docker build -t myapp:latest .
+ docker image save -o myapp-docker.tar myapp:latest
+ /usr/bin/printf '\n\033[0;31m copy the tar to the docker host and do docker image load -i myapp-docker.tar\033[0m\n'
+
We'll use docker-compose
to combine an nginx web server and our django app (served by gunicorn)
using traefik
as the "edge router" to allow them to both listen on various paths below https://localhost/
(technically this could
be done with Apache httpd or nginx too, but traefik does some docker magic).
See docker-compose.yml
for the gory details.
Just do a docker-compose up
and away you go (I've used --no-start --force-recreate and start/stop):
(env) django-training$ docker-compose up --no-start --force-recreate
Recreating django-training_nginx_1 ... done
Recreating django-training_demoapp_1 ... done
Recreating django-training_traefik_1 ... done
(env) django-training$ docker-compose start
Starting nginx ... done
Starting demoapp ... done
Starting traefik ... done
(env) django-training$ docker-compose stop
Stopping django-training_traefik_1 ... done
Stopping django-training_demoapp_1 ... done
Stopping django-training_nginx_1 ... done
(env) django-training$
Our preferred environment for running Django apps is with mod_wsgi
under Apache httpd. At CUIT,
this is generally done with Puppet. For simplicity, I'll just show some sample files:
/etc/httpd/conf.d/wsgi.conf
:
# The WSGI Apache module configuration file is being
# managed by Puppet. Any changes will be overwritten.
<IfModule mod_wsgi.c>
WSGISocketPrefix /var/run/wsgi
WSGIPythonHome "/var/www/django-jsonapi-training/env"
WSGIPythonPath "/var/www/django-jsonapi-training/env/lib/python3.6/site-packages"
</IfModule>
/etc/httpd/conf.d/wsgi.load
:
LoadModule wsgi_module modules/mod_wsgi_python3.6.so
/etc/httpd/conf.d/10-myserver.cc.columbia.edu
:
# ************************************
# Vhost template in module puppetlabs-apache
# Managed by Puppet
# ************************************
<VirtualHost *:8443>
ServerName myserver.cc.columbia.edu
## Vhost docroot
DocumentRoot "/var/www/django-jsonapi-training"
## Directories, there should at least be a declaration for /var/www/django-jsonapi-training
<Directory "/var/www/django-jsonapi-training">
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
Allow from all
</Directory>
## Logging
ErrorLog "/var/log/httpd/myserver.cc.columbia.edu_error_ssl.log"
ServerSignature Off
CustomLog "/var/log/httpd/myserver.cc.columbia.edu_access_ssl.log" "virtualhost_snat"
## SSL directives
SSLEngine on
SSLCertificateFile "/etc/pki/tls/certs/localhost.crt"
SSLCertificateKeyFile "/etc/pki/tls/private/localhost.key"
SSLCertificateChainFile "/etc/pki/tls/certs/localhost.csr"
SSLCACertificatePath "/etc/pki/tls/certs"
SSLCACertificateFile "/etc/pki/tls/certs/intermediateCAbundle.crt"
WSGIDaemonProcess django-jsonapi-training
WSGIProcessGroup django-jsonapi-training
WSGIScriptAlias / "/var/www/django-jsonapi-training/wsgi.py"
WSGIPassAuthorization On
WSGIChunkedRequest On
</VirtualHost>
and /var/www/django-jsonapi-training/wsgi.py
:
"""
Generated by Puppet. DO NOT EDIT.
WSGI config for django-jsonapi-training project.
It exposes the WSGI callable as a module-level variable named ``application``.
https://modwsgi.readthedocs.io/en/develop/user-guides/virtual-environments.html
"""
import sys
import site
import os
# Calculate path to site-packages directory.
python_home = "/var/www/django-jsonapi-training/env"
python_version = ".".join(map(str, sys.version_info[:2]))
site_packages = python_home + "/lib/python%s/site-packages" % python_version
# Add the site-packages directory.
site.addsitedir(site_packages)
from django.core.wsgi import get_wsgi_application
os.environ["DJANGO_SETTINGS_MODULE"] = "training.settings"
os.environ["DJANGO_SECRET_KEY"] = "123456789012345687890"
os.environ["DJANGO_DEBUG"] = "false"
os.environ["DJANGO_SQLSERVER"] = "true"
os.environ["DJANGO_SQLSERVER_DB"] = "mydb"
os.environ["DJANGO_SQLSERVER_USER"] = "myuser"
os.environ["DJANGO_SQLSERVER_PASS"] = "mypass"
os.environ["DJANGO_SQLSERVER_HOST"] = "mydbhost"
os.environ["OAUTH2_SERVER"] = "https://oauth-test.cc.columbia.edu"
os.environ["RESOURCE_SERVER_ID"] = "demo_resource_server"
os.environ["RESOURCE_SERVER_SECRET"] = "wL0pgS5RcNOgdOSSmejzZNA605d3MtkoXMVSDaJxmaTU70XnYQPOabBAYtfkWXay"
application = get_wsgi_application()
Make careful note to have WSGIPassAuthorization On
or the Authorization
header will not be
passed through to the Django app. Alternatively, look into
mod_auth_openidc
and use the REMOTE_USER
. I have not
tested this approach. It depends on whether your backend server needs to introspect the Bearer token.