如何在Debian9上使用Postgres、Nginx和Gunicorn设置Django

来自菜鸟教程
跳转至:导航、​搜索

介绍

Django 是一个强大的 Web 框架,可以帮助您启动 Python 应用程序或网站。 Django 包含一个简化的开发服务器,用于在本地测试您的代码,但对于任何与生产相关的事情,都需要一个更安全、更强大的 Web 服务器。

在本指南中,我们将演示如何在 Debian 9 上安装和配置一些组件以支持和服务 Django 应用程序。 我们将设置一个 PostgreSQL 数据库,而不是使用默认的 SQLite 数据库。 我们将配置 Gunicorn 应用程序服务器以与我们的应用程序交互。 然后,我们将设置 Nginx 以反向代理 Gunicorn,使我们能够访问其安全性和性能特性来为我们的应用程序提供服务。

先决条件

为了完成本指南,您应该拥有一个带有基本防火墙的全新 Debian 9 服务器实例和一个配置了 sudo 权限的非 root 用户。 您可以通过我们的 初始服务器设置指南 来了解如何设置它。

我们将在虚拟环境中安装 Django。 将 Django 安装到特定于您的项目的环境中将允许单独处理您的项目及其要求。

一旦我们的数据库和应用程序启动并运行,我们将安装和配置 Gunicorn 应用程序服务器。 这将作为我们应用程序的接口,将客户端请求从 HTTP 转换为我们的应用程序可以处理的 Python 调用。 然后我们将在 Gunicorn 前面设置 Nginx,以利用其高性能的连接处理机制和易于实现的安全特性。

让我们开始吧。

第 1 步 — 从 Debian 存储库安装软件包

要开始这个过程,我们将从 Debian 存储库下载并安装我们需要的所有项目。 稍后我们将使用 Python 包管理器 pip 安装其他组件。

我们需要更新本地的 apt 包索引,然后下载并安装包。 我们安装的包取决于您的项目将使用哪个版本的 Python。

如果您将 Django 与 Python 3 一起使用,请键入:

sudo apt update
sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl

Django 1.11 是支持 Python 2 的最后一个 Django 版本。 如果您正在开始新项目,强烈建议您选择 Python 3。 如果您仍需要使用 Python 2,请键入:

sudo apt update
sudo apt install python-pip python-dev libpq-dev postgresql postgresql-contrib nginx curl

这将安装 pip、稍后构建 Gunicorn 所需的 Python 开发文件、Postgres 数据库系统和与之交互所需的库,以及 Nginx Web 服务器。

第 2 步 — 创建 PostgreSQL 数据库和用户

我们将直接进入并为我们的 Django 应用程序创建一个数据库和数据库用户。

默认情况下,Postgres 使用称为“对等身份验证”的身份验证方案进行本地连接。 基本上,这意味着如果用户的操作系统用户名与有效的 Postgres 用户名匹配,则该用户无需进一步身份验证即可登录。

在 Postgres 安装期间,创建了一个名为 postgres 的操作系统用户,以对应于 postgres PostgreSQL 管理用户。 我们需要使用这个用户来执行管理任务。 我们可以使用 sudo 并通过 -u 选项传入用户名。

通过键入以下内容登录到交互式 Postgres 会话:

sudo -u postgres psql

您将收到一个 PostgreSQL 提示,我们可以在其中设置我们的要求。

首先,为您的项目创建一个数据库:

CREATE DATABASE myproject;

注意: 每个 Postgres 语句都必须以分号结尾,因此如果遇到问题,请确保命令以分号结尾。


接下来,为我们的项目创建一个数据库用户。 确保选择安全密码:

CREATE USER myprojectuser WITH PASSWORD 'password';

之后,我们将为刚刚创建的用户修改一些连接参数。 这将加快数据库操作,从而不必在每次建立连接时都查询和设置正确的值。

