©Unsplash
Category: Hacks
Ship Django project as Docker image.
Recently I needed to deploy inherited Django project. My usual approach would be to ship it as docker container. Since python is not exactly my thing, I googled how to. Unfortunately there was no simple and clean tutorial. So I wrote it (with Gunincorn and Nginx to be production ready).
Tags: DIY.
Let's ship a Django project as a Docker image.
A couple of prerequisites before we start:- docker installed and running on your machine,
- since my tutorial is minimalistic, it is addressed to people with some experience.
You can begin with existing code, or create a new Django project:
django-admin startproject dockerdjangoIn my case file structure looks like this (before adding docker related code):
dockerdjango ├─appsample │ └... ├─dockerdjango │ ├... │ ├settings.py │ ├urls.py │ ├views.py │ └wsgi.py ├─static │ └... ├─templates │ └... ├─db.sglite3 ├─manage.py └─requirements.txtIn my approach, to build a Docker image we need 3 more files:
1. /Dockerfile (/ represents root of your project next to manage.py)
FROM python:3.11.3-slim-bullseye # install nginx RUN apt-get update && apt-get install nginx vim -y --no-install-recommends COPY nginx.site.conf /etc/nginx/sites-available/default RUN ln -sf /dev/stdout /var/log/nginx/access.log \ && ln -sf /dev/stderr /var/log/nginx/error.log # copy source and install dependencies RUN mkdir -p /opt/app RUN mkdir -p /opt/app/pip_cache RUN mkdir -p /opt/app/dockerdjango COPY requirements.txt server-up.sh /opt/app/ COPY .pip_cache /opt/app/pip_cache/ COPY dockerdjango /opt/app/dockerdjango/ COPY appsample /opt/app/appsample/ COPY static /opt/app/static/ # not default django templates folder (setup in settings.py) COPY templates /opt/app/templates/ WORKDIR /opt/app RUN pip install -r requirements.txt --cache-dir /opt/app/pip_cache RUN chown -R www-data:www-data /opt/app RUN chmod 755 server-up.sh # start server EXPOSE 8020 STOPSIGNAL SIGTERM CMD ["/opt/app/server-up.sh"]2. /nginx.site.conf
# nginx default site config for /etc/nginx/sites-available/default server { listen 8020; server_name example.org; location / { proxy_pass http://127.0.0.1:8010; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /static { root /opt/app; } }and bash script to run it
3. /server-up.sh
#!/usr/bin/env bash # start-up.sh # Start Gunicorn processes & start the Nginx process and put it in the background (gunicorn dockerdjango.wsgi --user www-data --bind 0.0.0.0:8010 --workers 3) & nginx -g "daemon off;" # more on gunicon https://docs.gunicorn.org/en/stable/deploy.html # more on nginx in container https://docs.nginx.com/nginx-management-suite/nim/previous-versions/v1/tutorials/containers/And a few updates to /djangodocker/settings.py
ALLOWED_HOSTS = ['*'] # not default STATICFILES_DIRS = [ # not default BASE_DIR / "static", ] CSRF_TRUSTED_ORIGINS = ['http://127.0.0.1:8020'] # not defaultYour /requirements.txt should contain at least:
django == 4.2.1 gunicorn == 20.1.0Now you're ready to build the image (type in terminal in root of your project where Dockerfile is located):
docker build -t yourfancytag .and run a container build out of the image:
docker run -it -p 8020:8020 --name dockerdjango yourfancytagYou should be able to access http://127.0.0.1:8020, in my project: /GitHub link below/ Later you can fire it without interactive terminal (-it)
docker run dockerdjango
Any issues?
First check your Dockerfile, it happens you're trying to copy or run something that does not exist in your project. Audit static and templates folders, they are not default ones.Still failing? Compare with my project, it is published on Github: https://github.com/msatbsx/dockerdjango or clone it (due to Cloudflare email obfuscation being unable to switch off in the line below please replace USER with git [at] github.com):
git clone USER:msatbsx/dockerdjango.gitYou can also build from my image and explore (it can be pulled from hub.docker.com):
docker run -d -p 8020:8020 --name dd msatbsx/dockerdjango:1When the container is already created, you can run it interactive and explore inside:
docker exec -it dd bash
Michal