2

I am running a flask app using gunicorn behind nginx proxy and am trying to get the gaiohttp worker to work. The app just returns 404 for all URLS when the gaiohttp worker is selected

When using the sync or gevent workers everything works fine. Also not running directly to gunicorn and gaiohttp i.e. not using nginx it works fine.

I have read everything I can find.

Am I missing something? Is gaiohttp worker valid when running behind a nginx proxy?

My nginx config:

location /app {
    proxy_pass http://127.0.0.1:9002;
    rewrite    /app(.*) /$1  break;
    proxy_redirect     off;
    proxy_buffering on;
    proxy_pass_header Server;
    proxy_set_header X-Scheme $scheme;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Host $server_name;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Script-Name /app;
    }

gunicorn:

/usr/bin/gunicorn --workers 2 -k gaiohttp -b 127.0.0.1:9002 app:app

Using the latest versions of gunicorn etc etc.

4

2 回答 2

2

I managed to work this out.

The line here was causing the issue in the nginx config:

rewrite /app(.*) /$1 break;

And I needed a proxy middleware in my Flask app to handle the reverse proxy correctly.

class ReverseProxied(object):
'''Wrap the application in this middleware and configure the
front-end server to add these headers, to let you quietly bind
this to a URL other than / and to an HTTP scheme that is
different than what is used locally.

In nginx:
location /myprefix {
    proxy_pass http://192.168.0.1:5001;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Scheme $scheme;
    proxy_set_header X-Script-Name /myprefix;
    }

:param app: the WSGI application
'''
def __init__(self, app):
    self.app = app

def __call__(self, environ, start_response):
    script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
    if script_name:
        server = environ.get('HTTP_X_FORWARDED_SERVER', '')
        if server:
            environ['HTTP_HOST'] = server
        environ['SCRIPT_NAME'] = script_name
        path_info = environ['PATH_INFO']
        if path_info.startswith(script_name):
            environ['PATH_INFO'] = path_info[len(script_name):]

    scheme = environ.get('HTTP_X_SCHEME', '')
    if scheme:
        environ['wsgi.url_scheme'] = scheme
    return self.app(environ, start_response)

In the __init__.py of the app: app.wsgi_app = ReverseProxied(app.wsgi_app)

于 2016-04-02T06:03:02.403 回答
0

Another solution is to Add a prefix to all Flask routes.

Sub-mounted inside of another WSGI container

Assuming that you are going to run this application inside of a WSGI container (mod_wsgi, uwsgi, gunicorn, etc); you need to actually mount, at that prefix the application as a sub-part of that WSGI container (anything that speaks WSGI will do) and to set your APPLICATION_ROOT config value to your prefix:

app.config["APPLICATION_ROOT"] = "/app"

@app.route("/")
def index():
    return "The URL for this page is {}".format(url_for("index"))

# Will return "The URL for this page is /app"

Setting the APPLICATION_ROOT config value simply limit Flask's session cookie to that URL prefix. Everything else will be automatically handled for you by Flask and Werkzeug's excellent WSGI handling capabilities.

An example of properly sub-mounting your app

If you are not sure what the first paragraph means, take a look at this example application with Flask mounted inside of it:

from flask import Flask, url_for
from werkzeug.serving import run_simple
from werkzeug.wsgi import DispatcherMiddleware

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/app'

@app.route('/')
def index():
    return 'The URL for this page is {}'.format(url_for('index'))

def simple(env, resp):
    resp(b'200 OK', [(b'Content-Type', b'text/plain')])
    return [b'Hello WSGI World']

app.wsgi_app = DispatcherMiddleware(simple, {'/app': app.wsgi_app})

if __name__ == '__main__':
    app.run('localhost', 5000)

Proxying requests to the app

If, on the other hand, you will be running your Flask application at the root of its WSGI container and proxying requests to it (for example, if it's being FastCGI'd to, or if nginx is proxy_pass-ing requests for a sub-endpoint to your stand-alone uwsgi / gevent server then you can either:

  • Use a Blueprint, as Miguel points out in his answer.
  • or use the DispatcherMiddleware from werkzeug (or the PrefixMiddleware from su27's answer) to sub-mount your application in the stand-alone WSGI server you're using. (See An example of properly sub-mounting your app above for the code to use).
于 2020-05-30T20:47:34.243 回答