如何使用DigitalOcean托管数据库和空间设置可扩展的Django应用程序

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

介绍

Django 是一个强大的 Web 框架,可以帮助您快速启动 Python 应用程序或网站。 它包括几个方便的功能,例如 对象关系映射器 、Python API 和可自定义的应用程序管理界面。 它还包括一个 缓存框架 并通过其 URL Dispatcher模板系统 鼓励干净的应用程序设计。

开箱即用,Django 包含一个用于测试和本地开发的最小 Web 服务器,但它应该与一个更强大的服务基础架构配对,用于生产用例。 Django 通常使用 Nginx Web 服务器来处理静态文件请求和 HTTPS 重定向,以及 Gunicorn WSGI 服务器来为应用程序提供服务。

在本指南中,我们将通过将 Javascript 和 CSS 样式表等静态文件卸载到 DigitalOcean Spaces 来增强此设置,并可选择使用 C 内容 Delivery N[X208X ] 网络或 CDN,它将这些文件存储在更靠近最终用户的位置,以减少传输时间。 我们还将使用 DigitalOcean Managed PostgreSQL 数据库 作为我们的数据存储,以简化数据层并避免手动配置可扩展的 PostgreSQL 数据库。

先决条件

在开始阅读本指南之前,您应该准备好以下内容:

  • 一个全新的 Ubuntu 18.04 服务器实例,带有基本防火墙和配置了 sudo 权限的非 root 用户。 您可以通过使用 Ubuntu 18.04 运行 初始服务器设置来了解如何设置它。
  • DigitalOcean 管理的 PostgreSQL 集群。 要了解如何创建集群,请参阅 DigitalOcean Managed Databases 产品文档
  • 一个 DigitalOcean 空间,用于存储您的 Django 项目的静态文件和该空间的一组访问密钥。 要了解如何创建空间,请参阅 如何创建空间 产品文档,要了解如何为空间创建访问密钥,请参阅 使用访问密钥共享对空间的访问
  • 在您的服务器上安装、保护和配置 Nginx 以使用您选择的域名。 有关使用 Let's Encrypt 设置 A 记录和保护 Nginx 安装的更多信息,请参阅 如何在 Ubuntu 18.04 上使用 Let's Encrypt 保护 Nginx。

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

首先,我们将从 Ubuntu 存储库下载并安装我们需要的所有项目。 稍后我们将使用 Python 包管理器 pip 安装其他组件。

我们需要先更新本地的apt包索引,然后下载安装包。

在本指南中,我们将使用 Django 和 Python 3。 要安装必要的库,请登录到您的服务器并键入:

sudo apt update
sudo apt install python3-pip python3-dev libpq-dev curl postgresql-client

这将安装 pip、构建 Gunicorn 所需的 Python 开发文件、构建 Pyscopg PostgreSQL Python 适配器所需的 libpq 头文件和 PostgreSQL 命令行客户端。

当提示开始下载和安装软件包时,点击 Y,然后点击 ENTER

接下来,我们将配置数据库以使用我们的 Django 应用程序。

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

我们现在将为我们的 Django 应用程序创建一个数据库和数据库用户。

首先,通过从 Cloud 控制面板 导航到 Databases,然后单击进入您的数据库,为您的集群获取 Connection Parameters。 您应该会看到一个 Connection Details 框,其中包含您的集群的一些参数。 记下这些。

返回命令行,使用这些凭据和我们刚刚安装的 psql PostgreSQL 客户端登录到您的集群:

psql -U username -h host -p port -d database -set=sslmode=require

出现提示时,输入 Postgres 用户名旁边显示的密码,然后点击 ENTER

您将收到一个 PostgreSQL 提示,您可以从中管理数据库。

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

CREATE DATABASE polls;

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


我们现在可以切换到 polls 数据库:

\c polls;

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

CREATE USER myprojectuser WITH PASSWORD 'password';

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

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

在 PostgreSQL 提示符下输入以下命令:

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 polls TO myprojectuser;

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

\q

您的 Django 应用程序现在已准备好连接和管理此数据库。

在下一步中,我们将安装 virtualenv 并为我们的 Django 项目创建一个 Python 虚拟环境。

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

现在我们已经设置了数据库以与我们的应用程序一起工作,我们将创建一个 Python 虚拟环境,该环境将这个项目的依赖项与系统的全局 Python 安装隔离开来。

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

