Django 教程第 11 部分:将 Django 部署到生产环境

你已经使用 Django 创建并测试了示例网站,现在是时候将其安装到 Web 服务器上,以便任何人都可以通过公共互联网访问它。本页介绍了如何托管 Django 项目以及你需要为生产部署做哪些准备。

预备知识 完成之前所有的教程主题,包括Django 教程第 10 部分:测试 Django Web 应用程序
目标 学习可以在何处以及如何将 Django 应用部署到生产环境。

概述

当你的网站完成(或“足够完成”以开始公开测试)后,你需要将其托管在一个比你个人开发计算机更公开、更易于访问的地方。

到目前为止,你一直在开发环境中工作,使用 Django 开发 Web 服务器将你的网站共享到本地浏览器/网络,并使用(不安全的)开发设置运行你的网站,这些设置会暴露调试和其他私人信息。在你可以在外部托管网站之前,你首先需要:

  • 对你的项目设置进行一些更改。
  • 选择一个用于托管 Django 应用的环境。
  • 选择一个用于托管任何静态文件的环境。
  • 为你的网站设置一个生产级别的基础设施。

本教程为你选择托管站点提供了一些指导,简要概述了为了让你的 Django 应用准备好生产环境需要做些什么,并提供了一个将 LocalLibrary 网站安装到 Railway 云托管服务上的工作示例。

什么是生产环境?

生产环境是服务器计算机提供的环境,你将在其中运行你的网站供外部消费。该环境包括:

  • 网站运行的计算机硬件。
  • 操作系统(例如 Linux、Windows)。
  • 你的网站所基于的编程语言运行时和框架库。
  • 用于提供页面和其他内容的 Web 服务器(例如 Nginx、Apache)。
  • 在你的 Django 网站和 Web 服务器之间传递“动态”请求的应用服务器。
  • 你的网站所依赖的数据库。

备注: 根据你的生产环境配置,你可能还会有一个反向代理、负载均衡器等。

服务器计算机可以位于你的场所内,通过高速链接连接到互联网,但更常见的是使用托管在“云端”的计算机。这实际上意味着你的代码运行在你的托管公司数据中心里的某个远程计算机(或可能是“虚拟”计算机)上。远程服务器通常会以一定的价格提供一定水平的计算资源(CPU、RAM、存储内存等)和互联网连接。

这种可远程访问的计算/网络硬件被称为基础设施即服务 (IaaS)。许多 IaaS 供应商提供预装特定操作系统的选项,你必须在其上安装生产环境的其他组件。其他供应商则允许你选择功能更齐全的环境,可能包括一个完整的 Django 和 Web 服务器设置。

备注: 预构建的环境可以使你的网站设置非常容易,因为它们减少了配置,但可用的选项可能会限制你使用不熟悉的服务器(或其他组件),并且可能基于较旧版本的操作系统。通常情况下,最好自己安装组件,这样你可以得到你想要的组件,当需要升级系统部分时,你也有个大致的了解!

其他托管提供商将 Django 作为平台即服务 (PaaS) 产品的一部分来支持。在这种托管方式中,你不需要担心大部分的生产环境(Web 服务器、应用服务器、负载均衡器),因为托管平台会为你处理这些——以及为了扩展你的应用所需的大部分工作。这使得部署变得非常简单,因为你只需要专注于你的 Web 应用程序,而不是所有其他的服务器基础设施。

一些开发者会选择 IaaS 提供的更大灵活性而不是 PaaS,而另一些开发者则会欣赏 PaaS 减少的维护开销和更容易的扩展性。当你刚开始时,在 PaaS 系统上设置你的网站要容易得多,因此我们将在本教程中这样做。

备注: 如果你选择一个对 Python/Django 友好的托管提供商,他们应该会提供关于如何使用不同配置的 Web 服务器、应用服务器、反向代理等来设置 Django 网站的说明(如果你选择 PaaS,这将不相关)。例如,DigitalOcean Django 社区文档中有许多针对各种配置的逐步指南。

选择托管服务提供商

有许多托管提供商已知积极支持或与 Django 良好协作,包括:HerokuDigitalOceanRailwayPython AnywhereAmazon Web ServicesAzureGoogle CloudHetznerVultr Cloud Compute——仅举几例。这些供应商提供不同类型的环境(IaaS、PaaS),以及不同价格的不同级别的计算和网络资源。

选择托管时需要考虑的一些事项

  • 你的网站可能会有多繁忙,以及满足该需求所需的数据和计算资源的成本。
  • 对水平扩展(增加更多机器)和垂直扩展(升级到更强大的机器)的支持程度及其成本。
  • 供应商的数据中心位置,从而决定了访问速度可能最快的地方。
  • 托管商的历史正常运行时间和停机时间表现。
  • 提供用于管理网站的工具——它们是否易于使用且安全(例如 SFTP 与 FTP)。
  • 用于监控服务器的内置框架。
  • 已知的限制。一些托管商会故意阻止某些服务(例如电子邮件)。其他一些则在某些价格层级中只提供一定小时数的“在线时间”,或者只提供少量的存储空间。
  • 额外的好处。一些提供商会提供免费域名和对 TLS 证书的支持,否则你将不得不为此付费。
  • 你所依赖的“免费”层级是否会随时间到期,以及迁移到更昂贵层级的成本是否意味着你一开始就应该使用其他服务!

好消息是,当你刚开始时,有相当多的网站提供用于评估和测试的“免费”计算环境。这些通常是资源相当受限/有限的环境,你需要注意它们可能会在某个介绍期后到期或有其他限制。然而,它们非常适合在托管环境中测试低流量网站,并且当你的网站变得更繁忙时,可以轻松迁移到付费以获取更多资源。此类中的热门选择包括 Vultr Cloud ComputePython AnywhereAmazon Web ServicesMicrosoft Azure 等。

