Use Gunicorn, Cloudflared and Ngrok on Linux for a Simple Production
Dou Liu

A local Flask web server is not enough when …

For about half a year, the services which support my virtual museum guide, such as Knowledge Graph query and recommendation generating, are hosted on my local machine intermittently. These services are created as APIs using Flask framework, and then I used Ngrok to make the services exposed to the Internet. For web applications in university projects, I usually start with Flask because of the lightness and easiness of Flask: all you have to do is one line like

app.run()

after defining the functions, then an locally accessible web application is up running. However, Flask is not suitable for production since its built-in server does not scale well. Just like the WARNING received when you run a simple Flask project:

* Serving Flask app 'app' (lazy loading)
* Environment: production
  WARNING: This is a development server. Do not use it in a production deployment.
  Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

A demo presentation for the virtual museum guide is coming soon in mid June and I am planning to deploy the project at the server of University of Twente. Before that, we need to make it production-ready (to a bit wider audience),

Use Gunicorn as server

Gunicorn ‘Green Unicorn’ is a Python WSGI HTTP for UNIX. It’s a pre-fork worker model. The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resources, and fairly speedy.

This article explains how to use gunicorn to optimize the performance.
To install the gunicorn:

pip install gunicorn 

Using gunicorn to run the flask application:

gunicorn -w 1 -b 0.0.0.0:5000 app:app

Note:

  1. -w equals to --workers, which represents the number of workers;
  2. -b equals to --bind, which specifies the bind address (must be local host and for both IPV4 and IPV6);
  3. -k equals to --worker-class, specifies the type of workers to use, such as sync, eventlet, gevent, tornado and gthread. And we could use gevent here, gevent is a coroutine-based Python networking library that uses greenlet to provide a high-level synchronous API on top of the libev or libuv event loop.

Create a Linux service

Create service firstly:

sudo nano /etc/systemd/system/<service-name>.service

Then write:

[Unit]
Description=Gunicorn instance to serve virtual museum agent
After=network.target

[Service]
User=<user-name> 
Group=www-data
ExecStartPre=/bin/sleep 60
WorkingDirectory=/home/<user-name>/HHAI2022_Virtual_Museum_Guide                             
Environment="PATH=/home/<user-name>/HHAI2022_Virtual_Museum_Guide/<venv-name>/bin"
ExecStart=/home/<user-name>/HHAI2022_Virtual_Museum_Guide/<venv-name>/bin/gunicorn -w 1 -b 0.0.0.0:5000 app:app

[Install]
WantedBy=multi-user.target

Then enable the service:

sudo systemctl enable <service-name>.service --now

Expose the service to the Internet

Option 1: Cloudflare (Free)

Cloudflare can create a Tunnel and securely make a local service accessible to the Internet. This part I followed a tutorial to create such a tunnel via Cloudflare on Ubuntu 20.04 LTS server.

Cloudflare is a global network designed to make everything you connect to the Internet secure, private, fast, and reliable. Cloudflare offers a suite of services and Zero Trust Services are the services we will utilize in the following tutorials. Zero Trust Services consist of Teams, Access, Gateway and Browser Isolation.

Our main goal is to obtain a free domain from Freenom and connect our hosted applications on a Ubuntu 20.04 LTS server within our local home network via a Cloudflare Tunnel to the world wide web securely without any port-forwarding complications or altering firewall.

Step 1: Install Cloudflare

curl -LO https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
sudo rm cloudflared-linux-amd64.deb

Step 2: Log in

cloudflared tunnel login

At this point, a URL will pop up, open it with a browser, log in to authentication, and then select the domain name you want to use to penetrate the internal network.

After success, a certificate will be generated and placed in ~/Cloudflared/Cert.pem.

Step 3: Create a tunnel

cloudflared tunnel create <Tunnel-NAME>

After success, it will be prompted that the json file has been placed in ~/.CloudFlared/<Tunnel-UUID>.json.

Step 4: Create DNS records to route traffic to the Tunnel

cloudflared tunnel route dns <Tunnel-NAME> <SUBDOMAIN>

Step 5: Create config file for the new tunnel

nano ~/.cloudflared/config.yml

Change the config as:

tunnel: <Tunnel-NAME>
credentials-file: /home/<user-name>/.cloudflared/<Tunnel-UUID>.json
ingress:
  - hostname: <domain-name>
    service: http://localhost:5000
    originRequest:
      noTLSVerify: true
  - service: http_status:404

Step 6: Create a Linux service

First, we create a service:

nano /etc/systemd/system/cloudflared.service

Write the content like:

[Unit]
Description=cloudflared
After=network.target

[Service]
TimeoutStartSec=0
Type=notify
ExecStart=/usr/bin/cloudflared --loglevel debug --transport-loglevel warn --config /home/<user-name>/.cloudflared/config.yml tunnel run <Tunnel-NAME>
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

Then, enable this service:

systemctl enable cloudflared --now

As a result, the Linux server will automatically run this service.

Ngrok (Subscribe needed when long-term use)

Ngrok is a cross-platform application that exposes local server ports to the Internet.

Pros Cons
Light and easy For free users, domain name changes every time the Ngrok restarts
Stable 240 euro a year for a fixed domain name

Following the steps:

  1. Install ngrok on the linux server with Apt:
    curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | \
          sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null && \
          echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | \
          sudo tee /etc/apt/sources.list.d/ngrok.list && \
          sudo apt update && sudo apt install ngrok
  2. Add auth with
    ngrok config add-authtoken [authtoken]
  3. Start listening the service
    ngrok http 5000
  4. Congrats! Easy right?
    Note: The forwarding address is temporary, and it will change when we restart the ngrok.
    Session Status                online                                                                                                                                                       
    Account                       <your account> (Plan: Free)                                                                                                                            
    Update                        update available (version 3.0.5, Ctrl-U to update)                                                                                                           
    Version                       3.0.3                                                                                                                                                        
    Region                        Europe (eu)                                                                                                                                                  
    Latency                       19.657864ms                                                                                                                                               
    Web Interface                 http://127.0.0.1:4040                                                                                                                                        
    Forwarding                    https://d0e3-86-89-214-254.eu.ngrok.io -> http://localhost:5000                                                                                              
                                                                                                                                                                                               
    Connections                   ttl     opn     rt1     rt5     p50     p90                                                                                                                  
                                  0       0       0.00    0.00    0.00    0.00     
 Comments