升级 pip 并输入以下命令安装软件包:

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

安装 virtualenv 后,我们可以创建一个目录来存储我们的 Python 虚拟环境,并使其与 Django polls 应用程序一起使用。

创建一个名为 envs 的目录并导航到它:

mkdir envs
cd envs

在此目录中,通过键入以下命令创建一个名为 polls 的 Python 虚拟环境:

virtualenv polls

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

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

source polls/bin/activate

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

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

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


pip install django gunicorn psycopg2-binary

您现在应该拥有运行 Django polls 应用程序所需的所有软件。 在下一步中,我们将创建一个 Django 项目并安装这个应用程序。

第 4 步 - 创建投票 Django 应用程序

我们现在可以设置我们的示例应用程序。 在本教程中,我们将使用 Django 文档 中的 Polls 演示应用程序。 它包括一个允许用户查看投票并在其中投票的公共站点,以及一个允许管理员修改、创建和删除投票的管理控制面板。

在本指南中,我们将跳过教程步骤,并简单地从 DigitalOcean 社区 django-polls repo 克隆最终应用程序。

如果您想手动完成这些步骤,请在您的主目录中创建一个名为 django-polls 的目录并导航到该目录:

cd
mkdir django-polls
cd django-polls

从那里,您可以按照官方 Django 文档中的 Writing your first Django app 教程进行操作。 完成后,跳至 步骤 5

如果您只想克隆完成的应用程序,请导航到您的主目录并使用 git 克隆 django-polls repo

cd
git clone https://github.com/do-community/django-polls.git

cd 进去,列出目录内容:

cd django-polls
ls

您应该看到以下对象:

OutputLICENSE  README.md  manage.py  mysite  polls  templates

manage.py 是用于操作应用程序的主要命令行实用程序。 polls 包含 polls 应用程序代码,mysite 包含项目范围代码和设置。 templates 包含管理界面的自定义模板文件。 要了解有关项目结构和文件的更多信息,请参阅 Django 官方文档中的创建项目

在运行应用程序之前,我们需要调整其默认设置并将其连接到我们的数据库。

第 5 步 — 调整应用程序设置

在这一步中,我们将修改 Django 项目的默认配置以提高安全性,将 Django 连接到我们的数据库,并将静态文件收集到本地目录中。

首先在文本编辑器中打开设置文件:

nano ~/django-polls/mysite/settings.py

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

在方括号中,列出与您的 Django 服务器关联的 IP 地址或域名。 每个项目都应列在引号中,条目之间用逗号分隔。 您的列表还将包括 localhost,因为您将通过本地 Nginx 实例代理连接。 如果您希望包含对整个域和任何子域的请求,请在条目的开头添加句点。

在下面的代码片段中,有一些注释掉的示例演示了这些条目的外观:

~/django-polls/mysite/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 数据库,所以我们需要调整这些设置。

我们将告诉 Django 使用我们随 pip 安装的 psycopg2 数据库适配器,而不是默认的 SQLite 引擎。 我们还将重用 Step 2 中引用的 Connection Parameters。 您始终可以从 DigitalOcean Cloud Control Panel 的托管数据库部分找到此信息。

使用您的数据库设置更新文件:数据库名称 (polls)、数据库用户名、数据库用户的密码以及数据库 hostport。 请务必用您自己的信息替换特定于数据库的值:

~/django-polls/mysite/settings.py

. . .

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

. . .

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

~/django-polls/mysite/settings.py

. . .

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

完成后保存并关闭文件。

至此,您已经配置了 Django 项目的数据库、安全性和静态文件设置。 如果您从一开始就遵循 polls 教程并且没有克隆 GitHub 存储库,您可以继续进行 步骤 6。 如果您克隆了 GitHub 存储库,则还有一个额外的步骤。

Django 设置文件包含一个 SECRET_KEY 变量,用于为各种 Django 对象创建散列。 将其设置为唯一的、不可预测的值很重要。 SECRET_KEY 变量已从 GitHub 存储库中清除,因此我们将使用 django Python 包中内置的一个名为 get_random_secret_key() 的函数创建一个新变量。 从命令行,打开 Python 解释器:

python

您应该看到以下输出和提示:

OutputPython 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

从 Django 包中导入 get_random_secret_key 函数,然后调用该函数:

from django.core.management.utils import get_random_secret_key
get_random_secret_key()

将生成的密钥复制到剪贴板。