大多数提供商还提供一个“基础”层级,专为小型生产网站设计,提供更有用的计算能力和更少的限制。RailwayHerokuDigitalOcean 是拥有相对便宜的基础计算层级(每月 5 到 10 美元范围)的热门托管提供商的例子。

备注: 请记住,价格不是唯一的选择标准。如果你的网站成功,可扩展性可能会成为最重要的考虑因素。

准备发布你的网站

使用 django-adminmanage.py 工具创建的Django 骨架网站的配置是为了让开发更容易。出于安全或性能原因,许多 Django 项目设置(在 settings.py 中指定)在生产环境中应有所不同。

备注: 通常会为生产环境准备一个单独的 settings.py 文件,和/或有条件地从一个单独的文件或环境变量中导入敏感设置。然后应该保护该文件,即使其余的源代码在公共存储库中可用。

你必须检查的关键设置是:

  • DEBUG。在生产环境中应设置为 False (DEBUG = False)。这会阻止显示敏感/机密的调试跟踪和变量信息。
  • SECRET_KEY。这是一个用于 CSRF 保护等的大型随机值。重要的是,生产中使用的密钥不应在源代码控制中,也不应在生产服务器外部可访问。

Django 文档建议,机密信息最好从环境变量加载或从仅限服务器的文件中读取。让我们更改LocalLibrary应用程序,以便如果定义了SECRET_KEYDEBUG变量,就从环境变量中读取它们,否则回退到根目录中 .env 文件中定义的值,最后使用配置文件中的默认值。这是非常灵活的,因为它允许托管服务器支持的任何配置。

为了从文件中读取环境变量值,我们将使用 python-dotenv。这是一个用于从文件中读取键值对并将其用作环境变量的库,但仅当相应的环境变量未定义时。

将该库安装到你的虚拟环境中,如下所示(并更新你的 requirements.txt 文件):

bash
pip3 install python-dotenv

然后打开 /locallibrary/settings.py,在 BASE_DIR 定义之后、安全警告之前插入以下代码:# SECURITY WARNING: keep the secret key used in production secret!

python
# Support env variables from .env file if defined
import os
from dotenv import load_dotenv
env_path = load_dotenv(os.path.join(BASE_DIR, '.env'))
load_dotenv(env_path)

这会从 Web 应用程序的根目录加载 .env 文件。当在 os.environ.get('<KEY>'', '<DEFAULT VALUE>') 中使用键时,如果文件中定义了形如 KEY=VALUE 的变量,它们就会被导入。

备注: 你添加到 .env 的任何值都可能是机密!你不能将它们保存到 GitHub,并且应该将 .env 添加到你的 .gitignore 文件中,以防意外添加。

接下来禁用原始的 SECRET_KEY 配置,并添加如下所示的新行。在开发期间,不会为该密钥指定环境变量,因此将使用默认值(在这里使用什么密钥或密钥是否“泄露”都无关紧要,因为你不会在生产中使用它)。

python
# SECURITY WARNING: keep the secret key used in production secret!
# SECRET_KEY = 'django-insecure-&psk#na5l=p3q8_a+-$4w1f^lt3lx1c@d*p4x$ymm_rn7pwb87'
import os
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'django-insecure-&psk#na5l=p3q8_a+-$4w1f^lt3lx1c@d*p4x$ymm_rn7pwb87')

然后注释掉现有的 DEBUG 设置,并添加如下所示的新行。

python
# SECURITY WARNING: don't run with debug turned on in production!
# DEBUG = True
DEBUG = os.environ.get('DJANGO_DEBUG', '') != 'False'

DEBUG 的值默认为 True,但只有当 DJANGO_DEBUG 环境变量的值设置为 False 或在 .env 文件中设置了 DJANGO_DEBUG=False 时,它才会是 False。请注意,环境变量是字符串而不是 Python 类型。因此,我们需要比较字符串。将 DEBUG 变量设置为 False 的唯一方法是实际将其设置为字符串 'False'

你可以在 Linux 上通过发出以下命令将环境变量设置为“False”:

bash
export DJANGO_DEBUG=False

你可能想要更改的设置的完整清单可以在部署清单(Django 文档)中找到。你也可以使用下面的终端命令列出其中的一些:

python
python3 manage.py check --deploy

Gunicorn

Gunicorn 是一个纯 Python HTTP 服务器,通常用于为 Django WSGI 应用程序提供服务。

虽然在开发期间我们不需要Gunicorn来为我们的 LocalLibrary 应用程序提供服务,但我们会在本地安装它,以便在部署应用程序时它成为我们需求的一部分。

首先确保你处于设置开发环境时创建的 Python 虚拟环境中(使用 workon [name-of-virtual-environment] 命令)。然后在命令行中使用 pip 在本地安装 Gunicorn

bash
pip3 install gunicorn

数据库配置

SQLite 是你一直在开发中使用的默认 Django 数据库,对于中小型网站来说是一个合理的选择。不幸的是,它不能在一些流行的托管服务(如 Heroku)上使用,因为它们在应用程序环境中不提供持久数据存储(这是 SQLite 的要求)。虽然这可能不会影响我们的示例部署,但我们将向你展示另一种在 Railway、Heroku 和其他一些服务上可行的方法。

方法是使用一个在互联网上某个地方以其自身进程运行的数据库,并通过作为环境变量传递的地址由 Django 库应用程序访问。在这种情况下,我们将使用一个也托管在 Railway 上的 Postgres 数据库,但你可以使用任何你喜欢的数据库托管服务。

