How to host multiple flask apps under a single domain hosted on nginx? - nginx

What I am trying to achieve:
I have an nginx web server hosting mydomain.com. When someone types my domain.com into their client I would like my server to serve index.html from /var/www/mydomain/ When they type mydomain.com/flaskapp1 they should see flaskapp1. When they type mydomain.com/flaskapp2 they should see flaskapp2.
I have managed to get one or the other flask apps served using the tutorial here http://www.markjberger.com/flask-with-virtualenv-uwsgi-nginx/ but when trying to implement serving two separate flask apps I run into difficulty. Instead of seeing the flask app I get a 404 message when I try to access either of the flask apps with mydomain.co.uk/flaskapp or mydomain.co.uk/flaskapp2 in a browser.
This is what I have so far:
cat /etc/nginx/sites-available/mydomain.co.uk
server {
listen 80;
server_name www.mydomain.co.uk mydomain.co.uk;
location / {
root /var/www/html/;
index index.html index.htm;
}
location /flaskapp {
include uwsgi_params;
uwsgi_pass unix:/tmp/flaskapp.sock;
}
location /flaskapp2 {
include uwsgi_params;
uwsgi_pass unix:/tmp/flaskapp2.sock;
}
}
The above conf file has been sim linked into /etc/nginx/sites-enabled.
cat /etc/uwsgi/apps-available/flaskapp.ini
[uwsgi]
vhost = true
socket = /tmp/flaskapp.sock
venv = /var/www/flaskapp/venv
chdir = /var/www/flaskapp
module = flaskapp
callable = app
cat /etc/uwsgi/apps-available/flaskapp2.ini
[uwsgi]
vhost = true
socket = /tmp/flaskapp2.sock
venv = /var/www/flaskapp2/venv
chdir = /var/www/flaskapp2
module = flaskapp2
callable = app
Both .ini files have been symlinked into /etc/uwsgi/apps-enabled. UWSGI restarts fine without any issues and is up and running. Both flaskapp.sock and flaskapp2.sock are owned by www-data
cat /var/www/flaskapp/flaskapp.py
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello World from flaskapp1!'
if __name__ == '__main__':
app.run(host='0.0.0.0')
cat /var/www/flaskapp2/flaskapp2.py
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello World from flaskapp2!'
if __name__ == '__main__':
app.run(host='0.0.0.0')
cat /var/www/mydomain.co.uk/index.html
<!DOCTYPE html>
<html>
<body>
<h1>mydomain.co.uk</h1>
<p>This is the index page of my domain.co.uk</p>
</body>
</html>
Both virtual environments have flask installed and will run the flask apps using the development server.
I hope it's something simple that I've missed.

Looking at the uwsgi documentation for NGINX here.
Specifically:
Unfortunately nginx is not able to rewrite PATH_INFO accordingly to
SCRIPT_NAME. For such reason you need to instruct uWSGI to map
specific apps in the so called “mountpoint” and rewrite SCRIPT_NAME
and PATH_INFO automatically:
Changing my flaskapp.ini and flaskapp2.ini files to contain the mount points for the apps and turning on the manage-script-name variable has worked.
cat /etc/uwsgi/apps-available/flaskapp.ini
[uwsgi]
vhost = true
socket = /tmp/flaskapp.sock
venv = /var/www/flaskapp/venv
chdir = /var/www/flaskapp
module = flaskapp
callable = app
mount = /flaskapp=flaskapp.py
manage-script-name = true
cat /etc/uwsgi/apps-available/flaskapp2.ini
[uwsgi]
vhost = true
socket = /tmp/flaskapp2.sock
venv = /var/www/flaskapp2/venv
chdir = /var/www/flaskapp2
module = flaskapp2
callable = app
mount = /flaskapp2=flaskapp2.py
manage-script-name = true
And now both flask apps are running via uwsgi through nginx as required.

