Gunicorn in Docker
With Docker, our Django project is the only thing running. This means that we don't need gunicorn running in daemon mode and logging to files. We need gunicorn running in the foreground and logging to the console.
Setting up the gunicorn configuration
I found a great article with instructions on using gunicorn in Docker.
In summary, our Docker-specific gunicorn configuration is:
import os for k, v in os.environ.items(): if k.startswith("GUNICORN_"): key = k.split('_', 1).lower() locals()[key] = v
This allows us to specify the gunicorn configuration using environment variables.
Setting up the gunicorn logging
The same article includes instructions about setting up logging. We added
requirements.txt so the required library is included. Chances are, we will be changing all our logging to use this.
We added a
docker_gunicorn_conf.py file to the repo:
[loggers] keys=root, gunicorn.error [handlers] keys=console [formatters] keys=json [logger_root] level=INFO handlers=console [logger_gunicorn.error] level=ERROR handlers=console propagate=0 qualname=gunicorn.error [handler_console] class=StreamHandler formatter=json args=(sys.stdout, ) [formatter_json] class=jsonlogging.JSONFormatter
We know that we need to set at least these variables:
We will set their values in the
Spoiler alert! We will need a script that Docker will run as soon as it starts up the container. We want it to start gunicorn (and do a few other things), so we will create a new script:
#!/bin/bash python $HOMEDIR/manage.py collectstatic --noinput python $HOMEDIR/manage.py migrate --noinput #exec newrelic-admin run-program \ gunicorn \ --log-config $HOMEDIR/conf/gunicorn_logging.conf \ --config $HOMEDIR/conf/docker_gunicorn_conf.py \ conf.wsgi:application
chmod a+x docker-entrypoint.sh to make it executable.
In case you haven't guessed,
$HOMEDIR is set in the
Why are you running the
collectstatic command here? Good question! You should be proud of yourself for the amount of awareness you maintain.
collectstatic command was in the
Dockerfile, so it could be a part of the Docker image. However, whether it is by design, because of some of our code, or a third-party app, the command loads all the settings and apps and attempts to connect to the database when it runs. We can't have that.
At least it doesn't take long to run, and doesn't do anything if it has already run.
I'll explain more next time when I cover the
Why are you running the
migrate command here? Mostly because most of the example configurations for Docker and Django do this and I can't think of a better place to do it. Like the
collectstatic command, it really only needs to be run once for the image, no matter how many machines use it. And also like the
collectstatic command, we don't (necessarily) have a connection to the database to do this.
Also, it is a bit safer. In the worst case, a migration hasn't been applied, and the container will take a little longer to run the first time. In the best case, it doesn't need to do anything and starts up right away.
We'll conver creating the initial
Dockerfile and testing it out to see if what we have done works.