我们将默认编码设置为 UTF-8,这是 Django 所期望的。 我们还将默认事务隔离方案设置为“已提交读”,这会阻止来自未提交事务的读取。 最后,我们正在设置时区。 默认情况下,我们的 Django 项目将设置为使用 UTC。 这些都是来自 Django 项目本身 的所有建议:

ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myprojectuser SET timezone TO 'UTC';

现在,我们可以让我们的新用户访问管理我们的新数据库:

GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;

完成后,键入以下命令退出 PostgreSQL 提示:

\q

Postgres 现在已设置好,以便 Django 可以连接并管理其数据库信息。

第 3 步 — 为您的项目创建 Python 虚拟环境

现在我们有了数据库,我们可以开始准备其余的项目需求。 我们将在虚拟环境中安装我们的 Python 需求,以便于管理。

为此,我们首先需要访问 virtualenv 命令。 我们可以用 pip 安装它。

如果您使用的是 Python 3,请升级 pip 并通过键入以下命令安装包:

sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv

如果您使用的是 Python 2,请升级 pip 并通过键入以下命令安装包:

sudo -H pip install --upgrade pip
sudo -H pip install virtualenv

安装 virtualenv 后,我们就可以开始构建我们的项目了。 创建并移动到一个目录,我们可以在其中保存我们的项目文件:

mkdir ~/myprojectdir
cd ~/myprojectdir

在项目目录中,通过键入以下命令创建 Python 虚拟环境:

virtualenv myprojectenv

这将在您的 myprojectdir 目录中创建一个名为 myprojectenv 的目录。 在内部,它将安装 Python 的本地版本和 pip 的本地版本。 我们可以使用它来为我们的项目安装和配置一个隔离的 Python 环境。

在我们安装项目的 Python 需求之前,我们需要激活虚拟环境。 您可以通过键入:

source myprojectenv/bin/activate

您的提示应更改以指示您现在正在 Python 虚拟环境中操作。 它看起来像这样:(myprojectenv)user@host:~/myprojectdir$

在您的虚拟环境处于活动状态时,安装 Django、Gunicorn 和带有 pip 本地实例的 psycopg2 PostgreSQL 适配器:

注意:当虚拟环境被激活时(当你的提示前面有(myprojectenv)时),使用pip而不是pip3,即使你正在使用蟒蛇 3。 无论 Python 版本如何,该工具的虚拟环境副本始终命名为 pip


pip install django gunicorn psycopg2-binary

您现在应该拥有启动 Django 项目所需的所有软件。

第 4 步——创建和配置一个新的 Django 项目

安装 Python 组件后,我们可以创建实际的 Django 项目文件。

创建 Django 项目

由于我们已经有一个项目目录,我们将告诉 Django 在此处安装文件。 它将使用实际代码创建一个二级目录,这是正常的,并在此目录中放置一个管理脚本。 关键是我们明确定义了目录,而不是让 Django 做出与当前目录相关的决定:

django-admin.py startproject myproject ~/myprojectdir

此时,您的项目目录(本例中为 ~/myprojectdir)应具有以下内容:

  • ~/myprojectdir/manage.py:一个 Django 项目管理脚本。
  • ~/myprojectdir/myproject/:Django 项目包。 这应该包含 __init__.pysettings.pyurls.pywsgi.py 文件。
  • ~/myprojectdir/myprojectenv/:我们之前创建的虚拟环境目录。

调整项目设置

我们应该对新创建的项目文件做的第一件事是调整设置。 在文本编辑器中打开设置文件:

nano ~/myprojectdir/myproject/settings.py

首先找到 ALLOWED_HOSTS 指令。 这定义了服务器地址或域名的列表,可用于连接到 Django 实例。 任何带有不在此列表中的 Host 标头的传入请求都将引发异常。 Django 要求您设置它以防止某一类安全漏洞。

