
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:
-w
equals to--workers
, which represents the number of workers;-b
equals to--bind
, which specifies the bind address (must be local host and for both IPV4 and IPV6);-k
equals to--worker-class
, specifies the type of workers to use, such assync
,eventlet
,gevent
,tornado
andgthread
. 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:
- 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
- Add auth with
ngrok config add-authtoken [authtoken]
- Start listening the service
ngrok http 5000
- 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