uWSGI: How can I mount a paste-deploy (Pyramid) app?

1.2k views Asked by At

What I have:

I have a Pyramid application that is built from a Paste ini, served by uWSGI and proxied by nginx. It works great. Here is the nginx config:

server {
    listen 80;
    server_name localhost;
    access_log /var/log/myapp/nginx.access.log;
    error_log /var/log/myapp/nginx.error.log warn;

    location / {
        uwsgi_pass localhost:8080;
        include uwsgi_params;
    }
}

Here is the uWSGI ini configuration:

[uwsgi]
socket = 127.0.0.1:8080
virtualenv = /srv/myapp/venv
die-on-term = 1
master = 1
logto = /var/log/myapp/uwsgi.log

This configuration is located inside Pyramid's production.ini, such that I serve the application with this command:

uwsgi --ini-paste-logged production.ini

All of this works just fine.

What I want to do:

One simple change. I want to serve this application as a subfolder, rather than as the root. Rather than serving it from http://localhost, I want to serve it from http://localhost/myapp.

And now everything is broken.

If I change the nginx location directive from / to /myapp or /myapp/, I get 404s, because the WSGI application receives uris that are all prepended with /myapp.

The uWSGI solution appears to be to mount the WSGI callable on the subfolder, and then pass the --manage-script-name option, at which point uWSGI should magically strip the subfolder prefix from the uri and fix the issue.

However, the documentation and every other resource I've found have only given examples of the form:

mount = /myapp=myapp.py

I don't have a myapp.py that contains a WSGI callable, because my callable is being built by PasteDeploy.

So, is it possible to mount the WSGI callable from within the Paste ini? Or am I going to have to split the uwsgi configuration out of the Paste ini and also define a separate wsgi.py with a call to paste.deploy.loadapp to generate a wsgi callable that I can mount?

Or is there another way to serve this app as a subfolder from nginx while not messing up the url reversing?

3

There are 3 answers

0
TimSC On

I wanted to do what you suggest but this is the closest solution I could find: if you are willing to modify your PasteDeploy configuration, you can follow the steps at: http://docs.pylonsproject.org/docs/pyramid/en/1.0-branch/narr/vhosting.html

Rename [app:main] to [app:mypyramidapp] and add a section reading:

[composite:main]
use = egg:Paste#urlmap
/myapp = mypyramidapp

I also had to add this to my nginx configuration:

uwsgi_param     SCRIPT_NAME '';

and install the paste module

sudo pip3 install paste

I wonder if there is a way to "mount" a PasteDeploy as to original question asked...

0
Code Painters On

I've hit this very problem with my deployment after switching from Python2 to Python3.

  1. with Python2 I used the uwsgi_modifier1 30; trick, but it doesn't work anymore with Python3, as described here: https://github.com/unbit/uwsgi/issues/876

  2. It is very badly documented (not at all? I know it from reading the uWSGI source code), but --mount option accepts the following syntax:

--mount=/app=config:/path/to/app.ini

Please note: with --mount you also need --manage-script-name option.

There are other problems with it: https://github.com/unbit/uwsgi/issues/2172

  1. It's trivial to write a wrapper script around Paste-Deploy app, which is the way I deploy now:
from paste.script.util.logging_config import fileConfig as configure_logging
from paste.deploy import loadapp as load_app
from os import environ

config_file = environ['INI_FILE']

configure_logging(config_file)
application = load_app('config:' + config_file)

Save it to e.g. app.py and you can use it with --mount /app=app.py, the INI_FILE environment var should point to your .ini file.

As a side note - I consider moving away from uWSGI, it's buggy and documentation lacks a lot.

1
davidjb On

Yes, it's definitely possible to mount your Pyramid as a subdirectory with Nginx. What you'll need to use is the Modifier1 option from uWSGI like so:

location /myapp {
    include uwsgi_params;
    uwsgi_param SCRIPT_NAME /myapp;
    uwsgi_modifier1 30;
    uwsgi_pass localhost:8080;
}

The magic value of 30 tells uWSGI to remove the parameter of SCRIPT_NAME from the start of PATH_INFO in the request. Pyramid receives the request and processes it correctly.

As long as you're using the standard Pyramid machinery to generate URLs or paths within your application, SCRIPT_NAME will automatically be incorporated, meaning all URLs for links/resources etc are correct.

The documentation isn't the clearest, but there's more on the modifiers available at: https://uwsgi-docs.readthedocs.org/en/latest/Protocol.html