A brief overview of WSGI and usage of Gunicorn and Nginx in Django
This article will briefly explain the functionality and usage of WSGI, Gunicorn and Nginx in collaboration with Django. It will help you to understand the process of deploying Django applications on the server and will clarify the role of WSGI file.
Let's start this by creating a simple Django server. The first step is to install python dependencies. We would only need Django and Gunicorn.
$ pip install django gunicorn
Second step is to create a Django project:
$ django-admin startproject django_server_application .
The next step that most of the Django developers normally do on local environment is to run the Django project with the following command in the terminal.
$ python manage.py runserver
It's a management command built in Django and will simplify the process of running development server in no time. This server is only suitable for local development and is not a wise option to run application on production environment. We won't dig deep into risks and vulnerabilities here and Django documentation also warns about it's implications.
runserver management command is neither scalable nor reliable. It's design to run servers instantly for local development. So, instead of using this management command, we’re going to use Gunicorn to run our Django application.
Gunicorn and WSGI
Gunicorn is a library that is battle-tested, reliable and easily scalable by creating multiple workers. It's a WSGI server and it's capable to run any web application that supports WSGI like Django or Flask applications.
So what exactly is WSGI?
It's protocol and standard of communication between a web server and web application. In this example, Gunicorn is the web server while Django and Flask can play the role of web applications. WSGI is like a common language of communication between web servers and web applications. Web server needs a way to communicate messages with web applications and web applications should know how to respond to web server requests. So WSGI is the common medium to support this 2 way communication.
Let's create a python application that would be compatible with WSGI. WSGI expects a callable object that takes 2 arguments and it should return an iterable of strings.
Here is the simplified example of WSGI compatible python script:
# hello_world.py
def process_http_request(environ, start_response):
? ? status = '200 OK'
? ? response_headers = [
? ? ? ? ('Content-type', 'text/plain; charset=utf-8'),
? ? ]
? ? start_response(status, response_headers)
? ? text = 'Hello World'.encode('utf-8')
? ? return [text]
Now save this script file and specify it's path to the below Gunicorn server command in order to test our application script:
$ gunicorn hello_world:process_http_request --bind 127.0.0.1:8000
Now open this URL in your browser:
You would a see blank page with a 'Hello World' response. Congratulation you've successfully deployed a web application on a WSGI compatible Gunicorn server.
Let's talk about the arguments of our running script. The first argument is environ which is a dictionary that contains information about the request sent by the browser to our server. Whenever a request is made, Gunicorn gets the request, it populates the dictionary, and when it calls the function process_http_request, it passes this dictionary as a parameter.
The second argument start_response is a function that we need to call if we want to send a status code and headers of the response.
Gunicorn & WSGI with Django
Whenever a Django project is created, a wsgi.py file is also generated automatically. Inside this file, there is a function named as get_wsgi_application.
领英推荐
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
application = get_wsgi_application()
Django calls this function to return a WSGI compatible callable object. This callable object also takes two arguments environ and start_response and similarly returns an iterable of strings.
This callable object is similar to the one that we implemented above but this one is much more complicated.
Let’s run Gunicorn WSGI server for our Django application:
$ gunicorn lifecycle.wsgi:application --bind 0.0.0.0:8000
Congratulations, now we're running our Django application on a WSGI server. This brings us one step closer to safely deploy our web application on a real server. Now let's take about Nginx server.
Nginx with Django
Nginx will behave as our interface server to the outside world. It will handle and process all the requests sent by by the browser.
It's time to bring together Nginx, Gunicorn and Django in action. Whenever the browser wants a dynamically generated content like an HTML page, Nginx will collect the request and forward it to Gunicorn and Django as Django is responsible for generating HTML pages. Once the processing is done, Gunicorn will take the HTML page generated by Django and will send it back to Nginx and Nginx will serve it back to the browser as a response.
We don't need Gunicorn and Django to process all the user requests. Whenever the browser will request some static content or media files, Nginx can directly server those files without involving Gunicorn and this will save our processing power of Django to serve necessary requests fast and better.
Now let’s try to quickly install Nginx and run a web server. Here’s the command to install Nginx:
$ sudo apt install nginx
Once the installation is done, we need to configure our firewall to allow incoming traffic on port 80 and port 443 if we wanna setup HTTPS. Let's just settle without HTTPS for now, so we'll open port 80 only.
$ sudo ufw allow 'Nginx HTTP'
Let’s create a configuration file:
$ vi /etc/nginx/sites-available/django_server_application
upstream server_django {
? ? server 0.0.0.0:8000;
}
server {
? ? listen 80;
? ? location / {
? ? ? ? proxy_pass https://server_django;
? ? ? ? proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
? ? ? ? proxy_set_header Host $host;
? ? ? ? proxy_redirect off;
? ? }
? ? location /static/ {
? ? ? ? alias /app/static/;
? ? }
}
In the above configurations, whenever the location will be root, Nginx will proxy requests to Gunicorn server and if the location is static, then Nginx will take the responsibility of serving static files without disturbing Gunicorn and Django.
With our new configuration file above, it's time to remove the default Nginx configuration file. Simply replace the default file with our newly created file in the sites-available folder of Nginx.
$ rm -rf /etc/nginx/sites-available/default
$ rm -rf /etc/nginx/sites-enabled/default
The next step is to create the soft link in the sites-enabled folder.
$ sudo ln -s /etc/nginx/sites-available/django_server_application /etc/nginx/sites-enabled/
Once the configuration is done, its time restart our Nginx server:
$ sudo systemctl restart nginx
Conclusion
So now you're in a better position to understand the core concepts of WSGI, Gunicorn and Nginx. If you have any questions, leave the comments below.
Gen AI and Automation Expert, 10% entrepreneur | Lead Software Engineer at @metatechpk
1 年As a Django learner and background of PHP development, I may have many questions. During my learning period I had read many articles but most of them are unable to promise what they say in title. This article and guide may be suitable for local machine, once we start launching python app on real server where you don't have root privileges and this might not be enough! I would love if you can write an article for that too, also considering the python virtual environment and workers. I really appreciate.
Python AI Engineer @alkushk | chatbots | AI Agents | ETL | Data Pipes | MLOps | Django, Flask, FastAPI | E-Commerce |ML | TensorFlow | Keras | Docker | K8s | AWS, Azure, GC | 5+ yrs GCC, Africa
2 年Thank You for sharing knowledge ??
Software Engineer at KMMRCE
2 年Great one Rashid Mahmood
Python - Tech Lead
2 年good info. Thanks!