如何在Ubuntu 20.04上用Postgres、Nginx和Uvicorn设置一个ASGI Django应用程序

2,469 阅读25分钟

简介

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

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

Django允许 "外部异步,内部同步 "模式,允许你的代码在内部同步,而ASGI服务器异步处理请求。通过允许webserver有一个异步的可调用性,它可以为每个应用程序处理多个传入和传出的事件。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网络服务器。接下来,你将为你的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所期望的。你还会将默认的事务隔离方案设置为 "read committed",这将阻止从未提交的事务中读取数据。最后,你要设置时区。默认情况下,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

这将创建一个名为 myprojectenv在你的 myprojectdir目录中。 在里面,它将安装一个本地版本的 Python 和一个本地版本的pip 。 你可以用它来为你的项目安装和配置一个孤立的 Python 环境。

在安装项目的 Python 需求之前,你需要激活这个虚拟环境。你可以通过输入以下内容来完成。

source myprojectenv/bin/activate

你的提示应该改变,以表明你现在是在一个 Python 虚拟环境中操作。 它看起来会像这样。 (myprojectenv)user@host:~/myprojectdir$.

你将在虚拟环境中安装Django。将Django安装到一个特定于你的项目的环境中,将允许你的项目和它们的需求被分开处理。在你的虚拟环境激活后,用pip 的本地实例安装Django、Gunicorn、Uvicorn和psycopg2 PostgreSQL适配器。

**注意:**当虚拟环境被激活时 (当你的提示符前面有(myprojectenv) ) ,使用pip 而不是pip3 ,即使你使用的是 Python 3。虚拟环境中的工具副本总是被命名为pip ,不管 Python 的版本如何。

pip install django gunicorn uvicorn psycopg2-binary

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

第四步 - 创建和配置一个新的 Django 项目

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

创建Django项目

由于你已经有了一个项目目录,你可以告诉Django将文件安装在这里。它将创建一个包含实际代码的二级目录,这很正常,并在这个目录中放置一个管理脚本。这其中的关键是你明确定义了这个目录,而不是让Django相对于你的当前目录做出决定。

django-admin startproject myproject ~/myprojectdir

在这一点上,你的项目目录 (~/myprojectdir在本教程中)应该有以下内容。

  • ~/myprojectdir/manage.py: 一个Django项目管理脚本。
  • ~/myprojectdir/myproject/: Django项目包。 这应该包含__init__.py,asgi.py, settings.py ,urls.py, 和wsgi.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索引页。

Django index page

如果你在地址栏中的URL末尾加上/admin ,你会被提示输入你用createsuperuser 命令创建的管理用户名和密码。

Django admin login

在认证之后,你可以访问默认的Django管理界面。

Django admin interface

当你完成探索后,在终端窗口点击CTRL+C ,关闭开发服务器。

测试Gunicorn对项目的服务能力

在本教程中,你将使用Gunicorn与Uvicorn搭配来部署应用程序。虽然Gunicorn传统上是用来部署WSGI应用程序的,但它也提供了一个可插拔的接口来提供ASGI部署。它通过允许你消费由ASGI服务器(uvicorn )暴露的工作者类来做到这一点。因为Gunicorn是一个更成熟的产品,比Uvicorn提供更多的配置,Uvicorn的维护者建议使用gunicorn ,并使用uvicorn 工人类作为全功能的服务器和进程管理器。

在离开虚拟环境之前,你要测试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 命令,你通过指定 Django 的asgi.py 文件的相对目录路径传递给 Gunicorn 一个模块,该文件是你的应用程序的入口,使用 Python 的模块语法。在这个文件里面,定义了一个叫做application 的函数,它被用来与应用程序进行通信。要了解更多关于ASGI规范的信息,请访问ASGI官方网站

当你完成测试后,在终端窗口点击CTRL+C ,停止Gunicorn。

现在你已经完成了对Django应用程序的配置。你可以通过键入来退出虚拟环境。

deactivate

你的提示符中的虚拟环境指示灯将被移除。

第五步 - 为 Gunicorn 创建 systemd Socket 和服务文件

在上一节中,你测试了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
Outputgunicorn.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.


如果来自curl 的输出或systemctl 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忽略任何寻找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地址,查看显示有火箭图片的Django欢迎页面。

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

如果你有一个域名,最简单的方法是使用Let's Encrypt来获得SSL证书,以保证你的流量。按照本指南,在Ubuntu 20.04上用Nginx设置Let's Encrypt。使用本教程中创建的Nginx服务器块,按照该步骤操作。

第9步 - 排除Nginx和Gunicorn的故障

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

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

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

Nginx使用server_name 来决定使用哪个服务器块来响应请求。如果你收到的是Nginx的默认页面,这表明Nginx无法将请求明确匹配到一个服务器块,所以它回到了/etc/nginx/sites-available/default 中定义的默认块。

你项目中的服务器块server_name ,必须比默认服务器块中的更具体,才能被选中。

Nginx显示502坏网关错误,而不是Django应用程序

502错误表明Nginx无法成功代理请求。很多配置问题都会出现502错误,因此需要更多的信息来正确排除故障。

寻找更多信息的主要地方是Nginx的错误日志。一般来说,这将告诉你在代理事件中哪些条件导致了问题。按照Nginx的错误日志,输入。

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

现在,在浏览器中做另一个请求,产生一个新的错误(尝试刷新页面)。你应该收到一条新的错误信息写到日志中。如果你看一下该信息,应该可以帮助你缩小问题的范围。

你可能会收到以下信息。

"`[secondary_label Output]
connect() to unix:/run/gunicorn.sock failed (2: No such file or directory)

OutputThis indicates that Nginx could not find the `gunicorn.sock` file at the given location. You should compare the `proxy_pass` location defined within `/etc/nginx/sites-available/myproject` file to the actual location of the `gunicorn.sock` file generated by the `gunicorn.socket` systemd unit.

If you cannot find a `gunicorn.sock` file within the `/run` directory, it generally means that the systemd socket file was unable to create it. Go back to the [section on checking for the Gunicorn socket file](#checking-for-the-gunicorn-socket-file) to step through the troubleshooting steps for Gunicorn.

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

这表明Nginx因为权限问题而无法连接到Gunicorn套接字。当程序使用根用户而不是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

输出显示每个目录组件的权限。通过查看权限(第一列)、所有者(第二列)和组所有者(第三列),你可以弄清楚允许对socket文件的什么类型的访问。

在上面的例子中,套接字文件和通往套接字文件的每个目录都有世界读和执行的权限(目录的权限栏以r-x 结尾,而不是--- )。 Nginx进程应该能够成功访问该套接字。

如果通往套接字的任何目录没有世界读取和执行的权限,Nginx将无法访问套接字,除非允许世界读取和执行的权限,或者确保组的所有权被赋予Nginx所属的组。

Django显示:"could not connect to server:连接被拒绝"

当你试图在网络浏览器中访问应用程序的某些部分时,你可能会从Django收到一条信息。

"`[secondary_label Output]
OperationalError at /admin/login/
could not connect to server:连接被拒绝
服务器是否在主机 "localhost"(127.0.0.1)上运行,并
在5432端口接受TCP/IP连接?


This indicates that Django is unable to connect to the Postgres database.  Make sure that the Postgres instance is running by typing:

```command
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简化了创建项目和应用程序的过程,提供了许多常见的部分,使你能够专注于独特的元素。通过利用本文描述的一般工具链,你可以从一个服务器上为你创建的应用程序提供服务。