数据库连接信息将通过名为 DATABASE_URL 的环境变量提供给 Django。我们将使用 dj-database-url 包来解析 DATABASE_URL 环境变量,并自动将其转换为 Django 所需的配置格式,而不是在 Django 中硬编码这些信息。除了安装 dj-database-url 包外,我们还需要安装 psycopg2,因为 Django 需要它来与 Postgres 数据库交互。

dj-database-url

dj-database-url 用于从环境变量中提取 Django 数据库配置。

在本地安装它,使其成为我们在部署服务器上设置的需求的一部分:

bash
pip3 install dj-database-url

settings.py

打开 /locallibrary/settings.py 并将以下配置复制到文件底部:

python
# Update database configuration from $DATABASE_URL environment variable (if defined)
import dj_database_url

if 'DATABASE_URL' in os.environ:
    DATABASES['default'] = dj_database_url.config(
        conn_max_age=500,
        conn_health_checks=True,
    )

如果设置了环境变量,Django 现在将使用 DATABASE_URL 中的数据库配置;否则,它将使用默认的 SQLite 数据库。conn_max_age=500 的值使连接持久化,这比在每个请求周期重新创建连接效率高得多(这是可选的,如果需要可以移除)。

psycopg2

Django 需要 psycopg2 才能与 Postgres 数据库一起工作。在本地安装它,使其成为我们在 Railway 远程服务器上设置的需求的一部分:

bash
pip3 install psycopg2-binary

请注意,除非设置了 DATABASE_URL,否则 Django 在开发期间将默认使用 SQLite 数据库。你可以完全切换到 Postgres,并通过在开发环境中设置相同的环境变量来为开发和生产使用相同的托管数据库(Railway 使得为生产和开发使用相同的环境变得容易)。或者,你也可以在本地计算机上安装并使用自托管的 Postgres 数据库

在生产环境中提供静态文件

在开发期间,我们使用 Django 和 Django 开发 Web 服务器来提供动态 HTML 和静态文件(CSS、JavaScript 等)。这对静态文件来说是低效的,因为请求必须通过 Django,尽管 Django 对它们不做任何处理。虽然这在开发期间无关紧要,但如果我们在生产中使用相同的方法,将会产生显著的性能影响。

在生产环境中,我们通常将静态文件与 Django Web 应用程序分开,使其更容易直接从 Web 服务器或内容分发网络(CDN)提供服务。

重要的设置变量是:

  • STATIC_URL:这是将提供静态文件的基本 URL 位置,例如在 CDN 上。
  • STATIC_ROOT:这是一个目录的绝对路径,Django 的 collectstatic 工具将在此处收集我们模板中引用的任何静态文件。一旦收集完毕,这些文件就可以作为一个组上传到文件托管的任何地方。
  • STATICFILES_DIRS:这列出了 Django 的 collectstatic 工具应该搜索静态文件的其他目录。

Django 模板中引用静态文件位置时,是相对于 static 标签的(你可以在Django 教程第 5 部分:创建我们的主页中定义的基础模板中看到这一点),该标签又映射到 STATIC_URL 设置。因此,静态文件可以上传到任何主机,你可以使用此设置更新你的应用程序以找到它们。

collectstatic 工具用于将静态文件收集到由 STATIC_ROOT 项目设置定义的文件夹中。它通过以下命令调用:

bash
python3 manage.py collectstatic

在本教程中,可以在上传应用程序之前运行 collectstatic,将应用程序中的所有静态文件复制到 STATIC_ROOT 中指定的位置。然后 Whitenoise 会从 STATIC_ROOT 定义的位置(默认情况下)找到文件,并在由 STATIC_URL 定义的基本 URL 处提供它们。

settings.py

打开 /locallibrary/settings.py 并将以下配置复制到文件底部。BASE_DIR 应该已经在你的文件中定义了(STATIC_URL 可能在文件创建时已经定义了。虽然不会造成任何损害,但你最好删除重复的先前引用)。

python
# Static files (CSS, JavaScript, Images)
# https://docs.django.ac.cn/en/5.0/howto/static-files/

# The absolute path to the directory where collectstatic will collect static files for deployment.
STATIC_ROOT = BASE_DIR / 'staticfiles'

# The URL to use when referring to static files (where they will be served from)
STATIC_URL = '/static/'

我们实际上将使用一个名为 WhiteNoise 的库来提供文件服务,我们将在下一节中安装和配置它。

Whitenoise

在生产环境中提供静态文件有很多方法(我们在前几节中看到了相关的 Django 设置)。WhiteNoise 项目提供了一种在生产环境中直接从 Gunicorn 提供静态资产的最简单方法之一。

请查看 WhiteNoise 文档,了解其工作原理以及为什么该实现是提供这些文件的相对有效的方法。

设置 WhiteNoise 以与项目一起使用的步骤在此处给出(并在下面重现):

安装 whitenoise

使用以下命令在本地安装 whitenoise:

bash
pip3 install whitenoise

settings.py

要将 WhiteNoise 安装到你的 Django 应用程序中,请打开 /locallibrary/settings.py,找到 MIDDLEWARE 设置,并将 WhiteNoiseMiddleware 添加到列表的顶部附近,紧接在 SecurityMiddleware 下方:

python
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

可选地,你可以在提供静态文件时减小其大小(这样更有效)。只需将以下内容添加到 /locallibrary/settings.py 的底部:

python
# Static file serving.
# https://whitenoise.readthedocs.io/en/stable/django.html#add-compression-and-caching-support
STORAGES = {
    # ...
    "staticfiles": {
        "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
    },
}

你不需要做任何其他事情来配置 WhiteNoise,因为它默认使用你的项目的 STATIC_ROOTSTATIC_URL 设置。

依赖项