在方括号中,列出与您的 Django 服务器关联的 IP 地址或域名。 每个项目都应列在引号中,条目之间用逗号分隔。 如果您希望请求整个域和任何子域,请在条目的开头添加句点。 在下面的代码片段中,有一些注释掉的示例用于演示:

注意: 请务必将 localhost 作为选项之一,因为我们将通过本地 Nginx 实例代理连接。


~/myprojectdir/myproject/settings.py

. . .
# The simplest case: just add the domain name(s) and IP addresses of your Django server
# ALLOWED_HOSTS = [ 'example.com', '203.0.113.5']
# To respond to 'example.com' and any subdomains, start the domain with a dot
# ALLOWED_HOSTS = ['.example.com', '203.0.113.5']
ALLOWED_HOSTS = ['your_server_domain_or_IP', 'second_domain_or_IP', . . ., 'localhost']

接下来,找到配置数据库访问的部分。 它将以 DATABASES 开头。 文件中的配置用于 SQLite 数据库。 我们已经为我们的项目创建了一个 PostgreSQL 数据库,所以我们需要调整设置。

使用您的 PostgreSQL 数据库信息更改设置。 我们告诉 Django 使用与 pip 一起安装的 psycopg2 适配器。 我们需要给出数据库名称、数据库用户名、数据库用户的密码,然后指定数据库位于本地计算机上。 您可以将 PORT 设置保留为空字符串:

~/myprojectdir/myproject/settings.py

. . .

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}

. . .

接下来,向下移动到文件的底部并添加一个设置,指示静态文件的放置位置。 这是必要的,以便 Nginx 可以处理对这些项目的请求。 以下行告诉 Django 将它们放在基础项目目录中名为 static 的目录中:

~/myprojectdir/myproject/settings.py

. . .

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

完成后保存并关闭文件。

完成初始项目设置

现在,我们可以使用管理脚本将初始数据库模式迁移到我们的 PostgreSQL 数据库:

~/myprojectdir/manage.py makemigrations
~/myprojectdir/manage.py migrate

通过键入以下内容为项目创建管理用户:

~/myprojectdir/manage.py createsuperuser

您必须选择用户名,提供电子邮件地址,然后选择并确认密码。

我们可以通过键入以下命令将所有静态内容收集到我们配置的目录位置:

~/myprojectdir/manage.py collectstatic

您必须确认操作。 然后,静态文件将放置在项目目录中名为 static 的目录中。

如果您遵循初始服务器设置指南,您应该有一个 UFW 防火墙来保护您的服务器。 为了测试开发服务器,我们必须允许访问我们将使用的端口。

通过键入以下内容为端口 8000 创建一个例外:

sudo ufw allow 8000

最后,您可以使用以下命令启动 Django 开发服务器来测试我们的项目:

~/myprojectdir/manage.py runserver 0.0.0.0:8000

在您的网络浏览器中,访问您的服务器的域名或 IP 地址,后跟 :8000

http://server_domain_or_IP:8000

您应该会看到默认的 Django 索引页面:

如果将 /admin 附加到地址栏中 URL 的末尾,系统将提示您输入使用 createsuperuser 命令创建的管理用户名和密码:

通过身份验证后,您可以访问默认的 Django 管理界面:

完成探索后,在终端窗口中点击 CTRL-C 以关闭开发服务器。

测试 Gunicorn 为项目服务的能力

在离开我们的虚拟环境之前,我们要做的最后一件事是测试 Gunicorn 以确保它可以为应用程序服务。 我们可以通过进入我们的项目目录并使用 gunicorn 来加载项目的 WSGI 模块来做到这一点:

cd ~/myprojectdir
gunicorn --bind 0.0.0.0:8000 myproject.wsgi

这将在运行 Django 开发服务器的同一界面上启动 Gunicorn。 您可以返回并再次测试该应用程序。

注意: 管理界面不会应用任何样式,因为 Gunicorn 不知道如何找到负责此的静态 CSS 内容。