CTRL+D 退出 Python 解释器。

接下来,再次在文本编辑器中打开设置文件:

nano ~/django-polls/mysite/settings.py

找到 SECRET_KEY 变量并粘贴您刚刚生成的密钥:

~/django-polls/mysite/settings.py

. . .

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'your_secret_key_here'

. . .

保存并关闭文件。

现在,我们将使用 Django 开发服务器在本地测试应用程序,以确保一切都已正确配置。

第 6 步 — 测试应用程序

在我们运行 Django 开发服务器之前,我们需要使用 manage.py 实用程序来创建数据库模式并将静态文件收集到 STATIC_ROOT 目录中。

导航到项目的基本目录,并使用 makemigrationsmigrate 命令在我们的 PostgreSQL 数据库中创建初始数据库模式:

cd django-polls
./manage.py makemigrations
./manage.py migrate

makemigrations 将根据对 Django 模型所做的更改创建迁移或数据库架构更改。 migrate 会将这些迁移应用到数据库模式。 要了解有关 Django 中迁移的更多信息,请参阅 Django 官方文档中的 Migrations

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

./manage.py createsuperuser

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

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

./manage.py collectstatic

然后,静态文件将放置在项目目录中名为 static 的目录中。

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

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

sudo ufw allow 8000

使用 Django 开发服务器测试应用程序

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

./manage.py runserver 0.0.0.0:8000

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

http://server_domain_or_IP:8000/polls

您应该会看到 Polls 应用程序界面:

要查看管理界面,请访问您的服务器的域名或 IP 地址,后跟 :8000 和管理界面的路径:

http://server_domain_or_IP:8000/admin

您应该会看到 Polls 应用管理员身份验证窗口:

输入您使用 createsuperuser 命令创建的管理用户名和密码。

验证后,您可以访问投票应用的管理界面:

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

使用 Gunicorn 测试应用程序

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

gunicorn --bind 0.0.0.0:8000 mysite.wsgi

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

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


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

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

我们现在将应用程序的静态文件卸载到 DigitalOcean Spaces。

第 7 步 — 将静态文件卸载到 DigitalOcean Spaces

此时,Gunicorn 可以为我们的 Django 应用程序提供服务,但不能为它的静态文件提供服务。 通常我们会配置 Nginx 来提供这些文件,但在本教程中,我们将使用 django-storages 插件将它们卸载到 DigitalOcean Spaces。 这使您可以通过集中其静态内容并释放服务器资源来轻松扩展 Django。 此外,您可以使用 DigitalOcean Spaces CDN 交付此静态内容。

有关将 Django 静态文件卸载到对象存储的完整指南,请参阅 如何使用 Django 设置对象存储。

安装和配置 django-storages

我们将从安装 django-storages Python 包开始。 django-storages 包为 Django 提供了 S3Boto3Storage 存储后端,它使用 boto3 库将文件上传到任何与 S3 兼容的对象存储服务。

首先,使用 pip 安装 django-storagesboto3 Python 包:

pip install django-storages boto3

接下来,再次打开应用程序的 Django 设置文件:

nano ~/django-polls/mysite/settings.py

向下导航到文件的 INSTALLED_APPS 部分,并将 storages 附加到已安装应用程序列表中:

~/django-polls/mysite/settings.py

. . .

INSTALLED_APPS = [
    . . .
    'django.contrib.staticfiles',
    'storages',
]

. . .

将文件向下滚动到我们之前修改的 STATIC_URL。 我们现在将覆盖这些值并附加新的 S3Boto3Storage 后端参数。 删除您之前输入的代码,并添加以下块,其中包括您的空间的访问和位置信息。 请记住用您自己的信息替换此处突出显示的值::

~/django-polls/mysite/settings.py

. . .

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

AWS_ACCESS_KEY_ID = 'your_spaces_access_key'
AWS_SECRET_ACCESS_KEY = 'your_spaces_secret_key'

AWS_STORAGE_BUCKET_NAME = 'your_space_name'
AWS_S3_ENDPOINT_URL = 'spaces_endpoint_URL'
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
AWS_DEFAULT_ACL = 'public-read'

STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

STATIC_URL = '{}/{}/'.format(AWS_S3_ENDPOINT_URL, AWS_LOCATION)
STATIC_ROOT = 'static/'

