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

Django是一个功能强大的Web框架,可以帮助您快速启动Python应用程序或网站。它包括几个方便的功能,如对象关系映射器,Python API和...

介绍

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

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

在本指南中,我们将通过将静态文件(如Javascript和CSS样式表)卸载到DigitalOcean Spaces来扩充此设置,并可选择使用C ontent D elivery N etwork或CDN来提供它们,这些文件将这些文件存储得更靠近最终用户以减少传输时间。 我们还将使用DigitalOcean Managed PostgreSQL数据库作为我们的数据存储来简化数据层,并避免手动配置可扩展的PostgreSQL数据库。

先决条件

在开始本指南之前,您应该可以使用以下内容:

第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 ,构建Pyscopg所需的Python开发文件,构建Pyscopg PostgreSQL Python适配器所需的libpq头文件以及PostgreSQL命令行客户端。

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

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

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

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

首先,通过从云控制面板导航到数据库 ,然后单击进入数据库,获取群集的连接参数 您应该看到包含集群的一些参数的“ 连接详细信息”框。 请注意这些。

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

psql -U do_admin -h host -p port -d database

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

您将获得一个PostgreSQL提示符,您可以从中提示您管理数据库。

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

CREATE DATABASE polls;

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

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

\c polls;

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

CREATE USER myprojectuser WITH PASSWORD 'password';

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

我们将默认编码设置为UTF-8 ,这是Django所期望的。 我们还将默认事务隔离方案设置为“read committed”,它阻止从未提交的事务中读取。 最后,我们正在设定时区。 默认情况下,我们的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 $

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

注意:当虚拟环境被激活时(当你的提示在其前面有(polls)时),使用pip而不是pip3 ,即使你使用的是Python 3.虚拟环境的工具副本总是命名为pip ,不管Python是什么版。

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文档编写您的第一个Django应用程序教程。 完成后,请跳至第5步

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

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

cd进去,列出目录内容:

cd django-polls
ls

您应该看到以下对象:

LICENSE  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实例的地址或域名列表。 具有不在此列表中的主机头的传入请求将引发异常。 Django要求您将其设置为防止某类安全漏洞

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

在下面的代码段中,有一些注释掉的示例可以演示这些条目应该是什么样子:

〜/ Django的投票/ 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引擎。 我们还将重用第2步中引用的连接参数 您始终可以从DigitalOcean 云控制面板的“托管数据库”部分中找到此信息。

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

〜/ Django的投票/ 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的投票/ 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存储库中SECRET_KEY ,因此我们将使用内置于django Python包中的get_random_secret_key()创建一个新变量。 从命令行打开Python解释器:

python

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

Python 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的投票/ 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文档中的迁移

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

./manage.py createsuperuser

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

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

./manage.py collectstatic

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

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

键入以下命令为端口8000创建例外:

sudo ufw allow 8000

使用Django Development Server测试应用程序

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

./manage.py runserver 0.0.0.0:8000

在Web浏览器中,访问服务器的域名或IP地址,然后访问:8000polls路径:

http://server_domain_or_IP:8000/polls

你应该看到民意调查app界面:

民意调查App界面

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

http://server_domain_or_IP:8000/admin

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

民意调查管理员身份验证页面

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

身份验证后,您可以访问民意调查应用程序的管理界面:

民意调查管理主界面

完成浏览后,在终端窗口中按CTRL-C关闭开发服务器。

使用Gunicorn测试应用程序

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

gunicorn --bind 0.0.0.0:8000 mysite.wsgi

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

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

我们通过指定Django的wsgi.py文件(我们的应用程序的入口点)的相对目录路径向wsgi.py传递了一个模块。 该文件定义了一个名为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 S3Boto3Storage包为Django提供了S3Boto3Storage存储后端,后端使用boto3库将文件上传到任何兼容S3的对象存储服务。

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

pip install django-storages boto3

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

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

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

〜/ Django的投票/ mysite的/ settings.py
. . .

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

. . .

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

〜/ Django的投票/ 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 :您在教程先决条件中创建的Space的访问密钥ID。 如果您未创建一组访问密钥,请参阅使用访问密钥共享对空间的访问权限
  • AWS_SECRET_ACCESS_KEY :Dig​​italOcean Space的密钥。
  • AWS_STORAGE_BUCKET_NAME :您的DigitalOcean Space名称。
  • AWS_S3_ENDPOINT_URL :用于访问对象存储服务的端点URL。 对于DigitalOcean,这将类似于https://nyc3.digitaloceanspaces.com具体取决于Space区域。
  • 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时,它将开始从这个Space提供CSS和Javascript等静态资源。

在我们测试这一切都正常运行之前,我们需要为Spaces文件配置跨源资源共享(CORS)标头,否则您的Web浏览器可能会拒绝访问某些静态资产。

配置CORS标头

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

首先,使用Cloud Control Panel导航到Space的Settings页面:

“设置”选项卡的屏幕截图

在“ CORS配置”部分中,单击“ 添加”

CORS高级设置

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

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

在“ 允许的方法”下 ,选择“ GET”

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

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

单击保存选项

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

此时,您可以选择为您的Space启用CDN,它将从边缘服务器的分布式网络提供这些静态文件。 要了解有关CDN的更多信息,请参阅使用CDN加速静态内容交付 这可以显着提高Web性能。 如果您不想为Space启用CDN,请跳至下一节“ 测试空间静态文件传递”

启用CDN(可选)

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

为Space启用CDN后,使用Cloud Control Panel导航至该区域。 您应该在Space名称下看到一个新的Endpoints链接:

空间端点列表

这些端点应包含您的Space名称。

请注意添加了新的Edge端点。 此端点通过CDN路由对Spaces对象的请求,尽可能地从边缘缓存为它们提供服务。 记下这个Edge端点,因为我们将使用它来配置django-storages插件。

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

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

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

〜/ Django的投票/ 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:
# https://www.howtoing.com/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

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

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

测试空间静态文件传递

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

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

cd ~/django-polls

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

python manage.py collectstatic

您应该看到以下输出:

You 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确认。

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

121 static files copied.

这证实了Django成功将polls app静态文件上传到您的Space。 您可以使用Cloud Control Panel导航到Space,并检查static目录中的文件。

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

启动Gunicorn服务器:

gunicorn --bind 0.0.0.0:8000 mysite.wsgi    

在Web浏览器中,访问服务器的域名或IP地址,然后:8000/admin

http://server_domain_or_IP:8000/admin

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

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

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

您应该看到以下窗口:

Chrome开发者工具窗口

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

这证实了CSS样式表和字体是从Space的CDN正确提供的。

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

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

deactivate

您的提示应该恢复正常。

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

第8步 - 为Gunicorn创建系统的套接字和服务文件

第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进程可以收集journald日志。 我们还可以在这里指定任何可选的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

您应该看到以下输出:

Failed 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
/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
● gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   Active: inactive (dead)

To test the socket activation mechanism, we can send a connection to the socket through curl by typing:

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

You should see the HTML output from your application in the terminal. This indicates that Gunicorn has started and is able to serve your Django application. You can verify that the Gunicorn service is running by typing:

sudo systemctl status gunicorn
● 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"

If the output from curl or the output of systemctl status indicates that a problem occurred, check the logs for additional details:

sudo journalctl -u gunicorn

You can also check your /etc/systemd/system/gunicorn.service file for problems. If you make changes to this file, be sure to reload the daemon to reread the service definition and restart the Gunicorn process:

sudo systemctl daemon-reload
sudo systemctl restart gunicorn

Make sure you troubleshoot any issues before continuing on to configuring the Nginx server.

Step 8 — Configuring Nginx HTTPS and Gunicorn Proxy Passing

Now that Gunicorn is set up in a more robust fashion, we need to configure Nginx to encrypt connections and hand off traffic to the Gunicorn process.

If you followed the preqrequisites and set up Nginx with Let's Encrypt, you should already have a server block file corresponding to your domain available to you in Nginx's sites-available directory. If not, follow How To Secure Nginx with Let's Encrypt on Ubuntu 18.04 and return to this step.

Before we edit this example.com server block file, we'll first remove the default server block file that gets rolled out by default after installing Nginx:

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

We'll now modify the example.com server block file to pass traffic to Gunicorn instead of the default index.html page configured in the prerequisite step.

Open the server block file corresponding to your domain in your editor:

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


}

This is a combination of the default server block file created in How to Install Nginx on Ubuntu 18.04 as well as additions appended automatically by Let's Encrypt. We are going to delete the contents of this file and write a new configuration that redirects HTTP traffic to HTTPS, and forwards incoming requests to the Gunicorn socket we created in the previous step.

If you'd like, you can make a backup of this file using cp . Quit your text editor and create a backup called example.com.old :

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

Now, reopen the file and delete its contents. We'll build the new configuration block by block.

Begin by pasting in the following block, which redirects HTTP requests at port 80 to 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;
}

Here we listen for HTTP IPv4 and IPv6 requests on port 80 and send a 301 response header to redirect the request to HTTPS port 443 using the example.com domain. This will also redirect direct HTTP requests to the server's IP address.

After this block, append the following block of config code that handles HTTPS requests for the example.com domain:

/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;
    }
}

Here, we first listen on port 443 for requests hitting the example.com and www. example.com domains.

Next, we provide the same Let's Encrypt configuration included in the default server block file, which specifies the location of the SSL certificate and private key, as well as some additional security parameters.

The location = /favicon.ico line instructs Nginx to ignore any problems with finding a favicon.

The last location = / block instructs Nginx to hand off requests to the Gunicorn socket configured in Step 8 . In addition, it adds headers to inform the upstream Django server that a request has been forwarded and to provide it with various request properties.

After you've pasted in those two configuration blocks, the final file should look something like this:

/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;
        }
}

完成后保存并关闭文件。

Test your Nginx configuration for syntax errors by typing:

sudo nginx -t

If your configuration is error-free, restart Nginx by typing:

sudo systemctl restart nginx

You should now be able to visit your server's domain or IP address to view your application. Your browser should be using a secure HTTPS connection to connect to the Django backend.

To completely secure our Django project, we need to add a couple of security parameters to its settings.py file. Reopen this file in your editor:

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

Scroll to the bottom of the file, and add the following parameters:

~/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

These settings tell Django that you have enabled HTTPS on your server, and instruct it to use "secure" cookies. To learn more about these settings, consult the SSL/HTTPS section of Security in Django .

When you're done, save and close the file.

Finally, restart Gunicorn:

sudo systemctl restart gunicorn

At this point, you have configured Nginx to redirect HTTP requests and hand off these requests to Gunicorn. HTTPS should now be fully enabled for your Django project and app. If you're running into errors, this discussion on troubleshooting Nginx and Gunicorn may help.

Warning: As stated in Configuring CORS Headers , be sure to change the Origin from the wildcard * domain to your domain name ( https://example.com in this guide) before making your app accessible to end users.

结论

In this guide, you set up and configured a scalable Django application running on an Ubuntu 18.04 server. This setup can be replicated across multiple servers to create a highly-available architecture. Furthermore, this app and its config can be containerized using Docker or another container runtime to ease deployment and scaling. These containers can then be deployed into a container cluster like Kubernetes . In an upcoming Tutorial series, we will explore how to containerize and modernize this Django polls app so that it can run in a Kubernetes cluster.

In addition to static files, you may also wish to offload your Django Media files to object storage. To learn how to do this, consult Using Amazon S3 to Store your Django Site's Static and Media Files . You might also consider compressing static files to further optimize their delivery to end users. To do this, you can use a Django plugin like Django compressor .