Deploy Django and Flask Applications in the cloud using Nginx, Gunicorn and Supervisor (Ubuntu 18.04)
Every developer always reaches that point where they’ve built an app and want it to be tested and used by the end user. This article shows how to deploy Flask or Django Applications on a VPS(Virtual Private Server) using Nginx, Gunicorn and Supervisor to manage the deployment.
Prerequisites
You’ll need a VPS. You can get one on AWS or GCP. I’ll be using AWS in this article. You can refer to my article on setting up a VPS on AWS and also configuring a swap space for your VPS.
Step 1: Install Packages
Connect to your VPS. First of all, we’ll run an update on the instance.
$ sudo apt-get update && sudo apt-get -y upgrade
Next we install python3.6 on the instance if not already installed.
$ sudo apt-get install python3.6
Next we install Nginx. Nginx is a web server which can also be used as a reverse proxy, load balancer, mail proxy and HTTP cache.
$ sudo apt-get -y install nginx
Once Nginx is installed, it starts running. You can check if it has successfully installed by visiting the public IP address of your VPS. On AWS you can get it on the EC2 Instances page.
Once you get the IP address, go to http://<IP address>. Make sure it’s http and not https. https will return an error because ssl has not been set on the server.
Install Supervisor next. Supervisor is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems.
$ sudo apt-get -y install supervisor $ sudo systemctl enable supervisor
$ sudo systemctl start supervisor
Step 2: Setup your Django/Flask Application
Now that you’ve installed the required applications, you need to get your app source code into the VPS. We are going to use Github here. The app being deployed is a Django app that I contributed on. We are going to clone it into the VPS. You can view it here.
$ git clone https://github.com/mohitpandeyji/askai.ga.git
Enter the App directory to complete the setup from the README of the repo:
$ cd askai.ga
On the README, conda was used in creating the virtual environments but we won’t be using that here, we’ll be using the python package venv in creating the virtual environment:
$ python3 -m venv env
Activate the virtual environment:
$ source env/bin/activate
Install python packages/dependencies required by the app and Gunicorn:
(env) $ pip3 install -r requirements.txt(env) $ pip3 install gunicorn
Follow the other instructions from the README:
(env) $ python3 manage.py makemigrations(env) $ python3 manage.py migrate
If we try running the application now, we won’t be able to visit it because we’ve not created a path for it to the outside world. This is what Nginx is for. So let’s go ahead and configure Nginx.
Step 3: Setup Nginx
Having an app listen on port 80
requires special configuration. That is because ports below 1024 are protected by the operating system. We are going to use Nginx. to proxy outside requests from port 80
to the internal port 8000
, where the app is running.
Add a new configuration file named app inside /etc/nginx/sites-available/:
(env) $ sudo nano /etc/nginx/sites-available/app
Copy the following as the contents of this configuration file:
upstream app_server {
server 127.0.0.1:8000;
}
server {
listen 80;
# server_name paygo.cf; keepalive_timeout 5;
client_max_body_size 4G;
access_log /home/ubuntu/logs/nginx-access.log;
error_log /home/ubuntu/logs/nginx-error.log;
location /static/ {
alias /home/ubuntu/askai.ga/static/;
}
# checks for static file, if not found proxy to app
location / {
try_files $uri @proxy_to_app;
}
location @proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
}
The line with server_name paygo.cf;
will be uncommented once you get your domain name and paygo.cf
will be replaced with your domain name. You can refer to this article on how to get your domain name.
Save and close the file: press ctrl+x, type y
and press Enter.
Create a folder where logs will be stored in the home directory:
(env) $ cd ~(env) $ mkdir logs
Create a symbolic link to the sites-enabled folder:
(env) $ sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/app
Remove the default NGINX website:
(env) $ sudo rm /etc/nginx/sites-enabled/default
Restart the NGINX service:
(env) $ sudo service nginx restart
With Nginx running now you can start your django app:
(env) $ cd askai.ga(env) $ python3 manage.py runserver// [You can also run the app by using gunicorn] //(env) $ gunicorn openvino_api.wsgi:application
At this point, the app should be available on the URL http://<IP address>. Remember you’ve not yet set ssl so don’t use https yet. If you close your command line window, the running gunicorn or python command gets terminated. The web server is not running anymore, and therefore not reachable anymore on <IP address>
. Does this mean that you have to keep your personal computer and the command line open all the time? Of course not, you can keep the process running in background. One way is to make use of Supervisor in creating a background process which we’ll discuss.
Stop the application: press ctrl+c.
Step 4: Configure Gunicorn
So, Gunicorn is the one responsible for executing the Django code behind a proxy server. Create a new file named gunicorn_start inside /home/ubuntu:
(env) $ nano /home/ubuntu/gunicorn_start
Copy the following as the contents of this script. This script will start the application server. We are providing some information such as where the Django project is, which application user to be used to run the server, and so on.
#!/bin/bash
NAME="app"
DIR=/home/ubuntu/askai.ga
USER=ubuntu
GROUP=ubuntu
WORKERS=2
BIND=unix:/home/ubuntu/run/gunicorn.sock
DJANGO_SETTINGS_MODULE=openvino_api.settings
DJANGO_WSGI_MODULE=openvino_api.wsgi
LOG_LEVEL=error
cd $DIR
source env/bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DIR:$PYTHONPATH
exec env/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
--workers $WORKERS \
--user=$USER \
--group=$GROUP \
--bind=$BIND \
--log-level=$LOG_LEVEL \
--log-file=-
Save and close the file: press ctrl+x, type y
and press Enter.
Now make this file executable:
(env) $ cd ~(env) $ sudo chmod u+x gunicorn_start
Create a folder for the socket file:
(env) $ mkdir run
Step 5: Configure Supervisor
First, create an empty log file inside the /home/ubuntu/logs/ folder:
(env) $ cd ~(env) $ touch logs/gunicorn.log
Now create a new supervisor file:
(env) $ sudo nano /etc/supervisor/conf.d/app.conf
Note: Supervisor does not load environment variables when running a job or service. Because of this issue you’ll have to load the environment variables manually in the supervisor script. This app needs some specific environment variables so we’ll load them in the supervisor script. Copy the following as the contents of the script:
[program:app]
command=/home/ubuntu/gunicorn_start
environment=PYTHONPATH=/opt/intel/openvino_2019.3.376/python/python3.6:/opt/intel/openvino_2019.3.376/python/python3:/opt/intel/openvino_2019.3.376/deployment_tools/open_model_zoo/tools/accuracy_checker:/opt/intel/openvino_2019.3.376/deployment_tools/model_optimizer:,PATH=%(ENV_PATH)s,LD_LIBRARY_PATH=/opt/intel/openvino_2019.3.376/opencv/lib:/opt/intel/opencl:/opt/intel/openvino_2019.3.376/deployment_tools/inference_engine/external/hddl/lib:/opt/intel/openvino_2019.3.376/deployment_tools/inference_engine/external/gna/lib:/opt/intel/openvino_2019.3.376/deployment_tools/inference_engine/external/mkltiny_lnx/lib:/opt/intel/openvino_2019.3.376/deployment_tools/inference_engine/external/tbb/lib:/opt/intel/openvino_2019.3.376/deployment_tools/inference_engine/lib/intel64:/opt/intel/openvino_2019.3.376/openvx/lib
user=ubuntu
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/home/ubuntu/logs/gunicorn.log
Save and close the file: press ctrl+x, type y
and press Enter.
To get the environment variables, the echo command is used to display them:
Run the commands below:
(env) $ sudo supervisorctl reread
(env) $ sudo supervisorctl update
Now check the status:
(env) $ sudo supervisorctl status appOutput
app RUNNING pid 308, uptime 0:00:07
If changes are made in your source code through Github and a git pull
is made or through an editor while the gunicorn service is running, you’ll need to update the gunicorn service through supervisor so that the changes will reflect. To do that run this command:
(env) $ sudo supervisorctl restart app
With supervisor running the gunicorn script, we need to update the Nginx configuration file to make use of the gunicorn socket file. Open your configuration file named app inside /etc/nginx/sites-available/:
(env) $ sudo nano /etc/nginx/sites-available/app
Edit the upstream app_server
block:
// Remove
server 127.0.0.1:8000;// Replace with
server unix:/home/ubuntu/run/gunicorn.sock fail_timeout=0;
Restart the NGINX service:
(env) $ sudo service nginx restart
At this point, the app should be available on the URL http://<IP address>. Remember you’ve not yet set ssl so don’t use https yet.
Congrats!!!
You have completed all required installation, configuration and build steps in this guide to use Gunicorn, Supervisor and Nginx in running your Django/Flask applications. You can close your terminal window and your app will keep running. You can now host your applications in the cloud.
You can refer to this article on how to get your domain name and set ssl on your server.
REFERENCE Links:
1) Linux material : http://linuxcommand.org/lc3_learning_the_shell.php
2) Django Deployment : https://simpleisbetterthancomplex.com/series/2017/10/16/a-complete-beginners-guide-to-django-part-7.html
3) Flask Deployment: https://www.markusdosch.com/2019/03/how-to-deploy-a-python-flask-application-to-the-web-with-google-cloud-platform-for-free/