你的 Web 应用程序的 Python 依赖项应存储在你的存储库根目录下的 requirements.txt 文件中。许多托管服务会自动安装此文件中的依赖项(在其他服务中,你必须自己执行此操作)。你可以使用命令行上的 pip 创建此文件(在仓库根目录中运行以下命令):

bash
pip3 freeze > requirements.txt

安装完上述所有不同的依赖项后,你的 requirements.txt 文件应至少包含以下项目(尽管版本号可能不同)。请删除下面未列出的任何其他依赖项,除非你已明确为该应用程序添加了它们。

Django==5.0.2
dj-database-url==2.1.0
gunicorn==21.2.0
psycopg2-binary==2.9.9
wheel==0.38.1
whitenoise==6.6.0
python-dotenv==1.0.1

更新你在 GitHub 上的应用程序仓库

许多托管服务允许你从本地存储库或基于云的源代码版本控制平台导入和/或同步项目。这可以使部署和迭代开发变得更加容易。

你应该已经在使用 GitHub 存储本地图书馆的源代码了(这是在使用 Git 和 GitHub 进行源代码管理中作为设置开发环境的一部分完成的)。

这是一个备份你的“纯净”项目的好时机——虽然我们将在以下部分进行的一些更改可能对任何托管服务的部署(或开发)都有用,但其他更改可能不是。假设你已经将到目前为止的所有更改备份到 GitHub 上的 `main` 分支,你可以创建一个新分支来备份你的更改,如下所示:

bash
# Fetch the latest main branch
git checkout main
git pull origin main

# Create branch vanilla_deployment from the current branch (main)
git checkout -b vanilla_deployment

# Push the new branch to GitHub
git push origin vanilla_deployment

# Switch back to main
git checkout main

# Make any further changes in a new branch
git checkout -b my_changes_for_deployment # Create a new branch

示例:托管在 PythonAnywhere

本节提供了一个关于如何在 PythonAnywhere 上托管LocalLibrary的实践演示。

为什么选择 PythonAnywhere?

我们选择使用 PythonAnywhere 的原因有几个:

  • PythonAnywhere 有一个免费的初学者计划,这个计划是真正免费的,尽管有一些限制。对于 MDN 来说,所有开发者都能负担得起这一点非常重要!

    备注: 本教程曾托管在 Heroku、Railway,现在是 PythonAnywhere,当之前的免费计划停止时就会迁移。我们选择 PythonAnywhere 是因为我们认为这个计划很可能会保持免费。我们也保留了 Railway 的例子,它不是免费的,用于比较,并且因为它能让我们更容易地演示一些特性,比如与在不同服务上运行的 Postgres 数据库集成。

  • PythonAnywhere 负责基础设施,所以你不需要操心。不用担心服务器、负载均衡器、反向代理等,这使得入门变得容易得多。

  • 你在使用 PythonAnywhere 时学到的技能和概念是可以迁移的。

  • 服务和计划的限制对我们在本教程中使用 PythonAnywhere 并没有特别大的影响。例如:

    • 初学者计划允许在 <your-username>.pythonanywhere.com 域名下拥有一个 Web 应用,限制你的应用对外访问互联网,CPU/带宽较低,不支持 IPython/Jupyter notebook,没有免费的 Postgres 数据库。但有足够的空间来运行我们的基础网站!
    • 不支持自定义域名(在撰写本文时)。
    • 环境在不使用时会关闭,因此重新启动可能会很慢。你可以让它永远运行,但你需要每三个月访问一次网站并续订 Web 应用程序。
    • 有免费支持一个独立的 MySQL 数据库,但不支持 Postgres。在这个演示中,我们将只使用 Django 在托管的 Ubuntu 环境中创建的默认 SQLite 数据库。

PythonAnywhere 适合托管这个演示,并且如果需要,可以扩展到更大的项目。你应该花时间确定它是否适合你自己的网站

PythonAnywhere 是如何工作的?

PythonAnywhere 提供了一个完全基于 Web 的界面,用于上传、编辑和以其他方式处理你的应用程序。

通过这个界面,你可以启动一个连接到 Ubuntu Linux 环境的 bash 控制台,在其中创建你的应用程序。在这个演示中,我们将使用控制台克隆我们的本地图书馆 GitHub 仓库,并创建一个可以运行 Web 应用程序的 Python 环境。

免费计划不提供独立的 Postgres 支持。虽然我们可以使用其他托管服务来托管我们的数据库,但我们将只使用 Django 在托管的 Ubuntu 环境中创建的默认 SQLite 数据库(有足够的空间来演示图书馆的功能)。

一旦应用程序运行起来,就可以通过 bash 控制台设置环境变量来进行生产环境配置。

这就是开始所需的所有概述。

获取一个 PythonAnywhere 账户

要开始使用 PythonAnywhere,你首先需要创建一个账户:

  • 转到 PythonAnywhere 的计划与定价页面,然后选择创建初学者账户按钮。
  • 用你的用户名、电子邮件和密码创建一个账户,确认条款和条件,然后选择注册
  • 然后你将被登录并重定向到 PythonAnywhere 仪表板:https://www.pythonanywhere.com/user/<your_user_name>/

从 GitHub 安装库

接下来,我们将打开一个 Bash 提示符,设置一个虚拟环境,并从 GitHub 获取本地图书馆的源代码。我们还将配置默认数据库并收集静态文件,以便它们可以由 PythonAnywhere 提供服务。

  1. 首先,通过选择顶部应用程序栏中的Consoles来打开控制台管理屏幕。

  2. 然后选择 Bash 链接来创建并启动一个新的控制台。

    Image of PythonAnywhere Console management screen

    请注意,你创建的任何控制台都会被保存以供以后重复使用,连同其所有历史记录。上面的绿色箭头显示该账户有一个我们可以打开的控制台。

  3. 在控制台中,输入以下命令来创建一个名为 "env_local_library" 的 Python 3.10 虚拟环境,用于安装本地图书馆的依赖项。

    bash
    mkvirtualenv --python=python3.10 env_local_library
    

    这与设置 Django 开发环境中介绍的过程完全相同。我们可以给环境起任何名字,并且可以使用以下命令停用和重新激活它:

    bash
    deactivate
    workon env_local_library
    
  4. 接下来从 GitHub 获取库的源代码。PythonAnywhere 希望你将应用程序安装在以你的站点 URL 命名的文件夹中。

    备注: 因为我们使用的是免费账户,你只能将你的账户命名为 <your_pythonanywhere_username>.pythonanywhere.com(例如,如果你的用户名是“Odtsetseg”,你将不得不将本地图书馆的源代码放入一个名为 odtsetseg.pythonanywhere.com 的文件夹中)。

    输入以下命令将你的库源代码克隆到一个适当命名的文件夹中(你需要将用户名值替换为你自己的名字):

    bash
    git clone https://github.com/<github_username>/django-locallibrary-tutorial.git <your_pythonanywhere_username>.pythonanywhere.com
    
    # Navigate into the new folder
    cd <your_pythonanywhere_username>.pythonanywhere.com
    
  5. 使用 requirements.txt 文件安装库的依赖项:

    bash
    pip3 install -r requirements.txt
    
  6. 在托管计算机上创建并配置一个 SQLite 数据库(就像我们在开发期间做的那样)。

    bash
    python manage.py migrate
    

    备注: 对于 Railway 的例子,我们将配置一个 Postgres 数据库,并通过设置 DATABASE_URL 环境变量来连接它。重要的是,migrate 必须在配置使用哪个数据库之后调用。

  7. 将所有静态文件收集到一个可以在生产环境中提供服务的位置:

    bash
    python manage.py collectstatic --no-input
    
  8. 为访问网站创建一个超级用户(如Django 管理站点部分所述):

    bash
    python manage.py createsuperuser
    

    记下详细信息,因为你需要它们来测试你的网站。

设置 Web 应用

在获取了本地图书馆的源代码并在虚拟环境中安装了依赖项之后,我们需要告诉 PythonAnywhere 如何找到它们并将它们用作 Web 应用。

  1. 导航到网站的Web部分,然后选择添加一个新的 Web 应用链接。

    PythonAnywhere "Web" section showing button for adding a new app

    然后,创建新的 Web 应用向导将打开,引导你配置 Web 应用的主要属性。

  2. 选择下一步跳过 Web 应用域名配置。免费账户将根据你的用户名创建域名:<user_name>.pythonanywhere.com

    PythonAnywhere prompt for setting the domain name of new web app

  3. 选择一个 Python Web 框架屏幕中,选择手动配置

    PythonAnywhere prompt for selecting web framework used for the application

    手动配置让我们能够完全控制环境的配置方式。这现在不那么重要,但如果我们托管多个网站,可能使用不同版本的 Python 和/或 Django,这就很重要了。

  4. 选择一个 Python 版本屏幕中选择 3.10

    PythonAnywhere prompt for selecting Python version for Web application

    更一般地,你应该选择你正在使用的 Django 版本所允许的最新 Python 版本。

  5. 手动配置屏幕中选择下一步(该屏幕只是解释了一些配置选项)。

    PythonAnywhere prompt explaining next configuration options

    Web 应用已创建,并显示在 Web 部分,如图所示。屏幕上有一个重载按钮,你可以在进行任何进一步更改后使用它来重新加载 Web 应用程序。如屏幕上所述,你需要点击运行直到 3 个月后按钮,才能让网站再存活三个月(并持续下去)。

    PythonAnywhere Configured Web app

  6. 向下滚动到Web选项卡的“代码”部分,然后选择指向 WSGI 配置文件的链接。它的名称形式为 /var/www/<user_name>_pythonanywhere_com_wsgi.py

    PythonAnywhere WSGI file in Web tab, code section

    用以下文本替换文件中的内容(首先用你自己的用户名更新“hamishwillee”),然后选择保存按钮。

    python
    import os
    import sys
    
    path = '/home/hamishwillee/hamishwillee.pythonanywhere.com'
    if path not in sys.path:
        sys.path.append(path)
    
    os.environ['DJANGO_SETTINGS_MODULE'] = 'locallibrary.settings'
    
    from django.core.wsgi import get_wsgi_application
    application = get_wsgi_application()
    

    请注意,WSGI 文件的作用是帮助 Gunicorn 服务器找到本地图书馆应用程序。PythonAnywhere 希望这个文件在这个位置,这就是为什么不能使用项目中已有的 WSGI 文件的原因。

  7. 向下滚动到Web选项卡的“Virtualenv”部分。选择链接输入虚拟环境的路径(如果需要)并输入上一节中创建的虚拟环境的路径。如果你像建议的那样将其命名为“env_local_library”,路径将是:/home/<user_name>/.virtualenvs/env_local_library

    PythonAnywhere Virtual env section of Web tab

  8. 向下滚动到Web选项卡的“静态文件”部分。

    PythonAnywhere Static files section of Web tab

    选择输入 URL链接并输入 /static_files/。这是应用程序设置中的 STATIC_URL,反映了我们在上一节运行 collectstatic 时文件被复制到的位置。

  9. Web选项卡的顶部附近,选择重载按钮以重新启动网站。然后选择网站 URL 链接以启动实时网站。

PythonAnywhere Web screen with the link to launch the site highlighted

设置 ALLOWED_HOSTS 和 CSRF_TRUSTED_ORIGINS

当网站打开时,此时你会看到一个如下所示的错误调试屏幕。这是一个 Django 安全错误,因为我们的源代码没有在“允许的主机”上运行。

A detailed error page with a full traceback of an invalid HTTP_HOST header

备注: 这种调试信息在设置时非常有用,但在部署的网站中是一个安全风险。在下一节中,我们将向你展示如何使用环境变量在实时网站上禁用此级别的日志记录。

在你的 GitHub 项目中打开 /locallibrary/settings.py,并将 ALLOWED_HOSTS 设置更改为包含你的 PythonAnywhere 网站 URL。

python
## For example, for a site URL at 'hamishwillee.pythonanywhere.com'
## (replace the string below with your own site URL):
ALLOWED_HOSTS = ['hamishwillee.pythonanywhere.com', '127.0.0.1']

# During development, you can instead set just the base URL
# (you might decide to change the site a few times).
# ALLOWED_HOSTS = ['.pythonanywhere.com','127.0.0.1']

由于应用程序使用 CSRF 保护,你还需要设置 CSRF_TRUSTED_ORIGINS 键。打开 /locallibrary/settings.py 并添加如下一行:

python
## For example, for a site URL is at 'web-production-3640.up.railway.app'
## (replace the string below with your own site URL):
CSRF_TRUSTED_ORIGINS = ['https://hamishwillee.pythonanywhere.com']

# During development/for this tutorial you can instead set just the base URL
# CSRF_TRUSTED_ORIGINS = ['https://*.pythonanywhere.com']

保存这些设置并将它们提交到你的 GitHub 仓库。

然后,你需要更新你在 PythonAnywhere 上的项目版本。假设你在 <user_name>.pythonanywhere.com 文件夹中使用你的 Bash 提示符,并且你已经将更改推送到主分支,那么你可以在 Bash 提示符中使用以下命令导入它们:

bash
git pull origin main

使用 Web 选项卡上的重启按钮来重启应用程序。如果你刷新你托管的网站,它现在应该可以打开并显示网站的主页。

你应该能够使用上面创建的超级用户账户登录,并创建作者、类型、书籍等,就像你在本地计算机上做的那样。

在 PythonAnywhere 上使用环境变量

准备发布你的网站一节中,我们修改了应用程序,使其可以在生产环境中使用环境变量或 .env 文件中的变量进行配置。

具体来说,我们设置了库,以便你可以设置:

  • DJANGO_DEBUG=False 以减少在出现错误时向用户显示的调试跟踪信息。
  • DJANGO_SECRET_KEY 在生产环境中设置为某个机密值。
  • DATABASE_URL 如果你的应用程序使用托管数据库(本例中我们不使用)。

环境变量的设置方式取决于托管服务。对于 PythonAnywhere,你需要从一个环境文件中读取它们。我们已经为此做好了准备,所以我们只需要创建这个文件。

步骤如下:

  1. 打开一个 PythonAnywhere Bash 提示符。

  2. 导航到你的应用程序目录(将 <user-name> 替换为你自己的账户):

    bash
    cd ~/<user-name>.pythonanywhere.com
    
  3. 通过将环境变量作为键值对写入 .env 文件来设置它们。例如,要在 Bash 控制台中将 DJANGO_DEBUG 设置为 False,请输入以下命令:

    bash
    echo "DJANGO_DEBUG=False" >> .env
    
  4. 重启应用程序。

你可以通过尝试打开一个不存在的记录来测试操作是否成功(例如,创建一个类型,然后在 URL 栏中增加数字以打开一个尚未创建的记录)。如果环境变量已加载,你将收到“未找到”的消息,而不是详细的调试跟踪。

示例:托管在 Railway

本节提供了一个关于如何在 Railway 上安装 LocalLibrary 的实践演示。

为什么选择 Railway?

警告: Railway 不再有完全免费的入门级套餐。我们保留这些说明是因为 Railway 有一些很棒的功能,并且对于某些用户来说会是更好的选择。

Railway 是一个有吸引力的托管选择,原因有几个:

  • Railway 负责大部分基础设施,所以你不需要操心。不用担心服务器、负载均衡器、反向代理等,这使得入门变得容易得多。
  • Railway 专注于开发和部署的开发者体验,这使得学习曲线比许多其他替代方案更快、更平缓。
  • 你在使用 Railway 时学到的技能和概念是可以迁移的。虽然 Railway 有一些出色的新功能,但其他流行的托管服务也使用了许多相同的想法和方法。
  • Railway 文档清晰而完整。
  • 该服务似乎非常可靠,如果你最终喜欢上它,定价是可预测的,并且扩展你的应用非常容易。

你应该花时间确定 Railway 是否适合你自己的网站

Railway 是如何工作的?

Web 应用程序各自在自己独立的、隔离的虚拟化容器中运行。为了执行你的应用程序,Railway 需要能够设置适当的环境和依赖项,并且还需要了解它是如何启动的。对于 Django 应用,我们通过一些文本文件提供这些信息:

  • runtime.txt:声明要使用的编程语言和版本。
  • requirements.txt:列出你的网站所需的 Python 依赖项,包括 Django。
  • Procfile:启动 Web 应用程序要执行的进程列表。对于 Django,这通常是 Gunicorn Web 应用服务器(带有一个 .wsgi 脚本)。
  • wsgi.pyWSGI 配置,用于在 Railway 环境中调用我们的 Django 应用程序。

一旦应用程序运行,它可以使用环境变量中提供的信息进行自我配置。例如,使用数据库的应用程序可以通过变量 DATABASE_URL 获取地址。数据库服务本身可以由 Railway 或其他提供商托管。

