gunicorn
Need to read about gunicorn because I use it to deploy my Flask application on the server.
References
Definitions
- WSGI
- WSGI is the Web Server Gateway Interface. It is a specification that describes how a web server communicates with web applications, and how web applications can be chained together to process one request.
Notes
Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server 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.
Features:
- Natively supports WSGI, Django, and Paster
- Automatic worker process management
- Simple Python configuration
- Multiple worker configurations
- Various server hooks for extensibility
- Compatible with Python 3.x >= 3.7
Installation
Requirements: Python 3.x >= 3.7
To install the latest version of Gunicorn:
$ pip install gunicorn
You may want to install Eventlet and Gevent if you expect your application code may need to pause for extended periods of time during request processing. See the design docs for more information.
$ pip install greenlet # Required for both
$ pip install eventlet # For eventlet workers
$ pip install gunicorn[eventlet] # Or, using extra
$ pip install gevent # For gevent workers
$ pip install gunicorn[gevent] # Or, using extra
If you are using Debian GNU/Linux it is recommended that you use system packages to install Gunicorn - i.e., dnf apt-get install gunicorn
. This has a number of advantages:
- Zero-effort installation
- Sensible default locations for logs (
/var/log/gunicorn
) - Improved security
- Sensible upgrade path
Running Gunicorn
Basic usage:
$ gunicorn [OPTIONS] [WSGI_APP]
Where WSGI_APP
is of the pattern $(MODULE_NAME):(VARIABLE_NAME)$
. The module name can be a full dotted path. The variable name refers to a WSGI callable that should be found in the specified module.
WSGI_APP
is optional if it is defined in a config file
The variable name can be a variable (like the app
variable in a simple Flask application) or a function call - like when you are following the application factory
pattern.
Commonly Used Arguments
-c CONFIG, --config=CONFIG
- Specify a config file in the form
$(PATH)
- Specify a config file in the form
-b BIND, --bind=BIND
- Specify a server socket to bind in the form of
$(HOST)
- Specify a server socket to bind in the form of
-w WORKERS, --workers=WORKERS
- The number of worker processes. Should generally be 2-4.
-k WORKERCLASS, --worker-class=WORKERCLASS
- The type of worker process to run
-n APP_NAME, --name=APP_NAME
Configuration
- Gunicorn first reads environment variables for configuration settings
- Configuration settings in the command line override all other configuration settings
- Gunicorn then reads configuration from a framework specific configuration file
- Currently, only paster applications have access to framework specific settings.
- The third source of configuration information is an optional configuration file
gunicorn.conf.py
searched in the current working directory or specified using a command line argument. - The configuration file should be a valid Python source file with a python extension (e.g. gunicorn.conf.py)
- The forth place of configuration information are command line arguments stored in an environment variable named
GUNICORN_CMD_ARGS
To print the resolved configuration:
$ gunicorn --print-config APP_MODULE
Settings
- This page contains an exhaustive list of Gunicorn settings
Deploying Gunicorn
It is strongly recommended to use Nginx.
worker_processes 1;
user nobody nogroup;
# 'user nobody nobody;' for systems with 'nobody' as a group instead
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024; # increase if you have lots of clients
accept_mutex off; # set to 'on' if nginx worker_processes > 1
# 'use epoll;' to enable for Linux 2.6+
# 'use kqueue;' to enable for FreeBSD, OSX
}
http {
include mime.types;
# fallback in case we can't determine a type
default_type application/octet-stream;
access_log /var/log/nginx/access.log combined;
sendfile on;
upstream app_server {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response
# for UNIX domain socket setups
server unix:/tmp/gunicorn.sock fail_timeout=0;
# for a TCP configuration
# server 192.168.0.7:8000 fail_timeout=0;
}
server {
# if no Host match, close the connection to prevent host spoofing
listen 80 default_server;
return 444;
}
server {
# use 'listen 80 deferred;' for Linux
# use 'listen 80 accept_filter=httpready;' for FreeBSD
listen 80;
client_max_body_size 4G;
# set the correct host(s) for your site
server_name example.com www.example.com;
keepalive_timeout 5;
# path for static files
root /path/to/app/current/public;
location / {
# checks for static file, if not found proxy to app
try_files $uri @proxy_to_app;
}
location @proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
proxy_pass http://app_server;
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root /path/to/app/current/public;
}
}
}
It is recommended to pass protocol information to Gunicorn. Many web frameworks use this information to generate URLs. To configuration Nginx to pass an appropriate header, add a proxy_set_header
directive to your location
block:
...
proxy_set_header X-Forwarded-Proto $scheme;
...
Using Virtualenv
To serve an app from a Virtualenv it is generally easiest to just install Gunicorn directly into the Virtualenv. This will create a set of Gunicorn scripts for that Virtualenv which can be used to run applications normally.
Gunicorn and Flask
Gunicorn is easy to install, as it does not require external dependencies or compilation. It runs on Windows only under WSL.
Gunicorn should not be run as root because it would cause your application code to run as root, which is not secure. However, this means it will not be possible to bind port 80 or 443. Instead, a reverse proxy such as nginx should be used in front of Gunicorn.
When deploying Gunicorn and a Flask application behind a proxy, you need to tell Flask it is behind a Proxy. From the WSGI server and Flask application's perspectives, requests are now coming from the HTTP server to the local address, rather than from the remote address to the external server address. HTTP servers should set the X-Forwarded-
headers to pass on the real values to the application. The application can then be told to trust and use those values by wrapping it with the X-Forwarded-For Proxy Fix middleware provided by Werkzeug.
This middleware should only be used if the application is actually behind a proxy, and should be configured with the number of proxies that are chained in front of it.
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(
app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1
)