我们通过指定 Django 的 wsgi.py 文件的相对目录路径向 Gunicorn 传递了一个模块,该文件是我们应用程序的入口点,使用 Python 的模块语法。 在该文件中,定义了一个名为 application 的函数,用于与应用程序通信。 要了解有关 WSGI 规范的更多信息,请单击 这里

完成测试后,在终端窗口中点击 CTRL-C 以停止 Gunicorn。

我们现在已经完成了 Django 应用程序的配置。 我们可以通过键入以下内容退出我们的虚拟环境:

deactivate

提示中的虚拟环境指示器将被删除。

第 5 步 — 为 Gunicorn 创建 systemd 套接字和服务文件

我们已经测试过 Gunicorn 可以与我们的 Django 应用程序交互,但是我们应该实现一种更健壮的方式来启动和停止应用程序服务器。 为此,我们将制作 systemd 服务和套接字文件。

Gunicorn 套接字将在启动时创建并监听连接。 当连接发生时,systemd 会自动启动 Gunicorn 进程来处理连接。

首先使用 sudo 权限为 Gunicorn 创建和打开一个 systemd 套接字文件:

sudo nano /etc/systemd/system/gunicorn.socket

在内部,我们将创建一个 [Unit] 部分来描述套接字,一个 [Socket] 部分来定义套接字位置,以及一个 [Install] 部分来确保在合适的时间:

/etc/systemd/system/gunicorn.socket

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

完成后保存并关闭文件。

接下来,在您的文本编辑器中使用 sudo 权限为 Gunicorn 创建并打开一个 systemd 服务文件。 服务文件名应与套接字文件名匹配,扩展名除外:

sudo nano /etc/systemd/system/gunicorn.service

[Unit] 部分开始,用于指定元数据和依赖项。 我们将在此处描述我们的服务,并告诉 init 系统仅在达到网络目标后才启动它。 因为我们的服务依赖于套接字文件中的套接字,所以我们需要包含一个 Requires 指令来指示这种关系:

/etc/systemd/system/gunicorn.service

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

接下来,我们将打开 [Service] 部分。 我们将指定要在其下运行的用户和组。 由于它拥有所有相关文件,因此我们将授予我们常规用户帐户对该过程的所有权。 我们将组所有权授予 www-data 组,以便 Nginx 可以轻松地与 Gunicorn 通信。

然后,我们将绘制工作目录并指定用于启动服务的命令。 在这种情况下,我们必须指定 Gunicorn 可执行文件的完整路径,该可执行文件安装在我们的虚拟环境中。 我们将进程绑定到我们在 /run 目录中创建的 Unix 套接字,以便进程可以与 Nginx 通信。 我们将所有数据记录到标准输出,以便 journald 进程可以收集 Gunicorn 日志。 我们还可以在此处指定任何可选的 Gunicorn 调整。 例如,我们在这种情况下指定了 3 个工作进程:

/etc/systemd/system/gunicorn.service

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myprojectdir
ExecStart=/home/sammy/myprojectdir/myprojectenv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myproject.wsgi:application

最后,我们将添加一个 [Install] 部分。 如果我们允许它在启动时启动,这将告诉 systemd 将该服务链接到什么。 我们希望在常规多用户系统启动并运行时启动此服务:

/etc/systemd/system/gunicorn.service

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myprojectdir
ExecStart=/home/sammy/myprojectdir/myprojectenv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myproject.wsgi:application

[Install]
WantedBy=multi-user.target

至此,我们的 systemd 服务文件就完成了。 现在保存并关闭它。

我们现在可以启动并启用 Gunicorn 套接字。 这将在 /run/gunicorn.sock 现在和启动时创建套接字文件。 当与该套接字建立连接时,systemd 将自动启动 gunicorn.service 来处理它:

sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket

我们可以通过检查套接字文件来确认操作是否成功。

第 6 步 — 检查 Gunicorn 套接字文件

