I try to wire Flask, gunicorn and nginx together but it ends up with 400 Bad request and 500 Errors.
Hope that anyone can help.
nginx conf
server {
listen 8000;
server_name 127.0.0.1;
location /Hello {
uwsgi_pass 127.0.0.1:8081;
}
}
wsgi.py snippet:
from app import app
app.run(host="127.0.0.1", port=8081)
app.py
app = Flask(__name__)
app.secret_key = "Not A Secret Anymore By Now"
#app.route('/Hello')
def hello():
return("Hello")
#if __name__ == "__main__":
# app.run(host='0.0.0.0', port=8000)
run cmd
export FLASK_APP=app
export FLASK_ENV=development
gunicorn --bind 0.0.0.0:8081 wsgi:app
test cases
curl http://localhost/ >>> returns nginx homepage
curl http://localhost/Hello >>> returns 404 Not found
curl http://localhost:8000/ >>> returns nginx homepage
curl http://localhost:8000/Hello >>> returns 502 Bad Gateway
curl http://localhost:8081 >>> return Connection refused
I try to get the test cases working, but have not clue why the errors appears, (nginx is restarted).
Thank you.
A few problems here...
Your view function ends without a return statement, which is probably the cause of the first 500 error. Better use:
#app.route('/Hello')
def hello():
print("Hello")
return 'success'
Also as you metntion uwsgi.py is emtpy, there's no app object to import there. Best point the gunicorn command at a valid app object:
gunicorn --bind 0.0.0.0:8081 app:app
Also the nginx location block expects the request to go to http://example.com/Hello/Hello. So better make the location block like the following. (Also ensure the port is the same one you provded to gunicorn's --bind flag).
location / {
proxy_pass http://127.0.0.1:8081;
}
Here I've also used proxy_pass as per the gunicorn deployment (with nginx) docs.
Now the request should work:
# Direct to app server...
curl -i http://localhost:8081/Hello
HTTP/1.1 200 OK
Server: gunicorn/20.0.4
# ...
success
and also:
# Via nginx:
curl -i http://localhost/Hello
HTTP/1.1 200 OK
Server: nginx
...
success
Beware that running gunicorn on all interfaces (0.0.0.0) means external requests could reach the app server directly (bypassing nginx) if no firewall is in place to prevent this. Might be worth instead binding gunicorn to the local interface: --bind 127.0.0.1:8081.
Also be careful in general, there's lots of ways to make security slip-ups when configuring stuff like this yourself. Good luck.
Related
I have a Flask back end that is functional without using uwsgi and nginx.
I'm trying to deploy it on an EC2 instance with its front-end.
No matter what I do, I can't reach the back-end. I opened all the ports for testing purposes but that does not help.
Here's my uwsgi ini file:
[uwsgi]
module = main
callable = app
master = true
processes = 1
socket = 0.0.0.0:5000
vacuum = true
die-on-term = true
Then I use this command to start the app:
uwsgi --ini uwsgi.ini
The message returned is
WSGI app 0 (mountpoint='') ready in 9 seconds.
spawned uWSGI worker 1 (and the only) PID: xxxx
Then here is my Nginx conf file:
server {
server_name my_name.com www.ny_name.com
location / {
root /home/ubuntu/front_end/dist/;
}
location /gan {
proxy_pass https:localhost:5000/gan;
}
## below https conf by certbot
}
If my understanding is correct, whenever a request reaches "my_name.com/gan..." it will be redirected to the localhost on the port 5000 where the back-end is started by uwsgi.
But I can't reach it. I'm trying to simply do a get request on "my_name.com/gan" on my browser (it should return a random image) but I get a 502 by nginx.
Important to note, the front-end works fine and I can access it on browser.
My guess is that url is not in proper form
Try
proxy_pass http://0.0.0.0:5000;
This error occurs for me solely when using flask restplus with an nginx server. The server works correctly when just flask is used and the flask-restplus api works fine when running the app locally or even with:
uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi:application
on my EC2 instance.
I have tried using gunicorn along side nginx (rather than uwsgi) but I get the same error in my /var/log/nginx/error.log file:
2017/07/03 20:30:02 [crit] 957#957: *1 connect() to unix:/home/jamie/my_app/my_app.sock failed
(2: No such file or directory) while connecting to upstream,
client: #.#.#.#, server: #.#.#.#, request: "GET /favicon.ico HTTP/1.1", upstream:
"uwsgi://unix:/home/jamie/my_app/my_app.sock:", host: "#.#.#.#", referrer: "http://#.#.#.#/"
The app is envoked through a wsgi.py that looks like this:
from main import application
if __name__ == "__main__":
application.run('0.0.0.0')
And the main.py file contains the following:
from boto3 import resource
from flask import json, request, abort
from flask import Flask
from flask_restplus import Api, Resource, fields
application = Flask(__name__)
api = Api(application)
#api.route('/users', methods=['GET', 'POST'])
class Users(Resource):
def get(self):
return users_get()
#api.expect(resource_fields)
def post(self):
return user_login()
#api.route('/users/<string:Username>', methods=['GET', 'PUT', 'DELETE'])
class User(Resource):
def get(self, Username):
return user_get(Username)
def put(self, Username):
return create_user(Username)
def delete(self, Username):
return delete_user(Username)
In order to configure the server I followed this digitalocean tutorial and my config is essentially the same. If anyone wants to see it I can provide it.
My uwsgi ini file is as follows:
[uwsgi]
module = wsgi:application
master = true
processes = 5
socket = my_app.sock
chmod-socket = 666
vacuum = true
die-on-term = true
My nginx sever config is at etc/nginx/sites-available/my_app
like so:
server {
listen 80;
server_name #.#.#.#;
location / {
include uwsgi_params;
uwsgi_pass unix:/home/jamie/my_app/my_app.sock;
}
}
And finally my /etc/systemd/system/fitness_test_app.service file is like so:
[Unit]
Description=uWSGI instance to serve my_app
After=network.target
[Service]
User=jamie
Group=www-data
WorkingDirectory=/home/jamie/my_app
Environment="PATH=/home/jamie/my_app/myprojectenv/bin"
ExecStart=/home/jamie/my_app/myprojectenv/bin/uwsgi --ini my_app.ini
[Install]
WantedBy=multi-user.target
If anyone has any idea what could cause this problem an answer would be greatly appreciated as I have been unable to solve this issue for some time.
I don't know exactly what I am doing but I am experimenting with running Flask on nginx. I am boiling it down to the simple bit of code below. First I have a test app in Flask like this:
from flask import Flask, render_template
app = Flask(__name__, static_folder='client', template_folder='client/html')
def show_home_page():
return render_template("home.html")
#app.route('/')
def server():
return show_home_page()
if __name__ == '__main__':
app.run(threaded=True)
If I run python app.py I can go to http://localhost:5000 and see the "Hello World". Next I read that I need to run uwsgi but its not clear what params I need to pass to it. I tried different things like:
uwsgi -s /tmp/app.sock --manage-script-name --mount ./=app:app
I also noted that I need to set my nginx conf file to match, but I am stuck on that as well (I just get a welcome from nginx on port 5000) and it doesnt seem to link with my Flask app. I googled around a bit but nothing has clicked yet.
server {
listen 5000;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
You can use refer this for Flask with Nginx and uWSGI:
Python flask with Nginx and uWSGI
What is the clear way to run flask application with gevent backend server and utilize all processor cores? I have idea to run multiple copies of flask application where gevent WSGIServer listen one port in diapason 5000..5003 (for 4 processes) and nginx as load balancer.
But I'm not sure that this way is the best and may be there are some other ways to do it. For example, master process listen one port and workers process incoming connections.
I'll take a shot!
Nginx!
server section:
location / {
include proxy_params;
proxy_pass http://127.0.0.1:5000;
}
Flask App
This is a simple flask app that i will be using for this example.
myapp.py:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
uWSGI
Okay so I know that you said that you wanted to use gevent, but if you are willing to compromise on that a little bit I think you would be very happy with this setup.
[uwsgi]
master = true
plugin = python
http-socket = 127.0.0.1:5000
workers = 4
wsgi-file = myapp.py
callable = app
Gunicorn
If you must have gevent you might like this little setup
config.py:
import multiprocessing
workers = multiprocessing.cpu_count()
bind = "127.0.0.1:5000"
worker_class = 'gevent'
worker_connections = 30
Then you can run:
gunicorn -c config.py myapp:app
Thats right you have a worker for each cpu and 30 connections per worker.
See if that works for you.
If you are really sold on using nginx as a load balancer try something like this in your http section
upstream backend {
server 127.0.0.1:5000;
server 127.0.0.1:5002;
server 127.0.0.1:5003;
server 127.0.0.1:5004;
}
then one of these in the server section
location / {
include proxy_params;
proxy_pass http://backend;
}
Good Luck buddy!
Is it possible to disable nginx's custom error pages - if I may call them that - to display my framework's exception pages?
I can't really see my werkzeug debugger tool rendered in html...
UPDATE
OK, I got to make a very very simple flask application to work and I'll post the bits:
/home/my_user/.virtualenvs/nginx-test/etc/nginx.conf
worker_processes 1;
events { worker_connections 1024; }
http {
server {
listen 5000;
server_name localhost;
access_log /home/my_user/.virtualenvs/nginx-test/lib/nginx/access.log;
error_log /home/my_user/.virtualenvs/nginx-test/lib/nginx/error.log;
location / {
include uwsgi_params;
uwsgi_pass unix:/tmp/uwsgi.sock;
}
}
}
/home/my_user/dev/nginx_test/___init___.py
from flask import Flask
app = Flask(__name__)
#app.route('/')
def index():
raise Exception()
if __name__ == '__main__':
app.run('0.0.0.0', debug=True)
PYTHONPATH environment variable:
$ echo $PYTHONPATH
/home/my_user/dev/
How I run uwsgi:
$ uwsgi -s /tmp/uwsgi.sock --module nginx_test --callable app
How I run nginx:
$ nginx -c ~/.virtualenvs/nginx-test/etc/nginx.conf -p ~/.virtualenvs/nginx-test/lib/nginx/
If I hit the root page:
If I run nginx manually like:
python /home/my_user/dev/nginx_test/___init___.py
I will see instead, and what I want to see:
Of course I made sure it would work when I didn't raise the exception, but returned 'Hello World' for example on my index() function.
This is referred to custom error pages in .NET. I want to disable this and let nginx/uwsgi pass the html generated by the debugger directly to the browser instead of the internal server error thing.
UPDATE 2
Now if I change my flask app to enable debugging mode by:
/home/my_user/dev/nginx_test/___init___.py
from flask import Flask
app = Flask(__name__)
app.config.update(DEBUG=True)
#app.route('/')
def index():
raise Exception()
if __name__ == '__main__':
app.run('0.0.0.0', debug=True)
Then I get 502 error.
But if I instead of raise Exception:
/home/my_user/dev/nginx_test/___init___.py
from flask import Flask
app = Flask(__name__)
app.config.update(DEBUG=True)
#app.route('/')
def index():
return 'Hello World'
if __name__ == '__main__':
app.run('0.0.0.0', debug=True)
I get 'Hello World' on my browser when I hit the page (http://localhost:5000).
This "Internal Server Error" page is not from nginx but from Flask. It does so when debug mode is off.
uwsgi is importing your code as a module, not running at as a script. __name__ == '__main__' is False and the if statement is not executed. Try setting debug mode outside of the if:
app = Flask(__name__)
app.debug = True
However, it is strongly recommended to never leave the debug mode on a server on the public internet, since the user can make the server run any code. This is a serious security issue.
Use Flask#errorhandler to register your own error handlers in flask. For example to replace the 404 you would do something like:
app = Flask()
#app.errorhandler(404)
def handel_404(error):
return render_template('404.html')
Simon Sapin has really given you the correct answer. You need to enable debug in Flask. Nginx does not return any custom error pages unless you explictly configure it to do so.
If you use the following code you will see your debug messages from flask, proxied via Nginx.
from flask import Flask
app = Flask(__name__)
app.debug = True
#app.route('/')
def index():
raise Exception()
As per your update 2. You are seeing a 502 (bad gateway) because Flask is simply not returning any response at all, or a response that Nginx does not understand. A 502 is not a problem with Nginx. It means that whatever Nginx is trying to talk (your flask app in this case) is not working properly at all.
However, in many ways you shouldn't be doing this. Debugging mode should only be enabled when you are running flask on your local dev machine. Which is the whole point of the if __name__ == "__main__": line anyway.