In this post i will try to run the Django app we developed in this post using Gunicorn and serve static files using NGINX.
So what is …?
1. NGINX
NGINX is a high-performance Load balancer, a web server which can also be used as a reverse proxy, load balancer, mail proxy, and HTTP cache. It is free and opensource. Some high-profile companies using NGINX include Autodesk, Atlassian, Intuit, T-Mobile, GitLab, DuckDuckGo, Microsoft, IBM, Google, Adobe, Salesforce, VMWare, Xerox, LinkedIn, Cisco, Facebook, Target, Citrix Systems, Twitter, Apple, Intel, and many more. NGINX is made from ground up to offer low memory usage and high concurrency. Rather than starting new processes for each web request, NGINX uses an asynchronous, event-driven approach where requests are handled in a single thread. you can read more about NGINX here.
Gunicorn
It is a Python WSGI HTTP Server for UNIX, ported from Ruby’s Unicorn project. It is simple, light on server resources and fairly fast. It natively supports WSGI, web2py, Django and Paster. Please see the official Gunicorn docs for more information.
Enough with the definitions, let us start.
Modify your requirements.txt as below.
Django==2.1.5
mysqlclient==1.3.14
gunicorn==19.9.0
then, in your docker-compose file replace the command
python3 manage.py runserver 0.0.0.0:8000
with
gunicorn --timeout=30 --workers=2 --bind 0.0.0.0:8000 django_app.wsgi:application
What this means is that spawn 2 workers, which will timeout after 30 seconds of inactivity. Bind to the socket 8000. The general rule of thumb regarding the number of workers is 2 x number of cores.
You need to rebuild the docker image using the command
docker-compose build
and now run
docker-compose up
and navigate to “http://localhost:3000/polls/“. you should see the pools index page.
But wait for a second, You will see that none of the static files are loading. The reason is that Gunicorn is an application server and it does not serve static files. In order to resolve this issue, we will need NGINX and use it as a reverse proxy for Gunicorn. Nginx also improves performance, reliability, security, and scale. Simply, HTTP requests will be handled by Gunicorn and static ones by Nginx.
So, let’s add NGINX to our stack. For this, we will need to modify the docker-compose to include NGINX like below.
version: "3"
services:
app:
restart: always
build: .
command: bash -c "python3 manage.py collectstatic --no-input && python3 manage.py migrate && gunicorn --timeout=30 --workers=2 --bind :8000 django_app.wsgi:application"
volumes:
- .:/code
- static-volume:/code/static
expose:
- "8000"
depends_on:
- db
networks:
- web_network
- db_network
db:
image: mysql:latest
command: mysqld --default-authentication-plugin=mysql_native_password
volumes:
- "./mysql:/var/lib/mysql"
ports:
- "3306:3306"
restart: always
environment:
- MYSQL_ROOT_PASSWORD=secret123
- MYSQL_DATABASE=django_app
- MYSQL_USER=django_app
- MYSQL_PASSWORD=django_app123
networks:
- db_network
nginx:
image: nginx:latest
ports:
- "3000:8000"
volumes:
- static-volume:/code/static
- ./config/nginx:/etc/nginx/conf.d
depends_on:
- app
networks:
- web_network
networks:
web_network:
driver: bridge
db_network:
driver: bridge
volumes:
static-volume:
It simply means there are three services for this project: nginx, app, db.
nginx- contains the latest image from docker hub, which is available at port 3000
Now we need an NGINX config file. Create a folder “config/nginx” and add a file “djangoapp.conf”
upstream app {
ip_hash;
server app:8000;
}
server {
location /static/ {
autoindex on;
alias /code/static/;
}
location / {
proxy_pass http://app/;
}
listen 8000;
server_name localhost;
}
make sure you modify the “STATIC_ROOT ” in Django settings.py
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_URL = '/static/'
We will then simply rebuild and run Docker Compose to start our djangoapp
docker-compose build
docker-compose up