检查进程的状态以了解它是否能够启动:

sudo systemctl status gunicorn.socket

接下来,检查 /run 目录中是否存在 gunicorn.sock 文件:

file /run/gunicorn.sock
Output/run/gunicorn.sock: socket

如果 systemctl status 命令指示发生错误或在目录中没有找到 gunicorn.sock 文件,则表明 Gunicorn 套接字无法正确创建。 通过键入以下内容检查 Gunicorn 套接字的日志:

sudo journalctl -u gunicorn.socket

在继续之前,再看看您的 /etc/systemd/system/gunicorn.socket 文件以解决任何问题。

第 7 步 — 测试套接字激活

目前,如果您只启动了 gunicorn.socket 单元,则 gunicorn.service 将不会处于活动状态,因为套接字尚未收到任何连接。 您可以通过键入以下内容进行检查:

sudo systemctl status gunicorn
Output● gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   Active: inactive (dead)

要测试套接字激活机制,我们可以通过 curl 向套接字发送一个连接,输入:

curl --unix-socket /run/gunicorn.sock localhost

您应该在终端中看到应用程序的 HTML 输出。 这表明 Gunicorn 已启动并且能够为您的 Django 应用程序提供服务。 您可以通过键入以下内容来验证 Gunicorn 服务是否正在运行:

sudo systemctl status gunicorn
Output● gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   Active: active (running) since Mon 2018-07-09 20:00:40 UTC; 4s ago
 Main PID: 1157 (gunicorn)
    Tasks: 4 (limit: 1153)
   CGroup: /system.slice/gunicorn.service
           ├─1157 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application
           ├─1178 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application
           ├─1180 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application
           └─1181 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application

Jul 09 20:00:40 django1 systemd[1]: Started gunicorn daemon.
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1157] [INFO] Starting gunicorn 19.9.0
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1157] [INFO] Listening at: unix:/run/gunicorn.sock (1157)
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1157] [INFO] Using worker: sync
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1178] [INFO] Booting worker with pid: 1178
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1180] [INFO] Booting worker with pid: 1180
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1181] [INFO] Booting worker with pid: 1181
Jul 09 20:00:41 django1 gunicorn[1157]:  - - [09/Jul/2018:20:00:41 +0000] "GET / HTTP/1.1" 200 16348 "-" "curl/7.58.0"

如果 curlsystemctl status 的输出表明发生了问题,请查看日志以获取更多详细信息:

sudo journalctl -u gunicorn

检查您的 /etc/systemd/system/gunicorn.service 文件是否有问题。 如果您对 /etc/systemd/system/gunicorn.service 文件进行更改,请重新加载守护程序以重新读取服务定义并通过键入以下内容重新启动 Gunicorn 进程:

sudo systemctl daemon-reload
sudo systemctl restart gunicorn

确保在继续之前解决上述问题。

第 8 步 — 将 Nginx 配置为代理传递到 Gunicorn

现在 Gunicorn 已经设置好了,我们需要配置 Nginx 以将流量传递给进程。

首先在 Nginx 的 sites-available 目录中创建并打开一个新的服务器块:

sudo nano /etc/nginx/sites-available/myproject

在里面,打开一个新的服务器块。 我们将首先指定此块应侦听普通端口 80,并应响应我们服务器的域名或 IP 地址:

/etc/nginx/sites-available/myproject

server {
    listen 80;
    server_name server_domain_or_IP;
}

接下来,我们将告诉 Nginx 忽略查找 favicon 的任何问题。 我们还将告诉它在哪里可以找到我们在 ~/myprojectdir/static 目录中收集的静态资产。 所有这些文件都有一个标准的 URI 前缀“/static”,所以我们可以创建一个位置块来匹配这些请求:

/etc/nginx/sites-available/myproject

server {
    listen 80;
    server_name server_domain_or_IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/sammy/myprojectdir;
    }
}