The problem is here: both flask apps have no idea that they were served from subdirectory in your domain. Nginx is passing full URL to them, not URL relative to that directory, so every URL is dispatched relative to root. Let's see that in example:
Assume that you have 3 views in your flaskapp, served on urls /one/, /two/ and /three/. So when you're trying to access view on URL /one/, you are typing address: http://yourdomain.com/flaskapp/one/. Flask will receive from nginx URL /flaskapp/one/ but there is no such view in that app, so it will send 404 in response.
What you can do is inform each flaskapp that they aren't served on domain root, but on particular subdirectory. You can achieve this by sending SCRIPT_NAME uwsgi_param with subdirectory location in value, so your nginx config will have:
location /flaskapp {
include uwsgi_params;
uwsgi_pass unix:/tmp/flaskapp.sock;
uwsgi_param SCRIPT_NAME /flaskapp;
}
location /flaskapp2 {
include uwsgi_params;
uwsgi_pass unix:/tmp/flaskapp2.sock;
uwsgi_param SCRIPT_NAME /flaskapp2;
}

Adding the mounting point worked for me.
/etc/uwsgi/apps-available/flaskapp.ini
callable = app
mount = /flaskapp=/var/www/flaskapp
/etc/uwsgi/apps-available/flaskapp2.ini
callable = app
mount = /flaskapp2=/var/www/flaskapp2
Source https://neginfinity.bitbucket.io/2017/09/hosting-multiple-flask-applications-in-nginx.html

Related

flask+ngix+uwsgi not stripping script root from route ubuntu 20

I'm hoping someone can help me.
I am trying to re-deploy a set of flask apps on to a ubuntu 20 machine, from a Ubuntu 18 machine, but they are behaving differently to earlier deployments.
They have successfully been deployed on Ubuntu 14,16 and 18, including the conversion from python 2 to 3 at Ubuntu 18 deployment but the latest deployment on Ubuntu 20 has me totally stumped.
They are running with the config described below (successfully on Ubuntu 18). When deploying on a new Ubuntu 20 machine, flask is seeing the route as the script root (as well as the script root) which is resulting in a 404.
The setup currently working on U18 is as follows (nginx simplified for testing on non TLS connection)
The app running on :9999 is not on it's own location, and is working fine.
NGINX:
server {
underscores_in_headers on;
listen 80 default_server;
server_name _;
location /.well-known {
root /var/www/html/;
}
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:9999;
}
location /a {
uwsgi_param SCRIPT_NAME /a;
uwsgi_modifier1 30;
include uwsgi_params;
uwsgi_pass 127.0.0.1:10017;
}
}
UWSGI:
[uwsgi]
socket = :10017
plugin = python3
wsgi-file = /home/webmaster/app/run
callable = app
master = true
enable-threads = true
processes = 1
chdir= /home/webmaster/app
uid = www-data
gid = www-data
I set the 404 handler to return some URL information and instead of getting the app's landing page, I get the 404 with the following (i.e. nginx is passing to uwsgi, and the app is running)
URL:
http://192.168.0.250/a
Output:
url root http://192.168.0.250/a/
script root /a
request url http://192.168.0.250/a/a
request path /a
request full_path /a?
So url_root and script_root are as you'e expect, and what we want to see, but request_url (http://192.168.0.250/a/a) and request_path (/a) are not. For everything to have it's own location, request_path should be "/".
What I've tried
I referred to previous questions, particularly this one:
Q: Serving flask app on subdirectory nginx + uwsgi
and this one:
A: How to host multiple flask apps under a single domain hosted on nginx?
I've tried the suggestions in those posts, including the following nginx configurations:
super simple, with uwsgi being asked to do more:
location /c {
include uwsgi_params;
uwsgi_pass 127.0.0.1:10017;
}
with UWSGI:
[uwsgi]
socket = :10017
plugin = python3
wsgi-file = /home/webmaster/app/run
callable = app
master = true
enable-threads = true
processes = 1
chdir= /home/webmaster/app
uid = www-data
gid = www-data
mount = /a=run
manage-script-name = true
This did not solve the problem
I next tried a re-write to (try) and trick uwsgi into thinking that it is running without the script root
location /b {
rewrite ^/b/(.*) /$1 break;
include uwsgi_params;
uwsgi_pass 127.0.0.1:10017;
}
This too was unsuccessful
I have also been through the suggestion in the above questions discussion sections, and whilst most work on modifying the script root, none have remove the script root from the path enabling a flask route of "/start" to serve the root of the app identified by www.app.com/a/start
I am also aware that uwsgi_modifier1 30; is depreciated, although this is the first deployment of these flask apps where that has been an issue.
I have also tried to use the parameter script_name=None on the flask side as per this question:
How can I avoid uwsgi_modifier1 30 and keep WSGI my application location-independent
but nothing thus far has worked, and I'm totally stumped. It's probably something simple, but I don't know where to look from here.
Last thing, these apps run in emperor mode, and running uwsgi from command line, both as individual .ini files, or the emperor file makes no difference.
uwsgi is running as a service installed via apt, not pip (but appearing to running fine with plugin=python3) in case anyone has had experience with this being problematic.
I'd like to stick with the apt installation if at all possible.
Thanks heaps in advance to anyone that can help.
[on edit]
This is my wsgi file where I tried to let flask handle the static route. Should have included it earlier, but it was an oversight.
Thanks
#!/usr/bin/python3
from application import app
app.config['SECRET_KEY'] = 'XXXXXXXXXX'
app.config["SESSION_COOKIE_SECURE"] = True
app.config["REMEMBER_COOKIE_SECURE"] = True
app.config["SESSION_COOKIE_HTTPONLY"] = True
app.config["REMEMBER_COOKIE_HTTPONLY"] = True
app.config['APPLICATION_ROOT'] = '/a'
app.static_url_path = '/a'
if __name__ == "__main__":
app.run(debug = True, host= '0.0.0.0', port= 5000)
For others who stumble across this:
use re-write as per other questions advice, just watch the expression you use for correctness.
This will fail:
location /foo {
rewrite ^/foo/(.*) /$1 break;
uwsgi_param SCRIPT_NAME /foo;
include uwsgi_params;
uwsgi_pass 127.0.0.1:10017;
}
as it will only work for script name "foo", route "/bar" at URL "/foo/bar". The root of /foo will fail.
This will work:
rewrite ^/foo(.*) /$1 break;
e.g.
location /foo {
rewrite ^/foo(.*) /$1 break;
uwsgi_param SCRIPT_NAME /foo;
include uwsgi_params;
uwsgi_pass 127.0.0.1:10017;
}
it will pass the script name, and take it out of the request path which makes it work.
the problem is the extra "/" in the rewrite.
It was a simple mistake that took me a lot of finding as I couldn't see the forest for the trees... so I hope this can assist someone else.

