This section will cover some of the software that we'll need to install on our server to serve our Flask application to the world. The basic stack is a front server that reverse proxies requests to an application runner that is running our Flask app. We'll usually have a database too, so we'll talk a little about those options as well.
13.2.1. Application runner
The server that we use to run Flask locally when we're developing our application isn't good at handling real requests. When we're actually serving our application to the public, we want to run it with an application runner like Gunicorn. Gunicorn handles requests and takes care of complicated things like threading.
To use Gunicorn, we install the gunicorn
package in our virtual
environment with Pip. Running our app is a simple command away.
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Hello World!"
A fine app indeed. Now, to serve it up with Gunicorn, we simply run the
gunicorn
command.
(ourapp)$ gunicorn rocket:app 2014-03-19 16:28:54 [62924] [INFO] Starting gunicorn 18.0 2014-03-19 16:28:54 [62924] [INFO] Listening at: http://127.0.0.1:8000 (62924) 2014-03-19 16:28:54 [62924] [INFO] Using worker: sync 2014-03-19 16:28:54 [62927] [INFO] Booting worker with pid: 62927
At this point, we should see "Hello World!" when we navigate our browser to http://127.0.0.1:8000.
To run this server in the background (i.e. daemonize it), we can pass
the -D
option to Gunicorn. That way it'll run even after we close our
current terminal session.
If we daemonize Gunicorn, we might have a hard time finding the process
to close later when we want to stop the server. We can tell Gunicorn to
stick the process ID in a file so that we can stop or restart it later
without searching through lists of running processess. We use the
-p <file>
option to do that.
(ourapp)$ gunicorn rocket:app -p rocket.pid -D (ourapp)$ cat rocket.pid 63101
To restart and kill the server, we can run kill -HUP
and kill
respectively.
(ourapp)$ kill -HUP `cat rocket.pid`
(ourapp)$ kill `cat rocket.pid`
By default Gunicorn runs on port 8000. We can change the port by adding
the -b
bind option.
(ourapp)$ gunicorn rocket:app -p rocket.pid -b 127.0.0.1:7999 -D
13.2.1.1. Making Gunicorn public
Caution Gunicorn is meant to sit behind a reverse proxy. If you tell it to listen to requests coming in from the public, it makes an easy target for denial of service attacks. It's just not meant to handle those kinds of requests. Only allow outside connections for debugging purposes and make sure to switch it back to only allowing internal connections when you're done.
If we run Gunicorn like we have in the listings, we won't be able to access it from our local system. That's because Gunicorn binds to 127.0.0.1 by default. This means that it will only listen to connections coming from the server itself. This is the behavior that we want when we have a reverse proxy server that is sitting between the public and our Gunicorn server. If, however, we need to make requests from outside of the server for debugging purposes, we can tell Gunicorn to bind to 0.0.0.0. This tells it to listen for all requests.
(ourapp)$ gunicorn rocket:app -p rocket.pid -b 0.0.0.0:8000 -D
Note
- Read more about running and deploying Gunicorn in the documentation.
- Fabric is a tool that lets you run all of these deployment and management commands from the comfort of your local machine without SSHing into every server.
13.2.2. Nginx Reverse Proxy
A reverse proxy handles public HTTP requests, sends them back to Gunicorn and gives the response back to the requesting client. Nginx can be used very effectively as a reverse proxy and Gunicorn "strongly advises" that we use it.
To configure Nginx as a reverse proxy to a Gunicorn server running on 127.0.0.1:8000, we can create a file for our app: /etc/nginx/sites-available/expl-oreflask.com.
# /etc/nginx/sites-available/exploreflask.com
# Redirect www.exploreflask.com to exploreflask.com
server {
server_name www.exploreflask.com;
rewrite ^ http://exploreflask.com/ permanent;
}
# Handle requests to exploreflask.com on port 80
server {
listen 80;
server_name exploreflask.com;
# Handle all locations
location / {
# Pass the request to Gunicorn
proxy_pass http://127.0.0.1:8000;
# Set some HTTP headers so that our app knows where the
# request really came from
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Now we'll create a symlink to this file at /etc/nginx/sites-enabled and restart Nginx.
$ sudo ln -s \ /etc/nginx/sites-available/exploreflask.com \ /etc/nginx/sites-enabled/exploreflask.com
We should now be able to make our requests to Nginx and receive the response from our app.
Note The Nginx configuration section in the Gunicorn docs will give you more information about setting Nginx up for this purpose.
13.2.2.1. ProxyFix
We may run into some issues with Flask not properly handling the proxied requests. It has to do with those headers we set in the Nginx configuration. We can use the Werkzeug ProxyFix to ... fix the proxy.
# app.py
from flask import Flask
# Import the fixer
from werkzeug.contrib.fixers import ProxyFix
app = Flask(__name__)
# Use the fixer
app.wsgi_app = ProxyFix(app.wsgi_app)
@app.route('/')
def index():
return "Hello World!"
Note
- Read more about ProxyFix in the Werkzeug docs.