最后,我们将创建一个 location / {} 块来匹配所有其他请求。 在这个位置中,我们将包含 Nginx 安装中包含的标准 proxy_params 文件,然后我们将流量直接传递到 Gunicorn 套接字:

/etc/nginx/sites-available/myproject

server {
    listen 80;
    server_name server_domain_or_IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/sammy/myprojectdir;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

完成后保存并关闭文件。 现在,我们可以通过将文件链接到 sites-enabled 目录来启用该文件:

sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

通过键入以下内容测试您的 Nginx 配置是否存在语法错误:

sudo nginx -t

如果没有报告错误,请继续并通过键入以下内容重新启动 Nginx:

sudo systemctl restart nginx

最后,我们需要对端口 80 上的正常流量开放防火墙。 由于我们不再需要访问开发服务器,我们也可以删除打开端口 8000 的规则:

sudo ufw delete allow 8000
sudo ufw allow 'Nginx Full'

您现在应该能够转到您的服务器的域或 IP 地址来查看您的应用程序。

注意: 配置 Nginx 后,下一步应该是使用 SSL/TLS 保护到服务器的流量。 这很重要,因为没有它,包括密码在内的所有信息都会以纯文本形式通过网络发送。

如果您有域名,获得 SSL 证书以保护您的流量的最简单方法是使用 Let's Encrypt。 按照 本指南 在 Debian 9 上设置 Let's Encrypt with Nginx。 使用我们在本指南中创建的 Nginx 服务器块执行该过程。

如果您没有域名,您仍然可以使用 自签名 SSL 证书 保护您的站点以进行测试和学习。 再次,使用我们在本教程中创建的 Nginx 服务器块执行该过程。


Nginx 和 Gunicorn 故障排除

如果这最后一步没有显示您的应用程序,您将需要对安装进行故障排除。

Nginx 显示默认页面而不是 Django 应用程序

如果 Nginx 显示默认页面而不是代理到您的应用程序,通常意味着您需要调整 /etc/nginx/sites-available/myproject 文件中的 server_name 以指向您的服务器的 IP 地址或域名。

Nginx 使用 server_name 来确定使用哪个服务器块来响应请求。 如果您看到默认的 Nginx 页面,则表明 Nginx 无法将请求明确匹配到服务器块,因此它回退到 /etc/nginx/sites-available/default 中定义的默认块。

项目服务器块中的 server_name 必须比要选择的默认服务器块中的更具体。

Nginx 显示 502 Bad Gateway 错误而不是 Django 应用程序

502 错误表示 Nginx 无法成功代理请求。 各种配置问题都以 502 错误表示,因此需要更多信息才能正确排除故障。

查找更多信息的主要地方是 Nginx 的错误日志。 通常,这将告诉您在代理事件期间哪些条件导致了问题。 通过键入以下内容遵循 Nginx 错误日志:

sudo tail -F /var/log/nginx/error.log

现在,在浏览器中发出另一个请求以生成新的错误(尝试刷新页面)。 您应该会看到写入日志的新错误消息。 如果您查看该消息,它应该可以帮助您缩小问题范围。

您可能会看到以下一些消息:

connect() 到 unix:/run/gunicorn.sock 失败(2:没有这样的文件或目录)

这表明 Nginx 无法在给定位置找到 gunicorn.sock 文件。 您应该将 /etc/nginx/sites-available/myproject 文件中定义的 proxy_pass 位置与 gunicorn.socket systemd 单元生成的 gunicorn.sock 文件的实际位置进行比较。

如果在 /run 目录下找不到 gunicorn.sock 文件,一般表示 systemd 套接字文件无法创建。 返回到检查 Gunicorn 套接字文件 部分,逐步完成 Gunicorn 的故障排除步骤。

connect() 到 unix:/run/gunicorn.sock 失败(13:权限被拒绝)

这表明由于权限问题,Nginx 无法连接到 Gunicorn 套接字。 当使用 root 用户而不是 sudo 用户执行该过程时,可能会发生这种情况。 虽然 systemd 能够创建 Gunicorn 套接字文件,但 Nginx 无法访问它。

如果在根目录 (/) 和 gunicorn.sock 文件之间的任何点存在有限的权限,就会发生这种情况。 通过将套接字文件的绝对路径传递给 namei 命令,我们可以查看套接字文件及其每个父目录的权限和所有权值:

namei -l /run/gunicorn.sock
Outputf: /run/gunicorn.sock
drwxr-xr-x root root /
drwxr-xr-x root root run
srw-rw-rw- root root gunicorn.sock

输出显示每个目录组件的权限。 通过查看权限(第一列)、所有者(第二列)和组所有者(第三列),我们可以确定允许对套接字文件进行何种类型的访问。

在上面的示例中,套接字文件和指向套接字文件的每个目录都具有全局读取和执行权限(目录的权限列以 r-x 而不是 --- 结尾)。 Nginx 进程应该能够成功访问套接字。

如果通向套接字的任何目录没有全局读取和执行权限,则 Nginx 将无法访问套接字,除非允许全局读取和执行权限或确保将组所有权授予 Nginx 所属的组的。

Django 正在显示:“无法连接到服务器:连接被拒绝”

尝试在 Web 浏览器中访问应用程序的某些部分时,您可能会从 Django 看到一条消息:

OperationalError at /admin/login/
could not connect to server: Connection refused
    Is the server running on host "localhost" (127.0.0.1) and accepting
    TCP/IP connections on port 5432?

这表明 Django 无法连接到 Postgres 数据库。 通过键入以下内容确保 Postgres 实例正在运行:

sudo systemctl status postgresql

如果不是,您可以通过键入以下命令启动它并使其在启动时自动启动(如果尚未配置为这样做):

sudo systemctl start postgresql
sudo systemctl enable postgresql

如果仍有问题,请确保 ~/myprojectdir/myproject/settings.py 文件中定义的数据库设置正确。

进一步的故障排除

对于其他故障排除,日志可以帮助缩小根本原因。 依次检查它们中的每一个,并查找指示问题区域的消息。

以下日志可能会有所帮助:

  • 检查 Nginx 进程日志,输入:sudo journalctl -u nginx
  • 通过键入以下内容检查 Nginx 访问日志:sudo less /var/log/nginx/access.log
  • 通过键入以下内容检查 Nginx 错误日志:sudo less /var/log/nginx/error.log
  • 通过键入以下内容检查 Gunicorn 应用程序日志:sudo journalctl -u gunicorn
  • 通过键入以下内容检查 Gunicorn 套接字日志:sudo journalctl -u gunicorn.socket

当您更新配置或应用程序时,您可能需要重新启动流程以适应您的更改。

如果您更新 Django 应用程序,您可以通过键入以下内容重新启动 Gunicorn 进程以获取更改:

sudo systemctl restart gunicorn

如果您更改 Gunicorn 套接字或服务文件,请重新加载守护程序并通过键入以下内容重新启动该进程:

sudo systemctl daemon-reload
sudo systemctl restart gunicorn.socket gunicorn.service

如果您更改 Nginx 服务器块配置,请测试配置,然后输入以下命令来测试 Nginx:

sudo nginx -t && sudo systemctl restart nginx

这些命令有助于在您调整配置时获取更改。

结论

在本指南中,我们在自己的虚拟环境中设置了一个 Django 项目。 我们已经配置 Gunicorn 来翻译客户端请求,以便 Django 可以处理它们。 之后,我们设置 Nginx 作为反向代理来处理客户端连接并根据客户端请求为正确的项目提供服务。

Django 通过提供许多通用部分使创建项目和应用程序变得简单,让您可以专注于独特的元素。 通过利用本文中描述的通用工具链,您可以轻松地为您从单个服务器创建的应用程序提供服务。