uWSGI with cookiecutter Pyramid application Part 1: Basic uWSGI + nginx¶
uWSGI
is a software application for building hosting services.
It is named after the Web Server Gateway Interface (the WSGI specification
to which many Python web frameworks conform).
This guide will outline broad steps that can be used to get a cookiecutter
Pyramid application running under uWSGI
and nginx. This particular
tutorial was developed and tested on Ubuntu 18.04, but the instructions should be
largely the same for all systems, where you may adjust specific path information
for commands and files.
注釈
For those of you with your hearts set on running your Pyramid application under uWSGI, this is your guide.
However, if you are simply looking for a decent-performing production-grade server with auto-start capability, Waitress + systemd has a much gentler learning curve.
With that said, let's begin.
Install prerequisites.
$ sudo apt install -y uwsgi-core uwsgi-plugin-python3 python3-cookiecutter \ python3-pip python3-venv nginx
Create a Pyramid application. For this tutorial we'll use the
starter
cookiecutter. See Creating a Pyramid Project for more in-depth information about creating a new project.$ cd ~ $ python3 -m cookiecutter gh:Pylons/pyramid-cookiecutter-starter
If prompted for the first item, accept the default
yes
by hitting return.1You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before. 2Is it okay to delete and re-clone it? [yes]: yes 3project_name [Pyramid Scaffold]: myproject 4repo_name [myproject]: myproject 5Select template_language: 61 - jinja2 72 - chameleon 83 - mako 9Choose from 1, 2, 3 [1]: 1
Create a virtual environment which we'll use to install our application.
$ cd myproject $ python3 -m venv env
Install your Pyramid application and its dependencies.
$ env/bin/pip install -e ".[testing]"
Create a new directory at
~/myproject/tmp
to house a pidfile and a unix socket. However, you'll need to make sure that two users have access to change into the~/myproject/tmp
directory: your current user (mine isubuntu
), and the user that nginx will run as (often namedwww-data
ornginx
).Add a
[uwsgi]
section toproduction.ini
. Here are the lines to include:1[uwsgi] 2proj = myproject 3chdir = /home/ubuntu/%(proj) 4processes = 2 5threads = 2 6offload-threads = 2 7stats = 127.0.0.1:9191 8max-requests = 5000 9master = True 10vacuum = True 11enable-threads = true 12harakiri = 60 13chmod-socket = 020 14plugin = python3 15pidfile=%(chdir)/tmp/%(proj).pid 16socket = %(chdir)/tmp/%(proj).sock 17virtualenv = %(chdir)/env 18uid = ubuntu 19gid = www-data 20# Uncomment `wsgi-file`, `callable`, and `logto` during Part 2 of this tutorial 21#wsgi-file = wsgi.py 22#callable = app 23#logto = /var/log/uwsgi/%(proj).log
And here is an explanation of the salient options:
1# Explanation of Options 2# 3# proj = myproject # Set a variable named "proj" 4# so we can use it elsewhere in this 5# block of config. 6# 7# chmod-socket = 020 # Change permissions on socket to 8# at least 020 so that, in combination 9# with "--gid www-data", nginx will be able 10# to write to it after uWSGI creates it. 11# 12# enable-threads # Execute threads that are in your app 13# 14# plugin = python3 # Use the python3 plugin 15# 16# socket = %(chdir)/tmp/%(proj).sock # Where to put the unix socket 17# pidfile=%(chdir)/tmp/%(proj).pid # Where to put PID file 18# 19# uid = ubuntu # Masquerade as the ubuntu user. 20# This grants you permissions to use 21# python packages installed in your 22# home directory. 23# 24# gid = www-data # Masquerade as the www-data group. 25# This makes it easy to allow nginx 26# (which runs as the www-data group) 27# access to the socket file. 28# 29# virtualenv = (chdir)/env # Use packages installed in your 30# virtual environment.
Invoke uWSGI with
--ini-paste-logged
.There are multiple ways to invoke uWSGI. Using
--ini-paste-logged
is the easiest, as it does not require an explicit entry point.1$ cd ~/myproject 2$ sudo uwsgi --plugin python3 --ini-paste-logged production.ini 3 4# Explanation of Options 5# 6# sudo uwsgi # Invoke as sudo so you can masquerade 7# as the users specfied by ``uid`` and 8# ``gid`` 9# 10# --plugin=python3 # Use the python3 plugin 11# 12# --ini-paste-logged # Implicitly defines a wsgi entry point 13# so that you don't have to. 14# Also enables logging.
Verify that the output of the previous step includes a line that looks approximately like this:
WSGI app 0 (mountpoint='/') ready in 1 seconds on interpreter 0x5615894a69a0 pid: 8827 (default app)
If any errors occurred, you will need to correct them. If you get a
uwsgi: unrecognized option '--ini-paste-logged'
, make sure you are specifying the python3 plugin.If you get an error like this:
Fatal Python error: Py_Initialize: Unable to get the locale encoding ModuleNotFoundError: No module named 'encodings'
check that the
virtualenv
option in the[uwsgi]
section of your.ini
file points to the correct directory. Specifically, it should end inenv
, notbin
.For any other import errors, it probably means that the package either is not installed or is not accessible by the user. That's why we chose to masquerade as the normal user that you log in as, so you would for sure have access to installed packages.
If you get almost no output at all, yet the process still appears to be running, make sure that
logto
is commented out inproduction.ini
.Add a new file at
/etc/nginx/sites-enabled/myproject.conf
with the following contents. Also change any occurrences of the wordubuntu
to your actual username.1server{ 2 server_name _; 3 4 root /home/ubuntu/myproject/; 5 6 location / { 7 include uwsgi_params; 8 # The socket location must match that used by uWSGI 9 uwsgi_pass unix:/home/ubuntu/myproject/tmp/myproject.sock; 10 } 11}
If there is a file at
/var/nginx/sites-enabled/default
, remove it so your new nginx config file will catch all traffic. (Ifdefault
is in use and important, simply add a realserver_name
to/etc/nginx/sites-enabled/myproject.conf
to disambiguate them.)Reload nginx.
$ sudo nginx -s reload
Visit http://localhost in a browser. Alternatively call
curl localhost
from a terminal. You should see the sample application rendered.If the application does not render, tail the nginx logs, then refresh the browser window (or call
curl localhost
) again to determine the cause. (uWSGI should still be running in a separate terminal window.)$ cd /var/log/nginx $ tail -f error.log access.log
If you see a
No such file or directory
error in the nginx error log, verify the name of the socket file specified in/etc/nginx/sites-enabled/myproject.conf
. Verify that the file referenced there actually exists. If it does not, check what location is specified forsocket
in your.ini
file, and verify that the specified file actually exists. Once both uWSGI and nginx both point to the same file and both have access to its containing directory, you will be past this error. If all else fails, put your sockets somewhere writable by all, such as/tmp
.If you see an
upstream prematurely closed connection while reading response header from upstream
error in the nginx error log, something is wrong with your application or the way uWSGI is calling it. Check the output from the window where uWSGI is still running to see what error messages it gives when youcurl localhost
.If you see a
Connection refused
error in the nginx error log, check the permissions on the socket file that nginx says it is attempting to connect to. The socket file is expected to be owned by the userubuntu
and the groupwww-data
because those are theuid
andgid
options we specified in the.ini
file. If the socket file is owned by a different user or group than these, correct the uWSGI parameters in your.ini
file.If you are still getting a
Connection refused
error in the nginx error log, check permissions on the socket file. Permissions are expected to be020
as set by your.ini
file. The2
in the middle of020
means group-writable, which is required because uWSGI first creates the socket file, then nginx (running as the groupwww-data
) must have write permissions to it or it will not be able to connect. You can use permissions more open than020
, but in testing this tutorial020
was all that was required.Once your application is accessible via nginx, you have cause to celebrate.
If you wish to also add the uWSGI Emperor and systemd to the mix, proceed to part 2 of this tutorial: uWSGI with cookiecutter Pyramid Application Part 2: Adding Emperor and systemd.
uWSGI has many knobs and a great variety of deployment modes. This is just one representation of how you might use it to serve up a cookiecutter Pyramid application. See the uWSGI documentation for more in-depth configuration information.
This tutorial is modified from the original tutorial Running a Pyramid Application under mod_wsgi.