简体   繁体   English

使用 Nginx、Gunicorn 和 Supervisor 部署 Django

[英]Deploying Django with Nginx, Gunicorn and Supervisor

I'm trying to deploy my Django app with Nginx and Gunicorn by following this tutorial , but I modified some steps so I can use Conda instead of ViritualEnv.我正在尝试按照本教程使用 Nginx 和 Gunicorn 部署我的 Django 应用程序,但我修改了一些步骤,以便可以使用 Conda 而不是 ViritualEnv。

The setup looks like this:设置如下所示:

  1. Nginx replies with my Vue app Nginx 回复我的 Vue 应用程序
  2. Requests from Vue are made to api.example.com来自 Vue 的请求被发送到 api.example.com
  3. Nginx listens to api.example.com and directs requests to Gunicorn's unix socket Nginx 监听 api.example.com 并将请求定向到 Gunicorn 的 unix socket

Things I've checked:我检查过的东西:

  1. I can see the Vue requests in Nginx's access.log .我可以在 Nginx 的access.log看到 Vue 请求。
  2. I can also see those requests with journalctl -f -u gunicorn , in the supervisor.log , and gunicorn's access.log我还可以通过journalctl -f -u gunicornsupervisor.log和 gunicorn 的 access.log 中看到这些请求
  3. When my Django app starts, it's creates a log file, so I can see that Gunicorn starts it.当我的 Django 应用程序启动时,它会创建一个日志文件,所以我可以看到 Gunicorn 启动了它。 But Django is not responding to requests from the unix socket.但是 Django 没有响应来自 unix 套接字的请求。
  4. I can see a response from Django when I ssh in and run the following command: curl --no-buffer -XGET --unix-socket /var/www/example/run/gunicorn.sock http://localhost/about .当我 ssh 进入并运行以下命令时,我可以看到来自 Django 的响应: curl --no-buffer -XGET --unix-socket /var/www/example/run/gunicorn.sock http://localhost/about This command only gives a response when any of my ALLOWED_HOSTS are used in place of localhost .此命令仅在使用我的任何ALLOWED_HOSTS代替localhost时给出响应。
  5. My Nginx, Supervisor and Gunicorn configurations all use the full path to gunicorn.sock .我的Nginx,监事,Gunicorn配置都使用的完整路径gunicorn.sock

Should I see Django running on port 8000 or anything if I do something like nmap localhost ?如果我执行类似nmap localhost我应该看到 Django 在端口 8000 上运行吗? I saw another post mention that Nginx should point to port 8000 and that gunicorn should be run with either:我看到另一篇文章提到 Nginx 应该指向端口 8000 并且 gunicorn 应该使用以下任一方式运行:

  1. gunicorn --bind 0.0.0.0:8000 <djangoapp>.wsgi --daemon
  2. gunicorn <djangoapp>.wsgi:application --bind <IP>:8000 --daemon
  3. gunicorn <djangoapp>.wsgi:application --bind=unix:/var/www/example/run/gunicorn.sock

But doesn't exposing port 8000 defeat the purpose of using Nginx as a reverse proxy and Gunicorn's unix socket?但是暴露 8000 端口不是违背了使用 Nginx 作为反向代理和 Gunicorn 的 unix socket 的目的吗? Doesn't exposing 8000 also increase the surface area for attack vectors?暴露 8000 不会增加攻击媒介的表面积吗? Or is it best practice to expose port 8000?还是公开端口 8000 是最佳实践? I'm a bit confused why I would use both expose that port and use both Nginx and Gunicorn.我有点困惑为什么我会同时使用公开该端口并同时使用 Nginx 和 Gunicorn。

My main problem : Why can I get responses from Django via the unix socket with curl, but not via requests from Vue?我的主要问题:为什么我可以通过带有 curl 的 unix 套接字从 Django 获得响应,而不是通过来自 Vue 的请求? Why aren't Vue's requests making it from Gunicorn to Django via the unix socket?为什么 Vue 的请求没有通过 unix 套接字从 Gunicorn 发送到 Django?

I'm really stuck.我真的被困住了。 Any suggestions?有什么建议?

Frontend Nginx config前端 Nginx 配置

server {
        listen 80 default_server;
        listen [::]:80 default_server;
        # server_name example.com;
        # server_name myIP;
        root /var/www/example/frontend/dist;
        server_name example.com www.example.com;

        location =/robots.txt {
                root /opt/example;
        }

        location /thumbnail/ {
                alias /opt/example/static/img/thumbnail/;
        }

        location /bg/ {
                alias /opt/example/static/img/bg/;
        }

        location / {
                try_files $uri $uri/ /index.html;
        }
}

API Nginx config API Nginx 配置

upstream backend_server {
        server unix:/var/www/example/run/gunicorn.sock fail_timeout=0;
}

server {
        listen 80;
        server_name api.example.com
        client_max_body_size 4G;

        access_log /var/log/nginx/api-access.log;
        error_log /var/log/nginx/api-error.log;

        location / {
                include proxy_params;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $host;
                proxy_headers_hash_max_size 512;
                proxy_headers_hash_bucket_size 128; 
                proxy_redirect off;
                if (!-f $request_filename) {
                        proxy_pass http://backend_server;
                }
        }
}