我们定义如下配置项:

  • AWS_ACCESS_KEY_ID:您在教程先决条件中创建的空间的访问密钥 ID。 如果您没有创建一组访问密钥,请参阅 使用访问密钥共享对空间的访问
  • AWS_SECRET_ACCESS_KEY:DigitalOcean Space 的密钥。
  • AWS_STORAGE_BUCKET_NAME:您的 DigitalOcean Space 名称。
  • AWS_S3_ENDPOINT_URL :用于访问对象存储服务的端点 URL。 对于 DigitalOcean,这将类似于 https://nyc3.digitaloceanspaces.com,具体取决于空间区域。
  • AWS_S3_OBJECT_PARAMETERS 设置静态文件的缓存控制头。
  • AWS_LOCATION:在对象存储桶中定义一个目录,将放置所有静态文件。
  • AWS_DEFAULT_ACL:定义静态文件的访问控制列表 (ACL)。 将其设置为 public-read 可确保最终用户可以公开访问文件。
  • STATICFILES_STORAGE:设置 Django 将用于卸载静态文件的存储后端。 此后端应与任何 S3 兼容的后端一起使用,包括 DigitalOcean Spaces。
  • STATIC_URL:指定 Django 在为静态文件生成 URL 时应该使用的基本 URL。 在这里,我们结合端点 URL 和静态文件子目录来构造静态文件的基本 URL。
  • STATIC_ROOT:指定在将静态文件复制到对象存储之前本地收集静态文件的位置。

完成编辑后保存并关闭文件。

从现在开始,当你运行 collectstatic 时,Django 会将你应用的静态文件上传到 Space。 当您启动 Django 时,它将开始从该空间提供静态资源,例如 CSS 和 Javascript。

在下一部分中,我们将为此空间启用 CDN,并可选择为空间 CDN 配置自定义子域。 这将通过在地理分布的边缘服务器网络中缓存它们来加速 Django 项目的静态文件的交付。 要了解有关 CDN 的更多信息,请参阅 使用 CDN 加速静态内容交付 。 如果您不想启用 Spaces CDN,请跳至 配置 CORS 标头

启用 CDN(可选)

要通过 DigitalOcean Spaces CDN 激活静态文件交付,首先为您的 DigitalOcean Space 启用 CDN。 要了解如何执行此操作,请参阅 DigitalOcean 产品文档中的 如何启用 Spaces CDN

如果您想将 自定义域 与 Spaces CDN 一起使用,请按照 如何使用子域 自定义 Spaces CDN 端点来创建子域 CNAME 记录和适当的 SSL 证书。

强烈建议使用带有 Spaces CDN 的自定义域。 通过使卸载的资产 URL 与 Django 站点的 URL 相似,这将极大地改善您站点的搜索引擎优化 (SEO)。 要将自定义域与 Spaces CDN 一起使用,您需要确保首先将您的域添加到您的 DigitalOcean 帐户。 要了解如何执行此操作,请参阅 如何添加域

为您的空间启用 CDN 并选择为其创建自定义子域后,使用 云控制面板 导航到您的空间。 您应该会在您的空间名称下看到一个新的 Endpoints 链接:

这些端点应包含您的空间名称。 如果您为 Spaces CDN 创建了自定义子域,此列表将包含一个名为 Subdomain 的附加端点。

Edge 端点通过 CDN 路由对 Spaces 对象的请求,尽可能从边缘缓存为它们提供服务。 记下这个 Edge 端点,因为我们将使用它来配置 django-storages 插件。 如果您为 Spaces CDN 创建了子域,则 Subdomain 端点是此 Edge 端点的别名。

接下来,再次编辑应用程序的 Django 设置文件:

nano ~/django-polls/mysite/settings.py

向下导航到我们最近修改的静态文件部分。 添加 AWS_S3_CUSTOM_DOMAIN 参数以配置 django-storages 插件 CDN 端点并更新 STATIC_URL 参数以使用这个新的 CDN 端点:

~/django-polls/mysite/settings.py

. . .

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

# Moving static assets to DigitalOcean Spaces as per:
# how-to-set-up-object-storage-with-django
AWS_ACCESS_KEY_ID = 'your_spaces_access_key'
AWS_SECRET_ACCESS_KEY = 'your_spaces_secret_key'

AWS_STORAGE_BUCKET_NAME = 'your_space_name'
AWS_S3_ENDPOINT_URL = 'spaces_endpoint_URL'
AWS_S3_CUSTOM_DOMAIN = 'spaces_edge_endpoint_URL'
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
AWS_DEFAULT_ACL = 'public-read'

STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

STATIC_URL = '{}/{}/'.format(AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
STATIC_ROOT = 'static/'

在这里,将 spaces_edge_endpoint_URL 替换为您刚刚记下的 Edge 端点,截断 https:// 前缀。 例如,如果 Edge 端点 URL 为 https://example.sfo2.cdn.digitaloceanspaces.com,则 AWS_S3_CUSTOM_DOMAIN 应设置为 example.sfo2.cdn.digitaloceanspaces.com

如果您创建了自定义子域,请将 spaces_edge_endpoint_URL 替换为自定义子域端点,截断 https:// 前缀。 例如,如果子域端点 URL 为 https://assets.example.com,则应将 AWS_S3_CUSTOM_DOMAIN 设置为 assets.example.com

完成后,保存并关闭文件。

当您启动 Django 时,它现在将使用 CDN 为您的 DigitalOcean Space 提供静态内容。

在我们测试这一切是否正常运行之前,我们需要为我们的 Spaces 文件配置 跨域资源共享 (CORS) 标头,否则您的 Web 浏览器可能会拒绝访问某些静态资产。 如果您为 Django 使用的同一个域使用带有 Spaces CDN 的自定义子域,您可以跳至 测试 Spaces 静态文件交付

配置 CORS 标头

CORS 标头告诉 Web 浏览器,在一个域中运行的应用程序可以访问位于另一个域的脚本或资源。 在这种情况下,我们需要允许 Django 服务器域的跨域资源共享,以便 Web 浏览器不会拒绝对 Space 中的静态文件的请求。

注意: 仅当您 不是 使用带有 Spaces CDN 的自定义子域时,才需要执行此步骤。


首先,使用云控制面板导航到您空间的 设置 页面:

CORS 配置 部分中,单击 Add

在这里,在 Origin 下,输入通配符原点 *

警告: 当您将应用部署到生产环境时,请务必将此值更改为您的确切源域(包括 http://https:// 协议)。 将此作为通配符来源是不安全的,我们在此仅出于测试目的这样做,因为当前不支持将来源设置为 http://example.com:8000(使用非标准端口)。


允许的方法 下,选择 GET

点击Add Header,在出现的文本框中输入Access-Control-Allow-Origin

Access Control Max Age 设置为 600 以便我们刚刚创建的标头每 10 分钟过期一次。

单击保存选项

从现在开始,您空间中的对象将包含适当的 Access-Control-Allow-Origin 响应标头,允许现代安全 Web 浏览器跨域获取这些文件。

测试空间静态文件交付

我们现在将测试 Django 是否正确地从我们的 DigitalOcean Space 提供静态文件。

导航到您的 Django 应用程序目录:

cd ~/django-polls

从这里,运行 collectstatic 收集静态文件并将其上传到您的 DigitalOcean Space:

python manage.py collectstatic

您应该看到以下输出:

OutputYou have requested to collect static files at the destination
location as specified in your settings.

This will overwrite existing files!
Are you sure you want to do this?

Type 'yes' to continue, or 'no' to cancel: 

输入 yes 并点击 ENTER 确认。

然后,您应该会看到如下输出

Output121 static files copied.

这确认 Django 已成功将 polls 应用程序静态文件上传到您的空间。 您可以使用云控制面板导航到您的空间,并检查static目录中的文件。

接下来,我们将验证 Django 是否正在重写适当的 URL。

启动 Gunicorn 服务器:

gunicorn --bind 0.0.0.0:8000 mysite.wsgi  

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

http://server_domain_or_IP:8000/admin

您应该再次看到 Polls 应用程序管理员身份验证窗口,这一次具有正确的样式。

现在,使用浏览器的开发人员工具检查页面内容并显示源文件存储位置。

要使用 Google Chrome 执行此操作,请右键单击该页面,然后选择 Inspect

您应该看到以下窗口:

在此处,单击工具栏中的 Sources。 在左侧窗格的源文件列表中,您应该在 Django 服务器的域下看到 /admin/login,在 Space 的 CDN 端点下看到 static/admin。 在 static/admin 中,您应该看到 cssfonts 目录。

这确认 CSS 样式表和字体从您的 Space 的 CDN 正确提供。

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

您可以通过输入 deactivate 来禁用活动的 Python 虚拟环境:

deactivate

您的提示应该恢复正常。

至此,您已经成功地从 Django 服务器卸载了静态文件,并从对象存储中为它们提供服务。 我们现在可以继续将 Gunicorn 配置为作为系统服务自动启动。

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

Step 6 中,我们测试了 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 个工作进程运行 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/django-polls
ExecStart=/home/sammy/envs/polls/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          mysite.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/django-polls
ExecStart=/home/sammy/envs/polls/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          mysite.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

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

检查 Gunicorn 套接字文件

查看进程状态,看是否启动成功:

sudo systemctl status gunicorn.socket

您应该看到以下输出:

OutputFailed to dump process list, ignoring: No such file or directory
● gunicorn.socket - gunicorn socket
   Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2019-03-05 19:19:16 UTC; 1h 22min ago
   Listen: /run/gunicorn.sock (Stream)
   CGroup: /system.slice/gunicorn.socket

Mar 05 19:19:16 django systemd[1]: Listening on 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 文件以解决任何问题。

测试套接字激活

目前,如果你只启动了 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 Tue 2019-03-05 20:43:56 UTC; 1s ago
 Main PID: 19074 (gunicorn)
    Tasks: 4 (limit: 4915)
   CGroup: /system.slice/gunicorn.service
           ├─19074 /home/sammy/envs/polls/bin/python3 /home/sammy/envs/polls/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock mysite.wsgi:application
           ├─19098 /home/sammy/envs/polls/bin/python3 /home/sammy/envs/polls/bin/gunicorn 
. . .

Mar 05 20:43:56 django systemd[1]: Started gunicorn daemon.
Mar 05 20:43:56 django gunicorn[19074]: [2019-03-05 20:43:56 +0000] [19074] [INFO] Starting gunicorn 19.9.0
. . .
Mar 05 20:44:15 django gunicorn[19074]:  - - [05/Mar/2019:20:44:15 +0000] "GET / HTTP/1.1" 301 0 "-" "curl/7.58.0"

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

sudo journalctl -u gunicorn

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

sudo systemctl daemon-reload
sudo systemctl restart gunicorn

确保在继续配置 Nginx 服务器之前解决任何问题。

第 8 步 — 配置 Nginx HTTPS 和 Gunicorn 代理传递

现在 Gunicorn 以更健壮的方式设置,我们需要配置 Nginx 来加密连接并将流量传递给 Gunicorn 进程。

如果您遵循先决条件并使用 Let's Encrypt 设置 Nginx,您应该已经在 Nginx 的 sites-available 目录中拥有与您的域对应的服务器块文件。 如果没有,请按照 How To Secure Nginx with Let's Encrypt on Ubuntu 18.04 并返回此步骤。

在我们编辑这个 example.com 服务器块文件之前,我们将首先删除安装 Nginx 后默认推出的 default 服务器块文件:

sudo rm /etc/nginx/sites-enabled/default

我们现在将修改 example.com 服务器块文件以将流量传递给 Gunicorn,而不是在先决条件步骤中配置的默认 index.html 页面。

在编辑器中打开与您的域对应的服务器块文件:

sudo nano /etc/nginx/sites-available/example.com

您应该会看到如下内容:

/etc/nginx/sites-available/example.com

server {

        root /var/www/example.com/html;
        index index.html index.htm index.nginx-debian.html;

        server_name example.com www.example.com;

        location / {
                try_files $uri $uri/ =404;
        }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;
        listen [::]:80;

        server_name example.com www.example.com;
    return 404; # managed by Certbot


}

这是在 How to Install Nginx on Ubuntu 18.04 中创建的默认服务器块文件以及 Let's Encrypt 自动附加的附加内容的组合。 我们将删除该文件的内容并编写一个新配置,将 HTTP 流量重定向到 HTTPS,并将传入请求转发到我们在上一步中创建的 Gunicorn 套接字。

如果您愿意,可以使用 cp 备份此文件。 退出文本编辑器并创建一个名为 example.com.old 的备份:

sudo cp /etc/nginx/sites-available/example.com /etc/nginx/sites-available/example.com.old

现在,重新打开文件并删除其内容。 我们将逐块构建新的配置。

首先粘贴以下块,它将端口 80 上的 HTTP 请求重定向到 HTTPS:

/etc/nginx/sites-available/example.com

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://example.com$request_uri;
}

在这里,我们在端口 80 上侦听 HTTP IPv4 和 IPv6 请求,并使用 example.com 域发送 301 响应头以将请求重定向到 HTTPS 端口 443。 这也会将直接 HTTP 请求重定向到服务器的 IP 地址。

在此块之后,附加以下配置代码块,用于处理 example.com 域的 HTTPS 请求:

/etc/nginx/sites-available/example.com

. . . 
server {
    listen [::]:443 ssl ipv6only=on;
    listen 443 ssl;
    server_name example.com www.example.com;
    
    # Let's Encrypt parameters
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    
    location = /favicon.ico { access_log off; log_not_found off; }

    location / {
        proxy_pass         http://unix:/run/gunicorn.sock;
        proxy_redirect     off;

        proxy_set_header   Host              $http_host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto https;
    }
}

在这里,我们首先在端口 443 上监听访问 example.comwww.example.com 域的请求。

接下来,我们提供默认服务器块文件中包含的相同 Let's Encrypt 配置,该配置指定 SSL 证书和私钥的位置,以及一些额外的安全参数。

location = /favicon.ico 行指示 Nginx 忽略查找图标的任何问题。

最后一个 location = / 块指示 Nginx 将请求传递给在 Step 8 中配置的 Gunicorn 套接字。 此外,它添加标头以通知上游 Django 服务器请求已被转发,并为其提供各种请求属性。

粘贴这两个配置块后,最终文件应如下所示:

/etc/nginx/sites-available/example.com

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://example.com$request_uri;
}
server {
        listen [::]:443 ssl ipv6only=on;
        listen 443 ssl;
        server_name example.com www.example.com;

        # Let's Encrypt parameters
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

        location = /favicon.ico { access_log off; log_not_found off; }

        location / {
          proxy_pass         http://unix:/run/gunicorn.sock;
          proxy_redirect     off;

          proxy_set_header   Host              $http_host;
          proxy_set_header   X-Real-IP         $remote_addr;
          proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
          proxy_set_header   X-Forwarded-Proto https;
        }
}

