The Math and Code Behind…
Facial alignment is a prereq…
3 years, 4 months ago
by Sabbir Ahmed
"Docker is Inception for Linux", is how I like to put it when it comes to explaining docker. Cause docker is exactly like this, a Linux inside a Linux sharing the host kernel. For any compassionate Linux user, can learn docker in a matter of days. In this article, I will discuss dockerizing a complete Django app in a production environment (Nginx, PostgreSQL, and Gunicorn) with docker-compose. So, let's begin-
Fulfilling the prerequisites
That's it. Now,
Get the code - by-sabbir/django-gunicorn-nginx-docker
Create a .env file at the root (the same directory that contains docker-compose.yml) and edit as following -
SECRET_KEY="YOUR SECRET KEY" DB_HOST="db" DB_PASSWORD="hello" #from docker-compose.yml DB_USER="postgres" #from docker-compose.yml DB_NAME="postgres" #from docker-compose.yml DB_PORT="5432" #from docker-compose.yml DB_ENGINE="django.db.backends.postgresql"
The system will be up and running with those two following commands
docker-compose build
This will download all the dependencies and docker images from the docker hub
docker-compose up -d
Last we have to copy the static files to our specified directory at settings.py, here's how we'll do it,
docker-compose exec web python manage.py collectstatic --no-input
Hopefully, now your project will be up and running by now and you can access it at localhost:8008 like below. If not use docker-compose logs -f
for debugging purposes.
The Design/Thinking Process
Basically, the problem is to run a Django app inside docker in a production setup, which means with Nginx as HTTP server, Postgresql as DB server. Here's my plan - I will run the App, the HTTP server, and the DB server in separate containers makes it more manageable and robust. The App will be run on Gunicorn, later will user Nginx's proxypass. We need a shared volume for the HTTP server and the App for static and media file hosting and a persistent filesystem for DB. All the secrets are going to be stored in a totally separated .env file. Let's sum up-
Let's see the web service:
web: build: . container_name: postgres_deploy_web command: gunicorn app.wsgi:application --bind 0.0.0.0:8000 volumes: - ./app:/app/ - staticfiles:/app/static/ expose: - 8000 env_file: - ./.env depends_on: - db
the second line 'build .' means it will create a docker image from the root Dockerfile, let's take a look at the file,
FROM python:lapostgres ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 RUN mkdir /app WORKDIR /app RUN pip install --upgrade pip COPY requirements.txt . RUN pip install -r requirements.txt COPY ./app /app
Farley simple enough to understand. Let's move on to the next phase, the DB
db: image: postgres container_name: postgres_deploy_db volumes: - postgres_data:/var/lib/postgresql/data/ environment: - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=hello
We will just use the official Postgres docker image and postgres_data is the persistent data volume within docker. It should suffice.
Now, Nginx. You can do whatever you want with it. Nginx really brings you the ultimate power.
nginx: build: ./nginx container_name: postgres_deploy_nginx volumes: - staticfiles:/app/static/ ports: - 8008:80 depends_on: - web
So, let's dig a bit with these compose configs. The second line indicates it will compile whatever Dockerfile is in the 'nginx' folder. Let's follow the clue -
FROM nginx:1.19.0-alpine RUN mkdir /app RUN rm /etc/nginx/conf.d/default.conf COPY nginx.conf /etc/nginx/conf.d/ WORKDIR /app
Wow, that's easy. Simply, We just deleted the default config file and replaced it with our own. The question is what is in our config file, well let's see a bit simplified version -
upstream app { server web:8000; } server { listen 80; location / { proxy_pass http://app; } location /static/ { alias /app/static/; } }
upstream configure proxy request server for Nginx. Gunicorn is a Python WSGI server for our Django app. In the first location derivative, we proxy passed the '/' root location from the app to the HTTP server. The second location derivative is just an alias for static files. Now, we can get creative with the Nginx server.
This is it, I guess. Let me know if I missed anything. The Readme has more detailed commands. Feel free you alter the codes, And I will be checking Github issues, see you there.