My page is not responsive when I run the with UWSGI

I wanted a realtime dashboard and I cloned this https://github.com/roniemartinez/real-time-charts-with-flask.git
I installed the requirements and the application runs normally and fine whenever I run with the "python application.py" command. The "/" route in the application run responsively and fine...
I added the app to a uwsgi ini file and when I run. the web page of the "/" becomes irresponsive and bring the graphs every 30seconds. All works fine with the application. but I think the problem is either my nginx config or uwsgi ini.
what could be wrong ?
[uwsgi]
base = /home/pi/Desktop/Bingo
app = application
module = %(app)
home = %(base)/venv
pythonpath = /home/pi/Desktop/Bingo/venv/bin/python
socket = /home/pi/Desktop/Bingo/%n.sock
chmod-socket = 666
callable = app
logto = /home/pi/Desktop/Bingo/log/uwsgi/%n.log
My nginx config is
server {
listen 80;
server_name localhost;
charset utf-8;
client_max_body_size 75M;
location /static {
root /home/pi/Desktop/Bingo;
}
location / { try_files $uri #labapp;}
location #labapp {
include uwsgi_params;
uwsgi_pass unix:/home/pi/Desktop/Bingo/lab_app_uwsgi.sock;
}
}
The uwsgi logs probably contain some useful clues. Comparing your uwsgi.ini to one of mine, I see a few differences that might be significant. First, I always chdir to the base of the app, which in your case would look like
[uwsgi]
chdir = /home/pi/Desktop/Bingo
home = /home/pi/Desktop/Bingo/venv
module = application:application
... socket
... log
I also wonder if might also have a mismatch, depending on how you've named things, between
socket = /home/pi/Desktop/Bingo/%n.sock
and
uwsgi_pass unix:/home/pi/Desktop/Bingo/lab_app_uwsgi.sock;
The nginx error log is the place to look.
(See https://github.com/davewsmith/nginx-uwsgi-flask-starter/blob/master/provision/starter.ini for a working example)

uWSGI / Emperor: UnicodeEncodeError: 'ascii' codec can't encode character

i have big problem with encoding on uwsgi/emeror/nginx server.
My app is developed to batch excel files processing.
I use latest version flask and flask-extensions and use flask-excel.
My app runs on Digital Ocean / Ubuntu server and my config files are:
/etc/init/uwsgi.conf
description "uWSGI"
start on runlevel [2345]
stop on runlevel [06]
respawn
env UWSGI=/home/lukas/www/abissk/venv/bin/uwsgi
env LOGTO=/home/lukas/logs/abissk/emperor.log
exec $UWSGI --master --emperor /etc/uwsgi/vassals --die-on-term --uid www-data --gid www-data --logto $LOGTO
/etc/uwsgi/vassals/abissk_uwsgi.ini
[uwsgi]
plugins = python
#pcre = True
#application's base folder
base = /home/lukas/www/abissk/
#enable-threads = true
#python module to import
app = wsgi
module = %(app)
home = %(base)venv
pythonpath = %(base)
#socket file's location
socket = /home/lukas/www/abissk/%n.sock
#permissions for the socket file
chmod-socket = 644
#the variable that holds a flask application inside the module imported at line #6
callable = app
#location of log files
logto = /home/lukas/logs/abissk/%n.log
/home/lukas/www/abissk/wsgi.py
# -*- coding: utf-8 *-*
from app.app import create_app
from app.settings import ProdConfig
app = create_app(config_object=ProdConfig)
/etc/nginx/sites-enabled/abissk_nginx
server {
listen 80;
server_name benela.abis.sk;
charset utf-8;
client_max_body_size 75M;
location / { try_files $uri #yourapplication; }
location #yourapplication {
include uwsgi_params;
uwsgi_pass unix:/home/lukas/www/abissk/abissk_uwsgi.sock;
}
}
and here is my problem:
When start application with command:
~/www/abissk/venv/bin/uwsgi --ini /etc/uwsgi/vassals/abissk_uwsgi.ini
and upload excel files works all great BUT
when start same aplication with totaly same config files (in /etc/init/uwsgi.conf) with emperor, application works fine, but when upload excel file to batch processing, i see only message: "502 Bad Gateway" and in my log is:
UnicodeEncodeError: 'ascii' codec can't encode character '\xfd' in position 31: ordinal not in range(128)
i was try install uwsgi with apt-get / pip3 install and scenario is same: when run app directly without emperor, works all fine; when run app with emperor (with same configuration), every upload excel file to my app ends with crash :/
Thanks for any answer
add the following lines to your abissk_uwsgi.ini file to enforce uwsgi to use UTF-8.
env LANG=en_US.utf8
env LC_ALL=en_US.UTF-8
env LC_LANG=en_US.UTF-8
Add to uwsgi.ini:
env = LANG=en_US.UTF-8
Only this format helped for me
for Russian language add this in uwsgi.ini
env = LANG=ru_RU.utf8
env = LC_ALL=ru_RU.UTF-8
env = LC_LANG=ru_RU.UTF-8
None of the variables LANG, LC_ALL, LC_LANG helped me.
I fixed the bug only adding this to the uwsgi.ini:
env = PYTHONIOENCODING=UTF-8

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

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?
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
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...
I've hit this very problem with my deployment after switching from Python2 to Python3.
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
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
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.

How can I avoid uwsgi_modifier1 30 and keep WSGI my application location-independent?

I have a WSGI application using CherryPy hosted using uWSGI behind a ngnix server.
I would like for the application itself to be "portable". That is, the application should not know or care what URL it is mapped to, and should even work if mapped to multiple different URLs. I want to DRY by keeping the URL mapping information in one place only. Unfortunately, the only way I have found to do this involves using uwsgi_modifier 30, which has been called an ugly hack. Can I avoid that hack?
For the present purposes, I have created a tiny application called sample that demonstrates my question.
The ngnix config looks like this:
location /sample/ {
uwsgi_pass unix:/run/uwsgi/app/sample/socket;
include uwsgi_params;
uwsgi_param SCRIPT_NAME /sample;
uwsgi_modifier1 30;
}
The uwsgi config in /etc/uwsgi/apps-enabled/sample.js:
{
"uwsgi": {
"uid": "nobody",
"gid": "www-data",
"module": "sample:app"
}
}
...and the application itself:
#!/usr/bin/python
import cherrypy
class Root(object):
#cherrypy.expose
def default(self, *path):
return "hello, world; path=%r\n" % (path,)
app = cherrypy.Application(Root(), script_name=None)
It works:
The URL under which the application is mapped (/sample) appears only in one place: in the ngnix config file.
The application does not see that prefix and does not have to worry about it, it only receives whatever appears after /sample:
$ curl http://localhost/sample/
hello, world; path=()
$ curl http://localhost/sample/foo
hello, world; path=('foo',)
$ curl http://localhost/sample/foo/bar
hello, world; path=('foo', 'bar')
To motivate the reason for my question, let's say I have a development version of the application. I can make a second uwsgi app and point it to a different copy of the source code, add an extra location /sample.test/ { ... } to ngnix pointing to the new uwsgi app, and hack on it using the alternate URL without affecting the production version.
But it makes use of uwsgi_modifier1 30 which is supposedly an ugly hack:
http://uwsgi-docs.readthedocs.org/en/latest/Nginx.html
Note: ancient uWSGI versions used to support the so called “uwsgi_modifier1 30” approach. Do not do it. it is a really ugly hack
Now, I can do this:
location /something/ {
uwsgi_pass unix:/run/uwsgi/app/sample/socket;
include uwsgi_params;
}
...and this...
{
"uwsgi": {
"uid": "nobody",
"gid": "www-data",
"pythonpath": "", # no idea why I need this, btw
"mount": "/something=sample:app",
"manage-script-name": true
}
}
But it requires that I hardcode the path (/something) in 2 places instead of 1. Can I avoid that? Or should I stick with the original setup which uses uwsgi_modifier1 30?
My answer is really about simplifying things, because the following and the amount of configuration you have indicates one thing -- overkill.
CherryPy ⇐ WSGI ⇒ uWSGI ⇐ uwsgi ⇒ Nginx ⇐ HTTP ⇒ Client
CherryPy has production ready server that natively speaks HTTP. No intermediary protocol, namely WSGI, is required. For low traffic you can use it on its own. For high traffic with Nginx in front, like:
CherryPy ⇐ HTTP ⇒ Nginx ⇐ HTTP ⇒ Client
CherryPy has notion of an application and you can serve several applications with one CherryPy instance. CherryPy also can serve other WSGI applications. Recently I answer a related question.
Portability
The portability your are talking about is natively supported by CherryPy. That means you can mount an app to a given path prefix and there's nothing else to configure (well, as long as you build URLs with cherrypy.url and generally keep in mind that the app can be mounted to different path prefixes).
server.py
#!/usr/bin/env python3
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8
}
}
# proxy tool is optional
stableConf = {'/': {'tools.proxy.on': True}}
develConf = {'/': {'tools.proxy.on': True}}
class AppStable:
#cherrypy.expose
def index(self):
return 'I am stable branch'
class AppDevel:
#cherrypy.expose
def index(self):
return 'I am development branch'
cherrypy.config.update(config)
cherrypy.tree.mount(AppStable(), '/stable', stableConf)
cherrypy.tree.mount(AppDevel(), '/devel', develConf)
if __name__ == '__main__':
cherrypy.engine.signals.subscribe()
cherrypy.engine.start()
cherrypy.engine.block()
server.conf (optional)
server {
listen 80;
server_name localhost;
# settings for serving static content with nginx directly, logs, ssl, etc.
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

Resources