How to deploy Python 3 Flask application on Raspberry Pi 3 with Raspbian Stretch Lite, Nginx, Supervisor, Virtualenv and Gunicorn
Raspberry Pi 3 Model B boards make good gifts for programmers and if someone had gifted you with one, you may want to use it as a control center for interacting with various IoT sensors and devices at home.
In such a situation, you may want to build a Python 3 Flask application to present the web interface for accessing the control center.
After you had built that Python 3 Flask application, the next step will be to deploy it on your Raspberry Pi 3 for serving HTTP requests.
This post discusses how you can deploy a Python 3 Flask application on Raspberry Pi 3 with Raspbian Stretch Lite, Nginx, Virtualenv and Gunicorn.
Creating a simple Python 3 Flask application as a proof of concept
Let us create a simple Python 3 Flask application to demonstrate the deployment of Python 3 Flask application on our Raspberry Pi 3 with Raspbian Stretch Lite, Nginx, Supervisor, Virtualenv and Gunicorn.
Project structure of our simple Python 3 Flask application
The simple application consists of a directory with the following contents:
simpleapp |--requirements.txt |--start.sh |--src |--__init__.py |--static
Specifying Flask as a dependency in requirements.txt
The requirements.txt
file is for specifying the Python 3 dependencies that our Python 3 Flask application is going to need. Since this is a minimalistic application, the requirements.txt
contains only the following line:
Flask==0.12.2
Creating start.sh for Supervisor to start the Python 3 Flask application
The shell script is intended for the Supervisor daemon to start our Python 3 Flask application. The shell script contains the following content:
source simpleappenv/bin/activate gunicorn --workers 5 --bind unix:simpleapp.sock -m 007 src:app deactivate
The simpleappenv
directory contains a virtual environment that we will created later with virtualenv
. In this shell script, we first get into the virtual environment by sourcing the activate
script that resides in simpleappenv/bin
directory.
Once the virtual environment is activated, we will then use the gunicorn
binary to run our Python 3 Flask application. The command tells Gunicorn to use 5 workers for handling HTTP requests directed at the Python 3 Flask application. Gunicorn will listen for HTTP requests via the simpleapp.sock
unix socket. Only processes running the same user as this start.sh script can communicate with Gunicorn through the simpleapp.sock
unix socket. Gunicorn will also look into the src
package to find an app
variable to realise the Python 3 Flask application.
We will install a version of gunicorn that can run Python 3 applications into the virtual environment later.
The deactivate
command will be run when the Python 3 Flask application got terminated by whatever reason. In such a case, the executor of the shell script will get out of the virtual environment.
Building the Python 3 Flask application inside src/__init__.py
The src/__init__.py
Python script will be run when the src
directory is being read by Python. The src/__init__.py
file contains the following Python code:
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello there, welcome to our app!'
In this file, we import Flask, construct a Flask object and set it to the app
variable. We then decorate a function to return the message Hello there, welcome to our app! for HTTP requests directed at the root URL.
Using the static
directory to serve as the root directory for Nginx
The static
directory is meant to serve as the root directory for Nginx. We will use this directory to keep images, CSS, JavaScript and other kinds of static files.
Setting up Raspbian Stretch Lite on Raspberry Pi 3 to run Python 3 applications
As a first step to the deployment of your Python 3 Flask application, follow this tutorial to setup Raspbian Stretch Lite on Raspberry Pi 3 to run Python 3 applications. After doing so, you will get your Raspberry Pi 3 running the Raspbian Stretch Lite operating system with the following software pieces:
- SSH server which enables you to configure Raspbian Stretch Lite remotely.
- Python 3 for running your Python 3 Flask application.
- Python 3 package manager (pip3) for installing the dependencies for your Python 3 Flask application.
- Virtualenv for you to create an isolated environment for running your Python 3 Flask application.
Getting our Python 3 Flask application project files into our Raspberry Pi 3 via SSH
Assuming that our Raspberry Pi 3 is accessible via the IP address 192.168.1.109 and we are at the directory that contains the simpleapp
directory, we will run the following shell command to perform the copy over to the home directory of the pi
user:
scp -r simpleapp pi@192.168.1.109:~
After the files are transferred over to your Raspberry Pi 3, run the following command to get into your Raspbian Stretch Lite:
ssh pi@192.168.1.109
Once you log into your Raspbian Stretch Lite via SSH, move the project files to the /var/flaskapp
directory by running the following command:
sudo mkdir /var/flaskapp sudo mv ~/simpleapp /var/flaskapp/
Once you had moved the project files, make the /var/flaskapp/simpleapp/start.sh
executable by the owner:
sudo chmod 744 /var/flaskapp/simpleapp/start.sh
Installing Nginx on Raspbian Stretch Lite
Nginx runs very well on Raspberry Pi 3 and serves as a solid reverse proxy server for proxying HTTP traffic to different upstream servers. With Nginx in the same box as our Python 3 Flask application, we have the flexibility to complement the Python 3 Flask application with applications written in another language in the future.
To install Nginx, run the following commands:
sudo apt-get update sudo apt-get install nginx -y
After the installation had completed, verify the installation by running the following command:
sudo systemctl status nginx
This command should give you output similar to the following if Nginx had installed successfully:
● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2018-03-03 13:35:15 +08; 42s ago Docs: man:nginx(8) Process: 17632 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS) Process: 17629 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS) Main PID: 17633 (nginx) CGroup: /system.slice/nginx.service ├─17633 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; ├─17634 nginx: worker process ├─17635 nginx: worker process ├─17636 nginx: worker process └─17637 nginx: worker process Mar 03 13:35:15 raspberrypi systemd[1]: Starting A high performance web server and a reverse proxy server... Mar 03 13:35:15 raspberrypi systemd[1]: Started A high performance web server and a reverse proxy server.
Installing Supervisor on Raspbian Stretch Lite
Next up is the installation of Supervisor. Supervisor is a convenient tool for running applications as a server daemon. We will use Supervisor to run Gunicorn in an isolated environment.
To install Supervisor, run the following command:
sudo apt-get install supervisor -y
Once the command completes, run the following command to verify your installation:
sudo systemctl status supervisor
You should see output similar to the following if Supervisor had been installed successfully:
● supervisor.service - Supervisor process control system for UNIX Loaded: loaded (/lib/systemd/system/supervisor.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2018-03-03 13:52:12 +08; 3s ago Docs: http://supervisord.org Main PID: 18293 (supervisord) CGroup: /system.slice/supervisor.service └─18293 /usr/bin/python /usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf Mar 03 13:52:12 raspberrypi systemd[1]: Started Supervisor process control system for UNIX. Mar 03 13:52:14 raspberrypi supervisord[18293]: 2018-03-03 13:52:14,741 CRIT Supervisor running as root (no user in config file) Mar 03 13:52:14 raspberrypi supervisord[18293]: 2018-03-03 13:52:14,743 WARN No file matches via include "/etc/supervisor/conf.d/*.conf" Mar 03 13:52:14 raspberrypi supervisord[18293]: 2018-03-03 13:52:14,794 INFO RPC interface 'supervisor' initialized Mar 03 13:52:14 raspberrypi supervisord[18293]: 2018-03-03 13:52:14,796 CRIT Server 'unix_http_server' running without any HTTP authentication checking Mar 03 13:52:14 raspberrypi supervisord[18293]: 2018-03-03 13:52:14,797 INFO supervisord started with pid 18293
Preparing the virtual environment for running our Python 3 Flask application on Raspbian Stretch Lite
The next step is to prepare the virtual environment for running our Python 3 Flask application. To do so run the following commands:
cd /var/flaskapp/simpleapp virtualenv -p python3 simpleappenv source simpleappenv/bin/activate pip install -r requirements.txt deactivate
After the commands complete, we will have the virtual environment for running our Python 3 Flask application. This virtual environment will be available at the /var/flaskapp/simpleapp/simpleappenv
directory.
Installing Gunicorn inside the virtual environment
The next step is to install Gunicorn into the virtual environment that we had created. To do so, we run the following commands:
cd /var/flaskapp/simpleapp source simpleappenv/bin/activate pip install gunicorn deactivate
Installing gunicorn
inside the virtual environment will ensure that Supervisor get the correct version of Gunicorn to run our Python 3 Flask application. Although it is possible to indicate Gunicorn as a dependency in requirements.txt
, we left it out so that our Python 3 Flask application remains agnostic to the WSGI server that we use for deployment.
Change owner of the Python 3 Flask application directory to www-data
After we had installed Gunicorn inside the virtual environment, the next step will be to set the www-data
user as the owner of our Python 3 Flask application:
sudo chown -R www-data:www-data /var/flaskapp/simpleapp
Creating the Supervisor configuration file to run our Python 3 Flask application via Gunicorn
By default, the Supervisor daemon will look inside the /etc/supervisor/conf.d
directory to look for instructions to get applications running. To get the Supervisor daemon to run our Python 3 Flask MVP as its subprocess, we first point nano
to a configuration file inside the /etc/supervisor/conf.d
directory:
sudo nano /etc/supervisor/conf.d/simpleapp.conf
And then paste in the following contents:
[program:simple-flask-app] directory=/var/flaskapp/simpleapp command=/bin/bash -E -c ./start.sh autostart=true autorestart=true stopsignal=INT stopasgroup=true killasgroup=true user=www-data
This set of configurations will ensure that our Python 3 Flask application releases binded port(s) during a supervisor restart. In addition, we also indicated to Supervisor that we wanted to run our Python 3 Flask application as the www-data
user.
After we had created this set of configurations, we can restart Supervisor to get it to run our Python 3 Flask application for us:
sudo systemctl restart supervisor.service
You can verify whether your Python 3 Flask application is running successfully by using supervisorctl
:
sudo supervisorctl
In the supervisor control panel, you will see something similar to the following output if your Python 3 Flask application is running successfully:
simple-flask-app RUNNING pid 24444, uptime 0:00:24 supervisor>
You can also see the output from Gunicorn by running the following command in supervisorctl
:
tail simple-flask-app stdout
Which will give output similar to the following:
[2018-03-03 14:42:04 +0800] [24447] [INFO] Starting gunicorn 19.7.1 [2018-03-03 14:42:04 +0800] [24447] [INFO] Listening at: unix:simpleapp.sock (24447) [2018-03-03 14:42:04 +0800] [24447] [INFO] Using worker: sync [2018-03-03 14:42:04 +0800] [24450] [INFO] Booting worker with pid: 24450 [2018-03-03 14:42:04 +0800] [24451] [INFO] Booting worker with pid: 24451 [2018-03-03 14:42:04 +0800] [24452] [INFO] Booting worker with pid: 24452 [2018-03-03 14:42:04 +0800] [24453] [INFO] Booting worker with pid: 24453 [2018-03-03 14:42:04 +0800] [24454] [INFO] Booting worker with pid: 24454
We will also see the /var/flaskapp/simpleapp/simpleapp.sock
unix socket, which is where Nginx will proxy HTTP requests to.
Setting up Nginx to proxy HTTP request to our Python 3 Flask application
Once we are sure that Supervisor is running our Python 3 Flask application with Gunicorn, the next step is to configure Nginx to proxy HTTP requests to it. Suppose we want to reach our Python 3 Flask application via the domain name simpleapp.techcoil.com
, we first use nano to create the /etc/nginx/sites-enabled/simpleapp.techcoil.com
file:
sudo nano /etc/nginx/sites-enabled/simpleapp.techcoil.com
And then include the following contents:
server { server_name simpleapp.techcoil.com; listen 80; root /var/flaskapp/simpleapp/static; location / { try_files $uri @simpleapp-flask; } location @simpleapp-flask { include proxy_params; proxy_pass http://unix:/var/flaskapp/simpleapp/simpleapp.sock; } }
With this set of Nginx configurations, we told Nginx to listen on port 80 for HTTP requests directed at the domain simpleapp.techcoil.com
. For every such request, we tell Nginx to reference the directory /var/flaskapp/simpleapp/static
as the document root.
The location /
block tells Nginx to map request to a file inside the /var/flaskapp/simpleapp/static
directory and return it as a HTTP response if there is one. If the file does not exist, Nginx should issue an internal redirect to the location @simpleapp-flask
block.
The location @simpleapp-flask
block proxies HTTP requests to the /var/flaskapp/simpleapp/simpleapp.sock
unix socket with some common HTTP headers defined inside the /etc/nginx/proxy_params
file.
Accessing our Python 3 Flask application with a computer in the same network
To verify that our Python 3 Flask application is accessible via simpleapp.techcoil.com
, we will edit the host file on a computer that is in the same network as our Python 3 Flask application.
Add the following entries inside the host file to map the simpleapp.techcoil.com
domain to the IP address of the Raspberry Pi 3 that is running our Python 3 Flask application:
192.168.1.109 simpleapp.techcoil.com
After you had done so, you should be able to use your browser to access your Python 3 Flask application via http://simpleapp.techcoil.com
. The Python 3 Flask application should return you the HTTP response containing the Hello there, welcome to our app! message.