如何在Ubuntu20.04上使用Postgres、Nginx和Uvicorn设置ASGIDjango应用程序

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

介绍

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

部署 Django 应用程序的传统方法是使用 Web 服务器网关接口 (WSGI)。 但是,随着 Python 3 的出现和对异步执行的支持,您现在可以使用 异步服务器网关接口 (ASGI) 通过异步调用来执行 Python 应用程序。 作为 WSGI 的继承者,Python 中的 ASGI 规范是 WSGI 的超集,可以直接替代 WSGI。

Django 允许“外部异步,内部同步”模式,允许您的代码在内部同步,而 ASGI 服务器异步处理请求。 通过允许网络服务器具有异步调用,它可以为每个应用程序处理多个传入和传出事件。 Django 应用程序在内部仍然是同步的,以允许向后兼容并避免并行计算的复杂性。 这也意味着您的 Django 应用程序无需更改即可从 WSGI 切换到 ASGI。

在本指南中,您将在 Ubuntu 20.04 上安装和配置一些组件以支持和服务 Django 应用程序。 您将 设置 PostgreSQL 数据库 而不是使用默认的 SQLite 数据库。 您将配置 Gunicorn 应用程序服务器与 Uvicorn(一种 ASGI 实现)配对,以与您的应用程序异步交互。 然后,您将设置 Nginx 以反向代理 Gunicorn,让您可以访问其安全性和性能功能来为您的应用程序提供服务。

先决条件

要完成本教程,您需要:

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

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

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

使用以下命令安装必要的系统包:

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

此命令将安装用于设置虚拟环境的 Python 库、Postgres 数据库系统和与之交互所需的库,以及 Nginx Web 服务器。 接下来,您将为 Django 应用程序创建 PostgreSQL 数据库和用户。

第 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 需求,以便于管理。

首先,创建并移动到可以保存项目文件的目录:

mkdir ~/myprojectdir
cd ~/myprojectdir

然后使用 Python 内置的虚拟环境工具来创建一个新的虚拟环境。

python3 -m venv myprojectenv

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

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

source myprojectenv/bin/activate

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

您将在虚拟环境中安装 Django。 将 Django 安装到特定于您的项目的环境中将允许单独处理您的项目及其要求。 在您的虚拟环境处于活动状态时,安装 Django、Gunicorn、Uvicorn 和带有 pip 本地实例的 psycopg2 PostgreSQL 适配器:

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


pip install django gunicorn uvicorn psycopg2-binary

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

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

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

创建 Django 项目

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

django-admin startproject myproject ~/myprojectdir

此时,您的项目目录(本教程中的~/myprojectdir)应该有以下内容:

  • ~/myprojectdir/manage.py:一个 Django 项目管理脚本。
  • ~/myprojectdir/myproject/:Django 项目包。 这应该包含 __init__.pyasgi.py, settings.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/'
import os
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 与 Uvicorn 配对来部署应用程序。 虽然 Gunicorn 传统上用于部署 WSGI 应用程序,但它也提供了一个可插拔的接口来提供 ASGI 部署。 它通过允许您使用由 ASGI 服务器 (uvicorn) 公开的工作类来实现这一点。 因为 Gunicorn 是一个比 Uvicorn 更成熟的产品并且提供了更多的配置,所以 Uvicorn 的维护者推荐使用 gunicornuvicorn worker 类作为一个全功能的服务器和进程管理器。

在离开虚拟环境之前,您将测试 Gunicorn 以确保它可以为应用程序服务。

要将 uvicorn 工作者与 gunicorn 服务器一起使用,请进入您的项目目录并使用以下 gunicorn 命令加载项目的 ASGI 模块:

cd ~/myprojectdir
gunicorn --bind 0.0.0.0:8000 myproject.asgi -w 4 -k uvicorn.workers.UvicornWorker

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

注意: 不需要使用 Gunicorn 来运行您的 ASGI 应用程序。 要仅使用 uvicorn,您可以使用以下命令:

uvicorn myproject.asgi:application --host 0.0.0.0 --port 8080

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


如果在启动这些命令后仍然出现 Django 欢迎页面,则确认 gunicorn 为该页面提供服务并按预期工作。 通过使用 gunicorn 命令,您通过使用 Python 的模块语法指定 Django 的 asgi.py 文件的相对目录路径(这是应用程序的入口点)向 Gunicorn 传递了一个模块。 在该文件中,定义了一个名为 application 的函数,用于与应用程序通信。 要了解更多有关 ASGI 规范的信息,请访问 官方 ASGI 网站

