Running a Flask App as a Service on Linux
Prerequisites
- Linux environment
Description
This post describes how to run a Flask app on Linux as a service using Systemd. It is assumed that the service is accessed from the same network using an IP and Port number.
Prepare Linux Environment
Update package lists
Before you start we need to have a few things installed. First run the following command from your Linux environment.
sudo apt-get update
This downloads the package lists from the repositories and updates the information on the newest versions of packages and their dependencies.
Install Nginx
NOTE: You can skip this step if you do not need to host your service behind a web server like Nginx. It will not be used in the remainder of this post.
If you don’t already have it installed, you must install Nginx.
It easy to install Nginx with a single command:
sudo apt-get install nginx
Nginx will start automatically.
By default, Ngnix creates a web server root directory at /var/www/html/
with a default html web page in the directory. When nginx is up and running, the webpage will be visible by pointing the browser url to your Linux machine.
If for some reason Nginx does not start automatically, start the server with the following command:
sudo service nginx start
Install Gunicorn
You can do this either in a python or conda virtual environment. In this example I will use the base conda environment.
Activate the environment and install Gunicorn by typing
pip install gunicorn
Create working directory
Create a directory called hello_service
in which you will keep your python application.
cd ~
mkdir hello_service
Create a Flask App
For the Flask application we will create a simple hello world app.
from flask import Flask
app = Flask(__name__)
@app.route('/hello', methods=['GET'])
def hello():
return 'Hello World'
if __name__ == "__main__":
app.run(host='0.0.0.0')
Save this file in the hello_service
folder.
Create WSGI entry point
Next, create a file called wsgi.py that will serve as the entry point for our application code in app.py.
From inside the hello_service
folder type the following to create it using nano
, or use a text editor to create it.
nano wsgi.py
The Flask application code will be imported from app.py inside the entry point file:
from app import app
if __name__ == "__main__":
app.run()
Save and close the file.
Testing with Gunicorn
Check that Gunicorn can serve the application correctly. We can do this by simply passing the gunicorn command the name of our entry point file (minus the .py extension), plus the name of the application. In this case, it is wsgi:app
. We’ll also specify a publicly available interface and port to bind to. From the working directory hello_service
run the following command:
gunicorn --bind 0.0.0.0:5000 wsgi:app
Using a browser, visit your server’s IP address on port :5000 to see your application:
http://your_server_ip:5000/hello
When you have confirmed that it’s functioning properly, press CTRL-C in the terminal to stop the application.
Create Systemd Unit File
The systemd unit file must be saved to the system folder at /etc/systemd/system
. Run the following command to create it using nano
:
sudo nano /etc/systemd/system/hello_world.service
Inside the file add the following
[Unit]
Description=Gunicorn instance to serve hello world app
After=network.target
[Service]
WorkingDirectory=/home/sal/hello_service
Environment="PATH=/home/sal/miniconda3/bin"
EnviromentFile=/home/sal/.bashrc
ExecStart=/home/sal/miniconda3/bin/gunicorn --workers 1 --bind 0.0.0.0:5001 wsgi:app
[Install]
WantedBy=multi-user.target
Here is a description of each of the items in the unit file.
[Unit]
This section is used for defining metadata for the unit and configuring the relationship of the unit to other units.
Description
- Describes the name and basic functionality of the unit.
After
- The units listed in this directive will be started before starting the current unit. In this case network.target
will be started before the unit for our service.
[Service]
This section is used to provide configuration that is only applicable for services.
WorkingDirectory
- This is the folder where we saved the application app.py
Environment
- This is the path to the python executable for your environment. If using conda, this is typically found in the .conda
folder in the /envs/environmentname/bin
sub folder. In my case I’m using the base
environemnt to the subfolder is miniconda3/bin
EnvironmentFile
- This is the Path to your .bashrc
file. Make sure
ExecStart
- This is the command to run. We are launching gunicorn and it is located in the bin folder. The entry point file and name of application will be located in the working directory.
[Install]
This section is mandatory for autostart services, because it tells systemd at which moment during boot process your service should be started. Your process should be linked to some generic boot targets such as multi-user.target
or graphical.target
WantedBy
- The WantedBy dependency establishes a relationship between the current unit and the dependency unit without modifying the dependency unit. The current unit has WantedBy=multi-user.target
, a directory called multi-user.target.wants
will be created within /etc/systemd/system
(if not already available) and a symbolic link to the current unit will be placed within. Systemd will start your service whenever multi-user target
is started.
Save the file to the /etc/systemd/system
folder and close the file.
Run the service
First run daemon-reload. daemon-reload
reruns all generators, reload all unit files, and recreate the entire dependency tree.
systemctl daemon-reload
enable
command enables the service to start automatically at boot.
systemctl enable hellowworld.service
start
starts the service
systemctl start helloworld.service
Use status
to check that its running
systemctl status helloworld.service
You will see the following
If you restart your linux server now, the service should automatically start up on boot. If you visit http://your_server_ip:5001/hello
you should get the response string Hello World
.
Disable the service
To disable the service from starting automatically at boot, you can type
sudo systemctl disable hellowworld.service
This will remove the symbolic link that indicated that the service should be started automatically.
Troubleshooting
If there are issues try checking the sytem log for more details
sudo nano /var/log/syslog
Other useful commands
Stop a service
sudo systemctl stop SERVICE_NAME
Restart a service
sudo systemctl restart SERVICE_NAME
Show process ID for process listening on port
fuser -n tcp 8080
Show all processes
sudo netstat -nlp
Kill process by pid
sudo kill <pid>
Source Code
References
Salvatore S. © 2020