Deploying Flask application using uWSGI web server with Nginx
Using uWSGI to run a flask application
The built-in werkzeug
server certainly is not suitable for running production servers. The most obvious reason is the fact that the werkzeug
server is single-threaded and thus can only handle one request at a time.
Because of this we want to use the uWSGI Server to serve our application instead. In this example we will install uWSGI and run a simple test application with it.
Installing uWSGI:
pip install uwsgi
It is as simple as that. If you are unsure about the python version your pip uses make it explicit:
python3 -m pip install uwsgi # for python3
python2 -m pip install uwsgi # for python2
Now let’s create a simple test application:
app.py
from flask import Flask
from sys import version
app = Flask(__name__)
@app.route("/")
def index():
return "Hello uWSGI from python version: <br>" + version
application = app
In flask the conventional name for the application is app
but uWSGI looks for application
by default. That’s why we create an alias for our app in the last line.
Now it is time to run the app:
uwsgi --wsgi-file app.py --http :5000
You should see the message “Hello uWSGI …” by pointing your browser to localhost:5000
In order not to type in the full command everytime we will create a uwsgi.ini
file to store that configuration:
uwsgi.ini
[uwsgi]
http = :9090
wsgi-file = app.py
single-interpreter = true
enable-threads = true
master = true
The http
and wsgi-file
options are the same as in the manual command. But there are three more options:
-
single-interpreter
: It is recommended to turn this on because it might interfere with the next option -
enable-threads
: This needs to be turned on if you are using additional threads in your application. We don’t use them right now but now we don’t have to worry about it. -
master
: Master mode should be enable for various reasons
Now we can run the app with this command:
uwsgi --ini uwsgi.ini
Installing nginx and setting it up for uWSGI
Now we want to install nginx to serve our application.
sudo apt-get install nginx # on debian/ubuntu
Then we create a configuration for our website
cd /etc/nginx/site-available # go to the configuration for available sites
# create a file flaskconfig with your favourite editor
flaskconfig
server {
listen 80;
server_name localhost;
location / {
include uwsgi_params;
uwsgi_pass unix:///tmp/flask.sock;
}
}
This tells nginx to listen on port 80 (default for http) and serve something at the root path (/
). There we tell nginx to simply act as a proxy and pass every request to a socket called flask.sock
located in /tmp/
.
Let’s enable the site:
cd /etc/nginx/sites-enabled
sudo ln -s ../sites-available/flaskconfig .
You might want to remove the default configuration if it is enabled:
# inside /etc/sites-enabled
sudo rm default
Then restart nginx:
sudo service nginx restart
Point your browser to localhost
and you will see an error: 502 Bad Gateway
.
This means that nginx is up and working but the socket is missing. So lets create that.
Go back to your uwsgi.ini
file and open it. Then append these lines:
socket = /tmp/flask.sock
chmod-socket = 666
The first line tells uwsgi to create a socket at the given location. The socket will be used to receive requests and send back the responses. In the last line we allow other users (including nginx) to be able to read and write from that socket.
Start uwsgi again with uwsgi --ini uwsgi.ini
. Now point your browser again to localhost
and you will see the “Hello uWSGI” greeting again.
Note that you still can see the response on localhost:5000
because uWSGI now serves the application via http and the socket. So let’s disable the http option in the ini file
http = :5000 # <-- remove this line and restart uwsgi
Now the app can only be accessed from nginx (or reading that socket directly :) ).
Enable streaming from flask
Flask has that feature which lets you stream data from a view by using generators.
Let’s change the app.py
file
-
add
from flask import Response
-
add
from datetime import datetime
-
add
from time import sleep
-
create a new view:
@app.route(“/time/”) def time(): def streamer(): while True: yield ”
{}
“.format(datetime.now()) sleep(1)return Response(streamer())
Now open your browser at localhost/time/
. The site will load forever because nginx waits until the response is complete. In this case the response will never be complete because it will send the current date and time forever.
To prevent nginx from waiting we need to add a new line to the configuration.
Edit /etc/nginx/sites-available/flaskconfig
server {
listen 80;
server_name localhost;
location / {
include uwsgi_params;
uwsgi_pass unix:///tmp/flask.sock;
uwsgi_buffering off; # <-- this line is new
}
}
The line uwsgi_buffering off;
tells nginx not to wait until a response it complete.
Restart nginx: sudo service nginx restart
and look at localhost/time/
again.
Now you will see that every second a new line pops up.
Set up Flask Application, uWGSI, Nginx - Server Configurations boiler template (default, proxy and cache)
This is a porting of set up sourced from DigitalOcean’s tutorial of How To Serve Flask Applications with uWSGI and Nginx on Ubuntu 14.04
and some useful git resources for nginx servers.
Flask Application
This tutorial assume you use Ubuntu.
- locate
var/www/
folder. - Create your web app folder
mkdir myexample
cd myexample
optional You may want to set up virtual environment for deploying web applications on production server.
sudo pip install virtualenv
to install virtual environment.
virtualenv myexample
to set up virtual environment for your app.
source myprojectenv/bin/activate
to activate your environment. Here you will install all python packages.
end optional but recommended
Set up flask and gateway uWSGI
Install flask and uSWGI gateway:
pip install uwsgi flask
Example of flask app in myexample.py:
from flask import Flask
application = Flask(__name__)
@application.route("/")
def hello():
return "<h1>Hello World</h1>"
if __name__ == "__main__":
application.run(host='0.0.0.0')
Create file to communicate between your web app and the web server: gateway interface [https://en.wikipedia.org/wiki/Web_Server_Gateway_Interface]
nano wsgi.py
then import your webapp module and make it run from the gateway entry point.
from myexample import application
if __name__ == "__main__":
application.run()
To test uWSGI:
uwsgi --socket 0.0.0.0:8000 --protocol=http -w wsgi
To configure uWSGI:
-
Create a configuration file
.ini
nano myexample.ini
-
Basic configuration for gateway uWSGI
include header for using uwsgi
[uwsgi]
point it to your python module wsgi.py
module = wsgi
tell uWSGI to start a master node to serve requests
master = true
spawn number of processes handling requests
processes = 5
use a Unix socket to communicate with Nginx. Nginx will pass connections to uWSGI through a socket, instead of using ports. This is preferable because Nginx and uWSGI stays on the same machine.
socket = myexample.sock
ensure file permission on socket to be readable and writable
chmod-socket = 660
clean the socket when processes stop
vacuum = true
use die-on-term to communicate with Ubuntu versions using Upstart initialisations: see:
https://uwsgi-docs.readthedocs.io/en/latest/Upstart.html?highlight=die%20on%20term
die-on-term = true
optional if you are using virtual env You can deactivate
your virtual environment.
Nginx configuration We are gonna use nginx as:
- default server to pass request to the socket, using uwsgi protocol
- proxy-server in front of default server
- cache server to cache successful requests (as example, you may want to cache GET requests if your web application)
Locate your sites-available
directory and create a configuration file for your application:
sudo nano /etc/nginx/sites-available/myexample
Add following block, in comments what it does:
server {
# setting up default server listening to port 80
listen 8000 default_server;
server_name myexample.com; #you can also use your IP
# specify charset encoding, optional
charset utf-8;
# specify root of your folder directory
root /var/www/myexample;
# specify locations for your web apps.
# here using /api endpoint as example
location /api {
# include parameters of wsgi.py and pass them to socket
include uwsgi_params;
uwsgi_pass unix:/var/www/myexample/myexample.sock;
}
}
# Here you will specify caching zones that will be used by your virtual server
# Cache will be stored in /tmp/nginx folder
# ensure nginx have permissions to write and read there!
# See also:
# https://nginx.org/en/docs/http/ngx_http_proxy_module.html
proxy_cache_path /tmp/nginx levels=1:2 keys_zone=my_zone:10m inactive=60m;
proxy_cache_key "$scheme$request_method$host$request_uri";
# set up the virtual host!
server {
listen 80 default_server;
# Now www.example.com will listen to port 80 and pass request to https://example.com
server_name www.example.com;
# Why not caching responses
location /api {
# set up headers for caching
add_header X-Proxy-Cache $upstream_cache_status;
# use zone specified above
proxy_cache my_zone;
proxy_cache_use_stale updating;
proxy_cache_lock on;
# cache all responses ?
# proxy_cache_valid 30d;
# better cache only 200 responses :)
proxy_cache_valid 200 30d;
# ignore headers to make cache expire
proxy_ignore_headers X-Accel-Expires Expires Cache-Control;
# pass requests to default server on port 8000
proxy_pass https://example.com:8000/api;
}
}
Finally, link the file to sites-enabled
directory.
For an explanation of available and enabled sites, see answer:
[https://serverfault.com/a/527644]
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled
You are done now with nginx. However, you may want to check out this very precious boiler template: [https://github.com/h5bp/server-configs-nginx]
Very useful for fine tuning.
Now test Nginx:
sudo nginx -t
Launch Nginx:
sudo service nginx restart
Automate Ubuntu to start uWSGI The last thing is to make Ubuntu start the wsgi gateway communicating with your application, otherwise you should do it manually.
- Locate directory for initialisation scripts in Ubuntu, and create a new script:
sudo nano /etc/init/myexample.conf
-
Add following block, comments in line to explain what it does
# description for the purpose of this script description "uWSGI server instance configured to serve myproject" # Tell to start on system runtime 2, 3, 4, 5. Stop at any other level (0,1,6). # Linux run levels: [https://www.debianadmin.com/debian-and-ubuntu-linux-run-levels.html] start on runlevel [2345] stop on runlevel [!2345] # Set up permissions! "User" will be the username of your user account on ubuntu. setuid user # Allow www-data group to read and write from the socket file. # www-data is normally the group Nginx and your web applications belong to. # you may have all web application projects under /var/www/ that belongs to www-data group setgid www-data # tell Ubunutu which environment to use. # This is the path of your virtual environment: python will be in this path if you installed virtualenv. Otherwise, use path of your python installation env PATH=/var/www/myexample/myexample/bin # then tell to Ubuntu to change and locate your web application directory chdir /var/www/myexample # finally execute initialisation script, that load your web app myexample.py exec uwsgi --ini myexample.ini
Now you can activate your script: sudo start myexample