Skip to content

Deploying Python API to Production

Alex Gebhard edited this page Jun 17, 2021 · 6 revisions

The development server that runs when python3 app.py is called is not sufficient for a production environment. A more scalable and optimized server is needed. All Python APIs need a Web Server Gateway Interface (WSGI) to run. Gunicorn was chosen as the WSGI to run the API. Gunicorn is written in a fast, scalable, and simple WSGI library.

Install Gunicorn (Ubuntu Server)

  1. To install the latest version of gunicorn, run pip3 install gunicorn
  2. Create the systemd file for gunicorn. Systemd is the Linux service that manages processes that run in the background. Create the gunicorn.service file in /lib/systemd/system/ directory. Change the WorkingDirectory and ExecStartPre paths to point to the directory where app.py is located.
[Unit]
Description = The gunicorn service
After = network.target

[Service]
Type = simple
WorkingDirectory = /home/ubuntu/api/
ExecStartPre = /bin/bash -c 'source /home/ubuntu/api/myvenv/bin/activate'
ExecStart = gunicorn app:app -w 3
ExecReload = /bin/kill -s HUP $MAINPID
ExecStop = /bin/kill -s TERM $MAINPID
PrivateTmp = true
Restart=on-failure
RestartSec=5s

[Install]
WantedBy = multi-user.target
  1. To allow the gunicorn process to run on startup, run the following command: systemctl enable gunicorn.service
  2. To start the service, run: service gunicorn start

Let's Encrypt

Let's Encrypt is a non-profit that issues TLS certificates for free. TLS allows clients to use HTTPS to establish a secure connection to the server. Certbot is the client main client for Let's Encrypt. The installation instructions for Certbot can be found here.

NGINX

Exposing Gunicorn to the internet is possible, however it doesn't offer features such as TLS, HTTP Headers, or support for multiple domains. NGINX is a speedy, secure, and popular reverse proxy that runs in front of Gunicorn that handles incoming requests and routes them to the appropriate application.

NOTE: The user running the Gunicorn service needs to be in the "shadow" group for the ability to authenticate other users with PAM.

Installing NGINX

  1. See the NGINX Installation Instructions for how to install NGINX on Ubuntu

Configuring NGINX

  1. Replace the nginx.conf file in /etc/nginx/ with the following. Most of the TLS settings are taken from Mozilla.
worker_processes  1;

error_log  logs/error.log;
error_log  logs/error.log  notice;
error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    # Redirect any HTTP request to its HTTPS equivalent
    server {
        listen 80 default_server;
        listen [::]:80 default_server;

        location / {
            return 301 https://$host$request_uri;
        }
    }

    server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name api.tabot.sh;

        ssl_certificate /etc/letsencrypt/live/tabot.sh/cert.pem;
        ssl_certificate_key /etc/letsencrypt/live/tabot.sh/privkey.pem;
        ssl_session_timeout 1d;
        ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
        ssl_session_tickets off;
        # HSTS (ngx_http_headers_module is required) (63072000 seconds)
        add_header Strict-Transport-Security "max-age=63072000" always;

        # intermediate configuration
        ssl_protocols TLSv1.2;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
        ssl_prefer_server_ciphers off;

        # OCSP stapling
        ssl_stapling on;
        ssl_stapling_verify on;

        # verify chain of trust of OCSP response using Root CA and Intermediate certs
        ssl_trusted_certificate /etc/letsencrypt/live/tabot.sh/fullchain.pem;

        # replace with the IP address of your resolver
        resolver 8.8.8.8;

        #access_log  logs/host.access.log  main;

        location / {
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

Clone this wiki locally