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 nginxCreate a Pyramid application. For this tutorial we'll use the
startercookiecutter. See Creating a Pyramid Project for more in-depth information about creating a new project.$ cd ~ $ python3 -m cookiecutter gh:Pylons/pyramid-cookiecutter-starterIf prompted for the first item, accept the default
yesby 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 envInstall your Pyramid application and its dependencies.
$ env/bin/pip install -e ".[testing]"Create a new directory at
~/myproject/tmpto house a pidfile and a unix socket. However, you'll need to make sure that two users have access to change into the~/myproject/tmpdirectory: your current user (mine isubuntu), and the user that nginx will run as (often namedwww-dataornginx).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-loggedis 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
virtualenvoption in the[uwsgi]section of your.inifile 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
logtois commented out inproduction.ini.Add a new file at
/etc/nginx/sites-enabled/myproject.confwith the following contents. Also change any occurrences of the wordubuntuto 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. (Ifdefaultis in use and important, simply add a realserver_nameto/etc/nginx/sites-enabled/myproject.confto disambiguate them.)Reload nginx.
$ sudo nginx -s reload
Visit http://localhost in a browser. Alternatively call
curl localhostfrom 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.logIf you see a
No such file or directoryerror 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 forsocketin your.inifile, 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 upstreamerror 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 refusederror 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 userubuntuand the groupwww-databecause those are theuidandgidoptions we specified in the.inifile. If the socket file is owned by a different user or group than these, correct the uWSGI parameters in your.inifile.If you are still getting a
Connection refusederror in the nginx error log, check permissions on the socket file. Permissions are expected to be020as set by your.inifile. The2in the middle of020means 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 tutorial020was 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.