簡體   English   中英

使用 Nginx、Gunicorn 和 Supervisor 部署 Django

[英]Deploying Django with Nginx, Gunicorn and Supervisor

我正在嘗試按照本教程使用 Nginx 和 Gunicorn 部署我的 Django 應用程序,但我修改了一些步驟,以便可以使用 Conda 而不是 ViritualEnv。

設置如下所示:

  1. Nginx 回復我的 Vue 應用程序
  2. 來自 Vue 的請求被發送到 api.example.com
  3. Nginx 監聽 api.example.com 並將請求定向到 Gunicorn 的 unix socket

我檢查過的東西:

  1. 我可以在 Nginx 的access.log看到 Vue 請求。
  2. 我還可以通過journalctl -f -u gunicornsupervisor.log和 gunicorn 的 access.log 中看到這些請求
  3. 當我的 Django 應用程序啟動時,它會創建一個日志文件,所以我可以看到 Gunicorn 啟動了它。 但是 Django 沒有響應來自 unix 套接字的請求。
  4. 當我 ssh 進入並運行以下命令時,我可以看到來自 Django 的響應: curl --no-buffer -XGET --unix-socket /var/www/example/run/gunicorn.sock http://localhost/about 此命令僅在使用我的任何ALLOWED_HOSTS代替localhost時給出響應。
  5. 我的Nginx,監事,Gunicorn配置都使用的完整路徑gunicorn.sock

如果我執行類似nmap localhost我應該看到 Django 在端口 8000 上運行嗎? 我看到另一篇文章提到 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

但是暴露 8000 端口不是違背了使用 Nginx 作為反向代理和 Gunicorn 的 unix socket 的目的嗎? 暴露 8000 不會增加攻擊媒介的表面積嗎? 還是公開端口 8000 是最佳實踐? 我有點困惑為什么我會同時使用公開該端口並同時使用 Nginx 和 Gunicorn。

我的主要問題:為什么我可以通過帶有 curl 的 unix 套接字從 Django 獲得響應,而不是通過來自 Vue 的請求? 為什么 Vue 的請求沒有通過 unix 套接字從 Gunicorn 發送到 Django?

我真的被困住了。 有什么建議?

前端 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 配置

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 配置

#!/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 訪問.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"

但是暴露 8000 端口不是違背了使用 Nginx 作為反向代理和 Gunicorn 的 unix socket 的目的嗎?

在 gunicorn 中,您必須像這樣gunicorn --bind 127.0.0.1:8000 <djangoapp>.wsgi --daemon一樣在本地主機上公開 8000 端口。 考慮到您的 nginx 在同一台服務器上,將它暴露在0.0.0.0顯然是一個安全漏洞。

暴露 8000 不會增加攻擊媒介的表面積嗎? 還是公開端口 8000 是最佳實踐? 我有點困惑為什么我會同時使用公開該端口並同時使用 Nginx 和 Gunicorn。

您不需要公開端口 8000 您可以公開任何端口,但您需要告訴 gunicon 至少偵聽單個端口,以便 nginx 可以將請求傳遞給它。

關於同時使用 nginx 和 gunicorn,它們確實不同並且處理應用程序的非常不同的用例/功能。

Nginx 使用“事件驅動”方法來處理請求,因此 nginx 的單個 worker 可以同時處理 1000 個請求。 但另一方面,Gunicorn 主要(默認情況下)使用同步工作線程,這意味着請求將保留在工作線程中,直到它被處理。 (今天發布了兩次:p)

因此,如果您刪除 nginx,則您需要兩者,您的所有請求都將返回 50X,除非當前由 gunicorn 處理,直到工作人員空閑為止。 而且 gunicorn 不是用來處理用戶流量的,或者在更大的應用程序中,負載平衡之類的事情只能由 nginx 完成。 所以 nginx 在應用程序中有它自己的用途。

neeraj9194指出400 ,我對與 Nginx、Gunicorn 400 和 Django 相關的問題進行了更多搜索,我遇到了大量類似的問題。 看起來這主要是一個 Nginx 問題。 博客中的答案解決了我的問題。

我將 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