Gunicorn config Gunicorn 配置

#!/bin/bash

NAME=”backend”
DJANGODIR=/var/www/example/backend
SOCKFILE=/var/www/example/run/gunicorn.sock
USER=django
GROUP=example
NUM_WORKERS=3
DJANGO_SETTINGS_MODULE=backend.settings
DJANGO_WSGI_MODULE=backend.wsgi
CONDA_SRC=/home/justin/anaconda3/etc/profile.d/conda.sh
GUNICORN=/home/justin/anaconda3/envs/production/bin/gunicorn

echo “starting backend”

cd $DJANGODIR
    source $CONDA_SRC
conda activate production
    export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
    export PYTHONPATH=$DJANGODIR:$PYTHONPATH
    
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR

exec $GUNICORN
 ${DJANGO_WSGI_MODULE}:application \
  --name $NAME \
  --workers $NUM_WORKERS \
  --user=$USER --group=$GROUP \
  --bind=unix:$SOCKFILE \
  --log-level=debug \
  --log-file=- \
  --error-logfile=/var/www/example/backend/logs/gunicorn-error.log \
  --access-logfile=/var/www/example/backend/logs/gunicorn-access.log

Gunicorn access.log Gunicorn 访问.log

- - [08/Sep/2020:01:51:24 -0400] "OPTIONS /c/about/ HTTP/1.0" 200 0 "http://example.com/c/about" "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Mobile Safari/537.36"
- - [08/Sep/2020:01:51:24 -0400] "POST /c/about/ HTTP/1.0" 400 143 "http://example.com/c/about" "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Mobile Safari/537.36"

But doesn't exposing port 8000 defeat the purpose of using Nginx as a reverse proxy and Gunicorn's unix socket?但是暴露 8000 端口不是违背了使用 Nginx 作为反向代理和 Gunicorn 的 unix socket 的目的吗?

In gunicorn, you have to expose 8000 port on localhost like this gunicorn --bind 127.0.0.1:8000 <djangoapp>.wsgi --daemon .在 gunicorn 中,您必须像这样gunicorn --bind 127.0.0.1:8000 <djangoapp>.wsgi --daemon一样在本地主机上公开 8000 端口。 Exposing it on 0.0.0.0 will obviously be a security vulnerability considering your nginx in on the same server.考虑到您的 nginx 在同一台服务器上,将它暴露在0.0.0.0显然是一个安全漏洞。

Doesn't exposing 8000 also increase the surface area for attack vectors?暴露 8000 不会增加攻击媒介的表面积吗? Or is it best practice to expose port 8000?还是公开端口 8000 是最佳实践? I'm a bit confused why I would use both expose that port and use both Nginx and Gunicorn.我有点困惑为什么我会同时使用公开该端口并同时使用 Nginx 和 Gunicorn。

You don't need to expose port 8000 you can expose any port but you need to tell gunicon to listen on at least a single port so that nginx can pass requests to it.您不需要公开端口 8000 您可以公开任何端口,但您需要告诉 gunicon 至少侦听单个端口,以便 nginx 可以将请求传递给它。

And regarding using both nginx and gunicorn, they both are really different and handle very different use case/functions of an application.关于同时使用 nginx 和 gunicorn,它们确实不同并且处理应用程序的非常不同的用例/功能。

Nginx uses "event‑driven" approach to handle requests so a single worker of nginx can handle 1000s of req simultaneously. Nginx 使用“事件驱动”方法来处理请求,因此 nginx 的单个 worker 可以同时处理 1000 个请求。 But Gunicorn on the other hand mostly(by default) uses sync worker which means a request will remain with a worker till it is processed.但另一方面,Gunicorn 主要(默认情况下)使用同步工作线程,这意味着请求将保留在工作线程中,直到它被处理。 (posted this twice today :p) (今天发布了两次:p)

So you need both if you remove nginx all your requests will return 50X except which are currently handled by gunicorn until the worker is free.因此,如果您删除 nginx,则您需要两者,您的所有请求都将返回 50X,除非当前由 gunicorn 处理,直到工作人员空闲为止。 And also gunicorn is not made to handle user traffic or in bigger application things like load balancing can only be done by nginx.而且 gunicorn 不是用来处理用户流量的,或者在更大的应用程序中,负载平衡之类的事情只能由 nginx 完成。 So nginx has it's own purpose in an application.所以 nginx 在应用程序中有它自己的用途。

After neeraj9194 pointed out the 400 , I did more searching for issues relating to Nginx, Gunicorn 400 and Django and I came across a ton of similar issues.neeraj9194指出400 ,我对与 Nginx、Gunicorn 400 和 Django 相关的问题进行了更多搜索,我遇到了大量类似的问题。 Looks like it's mainly an Nginx issue.看起来这主要是一个 Nginx 问题。 The answer in this blog fixed my issue.博客中的答案解决了我的问题。

I replaced the location block in my API Nginx config with:我将 API Nginx 配置中的location块替换为:

location / {
      proxy_set_header Host $host;
      proxy_pass http://backend_server;
      proxy_set_header X-Forwarded-Host $server_name;
      proxy_set_header X-Real-IP $remote_addr;
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM