附录 E 部署故障排除

    附录 E 部署故障排除 - 图1
    成功地部署应用程序让人很有成就感,在首次成功时尤其如此。然而,部署过程中可能出现很多障碍,而且有些障碍难以识别并逾越。本附录旨在帮助你理解现代部署方法,并提供排除部署故障的具体方法。

    如果这里提供的额外信息不足以帮助你顺利地完成部署过程,请参阅本书提供的在线资源,其中的“更新”应该能够帮助你成功地完成部署。

    E.1 理解部署

    在排除部署故障时,对典型的部署原理有清晰认识大有裨益。所谓部署(deployment),指的是这样的一个过程:将运行在本地系统中的项目复制到远程服务器上,使其能够响应互联网用户发出的请求。相比于典型的本地系统,远程环境有很多重要的不同之处:使用的操作系统可能不同,而且很可能只是物理服务器上众多的虚拟服务器之一。

    要部署项目或将其推送到远程服务器上,需要执行如下步骤。

    • 在位于数据中心的物理服务器上创建虚拟服务器。
    • 在本地系统和远程服务器之间建立关联。
    • 将项目的代码复制到远程服务器上。
    • 确定项目依赖哪些库,并在远程服务器上安装这些库。
    • 创建数据库并运行所有的迁移。
    • 将静态文件(CSS、JavaScript 文件和媒体文件)复制到可被高效访问的位置。
    • 启动服务器以处理到来的请求。
    • 在项目准备好处理请求后,将到来的请求路由到项目。

    部署过程包含上述众多步骤,难怪常常以失败告终。所幸,了解部署过程之后,你就更有机会发现问题所在。通过这些发现,你就有可能找到解决方案,使得接下来的部署获得成功。

    可以在运行一种操作系统的本地系统中开发项目,并将项目推送到运行另一种操作系统的服务器上。知道目标服务器使用的操作系统类型很重要,因为这可为某些故障排除工作提供指导。在本书编写期间,Platform.sh 的基础远程服务器运行的是 Debian Linux。大多数远程服务器是基于 Linux 的系统。

    E.2 故障排除基础

    有些故障排除步骤随操作系统而异,稍后将介绍。我们先来介绍每次排除部署故障时都应尝试采取的措施。

    你能获得的最佳资源是推送过程中生成的输出。这种输出可能令人望而生畏:在刚接触应用程序部署的人看来,这种输出既冗长又显得技术含量很高。所幸,你不需要全面理解这些输出。浏览日志输出的目的有两个:确定哪些部署步骤管用,以及确定哪些步骤不管用。如果能够达成这两个目的,或许就能够确定该如何调整项目或部署过程,让下一次推送取得成功。

    E.2.1 按屏幕上的建议做

    你推送到的平台有时会生成消息,提供有关如何解决问题的建议。如果你在未初始化 Git 仓库的情况下创建 Platform.sh 项目,并尝试推送该项目,将出现下面的消息:

    $ platform push
    ❶ Enter a number to choose a project:
    [0] ll_project (votohz445ljyg)
    >
    0

    ❷ [RootNotFoundException]
    Project root not found. This can only be run from inside a project
    directory.

    ❸ To set the project for this Git repository, run:
    platform project:set-remote [id]

    这里试图推送项目,但还未将本地项目关联到远程项目,因此 Platform.sh CLI 询问要推送到哪个远程项目(见❶)。我们输入 0,以选择列出的唯一一个项目。但出现了异常 RootNotFoundException(见❷)。这是因为 Platform.sh 在本地项目中查找目录 .git,以确定如何将本地项目关联到远程项目,而由于在创建远程项目时没有目录 .git,因此根本没有建立这种关联。CLI 提供了问题解决建议(见❸):使用命令 project:set-remote 指定要将本地项目关联到哪个远程项目。

    下面尝试按这个建议做:

    $ platform project:set-remote votohz445ljyg
    Setting the remote project for this repository to: ll_project (votohz445ljyg)

    The remote project for this repository is
    now set to: ll_project (votohz445ljyg)

    在上面的输出中,CLI 指出了远程项目的 ID——votohz4451jyg。因此我们执行建议的命令,并在命令中指定这个 ID,让 CLI 能够在本地项目和远程项目之间建立关联。

    再次推送这个项目:

    $ platform push
    Are you sure you want to push to the main (production) branch? [Y/n]
    y
    Pushing HEAD to the existing environment main
    —snip—

    推送成功了,说明按屏幕上的建议做是管用的。

    对于不能完全明白的命令,应慎之又慎。然而,如果有充分的理由认为命令不会带来什么害处,而且建议的来源是值得信任的,那么按工具提供的建议做或许是合理的选择。

    注意:别忘了,有些人提供的命令会扫描你的系统,或者让你的系统对远程漏洞利用程序敞开大门。按值得信任的公司或组织开发的工具提供的建议做是一码事,按网上陌生人的建议做是另一码事。每当面对远程连接时,都务必万分谨慎。

    E.2.2 阅读日志输出

    前面说过,当你执行诸如 platform push 等命令时,出现的日志输出可能冗长且令人生畏。下面是另一次执行命令 platform push 时显示的日志输出片段,请仔细阅读,看看能否发现问题出在什么地方:

    —snip—
    Collecting soupsieve==2.3.2.post1
    Using cached soupsieve-2.3.2.post1-py3-none-any.whl (37 kB)
    Collecting sqlparse==0.4.2
    Using cached sqlparse-0.4.2-py3-none-any.whl (42 kB)
    Installing collected packages: platformshconfig, sqlparse,…
    Successfully installed Django-4.1 asgiref-3.5.2 beautifulsoup4-4.11.1…
    W: ERROR: Could not find a version that satisfies the requirement gunicorrn
    W: ERROR: No matching distribution found for gunicorrn

    130 static files copied to '
    appstatic'.

    Executing pre-flight checks…
    —snip—

    当部署以失败告终时,一个不错的策略是浏览日志输出,看看能否发现类似于警告或错误的内容。警告很常见,通常指出了该如何修改项目的依赖库,可帮助开发人员在问题带来麻烦前解决它们。

    在成功的推送过程中,可能会出现警告,但不会有任何错误。在这里,Platform.sh 无法安装 gunicorrn,这是因为文件 requirements_remote.txt 存在输入错误,将原本想要包含的 gunicorn 错误地拼写成了 gunicorrn(多了一个 r)。在日志输出中,并非总能轻松地找出根本原因,在问题引发一连串错误和警告时尤其如此。就像阅读本地系统显示的 traceback 一样,最好仔细审视开头的几个错误和末尾的几个错误,因为中间的错误通常表明内部包“抱怨”有地方出了问题,进而将有关错误的消息告知其他内部包。我们能够修复的通常是开头或末尾的错误。

    我们有时候能够找出错误,有时候则可能根本不知道输出是什么意思。然而,阅读日志输出绝对值得尝试,倘若根据日志输出成功地找出了错误,将获得极大的满足感。花在浏览日志输出上的时间越多,就越能找到对你来说最有意义的信息。

    E.3 随操作系统而异的故障排除技巧

    我们既可以在任何操作系统上进行开发,也可以将项目推送到任何远程系统上。用来推送项目的工具很先进,会在必要时修改项目,确保它能够在远程系统上正确地运行。然而,可能会出现一些与操作系统相关的问题。

    在部署到 Platform.sh 上的过程中,安装 Platform.sh CLI 是最有可能出现麻烦的步骤之一。安装命令如下:

    $ curl -fsS https://platform.sh/cli/installer | php

    这个命令的开头是 curl。curl 是一款让你能够在终端中通过指定 URL 来请求远程资源的工具,这里使用它来从 Platform.sh 服务器上下载 Platform.sh CLI 安装程序。在这个命令中,-fsS 是一组修改 curl 运行方式的标志:标志 f 让 curl 隐藏大部分错误消息,以便由 CLI 安装程序进行处理,而不是报告给用户;标志 s 让 curl 默默地运行,即由 CLI 安装程序决定在终端中显示哪些信息;标志 S 让 curl 在整个命令以失败告终时显示一条错误消息。命令末尾的 | php 让系统使用 PHP 解释器来运行下载的安装程序,因为 Platform.sh CLI 是使用 PHP 编写的。

    这意味着要安装 Platform.sh CLI,系统必须安装了 curl 和 PHP。要使用 Platform.sh CLI,还需要 Git 以及能够运行 Bash 命令的终端。Bash 是大多数服务器环境支持的语言。现代系统大多有足够的存储空间,能够安装多个类似 Platform.sh CLI 的工具。

    接下来的几小节介绍如何在不同的操作系统中满足这些需求。如果你还没有安装 Git,请按附录 D 中的 Git 安装说明进行安装,再根据你使用的操作系统跳到这里相应的小节。

    注意:在帮助你理解终端命令方面,explainshell 是一个出色的工具。在其网站中输入你想理解的命令,它就会显示与命令各个部分相关的文档。请使用安装 Platform.sh CLI 的命令来试一试。

    E.3.1 从 Windows 系统部署

    近年来,Windows 系统受程序员欢迎的程度有所回升。Windows 集成了其他操作系统的众多元素,在本地开发以及与远程系统交互的方式上,给用户提供了很大的选择空间。

    从 Windows 系统部署时,我们面临的最大困难之一是,Windows 操作系统使用的内核与基于 Linux 的远程服务器使用的内核不同。Windows 系统提供的工具集和语言不同于 Linux 系统,因此从 Windows 系统部署时,需要设法在本地环境中集成基于 Linux 的工具集。

    • 使用 WSL

    一种流行的方法是使用 WSL(Windows Subsystem for Linux),这种环境使开发人员可在 Windows 系统上直接运行 Linux。搭建 WSL 环境后,在 Windows 系统中使用 Platform.sh CLI 就像在 Linux 系统中使用它一样简单,因为 Platform.sh CLI 不知道自己运行在 Windows 系统中,它只能看到自己所处的 Linux 环境。

    搭建 WSL 环境的过程分为两步:先安装 WSL,再选择要在 WSL 环境中安装的 Linux 发行版。这里不详细介绍如何搭建 WSL 环境。如果你想使用 WSL 来部署项目,但还未搭建 WSL 环境,可参阅相关的文档 What is the Windows Subsystem for Linux?。搭建 WSL 环境后,可按本附录后面 E.3.3 节的说明来部署项目。

    • 使用 Git Bash

    要搭建用来部署项目的本地环境,另一种方法是使用 Git Bash,这个终端环境与 Bash 兼容,但运行在 Windows 系统中。在使用 Git 网站上提供的安装程序安装 Git 时,就同时安装了 Git Bash。使用 Git Bash 的方法确实可行,但不像使用 WSL 那样行云流水。在这种方法中,有些步骤是在 Windows 终端中完成的,而其他步骤是在 Git Bash 终端中完成的。

    首先,需要安装 PHP。可以使用 XAMPP,这个包捆绑了 PHP 及其他几个开发者工具。请访问 XAMPP 官方网站,并单击下载 XAMPP for Windows 的按钮。打开并运行安装程序,如果出现有关用户账户控制(User Account Control,UAC)限制方面的警告,就单击 OK 按钮,再接受所有的默认设置。

    运行安装程序后,需要将 PHP 添加到系统环境变量 Path 中,让 Windows 知道到哪里去查找 PHP。为此,在“开始”菜单的搜索框中输入 path 并单击“编辑系统环境变量”,再单击“环境变量”按钮。选中环境变量 Path,再单击“编辑”按钮。单击“新建”按钮,以便在当前的路径列表中添加一条新路径。如果你在运行 XAMPP 安装程序时接受了默认设置,请输入 C:\xampp\php,再单击“确定”按钮。然后,关闭所有还处于打开状态的系统对话框。

    安装 PHP 并将其路径添加到环境变量 Path 中后,就可安装 Platform.sh CLI 了。这需要以管理员身份打开一个 Windows 终端窗口,方法是在“开始”菜单的搜索框中输入 command,再单击“命令提示符应用”下方的“以管理员身份运行”。在打开的终端窗口中,执行如下命令:

    > curl -fsS https://platform.sh/cli/installer | php

    正如前面说过的,这将安装 Platform.sh CLI。

    最后,你将在 Git Bash 中工作。要打开 Git Bash 终端窗口,在“开始”菜单中搜索 git bash,再单击“Git Bash 应用”。这将打开一个终端窗口。在这个终端窗口中,除了执行基于 Windows 的命令(如 dir)以外,还可执行基于 Linux 的命令(如 ls)。为确认成功地安装了 Platform.sh CLI,执行命令 platform list,你将看到一个列表,其中包含 Platform.sh CLI 中的所有命令。从现在开始,就可以在 Git Bash 终端窗口中使用 Platform.sh CLI 来执行所有的部署工作了。

    E.3.2 从 macOS 系统部署

    macOS 操作系统并非基于 Linux 的,但 macOS 和 Linux 是基于类似的原则开发的。这意味着在 macOS 系统中使用的很多命令和工作流程也适用于远程服务器环境。为了确保本地 macOS 环境有所有必要的工具,可能需要安装一些开发者资源。因此,在工作过程中,如果系统询问是否要安装命令行开发者工具,请单击 Install 按钮。

    在安装 Platform.sh CLI 时,最容易遇到的麻烦是之前未安装 PHP。如果出现一条消息,指出找不到命令 php,就说明需要安装 PHP。安装 PHP 的最简单方法之一是使用包管理器 Homebrew,它可简化程序员依赖的各种包的安装工作。如果你还没有安装 Homebrew,可访问其官方网站,并按其中的说明进行安装。

    安装 Homebrew 后,使用下面的命令来安装 PHP:

    $ brew install php

    这将运行一段时间,完成后就能成功地安装 Platform.sh CLI 了。

    E.3.3 从 Linux 系统部署

    大多数服务器环境是基于 Linux 的,因此在 Linux 系统中安装并使用 Platform.sh CLI 时,几乎不会遇到什么麻烦。在新安装了 Ubuntu 的系统中安装 Platform.sh CLI,它会准确地指出需要安装哪些包:

    $ curl -fsS https://platform.sh/cli/installer | php
    Command 'curl' not found, but can be installed with:
    sudo apt install curl
    Command 'php' not found, but can be installed with:
    sudo apt install php-cli

    在实际的输出中,还包含有关其他包和版本的信息。下面的命令会安装 curl 和 PHP:

    $ sudo apt install curl php-cli

    执行这个命令后,应该就能成功地安装 Platform.sh CLI 了。由于本地环境与大部分基于 Linux 的托管环境很像,因此大部分终端使用技巧也适用于远程环境。

    E.4 其他部署方法

    如果对你来说部署到 Platform.sh 上不合适,或者你想尝试不同的方法,有很多托管平台可供选择。在托管到一些平台上时,部署过程与第 20 章描述的类似,但其他平台要求使用截然不同的方法来执行本附录开头描述的步骤。

    • 对于前面使用 CLI 执行的步骤,Platform.sh 允许使用浏览器来执行。如果相较于基于终端的工作流程,你更喜欢基于浏览器的界面,那么你可能更愿意使用这种方法。
    • 还有很多其他的托管提供商同时提供了基于 CLI 的方法和基于浏览器的方法。在这些提供商中,有些在浏览器中提供终端,让你无须在本地系统中安装任何软件。
    • 有些服务提供商允许你将项目推送到诸如 GitHub 等代码托管网站上,并将 GitHub 仓库关联到提供商。这样,提供商可从 GitHub 拉取你的代码,而不要求你直接将代码从本地系统推送给提供商。Platform.sh 也支持这种工作流程。
    • 有些提供商提供一系列服务,供你选择用来搭建项目所需的基础设施。这通常要求你对部署过程以及支持项目的远程服务器需要满足什么要求有更深入的认识。这样的托管平台包括 Amazon Web Services(AWS)和 Microsoft Azure。在使用这种平台时,很难确定总共需要支付多少费用,因为每项服务本身都可能产生费用。
    • 很多人将项目托管到虚拟专用服务器(Virtual Private Server,VPS)上。采用这种方法意味着租用一个像远程计算机一样的虚拟服务器,登录该服务器,安装运行项目所需的软件,将代码复制到虚拟服务器上,设置正确的连接,并让服务器开始接受请求。

    每隔一段时间,就会出现新的托管平台和托管方法。你可以选择一个看起来很有吸引力的平台,并花些时间了解相关的部署过程。在托管项目足够长的时间之后,就能充分了解当前提供方的方法有哪些优点和缺点了。没有托管平台是完美无缺的,你需要不断地做出判断:对于目前的具体情况而言,当前使用的提供方是否足够好。

    在选择部署平台和部署方法方面,最后还有一点需要提醒。有些人会不遗余力地诱导你采用过于复杂的部署方法和服务,它们旨在让项目高度可靠,并能够同时向数百万用户提供服务。很多程序员花费大量的时间、资金和精力打造出了复杂的部署策略,最终却发现几乎没有人使用其项目。对于大多数 Django 项目来说,便宜的托管套餐(small hosting plan)就能满足需求,它们经过调优每分钟能够处理数千个请求。如果项目的流量不会超过这个量级,只要花时间对部署进行配置,就能让它在小型平台上很好地运行,不需要投资购买为全球最大的网站准备的基础设施。

    在有些情况下,部署是一项极具挑战性的工作,但线上项目运行良好也会带来极大的满足感。将这种挑战视为享受,并在需要时去寻求帮助吧。