完成后保存并关闭文件。

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

sudo nginx -t

如果您的配置没有错误,请键入以下命令重新启动 Nginx:

sudo systemctl restart nginx

您现在应该能够访问您的服务器的域或 IP 地址来查看您的应用程序。 您的浏览器应该使用安全的 HTTPS 连接来连接到 Django 后端。

为了完全保护我们的 Django 项目,我们需要在其 settings.py 文件中添加几个安全参数。 在编辑器中重新打开此文件:

nano ~/django-polls/mysite/settings.py

滚动到文件底部,并添加以下参数:

~/django-polls/mysite/settings.py

. . .

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True

这些设置告诉 Django 您已在服务器上启用 HTTPS,并指示它使用“安全”cookie。 要了解有关这些设置的更多信息,请参阅 Security in DjangoSSL/HTTPS 部分

完成后,保存并关闭文件。

最后,重启 Gunicorn:

sudo systemctl restart gunicorn

至此,您已将 Nginx 配置为重定向 HTTP 请求并将这些请求交给 Gunicorn。 现在应该为您的 Django 项目和应用程序完全启用 HTTPS。 如果您遇到错误,关于 对 Nginx 和 Gunicorn 进行故障排除的讨论可能会有所帮助。

警告: 如果您没有 not 为 Spaces CDN 配置自定义子域,如 Configuring CORS Headers 中所述,请务必更改 Origin ] 从通配符 * 域到您的域名(本指南中的 https://example.com),然后让最终用户可以访问您的应用程序。


结论

在本指南中,您设置并配置了在 Ubuntu 18.04 服务器上运行的可扩展 Django 应用程序。 此设置可以跨多个服务器复制以创建高可用性架构。 此外,该应用程序及其配置可以使用 Docker 或其他容器运行时进行容器化,以简化部署和扩展。 然后可以将这些容器部署到像 Kubernetes 这样的容器集群中。 在即将发布的教程系列中,我们将探讨如何将这个 Django polls 应用程序容器化和现代化,以便它可以在 Kubernetes 集群中运行。

除了静态文件,您可能还希望将 Django 媒体文件卸载到对象存储中。 要了解如何执行此操作,请参阅 使用 Amazon S3 存储您的 Django 站点的静态和媒体文件。 您还可以考虑压缩静态文件以进一步优化它们向最终用户的交付。 为此,您可以使用 Django 插件,例如 Django 压缩器