完成测试后,在终端窗口中点击 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 调整。 这是指定三个工作进程的示例:

/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 - \
          -k uvicorn.workers.UvicornWorker \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myproject.asgi: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 - \
          -k uvicorn.workers.UvicornWorker \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myproject.asgi:application

[Install]
WantedBy=multi-user.target

这样,systemd 服务文件就完成了。 现在保存并关闭它。

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

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

现在您已经制作了 systemd 服务和套接字文件,您将通过检查套接字文件来确认操作成功。

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

在此步骤中,您将检查 Gunicorn 套接字文件。 首先,检查进程的状态,看看它是否能够启动:

sudo systemctl status gunicorn.socket

输出将类似于以下内容:

Output● gunicorn.socket - gunicorn socket
     Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor prese>
     Active: active (listening) since Fri 2020-06-26 17:53:10 UTC; 14s ago
   Triggers: ● gunicorn.service
     Listen: /run/gunicorn.sock (Stream)
      Tasks: 0 (limit: 1137)
     Memory: 0B
     CGroup: /system.slice/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 Thu 2021-06-10 21:03:29 UTC; 13s ago
TriggeredBy: ● gunicorn.socket
   Main PID: 11682 (gunicorn)
      Tasks: 4 (limit: 4682)
     Memory: 98.5M
     CGroup: /system.slice/gunicorn.service
             ├─11682 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 -k uvicorn.workers.UvicornWorker --bind unix:/run/gunicorn.sock myproject.asgi:application
             ├─11705 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 -k uvicorn.workers.UvicornWorker --bind unix:/run/gunicorn.sock myproject.asgi:application
             ├─11707 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 -k uvicorn.workers.UvicornWorker --bind unix:/run/gunicorn.sock myproject.asgi:application
             └─11708 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 -k uvicorn.workers.UvicornWorker --bind unix:/run/gunicorn.sock myproject.asgi:application

Jun 10 21:03:29 django gunicorn[11705]: [2021-06-10 21:03:29 +0000] [11705] [INFO] ASGI 'lifespan' protocol appears unsupported.
Jun 10 21:03:29 django gunicorn[11705]: [2021-06-10 21:03:29 +0000] [11705] [INFO] Application startup complete.
Jun 10 21:03:30 django gunicorn[11707]: [2021-06-10 21:03:30 +0000] [11707] [INFO] Started server process [11707]
Jun 10 21:03:30 django gunicorn[11707]: [2021-06-10 21:03:30 +0000] [11707] [INFO] Waiting for application startup.
Jun 10 21:03:30 django gunicorn[11707]: [2021-06-10 21:03:30 +0000] [11707] [INFO] ASGI 'lifespan' protocol appears unsupported.
Jun 10 21:03:30 django gunicorn[11707]: [2021-06-10 21:03:30 +0000] [11707] [INFO] Application startup complete.
Jun 10 21:03:30 django gunicorn[11708]: [2021-06-10 21:03:30 +0000] [11708] [INFO] Started server process [11708]
Jun 10 21:03:30 django gunicorn[11708]: [2021-06-10 21:03:30 +0000] [11708] [INFO] Waiting for application startup.
Jun 10 21:03:30 django gunicorn[11708]: [2021-06-10 21:03:30 +0000] [11708] [INFO] ASGI 'lifespan' protocol appears unsupported.
Jun 10 21:03:30 django gunicorn[11708]: [2021-06-10 21:03:30 +0000] [11708] [INFO] Application startup complete.

如果 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 以将流量传递给进程。 在这一步中,您将在 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 忽略查找图标的任何问题。 您还将告诉它在哪里可以找到您在 ~/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 地址来查看显示火箭图像的 Django 欢迎页面。

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

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


第 9 步 — 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() to unix:/run/gunicorn.sock failed (2: No such file or directory)

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

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

connect() to unix:/run/gunicorn.sock failed (13: Permission denied)

这表明由于权限问题,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

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

结论

在本指南中,您已经在其自己的虚拟环境中设置了一个 ASGI Django 项目。 您已将 Gunicorn 和 Uvicorn 配置为异步转换客户端请求,以便 Django 可以处理它们。 之后,您将 Nginx 设置为充当反向代理来处理客户端连接并根据客户端请求为正确的项目提供服务。

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