NGINX and uWSGI for Flask-Philo app deployment

0. Install the tools you will need:

Install uWSGI:

sudo pip install uwsgi

Install NGINX:

sudo apt-get install nginx

1. Create a flask app using flask-philo:

To quickly generate a new Flask-Philo project, navigate to the directory in which you want to create the project and run:

flask-philo-admin startproject my_project

2. Create the WSGI Entry Point

Next, you will need to create a file that will serve as the entry point for your application. This will tell your uWSGI server how to interact with the application.

For example, you can call the file manage_uwsgi.py:

import os
import sys
import argparse


BASE_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(os.path.join(BASE_DIR, '../'))


from flask_philo import init_app

os.environ.setdefault('FLASK_PHILO_SETTINGS_MODULE', "config.development")

app = init_app(__name__, BASE_DIR)

3. Create a uWSGI Configuration File

In order to create something robust for long-term usage. You will create a uWSGI configuration file with some options.

[uwsgi]
pythonpath = <you_code_dir>/src/
wsgi-file = <you_code_dir>/src/manage_uwsgi.py
enable-threads = true
reload-on-rss=200
cpu-affinity = 1
reload-mercy = 20
max-requests = 5000
py-autoreload = 1
processes = 8
threads = 4
buffer-size = 95535
master = true
socket=/tmp/my_project.sock
chmod-socket = 660
plugin=python3
callable=app
harakiri = 40
vacuum = true
die-on-term = true
uid = www-data
gid = www-data

Some important observations can be made here:

  • processes = 8 refers to the number of worker processes that will serve actual requests;
  • Since you will be using NGINX to handle client connections that will pass requests to uWSGI and the components will operate on the same computer, you will use a Unix socket. Sockets will be a more secure and faster solution;
  • socket=/tmp/my_project.sock refers to the permissions on the socket, it’s set this way to allow NGINX to access it;
  • callable = app is the entry point into the application where the web server can call a functions with some parameters.

4. Configure NGNIX to proxy to uWSGI

Create the NGNIX config file in order to establish the connection between NGNIX web server to uWSGI. This connection will be made via socket. You can call this file default:

server {
            server_name                    <your_nginx_server_name>;
            listen                         80;
            rewrite                        ^ https://$server_name$request_uri? permanent;
            access_log                     off;
}

server {
            listen                          443  ssl;
            ssl_certificate                 /etc/nginx/ssl/nginx.crt;
            ssl_certificate_key             /etc/nginx/ssl/nginx.key;
            server_name                     <your_nginx_server_name>;
            underscores_in_headers          on;

            keepalive_timeout               0;
            keepalive_requests              1000;
            client_max_body_size            20m;
            client_body_buffer_size         128k;
            server_tokens                   off;
            sendfile                        on;
            uwsgi_buffer_size               264k;
            uwsgi_buffers                   8 264k;
            uwsgi_busy_buffers_size         264k;
            uwsgi_connect_timeout           600s;
            uwsgi_read_timeout              600s;
            uwsgi_send_timeout              600s;
            uwsgi_ignore_client_abort       on;
            uwsgi_intercept_errors          on;
            uwsgi_max_temp_file_size        1024m;

            location / {
                uwsgi_read_timeout          30s;
                include                     uwsgi_params;
                proxy_connect_timeout       30s;
                proxy_send_timeout          30s;
                proxy_read_timeout          30s;
                send_timeout                30s;
                uwsgi_pass                  unix:/tmp/my_project.sock;
                proxy_http_version          1.1;
                proxy_redirect              off;
            }
        }

        gzip_http_version                   1.1;
        gzip_vary                           on;
        gzip_comp_level                     6;
        gzip_proxied                        any;
        gzip_types                          application/javascript text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
        gzip_buffers                        16 8k;
        gzip_disable                        "MSIE [1-6].(?!.*SV1)";
        gzip_proxied                        expired no-cache no-store private auth;

You will need to create some config files to handle variables and make reference to them like this: <your_nginx_server_name>.

5. Using systemd service file to manage multiple applications:

systemd will be responsible to start, stop, and keep alive the processes needed. It also:

  • Provides aggressive parallelization capabilities
  • Uses socket and D-Bus activation for starting services
  • Offers on-demand starting of daemons
  • Implements transactional dependency-based service control logic
  • Tracks processes using Linux cgroups
  • Supports snapshotting and restoring
  • Maintains mount and automount points

You will need to create a service file, for example my_project.service. This file will contain the following content:

[Unit]
Description=My Project
After=syslog.target
ConditionPathExists=<you_code_dir>/src/manage.py

[Service]
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/usr/bin/uwsgi --ini <you_code_dir>/uwsgi.ini
RuntimeDirectory=<you_code_dir>/
KillMode=process
Restart=on-failure

[Install]
WantedBy=multi-user.target
Alias=my_project.service

6. To manage systemd service:

If you want to start systemd service, Debian-like for example, you should:

  • Place your my_project.service file in /etc/systemd/system/ folder
  • Run the following commands:
systemctl daemon-reload
systemctl start|stop|restart my_project

or

systemctl daemon-reload
systemctl start|stop|restart my_project.service

For example, to start the service:

systemctl daemon-reload
systemctl start my_project.service

Example: How to deploy using Heroku

1. Create a uwsgi.ini file with the following content:

[uwsgi]
http-socket = :$(PORT)
master = true
processes = 4
die-on-term = true
module = manage_uwsgi:app
memory-report = true
stats = :22222
exec-pre-app = /sbin/ifconfig eth0

2. Create a file called Procfile with the following content:

web: uwsgi --ini uwsgi.ini

3. Then run the following command on a terminal window inside your project dir:

git push heroku master

4. To know more about how to deploy on Heroku, go to:

http://uwsgi-docs.readthedocs.io/en/latest/tutorials/heroku_python.html

You can also configure Chef (https://www.chef.io/) to perform all the steps in an automated fashion.