开发者通过 Railway 网站以及一个特殊的命令行界面(CLI)工具与 Railway 交互。CLI 允许你将本地 GitHub 仓库与一个 Railway 项目关联,从本地分支上传仓库到实时站点,检查正在运行的进程的日志,设置和获取配置变量等等。最有用的功能之一是你可以使用 CLI 在本地运行你的项目,使用与实时项目相同的环境变量。

为了让我们的应用程序在 Railway 上工作,我们需要将我们的 Django Web 应用程序放入一个 git 仓库中,添加上述文件,与一个数据库插件集成,并进行更改以正确处理静态文件。一旦我们完成了所有这些,我们就可以设置一个 Railway 账户,获取 Railway 客户端,并安装我们的网站。

这就是开始所需的所有概述。

为 Railway 更新应用

本节解释了你需要对我们的 LocalLibrary 应用程序进行的更改,以便它能在 Railway 上工作。我们实际上只需要创建一个 Procfile 和一个 runtime.txt 文件,因为几乎所有其他东西都已经存在了。

请注意,这些更改不会妨碍你使用我们已经学过的本地测试和工作流程。

Procfile

Procfile 是 Web 应用程序的“入口点”。它列出了 Railway 将执行以启动你网站的命令。

在你的 GitHub 仓库根目录下创建文件 Procfile(没有文件扩展名),并复制粘贴以下文本:

web: python manage.py migrate && python manage.py collectstatic --no-input && gunicorn locallibrary.wsgi

web: 前缀告诉 Railway 这是一个 Web 进程,可以向其发送 HTTP 流量。然后我们调用 Django 迁移命令 python manage.py migrate 来设置数据库表。接下来,我们调用 Django 命令 python manage.py collectstatic 将静态文件收集到由 STATIC_ROOT 项目设置定义的文件夹中(参见下文的在生产环境中提供静态文件部分)。最后,我们启动 gunicorn 进程,这是一个流行的 Web 应用服务器,将配置信息传递给模块 locallibrary.wsgi(与我们的应用程序骨架一起创建:/locallibrary/wsgi.py)。

你会注意到,我们已经设置了项目以包含 gunicorn 并支持提供静态文件!

你也可以使用 Procfile 来启动工作进程或在发布部署前运行其他非交互式任务。

运行时

如果定义了 runtime.txt 文件,它会告诉 Railway 使用哪个版本的 Python。在仓库的根目录下创建该文件并添加以下文本:

python-3.10.2

备注: 托管提供商不一定支持每个 Python 运行时的次要版本。他们通常会使用与你指定的值最接近的支持版本。

重新测试并保存更改到 GitHub

在继续之前,首先在本地再次测试网站,确保它没有被上述任何更改破坏。像往常一样运行开发 Web 服务器,然后检查网站在你的浏览器中是否仍然按预期工作。

bash
python3 manage.py runserver

接下来,让我们将更改 push 到 GitHub。在终端中(导航到我们的本地仓库后),输入以下命令:

python
git checkout -b railway_changes
git add -A
git commit -m "Added files and changes required for deployment"
git push origin railway_changes

然后在 GitHub 上创建并合并 PR。

现在我们应该准备好开始在 Railway 上部署 LocalLibrary 了。

获取一个 Railway 账户

要开始使用 Railway,你首先需要创建一个账户:

  • 访问 railway.com 并点击顶部工具栏中的登录链接。
  • 在弹窗中选择 GitHub,使用你的 GitHub 凭据登录。
  • 然后你可能需要去你的电子邮件中验证你的账户。
  • 然后你将被登录到 Railway.com 仪表板:https://railway.com/dashboard

从 GitHub 部署到 Railway

接下来,我们将设置 Railway 以从 GitHub 部署我们的图书馆。首先从网站顶部菜单中选择仪表板选项,然后选择新建项目按钮。

Railway website dashboard with new project button

Railway 将显示新项目的选项列表,包括从首先在你 GitHub 账户中创建的模板部署项目的选项,以及一些数据库选项。选择从 GitHub 仓库部署

Railway website screen - deploy

在设置过程中你与 Railway 共享的 GitHub 仓库中的所有项目都会显示出来。选择你本地图书馆的 GitHub 仓库:<user-name>/django-locallibrary-tutorial

Railway website screen showing a dialog to choose an existing GitHub repository or choose a new one

选择立即部署来确认你的部署。

Confirmation screen - select deploy

Railway 随后将加载并部署你的项目,在部署选项卡上显示进度。当部署成功完成时,你会看到如下所示的屏幕。

Railway website screen - deployment

你可以点击网站 URL(上面高亮显示的部分)在浏览器中打开网站(它仍然无法工作,因为设置尚未完成)。

设置 ALLOWED_HOSTS 和 CSRF_TRUSTED_ORIGINS

当网站打开时,此时你会看到一个如下所示的错误调试屏幕。这是一个 Django 安全错误,因为我们的源代码没有在“允许的主机”上运行。

A detailed error page with a full traceback of an invalid HTTP_HOST header

备注: 这种调试信息在设置时非常有用,但在部署的网站中是一个安全风险。一旦网站运行起来,我们将向你展示如何禁用它。

在你的 GitHub 项目中打开 /locallibrary/settings.py,并将 ALLOWED_HOSTS 设置更改为包含你的 Railway 网站 URL。

python
## For example, for a site URL at 'web-production-3640.up.railway.app'
## (replace the string below with your own site URL):
ALLOWED_HOSTS = ['web-production-3640.up.railway.app', '127.0.0.1']

# During development, you can instead set just the base URL
# (you might decide to change the site a few times).
# ALLOWED_HOSTS = ['.railway.com','127.0.0.1']

由于应用程序使用 CSRF 保护,你还需要设置 CSRF_TRUSTED_ORIGINS 键。打开 /locallibrary/settings.py 并添加如下一行:

