构建可伸缩的-PHP-Web-应用-全-

构建可伸缩的 PHP Web 应用(全)

原文:Building Scalable PHP Web Applications

协议:CC BY-NC-SA 4.0

一、介绍

“云”是新的技术术语。似乎每个人都在谈论“迁移到云”,但很少有人真正知道这意味着什么,更不知道如何利用它。随着每一次技术的转变,有些人认为新技术将解决他们所有的问题,甚至没有盘点那些问题是什么。许多技术都做出了承诺,其中一些甚至是真的,但是要实现它们,你必须以正确的方式使用技术。

这本书主要是为那些想开始将他们的应用迁移到云上,并想知道如何开始和他们可用的不同选项的开发人员准备的。其次,这本书是为那些想要精通云技术和想法的管理者准备的,以便更好地理解可用的选项以及不同的开发决策将如何影响它们。这本书的重点是 PHP,但是,即使 PHP 不是您打算使用的部署语言,这本书也会告诉您在任何语言或平台中构建云应用需要知道什么。

这本书将讨论几种不同的云选项,但将侧重于开发 Linode 和类似的云,原因我们将在第二章中看到。

请注意,这本书在特定的基础设施供应商之间做了许多比较。我不代表任何特定的公司,也不保证这些比较是万无一失的或永久的。然而,它们是我在写作时的经验和知识的结果,我已经尽力提供尽可能多的事实信息。尽管如此,尽管本书中讨论的一般考虑因素不太可能改变,但具体的供应商、产品以及它们如何符合标准可能会随着时间的推移而改变。

此外,虽然设置、编程和配置任务的一般大纲可能会在很长一段时间内保持不变,但随着产品和平台的变化,具体步骤和屏幕截图可能会与本书不同。尽管如此,这里给出的步骤应该仍然是一个可靠的指南,告诉你应该从不同的云供应商那里得到什么类型的东西。

1.1 先决条件

这本书的先决条件很少。即使你不熟悉我们正在使用的具体工具,这本书有足够的一步一步的指示,你应该能够相当容易地跟随它。

因为这是一本关于 web 应用的书,所以它假设您熟悉 HTML、CSS 的绝对基础知识,以及互联网如何工作的基础知识(即域名、IP 地址等)。).如果你不熟悉这些东西,你应该把这本书放下,拿起一本我之前的书,新程序员从这里开始新程序员从这里开始并没有触及你需要知道的每一个话题,但是如果你理解了它的概念,你应该能够理解本书中的大多数例子。

本书中的代码是基于 PHP 的,但是代码足够简单,无论你熟悉什么语言,你都应该能够理解。之所以选择 PHP,是因为用 PHP 很容易编写简短易懂的 web 应用。例如,在我的工作中,我几乎总是用 Ruby on Rails 编程。然而,如果不熟悉整个 Ruby on Rails 系统,理解 Rails 应用几乎是不可能的。另一方面,PHP 没有 Rails 那么“神奇”,但这让读者更清楚发生了什么。对于 PHP,特别是在本书使用它的水平上,我相信任何有编程语言经验的人都能够理解正在发生的事情。

本书还假设您对数据库和 SQL 有基本的了解。这不是一个绝对的要求,但是数据库代码本身很大程度上没有得到解释。然而,SQL 代码对任何人来说应该是不言而喻的,即使是对 SQL 稍有了解的人。

最后,这本书使用 Linux 作为操作系统的选择。然而,即使你对 Linux 一无所知,这本书也给了你需要输入的每一个命令,所以你实际上不需要了解 Linux 就可以使用这本书。附录 A 有一个常用 Linux 命令的列表,如果你想了解更多,可以参考一下。

选择 Linux 的原因是因为它是一个非常容易在云环境中安装和使用的操作系统。虽然在云环境中使用 Windows 是可能的,但是 Linux 是为这种类型的应用从头开始构建的。命令行虽然看起来晦涩难懂,但实际上使服务器管理变得快速而轻松。

此外,因为 Linux、PHP 和 PostgreSQL(我们的数据库系统)都是自由软件,所以使用它们不需要考虑许可。你只需要安装你需要的东西,然后继续你的生活。你不需要担心你什么时候需要付钱给谁。您不需要担心您的使用是否与您的产品许可相匹配。你不需要担心有人审计你的业务来验证合规性。有了自由软件,软件只是一个工具——你可以安装它,然后忘记它。

原则上,我对专有软件没有问题,但是,因为它引入了大量额外的管理问题,如果可能的话,我会尽量避免。我并不反对人们为他们的工作获得报酬,但我确实对永无止境的、不必要的麻烦持原则性立场,当你过度依赖许可质量很差的专有软件时,你最终会遇到这种麻烦。

本书重点介绍 CentOS 7 Linux 发行版,因为 CentOS 往往是一个相当稳定和健壮的 Linux 发行版,受到大多数供应商的支持,许多其他 Linux 发行版都使用 CentOS 作为起点。

1.2 印刷惯例

这本书使用了一些你应该知道的简单的印刷惯例。当提到代码、键入的命令、URL 或任何其他用于键入的文本时,这本书使用的字体如下:type me here。与命令名相同的程序名(你可以输入使用)也是这样写的,用户名和文件名也是这样写的。

但是,您输入的许多内容需要用您自己的值替换,比如您部署的机器的 IP 地址。这些需要替换的东西都是全大写字母的单词给的。例如,要访问你自己的网站,你可以在浏览器中输入 http://YOUR.DOMAIN.HERE/,其中YOUR.DOMAIN.HERE是指你自己网站的域名。文本中描述了这些的含义以及它们应该被替换为什么。

请注意,PHP 实际上有几个默认的变量名,我们将使用的都是大写的。所以$_GET$_POST$_FILES都是 PHP 中的实变量名,不应该被替换。

这本书描述了如何使用各种第三方服务。当描述各种网站上的菜单、按钮和用户界面元素的字段名称时,这些项目通常会放在双引号中。例如,要从大多数程序中打印出一个文件,你可以进入“文件”,然后点击“打印”

1.3 键入或下载代码

这本书围绕一个简单的留言簿应用,使用不同的应用架构构建和重建它。应用的全部代码都在这本书里。老实说,我认为自己把这本书里的所有代码打出来会对你有很大好处。没有太多,它会帮助你思考你在构建什么。然而,我知道当你只是试图处理这些例子时,这可能会变得很乏味,并且当你试图解决问题时,拥有完整的、可工作的代码是非常有帮助的。

因此,我们在本书中介绍的应用的所有变体的代码都可以从 www.github.com 下载。存储库中的每个分支都代表了对书中应用的不同修改。使用存储库,您需要做的唯一更改是本书正文中提到的特定于环境的更改,比如设置数据库服务器的 IP 地址。这些也在每个分支的README文件中。

您可以从以下网址下载代码

https://github.com/johnnyb/cloud-example-application

如果您自己键入代码,请密切注意空格在程序中的使用位置。代码完全按照应该键入的样子编写,除非文本在其他地方指明,否则在应该的地方换行。将空格和换行符放在错误的地方,或者将它们留在它们应该在的地方,肯定会弄乱代码。

二、什么是云

对于什么是“在云中”有一点混淆。一些人甚至错误地认为仅仅是在互联网上就是在利用云技术。

云技术和其他类型的互联网托管的本质区别在于,云服务至少提供了快速扩展应用的能力。过去,开发人员会将他们的 web 应用部署到他们在特定场所购买或租赁的固定服务器上。获得更多的设备是可能的,但这总是要花费相当多的时间和精力。通常,开发人员必须为服务器投入大量资金,购买、配置和部署服务器。这个过程可能需要几周甚至几个月。即使直接从托管公司购买,汇总报价和启动运行的过程也可能需要一周以上。

云的承诺是,不必经历新服务器的物理设置过程,可以即时或至少在几分钟或几小时内获得额外的容量,而不是几天、几周或几个月。对于某些解决方案,您甚至不必担心机器—云解决方案会根据您的需要自动将您的应用扩展到任意多的机器上。对于其他人来说,只需点击几下鼠标就可以请求、映像和启动一台新机器,这台机器是某台现有机器的副本,然后将它添加到您的 web 应用中。

无论如何,云的核心思想是即时、自动化的可伸缩性和灵活性。

许多云提供商甚至提供对多个数据中心的访问。是要一套美国的服务器,一套欧洲的服务器?没问题。只需点击几下,就能搞定。

2.1 基础设施即服务

云计算经常令人困惑,因为云计算有几种不同的类型,每种类型都有自己的优点和缺点。云服务类型之间的差异主要基于所提供的抽象级别。

本书将重点介绍的最基本类型的云服务被称为基础设施即服务,缩写为 IaaS。IaaS 意味着您可以购买和部署基础设施(服务器、负载平衡器、防火墙等)。)只需点击一下按钮。这是通过服务器虚拟化技术实现的。

服务器虚拟化允许 IaaS 服务公司部署单个非常大的服务器,并将其拆分为多个较小的服务器。每台服务器运行一个虚拟机管理程序程序,该程序允许公司快速自动地拆分服务器。一家公司可能会将一台拥有 16 个处理器和 64g RAM 的计算机分成 4 个虚拟机,每个虚拟机拥有 4 个处理器和 16g RAM。

这样做有三个好处。首先是空间。通过购买最大的机器,IaaS 公司为自己提供了每机架单元最大的计算能力,这在服务器机房中是一种有价值的商品。因此,通过购买一台大型机器并将其分成四台较小的机器,他们只使用了原本可以使用的空间的四分之一。

第二是每个 CPU 内核的成本。通过将如此多的 CPU 核心和如此多的内存打包到一台服务器中,它们的每 CPU 核心成本和每千兆字节内存成本都会下降。因此,通过购买更大的计算机,他们可以为购买较小的虚拟服务器的用户提供更低的每 CPU 核心成本。

然而,第三个优势也是最重要的—可管理性。为了支持虚拟化,每台机器的一小部分专用于管理程序,即管理服务器上其他虚拟机的小操作系统。因为这些机器是虚拟机,而不是真正的硬件设备,所以非常容易管理。操作员可以向管理程序发送命令,管理程序可以立即设置新的虚拟机,克隆新的引导磁盘,并在几分钟内启动新的虚拟服务器。

从历史上看,如果我想要一台新的服务器,我必须购买服务器,然后在控制台前安装相关的操作系统和系统软件,最后将服务器搬到机架上,插上电源并打开。我必须确保网络接通并配置好。我可能需要配置 BIOS 来允许键盘断开连接的操作。如果机器出了问题,我要做机器的备份,找一个新的物理机,把备份复制到新机器上,装上新机器,和旧机器物理换出来。

有了虚拟机,我可以让虚拟机管理程序处理所有这些事情。我所需要的只是运行虚拟机管理程序的足够多的额外服务器,这样当我需要一台新机器时,我就可以告诉虚拟机管理程序为我创建一台新的虚拟机,以及我想要使用的磁盘副本的位置。

更好的是,对于大多数 IaaS 平台,您甚至不需要担心虚拟机管理程序和容量。IaaS 供应商会为您完成所有这些工作。IaaS 供应商提供了一个点击式界面,允许您只需登录到 web 管理控制台就可以启动虚拟服务器。你告诉它你想要多大的机器(即 CPU 核心的数量和内存的大小),它就会为你分配一台服务器。你告诉它你想在上面安装什么(要么是一个基本操作系统,要么是一个现有机器的克隆),它就会把它复制到引导盘上,然后帮你启动。瞧啊。您已经有了一台新的服务器。

此外,对于大多数 IaaS 服务,如果存在物理硬件问题,该服务会为您解决。如果检测到严重的硬件问题,他们会简单地关闭您的服务器,将其迁移到新的服务器,重新启动,并向您发送电子邮件,让您知道发生了什么。如果问题不太严重,一些供应商会通知您,要求您在最方便的时候按下按钮执行迁移。

更好的是定价模式。大多数 IaaS 供应商都可以选择小时定价。你需要一台机器,但只是几个小时?有了 Linode,你可以以每小时不到 1.00 美元的价格获得一台 32 核 64g 的机器!

在第三章中,我们将看看建立云服务器所需的具体步骤。

2.2 平台即服务

扩展应用的另一个选项称为平台即服务,缩写为 PaaS。借助 PaaS,PaaS 定义了一个让你运行应用的平台,而不是给你裸机,让你随心所欲地运行。PaaS 供应商管理整个平台,您只需担心您的应用。

例如,Heroku 是一个流行的 PaaS 供应商。Heroku 处理所有的服务器管理和维护。你甚至不能登录他们的机器!您只需将代码推给他们,他们就会为您将代码部署到他们的机器上。其他常见的 PaaS 供应商包括 Google App Engine、Windows Azure、亚马逊的 Elastic Beanstalk 和 OpenShift。

使用 PaaS,您可以选择使用多少“工作人员”,系统将根据需要在多少台机器上分配工作(工作人员只是一个活动的流程,每个 PaaS 供应商都有自己的术语)。因此,PaaS 供应商负责平台(硬件、操作系统、安装的应用),您只需管理应用代码。

这在理论上听起来很棒——您再也不用管理服务器了!这一切都是在幕后为你自动完成的。然而,现实是,PaaS 平台并不像它们看起来那样透明,PaaS 供应商往往将它们的附加值定得相当高。

2.3 码头工人

Docker 是该领域一项新技术,引起了很多关注。Docker 应用介于 IaaS 和 PaaS 之间。Docker 应用是一个映像,本质上包含了运行应用所需的所有程序的完整安装。

它们可以像 PaaS 一样进行管理、部署和扩展,在 PaaS 中,您只需告诉服务您想要运行多少个,它就会负责将您的映像部署到正确的位置,但您拥有更大程度的控制,类似于 IaaS。

Docker 的一个有趣的部分是将不同的服务“链接”在一起的能力。本质上,您将 Docker 容器提供或需要的不同类型的服务命名,然后可以使用这些名称告诉您的服务如何找到彼此。然而,这通常只是在应用的初始设置阶段出现的问题,在此之后,您是手动还是使用高级工具将应用组件链接在一起就无关紧要了。

Docker 本身是技术,不是厂商。有许多供应商允许您部署 Docker 应用,包括负责 Docker 技术的 Docker Inc .

2.4 为什么选择 IaaS

出于多种原因,本书集中讨论 IaaS 云模型。首先是 IaaS 非常灵活。无论您想要运行什么类型的工作负载,无论您想要运行什么平台,IaaS 都会为您提供裸服务器。你如何对待他们取决于你自己。这也意味着您不会被特定供应商的系统所束缚。

第二,IaaS 的工作相当有预见性。虽然存在差异,但大多数 IaaS 供应商的运营方式是相当相似的。你选择一个盒子大小,说你想要什么,然后按下按钮。这些服务的质量、成本和灵活性有很大的差异,但它们都非常相似。

有了 PaaS,您的选择就更加有限了。首先,您受限于您的供应商提供的平台。如果你用 Ruby on Rails 编程,你只能使用 Ruby on Rails PaaS。虽然大多数 PaaS 供应商已经向相当多的不同应用服务器开放了他们的系统,但是仍然存在一些限制。第二,您必须编写与他们配置平台的方式相匹配的代码。这通常涉及很少或没有本地文件存储、要连接的某些特定类型的数据库、仅某些允许的平台选项或扩展,以及在如何配置服务器方面很少或没有灵活性。有时这很好,但有时这太严格了。

此外,PaaS 通常太不透明。访问服务器日志通常很困难,调试特定于服务器的问题几乎是不可能的。有时 PaaS 供应商有工具可以提供帮助,但是它们无法直接在有问题的机器上进行调试。这种不透明有时会导致毁灭性的结果。例如,如果您购买了一个 PaaS 数据库系统,但不知何故您的数据库被破坏了,那么您的选择就非常有限。你基本上要打电话给公司,乞求帮助。有些公司在这方面反应很积极,但这让我很紧张。

还要记住,为了让 PaaS 系统正常工作,他们必须不断升级他们的系统。事实上,这就是你付钱让他们做的事情。然而,不能保证他们明天进行的升级不会意外地破坏您的应用。也许升级是必要的,但这不是你能做的决定。

类似地,使用 PaaS 供应商意味着您必须按照他们的时间表升级。如果他们决定放弃他们的基础设施,您必须重写代码来解决这个问题。如果他们认为你使用的软件版本过时了,你必须重写你的代码来使用新版本。简而言之,PaaS 意味着你失去了对你的技术的控制,而 IaaS 意味着你拥有完全的控制权。虽然使用 PaaS 可以消除某些麻烦,但它们通常会被引入的麻烦所弥补。

最后,PaaS 通常非常昂贵。例如,对于 Heroku(一个常见的 Ruby on Rails PaaS 供应商)上的四个 CPU 内核,每月花费 100 美元。对于 Linode 上的 4 核机器,成本仅为每月 40 美元,并且 Linode 机器更快。我发现,对于同等性能,大多数 PaaS 供应商的收费是优秀 IaaS 供应商的三倍左右。PaaS 供应商为您做了更多,但是只有当您的开发人员队伍中没有任何系统管理经验时,这才是值得的,而且无论如何,您都要为跟上 PaaS 平台的步伐而付出代价。

如果您的组织知道如何设置和维护服务器(这本书为学习如何做提供了一个良好的开端),IaaS 是目前利用云技术最简单、最灵活、最快、最便宜的方式,并且它不会将您局限于特定的供应商。如果您决定真的想要使用类似 PaaS 的系统,只需知道有许多开源的 PaaS 系统可以在您的 IaaS 服务器上运行。

2.5 选择 IaaS 供应商

选择 IaaS 服务或供应商时有许多考虑因素。最重要的考虑是服务的可靠性。如果服务中断,即使有一个优秀的 web 应用也没有用。如果你不能得到你的数据,从长远来看,它不会为你的公司赚钱。因此,服务的可靠性应该会对您的决策产生重大影响。

这也延伸到他们解决票证的能力。每个服务都会在某个时候出现问题。如果一家公司不能及时响应或解决问题,那么您就不应该让生产系统占用它们。

下一个主要因素是性价比。许多早期的云基础设施公司非常重视云计算的灵活性,结果几乎每个云解决方案的成本都高得惊人。亚马逊网络服务(AWS)的云计算服务 EC2 就是一个很好的例子。如前所述,一台 8GB 内存的 Linode 4 核机每月 40 美元。EC2 上类似指定的机器(c5.xlarge机器)是 122 美元/月。从历史上看,即使在相同的规格下,EC2 也往往比 Linode 慢得多。在云计算的早期,EC2 是该领域仅有的大玩家之一,为了获得灵活性,他们让你付出了高昂的代价。就个人而言,按照 EC2 的要价,我更愿意走出云,做一个传统的托管设置,在那里租赁物理服务器。对我来说,这种灵活性不值 AWS 要求的价格。

另一方面,有些云服务的价格高得不可思议。也就是说,你可以从价格上认识到,他们不可能以这样的价格提供可靠的长期服务。要么公司倒闭,要么公司不得不提高价格,要么服务最终会被超额预订,降级到不可用的地步。一个很好的例子就是 CloudAtCost ( www.cloudatcost.com )。有了 CloudAtCost,你只需支付一次性费用,就可以永远保留服务器。例如,花 70 美元,你可以得到 2 个 CPU 内核和 1GB 内存。但是那是每月的费用。那是一笔一次性的费用!正如我所说,这是一个令人难以置信的好价格。他们收取每年 9 美元的账户维护费,但不管你有多少台服务器,这个费用都是一样的(这可能是为了让他们可以关闭不再维护的服务器)。

*这种服务可能对一些事情有好处。例如,如果您想要一个开发服务器,即使网络偶尔出现故障,或者技术人员不能快速响应,都没有关系。而且,如果有一天他们关了门,你也不会出局太多。但我肯定不会把我的生意押在这样的服务上。如果你想看看其他价格极其便宜的服务,请查看 www.lowendbox.com

最后一个因素是灵活性。能够点击设置一台新机器是一回事,但是如果您不能存储磁盘映像,并且每次启动一台机器时都必须重建一台机器,那该怎么办?这将是一个艰苦的过程,你将失去云计算的一个主要优势。这是 AWS 大放异彩的一个领域。AWS 不仅包括其云计算服务(EC2),还为云基础设施的几乎每个方面提供配置、控制和自动化。AWS 提供可扩展存储解决方案、视频转码服务、可扩展数据库、消息排队服务和搜索服务。这为您的集群提供了一个外部监控服务,当负载增加时,它会自动生成新的服务器来处理负载,并在网络负载减少时,从您的集群中删除服务器并关闭它们。换句话说,AWS 附带的额外服务几乎是无限的。

然而,到最后,AWS 的灵活性被 EC2 糟糕的性价比压倒了。然而,令人欣慰的是,AWS 更好的服务(如 S3 和 CloudFront)可以单独使用,即使你使用另一个提供商作为你的主要 IaaS 供应商。我们将在后面的章节中讨论如何做到这一点。

如果你还没有猜到,我对云供应商的偏好是 Linode ( www.linode.com )。他们的性价比是无与伦比的。原因有三。第一,设备较新。其次,他们只使用固态硬盘(即固态硬盘,没有旋转磁盘),通常比普通磁盘快一个数量级。这可能是 Linode 表现最重要的方面之一。第三,他们实施了控制措施来防止“吵闹的邻居”。

高噪音邻居是与您的机器在同一物理服务器上的虚拟机,但是它使用了计算机的所有 I/O 资源。在 IaaS 平台上,你不能选择(甚至不知道)谁在共享同一个物理硬件。因此,使用防止资源占用的服务非常重要。Linode 实施了许多控制措施来防止任何单个虚拟机过度使用资源。这不仅对你自己的虚拟服务器的性能有好处,而且也意味着你不必担心成为别人的坏邻居!

虽然 Linode 没有 AWS 的灵活性,但这种灵活性也是不容错过的。你想用 AWS 做的大部分重要的事情用 Linode 来做都是极其简单的,AWS 增加的特性让 AWS 的学习曲线变得陡峭而混乱。当有人花费额外的钱来获得 AWS 的灵活性时(例如,创建一个系统来自动引导新机器以响应负载),他们可能已经在 Linode 上花费了相同的钱来为他们的集群提供足够的容量,这样它就不会有问题了。

还有其他类似于 Linode 的服务——digital ocean 通常被认为是价格相当的类似服务。然而,我和 Linode 的经历非常积极,我觉得没有必要对它们都进行研究。Linode 提供了我需要的东西,有一个易于使用的界面,而且价格非常便宜。因此,本书将重点介绍在 Linode 上开发云应用。

选择供应商时,仔细阅读他们的服务条款也很重要,以确保您的预期用途是兼容的。不仅某些服务不允许某些工作负载(例如,某些服务禁止群发电子邮件营销),许多服务还限制了允许您扩展网络的速度。这主要是为了防止滥用,但提前发现也很重要。在任何情况下,您都可能希望接触潜在的云供应商,并确保您的计划使用符合他们的服务指南,然后再选择一家。

2.6 一些重要术语

在我们进一步讨论之前,我想澄清一下本书中使用的一些术语。借助 IaaS,您租用的基本上都是机器。它们是虚拟机器(即真实机器的分区),但尽管如此,它们本身也可以被认为是抽象的机器。在云计算行话中,它们通常被称为节点。由于它们向网络上的其他机器和/或用户提供服务,它们也被认为是服务器。因此,节点的另一个常用术语是 VPS,即虚拟专用服务器。在本书中,机器、节点和服务器这三个术语可以互换使用,尽管具体术语的选择通常是基于您目前的使用方式。

一个集群是一组协同工作的节点。因此,我们正在云中构建一个集群。在云中可扩展的服务被称为云集群,或者简称为

术语可扩展性是云计算中的一个重要术语。可伸缩性不仅仅是性能。性能是指效率或原始速度。可扩展性是指系统快速扩展其处理能力的能力。例如,一个每秒可以处理 2,000 个请求的程序可能被认为是高性能的,但是如果该程序的速度不能通过添加另一个节点来提高,那么它就是不可伸缩的。

另一方面,即使是低性能的程序也可能是可伸缩的。如果我的程序每秒只处理 1 个请求,但我添加的每个节点都会增加它可以处理的请求数,那么我的程序就是可伸缩的。如果我将这个低性能的程序扩展到 4000 个节点,它将能够处理比以前每秒最多处理 2000 个请求更多的请求。根据整个集群的吞吐量,整个集群(包含 4,000 个节点)现在可能被认为是高性能的,即使其各个部分并不是高性能的。

通常,可伸缩性取决于您并行化任务的能力。并行化是指任务彼此独立运行的能力,即使在其他机器上也是如此。这两个任务越需要相互协调,它们的并行性就越差。在为可伸缩性设计应用时,您需要特别注意系统的不同部分需要协调的方式,并致力于最小化或消除这些协调点的影响。

希望您构建的东西既有高性能(即使对于少量节点)又有可伸缩性(因此添加更多节点可以提高性能)。让一个应用运行良好是关于寻找慢代码并用快代码替换它,或者重写你的应用以不需要慢代码。使应用可扩展是关于寻找阻止并行化的瓶颈,并用可扩展到多个节点的代码替换这些瓶颈,或者重新设计您的架构,以便瓶颈一开始就不会出现。

*本书的重点是可伸缩性,尽管性能也在考虑之列。

img/487291_1_En_2_Figa_HTML.jpg跳过服务器机房

这里有一个有趣的故事,是我在处理服务器机房时不会错过的事情。在 20 世纪 90 年代末,我曾经在一家托管自己服务器的公司担任程序员/系统管理员。这些服务器不在常规的服务器机架上,而是放在金属线架子上。主要的面向外部的服务器在顶层架子上。

我们需要一些额外的驱动器空间,所以我在服务器上安装了一个外部驱动器包。当时,将外部存储连接到服务器的主要模式被称为 SCSI(读作“scuzzy”),它充满了问题。过了一段时间,服务器访问驱动器包开始出现很多问题,所以我去排除故障。

因为服务器在架子的顶层,所以我站在凳子上操作机器。我花了大约半个小时试图找出问题所在。意识到这发生在我们的主要外部服务器上,所以每一分钟都很重要,因为我们(非常活跃的)网站有问题。

因此,我设法把所有东西从新的驱动器包中移走,然后断开驱动器,把它们带回我的办公室,看看问题出在哪里。然而,我花了太多的时间站在凳子上摆弄电脑,我忘记了,你知道,我是站在凳子上。所以,我抓起驱动器包,走出了房间,或者说,我打算这样做。谢天谢地,我和车手们都没有受伤,但我的自尊心却受到了伤害。

尽管回忆这些故事很有趣,但我很高兴那些日子已经过去了。**

三、设置云服务器

与大多数云提供商一样,在 Linode 上设置云服务器极其简单。本章通过截图向您展示了启动和运行的基本过程。

3.1 创建虚拟服务器

如果你是一名网站开发人员或管理者,希望你可以不太麻烦地注册 Linode 服务。它需要预先准备一张信用卡,但是你会发现运行这本书里所有东西的费用可能比这本书本身要少,假设当你用完它们的时候你关闭了你的服务。

在继续之前,请立即在 Linode 创建您的帐户。

在您注册并登录之后,Linode 会将您带到您的仪表板,它看起来应该类似于图 3-1 。

img/487291_1_En_3_Fig1_HTML.jpg

图 3-1

Linux 仪表板

您目前没有进行任何设置,因此您的仪表板相当空。要开始,请单击“创建”按钮。Linode 将他们的虚拟服务器称为“节点”或“Linodes”,因此选择“Linode”来创建一台新机器。

然后,Linode 会问您几个问题,以帮助您配置要使用的节点。虽然有许多好的选择,但为了能够跟随这本书,请使用这里的一些:

  1. 在“选择发行版”下,选择“CentOS 7”

  2. 在“区域”下,您选择什么并不重要,但是您每次都必须选择相同的区域,以便您的服务器能够相互通信。这本书将使用“德克萨斯州达拉斯”的设施。

  3. 在“Linode 计划”中,最便宜的是“Nanode 1GB”计划,它足以满足我们的目的。在撰写本文时,这项计划每小时的运行成本不到 1 美分。

  4. 在“Linode Label”下,我们将称这台机器为template_node

  5. 您可以忽略“添加标签”部分。当您有很多机器时,标签对于将它们分组在一起很有用。

  6. 在“Root Password”下,为该机器添加一个密码。请确保密码是安全的,因为有很多黑客只是到处尝试不同的 root 帐户密码。每月有 50,000 次这样的黑客攻击并不罕见。

  7. 目前,你可以保持“可选附件”不变。我们将在本书的后面处理备份和私有 IP 地址。

完成所有这些设置后,点击“创建”按钮,Linode 将开始构建您的机器。Linode 会将你带到你的机器的仪表板上,除了别的以外,它还会有一个进度条和一个“活动提要”(见图 3-2 )。当进度条完成时,您现在是云中一个新服务器的骄傲的所有者了!

img/487291_1_En_3_Fig2_HTML.jpg

图 3-2

您的新节点的仪表板

img/487291_1_En_3_Figa_HTML.jpg选择服务器

Linode 根据(a)服务器将服务的工作负载类型和(b)服务器自带的内存大小对服务器进行分类。工作负载类型有“Nanode”(超小型和超廉价的实例)、“Standard”(平衡的 CPU/RAM)、“Dedicated CPU”和“High Memory”(也有“GPU”,但这些是针对与这里考虑的完全不同类型的云计算)。

在每种工作负载下,服务器都是根据它们附带的内存量来命名的。你猜对了,一个“Linode 4GB”配有 4GB 内存。它还将指定有多少个虚拟 CPU 和多少磁盘空间。通常,内存量的增长速度比内核数量的增长速度快,考虑到内存通常是比 CPU 能力更强的限制因素,这是有道理的。它们各自也有不同数量的磁盘空间,但是我认为磁盘空间是次要的考虑因素,因为第八章将展示如何建立一个具有无限可用磁盘空间的服务。

在选择生产服务器时,出于今后将变得更加清楚的原因,我通常为数据库选择相当大的规模(因为它们更难复制),为 web 服务器选择低到中等的规模(因为我可以通过添加更多的服务器来轻松增加容量)。

3.2 登录并四处查看

所以,你有一台机器,但是它在哪里,你如何访问它?

单击仪表板上的“网络”选项卡。它看起来应该类似于图 3-3 。在“访问”下,有一个标题为“SSH 访问”的区域这里有您需要在命令行中键入的命令,以便登录。使用您为节点创建的密码登录。

img/487291_1_En_3_Fig3_HTML.jpg

图 3-3

网络选项卡

img/487291_1_En_3_Figb_HTML.jpg命令行?那是什么?

命令行是访问计算机的老派方式。在漂亮的图形界面出现之前很久,人们通过打字与电脑互动。对于许多事情来说,尤其是对于与系统管理相关的任务来说,命令行仍然是管理系统的最佳方式。

如果您对命令行没有任何经验,请不要担心!这本书并不假设你有这方面的专业知识,并将引导你完成每一步。如果你只是想弄清楚如何进入命令行,这里是你在每个主要操作系统中要做的事情:

  • Windows 10 : Windows 实际上有两个命令行系统。旧的“命令提示符”(cmd.exe)和新的 PowerShell。只需点击 Windows 图标并输入PowerShell即可开始。您将至少需要 2018 年 4 月的更新,以便在没有进一步设置的情况下运行此处的命令。

  • MacOS X :每台 Mac 都附带一个名为“终端”的应用。您可以使用 Spotlight search 找到它,或者您可以前往“应用”,然后前往“实用工具”,在那里找到它。我建议你把它添加到你的 Dock 中,因为你可能会经常用到它。

  • Linux :每个 Linux 发行版都安装了一个命令行程序,通常被命名为“终端”或“Bash 提示符”或类似的东西。

当您第一次启动命令行时,它会向您显示一些文本,后面跟着一个闪烁的光标。现在您可以开始输入命令了!

如果您是 Linux 新手,ssh是一个非常方便的工具,它允许远程、安全地连接到您的服务器命令行。也就是说,您可以ssh进入您的机器,它就像您登录到控制台一样工作。此外,连接是加密的,所以你不必担心有人窃听你或窃取你的密码。ssh默认安装在每个主要的操作系统上,所以你应该已经安装了。如果你用的是旧版本的 Windows,可能要下载一个单独的 ssh 应用,比如 PuTTY,可以从 www.putty.org 免费下载。

要登录到您的机器,只需打开一个命令行,键入“SSH 访问”部分下面列出的命令。它应该显示类似于ssh root@MY.IP.ADRESS.HERE的内容,其中MY.IP.ADDRESS.HERE是您的 Linode 的 IP 地址。因为ssh之前没有见过这台电脑,所以很可能会警告你主机的真实性无法建立,并询问你是否要继续连接。就回答yes。它只会在第一次询问您,因为ssh会记住远程计算机。然后,输入您在设置机器时设置的密码作为密码。图 3-4 显示了这可能的样子。

img/487291_1_En_3_Fig4_HTML.png

图 3-4

从命令行登录

您现在已登录到您的机器!

最后一行被称为“命令提示符”它提供了会话当前状态的基本信息。root是您的用户名。这是 Linux 上管理用户的名称。li1125-199(或@符号后的任何符号)是您机器的名称。最后,~告诉你你在什么目录下(对于新手程序员来说,“目录”是“文件夹”的旧说法)。~表示用户的“主”目录。

如果您不熟悉 Linux,了解以下几个命令会有所帮助:

  • pwd:表示“打印工作目录”这将告诉您当前正在哪个目录下工作。如果你第一次登录时这样做,它应该会显示/root。Linux 目录不是以驱动器号开始,而是以斜杠(/)开始作为顶级目录。/root是根用户的主目录。

  • mkdir:这代表“制作目录”这将在当前目录中创建一个新目录。

  • cd:这代表“改变目录”如果你给它一个目录名,它将转到那个目录。如果您键入不带任何参数的命令,它将带您到您的主目录。命令cd /将带您到根目录(注意根目录是顶级目录的术语,不是根用户的目录)。如果一个目录名以斜杠开头,cd命令会认为它是一个绝对路径,从根目录开始。如果目录名以波浪符号(~)开头,cd命令将解释相对于您的主目录的路径。否则,它会将路径解释为相对于当前目录。

  • 这代表“列表”,给你一个当前目录下的文件列表。要查看文件权限,在命令中添加选项-l。要查看隐藏文件,添加选项-a。因此,要查看隐藏文件和文件权限,输入ls -l -a

  • Nano 是你易于使用的文本编辑器。如果你将运行机器作为你工作的一部分,你也应该学习vim,因为这对你来说会更有效率,但那是一项更困难的任务。Nano 易于使用,对于入门来说已经足够了。如果你想在当前目录下创建一个名为test.txt的文件,输入nano test.txt并开始输入。组合键 control-o 将保存(即输出)您的文件,control-x 将退出。

  • systemctl:这个命令处理在某些 Linux 发行版上启动和停止系统服务,包括 CentOS。本章稍后将向您介绍如何使用它。

  • logout:退出当前用户会话。您也可以通过键入exit或 control-d 来完成此操作。

我鼓励你花些时间来研究一下命令mkdircdlsnano。尝试创建一个新目录,进入该目录,并在那里创建一个新文件。然后,尝试注销,用ssh重新登录,找到你的文件,并查看它。这样做几次,直到您完全熟悉登录、注销、导航目录和编辑文件的过程。

在您熟悉了在主目录中创建文件和目录之后,您应该扩展到其他目录。您还不应该在那里编辑文件(您的主目录之外的文件对操作系统来说可能意味着一些重要的东西),但是四处看看没有坏处。

要开始四处查看,请转到根目录(cd /)并四处查看(ls)。你会看到许多目录,其中大部分都记录在 Linux 文件系统层次标准中(参见 www.pathname.com/fhs)。尽管文件系统层次结构标准是关于目录用途的一般信息的好地方,但它不再被严格遵守,所以不要对一些偏差感到惊讶。然而,简而言之,/etc包含服务器配置信息,/home包含除 root 之外的用户的主目录,/usr包含已安装的程序,/opt包含定制的程序和其他特定于服务器的项目,/var包含定期变化的信息(例如,日志文件、缓存、队列等)。).)

3.3 更新您的系统

系统启动并运行后,您应该做的第一件事是用最新的升级和安全软件包更新服务器。CentOS 使用yum来管理系统软件安装。yum简单安全地下载、安装、升级、删除和验证软件包。当你要求yum安装或更新一个包时,它足够聪明,可以在远程服务器上找到这个包,验证这个包的真实性,查找并安装这个包所依赖的任何其他软件,并跟踪所有已安装的包及其文件。yum只能由根用户运行,但到目前为止,这是我们唯一可用的用户。

当你有一个新的服务器时,你应该做的第一件事就是把它所有的安装包更新到最新的版本。幸运的是,使用yum这真的很容易。只需运行以下命令:

yum -y update

这通常会下载一个非常大的数量的包——这很好。CentOS 不断修复发行版中每一个软件的错误和解决安全问题,因此这些更新会变得很大。然而,CentOS 也非常小心地确保它包含的修订版不包含任何不兼容的升级。所以,通过运行yum update,你可以让自己跟上时代,并且你不太可能因为运行它而意外地打破任何东西。

3.4 运行 Web 服务器

默认情况下,Linode 附带的 Linux 发行版只安装绝对必要的软件。这实际上很棒,因为维护安全性的主要方法之一是只安装您绝对需要的东西,这将您面临的潜在漏洞的数量减到最少。但是,默认情况下,不会安装 web 服务器。

如果你在浏览器中输入你的网络服务器的 IP 地址,你的浏览器会回应说它不能连接到电脑。这是因为 web 服务器尚未运行。为了安装 web 服务器,我们将使用 CentOS 的yum包管理器。这本书涵盖了 Apache web 服务器,计算机称之为httpd,但是也有其他的可能性。

要安装httpd,请运行:

yum -y install httpd

运行yum的时候在什么目录并不重要。无论您从哪里运行它,它都会将软件包安装到正确的目录中。yum将列出您想要安装的软件包,以及运行它所需的其他软件包。

现在你的 web 服务器已经安装好了,但是它没有运行。要运行 web 服务器,只需输入:

systemctl start httpd

这个命令也不关心您在什么目录中。

现在您的 web 服务器正在运行,但是您可能仍然无法连接到它。这是因为 CentOS 在默认情况下会运行防火墙。因此,我们需要在防火墙上增加漏洞,以允许从外部访问 web 服务器。

为此,发出以下命令:

firewall-cmd --add-service http
firewall-cmd --add-service http –permanent
firewall-cmd --add-service https
firewall-cmd --add-service https --permanent

这些命令会将 HTTP 和 HTTPS 添加到远程用户可以连接的服务列表中。管理您的防火墙。添加服务(--add-service)允许访问该服务。在没有--permanent标志的情况下运行它会修改当前的防火墙。添加--permanent标志告诉防火墙在服务器重启时将该规则准备好。要立即启用该规则,并在服务器重新启动后仍启用它,您需要这两个命令。

您可以通过发出以下命令来查看允许的服务列表:

firewall-cmd --list-services

当这些命令已经运行时,你可以简单地在你的浏览器中找到你的服务器的 IP 地址,你应该得到一个如图 3-5 所示的测试屏幕。

img/487291_1_En_3_Fig5_HTML.jpg

图 3-5

Web 服务器的测试屏幕

这意味着您的 web 服务器已经启动并正常运行——祝贺您!

然而,还有一个问题需要考虑。即使服务器现在正在运行,如果你重新启动你的机器,它将不会在启动时运行。要确保该服务也在启动时运行,请发出以下命令:

systemctl enable httpd

要进行测试,请转到您的节点的仪表板,在页面的右上角,应该显示“正在运行”如果你点击它,它会显示一个“重启”按钮。单击该按钮重新启动计算机。当您的服务重新启动时,测试网页可能会在某个时候消失。但是,一旦重新启动的进度条完成,测试网页应该可以再次使用。

重新启动将使您注销,因为计算机不再运行。但是,您可以再次登录,您将回到 root 的主目录。

3.5 建立自己的网页

如果网站没有内容,我们得到的测试页面是自动生成的。要为网站创建内容,您只需将一些内容放在正确的位置。

进入/var(类型cd /var)并环顾四周(类型ls)。您看到的其中一个目录将是www.这是 web 服务器提供的数据(即网页)的默认目录。走进www(类型cd www)四处看看(类型ls)。目录是你放置 HTML 和 PHP 文件的地方。进入那个目录(键入cd html)。要验证您是否在正确的位置,键入pwd,它应该会告诉您您在/var/www/html中。

现在您在/var/www/html中,您将为 web 服务创建页面。使用nano index.html创建文件index.html,并在其中放些东西(如果你不知道键入什么,只需键入hello there或类似的东西)。使用 control-o 保存文件,使用 control-x 退出编辑器。一旦你创建了这个文件,你就可以进入你的 IP 地址,这个文件就会显示为默认页面。

3.6 安装 PHP 7

因为这是一本关于 web 应用开发的书,我们想做的不仅仅是网页。我们需要在服务器端启用脚本。因此,我们需要安装我们正在开发的应用框架,以及让它在 Apache 上运行的插件。本书重点介绍 PHP 7。不幸的是,CentOS 7 只有 PHP 5 可用。因此,我们将不得不从另一个库加载 PHP 7。

两个常用的软件包仓库是 EPEL(Enterprise Linux 的额外软件包)仓库和 Remi Collet 的仓库。很容易加载新的存储库供yum查找。每个存储库都有一个 URL,yum可以加载这个 URL,这样yum就可以在将来的安装命令中使用它。要启用这些存储库,只需键入以下内容(缩进的行应该与前一行放在同一行):

yum install -y
  https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
yum install -y
  https://rpms.remirepo.net/enterprise/remi-release-7.rpm

现在我们可以安装 PHP 7 了。为此,只需键入:

yum install -y php74

这将安装基本的 PHP 7 包及其依赖项,但没有其他的了。安装完成后,仍然在/var/www/html目录中,运行命令nano test.php并输入以下脚本:

img/487291_1_En_3_Figc_HTML.png

完成此操作后,用 control-o 保存文件,用 control-x 退出。现在可以直接使用命令行运行该文件。键入命令:

php74 test.php

它将运行您的文件中的代码,并像代码所说的那样输出字符串This is a test

现在,打开你的网络浏览器,进入 http://MY.IP.ADDRESS.HERE/test.php. 注意,它是而不是作为 PHP 脚本运行的。这是因为 Apache 和 PHP 没有连接在一起。

我们现在需要 PHP 连接到 Apache。这是通过 FastCGI 进程管理器完成的(FastCGI 是允许 PHP 和 Apache 进行通信的协议)。您可以使用以下命令进行安装:

yum install -y php74-php-fpm

这是一个独立的过程,因此也必须启用和启动它。

systemctl enable php74-php-fpm
systemctl start php74-php-fpm

现在,我们必须配置 Apache 将.php文件连接到 PHP 7 解释器。使用nano创建一个名为/etc/httpd/conf.d/php.conf的文件,并放入该文本(缩进的行应该在与前一行相同的)。

img/487291_1_En_3_Fig6_HTML.png

图 3-6

PHP 配置文件

这段代码告诉 Apache 将所有对以.php结尾的文件的请求转发(称为代理)到我们之前安装的 FastCGI 服务。最后一行告诉 Apache,它可以将index.php作为目录索引,这意味着如果有人删除了文件名,并且有一个index.php文件可用,它可以将该文件作为该目录中的默认页面。

现在,要让它正常工作,重启 Apache:

systemctl restart httpd

现在你应该可以用你的浏览器点击这个文件了,它应该是通过 PHP 7 处理的。万岁!

安装 PHP 时,您只安装了基础包。要查看您可以安装的所有扩展,请运行命令yum search php74。这将显示所有可用 PHP 包的列表,每个包都可以用yum install -y安装。

3.7 关闭 SELinux

SELinux 是 Linux 的一个安全增强特性。虽然从理论上讲,SELinux 可以做很多事情来最小化服务器上的安全风险,但实际上它对于实际应用来说太笨拙了。SELinux 对于构建良好的站点通常是不需要的,对于构建不良的站点也没有足够的保护。相反,它最终增加了一个很大的系统管理难题,但收效甚微。如果您运行 SELinux,最有可能的结果是,您将花费数天时间试图找出某些东西不工作的原因,结果却发现 SELinux 正在无缘无故地阻止一些基本操作。

要关闭 SELinux,编辑文件/etc/selinux/config,将SELINUX=enforcing改为SELINUX=permissive

重启你的机器。当它重新启动时,登录并发出命令getenforce。上面应该写着Permissive。您现在可以开始工作了。

注意,如果不禁用 SELinux,本书中构建的应用将无法运行,因为 SELinux 会阻止应用连接到数据库。

3.8 为开发设置用户

到目前为止,我们已经使用了根用户(即超级用户)。虽然有许多事情需要 root 用户,但出于安全原因,通常情况下您应该尽可能少花时间作为 root 用户。因为根用户可以做任何事情,所以很容易违反安全措施,因为根用户要么破坏系统,要么意外地允许未经授权的实体访问您的系统。

因此,我们将创建一个非管理用户来完成我们的大部分任务。这个用户将被命名为fred。要创建用户,请键入以下内容:

useradd fred

这将 Fred 设置为系统上的用户,为他创建一个主目录,并给他一个用户和组号。现在,我们需要通过键入以下内容为 Fred 设置密码:

passwd fred

这将提示您输入密码,并通过要求您重复密码来确保您输入的密码是正确的。我们希望 Fred 能够添加和修改/var/www/html目录中的文件,因此我们需要授予他这些文件的所有权。我们使用chown命令来实现这一点:

chown -R fred /var/www/html

这告诉将/var/www/html目录及其下所有文件的所有者更改为fred。我们现在可以注销,或者我们可以打开一个新窗口,以fred的身份ssh回到机器中:

ssh fred@MY.IP.ADDRESS.HERE

这将把我们带到弗雷德的主目录。我们现在可以cd进入/var/www/html并像以前一样修改文件,因为它们现在归 Fred 所有。请注意,由于 root 是超级用户,所以即使文件归 Fred 所有,他仍然可以修改这些文件。

3.9 向服务器传输文件

现在,大多数人不喜欢直接在他们的服务器上编程。通常,他们希望在自己的机器上编写程序,然后传输到服务器。为此,您需要一个支持 SFTP 协议的工具。SFTP 基本上是基于 SSH 的 FTP。

跨 Windows、Macintosh 和 Linux 工作的最简单的 SFTP 解决方案是 FileZilla ( www.filezilla-project.org )。您可以使用普通(免费)或专业(付费)版本。要使用 FileZilla,在你安装之后,只要打开它,点击“文件”,然后点击“网站管理器”单击“新建站点”按钮。

填写类似于图 3-7 的屏幕。在“主机”框中,输入您的 Linode 服务器的 IP 地址。在“协议”框中,选择 SFTP。将“登录类型”设置为正常,将用户名设置为fred,将密码设置为您为 Fred 设置的任何密码。你也可以把网站的名字改成一个容易记住的名字(我把我的名字叫做“Linode 虚拟服务器”)。

img/487291_1_En_3_Fig7_HTML.jpg

图 3-7

设置 FileZilla 进行连接

完成后,点击“连接”按钮。

第一次连接时,可能会弹出一个对话框,提示“未知主机密钥”这很好——这和你第一次通过ssh连接时的想法是一样的。该软件以前从未见过服务器。单击标有“始终信任该主机”的框,然后单击“确定”一旦连接上,它将看起来类似于图 3-8 。

img/487291_1_En_3_Fig8_HTML.jpg

图 3-8

FileZilla 连接到你的 Linode 服务器

FileZilla 有两个窗格——左窗格是您的本地计算机,右窗格是远程计算机。它会告诉你在每一个目录中它正在查看哪个目录。您需要做的是将本地目录设置为您想要传输文件的位置,将远程目录设置为您想要放置这些文件的位置(大概是/var/www/html)。一旦设置了这些目录,下面的两个窗格就会列出实际的文件,您可以简单地来回拖放它们。

作为练习,在您的本地计算机上创建几个简单的 PHP 文件,并将它们传输到服务器,并验证您可以通过 web 浏览器看到它们。

img/487291_1_En_3_Figd_HTML.jpg编辑PHP.INI

对于某些应用,您可能需要修改php.ini文件。我们安装的 PHP 7 版本将php.ini文件放在目录/etc/opt/remi/php74/中。但是,只有 root 用户可以访问该文件。

如果您需要用 FileZilla 传输它,您将需要以 root 用户身份使用 root 用户的密码重新连接。也可以通过ssh登录,直接用nano修改。在任何情况下,修改该文件时都要小心。此外,在修改文件之后,一定要使用

systemctl restart php74-php-fpm

这似乎需要做大量的工作来设置。虽然有更快的开始方法,但这种方法有几个优点。首先,你现在比大多数人更熟悉所有这些部分是如何连接在一起的。第二,你有一个运行 PHP 7 的网络服务器,而不是某个十年前的版本。最后,您需要的一切都有了,没有什么是没有的,这将有助于您保持 web 服务器的安全。

img/487291_1_En_3_Fige_HTML.jpg其他要安装的工具

就个人而言,我喜欢用工具打包我的节点。我成为 Linux 用户的时间比我的许多读者还长,所以我非常熟悉大量的工具。幸运的是,它们都很容易安装(你必须以 root 用户身份登录才能安装)。

无论如何,以下是我经常使用的工具的安装命令:

yum install -y git
yum install -y screen
yum install -y telnet
yum install -y bind-utils
yum install -y traceroute
yum install -y nmap
yum install -y strace
yum install -y perl

本书不是对这些命令的介绍,但是它们值得研究。

四、创建简单的 Web 应用

在本章中,我们将创建一个极其简单的 web 应用,用于后续章节中的演示。这里的目标是让一个完整的端到端应用启动并运行。

我们将要开发的应用将只是一个留言簿,这样任何人都可以在留言簿上留言。

4.1 设置数据库服务

任何好的 web 应用都有一个数据库。我选择的数据库一直是 PostgreSQL ( www.postgresql.org )。有一种神话认为 PostgreSQL 很慢。这是有一定道理的——在上世纪 90 年代。

然而,从 PostgreSQL 7 开始,PostgreSQL 已经是一个顶级的表现者,并且它只是随着每个版本变得更好。此外,PostgreSQL 在复杂查询方面一直表现出色,今天依然如此。PostgreSQL 的目标是无限制编程。例如,在 PostgreSQL 文本列中,您可以在单行的一列中存储高达 4gb 的数据,并且仍然可以按它进行排序。在许多数据库中,您的大部分时间都花在使数据与数据库的首选架构相匹配上。我发现,使用 PostgreSQL,数据库通常已经为您自己的数据架构做好了准备。

虽然这不是一本关于 PostgreSQL 的书,但我们将讨论它的一些与节点集群相关的特性。

要安装 PostgreSQL,只需以 root 用户身份执行以下操作(本部分的所有操作都应以 root 用户身份执行):

yum install -y postgresql-server

这将安装 PostgreSQL 所需的所有软件包。要设置初始数据库,请键入以下内容:

postgresql-setup initdb

这将创建 PostgreSQL 运行所需的所有目录和文件。接下来,我们需要设置连接到 PostgreSQL 数据库的身份验证方法。PostgreSQL 将其数据和配置存储在目录/var/lib/pgsql/data中。控制数据库访问的文件是pg_hba.conf

编辑该文件(键入nano /var/lib/pgsql/data/pg_hba.conf)以在顶部添加以下两行:

local all all trust
host all all all md5

第一行表示信任所有来自本地的连接(即,不通过网络)。因此,我们在命令行上直接处理数据库时不需要密码。第二行表示任何人都可以使用适当的密码通过网络连接到数据库。这有点不安全(我们不希望任何人都能连接到我们的数据库),除了默认情况下数据库只监听本地地址 127.0.0.1,所以现在无论如何你不能从外部连接到它。确保保存文件,然后退出编辑器。

请注意,即使我们有适当的限制(仅限本地连接、防火墙等)。),许多人会认为前面的配置过于暴露,不符合他们的喜好。这里的措施是为了平衡安全性和易学性。要了解更多关于保护 PostgreSQL 的信息,您应该阅读关于pg_hba.conf文件的 www.postgresql.org 上的文档。

现在是时候打开我们的数据库了。为此,请输入以下内容:

systemctl enable postgresql
systemctl start postgresql

您的数据库系统现在已经启动并运行。您已经创建了数据库系统,但是没有创建数据库本身。然而,首先我们需要一个数据库用户。createuser命令创建一个新的数据库用户:

createuser -U postgres -d -P gbuser

该命令以数据库管理员用户(-U postgres)的身份运行,创建一个名为gbuser的新用户,他可以创建数据库(-d),并提示您为这个新的数据库用户(-P)设置一个密码。当它提示您时,将密码设置为您想要的任何值,并记下它,以便以后使用。我们将在本书中需要的地方使用密码mypassword,但是请注意,这将是一个在生产中实际使用的可怕密码。当使用命令行时,您不需要使用密码,因为我们已经将它设置为trust,但是当从您的应用连接时,您将需要它。

要以此用户身份创建数据库,请键入:

createdb -U gbuser guestbookapp

这将创建一个新的数据库作为给定的数据库用户。现在,为了创建表,我们需要登录数据库。psql命令将为您提供一个到数据库的交互式 SQL 会话。要使用它,只需键入:

psql -U gbuser guestbookapp

命令提示符会切换到类似于guestbookapp=>的状态,这表示您在数据库中。要随时退出,可以键入\q。像许多系统管理命令一样,当您运行 PostgreSQL 命令时,它并不真正关心您在文件系统中的位置。它与运行在自己目录中的数据库服务通信。

现在我们已经连接到数据库,我们将使用以下命令创建一个表:

create table gb_entries(id serial primary key, name text, email text, message text, created_at timestamp,
has_img bool default false);

id字段是用类型serial创建的,这是 PostgreSQL 的自动编号机制。要查看您刚刚创建的表格,请键入\d gb_entries。完成后,进入\q退出数据库。

4.2 The 代码

在我们编写 PHP 代码之前,我们需要安装一些额外的 PHP 库,以便我们可以连接到我们的数据库。将它们安装在:

yum install -y php74-php-pgsql php74-php-pdo
systemctl restart php74-php-fpm

我们的应用很简单:

  • 一个保存配置信息和常用功能的文件

  • 一个显示留言簿条目列表的文件

  • 一个显示个人留言簿条目的文件

  • 一个文件用于输入新的留言簿条目

  • CSS 样式表

这本书假设你知道一点 PHP 和 SQL,但即使你不知道,代码应该足够简单,无论你熟悉什么语言。因此,文件将在此呈现,不做过多评论。你会在本章末尾找到包含代码的数字。

图 4-1 显示了其他文件包含的常用功能。它有两个获取数据库连接的函数——一个用于获取只读连接,一个用于获取读/写连接。在这一点上,它们都返回相同的连接(并且它们确实都是读/写的),但是随着我们进一步开发应用,我们将会看到如何通过分离只用于读的连接和用于读和写的连接来获得很多好处。这两个函数都简单地使用 PDO (PHP 数据对象)通过连接字符串获取到数据库的连接。请注意,这些连接字符串包含数据库的密码。确保将这些连接字符串的上的密码更改为您之前为gbuser数据库用户输入的密码。

它还有getHeader()getFooter()函数,所以我们不必写太多的 HTML。另外,h()被用作htmlspecialchars()的一个较短版本,这样我们可以有更安全的输出。

图 4-2 是列出数据库中所有条目的 PHP 脚本。这段代码只是创建一个数据库语句,执行它,并遍历结果。

图 4-3 显示了从数据库中获取单个条目。同样,只准备和执行一条 SQL 语句,结果显示在屏幕上。

图 4-4 简单地显示了一个将被用来创建一个新的留言簿条目的表单。该表单将其数据发布到图 4-5 中的程序中。该程序根据在中输入的值创建新记录。然后,在执行 SQL insert 语句后,它将用户重定向回清单屏幕。

最后,图 4-6 是一个静态 CSS 文件,为整个过程提供了少量的样式。正如在第一章中提到的,如果你不想自己输入所有这些文件,你可以从 GitHub 获得它们:

https://github.com/johnnyb/cloud-example-application

要在服务器上直接使用 git,您需要以 root 用户的身份安装它:

yum install -y git

输入所有程序后,将它们发送到新服务器上的/var/www/html文件夹中。然后,将你的浏览器导航到 http://MY.IP.ADDRESS.HERE/list.php ,看看你的程序是否工作!如果没有,您可以通过以 root 用户身份登录并使用以下命令查看日志文件来检查 PHP-FPM 的错误日志:

tail -200 /var/opt/remi/php74/log/php-fpm/error.log

这将给出 PHP 错误日志的最后 200 行。

发出以下命令可以访问另一个日志,它将为您提供有用的信息:

tail -200 /var/opt/remi/php74/log/php-fpm/www-error.log

修复所有错误,然后重试。最可能的错误是程序中键入了错误的内容,或者连接字符串中列出的密码与您为 PostgreSQL 用户设置的密码不匹配。

查找错误消息的另一个地方是 web 服务器的错误日志。您可以使用以下命令查看该日志的结尾:

tail -200 /etc/httpd/logs/error_log

如果需要,可以在附录 C 中找到其他故障排除步骤。如果一切顺利,您应该会看到一个屏幕,提示您创建一个新条目。点击这个链接会给你一个要填写的表格。填写完表格后,点击“提交”会将新的留言簿条目添加到列表中。然后,您可以单击单个条目来查看完整信息。如果您的应用不这样做,那么可能是输入了错误的内容。

img/487291_1_En_4_Figa_HTML.jpg应用的局限性

本书的目标是让你快速了解如何扩展你的应用。因此,其他重要的开发方面,如错误处理、日志记录、净化数据和安全加固,都没有涉及。目标是传达一个应用,它可以快速输入,易于完全理解,并且不需要很深的平台知识来跟随或修改。尽管如此,我们还是实现了一些基本的安全实践,比如使用bindValue对通过$_GET$_POST发送的值进行适当的转义,并在将它们发送回用户时使用htmlspecialchars()对它们进行转义。

www.owasp.org 网站上可以找到关于安全编程的有用信息。

img/487291_1_En_4_Fig6_HTML.png

图 4-6

CSS 文件(guestbook.css)

img/487291_1_En_4_Fig5_HTML.png

图 4-5

创建留言簿条目(create.php)

img/487291_1_En_4_Fig4_HTML.png

图 4-4

新留言簿条目(new.php)

img/487291_1_En_4_Fig3_HTML.png

图 4-3

单个留言簿条目(single.php)

img/487291_1_En_4_Fig2_HTML.png

图 4-2

列出所有留言簿条目(list.php)

img/487291_1_En_4_Fig1_HTML.png

图 4-1

配置和常用功能(common.php)

五、设置基本云集群

此时,我们有了一个简单的云应用,它运行在一台服务器上。虽然我们可以将它放在我们只需点击就能构建的服务器上,这很好,但这并没有充分利用云。云的目标之一是创建一个应用集群——一组协同工作来解决问题的服务器,其中的计算能力可以根据需要进行扩展和收缩。

5.1 简单的双层架构

在本章中,我们将探索一个简单的两层架构。该架构将包括

  • 数据库服务器

  • 一组 web 服务器

  • 管理 web 服务器之间流量的负载平衡器

    img/487291_1_En_5_Fig1_HTML.jpg

    图 5-1

    一个简单的双层体系结构图

该架构的基本结构如图 5-1 所示。所有的连接都进入一个负载平衡器,它的工作是将连接转发到几个 web 服务器中的一个。负载平衡器不仅转发连接,而且还监视各个 web 服务器的健康状况,如果 web 服务器停止响应,它将停止发送 web 服务器连接。每个 web 服务器共享一个数据库服务器。

在开发云应用时,程序员不仅需要知道如何建立集群,还需要知道如何分析集群。如果你看一下图 5-1 中的图表,你可以看到所有的网络服务器都依赖于一个数据库服务器。这使得数据库服务器成为集群的限制因素。几乎每个集群,不管设计得多好,都有一些限制因素。目标是最小化它们对您的架构的影响。

因为这种应用体系结构受单个数据库节点的限制,所以它最适合用于中小型部署,在这种部署中,大部分处理是在 web 服务器上完成的,而不是在数据库上。示例应用因为简单,实际上在 web 服务器上只做很少的处理。尽管如此,本章将向您展示如何设置服务器以在这种配置中部署它。

5.2 复制节点

图 5-1 中描述的基本双层应用表明我们将需要几个服务器节点。我们可以通过使用 CentOS 的空白副本启动新机器,并单独配置每台机器来实现这一点。然而,由于我们已经花了时间来设置当前的服务器,使其按照我们希望的方式工作,我们应该利用我们花在配置所有东西上的时间。

Linode 提供了几种可以完成这项任务的服务,每种服务都有自己的优点和缺点。Linode 能够创建保存的映像,这些映像可用于直接创建新节点(您可以选择保存的映像,而不是选择操作系统)。Linode 还有一个克隆服务,如果源机器和目标机器都关闭,它允许您克隆现有的机器(这不是一个硬性规定,但是如果您试图克隆一个正在运行的机器,您可能会遇到一致性问题)。最后,您可以从机器的备份中进行克隆。

我更喜欢从备份中进行克隆,因为(a)无论如何你都应该定期备份你的服务器,(b)在你创建新的服务器时,你不必关闭机器什么也不做,以及(c)这迫使你在学习克隆服务器的同时使用备份系统并适应它(有一天你将需要从备份中恢复,所以在你需要它之前熟悉这个过程是很好的)。Linode 图像服务可以解决这个问题,但是对于实际生产应用来说,它有太多的限制。为了防止用户将图像服务用作备份服务,他们限制了图像的大小和数量,但我的大多数机器通常都比 Linode 图像允许的最小大小大。请注意,其他云服务(如 DigitalOcean)提供类似的服务,但有不同的限制。

如果我们想要启动一台与现有服务器完全相同的新服务器,那么我们只需备份当前服务器。我们需要做的是首先在我们的服务器节点上启用备份。要做到这一点,只需登录到 Linode,在列表中单击您的节点,然后在节点的仪表板上单击“Backups”选项卡。这将为您提供一个按钮,上面写着“启用该节点的备份”这增加了一个小的月费,但它肯定是值得的。单击该按钮,您的计算机将自动拥有可用的每周、每天和临时快照。屏幕现在看起来应该类似于图 5-2 。

img/487291_1_En_5_Fig2_HTML.jpg

图 5-2

Linode 备份管理屏幕

我们现在要做的是拍摄我们机器的快照。为您的备份输入一个名称(我将我的命名为“用于复制的快照”),然后单击“拍摄快照”按钮因为 Linode 服务器都在固态硬盘上,所以快照非常快,通常只需要 5-10 分钟。

img/487291_1_En_5_Figa_HTML.jpg其他备份设置

因为备份实际上会降低磁盘速度,所以 Linode 允许您指定备份窗口。您可以选择一天中要进行备份的时间,也可以选择一周中的某一天作为“每周备份”一天中的时间应该是系统使用率最低的时候,而一周中的某一天应该在任何主要批处理之前。例如,如果您在星期五进行批处理,将备份日期设置为星期四将确保您在主要处理发生之前有一个“之前”的快照。如果不进行大规模批处理,每周备份的日期并不重要。

备份开始后,我们可以进入下一个任务,即创建新的服务器。新服务器将成为我们的数据库服务器。

如我们在第三章中所做的那样,要创建机器,先进入“创建”,然后进入“Linode”。但是,在“映像”标题下,您需要选择“我的映像”,然后选择“备份”,而不是选择发行版这将显示具有可用备份的节点列表。单击您的节点,然后它将显示可用的备份,其中包括我们刚刚创建的备份。参见图 5-3 来看看这个应该是什么样子。

您可以根据自己的需要创建尽可能大的机器,但是为了便于练习,您最好使用最小的机器尺寸。这个节点需要和你的另一个节点在同一个数据中心(否则他们不能私下地、廉价地、快速地互相交谈),但是 Linode 会自动把一个从备份创建的新节点放到同一个数据中心。

对于 Linode 标签,让我们使用名称dbmaster,这样我们就知道这个节点将用于主数据库。拥有一堆没有名字(或者名字不好)的节点会很快变得难以管理,所以一定要确保总是给你的节点起描述性的名字。

img/487291_1_En_5_Fig3_HTML.jpg

图 5-3

从备份中引导新的节点

现在,单击“创建”来构建您的新节点。从备份创建时,一旦节点完全创建,您必须自己引导节点。在屏幕的右上角,应该显示“离线”点击它,然后选择“开机”来启动你的新机器。现在,您可以使用为初始机器设置的相同用户和密码ssh进入这台新机器。事实上,它还应该运行您创建的应用。它是另一台机器的精确副本,只是修改了网络设置。

img/487291_1_En_5_Figb_HTML.jpg恢复到更大的机器

如果您将备份恢复到较大的计算机,它可能不会利用您购买的所有磁盘空间。由于分区是直接复制的,旧分区的大小将与其在复制它的服务器上的大小相同,这可能小于您的可用空间。

要解决这个问题,首先关闭服务器电源。接下来,在节点的仪表板上,单击“高级”选项卡。您可以添加另一个磁盘来使用剩余的空间(这很难管理,所以我不推荐),或者调整主磁盘的大小来利用所有的空间。若要调整磁盘大小,请在“磁盘”下找到主磁盘它应该被命名为“CentOS 7 磁盘”(而不是标有“交换映像”的磁盘)之类的东西。点按主盘旁边的省略号(即...),然后选取“调整大小”然后,您可以将大小设置为它告诉您的最大值,然后单击“调整大小”按钮。

当它完成尺寸调整后,你可以重新启动你的机器,现在你已经准备好了。

5.3 设置您的专用网络

既然我们有两台机器,我们需要它们进行通信。他们可以通过他们的公共 IP 地址通信,但是这导致了几个问题。首先,如果你有不想公开的服务(比如你的数据库),如果你只有一个公开的 IP 地址,就更难阻止公众获取这些服务。此外,Linode 对你的公共 IP 地址上的流量收费,所以,如果你通过那个 IP 地址通信,Linode 会对你的内部流量收费。因此,拥有一个私有 IP 地址是很重要的,因为它能让计算机通过一个快速、自由、更安全的内部网络相互通信。

为了解决这些问题,Linode 允许你为你的服务器建立一个内部网络。一个帐户中的所有服务器共享一个内部网络(如果它们是为该网络设置的)。要将服务器添加到您的内部网络,您只需进入节点的“网络”选项卡,然后单击“添加专用 IPv4”它会给你一些关于私有地址的附加信息,你可以点击“分配”继续。这将为计算机分配一个专用 IP 地址。因为所有的 web 服务器都将与该服务器通信,所以您需要记下生成的私有 IP 地址。从现在开始,我们将把这个地址称为DB.MASTER.PRIVATE.IP,所以总是用您刚刚记下的服务器的私有 IP 地址来替换它。

注意,在一些云平台上(包括 Linode),私有 IP 地址并不是完全私有的。也就是说,同一数据中心的其他云客户可能也在该网络上。因此,虽然私有网络肯定比公共网络更安全,但是云上的私有网络并不能保证只有我们自己的计算机在连接。因此,在生产系统上,您仍然需要采取预防措施来防止不必要的访问,即使是在内部网络上。然而,Linode 确实会过滤每个节点的流量,因此您不必担心有人会窥探内部网络上的数据流量。

您需要重新启动节点才能完成该过程。

一旦启动完成,您仍然可以在 http://NEW.NODE.PUBLIC.IP/list.php, 看到应用,但是您将无法在私有 IP 地址上看到它,因为如上所述,它是私有的。

要查看服务器上的地址列表,请以 root 用户身份登录并发出以下命令:

ip addr show

这应该会打印出分配给该节点的所有 IP 地址。

img/487291_1_En_5_Figc_HTML.jpg私有 IP 地址

如果您不熟悉 IP 寻址,一些 IPv4 地址已被保留用于内部网络。这些地址包括

  • 192.168.X.X

  • 172.16–31.X.X

  • 10.X.X.X

这些 IP 地址都不允许用于互联网上的公共通信。

因此,在设置您的内部网络时,Linode 将从这些地址池中选择 IP 地址来配置您的机器。

另一个臭名昭著的 IP 地址是127.0.0.1,它被称为环回地址,这是一个机器可以用来指代自己的 IP 地址(实际上,整个范围127.X.X.X都是为此保留的,但通常只使用127.0.0.1)。

5.4 处理来自其他服务器的数据库连接

这台机器上现在有了应用和数据库的完整副本。但是,它仍然被配置为单服务器系统。我们需要将其配置为机器集群的主数据库。这一部分应该以 root 用户的身份执行,它将展示您需要做些什么来实现这一点。

由于这台机器是template_node的克隆,这意味着所有的用户、程序和配置都被复制到这个节点上。因此,您可以使用之前设置的密码以 root 用户身份ssh进入机器。

要将它用作其他节点的数据库服务器,您需要使数据库能够侦听来自这些节点的连接。默认情况下,PostgreSQL 只监听本地主机接口上的连接。我们不希望 PostgreSQL 监听公共互联网上的连接。因此,我们希望配置 PostgreSQL,使其监听本地主机及其私有 IP 地址上的连接。

要做到这一点,作为根用户,用nano打开文件/var/lib/pgsql/data/postgresql.conf,并修改显示为listen_addresses的行。将该行改为:

listen_addresses = 'localhost,DB.MASTER.PRIVATE.IP'

确保用节点的实际私有 IP 地址替换DB.MASTER.PRIVATE.IP。如果行首有注释标记(#),一定要去掉;否则,该命令将不会被激活。用 control-o 保存文件(只要按回车键来验证文件名,如果它问)。然后,要退出,请使用 control-x。现在,使用以下命令重新启动 PostgreSQL:

systemctl restart postgresql

此外,您将希望打开防火墙,以便它可以接受 PostgreSQL 的远程连接。您可以通过以下方式实现这一点:

firewall-cmd --add-service postgresql
firewall-cmd --add-service postgresql --permanent

通常,我让 web 服务器在数据库服务器上运行,这样我就可以检查它。但是,如果您愿意,可以使用以下命令关闭 web 服务器:

systemctl stop httpd
systemctl disable httpd

5.5 设置 Web 服务器

现在我们已经配置好了数据库,是时候设置我们的 web 服务器了。

请注意,我们实际上不会使用template_node作为服务器。我喜欢在身边放一个小机器,简单地用作未来盒子的模板,尤其是 webnodes。这样,我可以拥有一台最新的、已备份的小型计算机,当我准备好创建新的“映像”时,我只需创建一个命名备份即可使用。请注意,这将覆盖以前的快照映像,但对于我的目的来说,这通常是可以的。如果您需要保留旧版本,只需为您想要维护的每个配置保留一个模板节点。

现在我们将配置template_node成为一个 web 服务器模板。我们只需要做三点改变:

  1. 关闭该计算机上的数据库。

  2. 更改 web 应用的代码以指向我们的新数据库。

  3. 在机器上启用专用 IP 地址,使其能够使用专用网络。

为了完成第一个任务,我们需要做的就是以 root 用户身份登录到template_node机器并运行:

systemctl stop postgresql
systemctl disable postgresql

为了完成第二个任务,我们只需要修改common.php文件。我建议在您的本地机器上修改它,并使用 SFTP 传输新文件。不过也可以在服务器上用nano直接修改。您所要做的就是更改连接字符串。在当前显示为host=localhost的地方,将其改为host=DB.MASTER.PRIVATE.IP,其中DB.MASTER.PRIVATE.IP是您的dbmaster节点的私有 IP 地址。您需要进行两次修改——一次在getReadOnlyConnection()函数中,一次在getReadWriteConnection()函数中。完成后,用 SFTP 将代码加载回服务器。

此时,代码将不起作用,因为它不能访问dbmaster机器。这是因为 PostgreSQL 是唯一的监听它的私有 IP 地址,而template_node还没有私有 IP 地址进行通信。因此,您需要给机器添加一个私有 IP 地址,这样它就可以通过它的私有 IP 地址连接到dbmaster

使用第 5.3 节中概述的过程为机器创建一个私有 IP 地址(不要忘记之后重新启动!).

一旦你完成了这些步骤,你的template_node机器应该能够连接到dbmaster,所以测试一下。找到你的template_node服务器的 IP 地址,看看它是否还能工作。如果是的话,那么恭喜你,因为你刚刚实现了一个小的、两层的系统!

现在,正如我前面说过的,我们实际上不会使用template_node来服务请求。我们的目标是使用它,以便我们可以轻松地启动新的 web 服务器节点,根据需要扩展容量。

因此,既然template_node已经完全设置为一个 web 服务器,那么创建一个新的备份快照。这一步至关重要。每当您在template_node中进行更改时,您应该创建一个新的备份快照,这样您从它创建的新节点将会有您的新更改(尽管它根本不会影响现有的节点)。

我们现在将为我们的集群创建三个(或者任意多个)web 服务器节点。

以下是每个新 web 服务器节点的步骤:

  1. 使用第 5.2 节中的步骤创建一个新节点。确保(a)它是在与dbmaster相同的数据中心创建的,并且(b)将节点的名称设置为webnode1(或者 2,或者 3)。

  2. 使用第 5.3 节中的步骤,为节点添加一个私有 IP 地址,使其可以连接到私有网络上的dbmaster

  3. 机器完成启动后,通过查看节点的公共 IP 地址(即 http://WEB.NODE.PUBLIC.IP/list.php).) )上的 web 应用,验证其功能是否完整

最后,您应该有三台机器,webnode1webnode2webnode3,每一台都可以作为您的 web 应用的前端。现在您只需要将它们连接在一起,这将在下一节中介绍。

5.6 设置负载平衡器

现在我们有三台前端机器,都连接到一个数据库。我们如何将它们连接在一起?我们可以这样做的一个方法是建立一个 DNS 循环方案。其工作方式是在 DNS 中为一个主机名设置多个记录。然后,浏览器自己会选择要连接到哪个主机。这样做的问题是,如果您的一台机器出现故障,就没有办法引导用户离开该 IP 地址。Linode 现在实际上支持这种故障转移,但是它的用法超出了本书的范围。

img/487291_1_En_5_Fig4_HTML.jpg

图 5-4

创建节点平衡器

负载平衡器是一个更好的解决方案。负载平衡器位于您的集群前面,为您获取连接,然后将这些连接转发给可用于处理它们的服务器。此外,如果其中一台服务器出现故障,负载平衡器会检测到这一情况,并将流量转移到其余服务器上。然后,当您的服务器恢复时,负载平衡器也会检测到这一点,并将流量移回服务器。

在 Linode 中设置负载平衡器很容易。Linode 将其负载平衡器称为“节点平衡器”要设置节点平衡器,请单击“创建”,然后选择“节点平衡器”菜单项。这将带您进入图 5-4 所示的屏幕。

就像其他事情一样,你需要

  • 设置平衡器的名称(我们将使用“主平衡器”)。

  • 将平衡器放在与节点相同的数据中心。

此外,您需要添加一些额外的配置,如图 5-5 所示。确保选择了以下选项:

  • “端口”应设置为“80”

  • “协议”应设置为“HTTP”

  • “算法”应设置为“循环法”

  • “会话粘性”应设置为“无”

  • “主动健康检查”应设置为“无”

  • “被动健康检查”应该打开。

之后,您需要向您的平衡器添加至少一个节点(可以在创建平衡器之后添加更多节点)。在“标签”字段中输入节点的名称(我们假设为webnode1)。然后,在“IP 地址”字段中,从下拉列表中为您的节点选择 IP 地址。确定端口设置为“80”

完成这些设置后,单击“创建”创建平衡器。

创建平衡器后,您可以通过进入“配置”,然后进入“端口 80”,然后进入底部的“添加节点”来添加其余的节点。添加您创建的任意数量的节点。完成后点击“保存”。

平衡器将节点添加到列表中有时需要几分钟时间。要检查状态,请返回到节点平衡器配置屏幕。每台服务器旁边都有一个“状态”字段。当状态为“启动”时,服务器成功连接到平衡器。

如果您想为另一个端口配置一个负载平衡器,您可以使用“添加另一个配置”按钮。

img/487291_1_En_5_Fig5_HTML.jpg

图 5-5

节点平衡器附加设置

img/487291_1_En_5_Figd_HTML.jpg其他节点平衡器选项

节点平衡器有许多选项可用。下面是一些重要的描述。

端口:这是节点平衡器应该转发的 TCP 端口。这通常是 80 (HTTP)或 443 (HTTPS)。在我们的例子中,我们将使用端口 80。

协议:这是您希望服务器处理转发请求的方式。如果协议设置为 TCP,那么平衡器所做的唯一的事情就是将连接转发给你。如果它被设置为 HTTP 或 HTTPS,那么服务器实际上会为您处理连接的某些部分。HTTP 是我们将在本书中使用的。HTTPS 为安全站点增加了额外的支持,因为负载平衡器将为您处理 SSL 连接,从而从服务器上移除了大量负载(您还将证书和密钥信息上传到负载平衡器进行处理)。在进行 HTTPS 时,您可能希望平衡器上的端口不同于机器上的端口。在 HTTPS 的情况下,您应该将平衡器设置为连接到服务器上的未加密端口 80。如果你想做由你的机器而不是平衡器处理的 HTTPS,你可以选择 TCP(而不是 HTTPS)作为协议。由于获取和安装证书的复杂性,对于本书中的示例,选择端口 80 和 HTTP 进行负载平衡。

算法:这是负载均衡器决定将其转发给哪个服务器的方式。循环调度是默认设置,应该可以正常工作。

会话粘性:这是一个给定的用户是否应该继续连接到相同的网络服务器,一旦它已经建立了初始连接。只有当你在你的网络服务器上存储会话信息时,这才是重要的。想象一下,如果您的 web 服务器有重要的会话信息,但是下一个请求发送到不同的服务器!因此,此选项允许您配置客户端是否以及如何与服务器匹配。我们的应用没有会话信息,所以应该设置为“None”如果您的应用使用本地会话信息(或本地缓存,我们将在 6.1 节中看到),我会选择 HTTP Cookie 方法,因为它不会像“Table”方法那样妨碍您的负载测试。因为所有的请求都来自同一个 IP 地址,所以“Table”方法只是将整个负载测试指向一台服务器,而不是将其分散开来,看起来好像负载平衡没有给你任何帮助。

主动健康检查:这允许您为负载平衡器指定一个 URL,以便检查 web 服务器的状态。您可以将平衡器配置为只检查 HTTP 状态,或者在响应正文中查找特定的字符串。

后端节点:权重:为该服务器设置节点平衡器的首选项。权重越高,服务器连接越多。

后端节点:模式:设置平衡器中节点的模式。“接受”用于正常操作。“拒绝”实际上是关闭该节点,这意味着平衡器不会再向该节点发送任何请求。但是,如果打开了会话粘性,您可能不希望直接从“接受”转到“拒绝”“Drain”告诉平衡器接受来自在该节点上有会话的客户端的连接。“备份”表示只有当所有其他节点都关闭时才接受连接。

将所有 webnodes 添加到节点平衡器后,现在可以通过查看节点平衡器本身的 IP 地址来查看集群,节点平衡器会将您的请求转发到集群中的一台机器。您可以通过单击主菜单中的“节点平衡器”找到您的节点平衡器的 IP 地址。IP 地址将列在列表中您的平衡器旁边。它也列在节点平衡器“摘要”屏幕的右侧。

5.7 测量可扩展性

我们开发的简单 web 应用并没有从这里介绍的架构中获益太多。我们的 web 应用只是一个简单的外壳,用于一些数据库查询。因此,简单地添加前端设备无助于解决我们的应用受数据库限制的问题。将数据库从 web 服务器中分离出来会给我们带来一些帮助,因为它允许数据库服务器只关注数据库连接。然而,在核心上,我们在这个应用中所做的一切只是围绕数据库查询的一个薄薄的包装。

但是我们如何衡量我们应用的可伸缩性呢?衡量应用可伸缩性的一个常见的简单工具是 ApacheBench。ApacheBench 是 Macintosh 和大多数 Linux 发行版的标准。对于我们的例子,我们实际上可以从我们的template_node服务器运行 ApacheBench 来测试集群的其余部分。

为此,以rootfred的身份登录到您的template_node服务器。要运行一个简单的 ApacheBench 会话,只需键入:

ab http://BALANCER.IP.ADDRESS.HERE/list.php

显然,用负载平衡器的 IP 地址替换BALANCER.IP.ADDRESS.HERE

这将向负载平衡器发送一个请求,并记录处理该请求所花费的时间。输出将类似于图 5-6 。

由于这只是对单个请求进行了基准测试,因此其中没有太多有趣的信息。它说这个请求花费了 9.674 毫秒,推断出来,它估计我们每秒可以处理 103.37 个请求。

现在,ApacheBench 有几个选项可以让我们更充分地使用服务器。-n选项告诉 ApacheBench 要发出多少个请求(默认情况下我们发出了 1 个)。-c选项告诉 ApacheBench 要建立多少个并发(即同时)连接。如果没有-c,ApacheBench 将一次只运行一个请求。但是,如果您添加了-c 50,ApacheBench 将始终保持对 web 服务器的 50 个活动请求。

因此,为了练习集群,我做了以下工作:

ab -c 50 -n 1000 http://BALANCER.IP.ADDRESS.HERE/list.php

这总共发送 1,000 个请求,确保每次总是有 50 个活动的请求。结果如图 5-7 所示。

这表示有 50 个并发请求时,我们每个请求的平均时间下降到 5.136 毫秒,但是每个请求的平均时间增长到 256.822 毫秒。这实际上不是问题,因为每个请求的平均时间对于容量规划来说是最重要的。我们还被告知,按照这个速度,我们的服务器每秒可以处理 194.69 个请求。

img/487291_1_En_5_Fig7_HTML.png

图 5-7

1,000 个请求的 ApacheBench 结果

img/487291_1_En_5_Fig6_HTML.png

图 5-6

单个请求的 ApacheBench 输出示例

这听起来很棒,除了当您对单台机器进行测量时(您可以用您机器的 IP 地址之一替换 IP 地址),您会得到接近相同的结果。当并发请求数量激增时,负载均衡器可以处理稍高的压力,但总的来说,它们的结果基本相同。这是因为我们的应用几乎完全只是数据库的一个外壳。因此,这个数字反映了我们数据库服务器的最大容量。为了验证这一点,您可以暂时将数据库服务器的大小调整到更大的机器上。

要调整您的dbmaster服务器,进入dbmaster的仪表板,点击“resize”选项卡,并选择一个新的计划(我选择了 Linode 4GB)。现在点击“立即调整这个节点的大小”停机几分钟后,你的 Linode 会重新调整大小,它会保留它的 IP 地址!一旦它完成调整,它将启动,你现在将有一个调整后的服务器!

现在,您可以在这个配置上运行 ApacheBench,并看到调整数据库服务器的大小为您提供了比以前的配置更大的优势。在做了这个实验之后,我已经将dbmaster的大小调整回了一个毫微级 1GB,这样我们就可以更好地看到后面章节中描述的架构的效果。

如果应用不是如此依赖数据库,我们应该已经看到了这种架构的一些扩展优势。然而,即使存在数据库瓶颈,第六章也将着眼于架构的改进,这将提供显著更好的伸缩能力。这里的目标仅仅是看看架构是如何工作的,以及如何在 Linode 上设置它。

很高兴看到,仅仅因为您可以向系统添加节点,并不意味着它可以自动伸缩。

六、通过缓存提高可伸缩性

本章将介绍对前一章中描述的基本两层体系结构的一些调整。

6.1 了解缓存架构

在我们当前的应用中,数据库是主要的瓶颈。这意味着添加更多的 web 服务器不会显著增加云能够处理的负载。当您发现一个瓶颈时,最好花些时间考虑如何避免这个瓶颈。

在我们的例子中,我们的数据并不经常改变。即便如此,从留言簿中获取最新的数据也不是那么重要。如果有人需要等待几秒甚至几分钟才能看到最近的留言簿条目,那也不是世界末日。

当您有经常被访问的内容,但可以承受稍微陈旧(或非常陈旧)的内容时,您可以实现缓存来加快速度。缓存仅仅意味着拥有一个我们可以快速访问的结果的临时存储。数据库很慢,因为数据库主要关心数据完整性。您希望知道您的数据是安全的,存储在磁盘上,不会消失,并且可以通过任意查询进行访问。另一方面,缓存是短暂的——它们通常只是存储在内存中。他们的目标是不惜一切代价快速检索数据。例如,许多缓存如果填满了内存,就会开始丢弃数据。这没关系,因为如果缓存中不存在某个东西,我们可以去数据库重新获取数据。

简而言之,数据库关乎持久性和可靠性,而缓存关乎尽可能快地获取我想要的东西。缓存通常被实现为简单的键/值对,通常附有截止日期。也就是说,缓存存储的每一段数据都有一个指定的“键”例如,由于所有留言簿条目只有一个列表,我们可以为此使用一个缓存“键”。在应用中,这将被称为entrylist。但是,每个单独的留言簿条目可能最终会获得自己的缓存键(这样缓存就可以知道它是唯一的),这将包括条目的数据库 ID。缓存键通常只是普通的字符串,对于大多数缓存系统,内容可以是任何东西。缓存不做任何特殊的处理,它们只是在一个给定的键中存储你要求的任何东西,所以你必须小心不要用同一个键做两件不同的事情!

使用缓存的方式是,程序将首先确定对数据使用什么键。目标是能够很容易地从 URL 参数中推断出缓存键。程序首先检查缓存中是否有缓存键值。如果缓存包含该值,则使用该值。如果没有,程序就以“正常”的方式获取该值(即,通常从数据库中获取该值,可能还要进行一些额外的计算或操作),然后将该值保存到缓存中,并注明到期日期。然后,无论该值是来自缓存还是“正常”方式,该值都会在应用中使用。一旦过期(或者缓存中的值太满),缓存的值就会在下一次查询中消失,迫使程序以“正常”的方式获取一个新值。

img/487291_1_En_6_Fig1_HTML.jpg

图 6-1

具有本地缓存的双层架构

正如您所看到的,这种缓存与普通访问的方法确保了虽然缓存机制是首选,但非缓存机制也是可用的,并且该机制将填充缓存。

如上所述,数据库通常被认为是任何应用中速度较慢的部分,因为它必须非常小心地正确处理您的数据并永久保存这些数据。另一方面,缓存被认为是短暂的。如果我们只是想突然清空缓存,这不会影响页面的操作,因为它会返回到数据库并再次获取值。

如果一个站点负载很重,并且不断地请求相同的内容,即使缓存几秒钟也能大大提高速度,减少资源使用。我们之前在清单页面上对这个集群进行了负载测试,每秒钟可以处理大约 250 个请求。如果我们将该页面的结果缓存 10 秒钟,那么在这段时间内会减少 2,500 个数据库请求!

图 6-1 展示了我们如何修改我们的标准双层架构来添加一个缓存层。在这种架构中,每个单独的 web 服务器维护自己的结果缓存。这可能会导致 web 服务器之间的细微不一致,因为它们可能在不同的时间刷新了结果。但是,如果到期时间没有设置得太远,这些问题是最小的。此外,如果有问题,还记得我们在 5.6 节中讨论的负载平衡器“粘性”吗这将特定的用户与特定的服务器联系在一起,这意味着用户将始终在同一服务器上,这意味着他们将始终使用同一服务器缓存。此外,这使得缓存更有效,因为每个服务器只需缓存用户子集的数据。

img/487291_1_En_6_Fig2_HTML.jpg

图 6-2

具有全局缓存的双层架构

缓存什么以及缓存多长时间取决于应用。对于许多架构来说,有些部分可以缓存几分钟甚至几天,而有些部分只能缓存几秒钟或者根本不能缓存。缓存经常会导致意想不到的结果,所以在开发时最好包含一个可以用来完全关闭缓存的特性,以便查看缓存是否是问题所在。

如果保持所有服务器之间的缓存一致很重要,另一个可以考虑的架构是使用全局缓存,如图 6-2 所示。在这种体系结构中,不是每个 web 服务器维护自己的缓存,而是有一个它们都可以访问的共享缓存。这增加了一点网络延迟,因为所有的缓存调用都必须在网络上运行,但它增加了结果的一致性。

根据实现方式的不同,这种外部缓存也可能成为瓶颈。但是,有许多缓存服务器可以跨多个服务器,并在服务器之间平衡请求。尽管如此,这也增加了管理的复杂性。

几乎在所有情况下,我发现拥有一个单一的外部缓存服务很难设置和维护,几乎没有任何好处。在大多数情况下,将缓存放在每个 web 服务器上可以获得最大的可伸缩效率,即使这是以一点一致性为代价的,正如前面提到的,这可以通过使用负载平衡器的“粘性”来缓解

6.2 在应用中实现缓存

我们在这里要实现的缓存架构如图 6-1 所示,这既是因为它更容易实现,也是因为从长远来看它更容易管理。我们要做的是在template_node机器上执行配置更改,然后简单地关闭我们现有的webnodeX机器,并用从template_node复制的新机器替换它们。这比遍历每台服务器并进行更改要容易得多(也不容易出错)。

我们需要做的第一件事是安装缓存服务。我们将使用memcached作为我们的缓存服务,因为它易于运行、访问和管理。要安装并打开memcached,只需以 root 用户身份输入以下命令:

yum install -y memcached
systemctl enable memcached
systemctl start memcached

您还需要用于memcached的 PHP 扩展。这些必须被编译,所以我们需要安装更多的扩展(再次以 root 用户身份)。

img/487291_1_En_6_Fig3_HTML.png

图 6-3

Memcache 连接函数

yum install -y libmemcached
yum install -y php74-php-pecl-memcached

不要忘记memcached末尾的“d ”,因为还有另一个名为 just memcache的扩展,它不做我们想要的事情。注意,这也启用了 PHP 中的扩展。它为我们做到了这一点,但是如果你需要调整配置的 PHP 端,文件位于目录/etc/opt/remi/php74/php.d中。

现在我们需要重启我们的 PHP-FPM 进程来使用新的 PHP 扩展:

systemctl restart php74-php-fpm

接下来,我们需要修改我们的应用来创建到本地memcached服务的连接。因此,将图 6-3 中的代码添加到common.php中。代码中提到的11211号是memcached默认监听的端口。

您的代码使用缓存的方式如下:

  1. 创建一个缓存键来唯一标识缓存中的信息。

  2. 检查信息是否已经存在于缓存中。如果有,那就用吧。

  3. 如果信息不存在于缓存中,用慢的方式找到信息(例如,执行数据库查询)。

    img/487291_1_En_6_Fig4_HTML.png

    图 6-4

    重写list.php以使用缓存

  4. 获取信息,并使用具有未来过期时间的密钥将其存储在缓存中(在这种情况下,我们将过期时间设置为从设置时起 10 秒)。

接下来,我们将更新list.php函数来使用缓存。图 6-4 显示了如何重写list.php以使用缓存。这只是在$result中存储查询结果的脚本的顶部。实际的 HTML 输出代码保持不变。

6.3 重新映像集群

现在我们需要将它部署到我们的集群中。为此,我们需要首先对template_node进行另一次快照备份,然后对集群中的每台webnodeX服务器执行以下步骤:

  1. 转到节点平衡器,从配置中删除webnodeX服务器。

  2. 关闭电源并删除webnodeX服务器(可以从节点屏幕的“设置”选项卡中进行删除)。

  3. 基于我们的template_node备份创建一个新的webnodeX服务器(使用相同的名称)(确保添加一个私有 IP)。如有必要,打开电源。

  4. 将新的webnodeX服务器添加到节点平衡器配置中。

对于生产系统,这并不总是进行代码或系统更新的最佳方式。我们将在第十二章中介绍更多的方法。这种方法简单地删除所有的webnodeX服务器,并用新的替换它们。这是一个非常干净的机制,尽管您可能希望缓慢地执行它,以确保您的站点在重新映像服务器时保持运行。

6.4 测试我们的缓存架构

一旦您的新云集群启动并运行,就该测试新架构了,看看我们是否获得了任何性能提升。

对于这个设置,我在单独的服务器和平衡集群上运行 ApacheBench。由于不再依赖数据库来解决瓶颈问题,单个服务器每秒能够处理超过 800 个请求!

此外,由于数据库没有瓶颈,性能几乎可以线性扩展。线性缩放意味着您添加的每个框都可以为您带来相同的性能提升。在这种情况下,使用一台服务器,我们的性能是每秒 800 个请求,两台服务器每秒产生大约 1,500 个请求,而在三台服务器上,我们能够持续地每秒处理超过 2,300 个请求!

请注意,如果您没有看到类似的性能提升,请检查您的平衡器配置,以确保您没有在任何时候打开会话粘性,因为这会将您的 ApacheBench 会话限制在一台服务器上。此外,在“设置”选项卡上,确保“客户端连接节流”也没有打开。

图 6-5 显示了 ApacheBench 在完整集群上的输出。

因此,我们了解到,缓存不仅提高了应用的速度,还提高了应用的可伸缩性。因为我们只需在缓存过期时命中数据库,所以数据库的速度现在相对不重要。事实上,即使我们再次加速数据库(就像我们在第五章的结尾所做的那样),它对我们的总效率的影响也相对较小,因为它很少被使用。

不利的一面是,如果你实际使用这个应用,你会发现在你发布留言簿条目后,它不会立即显示在网站上。事实上,如果您快速重新加载页面,您可能会发现它会出现和消失,这取决于您所在的服务器何时刷新其缓存。

这可以通过多种方式来缓解。首先,您可以减少数据缓存的时间。在这个基准测试中,缓存 1 秒钟还是 10 秒钟没有什么区别。因此,只需将缓存过期行更改为以下内容(即,将过期时间设置为1秒而不是10)即可让应用快速刷新,而不会显著影响我们测试的这些大规模负载的性能:

$expiration_seconds = 1;

img/487291_1_En_6_Fig5_HTML.png

图 6-5

缓存配置的 ApacheBench 输出

更重要的是,如果您将会话绑定到特定的服务器,您实际上可以告诉服务器清除特定事件的单个键甚至整个缓存。因此,在create.php的末尾,我们可以添加下面几行来清除服务器上的列表缓存:

$cache = getCache();
$cache->delete("entrylist");

或者,如果一个应用足够复杂,需要删除大量的键,代码可以用$cache->flush();清除整个缓存。

无论如何,正如你所看到的,缓存架构会使你的应用变得稍微复杂和难以管理,但从通常戏剧性的性能和可伸缩性提升来看,它们通常是值得的。

img/487291_1_En_6_Figa_HTML.jpg缓存调试提示

缓存虽然非常有益,但也带来了一系列问题。为了更好地调试网页,最好总是有一组参数可以传递给应用,让它关闭缓存。例如,在我自己的许多应用中,在 URL 中传递一个no_cache=1会关闭缓存。当问题被报告时,这通常是我第一个去的地方。

以下是您可能遇到缓存问题的一些迹象,以及如何解决这些问题:

  • 问题:您的应用正在输出旧的内容,即使数据库中有更新的内容。

    诊断:缓存中有过时的内容。

    解决方案:让您的内容更快过期,或者提供额外的缓存密钥信息,让缓存知道您何时需要新数据。

  • 问题:你的应用在给定参数的情况下发出不适当的数据。

    诊断:这种情况经常发生在你的缓存键不够具体的时候。例如,如果内容以错误的语言出现,这可能意味着您需要附加当前语言作为缓存键的一部分。

    解决方案:给你的缓存键添加更多的参数,以确保你真正用一个唯一的键识别每一个唯一的内容,或者你可能决定这个内容太具体而不能被缓存。

  • 问题:同一个页面每次重新加载都会吐出不同的内容。

    诊断:缓存在每台服务器上有不同的内容,这取决于它被访问的时间。

    解决方案:有几种方法可以解决这个问题。您可以(a)减少到期前的时间量,(b)增加负载平衡器上的“粘性”,以确保同一个人总是访问同一台服务器(从而访问同一缓存),(c)利用全局(或同步)缓存而不是本地缓存,以及(d)添加额外的缓存关键参数,以更好地协调用户从缓存中获取的数据。

  • 问题:缓存并没有像你想象的那样加速你的应用。

    诊断:要么你从未命中你的缓存,要么你没有缓存正确的东西。

    解决方案:因为有很多方法会出错,所以有很多方法可以修复它。当每个用户访问不同的数据集时,通常会发生这种情况,因此不会从缓存中提取任何内容。这可以通过增加缓存大小和/或智能地将可能被访问的数据预加载到缓存中来解决。您可能还需要增加数据的到期时间。但是,您可能需要对数据进行大量的后处理,这比实际查询花费的时间要长。

七、数据库复制

有些东西根本无法缓存。即席报告、最新的更改以及访问模式分布在大量不相关页面上的站点都很难使用缓存进行优化。对于这样的工作负载,你可以部署一个更大的数据库服务器,但是最终这些也会受到限制。

因此,许多应用架构都包括数据库复制,其中有多个数据库服务器为请求提供服务。

7.1 数据库复制的类型

根据您的需要,有许多类型的数据库复制。复制的基本类型包括

  • 故障转移复制:在这种配置中,复制的服务器不帮助加载,但是它们确保如果主数据库服务器停机,有一个具有最新信息的数据库可以接管。

  • 主/副本复制:在这种配置中,主数据库是唯一具有读/写权限的数据库。副本服务器接收记录在主服务器上的数据(或稍后接收),但它们是主数据库的只读副本。所有更新都转到主数据库,但查询可以转到主服务器或任何副本服务器。这也称为“主/从”复制,其中副本数据库被视为“从数据库”

  • 多主复制:在这种配置中,所有数据库都被视为同等的“主”数据库,可以在其中任何一个数据库上执行写操作。然后,对任何给定数据库的写入将与集群的其余部分同步。

本章将集中讨论主/副本复制,因为它最容易实现,实际问题最少,而且用最少的努力获得最多的结果。多主控复制很少使用,因为它很难设置、维护和保持高效,而且很少有数据库支持它。即使受到支持,多主控复制也经常会引入新的难以解决的问题,例如数据冲突(即,当冲突的数据被提交到两个不同的服务器上时)。因此,为了保持简单,本书将集中讨论主/副本配置。

图 7-1 显示了典型主/副本架构的概念视图。

7.2 复制 PostgreSQL 数据库

这些年来,PostgreSQL 的复制系统在功能和易用性方面都有了很大的进步。虽然使用起来并不困难,但需要一些解释才能理解。

内置的 PostgreSQL 复制系统使用一种称为日志流的技术进行复制。为了保证数据的一致性,PostgreSQL 创建了一个预写日志,简称 WAL。基本上,PostgreSQL 将要做的更改写入 WAL,然后实际执行更改。这意味着,如果数据库服务器在更新过程中关闭,它会记录正在做的事情,并在重新启动时简单地完成操作。

img/487291_1_En_7_Fig1_HTML.jpg

图 7-1

主/副本数据库架构图

有趣的是,这正是复制服务器也需要知道的信息。因此,要实现数据库复制,PostgreSQL 只需将 WAL 文件发送到复制服务器,复制服务器同样会实现更改。这种类型的复制被称为 WAL 流。

为了在我们的集群中实现这一点,我们需要配置我们的主数据库,以便接收复制连接。以 root 用户身份登录dbmaster,编辑文件/var/lib/pgsql/data/postgresql.conf,设置如下设置:

wal_level = hot_standby
wal_keep_segments = 32
max_wal_senders = 4
hot_standby = on

如果您使用的是 PostgreSQL 的更高版本(9.4 或更高版本),您还需要设置:

max_replication_slots = 4

但是,该设置将破坏 CentOS 7.2 附带的 PostgreSQL 版本,我们将在本书中使用该版本。

这些配置更改完成了几件事:

  • wal_level调整 PostgreSQL 的“预写日志”(即 WAL),以便它保留足够的细节来发送副本服务器需要的所有内容。

  • 在使用后保留足够的数据,以便副本服务器在落后时仍能获得数据。我们将它设置为 32,这是一个相当保守的设置。这允许新的副本有大量时间来完全同步,并防止较小的网络故障和速度减慢导致服务器不同步。

  • max_wal_senders应该至少设置为副本服务器的数量加 2(并且,如果您决定安装较新的 PostgreSQL,您可能需要将max_replication_slots设置为相同的值)。

  • hot_standby = on允许副本服务器响应查询。

接下来,我们需要向 PostgreSQL 数据库添加一个 replicator 用户。键入psql -U postgres以访问数据库,然后键入以下内容以创建用于复制的用户replicator(全部在一行中):

CREATE ROLE replicator WITH REPLICATION
PASSWORD 'mypassword' LOGIN;

然后键入\q退出。

接下来,我们需要授予远程服务器打开到该服务器的复制连接的权限。在/var/lib/pgsql/data/pg_hba.conf中添加以下一行:

host replication replicator all md5

现在我们需要重新启动数据库,以便新设置生效:

systemctl restart postgresql

我们的服务器现在完全可以开始接受复制请求了。现在我们可以设置副本服务器了。

为了设置 PostgreSQL 副本服务器,我们需要做的第一件事是创建一个新的 Linode 节点,通过使用标准过程复制我们的模板节点来运行它。在本练习中,将新节点命名为dbreplica。您还需要为这台机器添加一个私有 IP 地址并记下来(在本书的其余部分,我们将把它称为DB.REPLICA.PRIVATE.IP)。

现在,启动dbreplica并以root的身份登录。

如果您按照说明操作,这台机器应该没有运行 PostgreSQL。但是,如果它正在运行,您可以使用systemctl stop postgresql将其关闭。一旦 PostgreSQL 关闭,我们需要清除现有的 PostgreSQL 安装。使用以下命令完成该操作:

rm -rf /var/lib/pgsql/data/*

现在,我们需要从主系统请求一个初始二进制备份,作为复制的起点。这是通过pg_basebackup命令完成的。要生成这个初始备份起点,首先切换到 postgres 用户,如下所示:

su - postgres

接下来,发出以下命令(全部在一行中):

pg_basebackup -x -U replicator -h DB.MASTER.PRIVATE.IP
 -D /var/lib/pgsql/data

它会要求您输入密码,然后从 master 数据库复制整个 PostgreSQL 实例,包括配置文件。接下来,在这一步完成后,您需要调整配置文件。我们在第五章中建立的postgresql.conf文件有一个包含服务器私有 IP 地址的命令listen_addresses。不幸的是,因为它是从主数据库复制的,所以它目前拥有主数据库的私有 IP 地址。要解决这个问题,只需打开/var/lib/pgsql/data/postgresql.conf并将listen_addresses配置改为:

listen_addresses = 'localhost,DB.REPLICA.PRIVATE.IP'

成功结束后,您需要告诉 PostgreSQL 该服务器将被用作热备用。

这是通过告诉服务器在启动时进入“连续恢复模式”来实现的。为此,我们创建了包含以下内容的文件/var/lib/pgsql/data/recovery.conf(最后两行应该在同一行中键入):

standby_mode = 'on'
primary_conninfo = 'host=DB.MASTER.PRIVATE.IP port=5432
 user=replicator password=mypassword'

一旦这一切就绪,您需要像这样退出回到 root 用户:

exit

现在您再次成为 root 用户,我们需要重新打开 PostgreSQL 数据库,并确保它在重新启动时会自动启动:

systemctl start postgresql
systemctl enable postgresql

我们还需要确保防火墙中有一个洞来接收数据库连接:

firewall-cmd --add-service postgresql
firewall-cmd --add-service postgresql --permanent

此时,您的系统已经启动并作为副本服务器运行了!

要验证这一点,请运行以下命令,该命令将列出所有正在运行的 PostgreSQL 进程:

ps afxw|grep postgres

列表中的一个进程应该在输出中包含单词recovering或 startup。这意味着数据库是启动的、活动的,并以主服务器的 WAL 日志为基础。

您还可以检查日志文件,这些文件位于目录/var/lib/pgsql/data/pg_log中。日志文件的结尾应该类似于“数据库系统已准备好接受只读连接”和“流式复制已成功连接到主数据库”

关于 Postgresql 复制的几点注意事项

PostgreSQL 实现称为“实例”,可以包含任意数量的数据库。如果您遵循本书中的说明,那么您的 PostgreSQL 实例只包含一个数据库(实际上是三个,因为 PostgreSQL 总是安装有一对模板数据库,template0template1)。使用createdb命令行程序或者在运行psql时发出create database指令来创建一个新的数据库是非常容易的。

在任何情况下,请记住,由于 PostgreSQL WAL 文件是系统级功能(即 WAL 文件由整个数据库实例共享),PostgreSQL 流复制复制整个 PostgreSQL 实例,而不仅仅是单个数据库。

7.3 设置应用以利用主/副本复制

应用本身已经构建为利用只读副本。如果你记得,我们实际上有两个连接函数,getReadWriteConnection()getReadOnlyConnection()。现在,它们都指向同一个服务器。为了使用我们新的只读副本,我们所要做的就是改变getReadOnlyConnection()函数中的连接信息,它会将所有只读连接转移到副本服务器上。

我们所要做的就是改变getReadOnlyConnection()函数中的host参数,使其指向DB.REPLICA.PRIVATE.IP

一旦完成,我们只需要重新部署我们的代码。通常,我们可以通过部署到template_node然后从它重新创建webnode服务器,或者通过将它单独部署到每个服务器来实现。

7.4 添加更多 PostgreSQL 副本服务器

如果分离出单个副本服务器并不能提高您需要的性能,那么您实际上可以根据需要拥有多个副本服务器。为此,您可以复制模板节点并重复 7.2 节中的过程,或者直接复制副本服务器。

复制副本服务器不像复制 web 节点那样自动,但是可以节省一些步骤。为此,您需要首先在当前的dbreplica节点上启用备份,然后从dbreplica的备份创建新的复制副本实例。创建每个副本实例后,您需要ssh到新的副本服务器,并将/var/lib/pgsql/data/postgresql.conf中的listen_addresses值设置为它自己的私有 IP 地址,因为默认情况下它将被设置为dbreplica的值。这样做之后,您将需要用systemctl restart postgresql重启 PostgreSQL。

在创建了大量数据库副本后,需要修改代码以利用数据库。如果我们可以为副本数据库创建一个负载平衡器,然后将所有应用代码指向负载平衡器,那就太好了。不幸的是,Linode 目前没有能力创建内部负载平衡器(即只接受私有网络上的请求的负载平衡器),因此我们必须提供自己的负载分配机制。我们将在应用代码中模拟这个特性,在获取数据库连接时随机选择一个服务器。因为我们没有负载平衡器,所以我们必须在您的应用代码中对服务器列表进行硬编码,并且添加新的副本服务器也需要修改应用代码,并将该代码刷新到所有 web 服务器。

为了理解如何修改应用代码,假设我们现在有三台副本服务器,它们的私有 IP 地址分别是DB.REPLICA.PRIVATE.IPDB.REPLICA2.PRIVATE.IPDB.REPLICA3.PRIVATE.IP。为了让我们的只读连接在这些之间循环,我们将根据图 7-2 重写我们的getReadOnlyConnection()函数。

一旦这段代码在我们的集群中就位,我们所有的只读请求将会在多个副本服务器之间进行负载平衡,如图 7-3 所示。这意味着我们唯一的瓶颈是读写数据库请求。无论如何,大多数应用都是由只读请求控制的,因此读写请求出现瓶颈通常不成问题。

img/487291_1_En_7_Fig3_HTML.jpg

图 7-3

多副本数据库配置图

img/487291_1_En_7_Fig2_HTML.png

图 7-2

连接到一组副本服务器

7.5 跨数据中心复制

对于非常大的应用,有时您需要比单个数据中心更好的地理覆盖范围。此外,超关键应用可能需要拥有多个数据中心所带来的可靠性,这样,如果一个数据中心出现故障,应用至少可以继续部分运行。

这种设置的创建有点复杂,所以本书不会像其他体系结构那样给出一步一步的方法。但是,要实现此功能,您需要执行以下基本步骤:

  1. 更改dbmaster,使其listen_address设置为*。因为它将接收来自其他数据中心的请求,所以它也必须监听公共 IP 地址。

  2. 防火墙dbmaster防止不受欢迎的第三方访问。

  3. 使用 SSL 加密到 PostgreSQL 的连接(参见后面如何启用它)。

  4. 在新的数据中心部署一个“主副本”,它将是dbmaster的副本,但也将是网络上其他副本的主副本(如果需要的话)。这仍然是只读的,但它是流向其他副本的副本。

  5. 将您的 web 应用的副本部署到新的数据中心,该数据中心具有用于读写连接的公共 IP 地址dbmaster,以及用于只读连接的本地副本的私有 IP 地址列表。

在这个过程的最后,您的集群架构应该看起来如图 7-4 所示。

随着您的部署变得越来越复杂,您对自动化部署处理的需求也在增加。有关自动部署的更多信息,请参见第十二章。

img/487291_1_En_7_Fig4_HTML.jpg

图 7-4

多站点架构图

如果您需要对本地设置中的数据库进行读写访问,那么配置会变得更加困难,因为您现在必须管理系统之间如何进行同步,以及如果系统之间的网络连接中断会发生什么。在这种情况下,您通常将本地(非共享)和全局(共享)数据分离到单独的数据库实例中。对于非共享数据,您需要在每个位置都有一个主数据库。对于共享数据,您将按照类似于图 7-4 的方式进行设置。

img/487291_1_En_7_Figb_HTML.jpg在 Postgresql 上启用加密

要在 PostgreSQL 上启用加密,您需要以 root 用户身份转到目录/var/lib/pgsql/data。在那里,您需要编辑postgresql.conf并添加以下行:

ssl = on

接下来,您需要生成一个 SSL 密钥和自签名证书。在同一个目录中,发出以下命令(全部在一行中):

openssl req -nodes -new -x509 -keyout server.key
  -out server.crt

这将要求您填写一份简短的表格,该表格将被编码到您的证书中。然后,您需要对生成的文件设置权限:

chown postgres:postgres server.key server.crt
chmod 600 server.key

如果此时重启数据库,服务器将允许 SSL 连接,但不要求SSL 连接。要要求 SSL 连接,您可以将pg_hba.conf中的任意或所有host行改为hostssl

完成所有更改后,您可以使用以下命令重新启动数据库:

systemctl restart postgresql

7.6 数据分片

数据库可伸缩性的另一个选项是数据分片。数据库分片在概念上相对简单——它只是意味着您以这样一种方式对数据进行分区,即不是所有的数据都依赖于同一个数据库系统。

例如,您可以共享您的数据,以便姓名以“A”和“B”开头的所有客户都在一个数据库中,姓名以“C”和“D”开头的所有客户都在另一个数据库中,依此类推。只要系统能够容易地确定它需要查询哪个数据库,您就可以用您喜欢的任何方法对数据进行分区。主要的一点是,数据本身被分散到不同的数据库系统,而不是所有的东西都由同一个数据库系统管理。

在这样的系统中,通常要么是应用负责分片,要么是有一个中间层负责将连接切换到适当的数据库。一些更新的工具已经被开发出来,比如pg_shard,它的目标是作为一个数据库插件无缝运行。

在任何情况下,分片都会带来一系列数据管理和数据完整性问题。数据库的一个目的是确保数据的一致性。从本质上说,分片消除了数据库为实现规模而提供的许多保护。因此,重要的是要确定你知道你为什么想要切分和你想要如何切分,以最小化风险。

例如,如果您将客户划分到不同的数据库,那么您也应该划分相关的记录,这样客户就可以由一个数据库提供服务,他们的记录也可以一起管理。想象一下,如果您必须从备份中恢复记录,但是一些相关的记录在不同的数据库系统上!

分片需要大量的工作,需要大量的规划,并且严重依赖于您的使用和工作负载的细节,因此很难笼统地谈论它。尽管如此,如果您正在寻找更多的方法来扩展您的数据库,分片绝对是一个选择。分片还可以与其他方法结合使用,如主副本复制或多主复制,但同样,它需要很多额外的爱、关心和管理才能使其正常工作。

如果您的登录组不共享任何数据,分片会更容易。例如,假设你建立了一个电子邮件营销系统。不同的客户很少在这样的系统上共享任何数据。所以,把他们的记录完全分开是没有问题的。

您可能会运行几个完全独立的云,每个云拥有不同的客户群。来自客户 A、B、C 和 D 的登录将被转移到云 1,来自客户 E、F、G 和 H 的登录将被转移到云 2,依此类推。如果这些云都是完全独立管理的,这样的设置还可以实现一定程度的灾难缓解,因为您不太可能同时让所有数据中心完全停机。

八、使用 CDN

到目前为止,我们对云应用的可伸缩性工作的重点是动态内容的服务(即从数据库查询中提取的内容)。然而,对于许多网站来说,动态内容实际上是系统中最小的部分。事实上,在任何网站上,大多数请求甚至不是针对动态内容,而是针对静态内容——图像、样式表和 JavaScript。因此,在考虑如何在云中扩展您的应用时,重要的是不要忘记扩展您的静态资产。

您可以通过创建更多的前端 web 服务器来扩展您的静态资产,因为它们通常是服务静态资产的服务器。然而,这可能会变得更昂贵(您必须保持更多服务器在线)并且更难管理(更多节点意味着更多管理)。

扩展静态资产实际上比扩展动态资产容易得多,因为您不必担心当它们改变时会发生什么。因此,有些服务是专门为扩展静态资产而构建的。扩展静态资产交付的服务被称为“内容交付网络”,也称为 CDN。

8.1 CDN 是如何工作的?

大多数 cdn 的工作方式是,它们有一个全球分布式内容服务器网络。假设您有一个 10MB 的图像,您希望由 CDN 提供服务。通常,如果该用户向您的服务器请求图像,那么您的服务器的处理时间和带宽将会被发送 10MB 的图像所限制。如果用户住在大洋的另一边,这是一个额外的问题,因为你的服务器会浪费大量资源来管理一个又慢又吵的连接。

CDN 允许你做的是将你的用户重定向到图片在 CDN 上的 URL。CDN 第一次看到 URL 时,通常对图像一无所知,但它有规则告诉它如何在您的服务上找到原始图像。从那时起,在 CDN 第一次获取图像后,每当有人请求该图像时,CDN 将设法将图像交付给用户,而完全不通过您的服务器。

此外,大多数 cdn 的服务器位于许多不同的物理位置。这些位置被称为“存在点”(pop),那里的服务器通常被称为“边缘服务器”(正因为如此,cdn 通常被称为“边缘缓存”)。在各种 pop 中使用边缘服务器不仅允许 CDN 通过大量服务器提供可扩展性,边缘服务器还允许 CDN 提供与用户物理距离很近的服务器。如果用户可以从附近的服务器获得大量数据,而不是漂洋过海去检索数据,这将大大提高用户的体验。

CDN 为服务静态资产提供了本质上无限的规模——对于任何知名的 CDN,您都不必担心它们的网络溢出。只要资产没有变化,CDN 将完全能够处理对该资产的任意数量的请求。

通常,CDN 的主要成本是带宽。然而,许多 cdn 的带宽成本比云服务器低。现在,有了 Linode,你就必须有一个非常活跃的网站来超越你的带宽。尽管如此,如果你这样做了,如果带宽是由 CDN 提供的话,你需要支付的费用会稍微少一些。在任何情况下,即使您的服务器有足够的带宽,cdn 提高可扩展性的好处通常也是值得的,因为您不需要连续运行服务器来处理可能出现或可能不出现的流量。你只需要为你实际使用的带宽付费。

8.2 设置简单的 CDN

幸运的是,大多数 cdn 非常容易建立。在本书中,我们将使用亚马逊的 CloudFront 作为 CDN,尽管存在许多其他选项(CloudFlare、StackPath、CDN77 和 Fastly,仅举几例)。cdn 的好处在于,由于它们提供的服务相当透明,很容易混合和匹配 cdn 的服务提供商。

CloudFront 的工作方式非常简单:

  1. 您在主站点上托管所有静态内容。这是你内容的“官方”存储库。

  2. 您在 CloudFront 上创建一个主机来提供您的内容(通常会有一个名称,比如xyzabc.cloudfront.net)。

  3. 你告诉 CloudFront 服务器你的主站点的 URL。

  4. 每次从站点链接到静态内容时,都是使用 CloudFront URL 链接,而不是链接到自己服务器上的内容。例如,如果你的图片在 http://mysite.example.com/mydirectory/myimage.png, 链接到它,你可以使用 URL http://xyzabc.cloudfront.net/mydirectory/myimage.png. CloudFront 将从你的配置中知道如何找到myimage.png,并将它缓存并提供给你的用户。

CloudFront 第一次接收到对图像的请求时,它会到您的站点获取图像。接下来,任何未来的请求都将由 CloudFront 使用请求图像的用户附近的服务器直接提供服务。

让我们看看如何使用 Amazon AWS 和 CloudFront 来实现这一点。你需要做的第一件事是在 http://aws.amazon.com注册 AWS。我想你可以自己完成注册过程。

AWS 有大量可用的服务,因此仪表盘不会列出所有服务,而是让您搜索一个。在搜索栏中输入“CloudFront ”,它将允许您进入 CloudFront 仪表板。

因为这是你第一次使用 CloudFront,它会给你一个按钮,上面写着“创建发行版”用 CloudFront 的术语来说,发行版就是一个 CDN 复制器服务。

点击“创建发行版”后,CloudFront 会询问您的交付方式,并让您选择“网络”或“RTMP”RTMP 是用于传输大量视频内容的协议。然而,由于我们只是分发像图像和样式表这样的基本资产,我们将选择从基本的 Web 交付开始。

然后亚马逊会询问你的销售细节,如图 8-1 所示。实际上,在这些选项下面有许多附加选项,但是唯一必需的字段是“原始域名”,这是您希望 CloudFront 从中获取资产的站点的 DNS 主机名。这个不能是一个 IP 地址,但必须是某种 DNS 主机名。如果您还没有为您的应用设置 DNS 主机名,您可以使用 Linode 自动生成的主机名。如果你进入 Linode 的节点平衡器屏幕,点击你的平衡器,它会给你一个(很长,可能分成两行)内部生成的平衡器主机名(类似于nb-BALANCER-PUBLIC-IP-ADDRESS.dallas.nodebalancer.linode.com)。填写“原始域名”后,向下滚动到页面底部。

img/487291_1_En_8_Fig2_HTML.jpg

图 8-2

CloudFront 发行版列表

img/487291_1_En_8_Fig1_HTML.jpg

图 8-1

创建 CloudFront 发行版

在页面底部,有一个“创建分发”按钮。点击此按钮后,您将进入一个显示新发行版的发行版列表,类似于图 8-2 。这个屏幕最重要的部分是“域名”,它向您展示了如何访问您新创建的发行版(您可能需要调整字段的大小,以便看到完整的名称)。它还会给你一个状态,从“进行中”到“已部署”需要 5 分钟到 1 小时的时间一旦部署完毕,您就拥有了一个正在运行的 CDN!

8.3 使用您的 CDN

一旦状态更改为“已部署”,该域名现在将完全反映您的原始站点。然而,它只是你网站的一个静态版本。如果你的网站发生变化,除非你要求,否则 CloudFront 不会更新它的资产。

假设 CloudFront 给你的域名是xyzabc.cloudfront.net。这意味着如果你去 http://xyzabc.cloudfront.net/list.php ,它会显示你的留言簿列表。但是,如果您随后去修改您的留言簿列表,这些更改将不会反映在您的 CDN 上,它会将所有内容视为静态资产。这就是为什么在大多数情况下,cdn 只提供静态资产——图像、样式表、JavaScript 等等。

因此,让我们修改应用,通过 CDN 只提供我们的样式表,而不是通过 CDN 访问整个站点。我们要做的就是修改common.php的一行。在getHeader()函数中,我们只需将<link>标签改为:

<link rel="stylesheet" href="http://xyzabc.cloudfront.net/guestbook.css" />

一定要用你的发行版的域名替换xyzabc.cloudfront.net!一旦在所有服务器上部署完毕,您的样式表现在将由 CDN 提供服务。

img/487291_1_En_8_Fig3_HTML.jpg

图 8-3

从 CDN 中删除内容

这意味着您的服务器几乎不会再提供样式表了。偶尔,CDN 可能会使其缓存的一些内容过期,但这取决于 CDN。CDN 将为自己优化存储多少数据,存储多长时间,以及重新请求原始文件的频率。对于更高级的应用,您可以配置 Apache 来提供一个Expires: HTTP 头或一个Cache-Control: HTTP 头,以指定 CDN 应该保存您的数据的最长时间。

然而,假设您部署了一个新版本的应用,它实际上有一个更新的样式表。这意味着 CDN 为您提供的版本现在已经过时了——它的缓存中可能仍然有旧版本。这根本不是问题,只是意味着你需要手动告诉 CDN“作废”你的内容,这样它才会再次请求。

要使 CloudFront CDN 上的内容无效,首先要单击 CloudFront 发行版的 ID。这将把您带到一个信息页面,描述在您的发行版上设置的所有选项。在最右边,有一个名为“无效”的标签单击此选项卡,然后单击“创建失效”这将把你带到一个类似于图 8-3 的屏幕。在“对象路径”字段中,只需清除那里的任何内容,只需键入/*即可使所有内容无效。虽然 CloudFront 允许细粒度的访问来使 CDN 无效并从 CDN 中删除特定的项目,但我发现,在大多数情况下,简单地使整个事情无效更容易、更干净。点击“无效”按钮使其无效。

失效可能需要几分钟才能扩散到 CloudFront 的所有服务器,但很快所有的服务器都会为您的新内容提供服务。当失效的“状态”变为“已完成”时,您将知道它何时完成

8.4 用 CDN 缓存整个站点

除了缓存单个内容片段,如样式表、JavaScript 和图像之外,现代 cdn 实际上还允许您缓存整个网站,为您提供跨互联网的即时可伸缩性。虽然这对于像我们这样的 web 应用不适用,但如果你有一个基本的网站,这可以让你的网站立即扩展到无限数量的用户,几乎不需要任何成本或额外的配置。

让我们来看看如何用 CloudFront 实现这一点。为了让 CDN 直接为您的网站服务,您需要将您的主站点的 DNS 指向 CDN 的服务器。

你可能认为你可以将你的网站的 A 记录设置为 CDN。然而,cdn 通常不会给你其主机的 IP 地址。其原因是 CDN 有多个 IP 地址用于 CDN(每个 PoP 一个),它们通常依赖 DNs 查找来决定将哪个 IP 地址分配给哪个客户端(即,它将为客户端分配一个在地理上靠近它们的 IP 地址)。这意味着我们不能仅仅在 DNS 中指定 CDN 的 IP 地址来将网站指向它。

相反,cdn 通常通过 DNS CNAME 记录来处理这类事情。CNAME 是一个“规范名称”——它告诉浏览器对 DNS 查询使用不同的名称,并将该名称的结果用于我们自己的 DNS 查找。如果您将www设置为 CloudFront 主机的 CNAME,那么 CloudFront 仍然可以使用自己的 DNS 机制为每个客户端分配正确的 IP 地址。

然而,这产生了另一个问题。CDN 需要知道您可能通过哪个主机名访问它。也就是说,如果您将www.example.com命名为abcxyz.cloudfront.com,CloudFront 需要知道当它的一台机器接收到对www.example.com,的请求时,它应该提供与abcxyz.cloudfront.com相关的缓存。

这是通过 AWS 中的分发设置完成的。这些设置位于发行版的“常规”选项卡下。点击“编辑”并寻找一个名为“备用域名(CNAMEs)”的字段您可以在这里输入您为该发行版命名的任何主机名(每行一个或用逗号分隔)。

然而,这带来了一个问题,因为 CDN 需要一个地方来检索您的数据,而您只是将您的 DNS 指向 CDN,而不是您自己的服务器!要解决这个问题,您只需设置一个内部 DNS 名称,CDN 从这个名称中提取(您可以将这个名称称为www2.example.comwww-internal.example.com ),然后将www的 DNS 设置为指向 CDN(注意,我们使用“内部”并不是指只对我们可见,因为它在公共互联网上与主站点一样多,而是表示它不是我们将最终用户导向的目的地)。

这样做,CDN 允许无限增长的访问者访问你的网站。

img/487291_1_En_8_Figa_HTML.jpg处理裸域名

从技术角度来说,您不能为根级域名创建 CNAME。你可以为www.example.com,做 CNAME,但不能为example.com做。虽然您的 DNS 服务器可能允许这样做,但这是违反规范的,可能会给不希望这样做的客户端带来各种奇怪的问题。因此,您需要确保有一种机制将您的裸域名(即example.com)重定向到您的 CNAMEd 主机(即www.example.com)。

为了解决这个问题,许多 DNS 提供商提供重定向服务,自动将对裸域名的所有请求重定向到www主机。如果你的 DNS 提供商不提供这项服务,wwwizer.com的好心人会免费为你提供这项服务。本质上,如果您将空白域的 A 记录指向174.129.25.170,它将自动重定向到前面有www的同一个域。

有关该服务的更多信息,请访问 http://wwwizer.com/naked-domain-redirect还要记住,和任何过于廉价的服务一样,你应该采取适当的谨慎措施。让他们重定向你的流量可以简化事情,但让一个你没有合同的第三方为你重定向你的流量也会有问题。

8.5 将 CloudFront 放在整个应用的前面

使用我们的应用,内容是动态的。这使得在它前面放置一个像 CloudFront 这样的 CDN 变得困难(但是,正如我们将看到的,并非不可能)。使用 CloudFront 带来了一个问题——我们如何防止人们查看陈旧的内容?

事实上,大多数 cdn 可以像我们处理本地缓存一样被处理。我们可以简单地为我们的内容设置一个最大到期日期。

在 CloudFront 上,如果你查看你的发行版,你会看到一个“行为”标签。点击这个,你会看到一个单一的,默认的行为。这些行为允许您在服务器的不同内容路径上设置不同的设置。对于这个例子,我们只需要编辑已经存在的那个。选择当前行为,然后点按“编辑”

如果向下滚动,您将看到一组 TTL(生存时间)值,以秒为单位。要更改这些值,首先将“对象缓存”设置从“使用原始缓存头”更改为“自定义”如果您不希望内容超过 5 秒,请将“最大 TTL”设置为 5。这意味着在重新查询内容之前,缓存将只保留内容 5 秒钟。因此,内容可能有点陈旧,但不会太陈旧。

这就引出了另一个问题 CDN 将如何处理向服务器发送表单?这也可以通过行为设置来处理。在“允许的 HTTP 方法”下,选择包含所有 HTTP 方法的选项。CDN 将只缓存 GET 和 HEAD 请求,但会将所有其他请求直接转发到您的服务器。

此外,默认情况下,CloudFront 不会将 cookies、HTTP 头或查询字符串转发给服务器。这是为了减少它必须发出的源请求的数量,以及它必须缓存的对象的数量。然而,在我们的应用中,您看到的内容是基于查询字符串的。因此,您需要将“查询字符串转发和缓存”设置为“全部转发,基于全部缓存”,以使应用正常工作。

所有这些设置完成后,单击“是,编辑”保存行为更改。

现在,您可以转到您的 CloudFront 发行版的 URL,并像使用常规网站一样使用它。您也可以按照 8.4 节中的说明,让用户通过您的网站自己的主机名访问它。

在您疯狂地使用这样的应用设置之前,请记住所有的缓存设置都是一种平衡。如果您的缓存使用了太多的键(即路径、头、查询字符串、cookies),那么您的大部分内容将被传递到服务器(即没有被缓存),并且您不会获得性能提升。事实上,这只会增加开销和成本,因为你必须支付高速缓存的带宽和服务器的带宽。但是,如果缓存设置过于宽松,用户将会看到过时的数据,或者更糟糕的是,看到其他人的数据!例如,如果您的内容是基于 cookie 进行个性化的,但是您没有使用 cookie 作为缓存键的一部分,那么如果 Jim Bob 请求了一个页面并将其放入缓存,当 Jane Doe 请求相同的页面时,她将获得 Jim Bob 的页面!

那么,拥有高度个性化 web 应用的人如何更好地利用 CDN 呢?答案就是把你的应用翻个底朝天。

8.6 彻底改变你的应用

我们在上一节中遇到的问题是向用户交付高度定制的页面,同时仍然充分利用缓存。拥有定制页面通常意味着它们是不可缓存的。这种困境可以通过为您的网站采用“由内向外”的页面架构来解决。

历史上,web 应用是通过让服务器生成页面来构建的。当用户导航到一个 URL 时,服务器获取一个模板,将其与用户数据结合,并添加页面内容以生成最终的 HTML 页面。然后,这个 HTML 页面作为一个整体返回给用户。

这种架构运行良好有多种原因:

  • 大多数 web 应用框架都是围绕这个范例构建的。

  • 这种架构非常符合 HTML 的结构方式。

  • 这种架构很容易构思和构建。

  • 这种架构需要更少的规划来实现。

  • 这种架构几乎不需要前期开发。

  • 网络的大部分历史都植根于这种架构。

  • 这种架构的优化是最后执行的(即过早优化是万恶之源)。

然而,这种架构的问题在于,对于大多数用例来说,它在高流量的情况下效率非常低。

通过多年来 Ajax 技术的发展,构建应用的另一种方法是将页面翻过来。也就是说,在典型的架构中,服务器生成包含动态内容的页面。在新的方式中,服务器生成一个基本上静态的页面,但是对于任何动态内容,回调到服务器。

例如,想象一下,有一个列出你的产品的页面。在典型的 web 框架中,您的服务器将执行以下任务:

  1. 用户的浏览器向服务器请求特定的页面。

  2. 服务器端开始 web 应用流程引擎(PHP,Ruby on Rails 等。).

  3. 应用访问数据库以获取产品列表。

  4. 应用为产品列表生成 HTML。

  5. 应用访问数据库以获取用户的状态信息(即登录信息、购物车信息、其他状态信息等)。).

  6. 该应用生成 HTML 页眉和页脚,其中包含用户的状态信息以及他们在页面导航中的位置。

  7. 应用将生成的片段拼接成整个 HTML 页面。

  8. 服务器将最终的 HTML 页面返回给用户。

  9. 用户的浏览器呈现页面。

正如你所看到的,这是一个相当复杂的过程,在整个页面被呈现在服务器上之前,用户不会得到任何反馈。这导致服务器需要等待很长时间才能完成。

然而,如果我们把它翻过来,我们可以实现一个更好的优化策略。我们要做的是将页面作为静态页面提供,然后让用户自己的浏览器负责获取基于用户的内容并将页面拼接在一起。

新的序列如下所示:

  1. 用户的浏览器从 CDN 请求页面。

  2. 假设它已经被缓存,用户附近的 CDN 服务器立即用页面内容(即产品列表)进行响应。

  3. 用户的浏览器加载内容并立即显示,在任何特定于用户的数据旁边有一个加载微调器。

  4. 该页面执行 JavaScript,向服务器发出请求,请求用户的状态信息。

  5. 服务器发回用户的 JSON 编码的状态信息。

  6. 该页面呈现网页的其余组件。

在这个序列中,服务器的负担大大减轻。它不再需要查询产品列表、生成页眉和页脚,或者将页面缝合在一起。所有这些都发生在用户和 CDN 之间的快速交易中。服务器只对用户的会话状态负责(即使这样也常常可以本地化到用户自己的浏览器!).这允许大量的动态、交互式内容,而对服务器的负担很小。

这个概念可以进一步扩展,甚至可以在事后生成产品列表。API 请求本身也可以被缓存,使用与主 web 页面相同或不同的设置。

如您所见,如果应用的架构合理,CDNs 为提高网站速度提供了一个非常灵活和强大的工具。

这种方法的主要缺点是实施时需要大量的计划和预见。这种方法需要决定一个 Ajax 和动态 HTML 框架,设计一个好的应用架构,开发 API 和 API 认证,并规划包含所有部分的页面布局。在旧的架构上,你通常可以随意地制作页面,它们仍然可以工作。当使用由内而外的架构时,您必须从计划开始,这需要相当多的前期工作。本书并不试图展示由内向外的页面架构的代码,正是因为它需要大量的代码来实现。

在任何情况下,无论您的应用架构是什么,大多数应用都可以从某种 CDN 解决方案中受益。

九、为无限磁盘空间使用 S3

对于一个真正可伸缩的站点,另一个经常需要的方面是无限的磁盘存储。管理服务器的大规模存储确实是一项艰巨的任务。决定多少冗余、多少可访问性、每台服务器有多少磁盘,以及如何管理磁盘以确保您提前知道磁盘是否出现故障是一项艰巨的任务。即使对于小规模的站点,管理文件也是困难的。

令人欣慰的是,使用文件存储服务将允许你把这些任务外包给第三方,可能比你自己做要便宜得多。文件存储服务的黄金标准是亚马逊的 S3 服务。S3 代表简单存储服务。这个缩写在很大程度上是正确的——对于简单的情况,S3 很容易设置,但是对于更复杂的任务,它也有相当大的灵活性。

S3 以极低的每千兆字节成本为你提供无限的空间。它将随着您的需要而扩展,并防止您遇到因本地存储文件而带来的所有文件管理难题。

其他云存储服务也存在,许多具有更好的定价结构。一些比较常见的包括 Backblaze B2,数字海洋的空间,和 Rackspace 的云文件。这里我们使用 S3,因为它拥有最广泛的采用和集成。

9.1 入门

S3 是亚马逊 AWS 工具套件的一部分。因此,您可以使用您在第八章中创建的相同登录来访问 AWS。

一旦登录 AWS(网址为 http://aws.amazon.com ),就可以在“存储”标题下访问 S3。当你点击 S3,你会得到一个类似图 9-1 的屏幕。

img/487291_1_En_9_Fig1_HTML.jpg

图 9-1

最初的 S3 屏幕

该屏幕的主按钮是“创建存储桶”按钮。S3 将所有文件组织成他们所谓的“桶”存储桶有点像命名硬盘。那是你储存所有文件的地方。桶名必须是唯一的,不仅对你的帐户,而且实际上在整个亚马逊。因此,对于 Amazon buckets,您不应该依赖任何特定的命名约定,这些约定假定您可以预测将来会有什么名称。相反,最好在您的应用中配置存储桶名称,这样更容易管理。

当您单击“创建存储桶”时,它会要求您输入存储桶名称和区域。AWS 将除了 CloudFront 之外的几乎所有服务组织成区域。就我们的目的而言,该地区本身并没有太大的不同。但是,如果您有特定的原因需要在特定的物理位置放置一个铲斗,AWS 允许您选择铲斗的放置位置。单击“创建”按钮创建您的存储桶。

一旦您成功地创建了您的 bucket,您应该得到一个 bucket 列表(只有您的一个 bucket)。如果你点击你的桶,你可以浏览你的空桶。要了解 S3,只需从硬盘上传一个文件到桶中。点击“上传”按钮,然后你可以从你的电脑拖放文件到你的桶。点击“上传”让他们上传。

9.2 的文件夹

如果您在 S3 控制台上四处看看,您会注意到您能够在您的存储桶中创建文件夹/目录。然而,在 S3,文件夹实际上是不存在的。实际上,S3 桶有一个完全扁平的结构,只有文件名和文件(从技术上讲,文件名称为“键”,文件本身称为“对象”)。但是,文件名可以包含斜杠字符。AWS 用户界面然后使用斜杠字符向您显示文件,就像它们在文件夹中一样。当你在 S3“创建一个文件夹”时,它实际上是创建一个文件夹名的空文件,文件名以斜杠结尾。简而言之,S3 控制台让你看起来像有文件夹和子文件夹,但实际上它只是一大堆文件,其中一些文件的名称中有斜线,S3 控制台用它来分隔成假文件夹,以便更容易查看。

9.3 获取凭据

在我们将 S3 帐户连接到服务器之前,我们需要创建一组安全凭据。为此,AWS 使用了一个名为 IAM 的系统,即“身份和访问管理”。IAM 允许您创建具有受限权限的用户,这样,如果您的安全密钥遭到破坏,就不会让攻击者完全控制您的环境。与其他服务一样,IAM 可以通过在其服务列表下搜索 IAM 来找到。当你第一次加载 IAM 屏幕时,它看起来如图 9-2 所示。

img/487291_1_En_9_Fig2_HTML.jpg

图 9-2

IAM 初始仪表板

IAM 主要使用“组”来管理权限,组本质上是权限的容器。因此,我们将从创建一个组开始。首先单击左侧面板上的“Groups ”,这将显示一个空的组列表。然后点按“创建新群组”这将打开一个屏幕,要求输入该组的名称。我们将使用名称guestbook-access(名称并不重要,我们稍后只需引用该名称)。单击“下一步”继续。

接下来,您将把策略附加到该组。策略是复杂的权限组。谢天谢地,AWS 有非常有用的预定义策略。出于我们的目的,我们只需要名为“AmazonS3FullAccess”的策略。您可以在“过滤器”框中搜索它,然后在找到时选择它。图 9-3 显示了它的样子。

img/487291_1_En_9_Fig3_HTML.jpg

图 9-3

将策略附加到组

最后,它会要求您审核并最终确定您的小组。单击“创建组”完成。

现在,您可以将用户添加到组中。在屏幕左侧,单击“用户”链接。这将把您带到一个空的 IAM 用户列表。要开始,请单击“添加用户”按钮。

在下一个屏幕中,将要求输入用户名和访问类型,如图 9-4 所示。我们称用户为application-user,尽管实际的名字并不重要。在“访问类型”下,选择“编程访问”这意味着创建的用户将不能登录,但只能使用 API。

img/487291_1_En_9_Fig4_HTML.jpg

图 9-4

创建 IAM 用户

在下一个屏幕中,它将要求您将用户添加到一个组。只需选择您之前创建的组(我们称之为我们的组guestbook-access)。下一个屏幕允许您为该用户设置标记。我们不需要,所以您可以跳过此屏幕继续。最后,它会要求您检查您的信息。此时,您可以单击“创建用户”,它将为您创建用户。

创建用户后,您现在可以下载他们的凭据。屏幕如图 9-5 所示。它列出了用户和两个特殊字段:“访问密钥 ID”和“秘密访问密钥”这两个字段实质上充当该用户的 API 的可重置用户名(“访问密钥 ID”)和密码(“秘密访问密钥”)。您可以下载凭据,也可以从屏幕上的字段中复制凭据。

img/487291_1_En_9_Fig5_HTML.jpg

图 9-5

正在检索 IAM 凭据

接下来,我们将把实际的访问密钥 ID 称为MYACCESSKEYID,把实际的秘密访问密钥称为MYSECRETACCESSKEY

如果您后来丢失了这些凭据,您将无法再次获得它们。但是,您可以返回到用户的记录中,创建一组新的凭证。如果以后服务器的安全性受到威胁,您可以停用旧的凭据,并为同一用户颁发新的凭据。

9.4 通过命令行访问 S3

AWS 有一个命令行工具,不仅允许访问 S3,还允许访问它们的各种可伸缩性 API。要安装它,请在模板节点上以 root 用户身份发出以下命令:

yum install -y awscli

AWS 命令行有两种主要方式来指定您的访问密钥 ID 和秘密访问密钥。您可以通过环境变量或配置文件来实现。环境变量更灵活,所以我们将走这条路。

如果您不熟悉命令行,环境变量是在命令行会话中设置的变量。不仅如此,您调用的命令还可以访问您的所有环境变量。此外,您设置的任何环境变量在您注销时都会消失,因此它们需要在您每次登录时重置(如果您希望它们在登录时自动设置,您可以将设置它们的命令放在一个名为.bash_profile的文件中)。

aws命令为您的凭证使用的环境变量是AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY。要设置这些变量,请在您的终端中输入以下命令(用您的实际按键替换MYACCESSKEYIDMYSECRETACCESSKEY):

export AWS_ACCESS_KEY_ID=MYACCESSKEYID
export AWS_SECRET_ACCESS_KEY=MYSECRETACCESSKEY

现在你可以使用aws命令来操作你的 S3 桶。要获得您的存储桶列表,发出以下命令:

aws s3 ls s3://

它应该列出您在第 9.1 节中创建的存储桶。

要列出该 bucket 的内容,发出以下命令(用 bucket 的实际名称替换BUCKET):

aws s3 ls s3://BUCKET/

为了理解命令的工作方式,aws是我们正在使用的主命令,s3告诉我们要使用哪组子命令,ls就像 Linux 上的ls命令(它列出了内容),s3://BUCKET/是我们想要查看的位置。

aws命令也给出了操作文件的其他常用命令。代替ls,我们可以使用cp将文件复制进和复制出桶。如果您有一个名为test.txt的文件,您可以使用以下命令将它复制到您的 bucket 中:

aws s3 cp test.txt s3://BUCKET/

要将一个文件你的桶复制到你的节点,只需切换s3://BUCKET/test.txt的位置。

此外,您可以为您的文件创建临时访问 URL。这些 URL 是签名的 URL。这意味着 AWS 知道一个授权的人生成了这个 URL,并且 AWS 将在指定的时间内信任这个 URL 作为访问该文件的有效手段。

这允许您直接将人们引导到 AWS 站点来检索他们需要的数据,而不是必须将数据传输到您的服务器,然后自己发送。它节省了处理能力、带宽和响应时间。

然而,为了做到这一点,我们需要知道这个桶在哪个区域。您在创建 bucket 时指定了一个区域,但是 AWS 并不总是显示该区域的“计算机化”版本,这是命令行所需要的。发出以下命令来查找您的 bucket 的区域(用您的 bucket 名称替换BUCKET):

aws s3api get-bucket-location --bucket BUCKET

这将返回一个 JSON 编码的值。这个键叫做LocationConstraint,这个值是这个桶所在区域的名称。该地区的一些常见值是类似于us-east-1us-east-2ca-central-1eu-west-2等字符串。我们将使用 REGION 来表示您所在的地区。

要获取访问文件的 URL,请使用以下命令:

aws s3 presign s3://BUCKET/FILE --region REGION

这将生成一个 URL,您可以将其复制并粘贴到您的浏览器中。瞧,文件将会出现!但是,这个网址只能用 3600 秒(1 小时)。如果您希望 URL 在不同的时间内有效,您可以使用--expires-in标志来告诉它。因此,如果您希望 URL 在 20 秒后过期,您只需在命令中添加--expires-in 20

img/487291_1_En_9_Figa_HTML.jpg内联指定环境变量

在我们开始编码之前,我想快速补充一下,让您知道设置环境变量的另一种方法。您可以设置一个环境变量,使其仅对一个应用有效,方法是在给定命令本身之前指定要在同一行上设置的环境变量。

例如,如果我要运行命令example-command,并且我想将环境变量MYVAR设置为myval,我可以这样运行命令:

MYVAR=myval example-command

这将设置环境变量,但仅用于运行命令。在运行命令时,您实际上可以设置任意多的环境变量,它们只需用空格隔开。例如,要设置两个值,我们可以这样做:

MYVAR1=val1 MYVAR2=val2 example-command

这就是我们在将要创建的应用中设置凭据的方式。出于配置目的,实际上最好将凭证(和其他配置信息)放在代码之外,并通过服务器上的环境变量来设置它们。然而,这需要比本书更深入的、特定于服务器的配置细节。

9.5 将您的应用连接到 S3

现在我们知道了如何从我们的服务器与 S3 对话,我们现在将把我们的留言簿应用连接到 S3,这样用户就可以上传他们的消息和图片。这实际上相当简单。我们要做的就是

  1. 创建一些通用函数来获取 AWS 配置信息。

  2. 允许我们的表单有一个图像字段。

  3. 创建留言簿条目时检查图像上传。

  4. 把图像传送到 S3。

  5. 查看留言簿条目时,为图像创建一个签名的 S3 URL。

第一步是在common.php中创建一些助手函数,用于处理 AWS 凭证和配置。图 9-6 显示了其功能。记住用您自己的值替换 BUCKET、ACCESSKEY 和 SECRETKEY。

img/487291_1_En_9_Fig6_HTML.png

图 9-6

AWS 配置对common.php的补充

函数getAWSCredentials()将返回一个字符串形式的凭证,该字符串可以在前置到命令字符串时设置环境变量。

下一步是在new.php中的表单上放置一个图像字段。这包括两个部分。我们必须做的第一件事是修改<form>标签,以便它允许文件上传。为此,将属性enctype="multipart/form-data"添加到<form>标签中。否则,文件输入标签实际上不会上传文件。

接下来,我们需要添加一个文件上传字段。就在提交按钮之前,添加以下几行:

<label>Image (JPEG)</label>
<input type="file" name="imagefile" />

现在,您的表单被配置为可以上传文件。接下来,我们将配置create.php来接受文件上传。

img/487291_1_En_9_Fig7_HTML.png

图 9-7

修改create.php以检测文件上传

我们必须改变两个部分。首先,用图 9-7 中的代码替换线$has_img = false;。这段代码检测一个文件是否被上传,如果是,确保数据库被更新以反映这一点。其次,将图 9-8 中的代码添加到显示$stmt->execute();的行之后。这是把文件传送给 S3 的代码。

img/487291_1_En_9_Fig8_HTML.png

图 9-8

修改 create.php 向 S3 传输文件

现在,我们只需要提供一种方法来查看图片,如果你点击留言簿条目。为此,我们需要修改single.php。只需将图 9-9 中的代码添加到显示getFooter()的行之前。

img/487291_1_En_9_Fig9_HTML.png

图 9-9

修改single.php以显示图像

这段代码将为上传请求一个签名的 URL,然后将它放在一个图像标签中以供查看。

注意,对于一个真实的应用,我们希望验证上传的文件确实是一个 JPEG 文件。否则,任何人都可以上传任何东西,黑客可以很容易地滥用该系统作为一个免费的文件共享网站,或用于其他邪恶的目的。此外,您可能希望有一个管理功能来验证上传的图像是适当的。否则,有人会轻易地把你的留言簿变成色情分享网站。这些超出了我们简单的示例应用的范围,但它们是值得记住的好东西。

还要记住,你最终不仅要为存储空间付费,还要为进出 S3 的所有流量支付带宽使用费。未能保持警惕可能会付出高昂的代价。

现在您需要在template_node上测试您的新代码,然后,当它工作时,通过重新映像服务器将它部署到您的云集群,如 6.3 节所述。

现在试试你的应用——它现在有了亚马逊 S3 无限的文件存储空间!

注意,还有一个用于 PHP 的 AWS 库,它具有 S3 功能。这里,我们使用命令行,因为我们已经在前一节中学习了该工具。关于 AWS PHP 库的信息可以在 https://docs.aws.amazon.com/sdk-for-php/ 获得。

img/487291_1_En_9_Figb_HTML.jpg关于 S3 签名到期的说明

在处理签名 URL 时要记住的一件事是要警惕它们如何与缓存交互。在这种情况下,single.php没有被缓存,所以没有真正的担心。但是,如果是的话,确保 URL 的过期时间比呈现的代码在缓存中的时间长得多是很重要的。

例如,如果 URL 只在 30 秒内有效,但缓存持续了一个小时,那么,在第一个 30 秒结束后,在这一小时的剩余时间里,用户将获得他们无法使用的 URL。当问题开始出现时(或者最好是在问题出现之前),请记住这一点。

img/487291_1_En_9_Figc_HTML.jpg S3 文件权限

除了已签名的 URL 之外,还可以通过授予对文件的公共访问权限来允许访问 S3 文件。这是可行的,但不是共享文件的最佳方法。问题是,如果你只是提供一个公开共享的网址供人们访问,那么这个网址就可以被分享,人们就可以出于自己的目的使用你的 S3 资源,完全绕过你的网络应用。这意味着你可能会花钱成为别人的文件服务器。

即使您没有立即在应用中实现对文件访问的谨慎控制,强制每个人使用由您的应用控制的签名 URL 来访问 S3 对象(如我们在此所示)也意味着当您准备好实现访问控制时,一切都准备好了。至少,它可以防止在互联网上公开分享 S3 网址,这些网址使用你的 AWS 帐户资源下载大文件。

如果您真的喜欢这样做而不是签名的 URL,您可以通过首先在 bucket 本身上启用公共访问,然后设置每个人都可读的单个文件来管理它。这也意味着您需要对新上传的文件设置权限。您可以在aws s3 cp命令中这样做,方法是在命令末尾添加以下标志(URL 应该与该行的其余部分在同一行,等号后面没有空格):

--grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers

但是,这只有在 bucket 本身被配置为允许公共访问时才有效。

十、将 AWS 用于托管

虽然这本书的大部分内容都集中在在 Linode 上托管你的应用,但是由于这么多的云托管都是基于 AWS 的,所以我认为值得看看 AWS 上的一些托管选项。

AWS 的一个问题就是它的大量选项。可用选项的数量实际上使管理变得相当困难。

举个例子,有一次我在一个团队中工作,需要访问日志。经过大量搜索,我们终于找到了允许访问日志文件的选项。然而,事实证明查看日志文件实际上是不同于检索日志文件所需的权限。因此,虽然我有权限查看日志文件,但我没有权限实际获取它们。他们最终放弃了细粒度的控制,让我做了管理员。这并不是说 AWS 没有这种能力,只是试图管理它所花费的时间和精力与您通过其他不太“自动化”的方式所节省的时间和精力一样多。拥有大量的选择,并试图成为每个人的一切,很容易让你淹没在没有人有时间和机会掌握的选项和设置中。

有这么多细粒度的控制,每一个都有自己的问题、怪癖和缺陷。AWS“支持”的不同平台和系统的数量非常大。然而,我发现在这种支持中经常有很大的漏洞,虽然可以解决,但这些解决方法有时让我希望我一开始就手工完成了。

10.1 使用亚马逊 Lightsail

亚马逊首次涉足云托管被称为 EC2——弹性计算云。像大多数 AWS 一样,这是一个非常灵活的云托管选项。然而,它存在许多 AWS 固有的问题——成功地设置和使用它确实很复杂。不仅设置复杂,而且定价复杂得离谱。他们不仅对计算机时间收费,而且对硬件 I/O 请求也收费。没错,他们跟踪并向你开出硬盘访问次数的账单。

为了更好地与 Linode 和 DigitalOcean 等更易用的服务竞争,AWS 推出了亚马逊 Lightsail。Lightsail 的功能集和定价结构与我们在 Linode 上看到的非常相似。可以从包含 CloudFront 和 S3 的同一个 AWS 管理控制台访问 Lightsail。只需搜索 Lightsail,仪表盘看起来应该类似于图 10-1 。

出于我们的目的,需要注意 Lightsail 和 Linode 之间的一些细微差异:

  1. 默认情况下,Lightsail 实例通过公钥而不是密码连接,并将您连接到普通用户,而不是以 root 用户身份连接。

  2. Lightsail 的 CentOS 发行版的预装软件包与 Linode 略有不同。

    img/487291_1_En_10_Fig1_HTML.jpg

    图 10-1

    亚马逊 Lightsail 的初始仪表板

  3. 所有 Lightsail 实例都会自动添加到 Amazon 的私有网络中。其实你在盒子上,看到的唯一的 IP 地址就是私网地址。您收到的外部 IP 地址会被路由到内部 IP 地址。

  4. Lightsail 实例的备份必须手动进行。

  5. Lightsail 有一个单独的数据库服务,如果您不想自己管理数据库服务器,可以使用它。

要启动并设置新实例,请单击“创建实例”按钮。这将把你带到一个类似于图 10-2 的屏幕。

在这里,您需要选择将托管您的节点的数据中心(实例位置)。这与 Linode 略有不同,因为 AWS 同时拥有数据中心和可用区域。在 AWS 中,数据中心被组织成多个可用性区域。这些区域可以轻松共享资源以实现负载平衡,但是每个可用性区域都有单独的电源线和外部互联网连接。本质上,它们就像在同一个数据中心一样,但是不同可用性区域中的服务器不太可能受到同一事件的影响(例如断电或互联网)。例如,您可以将主数据库放在一个可用性区域中,而将副本数据库放在另一个可用性区域中。然后,如果主服务器的可用性区域关闭,副本服务器(因为它位于不同的可用性区域)可以升级并充当主服务器。

img/487291_1_En_10_Fig2_HTML.jpg

图 10-2

创建 Lightsail 实例

我们想要选择的平台是“Linux/Unix”然后,选择“仅操作系统”(其他选项允许针对特定任务预配置服务器)。之后,选择“CentOS”

向下滚动,您可以忽略大多数其他问题,并选择您希望的节点大小。然后,为了识别您的实例,像我们之前做的那样称它为template-node。单击“创建实例”创建机器。

AWS 可能需要一段时间来创建您的计算机。当它出现时,你可以点击机器,你将得到一个类似于图 10-3 的仪表板。要登录,只需点击标有“使用 SSH 连接”的按钮这将打开一个终端窗口,您作为用户centos连接到该节点。

您可以使用这个centos用户来代替本书示例中的fred用户。或者,你可以在需要时创建fred。然而,您仍然需要使用这个盒子作为root来配置它。要以root的身份登录,只需发出以下命令:

sudo su -

img/487291_1_En_10_Fig3_HTML.jpg

图 10-3

Lightsail 节点仪表板

现在您的会话将作为root用户。

Lightsail 节点附带的软件包与 Linode 节点略有不同。要让您的 Lightsail 节点类似于 Linode 系统的起始点,请发出以下命令:

yum install -y nano
yum install -y firewalld
systemctl start firewalld
systemctl enable firewalld

从这一点来说,你可以按照第三章和第四章的节点设置说明进行操作。

在第五章中,从备份创建新实例的机制只是略有不同。在 Lightsail 中,使用“快照”功能创建备份。您只需转到节点的“Snapshots”选项卡,为快照命名,然后创建它。创建完成后,您可以从该快照创建一个新节点,如图 10-4 所示。负载平衡器可以从主屏幕的“网络”标签下的 Lightsail 中创建。

img/487291_1_En_10_Fig4_HTML.jpg

图 10-4

从快照创建节点

之后,关于创建云的其余信息不会改变,因为这都是关于节点本身发生了什么。

10.2 托管在弹性豆茎上

虽然本书的大部分内容都集中在基础设施即服务(IaaS)云上,但我确实想花至少一些时间来介绍平台即服务(PaaS)云。AWS 有一个名为“Elastic Beanstalk”的 PaaS 云,它运行各种不同的应用类型,PHP 就是其中之一。

PaaS 云消除了云计算的所有系统管理工作。然而,问题是大多数先进的云系统最终都需要一些系统管理。当您有奇怪的配置需求时,PaaS 系统并不是不可用,而是正确、可维护地配置您的 PaaS 系统,并与您的平台提供商同步的工作量最终会比您像使用 IaaS 云一样完全控制要多。在任何情况下,在这一节中,我们将回顾使用 Elastic Beanstalk 启动和运行我们的应用需要做些什么。

在 PaaS 系统中,我们无法控制机器。因此,我们不能将特定的机器指定为数据库服务器、作业服务器等等。相反,每个应用可以根据 PaaS 系统的需要在任意多的机器上扩展或缩小。这通常意味着 PaaS 系统将管理数据库。

对于 AWS 来说,这意味着使用他们的关系数据库服务—RDS。将应用连接到 RDS 非常简单。Elastic Beanstalk 将为所有连接信息设置环境变量,我们只需编写应用来读取它们。

为了做到这一点,我们只需改变common.php并替换getReadOnlyConnection()getReadWriteConnection()功能。这两个功能应该如图 10-5 所示。

img/487291_1_En_10_Fig5_HTML.png

图 10-5

使用 RDS 访问数据库

这将根据 RDS 发送的环境变量组合成一个连接字符串。

第二,由于您不能直接访问数据库,我们需要一个 PHP 脚本来为我们创建数据库表。创建一个名为createdb.php的文件,其中包含以下代码。

img/487291_1_En_10_Fig6_HTML.png

图 10-6

使用 RDS 创建数据库

请注意,由于我们没有特定的服务器,这些更改是对我们个人计算机硬盘上的应用的本地副本进行的。一旦完成这些更改,您就可以开始了。

要从 Elastic Beanstalk (EB)开始,请返回 AWS 管理控制台并搜索“Elastic Beanstalk”在仪表板上,单击标题为“创建新应用”的按钮给它起个名字,然后点击“创建”

在 EB 上,应用可以划分为“环境”环境可以用于各种各样的事情,包括准备环境和生产环境,为不同的任务准备不同的服务器组,以及在应用的不同版本之间快速切换。出于我们的目的,我们将只有一个环境。因此,单击按钮创建一个新环境。它会问你创建什么类型的环境。我们想要一个“网络服务器环境”

下一个屏幕是配置屏幕,如图 10-7 所示。如果愿意,您可以命名您的环境,但是该屏幕上唯一重要的设置是选择一个“预配置的平台”显然,我们想要“PHP”默认情况下,它将加载一个示例应用。这就是我们现在想要的。单击“创建环境”完成该过程。

img/487291_1_En_10_Fig7_HTML.jpg

图 10-7

创造新环境

创建完成后,您将拥有一个类似图 10-8 的仪表板。在“概述”部分,它给出了应用、版本和平台的基本健康状况。在“最近事件”下,它列出了系统最近经历的所有操作。请注意在 EB 中的一切最终花费的时间比你想象的要长得多,但是“最近的事件”帮助你密切关注它,并在你等待的时候给你一些可看的东西。在左侧,我们将重点关注的三个区域是“仪表板”(我们现在所处的位置)、“配置”(应用的设置方式)和“日志”

img/487291_1_En_10_Fig8_HTML.jpg

图 10-8

EB 环境仪表板

因为我们从一个示例应用开始,所以您已经可以在您的 web 浏览器中加载这个示例应用了。在仪表板上,它有一个指向应用 URL 的链接。您可以单击它,它会将您带到示例应用。

在上传我们的应用之前,我们需要首先设置数据库。为此,请转到“配置”部分,然后转到“数据库”并单击“修改”这将允许您为您的应用创建一个 RDS 实例。将“引擎”设置为postgres,给它一个用户名和密码(一定要记下这些!),然后单击“应用”当健康状态显示为复选标记时(可能需要 10-20 分钟),您就可以上传您的应用了。

要上传您的应用,请将您的所有文件压缩到一个单独的 zip 存档文件中。然后,在仪表板上,单击“上传和部署”按钮从您的硬盘驱动器中选择 zip 文件,为该版本命名(具体名称并不重要),然后单击“部署”健康状态将变为刷新微调器,它将部署您的应用。当微调器变回复选标记时,您的应用就部署好了!

然而,我们还没有完全完成。当你点击链接,它会给你一个“禁止”的信息。这是因为我们没有一个index.php文件(尽管如果您愿意,您可以添加一个)。然而,我们需要创建我们的数据库,所以我们需要首先导航到createdb.php文件来创建数据库(只需在浏览器的 URL 末尾添加/createdb.php并点击 enter)。这会给你带来一个空白页,这很好。

现在,如果您更改 URL 以转到list.php,一切都应该正常工作了!

现在,您可以对您的应用做许多事情来使其可伸缩。这些都可以从您环境的“配置”选项卡中获得。默认情况下,EB 创建单服务器环境。要将其升级到负载平衡的应用,只需进入“Capacity”部分,将“Environment Type”更改为“Load Balanced”这会给你很多选择,你可以玩。最简单(也是最重要)的是“最小”和“最大”数量的实例。将最小值设置为2,以确保它为您启动至少两台机器。单击“应用”,等待几分钟,您的应用现在负载平衡。

您可以进行的其他更改包括

  • 在“实例”下,您可以更改每个单独实例的大小。

  • 在“负载平衡器”下,您可以更改许多变量,包括平衡机制。

  • 在“滚动更新和部署”下,您可以更改部署策略以消除更新期间的停机时间(“不可变”选项最适合生产,但是执行更新需要很长时间)。

  • 在“软件”下,您可以设置环境变量和 web 服务器配置选项。

  • 在“数据库”下,您可以更改数据库的大小。您还可以单击“Endpoint”链接,转到 RDS 管理控制台,使用其他数据库管理实用程序。

进行配置更改后,点击“应用”将使用给定的更改重新部署您的应用,这可能需要几分钟时间。如果您在运行应用时遇到任何问题,您可以转到“日志”部分,下载并查看最新的日志消息。

十一、使用谷歌云平台

既然您已经有了与多家云提供商合作的经验,我希望您能够意识到云的基本组件并不会因为提供商的不同而有太大的变化。每个人可能调用不同的东西,可能有一点点不同的设置过程,或者可能有一些额外的能力或限制。然而,就其核心而言,不同提供商的 IaaS 云托管的基础非常相似。

这本身就是 IaaS 的一个优势。这意味着,如果您想从一个 IaaS 迁移到另一个 IaaS,这实际上是一个相当简单的过程。这可能需要计划和执行时间,但是,由于节点本身在您的控制之下,您可以使环境彼此完全相同。

我们要看的下一个提供商是谷歌云平台(GCP)。GCP 是 2008 年成立的新公司(Linode 成立于 2003 年,AWS 于 2006 年上线)。GCP 与 AWS 非常相似,尽管配置起来有点麻烦。您可以在 https://cloud.google.com 报名,开始体验 GCP。

img/487291_1_En_11_Fig1_HTML.jpg

图 11-1

GCP 欢迎屏幕

11.1 设置模板节点

关于 GCP,首先要知道的是一切都被组织成“项目”每个项目有点像它自己的帐户,每个都有自己的资源、服务等等,但是都可以在同一个登录中访问。

当你登录谷歌云平台时,它应该看起来像图 11-1 。请注意,在顶栏上,文本“Google Cloud Platform”旁边,列出了您当前项目的名称。您可以单击当前项目来更改项目或创建新项目。对于这个例子,我将创建一个名为Book Examples Project的新项目。

屏幕左侧列出了 GCP 服务。要创建新机器,请转到服务的“计算”部分,单击“计算引擎”,然后单击“虚拟机实例”GCP 称它的机器(或节点,正如利诺德所说)“虚拟机实例”要创建新机器,请单击“创建实例”按钮。这将弹出一个类似图 11-2 的屏幕

我们将我们的机器称为template-node,并选择最小的机器类型(本例中为“f1-micro”)。对于启动盘,我们会选择 CentOS 7。向下滚动,有一个“防火墙”部分。确定“允许 HTTP 流量”和“允许 HTTPS 流量”都已启用。其余的默认设置你可以不去管。当一切都设置好了,点击“创建”, GCP 将为你创建一个新的机器,并返回到虚拟机列表屏幕。

img/487291_1_En_11_Fig2_HTML.jpg

图 11-2

创建 GCP 虚拟机实例

创建机器后,您可以使用“连接”选项登录。在“连接”栏的下拉菜单中,选择“在浏览器窗口中打开”,它将在您的浏览器中为您提供一个ssh会话。

CentOS 的这个安装与 Linode 的安装非常相似,除了(a)它不包括nano(您可以用一个简单的yum install -y nano来修复它),以及(b)它自动为您创建一个用户,并让您以该用户的身份登录。您可以通过运行以下命令轻松切换到 root 用户:

sudo su -

从那时起,您可以安装nano并执行第 3 和 4 章中概述的所有配置步骤。

img/487291_1_En_11_Figa_HTML.jpg公共与私有 IP 地址

GCP 和 Linode 之间的一个有趣的区别是,在 Linode 上,您的节点的公共 IP 地址是物理连接到您的节点的。也就是你发出ip addr show的时候,显示的是公有 IP 地址。当您添加专用 IP 地址时,它会将该专用 IP 地址添加到您的节点中。

然而,在 GCP,你的节点以私有地址和公有地址开始。但是,私有地址是唯一物理映射到设备的地址。在网络设备上配置公共 IP 地址,以便将这些请求转发到您的机器。

因此,在机器上的所有配置中,您将使用包装盒上的私有 IP 地址。此外,您不必担心只能监听您的私有 IP 地址,因为这实际上是您所拥有的全部。GCP 网络控制哪些服务可以从外部进入您的机器(这就是为什么您在设置虚拟机实例时选中了 HTTP 和 HTTPS 框,以告诉 GCP 将这些类型的请求路由到您的私有 IP 地址)。

基本上,除非另有说明,GCP 的一切都仅限于本地网络。

11.2 为远程访问设置数据库服务器

为了将我们的template-node用作数据库服务器,我们需要准备好远程访问我们数据库的设备。为此,我们需要做到以下几点:

  1. 修改/var/lib/pgsql/data/postgresql.conf并设置listen_addresses='*'。我们不需要专门将其设置为私有 IP 地址,因为这是我们唯一的 IP 地址,除非我们将 GCP 配置为允许,否则无法从互联网访问它。用systemctl restart postgresql重启 PostgreSQL,这样修改就会生效。

  2. 更改防火墙,以便我们允许连接到 PostgreSQL 服务器。这将是命令firewall-cmd --add-port 5432/tcp和带有--permanent标志的相同命令。

  3. 修改 PHP 代码getReadOnlyConnection()getReadWriteConnection(),使它们连接到正确的私有 IP 地址。

现在,负载平衡集群可以使用服务器了。

11.3 创建复制映像

在 GCP 创建复制映像是一个有点过于复杂的三步过程。首先,您需要为您的实例创建一个“快照”,然后您需要从该快照创建一个“映像”,最后您将需要创建一个“实例模板”快照本质上是机器的备份。该映像本质上是一个快照,旨在用于为机器创建新的引导映像。最后,实例模板将图像与机器设置(大小、配置等)结合在一起。)可用于非常快速地部署相同的机器。

创建快照是一个相当简单的过程。从主菜单(点击“谷歌云平台”旁边的三个栏),转到“计算”部分,选择“计算引擎”,然后选择“快照”单击“创建快照”按钮。它将询问快照的名称、源磁盘和位置。您可以随意命名,选择您现有的 VM 实例作为源磁盘,您可以不考虑位置(“多区域”是最灵活的选择)。点击“创建”, GCP 将为您创建一个新的快照。

现在,GCP 允许你从快照中创建实例。然而,为了利用 GCP 更多的特色,最好是从你的快照中创造出 GCP 所说的“形象”。为了做到这一点,进入主菜单,然后“计算”部分,选择“计算引擎”,然后选择“图像”它将加载一个巨大的预配置图像列表。你可以忽略这些。我们想塑造自己的形象。单击“创建图像”按钮开始。

给图像命名(如template-image),并将“源”设置为“快照”这将弹出一个菜单,询问您希望从哪个快照创建映像。选择您刚刚创建的快照。如果你愿意,你可以加上一个“家庭”的名字。这将使您能够创建带有相同“系列”名称的此实例的更新版本。

现在点击“创建”在这里,您可以根据映像创建新的机器。创建新机器时,在“启动盘”下,您的映像将出现在“自定义映像”选项卡下。

最后,我们需要将这个图像打包成一个实例模板。实例模板位于主菜单的“计算引擎”和“实例模板”下创建实例模板的过程与创建常规虚拟机实例的过程相同。不同之处在于,它不会立即创建任何实例,而是可以在以后用于快速部署完全预配置的机器。确保在创建实例模板时,将引导映像设置为您在上一步中刚刚创建的映像。

img/487291_1_En_11_Figb_HTML.jpg更加小心我们的模板

请注意,因为我们在成为模板的机器上设置了数据库,所以集群中的每个新 web 服务器实际上都有一个运行 PostgreSQL 的未使用的数据库副本。这本身不是问题,但是如果您要进行生产部署,您可能希望确保 PostgreSQL 在模板上是关闭的。因为 GCP 有很多步骤,所以本章的目标是减少为了获得一个运行配置而必须完成的步骤。

11.4 创建负载平衡组

但是,您也可以创建一个自动扩展、负载平衡的计算机组,称为“实例组”这类似于我们在第五章中所做的,但是 GCP 将会为你管理你的应用。换句话说,随着机器上的负载增加,GCP 会自动启动新的、相同的机器,并将它们添加到负载平衡器下。

img/487291_1_En_11_Fig3_HTML.jpg

图 11-3

创建实例组

您可以在主菜单的“计算引擎”下找到实例组单击“创建实例组”开始该过程。图 11-3 显示了该过程的样子。

为您的实例组命名。为确保提高容错能力,请选择“位置”下的“多个区域”。选择我们在 11.3 节中创建的实例模板。确保“自动缩放”设定为“开”如果您愿意,您可以将最小实例数设置为大于 1,以确保 GCP 在多个服务器上平衡您的应用负载。单击“创建”以构建您的实例组。

默认情况下,实例组做得很少。我们需要的是为实例组带来流量的方法。这是通过负载平衡器完成的。

要创建负载平衡器,请在主菜单下查找“网络服务”,然后查找“负载平衡”单击“创建负载平衡器”开始。接下来选择“HTTP(S)负载平衡”接下来选择“从互联网到我的虚拟机”,因为我们希望这个负载平衡器充当互联网和您的机器之间的网关。

下一个屏幕显示了主要的配置区域。首先,给你的平衡器一个名字。接下来,在“后端配置”下,选择“后端服务”,然后“创建后端服务”您需要命名您的后端服务(名称并不重要),然后选择您的实例组,然后创建一个健康检查(健康检查只需要设置为 TCP 端口 80)。然后点击“创建”,它将创建您的后端服务。您可以将“主机和路径规则”和“前端配置”保留为默认值。点按“检查并完成”以查看您的所有设置。最后,单击“创建”来构建您的负载平衡器。您可以访问生成的 IP 地址,它将在您的机器上平衡负载。

请记住,GCP 需要相当长的时间才能真正完成创建您的负载平衡器。即使在 GCP“认为”它已经创建完毕,并告诉您所有的实例都已经添加到负载均衡器中之后,仍然需要几分钟的时间来实现这一点。因此,在您的负载平衡器处于活动状态的最初几分钟,GCP 可能会在访问 URL 时报告错误。

img/487291_1_En_11_Figc_HTML.jpg移除负载平衡器

移除 GCP 负载平衡器比看起来要难。要删除我们以这种方式创建的负载平衡器,我们需要执行以下操作:

  1. 从负载平衡器列表中删除负载平衡器本身。

  2. 在负载平衡器列表屏幕上,有一个名为“后端”的选项卡点击查看您的后端服务。

  3. 在“Backends”选项卡下,单击您为负载平衡器创建的后端,并将其删除。

  4. 现在您的平衡器被移除了,但是您还需要移除您的实例组,否则您将继续为您的机器付费(在其他步骤完成之前,您将无法移除您的实例或实例组)。

  5. 如果您不想为您的映像付费,您还必须删除您的实例模板,然后删除您的映像和快照。

11.5 其他 GCP 服务

和 AWS 一样,GCP 也有许多其他服务,在创建云应用时也很有用。几项类似的服务包括

  • 云 SQL(类似于 RDS)

  • 存储(类似于 S3)

  • Memorystore(类似于 ElastiCache)

此外,GCP 还通过名为谷歌应用引擎的服务提供 PaaS 服务。

总的来说,GCP 有许多与 Linode 和 AWS 相同的服务,但使用起来稍微复杂一些。在某些极端情况下,这种复杂性会增加额外的可配置性,但很少需要。例如,GCP 使得将不同的子目录映射到不同的实例组变得相对简单。这使得在同一个主机名下托管多个应用并使每个应用成为不同的实例组变得更加容易。

对于大多数应用,GCP 的复杂性超过了它提供的可配置性。

十二、服务器管理技术

到目前为止,任何时候我们想要更新我们的云,我们都必须重新映像我们所有的服务器。这并不可怕,但也不是最佳选择。如果您设想运行一个有 20 台不同服务器的服务器群,您真的想每次重新部署代码时都重新映像它们吗?可能不会。如果要安装服务器补丁,是否要重新映像所有服务器?同样,可能不会。

幸运的是,有许多工具可以帮助管理许多机器。它们都有各种不同的侧重点,有些侧重于简化重复性任务,而有些则是完整的管理解决方案。我是一个简单的人,我通常更喜欢简单的工具,而不是大型的、包罗万象的工具。

12.1 在多台服务器上运行命令

假设我们想在集群中的每台机器上安装一个新的软件。ImageMagick 是一个流行的图像处理软件包。如果我们想在所有节点上安装 ImageMagick,过程相当简单。我们只是 ssh 到每个盒子,运行yum install -y ImageMagick,然后退出。

谢天谢地,有一款软件可以帮我们做到这一点,它叫做pssh,代表并行 SSH。您不需要在服务器上安装任何新的东西。pssh安装在本地机器上,然后使用常规的ssh机制在所有远程机器上运行相同的命令。

然而,在您的template_node机器上安装pssh可能更简单,这样您可以从任何地方使用它(也就是说,您可以登录到您的模板节点并从那里运行pssh)。pssh在 EPEL 包中有,所以我们可以通过以下操作将其安装到template_node上:

yum install -y pssh

要使用它,我们需要创建一个文件(我们称之为servers.txt)列出我们管理的所有服务器。使用nano创建文件,并将每个服务器的公共 IP 地址放入文件中。要使用pssh,你所要做的就是键入:

pssh -A -h servers.txt --user root COMMAND

只需用您想要运行的命令替换COMMAND。因此,我们可以发出以下命令将 ImageMagick 安装到每台服务器上:

pssh -A -h servers.txt --user root yum install -y ImageMagick

pssh会要求我们输入密码(这就是-A所做的),然后它会让我们知道它什么时候完成了任务。现在你所要做的就是确保你的servers.txt文件是最新的,你可以很容易地从一台机器上执行大多数管理任务。

当命令运行时,它会告诉您每台服务器的状态,如果出现故障,它会告诉您命令给出的错误代码。例如,对于三台服务器,输出可能如下所示:

[1] 23:00:32 [SUCCESS] 45.79.7.179
[2] 23:00:32 [SUCCESS] 45.79.7.180
[3] 23:00:32 [FAILURE] 45.79.7.181 Exited with error code 1

然后,您可能希望查看命令失败的机器,并确定哪里出错了。

12.2 同步多台服务器上的文件

由于现在在每台服务器上运行一个命令很容易,如果能够将一组文件复制到每台服务器上就好了。谢天谢地,pssh附带了两个文件复制工具,分别叫做pscpprsync,这样你就可以轻松地将文件复制到远程服务器上。这样,当您部署新版本的软件时,就不必重新映像每台服务器。pscp程序有时被称为pscp.pssh,所以如果安装pssh后没有找到命令pscp,尝试使用pscp.pssh

假设您有文件testme.html,并且您希望以用户fred的身份将它复制到每个服务器上的/var/www/html。要执行该任务,只需键入以下内容(全部在一行中):

pscp -A -h servers.txt --user fred
 testme.html /var/www/html/testme.html

如果要复制整个目录,可以使用同样的过程,但是要加上-r进行递归复制。

然而,pscp的一个问题是它不会为你删除文件。如果您想让两个目录保持完全同步,无论是添加还是删除,您都可以使用prsync,尽管它的语法有点严格。

如果我想将本地目录mirror_me镜像到位于/var/www/html/mirror_me的服务器,我将发出以下命令(全部在一行中):

prsync -A -a -x --delete --user fred
 -h hosts.txt mirror_me /var/www/html/

通过对命令使用pssh,对单个文件使用pscp,对整个目录使用prsync,对一组服务器进行基本管理是相当容易的。

还有几个工具专门用于在多台服务器上部署应用。其中比较受欢迎的是 Capistrano。Capistrano 是用 Ruby 编写和定制的,尽管它可以用任何语言部署应用。Capistrano 自动化了许多与文件部署相关的任务,包括

  • git存储库同步

  • 在服务器上维护应用的早期版本,以实现快速回滚

  • 使用符号链接来管理部署,以便整个部署在瞬间切换

  • 运行与部署相关联的自定义任务和脚本

随着您的应用变得越来越复杂,您的工具需求也将变得越来越复杂,拥有一个像 Capistrano 这样的工具将会满足这些需求。

12.3 全方位服务解决方案

虽然pssh允许您向一组机器发出命令,Capistrano 允许您以更健壮的方式自动化部署,但还有其他解决方案可以更进一步,完全为您管理目标系统。这些被称为“配置管理”系统。这些系统虽然可以处理各种各样的情况,但也带来了复杂性。对于大多数系统来说,我认为配置管理系统是多余的,它们增加了比解决问题更多的复杂性。然而,他们做得很好的一件事是强迫您记录您的配置是什么(并且希望记录为什么要这样设置)。简单地拥有一个正确配置的服务器并不能告知未来的管理员(或您自己)您的配置的哪些部分是默认设置的,哪些部分是专门为某个目的而配置的。使用配置管理工具,您的系统配置可以被记录和版本控制。

作为一个喜欢极简方法的人,对于系统配置,我非常欣赏 Ansible 系统,它甚至不需要在远程服务器上安装任何东西。Ansible 通过ssh完成所有的配置,并且很容易启动和运行。 http://ansible.com/ 有一个很好的用户界面,但它带来了额外的金钱成本(尽管也有开源的用户界面)。然而,如果您需要的只是一个命令行工具,那么开源版本是所有配置管理工具中占用空间最小的一个。

您甚至可以从 EPEL 软件仓库安装它,只需简单地:

yum install -y ansible

然而,如果你想全面发展,虽然有几个其他的选择,但许多开发人员选择的是 Chef。Chef 基于 Ruby 编程语言,开发人员可以随心所欲地进行复杂的配置。Chef 还有许多反馈机制,允许您查看所有托管服务器的状态。使用 Chef,您不仅可以随心所欲地配置服务器,还可以收集服务器上的数据和分析。

对于刚刚起步的应用,我建议只保留配置更改的手工日志。随着应用的成长和成熟(有望获得数百万用户),明确配置管理变得更加重要,并且可能值得转向更全面的解决方案。

我们看到的一些云系统内置了一些配置管理。GCP 的实例模板/实例组系统可以被认为是一个极简的配置管理系统。实际上,当您保持实例模板最新时,GCP 将在您的实例组中部署它。Elastic Beanstalk 允许使用称为“EB 扩展”的环境定制来定制部署应用的机器尽管从技术上讲,Elastic Beanstalk 是一个 PaaS,但是这些扩展允许您在要部署到的平台上执行配置管理。

十三、Linux 安全基础知识

本章并不是云安全的完整指南,而仅仅是为您指明正确的方向。安全与其说是一系列步骤,不如说是一个过程和一种思考方式,因此本章将重点介绍您需要如何思考来保护服务器的完整性和数据的完整性。

13.1 基本考虑因素

对于系统管理员来说,最重要的安全注意事项如下:

  • 我如何降低连接到互联网的风险?

  • 我可以在我的系统上运行并保持其功能的最小服务集是多少?

  • 如何限制不是每个人都需要的服务对服务器的访问?

  • 我所有的软件都安装了最新的安全补丁吗?

  • 有人可以通过哪些方式滥用部署在服务器上的服务,我该如何缓解这种情况?

安全性的关键是确定服务器的用途,然后删除与该功能没有直接关系的任何内容,以防止不必要的后果。你运行的每一项服务都是一个潜在的安全漏洞——总有一天有人会发现如何利用它。因此,您不希望暴露在对功能没有直接贡献的点上。

13.2 检查您当前的服务器

检查服务器上当前正在运行和监听的服务的最佳工具是ss(套接字统计)。要查找正在侦听连接的每个 TCP 服务,请以 root 用户身份运行以下命令:

ss -plnt

写有LISTEN的每一行都是一个正在监听 IP 连接的服务。如果“本地地址”是127.0.0.1::1(对于 IPv6 地址)或节点的私有 IP 地址,这意味着服务是受保护的——只有机器本身上的应用可以看到服务(在私有 IP 地址的情况下,在私有网络上)。然而,如果本地地址是*0.0.0.0::(对于 IPv6 地址),这意味着它可供任何人连接。

同样,对于 UDP 连接,您可以:

ss -plnu

对于每个可连接的服务(TCP 或 UDP ),您应该确保它不被您的防火墙允许,除非您完全确定您希望人们连接。要获取防火墙允许的服务列表,请发出以下命令:

firewall-cmd –list-all

“服务”和“端口”下列出的项目是防火墙允许通过的项目。您应该允许的服务有dhcpv6-client(由云用来配置网络)、ssh(以便您可以登录)、http(用于非 SSL HTTP 连接)和https(用于启用 SSL 的 HTTP 连接)。

尽管防火墙会阻止与未列出的任何内容的连接,但如果您有任何正在运行的服务正在侦听您并不特别需要运行的连接,您应该禁用该服务。此外,您应该定期检查您的防火墙,以确保其配置正确。

此外,您应该清点所有正在运行的进程,即使它们没有侦听连接。在 Linux 上最好的方法是使用命令ps -afxww。注意,每个人都有自己喜欢的ps称呼方式,但这是我的。

我不能给你一个应该/不应该运行的列表。但是,最好是了解每一块是做什么的,把不需要的那块关掉。运行的服务越少越好。

您也可以使用命令rpm -qa列出您已经安装的所有软件包。随着你越来越有经验,你应该能够卸载你不需要的程序。

13.3 根用户

Linux 安装中最危险的部分是根用户。这是危险的,原因有二。首先,它是超级用户,因此它对其他一切都有权力。第二,每个人都知道它的用户名,这使得自动黑客工具更容易攻破。

有几种方法可以减轻根用户的问题。它们包括

  1. 根密码应该是安全的(即,即使计算机也很难猜到)。事实上,每个用户的密码应该是安全的。

  2. 服务器不应允许远程直接 root 登录。为了防止通过ssh的直接根登录,修改/etc/ssh/sshd_config。如果已经有一行写着PermitRootLogin,将值从yes改为no。如果那里没有一行,添加一行表示PermitRootLogin no。保存文件后,执行systemctl restart sshd,更改将生效。之后,您需要以普通用户的身份登录,并使用su命令将用户切换到root

  3. 服务应该很少作为根用户运行,除非有压倒性的理由这样做。如果一个服务必须以根用户的身份做一些事情,那么直接与远程计算机对话的那部分服务就不应该是根用户。

  4. 用户不应该花太多时间作为根用户。在这本书里,我们大部分时间都是作为 root 用户度过的。相反,用户应该在自己的帐户下登录,然后使用susudo切换到 root 用户,获得临时 root 权限。

  5. 你应该安装某种类型的服务拒绝程序,比如fail2ban,它会在一定次数的失败尝试后禁止来自特定 IP 地址的登录。

通过防止人们成为 root 用户,运行不以 root 用户身份运行的服务,以及保护 root 帐户免受外部访问,入侵者造成损害的能力大大降低。

大多数管理员使用sudo来管理对根用户的访问。sudo命令允许用户使用自己的密码临时访问根用户,或者在登录后无需密码。它已经安装在你的机器上,允许任何属于wheel组的用户以根用户身份运行任何命令。要将一个用户(比如,fred)添加到wheel组,发出命令:

usermod -a -G wheel fred

现在,fred用户可以作为根用户运行任何命令,只需将sudo添加到命令的开头。例如,如果fred想要显示文件/etc/sudoers(sudo命令的配置文件),Linux 通常不会允许他这样做。如果他做了cat /etc/sudoers,操作系统会给他一个错误。但是,如果fredwheel组中,并且他发出了命令sudo cat /etc/sudoers,操作系统会要求他输入密码,并在重新验证他的身份后,为他运行该命令。

为了将psshPermitRootLogin=no一起使用,您需要使用sudo来切换用户。但是,pssh不喜欢交互,在要求用户输入密码时会出问题。因此,您需要修改sudo的默认配置,以便允许用户在不提供密码的情况下使用sudo(他们仍然需要密码才能登录)。为此,作为根用户,向/etc/sudoers添加下面一行:

%wheel ALL=(ALL) NOPASSWD: ALL

现在,要使用pssh,您需要以fredsudo的身份登录来执行您的系统管理命令,如下所示:

pssh -A -h servers.txt --user fred sudo put_your_command_here

13.4 安装 Web 应用防火墙

web 应用防火墙是位于您的 web 应用和互联网之间的一个软件。web 应用防火墙的目的是检查传入流量中已知与恶意意图一致的模式,然后阻止这些请求。web 应用防火墙不能弥补 web 应用中糟糕的编程,但它通常会阻止自动化黑客工具找到漏洞。

Apache 为它提供了一个易于安装的 web 应用防火墙。要安装它,只需以 root 用户身份执行以下操作:

yum install -y mod_security mod_security_crs
systemctl restart httpd

然而,对于我们的测试站点,web 应用防火墙可能会阻止使用 URL 中的 IP 地址的请求,因为这是许多黑客攻击的特征!事实上,我发现对于许多生产系统,我不得不禁用几个单独的规则来让 web 应用防火墙与我的应用一起工作。这可能是痛苦的,但总的来说这是值得做的。

web 应用防火墙将在日志文件/var/log/httpd/error_log中记录拒绝请求的规则。因此,您可以在日志中找到规则 ID 号,然后在配置中禁用它。只需将行SecRuleRemoveById IDNUM添加到文件/etc/httpd/conf.d/mod_security.conf中,禁用不需要的规则。

13.5 检查 Rootkits

一个 rootkit 是一个由入侵你的服务器的人安装的软件,它可以让你更容易地控制你的服务器进行邪恶的行为。如果你的服务器是安全的,就不太可能有人闯入,但是定期检查还是有好处的。

rootkit 检查的两个标准软件是rkhunterchkrootkitrkhunter目前是 EPEL 的一部分,可以安装:

yum install -y rkhunter

要运行程序,只需做rkhunter --check。请注意,它可能会产生错误的警告和误报,因此请务必检查日志,以了解在查看问题时,它具体发现了什么。

13.6 其他安全软件

您可以安装和使用许多其他安全软件包。重要的是要知道有什么措施可用,并看看它们是否符合您的需求。

一些额外的 Linux 通用安全包包括

  • logwatch :该工具在记录到可疑活动时分析日志文件和电子邮件管理员。

  • 这个程序寻找重复登录失败和其他可疑的行为,并将阻止看起来像他们试图闯入的 IP 地址。

  • 安全增强的 Linux 是一种操作模式,在这种模式下,Linux 内核为程序提供了非常细粒度的访问控制,限制了每个程序和用户可以做的事情。SELinux 可以提供很多风险缓解,但是它的设置相当复杂,并且很容易意外地阻止您自己的应用做它们需要做的事情。我们在本书中禁用了 SELinux,因为它需要大量的配置问题。

  • 这是本书中使用的标准防火墙管理应用。

  • AuditD :该程序查找并记录应用的可疑活动。

  • 远程系统日志:这不是一个程序,但是系统日志可以配置为记录到远程服务器,这样入侵者就不能通过修改日志文件来掩盖他们的踪迹。

应用安全性

最难保护的是应用本身。实际上,如果不写另一本书,我无法提供很多建议。尽管如此,要记住的最重要的事情是

  1. 防御性编码。

  2. 验证来自用户的每一条数据。

  3. 正确转义发送给用户的所有内容。

  4. 在执行操作或显示数据之前,一定要仔细检查用户的权限。确保仅仅知道一个 URL 或一个参数不会自动给用户不适当的权力。

  5. 对发送到外部命令或程序的任何内容都要非常小心。仔细检查你是否已经正确地转义或过滤了所有内容。

  6. 想象一下,如果有人恶意操纵数据和请求,会发生什么。

  7. 总是尝试使用“最佳实践”来编码(例如, https://phptherightway.com/ )。在编码时使用最佳实践可以避免其他人引入的安全问题,包括那些您后来在应用增长时不小心引入的问题,以及由于代码库中其他地方的更改而将当前安全的代码变成不安全的代码。

另外,请务必在 www.owasp.org 查看常见漏洞列表

为了保护您的服务器和应用,您可以也应该做许多其他的事情,但是希望这已经给了您一个需要考虑的事情的开始列表。PHP 有时会因为安全问题而获得不必要的坏名声。然而,其原因并不在于语言,而是因为它通常是安全经验较少的新 web 程序员学习的第一门语言。有一些资源可以帮助您开始这方面的工作,包括

  1. 作者 Ben Edmunds :这是一个简明扼要的 PHP 安全实践指南。

*** Chris Snyder,Thomas Myer 和 Michael Southwell 撰写的Pro PHP Security:这是一个更全面的关于 PHP 安全和安全原则的指南。这是一本较老的书,但核心安全原则没有改变。

 *   ***掌握 Linux 安全并强化*** **作者 Donald Tevault** :如果你的服务器没有正确配置,编写安全的 PHP 代码对你没有任何帮助。

 *   ***实用信息安全管理*** **作者托尼·坎贝尔**:这本书将帮助你在更高层次上理解什么是安全的,什么是受保护的,如何管理权衡,以及安全的最终目标是什么。

 **

然而,最重要的事情是始终考虑您的应用如何被滥用,并始终学习新的方法来主动防范这些事情。

posted @ 2024-08-03 11:25  绝不原创的飞龙  阅读(3)  评论(0编辑  收藏  举报