Nginx not forwarding traffic to Gunicorn - python

I have a Flask app that I'm trying to host with Gunicorn and Nginx. I've followed several tutorials and I cannot seem to get Nginx to stop rendering the "Welcome to Nginx" page.
I've already confirmed that Supervisor successfully launches three Gunicorn workers, and Nginx is running in the background as well. SSL is confirmed working and Nginx picks up traffic on port 443. I've attempted to add Nginx to the same group as the user who owns the sock shared by Gunicorn and Nginx.
This is the bash script which Supervisor uses to start Gunicorn. This is confirmed working.
#!/bin/bash
NAME="Simon"
FLASKDIR=/var/www/Simon
SOCKFILE=/var/www/Simon/simon.sock
USER=glen
GROUP=glen
NUM_WORKERS=3
FLASK_SETTINGS_MODULE=config.py
FLASK_WSGI_MODULE=Simon.wsgi
echo "Starting $NAME as `whoami`"
# Activate the virtual environment
cd $FLASKDIR
source Simon/bin/activate
export FLASK_SETTINGS_MODULE=$FLASK_SETTINGS_MODULE
export PYTHONPATH=$FLASKDIR:$PYTHONPATH
# Create the run directory if it doesn't exist
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR
exec gunicorn Simon:simon \
--name $NAME \
--workers $NUM_WORKERS \
--user=$USER --group=$GROUP \
--bind=unix:$SOCKFILE \
--log-level=debug \
This the site config:
upstream simon_server {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response (in case the Unicorn master nukes a
# single worker for timing out).
server unix:/var/www/Simon/simon.sock fail_timeout=0;
}
server {
listen 443 ssl;
server_name glencoverx.com;
client_max_body_size 4G;
access_log /var/www/Simon/logs/nginx-access.log;
error_log /var/www/Simon/logs/nginx-error.log;
location /static/ {
alias /var/www/Simon/static/;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
proxy_redirect off;
if ($scheme != "https") {
return 301 https://$host$request_uri;}
}
ssl_certificate /etc/letsencrypt/live/glencoverx.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/glencoverx.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
}
Weirdly enough, the logs show no problems. I've completely deleted the Nginx default site config and removed its symlink. I don't understand why Nginx will not communicate with Gunicorn.

first it would be helpful if you would do something like:
listen 443 default ssl;
This will make that site default.
If you haven't already, service nginx restart and make sure that your cache is purged. Try using telnet to do a get to make sure that it's still enabled.
Check your nginx/conf.d and sites-enabled dirs to see if the symlink or the default website is still there.

Related

Need to run django application in a domain name without specific port

I am newbie to Django recently I created a Django app and I uploaded to the server. I assigned a domain name to it. each time I run the server I need to type xyz.com:8000 to see my website. Is there any way to resolve this issue? Also, I have doubt. Do I need to type python manage.py runserver 0:8000 to launch the website or it's just run automatically like PHP.
You should set up a web server!
The web server is required for any site to work. Currently the most popular are Apache and NGINX. It is the web server that responds to user requests. We need to ensure the interaction of the web server and the python application. Most popular solutions:
uwsgi
gunicorn
Consider an example with Nginx and Gunicorn:
Let's start by installing the Gunicorn module in a virtual environment:
pip install gunicorn
Configure the gunicorn service settings for our project:
sudo nano /etc/systemd/system/gunicorn.service
/etc/systemd/system/gunicorn.service:
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=my_user
Group=www-data
WorkingDirectory=/home/project_dir/project
ExecStart=/home/project_dir/project/venv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/project_dir/project/project.sock project.wsgi
[Install]
WantedBy=multi-user.target
We enable and run the gunicorn service, check its status:
sudo systemctl enable gunicorn
sudo systemctl start gunicorn
sudo systemctl status gunicorn
If all is well, install the nginx web server:
sudo apt install nginx
Configure the project site parameters:
sudo nano /etc/nginx/sites-available/project
/etc/nginx/sites-available/project:
server {
listen 80;
server_name <server IP or domain name>;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/project_dir/project;
}
location /media/ {
root /home/project_dir/project;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/project_dir/project/project.sock;
}
}
We use Nginx as a proxy for the gunicorn Python server:
proxy_pass http://unix:/home/project_dir/project/project.sock;
Create a link in the allowed sites folder “/etc/nginx/sites-enabled”:
sudo ln -s /etc/nginx/sites-available/project/etc/nginx/sites-enabled
We restart the Nginx service and add permissions to the firewall:
sudo systemctl restart nginx
sudo ufw allow 'Nginx Full'
Done! You can check the operation of our site by typing the IP address of the server in the browser.
P.S. Sorry for my english! If you see an error in the text or code, please edit me.

Bokeh server_document script loads from IP (HTTP) but not from domain (HTTPS)

I have a Bokeh application embedded in Flask and Gunicorn (see flask_gunicorn_embed.py). This works fine when I access everything through the IP address of the web server, but not when everything is proxied through Nginx. It will load everything from Flask, but not from Bokeh (the autoload.js).
Example:
I start Flask through Gunicorn with
gunicorn --workers 9 --bind 0.0.0.0:5000 --timeout 120 --log-file /some/directory/to/gunicorn/logs/gunicorn.log -m 0700 flask:app
I can now access everything from Flask through http://xxx.xxx.xxx.xxx:5000. I have adapted the flask_gunicorn_embed.py and the most important bits are
script = server_document('http://xxx.xxx.xxx.xxx:%d/bkapp' % port, resources=None)
sockets, port = bind_sockets("0.0.0.0", 0)
bokeh_tornado = BokehTornado({'/bkapp': bkapp}, extra_websocket_origins=["xxx.xxx.xxx.xxx:5000"])
If I now access a page which uses this Bokeh server everything works fine. It loads
http://xxx.xxx.xxx.xxx:XXXXX/bkapp/autoload.js?bokeh-autoload-element=1001&bokeh-app-path=/bkapp&bokeh-absolute-url=http://xxx.xxx.xxx.xxx:XXXXX/bkapp&resources=none
and displays the graphs, and creates a web socket for callbacks.
This is without Nginx as a reverse proxy. I don't want to use the IP address of the web server because I need to use HTTPS, which requires a domain.
Thus I have the following Nginx configuration:
server {
listen 80;
server_name example.com;
return 301 https://$server_name/;
}
server {
listen 443;
server_name example.com;
ssl on;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
client_max_body_size 50M;
location / {
include proxy_params;
proxy_pass http://127.0.0.1:5000;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host:$server_port;
proxy_buffering off;
}
location /static {
alias /some/directory/to/flask/static;
}
}
And I start Flask through Gunicorn with
gunicorn --workers 9 --bind 127.0.0.1:5000 --timeout 120 --log-file /some/directory/to/gunicorn/logs/gunicorn.log -m 0700 flask:app
I can now access everything from Flask through https://example.com. The flask_gunicorn_embed.py now looks like
script = server_document('https://example.com:%d/bkapp' % port, resources=None)
sockets, port = bind_sockets("0.0.0.0", 0)
bokeh_tornado = BokehTornado({'/bkapp': bkapp}, extra_websocket_origins=["example.com"])
However, every request that is generated through Bokeh's server_document
https://example.com:XXXXX/bkapp/autoload.js?bokeh-autoload-element=1001&bokeh-app-path=/bkapp&bokeh-absolute-url=https://example.com:XXXXX/bkapp&resources=none
results in either a connection timed out or a connection refused and thus not loading the scripts.
How can I make it such that it will connect and load the script?
It must have something to do with Nginx because if I request the file through the IP address it still works (due to the bind_sockets("0.0.0.0", 0)). But I cannot figure out what is causing this issue.
Edit:
It appears to be an issue with the fact that it uses HTTPS. My Nginx configuration is the same as the one given in the Bokeh documentation. The documentation says to use --use_xheaders, which is not possible because I am not using bokeh serve.
I do have
conf = {'use_xheaders': True}
bokeh_tornado = BokehTornado({'/bkapp': bkapp}, extra_websocket_origins=["example.com"], **conf)
bokeh_http = HTTPServer(bokeh_tornado, xheaders=True)
but it still won't load the scripts for HTTPS pages.
http://example_no_https.com will load the pages through the ports and https://example.com won't.
use_xheaders is an argument for the Bokeh Server class (which passes it to a Tornado HTTPServer), not by BokehTornado. If you are not using Server because you are coordinating BokehTornado and an HTTPServer yourself, then you will have manually configure this option on the HTTP server, since you don't have a Server class doing it for you:
http_server_kwargs.setdefault('xheaders', opts.use_xheaders)
HTTPServer(..., **http_server_kwargs)

Forwarding Nginx port to gunicorn instance

I was following this tutorial to setup my flask server.
https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-18-04#step-6-%E2%80%94-securing-the-application
When I got to step 6 I see that they are setting flask for the whole url but I would like to point it to a specific port.
This is the code I have for my nginx which points. This currenly produces a 404.
server {
listen 5000;
server_name site.com;
location / {
include proxy_params;
proxy_pass http://unix:/home/user/project/project.sock;
}
}
All the other files are the same as the tutorial. I have tried to modify the .sock file but it seems like it was generated automatically and it can't be modified. In addition I need to find a way for nginx to handle this before I worry about handling it from gunicorn.
My end goal is to have nginx foward requests to flask running when a request is sent to 0.0.0.0:5000 and have all other requests 0.0.0.0 , 0.0.0.0/* be handled by nginx.
Any help to undestand all this is really appreciated got lost at this point.
EDIT
my nginx configuration in sites-available
server {
server_name domain www.domain;
location / {
include proxy_params;
proxy_pass http://127.0.0.1:8080/;
}
}
If you want flask to be open to a port instead of a file you should override
[service] to
[service]
...
ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 -m 007 wsgi:app
And change your nginx config to proxy_pass http://127.0.0.1:8000/;
This way you can have access to port 8000 for checking how are working gunicorn and flask. Remember to be careful with firewall rules to secure port 8000. For a good discussion on which one is better you can try: gunicorn + nginx: Server via socket or proxy?

Python Django+Nginx+uwsgi 502 Bad Gateway

Centos7,when I connect to my website, shows 502 Bad Gateway,
I test my website with command
uwsgi --ini
systemctl start nginx
And I cant figure out what's happened,please help me!
here's nginx.conf
upstream django {
server 127.0.0.1:8000;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name example.com;
charset utf-8;
include /etc/nginx/default.d/*.conf;
location / {
include uwsgi_params;
uwsgi_pass django;
}
location /static/ {
alias /usr/local/etc/dmp/static/;
}
}
and uwsgi setting
[uwsgi]
chdir = /usr/local/etc/dmp
module = DMP_python.wsgi
plugins = python3
socket = :8000
chmod-socket = 666
master = true
processes = 2
vacuum = true
You're using the wrong setting to tell uwsgi to use an HTTP port. You need http-socket rather than socket.
There can be multiple reasons for which an upstream might return invalid or even do not return any response
Verify if upstream uwsgi is actually running locally in the centos instance and can handle incoming requests
for this to verify run it as http-socket = :8000 in uwsgi.ini and then run uwsgi --ini uwsgi.ini
if uwsgi running fine on localhost, then change config back to socket = :8000
On centos 7.x SELinux package is enabled and runs in enforcing mode. So it won't allow nginx to write/connect to a socket.
verify if SELinux has the policy for nginx to write to sockets
check if read/connect/write to socket is allowed grep nginx /var/log/audit/audit.log | audit2allow -m nginx
do so by grep nginx /var/log/audit/audit.log | audit2allow -M nginx
and finally semodule -i nginx.pp
permission to connect to socket issue should be resolved by now
verify if nginx can do network connection with upstream. Check nginx error.log or getsebool -a | grep httpd
to allow it run setsebool httpd_can_network_connect on -P

python - How to deploy Flask+Gunicorn+Nginx+supervisor on a cloud server?

I've read a lot of instructions since yesterday about this issue but all of them have similar steps. However I followed step by step but still can't get everything Ok.
Actually I can make Flask+Gunicorn+supervisor working but Nginx is not working well.
I connect my remote cloud server with SSH and I'm not deploying the site on my computer.
Nginx is installed correctly because when I visit the site via the domain name (aka. example.com) it shows the Nginx welcome page.
I use supervisor to start Gunicorn and the configuration is
[program:myapp]
command=/home/fh/test/venv/bin/gunicorn -w4 -b 0.0.0.0:8000 myapp:app
directory=/home/fh/test
startsecs=0
stopwaitsecs=0
autostart=false
autorestart=false
stdout_logfile=/home/fh/test/log/gunicorn.log
stderr_logfile=/home/fh/test/log/gunicorn.err
here I bind the server to port 8000 and I don't actually know what does 0.0.0.0 stand for but I think it doesn't mean the localhost because I can visit the site via http://example.com:8000 and it works well.
Then I tried to use Nginx as a proxy server.
I deleted /etc/nginx/sites-available/default' and '/etc/nginx/sites-enabled/default/ and created /etc/nginx/sites-available/test.com and /etc/nginx/sites-enabled/test.com and symlink them.
test.com
server {
server_name www.penguin-penpen.com;
rewrite ^ http://example/ permanent;
}
# Handle requests to example.com on port 80
server {
listen 80;
server_name example.com;
# Handle all locations
location / {
# Pass the request to Gunicorn
proxy_pass http://127.0.0.1:8000;
# Set some HTTP headers so that our app knows where the request really came from
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
To my understanding, what Nginx do is when I visit http://example.com it passes my request to http://example.com:8000.
I'm not quite sure that I should use proxy_pass http://127.0.0.1:8000 here because I don't know whether should Nginx pass the request to localhost But I 've tried to change it to 0.0.0.0:8000 but it still doesn't work.
Can anyone help?
0.0.0.0 means the server will accept connections from all IP address. See https://en.wikipedia.org/wiki/0.0.0.0 for more detail.
If the gunicorn server listens on 127.0.0.1, only you (or someone else on the same machine with gunicorn server) can access it through local loop https://en.wikipedia.org/wiki/Local_loop.
But since you use Nginx to accept connection from the internet, you can just proxy_pass http://127.0.0.1:8000; and change the command to command=/home/fh/test/venv/bin/gunicorn -w4 -b 127.0.0.1:8000 myapp:app. In this scenario, gunicorn itself only need to accept connections from Nginx which runs on the same machine with gunicorn.
The whole process looks like this
Connections from the Internet -> Nginx (reverse proxy, listen on 0.0.0.0:80) -> Gunicorn (which runs your Python code, listen on 127.0.0.1:8000)

Categories