Deployment
The following example documents one way to deploy Comics. As Comics is a standard Django project with an additional batch job for crawling, it may be deployed in just about any way a Django project may be deployed. Please refer to Django’s deployment documentation for further details.
In the following examples we assume that we are deploying Comics at
http://comics.example.com/, using Nginx, Gunicorn, and PostgreSQL. The Django
application and batch job is both running as the user comics-user
. The
static media files, like comic images, are served from
http://comics.example.com/static/.
Database
Comics should theoretically work with any database supported by Django. Though, development is mostly done on SQLite and PostgreSQL. For production use, PostgreSQL is the recommended choice.
Note
If you are going to use SQLite in a deployment with Nginx and so on, you need to ensure that the user the web server will be running as has write access to the directory the SQLite database file is located in.
Example .env
In the following examples, we assume the Comics source code is unpacked at
/srv/comics.example.com/app/comics
.
To change settings, you should not change the settings files shipped with
Comics, but instead override the settings using environment variables, or by
creating a file named /srv/comics.example.com/app/comics/.env
. You must
at least set DJANGO_SECRET_KEY
and database settings, unless you use
SQLite.
A full set of environment variables for a production deployment may look like this:
DJANGO_SECRET_KEY=replace-this-with-a-long-random-value
DJANGO_ADMIN=comics@example.com
DJANGO_DEFAULT_FROM_EMAIL=comics@example.com
DJANGO_MEDIA_ROOT=/srv/comics.example.com/htdocs/static/media/
DJANGO_MEDIA_URL=https://comics.example.com/static/media/
DJANGO_STATIC_ROOT=/srv/comics.example.com/htdocs/static/
DJANGO_STATIC_URL=https://comics.example.com/static/
DATABASE_URL=postgres://comics:topsecret-password@localhost:5432/comics
CACHE_URL=memcache://127.0.0.1:11211
COMICS_LOG_FILENAME=/srv/comics.example.com/app/log/comics.log
COMICS_SITE_TITLE=comics.example.com
COMICS_INVITE_MODE=true
Of course, you should change most, if not all, of these settings to fit your own installation.
If your are not running a memcached
server, remove CACHE_URL
variable
from your environment. Comics does not require a cache, but responses are
significantly faster with a cache available.
Example Gunicorn setup
Comics is a WSGI app and can be run with any WSGI server, for example Gunicorn. Gunicorn is a Python program, so you can simply install it in Comics’ own virtualenv:
source /srv/comics.example.com/app/venv/bin/activate
python -m pip install gunicorn
Then you need to start Gunicorn, for example with a systemd service:
[Unit]
Description=gunicorn-comics
After=network.target
[Install]
WantedBy=multi-user.target
[Service]
User=comics-user
Group=comics-user
Restart=always
ExecStart=/srv/comics.example.com/app/venv/bin/gunicorn --bind=127.0.0.1:8000 --workers=9 --access-logfile=/srv/comics.example.com/htlogs/gunicorn-access.log --error-logfile=/srv/comics.example.com/htlogs/gunicorn-error.log comics.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
WorkingDirectory=/srv/comics.example.com/app/comics
Environment=PYTHONPATH='/srv/comics.example.com/app/comics'
PrivateTmp=true
Example Nginx vhost
The web server Nginx can be used in front of Gunicorn to terminate HTTPS connections and effectively serve static files.
The following is an example of a complete Nginx vhost:
server {
server_name comics.example.com;
listen 443 ssl http2;
listen [::]:443 ssl http2;
access_log /srv/comics.example.com/htlogs/nginx-access.log;
error_log /srv/comics.example.com/htlogs/nginx-error.log error;
ssl_certificate /etc/letsencrypt/live/comics.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/comics.example.com/privkey.pem;
location /static {
root /srv/comics.example.com/htdocs;
expires max;
location ~* \/fonts\/ {
add_header Access-Control-Allow-Origin *;
}
}
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Scheme $scheme;
proxy_connect_timeout 10;
proxy_read_timeout 30;
proxy_pass http://localhost:8000/;
}
}
For details, please refer to the documentation of the Nginx project.
Collecting static files
When you’re not running in development mode, you’ll need to collect the static
files from all apps into the STATIC_ROOT
. To do this, run:
python manage.py collectstatic
You have to rerun this command every time you deploy changes to graphics, CSS and JavaScript. For more details, see the Django documentation on staticfiles.
Example cronjob
To get new comics releases, you should run comics_getreleases
regularly. In
addition, you should run clearsessions
to clear expired user sessions.
One way is to use cron
e.g. by placing the following in
/etc/cron.d/comics
:
MAILTO=comics@example.com
PYTHONPATH=/srv/comics.example.com/app/comics
1 * * * * comics-user python /srv/comics.example.com/app/comics/manage.py comics_getreleases -v0
1 3 * * * comics-user python /srv/comics.example.com/app/comics/manage.py clearsessions -v0
If you have installed Comics’ dependencies in a virtualenv instead of
globally, the cronjob must also activate the virtualenv. This can be done by
using the python
interpreter from the virtualenv:
MAILTO=comics@example.com
PYTHONPATH=/srv/comics.example.com/app/comics
1 * * * * comics-user /srv/comics.example.com/app/venv/bin/python /srv/comics.example.com/app/comics/manage.py comics_getreleases -v0
1 3 * * * comics-user /srv/comics.example.com/app/venv/bin/python /srv/comics.example.com/app/comics/manage.py clearsessions -v0
By setting MAILTO
any exceptions raised by the comic crawlers will be sent
by mail to the given mail address. 1 * * * *
specifies that the command
should be run 1 minute past every hour.