This article will cover creating a scalable Python Flask web application on Ubuntu 18.04 using Gunicorn and NGINX. The web application also leverages Virtualenv to provide an isolated Python environment and Systemd as a service manager. We loosely are defining web application to mean serving up a web page via a GET request, however this blog can be used as an example to build a website, Restful API, or any number of things.
*This is an updated approach to an older blog post of mine How to make a Scalable Python Web App using Flask, Gunicorn, NGINX on Ubuntu 14.04 from 2015.
I have created a GitHub repo scalable-ubuntu-flask-gunicorn-nginx available for you to get started quickly with a setup.sh file that does all the steps below via one bash script.
If you would like to do this step by step please continue reading below:
Assumptions:
Step 0
Update your Ubuntu packages
sudo apt update
Step 1
Time Matters! Sync’ing time is important all servers drift which can effect applications negatively.
sudo apt -y install ntpdate
sudo ntpdate pool.ntp.org
sudo apt -y install ntp
Step 2
Install Python Dependencies and Virtualenv, you will be installing Python 3 and leveraging Virtualenv to have an isolated environment for your Python dependencies specific to your application.
sudo apt -y install build-essential openssl
sudo apt -y install libpq-dev libssl-dev libffi-dev zlib1g-dev
sudo apt -y install python3-pip python3-dev
sudo pip3 install virtualenvwrapper
Step 3
Install NGINX, it will act as a reverse proxy for scaling purposes.
sudo apt -y install nginx
Step 4
Configure your .bashrc file so that your ubuntu user can use Virtualenv
vim ~/.bashrc
Add the following to the bottom of your .bashrc file
# default location of virtual environment directories
export WORKON_HOME=$HOME/.virtualenvs
# default python version to use with virtualenv
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
export VIRTUALENVWRAPPER_VIRTUALENV_ARGS=' -p /usr/bin/python3 '
source `which virtualenvwrapper.sh`
Step 5
Source your .bashrc file to activate your changes
source ~/.bashrc
Step 6
Create a directory to keep applications in
mkdir /home/ubuntu/flask_apps
cd /home/ubuntu/flask_apps
Step 7
Create your Virtualenv project environment named app01_env
virtualenv app01_env
Step 8
Activate your Virtualenv and install Python packages for your application specific to the new project environment. The three packages listed are gunicorn (webserver), flask (Python microframework), and setproctitle (to view your processes friendly name when using ps). After use deactivate to remove yourself from this environment.
source app01_env/bin/activate
pip install gunicorn
pip install flask
pip install setproctitle
deactivate
Step 9
Create your Python Flask application that says Hello World
vim /home/ubuntu/flask_apps/app01_env/app01.py
app01.py
from flask import Flask
hello = Flask(__name__)
@hello.route("/")
def greeting():
return "Hello World!
"
if __name__ == "__main__":
hello.run(host='0.0.0.0')
Step 10
Create your webserver gateway interface for the Flask microframework
vim /home/ubuntu/flask_apps/app01_env/wsgi.py
wsgi.py
from app01 import hello
if __name__ == "__main__":
hello.run()
Step 11
Create your Gunicorn config file which holds your Gunicorn settings. Gunicorn is essentially your webserver for your application. The below settings are optimized for a dual core server.
vim /home/ubuntu/flask_apps/app01_env/gunicorn_config.py
gunicorn.py
pidfile = 'app01.pid'
worker_class = 'gthread'
workers = 5
worker_connections = 1000
timeout = 30
keepalive = 2
threads = 2
proc_name = 'app01'
bind = '0.0.0.0:8080'
backlog = 2048
accesslog = 'access.log'
errorlog = 'error.log'
user = 'ubuntu'
group = 'ubuntu'
Step 12
Create a Systemd service file. Systemd is a service manager and in this use case it will manage Gunicorn.
sudo vim /etc/systemd/system/app01.service
app01.service
[Unit]
Description=Gunicorn for Flask app01
After=network.target
[Service]
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu/flask_apps/app01_env
Environment="PATH=/home/ubuntu/flask_apps/app01_env/bin"
ExecStart=/home/ubuntu/flask_apps/app01_env/bin/gunicorn --config=gunicorn_config.py wsgi:hello
[Install]
WantedBy=multi-user.target
Step 13
Start your application via Systemd and enable it to auto start after reboot
sudo systemctl start app01
sudo systemctl enable app01.service
Step 14
Create your applications NGINX config that makes NGINX listen on port 80 and proxies requests to Gunicorn on port 8080. NGINX will help with connection handling increasing performance.
sudo vim /etc/nginx/sites-available/nginx_app01.conf
nginx_app01.conf
server {
listen 80 default_server;
location / {
include proxy_params;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_buffers 8 24k;
proxy_buffer_size 4k;
proxy_pass http://127.0.0.1:8080;
}
}
Step 15
Delete your default NGINX conf file, enable your new NGINX conf and restart NGINX
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/nginx_app01.conf /etc/nginx/sites-enabled
sudo systemctl restart nginx
Step 16
If the server or instance you installed this on has port 80 available for you to reach. Enter the server IP or hostname into your browser and you should see “Hello World!” in red, which tells you your Python Flask application is working.
Performance Settings