Python gunicorn
Gunicorn is a WSGI Servers.
Documentation
official docs https://gunicorn.org/#docs flask+gunicorn logging https://medium.com/@trstringer/logging-flask-and-gunicorn-the-manageable-way-2e6f0b8beb2f
Hosting Flask APP
Flask App
example flask app
For the sake of completion, we'll define a simple REST API using flask. This will be the application we're hosting using gunicorn.
python appimport flask app = flask.Flask(__name__) @app.route('api/test', methods=['GET]) def route_api_test(): return 'connection successful!'example interaction
curl -X GET http://yoururl/api/test >>> connection successful!Virtualenv for Flask App
create virtualenv
Create a virtualenv to host your program, and install your program into it.python -m virtualenv /home/services/<yourprogram> cd /src/<yourprogram> /home/services/<yourprogram>/venv/bin/python setup.py installapp entrypoint
Gunicorn needs to be pointed the module/variable-name of your flask app. This step is unecessary unless you need to modify the environment before the app is run.
from yourprogram.restapi import appSystem Service
You probably want to have the startup managed by your init system. Here's a systemd service, adapt to your initsystem.
systemd
/etc/systemd/system/yourprogram.service
[Unit] Description=Your Program, it does... Requires=network-online.target [Service] Type=simple User=yourprogramuser Group=www-data Environment='SERVER_SOFTWARE=gunicorn' # set so can tell if gunicorn is running within app ExecStart=/home/services/yourprogram/venv/bin/gunicorn \ --bind unix:/home/services/tma-restapi/www-data/yourprogram.sock \ # may also use addr ex: '127.0.0.1:8000' --workers 4 \ --umask 007 \ --certfile /etc/letsencrypt/live/yoursite/cert.pem \ --keyfile /etc/letsencrypt/live/yoursite/privkey.pem \ yourmodule:restapi.app # module:rest-app-import-within-module [Install] WantedBy=multi-user.targetWebServer
nginx
/etc/nginx/nginx.conf
user www-data www-data; events { worker_connections 1024; } http { # ============== # Main Webserver # ============== # w3m http://localhost server { listen 80; # ipv4 listen [::]:80; # ipv6 # proxy to other webservers location /api/ { proxy_pass http://localhost:81/; ## <-- points to your gunicorn proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } # host your gunicorn on port 81 server { listen 81; server_name api.localhost; location / { include proxy_params; # alternatively: proxy_pass http://127.0.0.1:9000/api proxy_pass http://unix:/home/services/tma-restapi/www-data/tma-restapi.sock; } } }
Configuration
gunicorn can be configured globally, using a python-file, or using commandline params.
/path/to/config.py# attributes in this python file are read by gunicorn bind = '127.0.0.1:5000' workers = 4 umask = '007' log_level = 'info'gunicorn --config /path/to/config.py
Gotchas
Logging
gunicorn allows you to configure loggin on the commandline
--log-level info
. If you would like to reuse this loglevel throughout your application, you can check/set it.import logging gunicorn_logger = logging.getLogger('gunicorn.error') logging.root.setLevel(gunicorn_logger.level)Detecting Gunicorn
Your app may run differently under gunicorn than it does when using the regular
flask.Flask.run()
method.Unfortunately I have not found a good way of detecting the environment. The best I have come up with is in some environments, gunicorn sets the environment variable
SERVER_SOFTWARE
. I check for this in my pythonfile, and explicitly set it within the service that runs the flask app.is_hosted_by_gunicorn = os.environ.get('SERVER_SOFTWARE', '').startswith('gunicorn')
WARNING:
this does not always work -- but you can easily set it within the service-file that runs gunicorn