python
## For example, for a site URL is at 'web-production-3640.up.railway.app'
## (replace the string below with your own site URL):
CSRF_TRUSTED_ORIGINS = ['https://web-production-3640.up.railway.app']

# During development/for this tutorial you can instead set just the base URL
# CSRF_TRUSTED_ORIGINS = ['https://*.railway.app']

然后保存你的设置并将其提交到你的 GitHub 仓库(Railway 将自动更新并重新部署你的应用程序)。

配置并连接一个 Postgres SQL 数据库

接下来,我们需要创建一个 Postgres 数据库,并将其连接到我们刚刚部署的 Django 应用程序。(如果你现在打开网站,你会收到一个新的错误,因为无法访问数据库)。我们将把数据库作为应用程序项目的一部分来创建,尽管你也可以在一个独立的单独项目中创建数据库。

在 Railway 上,从网站顶部菜单中选择仪表板选项,然后选择你的应用程序项目。在这个阶段,它只包含一个用于你的应用程序的服务(可以选择这个服务来设置变量和服务的其他细节)。设置按钮可以选择来更改项目范围的设置。选择新建按钮,用于向项目添加服务。

Railway project with new service button highlighted

当提示要添加的服务类型时,选择数据库

Railway project - select database as new service

然后选择添加 PostgreSQL 开始添加数据库。

Railway project - select Postgres as new service

Railway 随后将在同一项目中配置一个包含空数据库的服务。完成后,你现在将在项目视图中看到应用程序和数据库服务。

Railway project with application and Postgres database service

选择 Web 服务,然后选择变量选项卡。选择新建变量,然后在变量名框中,选择添加引用。向下滚动并选择 DATABASE_URL(这是我们设置 locallibrary 从环境变量中读取的变量名)。

Railway website screen selecting a DATABASE_URL

然后选择添加来添加变量引用,最后选择部署(这会出现在一个弹窗中)。请注意,你也可以打开 Postgres 数据库,然后打开其变量选项卡,然后复制变量过来。

如果你现在打开项目,它应该会像在本地一样显示。但是请注意,目前还没有办法用数据填充图书馆,因为我们还没有创建一个超级用户账户。我们将使用我们本地计算机上的 CLI 工具来完成这个操作。

安装客户端

按照这里的说明下载并安装适用于你本地操作系统的 Railway 客户端。

客户端安装后,你将能够运行命令。一些更重要的操作包括将计算机的当前目录部署到关联的 Railway 项目(无需上传到 GitHub),以及使用与生产服务器上相同的设置在本地运行你的 Django 项目。我们将在下一节中展示这些操作。

你可以在终端中输入以下命令来获取所有可能命令的列表。

bash
railway help

备注: 在以下部分中,我们使用 railway loginrailway link 将当前项目链接到一个目录。如果系统将你登出,你需要再次调用这两个命令来重新链接项目。

配置一个超级用户

为了创建一个超级用户,我们需要针对生产数据库调用 Django 的 createsuperuser 命令(这与我们在Django 教程第 4 部分:Django 管理站点 > 创建超级用户中本地运行的操作相同)。Railway 不提供对服务器的直接终端访问,而且我们不能将此命令添加到Procfile中,因为它是交互式的。

我们能做的是在我们的 Django 项目连接到生产数据库时,在本地调用这个命令。Railway 客户端通过提供一种机制,使用与生产服务器相同的环境变量(包括数据库连接字符串)在本地运行命令,从而使这变得容易。

首先,在你的 locallibrary 项目的 git 克隆中打开一个终端或命令提示符。然后使用 loginlogin --browserless 命令登录到你的浏览器账户(按照客户端或网站的任何提示和说明完成登录):

bash
railway login

登录后,使用以下命令将你当前的 locallibrary 目录链接到相关的 Railway 项目。请注意,当提示时,你需要选择/输入一个特定的项目。

bash
railway link

现在本地目录和项目已经链接,你可以使用生产环境的设置来运行本地 Django 项目了。首先确保你的常规Django 开发环境已经准备好。然后调用以下命令,并按要求输入名称、电子邮件和密码:

bash
railway run python manage.py createsuperuser

现在你应该能够打开你的网站管理区域(https://[your-url].railway.app/admin/)并填充数据库,就像Django 教程第 4 部分:Django 管理站点中展示的那样。

设置配置变量

最后一步是使网站安全。具体来说,我们需要禁用调试日志并设置一个机密的 CSRF 密钥。从环境变量中读取所需值的工作已在准备发布你的网站中完成(参见 DJANGO_DEBUGDJANGO_SECRET_KEY)。

打开项目的信息屏幕并选择变量选项卡。这应该已经有如下所示的 DATABASE_URL

Railway - add a new variable screen

有很多方法可以生成一个加密安全的密钥。一个简单的方法是在你的开发计算机上运行以下 Python 命令:

bash
python -c "import secrets; print(secrets.token_urlsafe())"

选择新建变量按钮,输入键 DJANGO_SECRET_KEY 和你的秘密值(然后选择添加)。然后输入键 DJANGO_DEBUG 和值 False。最终的变量集应该如下所示:

Railway screen showing all the project variables

调试

Railway 客户端提供了 logs 命令来显示日志的尾部(每个项目在网站上有更完整的日志可用):

bash
railway logs

如果这提供的信息不够,你需要开始研究Django 日志

总结

关于在生产环境中设置 Django 应用的教程到此结束,同时也是关于使用 Django 的系列教程的结尾。我们希望你觉得它们有用。你可以在GitHub 上查看一个完整实现版本的源代码

下一步是阅读我们最后几篇文章,然后完成评估任务。

另见