AWS-NodeJS-入门指南-一-

199 阅读1小时+

AWS NodeJS 入门指南(一)

原文:Beginning Amazon Web Services With Node.js

协议:CC BY-NC-SA 4.0

一、Amazon Web Services 入门

欢迎使用 Node.js 开始 Amazon Web Services!在本书的整个过程中,您将学习如何优化 Node.js 应用,以便在 Amazon Web Services (AWS)上部署。通过使用 AWS 托管您的应用,您可以利用一系列通常称为“云”的特性。您将了解 AWS 的一些核心特性,了解如何设计您的应用堆栈,并将您的应用集成到 AWS 环境中。虽然您可以轻松地将代码上传到服务器并结束工作,但是学习如何将 AWS 的各种功能整合到您的项目中,将允许您充分利用云的优势。但是这些好处是什么呢?

了解云

首先也是最重要的是可伸缩性,即快速部署额外资源来支持您的应用的能力。在 AWS 等云托管提供商激增之前,如果应用的需求超过了硬件资源,部署额外的服务器是一项昂贵而费力的任务,通常超出了初创公司和中小型企业的能力。有了 AWS 上托管的应用,您可以按需分配资源,启动新的服务器并保持应用在线。根据 AWS 白皮书《云架构:最佳实践》, 1

Traditionally, applications are built for fixed, rigid and preconfigured infrastructures. Companies never need to configure and install servers every day. Therefore, most software architectures cannot solve the rapid deployment or reduction of hardware. Because the supply time and upfront investment for acquiring new resources are too high, software architects have never invested time and resources to optimize hardware utilization. This is acceptable if the hardware running the application is not fully utilized. The concept of "flexibility" in architecture is ignored, because the idea of having new resources in a few minutes is impossible.

响应应用需求的能力称为弹性。如果不是战略行动,用 100 台服务器替换一台服务器是没有用的。当需要手动启动和配置额外的服务器以响应需求时,这样做的成本导致许多企业/机构过度分配资源。这些额外的服务器将会一直运行,并且需要相应的维护,而不是为了流量峰值而增加额外的服务器。凭借 AWS 的灵活性,可以检测到流量峰值,并自动部署额外的资源。当需求恢复正常时,应用可以自动缩减到正常状态。使用您需要的东西,并为您使用的东西付费——这个简单的概念彻底改变了 web 应用开发。这节省了时间、金钱和精力,并降低了企业级软件的准入门槛。

正如您所看到的,可伸缩性和弹性是您的应用的重要属性。这些好处也意味着以不同的方式思考你作为软件开发人员和架构师的角色。从在固定硬件环境中开发转向云计算环境意味着我们现在除了软件开发人员之外,还是云架构师。这构成了开发人员必须考虑 web 应用的方式的重大变化。对于我们中的许多人来说,不再需要系统管理员或数据库管理员来维护基础设施。(他们在亚马逊、IBM、谷歌、Rackspace 等公司工作。,现在。)相反,虚拟硬件管理/云架构现在属于我们的领域。我们不仅从最佳编码实践和组织的角度考虑我们的应用,我们还必须考虑如何利用我们作为开发人员可用的大量资源。这意味着我们必须熟悉 AWS 的特性,并了解如何设计、配置和维护虚拟主机环境。

随着您学习如何成为一名云架构师,您将了解到许多 AWS 特有的优秀特性,以及弹性和可伸缩性的一般原理。AWS 的许多功能被组织成一系列重叠的服务。它们中的许多都有多余的特性,允许我们在设计系统时做出一些创造性的决定。所有这些服务都运行在位于亚马逊全球许多数据中心的虚拟化硬件环境中。我们将在接下来的章节中探讨其中的一些服务。

您将熟悉 Amazon Web Services 的基础知识。我已经讨论了使用 AWS 的一些一般原则和优点。稍后,我将更详细地讨论一些核心服务,以及我们与它们交互的不同方式。在我们开始之前,从同一页开始是很重要的。

本书中的方法

本书假设您至少已经是一名 Node.js 开发初学者,希望扩展您的技能,包括设计和开发一个具有可伸缩性和弹性的 Node.js 应用。您应该对 web 应用开发中的主要概念有一个基本的了解。您应该知道什么是 RESTful web 服务,熟悉 Git 或 SVN,并且手边有一个代码编辑器。

设计和开发应用是一个创造性的过程。这意味着必须做出许多高度主观的决定。首先,我们将使用 Amazon RDS(关系数据库服务)在 AWS 上托管 MySQL 数据库。许多 Node.js 开发人员更喜欢 MongoDB 而不是 MySQL。这是非常好的!然而,示例应用和后续说明主要关注 MySQL,它适合我们应用的需要。如果您想使用这本书来部署一个使用 MongoDB 的应用,您必须能够相应地重写数据库连接和查询。在这两种情况下,您都需要有关于数据库语言的基础知识。这只是一路上要做的许多创造性决定中的一个。在下一个项目中,您可能不同意某些观点或需要不同的方法,但您最终会更好地在下一个项目中做出这些决定,并且您将准备好作为开发人员和架构师与 AWS 服务一起工作。

为了在我们的应用中获得云计算的好处,您将了解可以集成到我们的应用中的各种 AWS 服务。这种集成将通过两种方式进行:通过 AWS 控制台中多个服务的配置和定制,以及通过 AWS SDK 在我们的应用代码库中以编程方式进行。在这种情况下,我们将使用 JavaScript AWS SDK,它旨在用于 Node.js 应用。然而,有针对各种服务器端语言的 SDK,书中的许多经验甚至对使用不同语言的开发人员有用。将 AWS 服务集成到具有类似功能的 PHP 应用中不会有太大的不同。

实际上,您可以在 AWS 管理控制台(AWS 控制台)中执行的每个任务都可以通过编程来执行,反之亦然。AWS 控制台通过提供对提示和文档的访问,以及为抽象概念提供可视化参考,增加了流程的清晰度。当你思考 AWS 能做的一切时,使用控制台会有很大帮助。你可能会问,“我怎么知道什么时候使用控制台,什么时候使用 SDK?”撇开学习曲线不谈,这是一个非常主观的话题。您可能会及时决定哪些规则最适合您的工作流程,但我们可以遵循一些基本规则。

Note

还有第三种与 AWS 服务交互的方法:AWS 命令行界面(CLI)。在最后一章之前,我们不会使用 CLI。

首先,例行任务肯定应该在 SDK 中执行。如果您的应用需要将每日日志或报告存储在 S3 存储桶中,您可能希望通过编程来完成。涉及文件传输的常规任务尤其适合 SDK。在我们的应用中,用户上传的图像将存储在一个 S3 桶中,供应用使用。当您了解如何做到这一点时,我将清楚地说明为什么使用 AWS 控制台是一个坏主意。现在,要知道在这个用例中,SDK 是一个非常省时的工具。这同样适用于事件驱动的任务,除了 CloudWatch 可以检测到它们的地方(比如服务器离线)。例如,如果您的应用必须在用户注册您的应用时生成电子邮件,您希望您的代码在它发生时立即触发它。我们将在第六章和第七章中更详细地探讨这些类型的事件。

然而,为了清楚起见,我们将在 AWS 控制台上执行许多一次性任务。与调试只需运行一次的代码相比,使用 AWS 控制台的 GUI 来诊断错误或完全避免错误要容易得多。例如,虽然我们可以通过编程方式创建一个警报,在应用对请求的响应缓慢时通知我们,但为了清晰起见,我们将在 AWS 控制台中这样做。当你学习的时候,你会发现自己用 AWS SDK 制造了可笑的错误。糟糕,我不是有意在无限循环中创建服务器实例。如果您在完成课程后感到雄心勃勃,您可能会编写本书中许多 AWS 控制台任务的脚本。我称之为额外学分。

在向您介绍我们将使用的 AWS 产品之前,重要的是要重申,使用 AWS 实现相同目标的方法不止一种。本书的范围仅限于一组产品,这些产品可以很好地协同工作,以实现我们的应用可伸缩性和弹性的目标,以及我们需要的功能集和合理的预算。然而,许多 AWS 服务有一些冗余。例如,我们将使用 OpsWorks 来管理我们的应用堆栈,但这不是唯一的服务。许多 AWS 开发人员更喜欢使用 Elastic Beanstalk 而不是 OpsWorks,因为它提供了简单性,还有其他原因。久而久之,AWS 服务已经变得越来越有凝聚力,所以我希望学习如何使用一些产品会让你走上了解更多、更轻松的道路。

许多 AWS 产品都使用 EC2 实例。EC2 或弹性计算云是 AWS 的基础。实例实际上是运行您选择的操作系统的虚拟服务器,托管在亚马逊的众多数据中心之一。该实例不固定在单个硬件上;它是在一台机器上运行的软件过程;如果那台机器崩溃或损坏,这个过程将在另一台机器上继续。在最坏的情况下,AWS 数据中心的硬件故障会中断您的应用,但是内置在云中的冗余将防止您的数据丢失。如果您的应用没有响应,您可以在 http://status.aws.amazon.com/ 检查所有 AWS 服务的状态。

当您创建和运行 EC2 实例时,您是在一个或多个 AWS 数据中心租用计算资源,这些数据中心是按地理区域组织的。这些资源的价格基于使用的能力和小时数。EC2 使用分层定价结构,由此 EC2 硬件规格(时钟速度、RAM 等)。)是根据它们的相对大小来命名的。当您创建一个实例时,资源被分配给您的实例,并且您的计费开始。不管你实际使用了多少你租用的资源。您需要为您的价格等级付费。您可以花很多钱保留最大的可用实例,但这将是一种浪费,就像前面描述的传统部署方法一样。相反,我们将使用其他 AWS 服务,根据我们的需要来扩大和缩小我们的 EC2 实例,以获得最大的性价比。由于我们现在也是云架构师和开发人员,我们将尽最大努力做到不吝啬。为您使用的东西付费;花钱买什么用什么。

虽然您可以手动创建和配置 EC2 实例,但是使用管理工具(如 OpsWorks)可以大大简化流程,并且可以大大降低设置复杂系统时的人为错误风险。我们将使用 OpsWorks 来管理我们的应用层、部署和应用的许多其他重要方面。OpsWorks 将是您配置我们项目的主要界面,也是您需要掌握的第一件事。在第二章中,将向您介绍 OpsWorks 的核心特性,并配置您的应用以部署到 EC2 实例。在本章结束时,您的应用将通过 OpsWorks 部署到 EC2 实例中。

在第三章中,您将使用 Amazon RDS 向您的应用添加一个可扩展的 MySQL 数据库。您还将了解如何向您的应用添加额外的服务器实例,以及如何设置负载平衡器来管理流量。在第四章和第五章中,你将学习如何在 CloudFront 中建立 CDN,以及如何处理文件传输和缓存。您还将了解路由 53 的 DNS 配置。在第六章的和第七章的中,您将学习如何使用 SES(简单电子邮件服务)从您的应用发送电子邮件,您将学习如何使用 CloudWatch 来监控您的应用。最后,在第八章中,您将通过将关键 API 端点限制为 HTTPS 连接来为您的用户保护您的应用。

尽管我们构建的应用相对简单,但使用云计算却不简单。除了 Node.js 的知识之外,完成这本书还需要一些工具和服务。

要求

当然,您需要 Node.js 和 MySQL 开发的所有工具:IDE、本地 MySQL 数据库和 Git 或 SVNclient(除非您更喜欢命令行)。此外,您还需要一个域名注册商、SSL 证书提供商,当然还有 AWS 的账户。

AWS 帐户

你首先需要的是你自己的 AWS 账户。您可以通过进入 http://aws.amazon.com/ 并点击注册按钮来创建一个。您需要提供帐单信息来完成注册过程。您现在应该完成此过程;没有激活服务的账户是免费的。随着您继续学习课程并激活更多服务,您将开始积累一些支出。注册后,您可以在这里随时查看您的支出: https://console.aws.amazon.com/billing/home (见图 1-1 )。

A978-1-4842-0653-9_1_Fig1_HTML.jpg

图 1-1。

The Billing and Cost Management dashboard

在上图中,您可以看到主计费和成本管理仪表板(注意,在此图中整个屏幕都是可见的,但情况并非总是如此)。您当前的月账单以粗体大字体突出显示,下方是一个进度条,范围从零到上个月的账单。这是给你一个每月费用的可靠预测,尽管它们可能会根据使用情况的变化而波动。不过,不要让我的账单吓到你,我的账户上运行的不仅仅是示例应用。

还有一个用于启用警报和通知的标注。您可以将 AWS 配置为在达到特定成本指标时向您发出警报。这样做的效用是不言自明的。要是电力公司提供这个功能就好了!

右边是一个圆形图和你的账单明细。如您所见,EC2 可能是您最大的开销,其次是 RDS 或您使用的任何数据库服务。您可以预期您的数据库服务账单会随着您的应用所做的查询量的增加而增加,因此也会随着您的用户群的扩大而增加。

你只为你使用的东西付费,但是如果你过度使用权力,你会在你的银行账户中感觉到。要完成这本书里的课程,你无疑会产生一笔不小的费用。确切的费用将取决于你完成这本书的速度,以及你是否让你的应用 24/7 运行。这里还有许多其他与报告和计费相关的工具和选项,不在这些课程的范围之内。我们将在本章的后面返回 AWS 控制台,开始配置帐户以用作生产环境。

Tip

为了省钱,你可以在不上课的时候关闭很多资源。

域注册

你需要自己的领域来完成所有的课程。这并不重要,只要你有能力改变域名服务器。如果你还没有域名,你可以在任何一个注册机构注册一个,比如 GoDaddy ( www.godaddy.com )。那不是对 GoDaddy 的认可;注册商大多都一样。预计这一成本约为每年 13 美元。你现在也可以直接在 AWS Route 53 仪表板上注册你的域名,如果你想把你所有的移动部件放在一个地方,这是很方便的。

SSL 证书

这个话题在第八章会有详细介绍,所以你现在不需要这个。但是,您必须向证书颁发机构提供有效的 SSL 证书。这至少每年要花费 9 美元。

代码库

接下来,当您在 AWS OpsWorks 中设置应用时,您会发现您需要选择一种部署应用的方式。你会有很多选择。最简单也可以说是最安全的部署方式是通过 GitHub ( https://github.com )账户,或者其他具有类似功能的 Git 账户托管服务。我们特别想要一个支持自动部署或者可以通过 SSH 连接被 AWS 访问的。下载示例项目后,您会希望将该项目添加到您自己的存储库中,您可以根据需要对其进行修改,并配置您自己的部署过程。

下载示例项目

您不会通过复制冗长的、一步一步的代码示例来开始编码课程。相反,您可以从这里下载示例项目开始:( www.apress.com/9781484206546 )。然后,我们将回顾预打包的代码,并在接下来的课程中反复修改。在执行此操作之前,请确保安装了最新版本的 Node.js(或至少 0.10.26 版)。下载示例项目的 zip 文件,或者将分支拖到您的机器上,并在代码编辑器中打开目录。

局部环境

您应该已经在本地计算机上安装了 Node.js,并且能够在命令行界面中运行它。开始时,您还需要一个本地 MySQL 数据库。最简单的方法就是用 MAMP ( www.mamp.info/en/ )或者 XAMPP ( www.apachefriends.org/index.html )。我只是假设您可以自己安装它,而不需要一步一步的指导(提示:转到网站并单击下载)。与 PHP 不同,你不需要 MAMP/XAMPP 来运行你的应用,但这是建立和访问本地 MySQL 数据库最简单的方法。你可能还想安装 MySQL Workbench ( www.mysql.com/products/workbench/ ),但是我会在后面详细讨论这个问题。

我们的应用的前端是一个 RESTful JSON API,供 web 或移动客户端使用。我们将在整个课程中与 API 进行交互,但是我们还没有客户端。因为我们将向 API 发出GETPOST HTTP 请求,所以在开发过程中,您需要的不仅仅是一个 web 浏览器来正确测试应用。幸运的是,在所有操作系统上都有许多 REST 客户端可用,这使得与像我们这样的 API 接口变得更加容易。有一个很好的 Google Chrome 扩展叫做 Advanced Rest Client,它应该可以很好地完成这项工作。你可以在 https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo 找到。

ExpressJS

您可能希望熟悉 ExpressJS,这是 Node.js 的一个流行的(可能是最流行的)web 应用框架。使用 ExpressJS 将完成大量路由和解析 HTTP 请求的繁重工作,并简化通过模板引擎、raw text/HTML 或 JSON 发送响应的过程。此外,ExpressJS 接受一些方便的全局配置,并允许您创建或安装中间件。这允许您通过公共函数传递对服务器的所有 HTTP 请求,这使得集成身份验证、字段验证和错误处理等功能变得容易。

该示例项目针对最新的主要版本 Express 4 进行了优化。如果你更熟悉 Express 2 或者 Express 3,那么这个版本有显著的变化。你应该复习 http://expressjs.com/guide/migrating-4.html 来提高速度。

现在让我们让您熟悉一下这个示例项目。已经为您创建了一个简单的 RESTful web 服务的一些基本功能。请记住,这本书的目标不是教你如何编码,也不是要让功能具有开创性。我希望,通过学习一个简单的应用,你会对如何将 AWS 集成到你自己的工作中有一个清晰的认识。

示例项目

虽然这在技术上不是 Node.js 指南,但我们将使用一个示例项目来将本书中的课程放在上下文中。样本项目将提供一个真实世界的应用来开发,而我们与 AWS 合作。一开始,我们将从一个基本的应用开始——它需要做大量的工作才能在现场使用。我们将在整本书中做一些这样的工作。

概观

示例项目是一个非常简单的基于照片的社交媒体应用的代码基础。功能很简单:用户可以注册一个帐户并登录。他们可以创建相册对象,相册对象由标题和照片对象集合组成。用户可以上传带标题的照片到相册,一次一张(但我们还没有存储和提供文件)。用户可以看到所有其他用户的列表,并获得特定用户的所有相册。用户通过 JSON 格式的 RESTful API 与应用进行交互,记录存储在 MySQL 数据库中。

随着课程的进行,我们将为应用添加更多功能。它将使用适当的安全用户认证方案。用户注册帐户时会收到欢迎电子邮件。我们将图像文件存储在 CDN 中,以便轻松快速地存储。应用日志将被生成并存储在 AWS 中,它将利用一系列的 AWS 服务。

像这样的应用有很多使用案例。从社交网络到新闻阅读器应用,无数平台的核心都拥有类似的功能。只需做一点工作,您就可以通过在用户之间创建一种关系来添加好友或关注的概念,以及用户创建和删除他们的方法。您可以给照片添加标签、地理位置数据和注释。

我们的应用输出是 JSON 格式的,但是 web 模板也可以很容易地由应用生成。请记住,虽然应用在这一点上很简单,但它会很快变得更复杂。示例项目的目的不是让您对 Node.js 编码技能感到眼花缭乱,而是提供一个简单、清晰的代码库,该代码库易于理解,并且可以扩展到更复杂的应用中。

Note

项目的大部分组织是使用express命令创建的,它会自动生成您的项目。如果您在课程结束后开始自己的项目,那么重新创建项目的组织将会很容易。

源代码组织

让我们从在代码编辑器中打开项目开始,看看内容。您应该在项目目录中看到以下内容:

/lib

/public

/routes

/setup

/views

server.js

package.json

在代码编辑器中打开package.json。您将看到一些 JSON 格式的项目信息和配置选项。您将看到 dependencies 属性,类似于以下内容:

"dependencies": {

"express": "∼4.8.6",

"body-parser": "∼1.6.6",

"cookie-parser": "∼1.3.2",

"morgan": "∼1.2.3",

"serve-favicon": "∼2.0.1",

"debug": "∼1.0.4",

"jade": "∼1.5.0",

... // additional dependencies truncated

}

这些是运行应用所需的其他 npm 模块。当您使用 ExpressJS app generator 命令时,它们中的许多会自动添加到依赖项列表中。您必须在本地安装这些程序,才能在本地运行应用。打开您的命令行界面(例如,OS X 的终端),并导航到此目录。在命令行中键入以下内容:

npm install

该命令将在同一目录中查找一个package.json文件,并尝试安装依赖属性中的所有模块。在几分钟的时间内,您应该会看到所有的软件包都下载并安装完毕。

让我们浏览一下项目中的其他一些文件和目录。总的来说,这个项目的结构遵循模型视图控制器(MVC)设计模式。如果您不知道这是什么,它只是将您的代码库组织成三种类型的类之间的逻辑分离的一种方式。在 MVC 中,这三种类型是模型,或对象定义和操作;应用的视图或输出;以及控制器,它包含所有的逻辑,根据应用的输入决定向模型发送什么信息/从模型中检索什么信息。当然,这是对概念的简化,但是当我们浏览它时,它会让您了解它是如何工作的。

首先,我们将探索lib目录,其中包含globals.js/model。正如前面所描述的,model目录包含了我们将要使用的各种对象的所有类定义。因为应用输出将主要是 JSON,或者由 JSON 填充的模板,所以模型本身将比使用 PHP 抽象得多,例如,如果使用 PHP,您可能会设计一个类,使用从数据库检索的属性来实例化对象。

model目录中的文件主要从控制器接收与数据库交互的命令,并将抽象对象发送回控制器作为响应。因此,示例应用中对象的属性是动态创建的。对于这种性质的应用,对象的生命周期非常短,因此使用灵活的数据模型通常是有意义的。如果您愿意,您可以创建自己的类,实例化它们,并填充它们,而不是遵循示例项目中的方法。如果这样做有助于更有效地维护您的代码库,那么这样做并没有错。

MVC 模式的控制器在/routes目录中。每个文件对应于用户请求中的一个目录路径(例如,/ photos /1/ users /adam),并处理对该路径内的 URL 的所有请求。它将从相应的模型中检索所需的任何数据,并发送响应。

那么风景在哪里呢?在响应是 JSON 的情况下,视图就是 JSON 数据本身。不过作为 ExpressJS4 app,原生支持 jade 模板引擎( http://jade-lang.com/ )。如果您要开发 web 模板,这些文件将位于/views目录中。在这种情况下,当考虑 MVC 时,视图并不总是在模板文件中可访问的。无论内容类型是text/html还是application/json,视图总是 HTTP 响应。

接下来,public目录存储所有的静态资产,比如前端模板使用的样式表、图像和 JavaScript 文件。这个目录是由 ExpressJS 自动生成的。在第四章中,你将学习如何使用 AWS CloudFront 更有效地为用户提供这些文件。

你已经了解了server.js。如果查看这个文件,您会看到请求路由是如何映射到/routes目录中的文件的。您还将在这里看到一系列中间件和 ExpressJS 配置。目前,这些都不太重要。

你也应该知道/lib/globals.js。这个文件只是存储全局配置和常用值的一个方便的地方,便于参考,不会污染全局名称空间。开发人员对于如何在他们的代码中处理这种类型的特性有各种不同的想法,所以这仅仅是许多方法中的一种。

示例项目还包括一个名为/setup/photoalbums.sql的文件。这是您可以导入到本地 MySQL 数据库的数据库模式。如果您打算在本地环境中测试示例应用,那么您需要将它导入到一个名为 photoalbums 的本地数据库中。稍后,您还需要将这个文件导入到 Amazon RDS 数据库中。

配置和启动

您可以通过以下两种方式之一启动应用:

  • 在命令行上键入node app.js
  • 在命令行上键入npm start

如果应用编译成功,您应该可以在http://localhost:8081查看欢迎页面(假设您使用了 8081 端口)。如果没有,您应该会在命令行界面上看到一个错误。如果是这样,很有可能是您的某个依赖项没有正确安装。如果是这种情况,尝试再次运行npm install

另一个可能的错误是端口 8081 在您的机器上不可用。当我们将应用部署到云中时,我们将使用端口 80。如果您必须改变这一点,打开/lib/globals并将applicationPort属性更改为不同的值,比如 8081。如果您的应用试图打开一个不可用的端口,错误将如下所示:

events.js:72

throw er; // Unhandled 'error' event

^

Error: listen EACCES

at errnoException (net.js:904:11)

at Server._listen2 (net.js:1023:19)

at listen (net.js:1064:10)

at Server.listen (net.js:1138:5)

如果这些建议都不能解决您的问题,您将不得不阅读命令行界面中的错误,并尝试自己解决它。只是提醒一下:任何时候你修改代码,你都必须重新编译你的应用。如果你习惯于使用 PHP 或前端 JavaScript 并刷新你的浏览器来测试一个补丁,这可能需要一些时间来养成定期重新编译你的应用的习惯。

使用示例应用

祝贺您让示例应用正常运行。除了安装几个 npm 包之外,您不需要在您的机器上做任何进一步的配置。从现在开始,示例项目源代码就是您的了!您现在应该将它登记到一个存储库中,这样如果您迷路了,您就有一个好的快照可以返回。

在余下的课程中,我们将在浏览器中的源代码、命令行界面和 AWS 控制台之间来回切换。让我们更深入地研究样本代码,以便更好地理解它是如何工作的。首先,我们来看看server.js。在文件的开头,您会看到所有包含的 npm 模块。从第 8 行开始,您将看到我们添加到项目中的所有源文件:

var routes = require('./routes/index');

var users = require('./routes/users');

var photos = require('./routes/photos');

var albums = require('./routes/albums');

var globals = require('./lib/globals');

再往下,在第 27 行左右,您会看到它们的使用位置:

app.use('/', routes);

app.use('/users', users);

app.use('/photos', photos);

app.use('/album', albums);

现在,我们将探索我们在应用中注册的每条不同路线的功能。

回家路线

第一行将 HTTP 请求定向到我们的应用到位于/routes/index.js的文件的根路径。我们可以在这里稍作停留,因为这条路线仅用于显示欢迎页面,所以你知道应用运行正常。在文件的顶部,您会看到

var express = require('express');

var router = express.Router();

我们构建应用的方式是,我们需要在每个控制器(路由)中包含expressexpress.Router(),以便与发送到所述控制器的 HTTP 请求进行交互。这意味着这些路由文件中的每一个都将在顶部实例化名为expressrouter的变量。

Note

完全有可能以一种方式组织你的代码,使你的控制器不必要求express,但是我们在这一点上遵循 ExpressJS 模板。

index.js中只登记了一条路线:

/* GET home page. */

router.get('/', function(req, res) {

res.render('index', { title: 'Photoalbums' });

});

如您所见,我们没有对请求做任何事情,只是发送了一个响应。不读取参数,不与模型交互,等等。我们简单地用来自views文件夹的索引模板和标题“Photoalbums”来响应。我们应用的其余部分使用 JSON 进行响应,但是对于这个登录页面,我们使用一个由 jade 模板生成的 HTML 响应。我不会详细讨论这个问题。需要知道的重要一点是,您可以使用 ExpressJS 响应对象发送各种不同的响应类型。更多信息请点击: http://expressjs.com/4x/api.html#response

/users/的请求被路由到/routes/users.js,依此类推。为了在我们的应用中实现,所有的路径或控制器文件都必须在server.js中注册。所有的控制器都是以同样的方式设计的,所以我们将从用户开始,作为一个很好的例子来说明一切是如何协同工作的。在server.js中,我们看这条线

app.use('/users', users);

并且知道按照要求去/routes/users.js看看会发生什么。

用户路线

目前,我们只有框架功能。这款应用的功能刚刚够基本的互动。随着课程的进行,用户会变得比现在更加健壮。为用户提供的现成功能包括

  • 注册账户(POST /users/register)
    • 参数:usernameemailpassword
  • 登录账户(POST /users/login)
    • 参数:usernamepassword
  • 账户注销(POST /users/logout)
  • 查看所有用户(GET /users/)
  • 按用户查看所有相册(GET /users/user/:user)
    • 参数:user

同样,这个示例代码只是为了帮助您入门。只需一点点努力,您就可以为用户帐户添加尽可能多的功能。您可以向用户数据库表中添加其他字段,如个人资料图像、个人简历或用户网站。然后,您可以添加一个更新方法,允许用户首先注册一个帐户,然后提交更新来完成他/她的配置文件。你可以要求用户在注册后确认他们的名字。您可以允许他们重置用户名或密码。记住这一点,让我们看看代码。在文件的顶部,您会看到一组不同的包含文件,如下所示:

var express = require('express');

var router = express.Router();

var model = require('./../lib/model/model-users');

因为我们在用户控制器中,所以我们知道我们会想要频繁地访问用户模型。我们将继续在这里包括用户模型文件。事实上,你可以期望每个控制器在顶部包含它的模型。然而,一些路线将需要包括多个模型类,尤其是当功能变得更加强大时。为了一致起见,我们可以将默认的模型变量命名为model,并将任何附加的模型命名为modelPhotomodelAlbum等。如果我们只需要访问另一个模型一次呢?我们将根据具体情况确定任何其他型号的可变范围。

让我们来看看用户控制器中的一个典型的router方法。

/* GET users listing. */

router.get('/', function(req, res) {

model.getAllUsers(function(err, obj){

if(err){

res.status(500).send({error: 'An unknown server error has occurred!'});

} else {

res.send(obj);

}

});

});

前面的代码是我们在 ExpressJS 控制器类中路由和处理 HTTP 请求的典型方式。从第一行可以看到,GET/users/路径的请求将在这里处理。如果有任何参数需要验证,我们会在方法开始时进行验证。然而,这只是检索所有用户列表的一种方式;没有用户输入。随着应用用户群的增长,我们可能希望允许用户通过该请求传递参数,以支持所有用户的分页列表。现在,我们可以保持简单,忽略任何用户输入。

我们立即用model.getAllUsers()从模型中检索数据。在这种情况下,没有参数被传递给模型。当我们收到从模型对象返回的消息时,我们检查它是一个错误对象还是我们想要使用的数据。

在某些情况下,我们会希望将模型对象的错误返回给用户。这看起来会像下面这样:

model.getAllUsers(function(err, obj){

if(err){

res.send(err);

}

});

然而,我们将在大多数情况下避免这样做。模型返回的一些错误可能是 MySQL 数据库查询错误。向用户公开关于数据库表的信息不是一个好的安全实践,MySQL 错误信息也不可能对我们的任何最终用户非常有用。对于控制器来说,检查从模型收到的错误并向向我们的 API 发出请求的客户机发送合适的消息会更合适。接下来,我们来看看模型,看看getAllUsers是做什么的。

与 routes 文件一样,让我们从顶部开始。

var mysql = require('mysql');

var globals = require('./../globals');

var connection = mysql.createConnection(globals.database);

当然,所有模型类中都需要mysql模块。我们将使用这个模块连接到数据库并执行所有查询。

Note

https://github.com/felixge/node-mysql 可获得mysql模块的完整文档。

如前所述,globals是一个存储公共变量而不污染global名称空间的对象,这里使用它是为了方便。MySQL 数据库连接被初始化为connection,使用存储在globals中的数据库配置。让我们看看第一种方法,我们已经知道了,getAllUsers()

function getAllUsers(callback){

connection.query('SELECT username, userID FROM users', function(err, rows, fields){

if(err){

callback(err);

} else {

callback(null, rows);

}

});

}

这个方法是模型获取函数所能得到的最简单的方法。没有要验证的参数,没有要转义的用户输入,也没有任何其他中间函数。我们简单地从数据库中检索所有用户,并将他们返回给routes/user中的回调函数。现在,让我们滚动到文件的最底部,注意exports赋值。

exports.getAllUsers = getAllUsers;

exports.getUser = getUser;

exports.createUser = createUser;

exports.loginUser = loginUser;

exports.logoutUser = logoutUser;

这几行非常重要,因为它们是你在这个文件中公开一个方法的手段;否则,所有方法都是私有的,或者对其他对象不可访问。如果您使用这个示例应用构建额外的功能,很容易忘记这一步。

回顾——事情的顺序

回到/routes/user一会儿,您应该看到请求是如何被处理的。以下是客户如何从我们的应用中检索用户列表的快速回顾:

server.js is listening for all HTTP requests at the designated port.   The client makes an HTTP GET request to /users/.   Server.js forwards the request to the controller at /routes/users.   The controller at /routes/users notes that the request is for “/” relative to “/users” and passes the request to the corresponding listener method.   The GET/” listener in the controller calls model.getAllUsers().   The model method getAllUsers() queries the database, processes the results, and returns the data set to the controller.   The controller populates the response object with data.   server.js sends an HTTP response with the data requested by the user.  

这些都有意义吗?如果您已经熟悉 ExpressJS,它可能没有告诉您任何新的东西。如果你是第一次学习这个,不要担心;在接下来的课程中,我们将花大量时间来学习这些概念。最好的学习方法是试着写下你自己的路线,看看怎样才能让它们发挥作用。永远不要低估试错的力量!

示例-使用参数

让我们回到/routes/users来看另一个概念。到目前为止,我们已经看到了在没有任何参数或客户端输入的情况下处理一个基本请求。当你不涉及任何变量时,失败的可能性很小。一旦我们开始接受来自客户的特定请求,故障点就开始迅速增加。我们可以以/users/login路线为例来看。

/* POST user login. */

router.post('/login', function(req, res) {

if(req.param('username') && req.param('password') ){

var params = {

username: req.param('username').toLowerCase(),

password: req.param('password')

};

model.loginUser(params, function(err, obj){

if(err){

res.status(400).send({error: 'Invalid login'});

} else {

res.send(obj);

}

});

} else {

res.status(400).send({error: 'Invalid login'});

}

});

这个路由接受 HTTP POST请求,并期望客户机已经发送了用户名和密码。然而,我们不能假定两个参数都包括在内。因此,在我们尝试使用这些参数之前,我们必须检查它们是否存在。如果参数已经包括在内,我们继续。否则,将发送无效登录错误来响应请求。

我们可以使用控制器来验证输入和格式化参数以传递给模型。在这种情况下,在我们将用户的登录凭证发送到模型之前,我们还希望实施一条规则:用户名应该不区分大小写。用请求参数实例化params对象,当params.username被设置时,用户名被转换成小写。然后我们可以看看这个模型,看看接下来会发生什么。

function loginUser(params, callback){

connection.query('SELECT username, password, userID FROM users WHERE username=' + connection.escape(params.username), function(err, rows, fields) {

if(err){

callback(err);

} else if(rows.length > 0){

var response = {

username: rows[0].username,

userID: rows[0].userID

}

callback(null, response);

} else {

var error = new Error("Invalid login");

callback(error);

}

});

}

现在,如果用户名有效,我们将从数据库中选择用户。在未来,我们将解密用户的密码来验证用户。目前,我们会自动返回一个成功响应,其中包括经过身份验证的用户的用户名和用户 ID。这里重要的一课是,在将客户机输入发送到模型之前,控制器用于净化和验证客户机输入。以这种方式分离关注点并封装功能是一个好主意。模型期望将一个具有必要属性的对象发送给它的方法,控制器负责构造该对象或拒绝格式错误的请求。如果您遵循这种模式,您将能够在不同的场景中安全地重用模型类中的公共方法,并且从长远来看,您的应用将更容易维护和调试。

尝试一下

是时候启动应用,看看它的运行情况了。如果应用没有运行,打开命令行界面,导航到应用目录,然后键入node server.js。打开 REST 客户端,输入以下 URL:

http://localhost:8081/users/register

如果你在不同的端口上运行应用,记得更换 8081。将您的 HTTP 方法设置为POST。添加名为usernamepasswordemailPOST参数。您可以在这些参数中输入任何想要的值。发送请求!

您应该会收到以下成功消息:

{

"message": "Registration successful!"

}

接下来,您应该能够使用刚刚注册的用户名登录。删除email参数,并将 URL 改为

http://localhost:8081/users/login

您应该会收到以下用户名和用户 ID 作为响应:

{

"username": "adam",

"userID": 1

}

您现在可以在浏览器中向http://localhost:8081/users/发出一个GET请求。您应该会在响应中看到您创建的用户。您可以创建其他用户,他们也会出现在此响应中。在这里,您可以使用登录响应中的用户 ID 为您的用户创建相册和照片。我将很快更详细地讨论这些。现在,让我们在/users/user/:user向用户细节 API 端点发出一个快速的GET请求,用您刚刚注册的用户名替换:user。您应该会看到类似以下的响应:

{

"username":"adam",

"userID":1,

"albums":[]

}

目前,这里没有太多新的信息。但是一旦你开始为这个用户创建相册,你将在这里得到关于他们的信息。接下来我们来看看专辑。

相册

每个用户可以拥有无限数量的相册。相册对象非常简单,由标题和用户 ID 组成。任何数量的照片也可以与相册相关联。

相册具有以下功能:

  • 创建新相册(POST /albums/upload)
    • 参数:userIDtitle
  • 按 ID 获取相册,包括相册中包含的所有照片(GET /albums/id/:albumID)
    • 参数:albumID
  • 删除相册(POST /albums/delete)
    • 参数:albumID

让我们打开/routes/albums.js仔细看看。在顶部声明的变量应该已经很熟悉了。在这种情况下,您可以看到model变量被设置为/lib/model/model-albums.js。第一条路线将允许我们创建一个相册。使用注册时收到的用户 ID,将 RESTful 客户机指向http://localhost:8081/albums/upload,将方法设置为POST,并为用户 ID 和标题添加字段。你可以把任何你想要的名字放在标题里。我们现在就用“你好世界”吧。您应该会收到以下响应:

{

"id":7,

"title":"Hello World"

}

In /routes/albums.js, find the route:

/* POST create album. */

router.post('/upload', function(req, res) {

if(req.param('title') && req.param('userID')){

var params = {

userID : req.param('userID'),

title : req.param('title')

}

model.createAlbum(params, function(err, obj){

if(err){

res.status(400).send({error: 'Invalid album data'});

} else {

res.send(obj);

}

});

} else {

res.status(400).send({error: 'Invalid album data'});

}

});

首先,验证所需的参数,并用我们传递给它的参数构造一个名为params的对象。虽然您可以将请求参数直接传递给模型,但是使用中间变量是一个好习惯,主要是为了可读性。让我们转到/lib/model/model-albums.js的模型文件,看看在createAlbum()发生了什么。

function createAlbum(params, callback){

var query = 'INSERT INTO albums SET ? ';

connection.query(query, params, function(err, rows, fields){

if(err){

callback(err);

} else {

var response = {

id : rows.insertId,

title : params.title

};

callback(null, response);

}

});

}

我们在这里传递的所有参数都被插入到 albums 表的新行中。然后,我们用自动递增的 ID 和标题创建一个响应对象。请注意,ID 可以作为rows.insertId访问。当在 MySQL 数据库中创建一行时,rows参数是一个单独的对象。您会发现SELECT查询的行为略有不同。

接下来的路线允许你通过 ID 获取相册。

/* GET album by ID */

router.get('/id/:albumID', function(req, res) {

if(req.param('albumID')){

var params = {

albumID : req.param('albumID')

}

model.getAlbumByID(params, function(err, obj){

if(err){

res.status(400).send({error: 'Invalid album ID'});

} else {

res.send(obj);

}

});

} else {

res.status(400).send({error: 'Invalid album ID'});

}

});

这很简单。如果请求中包含相册 ID,控制器将从模型中检索相册并发送相册数据作为响应。您可以使用刚刚创建的相册中的相册 ID。现在,前往/lib/model/model-albums.js并找到getAlbumByID()方法。

function getAlbumByID(params, callback){

var query = 'SELECT * FROM albums WHERE albumID=' + connection.escape(params.albumID);

connection.query(query, function(err, rows, fields){

if(rows.length > 0){

getPhotosForAlbum(rows[0], function(err, obj){

if(err){

callback(err);

} else {

callback(null, obj);

}

});

} else {

callback(null, []);

}

});

}

首先,从数据库中检索相册及其所有字段。在所有数据库查询中,rows是从数据库查询中填充的对象。在使用SELECT查询的情况下,rows总是一个数组。如果你找到了你要找的东西,rows的长度将为 1 或更长。在本例中,我们通过唯一标识符albumID选择一行。但是这一次,我们不在这里停下来,返回我们找到的数据。相反,我们调用getPhotosForAlbum()并将我们的结果传递给它,然后最终将数据发送回控制器。向下滚动到getPhotosForAlbum()查看那里发生了什么。

function getPhotosForAlbum(album, callback){

var modelPhotos = require('./model-photos');

modelPhotos.getPhotosByAlbumID(album, function(err, obj){

if(err){

callback(err);

} else {

album.photos = obj;

callback(null, album);

}

});

}

如果我们通过 ID 发送一个相册,终端用户期望我们提供与该相册相关的所有数据似乎是合理的。在这种情况下,我们需要获取与相册相关的所有照片。

Note

我们知道客户端有用户的信息,因为这是他/她检索专辑 ID 的方式。在功能更全面的应用中,您可能还想在这个响应中包含一些用户信息。

首先,我们在/lib/model/model-photos.js实例化一个对照片模型的引用。我们给相册一个photos属性,设置为我们从照片模型中获取的照片数组(即使它是空的)。导航到/lib/model/model-photos.js,找到getPhotosByAlbumID()完成路线。

function getPhotosByAlbumID(params, callback){

var query = 'SELECT * FROM photos WHERE published=1 AND albumID=' + connection.escape(params.albumID);

connection.query(query, function(err, rows, fields){

if(err){

callback(err);

} else {

if(rows.length > 0){

callback(null, rows);

} else {

callback(null, []);

}

}

});

}

这个方法只是检索所有带有我们传递给它的相册 ID 的照片。请注意,照片的值published必须设置为 1。对于相册和照片,我们将使用published=1来表示该对象可供公众消费,使用published=0来表示该对象被隐藏。这使我们能够提供删除功能,而不会实际破坏数据库中的数据。接下来我们可以看看删除,从/routes/album.js开始。

/* POST delete album. */

router.post('/delete', function(req, res) {

if(req.param('albumID')){

var params = {

albumID : req.param('albumID')

}

model.deleteAlbum(params, function(err, obj){

if(err){

res.status(400).send({error: 'Album not found'});

} else {

res.send(obj);

}

});

} else {

res.status(400).send({error: 'Invalid album ID'});

}

});

到目前为止,您已经多次看到了这种模式。所以,我们将立即前进到model.deleteAlbum()

function deleteAlbum(params, callback){

var query = 'UPDATE albums SET published=0 WHERE albumID=' + connection.escape(params.albumID);

connection.query(query, function(err, rows, fields){

if(err){

callback(err);

} else {

callback(null, {message: 'Album deleted successfully'});

}

});

}

如你所见,我们实际上并没有删除专辑。我们正在取消发布,这意味着它对所有用户都是不可见的。这是防止用户永久意外删除的好方法,它降低了恶意使用我们的应用的风险。如果有人的密码被盗或破解,他的所有内容被删除,我们可以恢复它没有太多的麻烦。还要注意,我们的回调没有返回任何数据,只是一条确认删除成功的消息。我们的应用非常简单,目前对于用户删除某些内容后会看到什么没有预期。目前这还可以,但是在你自己的应用中,你可能要考虑你的用户会期待什么样的响应。最后,我们将回顾照片。

照片

照片是个人照片/图像上传的对象。目前,应用中使用的照片对象不包括实际文件。此时,照片对象只不过是一个 ID 和一个标题。文件上传和 URL 生成是我们将专门为 AWS 定制的功能。我们将在后面的课程中构建此功能。

照片具有以下功能:

  • 创建新照片(POST /photos/upload)
    • 参数:albumIDcaptionuserID
  • 通过 ID ( GET /photos/id/:id)获取照片
    • 参数:id
  • 删除一张照片(POST /photos/delete)
    • 参数:id

你会注意到这些方法实际上与专辑中的方法相同。让我们回顾一下关于照片的几点,从上传一张“照片”开始。您应该从之前的 API 查询中获得一个相册 ID 和用户 ID。(我们假设两者都等于 1。)让我们继续创建一个新的照片对象,使用这些 id 作为参数。在您的 REST 客户端中,使用以下参数向http://localhost:8081/photos/upload发出一个POST请求:

userID: 1

albumID: 1

caption: "My First Photo"

响应只包含您刚刚创建的照片的 ID。

{

"id": 5

}

看看你刚才查询的方法,在/routes/photos里。

/* POST create photo. */

router.post('/upload', function(req, res) {

if(req.param('albumID') && req.param('userID')){

var params = {

userID  : req.param('userID'),

albumID : req.param('albumID')

}

if(req.param('caption')){

params.caption = req.param('caption');

}

model.createPhoto(params, function(err, obj){

if(err){

res.status(400).send({error: 'Invalid photo data'});

} else {

res.send(obj);

}

});

} else {

res.status(400).send({error: 'Invalid photo data'});

}

});

值得注意的一个区别是caption参数是可选的。如果标题存在,我们会将它包含在传递给model.createPhoto()的参数对象中。这显示了构造一个中介对象的价值,而不仅仅是将请求参数直接传递给模型。如果请求中省略了一个可选字段,我们只需让数据库应用默认值。继续向/photos/upload发出另一个请求,并删除caption参数。您应该会从 API 收到相同的响应。

现在,让我们花点时间检查一下我们的相册,以确保我们的照片在那里。向http://localhost:8081/albums/id/1发出GET请求。请注意,没有标题的照片有一个空的标题字符串。响应应该如下所示:

{

"albumID":1,

"userID":1,

"title":"Hello World",

"photos":[

{

"photoID":4,

"userID":1,

"albumID":1,

"caption":"My First Photo"

},{

"photoID":5,

"userID":1,

"albumID":1,

"caption":""

}

]

}

再花点时间浏览照片路线和模型的其余代码。不应该有什么惊喜。

使用示例应用开发

到目前为止,您已经看到示例应用只有基本的功能。如果您是一名经验丰富的 MySQL 开发人员,您现在应该已经注意到没有关联表,这限制了我们在对象之间建立多对多关系的能力。为了提供简单的示例,此功能已被省略。其他更重要的功能—上传文件、身份验证等。—不完整。随着您对 AWS 的了解越来越多,您将会充实这些特性。当您学习示例应用的课程时,请记住我们的目标是开发一个利用 AWS 服务的应用。还有很多其他的书籍教授 RESTful web 服务、Node.js 和 MySQL 的优点。记住这一点,让我们开始吧!

下一步将是我们在 AWS 中的第一个任务。按照前面的步骤,您应该已经注册了一个 AWS 帐户。我们将在 AWS 控制台中做的第一件事是学习如何使用 IAM(身份和访问管理)来管理应用基础架构中的权限和安全性。IAM 是 Amazon 针对需要管理多种权限的问题的解决方案。当管理权限时,我们不仅仅是说让其他用户访问我们的 AWS 基础设施,还包括 AWS 服务之间的访问。例如,您可能有一个 EC2 实例,您希望它能够连接到 RDS 数据库,但不希望它具有访问 OpsWorks 的 send 命令的权限。

对安全凭证过于慷慨是不好的做法。想象一下,你正在管理一个新闻网站。您可能希望为不同类型的用户提供不同的权限。读者只能阅读文章和发表评论。编辑将被允许发布和编辑文章,并删除用户评论。管理员将能够禁止用户,创建新的编辑,并执行站点级管理任务。您不希望授予所有用户管理权限,并相信他们会相应地采取行动。即使只是让你的编辑成为管理员,你也可能有一天醒来发现你自己的管理权力被剥夺了。

我们将同样谨慎地管理我们的 AWS 基础设施。AWS 控制台中的用户应该只拥有完成工作所需的权限。类似地,每个服务器实例都有一个角色要扮演,并且应该仅限于该角色。当您很好地掌握了 IAM 之后,您就可以部署您的应用,而不必将密码存储在等待被黑客攻击的配置文件中。

身份和访问管理

在这种类型的应用中管理权限和凭证有很多挑战。

首先,我们只希望一组选定的用户能够管理我们的基础设施。例如,只有某些团队成员能够重启服务器。也许一组不同的用户应该拥有邮件服务器的管理权限,而另一组不同的用户应该拥有数据库管理权限。

此外,还有管理每台服务器安全性的问题。组织和限制对一系列服务器的安全密钥的访问本身就是一门艺术。通常,我们还必须在应用的源代码中包含数据库访问凭证。我们不仅要担心限制对存储凭证的源代码的访问,还要确保开发环境凭证不会意外地部署到生产环境中,反之亦然。

除了所有这些问题之外,我们还有人员流动的问题。工程师和管理员要么辞职,要么被解雇,我们必须审查我们所有的安全措施,以确保他们不能接触任何敏感的东西。如果某个拥有大量安全访问权限的人意外退出,我们将不得不手忙脚乱地锁定我们所有的凭据。安全漏洞,尽管可能是短暂的,却是有保证的。每次我们必须重置凭证时,所有错误配置的风险都会重新引入系统。现在应该很清楚,即使像我们这样简单的应用,在控制内部安全方面也存在许多故障点和巨大的人为错误风险。

AWS 通过将所有用户安全、数据库和服务器安全以及 API 访问统一到一个名为身份和访问管理(IAM)的系统中来解决这些问题。现在,我们假设没有其他 AWS 用户需要我们关注。然而,我们确实希望确保我们在 OpsWorks 中创建的 EC2 实例能够与其他 AWS 服务正确交互。

IAM 仪表板

让我们登录 AWS 控制台并导航到 IAM。当您成功登录 AWS 时,您会看到一个由三列组成的 AWS 产品列表。您可以在第二列的部署和管理标题下找到 IAM。单击它,您应该会看到类似图 1-2 的内容(为了清晰起见,一些用户界面元素被裁剪掉了)。

A978-1-4842-0653-9_1_Fig2_HTML.jpg

图 1-2。

IAM dashboard

在 IAM 仪表板的左侧,您将找到 IAM 的导航(这是 AWS 控制台的标准界面范例)。在右侧,您将看到您已经创建的所有 IAM 资源的列表。页面上还有许多其他的教程和咨询 UI 元素。您可以在闲暇时随意探索这些内容,以熟悉仪表盘。作为 AWS 中的第一个正式任务,您必须在 IAM 中创建一个用户。

IAM 用户

用户是一个管理帐户,它有一个登录名(用户名/密码)以及其他安全凭据(如果需要)。现在,让我们创建一个单一的用户帐户来管理我们的应用。在导航中选择 Users 链接,您应该会看到一个空的表视图。单击页面顶部的“创建新用户”按钮。这将带您进入图 1-3 所示的屏幕。

A978-1-4842-0653-9_1_Fig3_HTML.jpg

图 1-3。

Create up to five IAM Users

在这个屏幕上,您最多可以创建五个新用户名,但我们只需要一个。在第一个输入字段中,键入用户名 photoadmin。有一个自动选中的复选框,表示将为每个用户生成一个访问密钥。如果我们使用这个用户在我们的应用中进行 AWS API 查询,我们将需要一个访问键。然而,我们的意图是,这个用户将有权访问 AWS 控制台的必要部分,而与应用本身的功能无关。因此,我们将在单击“创建”之前取消选中该框。

用户名现在应该出现在 users 表中。您会注意到,我们的用户不属于任何组,也没有访问密钥。单击用户的行会将我们带到该用户的详细视图。您应该会看到类似于图 1-4 的内容。

A978-1-4842-0653-9_1_Fig4_HTML.jpg

图 1-4。

The user detail view

这一页上有很多信息。首先,是用户 ARN,或亚马逊资源名称。ARN 本质上是任何种类的所有 AWS 资源的全局标识符。任何时间任何资源,例如用户、EC2 实例、数据库实例等。,则会自动生成一个 ARN。稍后您可能会发现它们很有用,但是我们现在不需要对这个 ARN 做任何事情。您会注意到“组”和“权限”标题下的粗体文本。我们的用户没有组,也没有策略!

策略可能是 IAM 中最重要的概念。策略本质上是对用户、组、角色或其他资源的一个或多个权限的声明。简而言之,它们是为 AWS 中的所有实体配置权限的通用系统。策略可以包含非常细粒度的权限,或者对 AWS 产品中的所有属性进行全面的“允许”。相反,您也可以明确拒绝对服务或资源的访问。出于我们的目的,我们希望用户拥有管理我们的应用使用的所有 AWS 产品的完全权限。该用户是我们的超级管理员。

IAM 组

这里的诱惑是开始向该用户添加新的策略。等等!我们确定这是个好主意吗?我们真的知道这是唯一拥有这种力量的使用者吗?从长远来看,这个项目可能会有不止一个超级管理员。或许创建一个小组并在那里管理我们的政策会更有意义。

在导航中选择 Groups,您将看到一个类似于我们前面看到的 users 表的屏幕。单击创建新组。我们应该怎么称呼这个团体?我们知道,我们希望这些管理员能够访问我们的应用使用的所有产品。我们给了这个群体巨大的力量,因为现在,我们只是为自己创造了一个用户。让我们称这个组为 PhotoAdmins。单击下一步,您将进入附加策略视图。

IAM 管理的策略

如果您以前使用过 AWS,您可能会注意到 IAM 策略生成工具已经发生了变化。现在有两种类型的 IAM 策略:托管策略和内联策略。受管策略是 AWS 管理的策略中的一个或多个权限。例如,AmazonEC2FullAccess 策略包括对 EC2 服务的完全访问,以及弹性负载平衡和 CloudWatch 等相关服务。内联策略是当您有特定策略需求时可以创建的自定义权限,我们稍后将对此进行讨论。

创建组时,系统会提示您附加托管策略。如图 1-5 所示的 Attach Policy 视图将很快成为一个好朋友。该工具的唯一目的是简化为用户、组和角色选择正确权限的复杂过程(稍后将详细介绍)。

A978-1-4842-0653-9_1_Fig5_HTML.jpg

图 1-5。

Selecting a managed IAM policy in the Attach Policy view

虽然我们可以选择其中一个管理策略,然后就此结束,但这并不有趣,不是吗?相反,让我们创建该组,然后手动向其中添加内联策略。单击下一步进入查看视图,如图 1-6 所示,您将再次看到您的组名和策略。单击创建组完成。

A978-1-4842-0653-9_1_Fig6_HTML.jpg

图 1-6。

Review Group

您将返回到组列表视图。选择 PhotoAdmins 组并展开 Inline Policies 面板。单击链接创建新的内联策略。在下一个视图中,您可以从以下选项中进行选择:

  • 策略生成器:该选项将启动一个向导,允许您向组中添加一系列单独的策略语句。这是一种配置需要访问某些(但不是全部)服务的组的简便方法。
  • 习俗政策:这是阻力最大的道路。所有策略都以 JSON 格式读写。定制策略选项允许您手动输入策略的 JSON。例如,如果您想要配置一个策略来授予特定 AWS 资源的权限,那么您可以在这里使用策略 JSON 中的 ARNs。在某种程度上,您可以直接使用 IAM 策略声明 JSON。亚马逊在这里也提供了独立的 JSON 生成器: http://awspolicygen.s3.amazonaws.com/policygen.html

让我们继续下去,确保我们的 PhotoAdmins 组拥有它需要的所有权力(仅此而已!)并选择策略生成器选项。单击选择。

IAM 权限编辑器

下一个屏幕是权限编辑器。这里,我们将添加用户需要的每个单独的权限。除了返回 IAM 创建其他策略之外,我们将为他们提供完成其余课程的能力。参见图 1-7 。

A978-1-4842-0653-9_1_Fig7_HTML.jpg

图 1-7。

Generating individual IAM policy statements

我们配置权限的第一个选项是 Effect。虽然我们可以选择“拒绝”来禁止对特定服务的访问,但由于我们的组目前没有权限,这将是逆向操作。我们将选择“允许”,因为我们将允许访问特定服务。

从下拉列表中选择 AWS 服务。首先,我们要选择 AWS OpsWorks。接下来,我们可以只选择该组拥有权限的特定操作。此下拉列表中填充了可以在此 AWS 服务中执行的所有可能的操作。AWS 服务下拉列表中的每个选项都将重新填充操作列表。AWS 总共有数百个(如果不是数千个)操作。现在,选择所有动作。如果我们只想授予特定资源权限,我们可以在 Amazon 资源名称(ARN)输入中输入它的 ARN。我们暂时把它保留为*或全部。单击添加语句。您应该立即看到您的权限声明出现在该屏幕上,如图 1-8 所示。

A978-1-4842-0653-9_1_Fig8_HTML.jpg

图 1-8。

Permissions statement listing

让我们添加我们需要的其余权限。对于下面的每个服务,创建一个允许该服务的所有操作的语句。

  • 亚马逊云前线
  • 亚马逊云观测
  • 亚马逊云观察日志
  • 亚马逊 EC2
  • 亚马逊 RDS
  • 亚马逊路线 53
  • 亚马逊路由 53 个域
  • 亚马逊 S3
  • 亚马逊的 SES

您应该会在页面上看到所有这些权限。然后单击下一步。您将看到一个自动生成的策略名称。您可以让它保持原样,除非您为了方便使用而想要一个特定的名称。看哪!您的策略 JSON 出现在策略文档文本区域。点击“应用策略”,您的内联策略将被添加到组中,如图 1-9 所示。

A978-1-4842-0653-9_1_Fig9_HTML.jpg

图 1-9。

IAM Group Inline Policies view

接下来,我们希望将我们的用户添加到组中。导航到“用户”选项卡,并从表格中选择 photoadmin。在组标题下,单击将用户添加到组。选中我们的 PhotoAdmins 组旁边的框,然后单击添加到组。您将看到该组现在出现在用户详细视图中。很快用户就可以登录并开始工作了。

向下滚动一点,您会看到用户还没有密码。让我们给他一个默认密码。点击右下角的管理密码。在“管理密码”页面上,我们现在为他分配一个自定义密码。选择分配自定义密码旁边的单选按钮,并在框中键入单词 photo。在这些字段的正下方有一个复选框,要求用户在登录时更改密码。显然,照片不是一个安全的密码。让我们选中该框,然后单击“应用”。

如果您不信任您的用户自己选择安全密码,您可以导航到“密码策略”选项卡,并选择用户设置密码时要强制执行的一些规则,如最小长度或要求一个数字。

回到 IAM 仪表板。在页面顶部,您应该会看到文本“IAM 用户登录链接”这是您的用户将用来登录其帐户的 URL。让我们测试一下我们的进度。

将 URL 复制到剪贴板,并通过单击浏览器窗口右上角的您的姓名并选择“注销”来注销 AWS 控制台。将 URL 粘贴回地址栏,您将看到一个典型的登录屏幕。输入用户名 photoalbum 和密码 photo。如果您强制用户在登录时重置密码,系统会要求您现在重置。然后进入 AWS 控制台。您应该仍然可以在仪表板上看到所有 AWS 服务,但是您将无法执行您现在被限制执行的任何操作。

摘要

现在,您已经设置了本地环境,并完成了示例项目。现在,您应该对应用的用途有了一个清晰的概念,并为开发应用的后续步骤做好了准备。通过在身份和访问管理中创建用户、组和策略,您已经迈出了使用 AWS 的第一步,并了解了 Amazon 资源名称,这是 AWS 的全局标识符。现在,您已经准备好开始构建您的应用了。在下一章中,您将了解 AWS OpsWorks,并开始第一次在 AWS 上设置应用!

Footnotes 1

亚马逊网络服务,“云架构:最佳实践”, http://aws.amazon.com/es/whitepapers/architecting-for-the-aws-cloud-best-practices/ ,2010 年 5 月 21 日。

二、使用 AWS OpsWorks

完成第一章后,你现在应该对我们正在构建的应用有了很好的理解。您还应该已经注册了 AWS 帐户,并在 IAM 中设置了管理用户。如果您跳过了介绍性章节,那么关于身份和访问管理(IAM)的教程非常重要。您必须熟悉 IAM 的主要概念,才能使用 AWS 做几乎任何事情。

接下来,我们将开始使用 OpsWorks 应用部署服务将我们的基本应用部署到 AWS 的过程。在本章中,您将在 OpsWorks 中创建一个应用实例,并首次将其部署到 Web 上。在本章中,我们不会给代码库添加任何功能。不管怎样,我们已经迈出了在云中托管应用的第一步!

了解 OpsWorks

有趣的是,AWS OpsWorks 并不是由 AWS 从头开始构建的。2012 年,亚马逊收购了一家名为 Peritor 的公司,该公司提供类似功能的第三方企业部署服务。这两种产品的技术基础被称为 Chef ( www.chef.io/ ),这是一个用于以编程方式配置、自动化和简化服务器部署的框架。虽然您启动的每个 EC2 实例都有预设的配置,但是使用 Chef,您只需编写很少的代码就可以轻松地对环境做出自己的更改。使用 Chef 的一个优点是,您不必了解 AWS 虚拟服务器包的所有细微差别。相反,Chef 为您提供了一个用于配置常见服务器端软件的 API,如 nginx、Apache、PHP,甚至 Node.js。在本书的后面,您将直接使用 Chef 来了解这有多简单。

OpsWorks 旨在简化应用环境的定制和管理,为应用堆栈中不同类型的资源提供图形用户界面(以及 API)。我将很快更详细地回顾这些资源。任何尝试过像 OpsWorks 那样手动配置和部署应用的人都会告诉你,这可以节省大量时间,并大大降低出错的风险。没有涉足系统管理的普通软件开发人员会发现,作为云架构师,他/她现在拥有超人的能力。

随着我们继续学习本课并讨论该服务的具体功能,使用 OpsWorks 的好处应该是不言而喻的。也就是说,在易用性方面,定制总是要付出代价的。在 2013 年 2 月一篇关于 AWS OpsWorks 发布的博客文章( www.allthingsdistributed.com/2013/02/awt-opsworks.html )中,亚马逊首席技术官沃纳·威格尔提供了图 2-1 所示的便捷图表,这是 AWS 的风格。

A978-1-4842-0653-9_2_Fig1_HTML.gif

图 2-1。

The plane of Convenience vs. Control in AWS application deployment

此图描绘了便利性与控制性的一维平面,显示了 OpsWorks 与其他应用部署选项的相对位置。如您所见,OpsWorks 介于管理应用堆栈的 DIY 方法和更简单、更少定制的弹性 Beanstalk 之间。如果这让你停下来重新评估 OpsWorks 是否真的是最适合你的想法,不要担心。有些人认为 OpsWorks 是弹性豆茎的下一代翻版。你与 AWS 合作的时间越长,你就越会看到它们推出新的功能和服务之间更好的协同作用。有时,这些改进的速度可能会令人无法抗拒:每隔几周,您就会收到 AWS 的一封电子邮件,通知您可能会在您的应用中使用六个新功能。因此,不要太担心星盘。在 AWS 世界中,两年是一段很长的时间,重要的是,对于 AWS 提供的服务,有一个广泛的抽象范围,它产生了一系列的功能和限制,这取决于服务是如何设计的。

此外,当我们用 OpsWorks 部署我们的应用时,我们在应用中使用了许多其他的 AWS 服务。最终产品将真正是其各部分的总和。在课程结束时,通过一些努力,你可能会明白如何将 OpsWorks 替换为 Elastic Beanstalk 或 CloudFormation。图 2-1 仅仅说明了当你戴上云架构师的帽子时,需要进行成本效益分析。

稍后,我们将开始探索 OpsWorks 控制面板。我们将回顾它的各种特性,同时了解对 OpsWorks 至关重要的各种其他 AWS 服务。我们还将一瞥基础技术厨师如何仍然与像我们这样的 AWS 用户相关。当您在 OpsWorks 中分配资源时,您的 AWS 帐户将开始产生使用费。在自问是否需要十个 m 3.2 x 大型 EC2 实例来托管 Photoalbums 应用时,请记住这一点。但是在我们开始在 OpsWorks 中分配资源之前,我们应该对这些资源多了解一点。

分配资源

当您分配 EC2 实例、RDS (Rational Database Service)数据库实例和许多其他资源时,您做出的最重要的决定是什么和在哪里。什么是不言自明的。AWS 对这些资源有自己的定价等级,您可以在官方文档中找到。使用和定价之间的关系因服务而异,它们被单独记录。

例如,EC2 实例是根据功率(内存、CPU 时钟速度和物理处理器)×小时速率×小时数来预留的。为了回顾 EC2 实例类型的规范,这里提供了一个分类: http://aws.amazon.com/ec2/instance-types/ 。在确定实例类型时,您将使用您的判断,将您的技术需求与您的财务资源进行比较,交叉引用实例类型与此处的定价: http://aws.amazon.com/ec2/pricing/ 。虽然传统的托管服务(还有一些云托管平台)会对你预定的资源按月收费,但按小时收费才是 AWS 如此有用的原因。还是那句话,用什么付什么。这就是资源。在哪里是一个单独的问题,如果你从传统的主机转移,这是一个新的概念。

Caution

一旦您提供了您的账单信息,AWS 将允许您根据需要提供昂贵的资源。如果您是 AWS 新手,请小心请求过多的资源。每小时 1.50 美元听起来并不多,除非你不小心让一个实例在线三个月。

区域和可用性区域

许多人认为云是某种模糊的、没有位置的全球实体。当然,仍然有真实的服务器和数据中心——数据并不是真的漂浮在对流层中。AWS 在全球各地运行数据中心,通常在地理上靠近主要的人口中心。在大多数情况下,有几个数据中心服务于相同的地理区域或地区。这些地区的名称有美国东部(北弗吉尼亚)、美国西部(俄勒冈州)和亚太地区(东京)。虽然地区的名称通常描述了西欧等大陆地区,但括号中的位置描述了数据中心所在的更具体的区域(参见图 2-2 )。请记住,AWS 在一个地区的其他地方维护额外的支持基础架构,并且并非所有服务在所有地区都可用。你可以在 http://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/ 找到更详细的信息。

A978-1-4842-0653-9_2_Fig2_HTML.jpg

图 2-2。

AWS region data centers mapped (not pictured: GovCloud region)

地理在构建 web 应用时非常重要。无论你的代码有多快,或者你运行着多少台服务器,你的数据仍然要通过网络与用户交换。自拨号上网时代以来,随着技术的发展,用户的期望也发生了巨大的变化。你的最佳选择是将你的应用放在离你的预期用户群最近的地方。

Note

我将在第四章和第五章中与 S3 和 CloudFront 讨论在全球范围内发布你的内容的其他方式。

如前所述,许多地区有不止一个物理数据中心。如果某种服务中断暂时使一个数据中心无法运行,其他数据中心会保持该区域在线。这些数据中心在 AWS 服务中被抽象为可用性区域。在每个可用性区域内都有 EC2 实例和我们提供的其他资源。您可以将可用性区域视为包含运行 AWS 服务的硬件的数据中心。这个概念如图 2-3 所示。

A978-1-4842-0653-9_2_Fig3_HTML.gif

图 2-3。

Illustration of a region containing four distinct availability zones

现在您已经熟悉了区域和可用性区域的概念,您可以开始考虑不同的场景了。本质上,我们仍然在现实世界中使用物理服务器。他们会崩溃;他们可以失去权力;他们可以有一个随机部分失败,等等。有无数的故障点,如果您的所有 EC2 实例都在一个可用性区域中,并且该区域断电了,该怎么办?您的应用将脱机。事实上,可以肯定的是,在某个时刻,您的某个可用性区域将会出现孤立的中断,因此我们要为此做好准备。

人们可以写一本关于云架构应急计划的书。就本书的范围而言,我们将保持简单。我们的应用将托管在单个区域中,实例位于多个可用性区域中。我们将使用其他 AWS 服务在其他地区更快地分发我们的内容。如果我们想要额外的冗余,我们也可以将我们的应用克隆到不同的地区。只要我们的数据库在所有地区保持同步,我们就可以随意复制我们的应用。我将在整本书中讨论这些种类的优化。

其他 IAM 角色

在我们进入 OpsWorks 仪表板之前,我们将在身份和访问管理中创建一个新角色。在第一章中,您在 PhotoAdmins IAM 组中创建了一个管理级别的用户,该用户应该能够在 OpsWorks 中执行任何任务。我还讨论了在我们的代码中存储凭证的安全风险,以防开发人员离开项目或装有应用本地副本的机器被盗或丢失。

为了避免这些风险,我们将使用 IAM 角色来管理与其他服务的认证。我们实际上需要两个新的 IAM 角色:一个用于托管我们的应用的 EC2 实例,另一个允许整个应用栈代表我们行事。通过为我们的实例创建一个角色,通常称为实例角色,我们可以通过 AWS API 以编程方式访问其他 AWS 服务,而无需在源代码中存储安全凭证。我们将首先创建这个角色。

实例角色

返回 IAM 控制面板,单击左侧导航栏中的“角色”。找到页面顶部的“创建新角色”按钮,然后单击它。在下一个屏幕上,将提示您输入角色的名称,如图 2-4 所示。

A978-1-4842-0653-9_2_Fig4_HTML.jpg

图 2-4。

Set Role Name

我们希望我们的角色有一个逻辑名称,所以我们将使用 AWS-ops works-photoalbums-ec2-role,photo albums 应用中 EC2 实例的角色,它由 AWS OpsWorks 初始化。您可以使用任何有意义的约定,但我们使用的是[服务]-[应用]-[角色]的格式。单击下一步继续。同样,在为此角色创建 IAM 策略时,我们有许多选项可供选择。现在,我们将创建一个没有策略的基本角色。随着我们继续阅读本书的其余部分,我们将为这个角色添加更多的策略。首先会提示您选择角色类型。在服务角色选项框中,您会在列表顶部看到 Amazon EC2。单击选择按钮前进。

我们将再次能够选择策略(参见图 2-5 )。我们稍后将附加一个策略,因此请单击“下一步”继续查看视图。

A978-1-4842-0653-9_2_Fig5_HTML.jpg

图 2-5。

Selecting no policies for the aws-opsworks-photoalbums-ec2-role

在下一个视图中,您有机会在创建角色之前检查它。如果一切正常,单击创建角色。我们现在有了一个角色,可以分配给运行我们应用的 EC2 实例。然而,我们仍然需要整个应用堆栈本身的角色。

服务角色

作为一个 OpsWorks 应用,我们的堆栈将需要权限来执行常规任务,比如重启实例、向 AWS 控制台报告指标等。作为用户,您可以在 OpsWorks 仪表板周围单击,并手动执行各种任务。我们希望确保我们创建的应用能够自己执行这些操作。我们将通过为整个应用堆栈创建一个角色来确保这一点。这个角色通常被称为服务角色。一旦创建了这个角色,我们就可以进入 OpsWorks 仪表板来创建我们的应用了。

返回 IAM 仪表板中的“角色”选项卡,再次单击“创建新角色”。我们再次需要为我们的新角色取一个名字。按照我们之前的命名约定,我们将把这个命名为 AWS-ops works-photo albums-service-role。命名约定也是[服务]-[应用]-[角色]。继续下一步将把您带到选择角色类型视图,在这里 AWS 服务角色已经打开并在您面前列出。将此列表向下滚动到末尾,在那里您会找到 AWS OpsWorks,然后单击 Select。在附加策略视图中,将只有一个可能的策略可供选择:AWSOpsWorksRole。这是 AWS 为 OpsWorks 服务角色提供的默认托管策略。选择该框,然后单击下一步。再次查看策略,并通过单击“创建角色”完成该过程。

您将返回到 Roles 视图,在这里您将看到您的新角色已经创建。选择您的新角色,进入角色详细信息视图。在权限标题下,您会看到 AWSOpsWorksRole 是唯一的策略(参见图 2-6 )。单击 Show Policy,将出现一个模态视图。

A978-1-4842-0653-9_2_Fig6_HTML.jpg

图 2-6。

Policies attached to the aws-opsworks-photoalbums-service-role

在模态视图中,您将有机会查看策略文档的原始 JSON。JSON 应该类似于清单 2-1 ,如下所示:

Listing 2-1. The OpsWorks Service Role Policy

{

"Version": "2012-10-17",

"Statement": [

{

"Effect": "Allow",

"Action": [

"cloudwatch:GetMetricStatistics",

"ec2:DescribeAccountAttributes",

"ec2:DescribeAvailabilityZones",

"ec2:DescribeInstances",

"ec2:DescribeKeyPairs",

"ec2:DescribeSecurityGroups",

"ec2:DescribeSubnets",

"ec2:DescribeVpcs",

"elasticloadbalancing:DescribeInstanceHealth",

"elasticloadbalancing:DescribeLoadBalancers",

"iam:GetRolePolicy",

"iam:ListInstanceProfiles",

"iam:ListRoles",

"iam:ListUsers",

"iam:PassRole",

"opsworks:*",

"rds:*"

],

"Resource": [

"*"

]

}

]

}

您会注意到在Statement数组中有一个 JSON 对象,它具有属性EffectActionResourceEffect应该是不言自明的:我们在这个语句中特别启用了权限。接下来,Action是一组 AWS 服务和动作。这些可能以后会更有意义,但是你可以通过观察它们来了解它们是什么。您可以从第一个操作中看到,对于默认的 OpsWorks 角色,我们只为GetMetricStatistics启用了 CloudWatch API 权限,但是我们为 EC2 和 Elastic Load Balancing 启用了各种读取权限,并为 OpsWorks 和 RDS 启用了所有操作。很快您就会看到,这些服务可以在 OpsWorks 中直接初始化和配置,因此堆栈拥有这些权限非常重要。

您还会注意到这里有一个非常重要的动作,名为iam:PassRole。该权限将允许您的应用将其角色传递给它所管理的 EC2 实例,使它们能够代表服务角色执行任务。这很重要,你很快就会发现原因。

最后,Resource数组只包含“*”,即所有资源。为了简单起见,我们提供 IAM 角色用于所有资源,即使它们是专门为我们的应用命名的。将来,您可以返回到这些角色的策略文档,并将它们限制为应用的资源。您将了解到在策略文档中定位特定资源的能力是一个强大的特性。

不幸的是,默认的 OpsWorks 服务角色对于我们的目的来说不够强大。如前所述,我们只有 EC2 的读取权限。然而,我们希望能够在 OpsWorks 中创建资源,因此我们需要附加一个额外的策略。从“AWS-ops works-photo albums-service-role”的详细视图中,单击“Attach Policy”以添加另一个托管策略。从策略列表中选择 AmazonEC2FullAccess,然后单击附加策略。您将返回到角色详细信息视图,并且应该看到现在有两个托管策略附加到该角色。

为了遵循最佳实践,Amazon 建议撤销根帐户访问密钥,并为根帐户启用多因素身份验证。您可以随意遵循这些指南,但是您至少应该学会如何通过用户帐户而不是根帐户来管理您的架构。然而,当在 OpsWorks 中创建我们的第一个应用堆栈时,我们必须拥有 IAM 管理员访问权限,在第一章中,我们没有将此权限授予我们的 PhotoAdmins 组。因此,创建我们的应用堆栈将是我们作为根用户的最后一个动作。虽然您现在可能拥有 AWS 帐户的 root 访问权限,但最好养成在任何 AWS 帐户(即您的雇主或客户的帐户)上作为用户工作的习惯。

到目前为止,您已经在 IAM 中做了很多工作,并且了解了很多我们还没有开始直接使用的技术。这可能有点令人困惑,因为我们已经快速地学习了一些非常抽象的概念。概括地说,这是迄今为止您使用 IAM 和 OpsWorks 所取得的成果:

  • 您使用 root 帐户登录 AWS,该帐户在 IAM 中使用过,接下来将用于创建您的第一个应用堆栈。
  • 您已经创建了一个 PhotoAdmins 组并定义了它的策略,授予该组管理大量 AWS 服务的权限,我们稍后将使用这些服务。
  • 您向 PhotoAdmins 组添加了一个用户,以后您可以使用该用户在 AWS 控制台中登录并使用您的应用。
  • 您已经为应用堆栈中的 EC2 实例创建了实例角色。
  • 您已经为您的应用堆栈本身创建了一个服务角色。

在我们继续学习 OpsWorks 仪表板之前,我们还需要进行一个步骤来了解 SSH 密钥。请注意,这一步是完全可选的。对于示例应用,您不需要学习这一课,但是学习起来可能会很方便。如果你现在不想学这个,跳过下面的部分。

QUICK DETOUR: SSH KEYS

我们之前了解到,运行 Amazon Linux 实例的好处之一是严格的默认安全设置。开箱即用,您的实例只能通过 SSH 连接—FTP、SFTP 和其他常见的连接方法被禁用。要启用它们,您必须在 AWS 中打开正确的端口,并在命令行上安装软件,这本身就是一个单独的教程。要设置这些方法,您首先需要能够通过 SSH 连接到您的实例。幸运的是,Amazon 使得为实例生成密钥变得很容易。

如果我们必须为应用中的每个实例存储一个单独的 SSH 密钥,这不是很烦人吗?除了丢失和混淆键的风险之外,在栈中添加和删除实例将是一个更加劳动密集型的过程。Amazon 让这变得简单了,它允许我们在 AWS 控制台中创建一个 SSH 密钥,并将其设置为栈中所有实例的默认密钥。理论上,您可以对所有堆栈使用一个主密钥,但是每个堆栈一个密钥似乎更有意义。现在让我们继续创建我们的密钥。首先,我们必须前往 EC2 仪表板。为此,您可以单击左上角的橙色框图标,或者打开它旁边的服务菜单,然后在“计算和网络”部分找到 EC2,如下图所示。有时,如果您不确定在哪个类别中可以找到您需要的服务,返回 AWS 控制台会更容易。

A978-1-4842-0653-9_2_Figa_HTML.jpg

EC2 仪表盘上有很多东西,比我现在需要详细查看的还要多。我们以后会在这里多呆些时间。在左侧导航中,您应该会看到一系列可折叠的部分,默认情况下它们是展开的。在“网络与安全”下,单击“密钥对”,如下图所示:

A978-1-4842-0653-9_2_Figb_HTML.jpg

在这里,您将看到为 EC2 实例生成的所有 SSH 密钥的列表。这些密钥的加密是 1024 位 SSH-2 RSA。每个区域有 5,000 个键的限制,但大多数用户似乎不太可能接近这个上限。除了在控制台中创建您的密钥对之外,您还可以自己生成它们并将其导入 AWS 然而,这超出了本书的范围。

注意:如果删除密钥对,不会影响已经使用它们的实例。如果您有私钥的副本,您仍然可以连接到您的实例。但是,您将无法使用已删除的密钥对预配新实例。

到目前为止,AWS 中的一些 UI 设计选择应该开始变得熟悉了。与 IAM 类似,Create Key Pair 按钮位于主要内容区域的左上角。这只是打开一个模态窗口,您可以在其中命名密钥对,而不是开始一个多步骤的过程。让我们将其命名为 aws-opsworks-photoalbums-key,遵循与我们之前创建的 IAM 角色类似的命名约定。

当您单击 Create 时,将会生成密钥,并且一个名为aws-opsworks-photoalbums-key.pem的文件应该会自动下载到您的机器上。扩展名.pem是“隐私增强邮件”的缩写,这个文件被称为私钥。

AWS 将生成并存储相应的公钥,形成一个密钥对。公钥的副本将被保存到使用该密钥对提供的所有实例中,并且您必须提供匹配的私钥,以便使用 Amazon 提供的 Java 插件在命令行或浏览器中与您的实例建立安全连接。我们还没有任何实例,所以我们现在不会使用私钥来连接任何东西。将密钥保存在安全的地方,最好备份在某个地方。(提示:您可以将一个副本存储在一个私有的 S3 桶中,我们稍后将设置这个桶。)

OpsWorks 环境

当我们在 OpsWorks 中设置第一个应用时,了解我们的工具是什么非常重要。幸运的是,Amazon 为 OpsWorks 环境提供了一个很好的例子,因为它与一个 PHP 应用有关。你可以在图 2-7 中找到。

A978-1-4842-0653-9_2_Fig7_HTML.gif

图 2-7。

The components of an OpsWorks stack

如您所见,AWS 中应用的所有组件都分组在 OpsWorks 堆栈中。堆栈是组成应用部署的所有组件的集合。您可能以前听说过应用堆栈这个术语;我一直在频繁使用它。OpsWorks 堆栈就是 AWS 环境中的应用堆栈。与传统的宿主环境不同,OpsWorks 中的堆栈组织分配给应用的所有资源。

在图 2-7 中,您可以看到包含在 OpsWorks 堆栈中的 PHP 应用服务器层。根据它的复杂性,我们的应用可以有很多层。对于像我们这样的基本 web 应用,只有几层:应用托管服务器(EC2 实例)、负载平衡器和数据库。

您还会注意到,应用与应用服务器实例是截然不同的。这代表了我们的应用的实际源代码,堆栈将负责部署它。OpsWorks 从代码库中检索源代码(通过互联网),并将源代码部署到我们的实例中。根据在堆栈、层和应用源代码级别定义的要求,OpsWorks 将确保正确配置实例,安装依赖项,并在实例上上传和运行应用。我将很快讨论这些组件中关注点的分离。

OpsWorks 仪表板

我们走了一些大的弯路,但最终是时候在 OpsWorks 中创建您的应用了。在 AWS 控制台中,在服务列表(中间列)中找到 OpsWorks 并单击它。由于巨大的“欢迎使用 OpsWorks”标题,您现在应该可以清楚地看到 OpsWorks 仪表板。您会注意到一个蓝色的大按钮,上面写着添加第一个堆栈。

大量

想象一下,我们在一个传统的共享主机提供商上托管我们的应用。您可以使用您的帐户登录管理控制台来管理您的资源。您可能在一个选项卡中有一些 web 应用,然后在另一个选项卡中有一些数据库,甚至所有应用都托管在同一个数据库的不同表中。在这种模式下,管理很快就会变得混乱。你受到你自己、你的同事和/或你的前任的支配,希望遵循合乎逻辑的命名惯例。甚至一些云托管平台仍然是这样,这大大增加了人为错误的风险,在这位作者看来。

每个堆栈由一组独立的资源组成,这意味着影响一个应用的问题仅限于该应用。如果你的一个应用崩溃了,被黑了,或者耗尽了资源,变得没有反应,其他栈不会受到影响。因为我们将在多个可用性区域中创建实例,所以导致我们的应用宕机的唯一外部因素是主要的 AWS 服务崩溃。听起来是部署应用的好方法,对吗?

在 OpsWorks 中,您可以创建任意多的堆栈,它会将项目的所有资源整齐地组织到每个堆栈中。这对于创建开发和登台环境特别有用,因为每个堆栈上都有一个克隆按钮!您还可以一键启动和停止所有服务,并同时在所有实例上运行命令。如此简单却又如此强大!让我们把相册放到网上吧。继续并单击添加第一个堆栈。下一页发生了很多事情,如图 2-8 所示。

A978-1-4842-0653-9_2_Fig8_HTML.jpg

图 2-8。

Creating an application stack in OpsWorks

第一个领域很简单,输入相册,然后进入下一个领域。现在我们必须选择一个地区。有可能辨别你的目标受众的地理位置吗?亚马逊的主要数据中心在弗吉尼亚州北部,因此该地区默认为美国东部。除非你有理由改变这一点,否则我们可以让它保持原样。

下一个字段询问您是否要选择 VPC 或虚拟私有云( http://aws.amazon.com/vpc/ )。如果您想要将应用部署到专用网络,您可以将应用分配到此处的 VPC。在我们的例子中,我们正在构建一个供公众在网上消费的应用,所以我们不会选择 VPC。VPC 可以包含公共子网和私有子网,但这可能是续集的主题!

Note

如果要使用 VPC,必须在创建 OpsWorks 应用之前创建它。

我已经谈到了可用性区域,所以您知道您创建的每个实例都将在特定的可用性区域中运行。当您分配新实例时,默认可用性区域只是预选的可用性区域。这是一个纯粹为了方便而设计的字段。如果您正在管理大量实例,并且知道您希望它们位于特定的可用性区域中,那么在这里选择它可以省去以后在大量实例上更改区域的麻烦。但是,您当然可以在以后更改实例的可用性区域。让我们在这里保留默认值。

Amazon Linux

您将会看到默认操作系统下拉菜单为您提供了一些选择。除非你正在构建一个明确需要 Ubuntu 的应用,否则你很可能会选择最新版本的 Amazon Linux。截至发稿时,最新的是亚马逊 Linux 2014.09。

Amazon Linux 是基于 Red Hat Enterprise Linux 的 Linux 版本,由 Amazon 管理。它是专门为部署到 EC2 实例而设计的,值得注意的一个主要特性是 Amazon Linux 是为 AWS 环境中的最大安全性而定制的。默认情况下,远程访问 Amazon Linux 实例的唯一方式是通过 SSH。可以通过 AWS 控制台打开其他方法。与部署你从网上下载的 Linux 版本相比,Amazon Linux 的安全配置让你放心,因为它已经通过了专家的安全缺陷检查,而没有太多的限制,以免损害你的软件。

这带来了另一点,即通过利用云,我们试图避免陷入操作系统配置的地狱。我们不想部署定制版的 Ubuntu 我们希望启动一个实例,并知道它已经准备好以启动的速度运行我们的软件。如果我们不能信任运行在我们众多服务器上的操作系统,那么我们又回到了起点。亚马逊已经写了很多关于他们构建 Linux 的文章,你可以在这里找到全部: http://aws.amazon.com/amazon-linux-ami/

亚马逊机器图片

您会注意到,除了 Amazon Linux 和 Ubuntu,您还可以选择使用自定义 AMI。此选项允许您创建运行特定软件包的实例,或者,如果您愿意,可以将实例锁定到特定版本的操作系统。亚马逊支持这一功能,但似乎不太情愿。它建议您应该使用 Amazon Linux,然后使用 Chef 定制您的安装,如果需要的话。这是最佳实践,但是您可以选择做您想做的任何事情。如果您使用 Chef 而不是自定义 AMI,那么您将受益于 Amazon 对您的操作系统的持续支持,同时允许您进行自定义选择。选择 Amazon Linux 并继续。

实例与 EBS

啊哦!又一个陌生的问题。我们的默认根设备类型应该是实例存储还是 EBS 支持的?这一切意味着什么?基于到目前为止对 EC2 实例的讨论,您现在应该认识到这些实例是短暂的。当您停止一个实例时(无论是否选择),存储在该实例上的所有数据都将丢失。因此,依赖 EC2 实例来实现数据持久性并不是一个好主意。即使 EC2 实例提供了永久的数据持久性,我们会想要使用它们吗?AWS 应该使基础设施更容易管理,管理跨实例存储的唯一数据可能会变得复杂。

亚马逊对这个问题的解决方案叫做弹性块存储。借助 EBS,您可以为持久数据存储提供可扩展的磁盘驱动器。您一次只能将一个 EBS 实例附加到一个 EC2 实例,但是您可以获取我们的 EBS 的快照,并使用它来实例化一个新的 EBS。

这在我们的应用中有用吗?最终,我们将接受文件上传并存储,供用户通过网络访问。您肯定知道您需要持久的磁盘存储,并且我们希望在多个 EC2 实例上运行我们的应用。显然,这不是解决方案,因为存在实例过时的风险。很高兴知道这个功能的存在;它只是不适合我们的用例。

我们知道我们将需要一些持久的数据存储,并且我们知道 EBS 不能用于多个实例。因此,我们现在可以选择实例存储,因为我们知道只能在 EC2 实例上存储临时数据。这意味着文件上传、日志文件等。,将不得不存储在其他地方,我们将很快更详细地讨论这一点。

选择实例存储作为默认根设备类型后,您必须选择一个 IAM 角色。这是我们之前创建的堆栈级服务角色。从下拉列表中选择 AWS-ops works-相册-服务-角色。接下来,我们可以选择设置默认的 SSH 密钥。如果您经常需要在命令行上连接到实例,这是一个很方便的字段。它通过假设堆栈中的所有 EC2 实例应该使用相同的密钥来简化密钥管理,这是一种比为每个实例生成一个新的密钥对好得多的方法。我们的应用不需要这个,所以您可以选择不选择默认的 SSH 密钥。提醒一下,SSH 密钥在本章前面已经详细讨论过了。如果您按照这些步骤创建了一个密钥对,现在就从下拉列表中选择它。

接下来,我们必须选择默认的 IAM 实例配置文件。您还记得,我们之前创建了两个 IAM 角色:一个服务角色和一个实例角色。这是我们选择实例角色的地方,AWS-ops works-photo albums-ec2-role。您会注意到,在此屏幕中,您可以选择动态创建一个新角色,这将使 OpsWorks 自动为您生成一个角色。

说到命名约定,下一个选项可能是令人困惑的主机名主题。下拉菜单中有很多选项,如“烘焙食品”、“欧洲城市”、“野猫”等。当您在 OpsWorks 中创建 EC2 实例时,您不希望它们仅仅被命名为 instance-1、instance-2 等。主机名主题选项只是用于实例的主题名称,如“Photoalbums - london-1”或“Photoalbums - paris-2”等。你可以选择任何让你开心的事情,但是请注意列表中的第一个选项,图层依赖。这将根据实例所属的 OpsWorks 层来命名实例,我们稍后将回顾这个概念。我倾向于选择这个选项,因为这意味着您的实例将被命名为“Photoalbums nodejs-app1”、“Photoalbums nodejs-app2”等。在接下来的课程中,我们将使用层相关主题,但是如果你认为用猫命名服务器会很有趣的话,你可以随意选择另一个选项。

最后是堆栈颜色,这没有任何技术意义。这是用于 OpsWorks 仪表板中堆栈的配色方案,纯属个人喜好。让我们选择最右边的红色。

堆栈选项—摘要

现在我们终于准备好点击右下角的添加堆栈。我们选择的选项出现在清单 2-2 中。再次检查您的选择,然后单击按钮。

Listing 2-2. Summary of Stack Creation Options

Name: Photoalbums

Region: US East

VPC: No VPC

Default Availability Zone: us-east-1a

Default operating system: Amazon Linux 2014.09

Default root device type: Instance store

IAM role: aws-opsworks-photoalbums-service-role

Default SSH key: aws-opsworks-photoalbums-key (optional)

Default IAM instance profile: aws-opsworks-photoalbums-ec2-role

Hostname theme: Layer Dependent

Stack color: red

恭喜你!您已经创建了第一个堆栈。我们现在处于 stack detail 视图中,该视图应列出设置应用的下四个主要步骤:

  • 添加你的第一层。
  • 添加您的第一个实例。
  • 添加你的第一个应用。
  • 在线查看您的申请。

所以,是的,在这一课中我们还有很长的路要走。请记住,本章的目标只是通过 OpsWorks 部署在 Web 上看到“Hello World”。下一步是了解图层。

层是应用的主要软件成分,以及与之配套的硬件。让我们稍微分解一下。当您在第一章中设置本地环境时,您必须完成两个主要任务:运行 Node.js 环境和运行数据库。这是我们应用的两个层次。在 OpsWorks 中,每一层都需要分配资源。

第一层,我们的 Node.js 应用,没有运行代码的机器是没有用的——它只是页面上的文字!因此,我们的 Node.js 应用层需要分配 EC2 实例。Layers 视图允许您将这些移动的部分联系在一起:源代码、环境和运行这两者的实例。

第二层,数据库层,有点不同。我们将使用 Amazon RDS 托管我们的数据库,我们将在后面的章节中设置它。就目前而言,理解这种看待问题的方式就足够了。

每个堆栈必须至少有一层,其中有两种类型:OpsWorks 层和 service 层。OpsWorks 层只是分配给它的 EC2 实例的蓝图。OpsWorks 为您提供了许多预设的 OpsWorks 层类型,这些层类型本身被分类为负载平衡器、应用服务器、数据库或其他。对于某些 OpsWorks 层子类型(尤其是 App Server),您可能需要基于同一蓝图的多个实例。其他类型,如负载平衡器,通常只用于单个实例。

除了 OpsWorks 层,AWS 还提供了第二种类型,我们称之为服务层。服务层允许您将其他 AWS 服务作为层添加到 OpsWorks 堆栈中。目前,仅支持一种服务层类型:RDS。我希望将来会推出更多的服务。

创建 OpsWorks 图层

是时候创建我们的第一层了。在堆栈详细信息页面的第一个标题下,单击添加层。就像我们创建堆栈时一样,我们会看到许多选项,如图 2-9 所示。

A978-1-4842-0653-9_2_Fig9_HTML.jpg

图 2-9。

Add Layer view

您可以看到主要的层类型是通过顶部的选项卡来区分的。我们还没有准备好添加 RDS(服务)层,因此我们将停留在 OpsWorks 选项卡中。我们的第一个选择是层类型。这是组织不同 OpsWorks 图层类型的地方。打开下拉菜单,你会看到一个列表,如图 2-10 所示。

A978-1-4842-0653-9_2_Fig10_HTML.jpg

图 2-10。

OpsWorks layer types

默认情况下,应用服务器标题下的 Rails 应用服务器是选中的。我们将选择 Node.js 应用服务器。突然间,我们的很多选择都消失了。嗯,那就简单了!下一个字段是 Node.js 版本。这使您可以选择使用不推荐的 Node.js 版本,以防您正在部署一个尚未在 Node.js 的最新版本中测试的应用。在撰写本文时,我们将坚持使用最新的 0.10.29 版本。

最后一个选项是弹性负载平衡器。我们将在下一章添加一个 ELB 到我们的堆栈中,所以我们暂时跳过这个。将此字段留空,然后单击添加层按钮。就这么简单!我们已经得到了我们的第一层,你应该被引导到层屏幕,如图 2-11 所示。

A978-1-4842-0653-9_2_Fig11_HTML.jpg

图 2-11。

Layers view

现在我们的堆栈中只有一层,那就是应用服务器层。运行我们的应用的所有 EC2 实例都将是这一层的一部分。如果你需要复习这是如何工作的,再看一下图 2-7 。

例子

如您所见,在 OpsWorks 仪表板中已经有许多新的地方可供探索。接下来,让我们继续将我们的第一个实例添加到层中。单击屏幕右侧的添加实例按钮。下一个视图有一些介绍性的文字,以及图 2-12 中的界面。

A978-1-4842-0653-9_2_Fig12_HTML.jpg

图 2-12。

Add an instance

你会注意到只有几个设置可供选择。大多数配置都基于我们在创建堆栈时选择的默认值。例如,Hostname 字段根据我们之前选择的主机名主题预先填充了一个唯一的名称。您可以将其保留为 nodejs-app1,除非您有自己的命名约定。

可能最大的决定(没有双关语)是大小。您将在这里看到一个长长的选项列表,包括当前一代和上一代的尺寸,以及各种优化选项。亚马逊定期发布新一代实例,其规格和价位与上一代不同。你可以在这里对照这个列表: http://aws.amazon.com/ec2/instance-types/#Instance_Types 。为了不让这个页面变得过于臃肿,前代的规格被移到这里单独的一页: http://aws.amazon.com/ec2/previous-generation/

在生产环境中,当创建应用服务器层的第一个实例时,您必须考虑很多因素。我将在后面详细讨论这一点。现在,选择列表中最小和最便宜的选项 ??.micro 是有意义的,一直到最底部。

接下来,我们为此实例选择可用性区域。同样,我们在创建堆栈时选择的默认值也被选中。您可以更改可用区域,但现在让它保持为 us-east-1a。我们稍后将在其他区域中创建实例,因此我们希望在默认区域中至少保留一个实例。继续并点击高级,这样您就可以看到额外的选项。

首先是扩容类型。这是我们稍后将深入研究的主题,但是现在我们希望我们的实例 24/7 在线,所以我们将选择该选项。您将在堆栈创建过程中认识到的其他字段。让我们不去管这些默认值,但是如果您想在实例级修改它们,最好知道它们在这里。点击 Add Instance,您将进入 Instances detail 视图,这是一个您非常熟悉的屏幕(见图 2-13 )。

A978-1-4842-0653-9_2_Fig13_HTML.jpg

图 2-13。

Instances view

在此视图中,您可以看到层中所有实例的状态。顶部的圆形图显示了处于各种状态的实例的数量和百分比:联机、启动、关闭、停止或错误。我们目前有一个实例已经停止,所以这里没有太多可操作的信息。在右上方,您会注意到一个按钮,上面写着 Start All Instances,这正是它所说的。您还可以单独启动或停止实例,例如,如果您想要手动扩展其中一个实例。或者,如果您想要手动扩展许多实例,您可以一次停止一个实例,扩展它们,然后重新启动它们,这样您的应用就不会出现任何停机。您还可以在此屏幕上向您的层添加更多实例。

在顶部黑色的 AWS 导航栏下面,您会注意到 OpsWorks 附加了一个灰色的导航栏。在左侧,您可以导航到当前堆栈,紧挨着它的是一个下拉菜单,可以导航到完全不同的堆栈或创建一个新的堆栈。单击导航按钮,您应该会看到类似于图 2-14 所示的下拉菜单。

A978-1-4842-0653-9_2_Fig14_HTML.jpg

图 2-14。

OpsWorks stack navigation menu

您将看到一些我们已经参观过的视图,以及一些不熟悉的视图。选择应用,我们将最终在 OpsWorks 中创建应用。

应用

Apps 视图顶部的介绍性文本解释了这个概念,正如我所希望的那样:

An application represents the code stored in the repository that you want to install on the application server instance. When you deploy the application, OpsWorks downloads the code from the repository to the specified server instance.

当我们创建应用时,我们将 OpsWorks 配置为获取示例应用的副本,并将其部署到应用服务器层中的每个实例,然后启动这些实例。您应该会看到一个蓝色消息框,通知您没有应用,但可以添加应用。点击这个,我们就可以开始创建这个应用了。

首先是名称字段,您可以在其中输入相册的名称。下一个字段是 Type,它应该默认为 Node.js,所以我们可以不去管它。下一部分的标题是数据源,在这里您可以选择想要使用哪种类型的数据库。您可以选择 RDS,这是一个服务层。如果您希望在 OpsWorks 中创建 MySQL 数据库层,可以选择 ops works;如果您托管的是静态应用,则可以选择 None。我们将选择 RDS 并在稍后创建一个 RDS 实例(参见图 2-15 )。

A978-1-4842-0653-9_2_Fig15_HTML.jpg

图 2-15。

Add an app to your OpsWorks stack

应用源

我们已经到了一个重要的决策点:我们如何将我们的源代码部署到我们的服务器上?这是一个重要的决定,不是对你的应用的功能,而是对你的开发团队的工作流程。我们将逐一查看这些选项。您可以选择最适合自己的方法,在以后的课程中,我将假设您可以自己管理部署。图 2-16 显示了一个示例配置。

A978-1-4842-0653-9_2_Fig16_HTML.jpg

图 2-16。

Application Source configuration in OpsWorks app

如果您打开 Repository type 菜单,您将会在三个标题下看到五种类型:源代码控制、Bundle 和其他。如果从源代码管理部署,则提供 URL 和凭据,这将允许 OpsWorks 连接到您的存储库并从指定的分支下载源代码。如果您选择 Bundle,OpsWorks 将从指定位置检索一个 zip 文件,将其解压缩,然后运行应用。最后一个选项是其他,是针对更高级的用户。您可以使用 Chef recipes 来处理部署过程,而不是由 OpsWorks 检索您的代码。

如果您熟悉 GitHub,最简单的方法是从您自己的 GitHub 库进行部署。如果您使用另一个允许您通过 SSH 连接的 Git 存储库服务,过程基本上是相同的。我将在下面描述不同的方法,你可以选择适合你的方法并继续下去。

从 Git 部署

您将需要生成一个 SSH 密钥,以便 OpsWorks 可以连接到您的存储库。如果你需要帮助,这里有一个方便的指南: https://help.github.com/articles/generating-ssh-keys/

Note

生成 SSH 密钥时,不要设置密码。OpsWorks 不支持带密码的 SSH 密钥,您将无法使用您的密钥进行部署。

生成密钥后,我们可以填写字段。首先是资源库 URL,您可以在 GitHub 的屏幕右侧找到它(参见图 2-17 )。如果您使用 Beanstalk 或其他存储库服务,您会在类似的侧栏中找到 SSH 克隆 URL。

A978-1-4842-0653-9_2_Fig17_HTML.jpg

图 2-17。

SSH clone URL in GitHub

将 SSH 克隆 URL 复制到剪贴板,并粘贴到 OpsWorks 中的存储库 URL 字段。接下来,我们需要您生成的私钥。它应该被命名为“github_rsa”之类的东西,并位于您计算机上的./ssh中。在纯文本编辑器中打开文件。它应该如下所示:

-----BEGIN RSA PRIVATE KEY-----

Proc-Type: 4,ENCRYPTED

DEK-Info: AES-128-CBC,3048941ED91AFBCE12E396E516EC35D4

0gkTkCilHDYOgommrpNVlmZjtKxrD4smsFOVgvhweaNv0G8aTMQcjYb461TqwdsJ

{{A BUNCH OF RANDOM CHARACTERS}}

iLOdRv+4XFKhN3ZKyJ9VwV0yxrV6hSR0FOwFzGtXAD8OJctMcyAwGctJJmNQmRe2

-----END RSA PRIVATE KEY-----

将文件内容复制到剪贴板上,并粘贴到存储库 SSH 密钥字段中。最后,您可以选择一个特定的分支/修订来部署。默认情况下使用 HEAD,所以我们不去管它。例如,如果您正在创建一个新的堆栈作为您的开发环境,您应该在这里输入您的开发分支名称。

从 Subversion 部署

与 Git 一样,您将为 OpsWorks 提供连接到您的存储库和下载源代码副本的凭证。使用 Subversion,您必须通过 HTTP 连接,提供您的帐户用户名和密码。与 OpsWorks 一样,您可以指定要部署的特定版本。但是,请注意,这次您必须在存储库 URL 中包含代码的完整路径。如果要从特定分支进行部署,请确保在存储库 URL 中包含目录路径。

从 HTTP 归档部署

如果您没有使用代码存储库,或者出于某种原因,您不想将目录从 repo 部署到 OpsWorks,还有一些其他选项。首先,您可以从 Web 上任何地方托管的归档进行部署。如果您选择 HTTP 存档,您只需要应用的 zip 存档的 URL。OpsWorks 将下载您的归档文件,提取它,并将其部署到您的实例中。如果您的归档受密码保护,您可以选择提供用户名和密码。如果你的 zip 文件可以在网上公开访问,显然这意味着你的源代码可以被任何人访问,这可能不是一个好主意。

从 S3 档案库部署

就像部署 HTTP 归档文件一样,您也可以从亚马逊 S3(简单存储服务)上托管的归档文件进行部署。要设置这一点,我们必须绕道,但如果可以的话,整合资源并使用 HTTP 上的 S3 是明智的。当您选择 S3 时,您会看到您需要三条信息:您的存档的 URL、访问密钥和秘密。这意味着我们必须创建一个 S3 时段和一个 IAM 用户来访问该时段。我们将快速浏览这些步骤,并在本书的后面花更多的时间和 S3 在一起。

首先,让我们创建我们的 IAM 用户,允许访问 S3 存储桶。我们可以先创建一个组,然后像以前一样添加用户。然而,权限很简单,如果有必要,我们可以重用这个用户,所以让我们保持简单,只创建一个用户。最好是在一个新的标签中进行这种迂回。单击(或右键单击或按住 Control 键单击,具体取决于您的操作系统)左上角的橙色框,返回 AWS 主屏幕,然后选择 IAM。单击左侧导航栏中的用户,然后单击用户页面顶部的创建新用户。我们将这个用户称为 photoalbums-s3。当您进入下一个屏幕时,请确保单击下载凭据,以保存用户访问密钥/密码的副本。然后单击关闭。

现在,您应该会在用户列表中看到您的用户。单击名称进入用户详细信息视图。你以前来过这里!接下来,我们必须为用户生成一个策略,允许用户拥有对 S3 的完全访问权限。在权限标题下,单击附加用户策略。我们再次处于策略生成器中。在“选择策略模板”标题下,向下滚动并选择“亚马逊 S3 完全访问”。您将有机会在生成策略之前对其进行检查(参见图 2-18 )。

A978-1-4842-0653-9_2_Fig18_HTML.jpg

图 2-18。

S3 Full Access Permissions policy

接下来,我们必须创建我们的 S3 桶。回到 AWS 主页,点击 S3。单击左上角的创建存储桶。将出现一个模态弹出窗口,提示您命名该桶。输入 photoalbums-source-[您的姓名首字母]作为您的存储桶名称,并选择您所在地区的美国标准(参见图 2-19 )。

A978-1-4842-0653-9_2_Fig19_HTML.jpg

图 2-19。

Creating an S3 bucket

选择名称和区域后,单击创建。您将返回到 S3 的主视图,您的存储桶将出现在左侧。点击它,你会看到你的桶的内容,目前是空的。单击此视图左上角的上传。创建一个你的源代码的档案,并把它拖到上传对话框中。您不需要在这里设置任何其他选项;只需点击开始上传。

您将再次返回文件列表,上传进度将出现在屏幕右侧。完成后,在文件列表中点击您的.zip。在右上角,您会看到一个分段控制,当前选择了“无”。点击属性,您的档案的属性会出现在右侧,如图 2-20 所示。找到链接,并将完整的 URL 复制到剪贴板。

A978-1-4842-0653-9_2_Fig20_HTML.jpg

图 2-20。

S3 object properties

回到 OpsWorks,将 URL 粘贴到 Repository URL 字段中。访问密钥 ID 和秘密访问密钥是您创建的 IAM 用户的凭证。如果您下载了凭据,请将其打开并复制到这些字段中。如果您能够遵循所有这些步骤,那么您应该能够从 S3 进行部署。若要将更新部署到您的代码,您必须每次都覆盖 S3 存储桶中的 zip,或者上传新的 zip 并在您的应用设置中更改路径。如您所见,这并不理想。一些存储库服务还允许您从他们的服务器部署到 S3,因此也可以从您的存储库部署 zip 到 S3。无论如何,这种方法比生成一个 SSH 密钥并直接从 Git 部署要耗费更多的人力。

创建您的应用

配置完应用源后,您将看到以下部分:环境变量、添加域和 SSL 设置。您将在后续课程中学习这些内容。如果其他一切看起来都很好,继续点击右下角的添加应用。你应该会返回到 Apps 视图,如图 2-21 所示,在这里你可以看到你的应用已经被创建了。

A978-1-4842-0653-9_2_Fig21_HTML.jpg

图 2-21。

The Apps view

部署您的应用

嗯,除了部署您的应用之外,没有太多事情要做了!在我们这样做之前,我们必须启动一个实例。单击左上角的导航并选择实例以返回实例视图。在右上角,单击启动所有实例。您将看到实例旁边的 Status 字段更改为 requested,然后是 pending,booting,running_setup,最后是 online。整个过程需要几分钟。

现在我们有了一个在线实例,我们可以进行部署了。在导航下,选择应用以返回到应用视图。您会在相册行右侧的“操作”栏中看到“部署”按钮。点击它,您现在应该会发现自己在部署应用视图中,如图 2-22 所示。

A978-1-4842-0653-9_2_Fig22_HTML.jpg

图 2-22。

Deploy App view

您将看到除了 Deploy 之外,在这个视图中还有其他命令可用。您可以取消部署您的应用、回滚到以前的版本、启动、停止或重新启动应用。现在,我们将使用 Deploy 命令。下面的注释字段是您自己关于部署的内部注释。

下面的实例标题通知您将在其中一个实例上进行部署。您只能部署到已经启动的实例。这是因为停止的实例实际上并不存在。您无需为资源付费,因此 AWS 数据中心不会提供任何资源。除了启动实例之外,不能与已停止的实例进行交互。如果单击“高级”,可以看到正在部署的实例列表。

如果出于某种原因,您想只部署到特定的实例,您可以在这里选择/取消选择它们。其中一种情况是,您的一个实例出错(崩溃),您需要部署一个补丁来解决这个问题。您部署到当前在线的实例,同时尝试将有问题的实例恢复在线。一旦上线,您可以再次运行部署,但是只能在刚刚从错误中恢复的实例上运行。您可能从未遇到过这种用例,但很高兴知道 OpsWorks 可以处理它!

点击【部署】,将进入部署视图,如图 2-23 所示。

A978-1-4842-0653-9_2_Fig23_HTML.jpg

图 2-23。

Deployment view

当部署运行时,您将看到一些活动指示器在旋转。完成后,顶部的状态将变为 successful,并且您的实例旁边会出现一个绿色复选标记。您会注意到这里有几个有趣的特性。在 SSH 列下,您可以选择通过 SSH 直接连接到任何正在运行的实例。你可能永远都不需要这么做,但拥有它是件好事。在日志列中,可以单击显示来查看实例的部署日志。

这些日志一开始可能有点让人不知所措;它们是部署应用时执行的所有厨师食谱的输出。它们通常应该是这样的:

[2014-10-26T18:48:44+00:00] INFO: Chef-client pid: 3167

如果您看到除了INFOWARN之外的任何日志类型,您可能想要进一步调查。如果你喜欢冒险,你可以通过日志来了解发生了什么。几个主要事件大致如下:

  • 运行opsworks_custom_cookbooks::load::execute命令。
  • 基于你的应用的配置和语言的附加食谱列表被执行。
  • 您的代码是通过 SSH 复制和验证的。
  • 您的代码被部署到/srv/www/photoalbums,Node.js 环境被配置。
  • 检测到您的package.json文件,并且安装了node_modules
  • 您的应用已启动(或重新启动)。

这是一种简化,但是它应该让您了解在幕后发生了什么来使您的代码在云中运行。现在是真相大白的时候了。打开 OpsWorks 导航菜单并选择“实例”。在 Public IP 列中,您应该看到您的单个实例的 IP 地址。点击它,您应该会看到我们的欢迎屏幕,如图 2-24 所示。

A978-1-4842-0653-9_2_Fig24_HTML.jpg

图 2-24。

Welcome to Photoalbums!

你能相信吗?我们终于在云中运行了我们的应用!因为我们没有配置负载平衡器或添加域,所以我们只是在单个实例上查看应用。这不是我们对 OpsWorks 的预期用途——我们不希望我们的用户直接访问 EC2 实例。我们将很快解决这个问题。

摘要

恭喜你,你有了一个驻留在网络上的应用,在云中!遗憾的是,在我们至少配置好数据库之前,它基本上还是没用的。你已经在这一章中讲述了很多,从探索 AWS 架构的主要概念到从头开始创建我们的应用堆栈和部署我们的代码。在下一章中,我们将使用 Amazon RDS 托管我们的数据库,我们的应用将第一次变得可用。通过学习如何使用 OpsWorks,您还学习了很多关于 AWS 的范例和术语,尤其是关于身份和访问管理以及 EC2 的知识。这是云架构和开发道路上的一大步。

三、OpsWorks 第二部分:数据库和扩容

现在我们有了一个正常工作的应用堆栈,我们可以开始真正地构建功能了。首先,我们必须将应用连接到数据库,这样我们就可以开始存储和检索内容。其次,我们将在堆栈中添加一个负载平衡器。最后,我们将为我们的应用设置一些自动伸缩行为,以便应用可以自动伸缩以满足需求的增长,具有弹性。

您应该还记得上一章中的内容,OpsWorks 堆栈中包含一个或多个层。在第二章中,我们创建了一个单层:应用服务器。我们将在本章中添加额外的层,这将完善设置 OpsWorks 的主要任务。我们将通过配置和附加一个高可用性的托管 MySQL 数据库来创建数据库层。我们还将在堆栈上附加一个负载平衡器,在多个 EC2 实例之间分配 web 流量。虽然我们将多次返回 OpsWorks 仪表板,但我们将与其他 AWS 服务一起构建我们应用的核心组件。

关系数据库服务

RDS 是 AWS 数据库服务产品之一,也是我们在这个项目中的选择。RDS 支持 MySQL,性价比高,可以作为服务层集成到 OpsWorks 中。与 EC2 类似,RDS 实例可以进行伸缩、克隆和性能问题监控。

敏锐的观察者会想,为什么我们不能创建一个新的 OpsWorks 层,并在一些 EC2 实例上运行 MySQL。OpsWorks 里不是有 MySQL 层类型吗?是的,有,你可以很容易地做到这一点。然而,RDS 不仅仅具有原始的计算能力。RDS 有一些很棒的特性,我们将在本章中进一步探讨。

如果您在 OpsWorks 的 MySQL 层中创建了一个 EC2 实例,那么您只是安装了必要的软件来在该实例上运行 MySQL 数据库。仍然由您来执行所有管理任务,最重要的是备份您的数据。您还可以根据需要安装软件更新。如果 MySQL 崩溃,或者实例出错,您将不得不手动恢复。

这些听起来都不吸引人,不是吗?幸运的是,RDS 可以为您自动完成所有这些任务!如果我们不得不做这么多的维护工作,我们就不会充分利用云或者利用我在过去两章中提到的优势。当您使用 RDS 实例时,您不必担心所有这些维护工作。对应用性能最重要的是错误恢复。当您创建 RDS 数据库时,您以与 EC2 实例相同的方式提供资源。您的数据库托管在特定区域内的特定可用性区域中。如果数据库层出现故障,而您正尝试手动恢复,这可能会导致整个应用长时间停机。RDS 不仅最大限度地减少了停机时间,还允许您在主实例停机时保持备份实例随时可用,这是一种称为多 AZ 部署的特性。

多 AZ 部署

当您的应用依赖于单个 RDS 数据库实例时,这自然会在您的应用中产生一个故障点:如果该可用性区域(或实例)出现故障,可能会导致您的堆栈出现全局故障。为了缓解这个问题,您可以使用多 AZ 部署。当您启用多 AZ 部署时,RDS 会自动在另一个可用性区域中提供备用数据库(参见图 3-1 ),并通过同步过程将数据复制到备用实例。每次在数据库上执行写或提交操作时,在事务完成之前,也会在备用数据库上执行该操作。缺点是,与单个可用性区域部署相比,这会导致延迟略有增加。如果您的应用要求每秒进行数千次数据库写入,这种微小的延迟差异可能会变得很明显。请记住,这可能会在您的应用中构成有形的性能成本。多 AZ 部署的成本也更高,因为您实际上使用的资源是其他方式的两倍。

A978-1-4842-0653-9_3_Fig1_HTML.gif

图 3-1。

Multi-AZ deployments

显然,多 AZ 部署需要进行成本效益分析。一方面,您不必担心通过手动启动数据库的备份副本来应对停机。任何不得不在周五晚上 11 点上班的工程师都知道这一点的价值。拥有一个完全最新的备份副本来运行您的应用并在几分钟内可用,这是一个技术奇迹,但一般人对此并不欣赏。另一方面,使用 Multi-AZ 会损失一点点写入速度,而且成本更高。如果 Multi-AZ 不值得花费,AWS 承诺 RDS 实例每月 99.95%的正常运行时间。如果您选择使用单个实例,您的应用将面临中断的风险。

然而,在我们这样的应用中,我们甚至可能不会注意到延迟的差异。我们的数据库模式很简单,查询也很简单。用户不太可能一次对每个用户执行大量的写操作,所以在我们的例子中,这似乎不是一个主要问题。事实是,虽然多 AZ 的好处可能是普遍的,但多 AZ 部署的性能成本取决于应用的性质以及预计的用户群规模和行为。

读取副本

正如刚才所讨论的,我们预计我们的数据库操作是读取密集型的。如果我们数据库上的大量读取操作(用 MySQL 术语来说,任何SELECT查询)成为我们数据库的瓶颈,这将对整个应用产生连锁反应。幸运的是,有一种方法可以将部分工作转移到另一个数据库实例,即读取副本。您可以创建这种类型的 RDS 实例(最多五个),指定一个主数据库并为您的读取副本选择一个区域和可用性。从术语“副本”可以推断,这些实例应该与原始数据库拥有相同的资源。

在图 3-2 中,您可以看到读取副本是如何工作的。数据库读取可以路由到读取副本,读取副本以较低的查询量从主实例读取。任何写操作都会绕过读复制副本,转到应用堆栈的原始可用性区域中的主实例。使用读取副本可以将大量工作从主数据库实例转移到副本。如果主数据库只需要处理少量的读操作和所有的写操作,会有很大的好处。

A978-1-4842-0653-9_3_Fig2_HTML.gif

图 3-2。

Cross-Region Read Replica behavior. The green lines represent write operations, and the black lines represent read operations

正如您可能从图 3-2 中推测的那样,RDS 实际上支持在其他区域创建读取副本,具有一个称为跨区域读取副本的特性。灾难恢复场景有一个好处。如果由于某种原因,整个 AWS 区域发生了中断,您必须在另一个区域快速重新部署整个堆栈。为此,您可以将堆栈克隆到不同的区域,并将先前存在的读取副本之一提升为新的主数据库。否则,从区域性中断中恢复是可能的,但是这个特性只是使它变得更容易。

如果您只想将堆栈移动到不同的区域,跨区域读取副本在非灾难场景中也很有用。假设我们的应用在美国很失败,但在德国却很受欢迎。有理由将堆栈移动到离我们的用户群更近的地理区域。

由于 OpsWorks 旨在让您在单个区域部署堆栈,并使用其他服务来提高您在其他区域的性能,因此我们不会从跨区域读取副本中获得太多好处,除非我们计划应对区域性灾难。您将很快了解到有助于我们提高性能的其他服务,但不管怎样,了解一下这项功能还是有好处的。

PRICING

有了这些额外的功能,你可能会立即怀疑 RDS 的可负担性。的确,它比 EC2 定价要简单一些,你可以在这里看到: http://aws.amazon.com/rds/pricing/ 。主要区别在于:除了为处理器和内存(也就是实例的计算能力)付费之外,还需要为数据存储和数据传输付费。数据存储是指存储在实例中的原始数据量,数据传输是从数据库到应用层的输入/输出。与其他服务类似,这是一种按需付费的模式,但这里有很大的偏差空间。

调配 IOPS 存储

尽管我们已经选择了实例大小,但我们没有向 Amazon 提供我们想要执行多少输入/输出操作的信息,I/O 速度和容量是衡量数据库性能的关键指标。如果我们的流量在一个小时内从五个用户激增到五千个用户,每秒钟进行一次数据库查询,我们将依赖 AWS 来动态地自动提供额外的 I/O 容量。

如果您考虑一下查询数据库时会发生什么,这是有意义的。当突然出现超过数据库 I/O 容量的请求时,它们会被放入一个队列中,而 RDS 则试图跟上流量。与此同时,RDS 试图为您的实例增加 I/O 资源,这完全发生在 AWS 数据中心的幕后(也称为云中)。但是,分配额外的容量很可能需要一秒钟以上的时间,请求留在队列中的每一秒钟都是用户等待所请求数据的额外一秒钟。当流量减少时,资源会被取消分配,并且会对您使用的资源进行收费。

如果我们要管理性能预期,这似乎不是生产中的最佳想法。幸运的是,您可以使用调配的 IOPS 存储预留 I/O 资源(每秒输入/输出操作数)。当您使用调配的 IOPS 存储时,您会按存储卷和每秒 I/O 操作数来保留 IOPS 资源。虽然这比随用随付更昂贵(如果您不使用您提供的资源),但提前预留资源可以保证在高流量期间保持一致的速度和性能。

让我们想象一下与之前相同的场景,只是使用了调配的 IOPS 存储。这一次,您保留了 10,000 IOPS 和 10GB 的存储空间。同样,您的流量从 5000 个用户激增到 5000 个用户,每秒钟进行一次数据库查询。在这种情况下,我们已经有足够的资源来处理双倍的 I/O 流量。当然,如果我们的流量翻倍,我们将再次陷入困境,但我们也可以为这种可能性做好准备。

数据库安全组

在创建数据库之前,我们必须创建一个 DB 安全组。如果我们的数据库在虚拟私有云中,公共访问将受到限制。因为我们没有使用 VPC,所以我们希望将数据库访问限制在应用堆栈中的 EC2 实例。因为我们也想在本地工作,我们将允许从我们的 IP 地址访问数据库。虽然您会从我们在身份和访问管理方面所做的工作中认识到这些概念,但我们实际上将在 RDS 中配置我们的安全组。

首先返回您的登录 URL,以您在第一章中创建的 photoadmin IAM 用户身份登录。从 AWS 控制台主页(或菜单)中,选择 RDS。您应该会看到类似于图 3-3 中的视图,其中显示了 RDS 仪表板最左边的两列。和往常一样,右边有一个专栏,其中有这里没有显示的其他链接和资源。

A978-1-4842-0653-9_3_Fig3_HTML.jpg

图 3-3。

The RDS dashboard

在左侧列中,单击安全组。在右侧,您会看到一个蓝色的大按钮,上面写着 Create DB Security Group。第一种观点很简单。您为您的安全组提供一个名称和描述(参见图 3-4 )。

A978-1-4842-0653-9_3_Fig4_HTML.jpg

图 3-4。

Create DB Security Group view in the RDS dashboard

您可以将该组命名为 photoalbums-rds-group。描述可以是以后对你有用的任何东西。然后,单击是,创建。您将返回到 RDS 安全组视图,并且您应该看到您的组出现在表格中,如图 3-5 所示。

A978-1-4842-0653-9_3_Fig5_HTML.jpg

图 3-5。

Your RDS security groups

您会注意到状态栏中的红色文本“无授权”这意味着尽管该安全组已经创建并可以分配给 RDS 实例,但该组目前不提供对任何实例的访问。这是一个方便的警告,说明你在这里的工作还没有完成。

为您的安全组选择表行,您将能够创建一个新的连接类型,以便在您的安全组中进行授权。这两种类型是 CIDR/IP 和 EC2 安全组。我们将分别创建一个。

当您授权一个 CIDR/IP 时,您将列出一个特定的 IP 地址来连接到您的数据库。这对于开发来说是理想的,因为我们可以列出自己的 IP 地址来连接 RDS 实例。默认情况下,您将在字段中看到您当前的 IP 地址。如果你使用代理/防火墙,你必须禁用它,或者如果你在公司网络上,与网络管理员合作。如果您没有代理或在防火墙后面,请保留 CIDR/IP 地址不变,然后点按“授权”。否则,确定正确的 IP 并相应地更改值。请记住,如果您有一个动态 IP,您将不得不在每次 IP 地址刷新时重复这个过程。

您将在您的安全组旁边看到一个活动指示器,并且会出现一个新表,其中包含您的安全组的授权连接(参见图 3-6 )。接下来,我们将创建一个 EC2 安全组类型的新连接。就像 RDS 一样,EC2 也有自己的安全组。事实上,我们的应用服务器层中的所有实例都是它们自己的安全组的一部分。

A978-1-4842-0653-9_3_Fig6_HTML.jpg

图 3-6。

DB Security Group, authorizing connections

从连接类型下拉列表中选择 EC2 安全组。您会注意到,除了您自己的帐户之外,您还可以选择另一个 AWS 帐户。在撰写本文时,Amazon 正在推出跨帐户连接,从而允许另一个 AWS 帐户上的 EC2 实例连接到您的数据库。保持此帐户处于选中状态,并在 EC2 安全组下拉列表中选择 AWS-OpsWorks-nodejs-AppServer,然后单击授权。现在,您应该在安全组的连接表中看到第二行。

创建 RDS 数据库

现在您有了一个 RDS 安全组,它将允许来自您的本地机器、您在 OpsWorks 中的应用服务器层的连接,而不允许来自其他地方的连接!事不宜迟,让我们继续创建我们的数据库层。在 RDS 主页屏幕上,您将看到一个蓝色的大按钮,邀请您启动一个 DB 实例。单击该按钮将带您进入多步实例设置。如图 3-7 所示,第一步是选择数据库引擎,在撰写本文时,它包括四个选项:MySQL、PostgreSQL、Oracle 和 SQL Server。这是显而易见的,因为我已经讨论过在我们的应用中使用 MySQL。单击选择并继续。

A978-1-4842-0653-9_3_Fig7_HTML.jpg

图 3-7。

RDS database engine selection

第 2 步很有趣,因为这是 AWS 就如何配置生产环境和开发环境给出明确建议的罕见场景之一。如图 3-8 所示,步骤 2 的标题是“生产?”对我们来说,答案是肯定的!

A978-1-4842-0653-9_3_Fig8_HTML.jpg

图 3-8。

Choosing to use an RDS instance in a production environment

如您所见,Amazon 强烈建议在生产环境中使用多 AZ 部署和调配的 IOPS 存储功能。我们会听从他们的建议,选择“是”,然后单击“下一步”。在第 3 步中,我们将配置我们的数据库细节(参见图 3-9 )。

A978-1-4842-0653-9_3_Fig9_HTML.jpg

图 3-9。

RDS database details

实例规格

License Model 字段可以保留为 general-public-license,也可以保留 DB 引擎版本。如果你需要一个特定的 MySQL 版本,你可以在这里选择。接下来,在 DB Instance Class 字段中选择实例的大小。出于我们的目的,db.t1.micro 应该可以工作。让我们保留多 AZ 部署和调配的 IOPS (SSD)。您可以现在选择磁性存储以节省资金,以后再切换到调配的 IOPS,但转换将需要不确定的时间。在这种情况下,AWS 控制台将显示一条消息,通知您在应用更改时,多 AZ 备份实例将正常工作。我艰难地认识到情况并不总是这样。

如果您听取了我的建议并选择了“已调配 IOPS ”,您必须选择您希望分配多少存储和多少 IOPS。当您选择“调配的 IOPS”作为存储类型时,将选择 100GB 存储和 1000 调配的 IOPS。亚马逊推荐 IOPS 与存储的比率在 3:1 到 10:1 之间。我们可以暂时不考虑这个设置。随着时间的推移,我们将为我们的应用建立一个操作历史,并根据我们对收集的指标的分析来扩大或缩小这些资源。

Note

在现实世界的应用中,在 AWS 提供的最小实例中使用所有这些 RDS 特性有点傻。我们选择 db.t1.micro 只是为了经济高效的实践。

设置

如果您以前曾经建立过数据库,这些设置会很熟悉。您将为您的数据库设置唯一的标识符、用户名和密码。在数据库实例标识符中,输入 photoalbums。为了便于讨论,我们将主用户名命名为 admin。你实际上可以设置任何你想要的用户名和密码,只要你记住它!设置好凭据后,单击下一步。

高级设置

步骤 4 将我们带到高级设置,您可以在图 3-10 中看到。在网络安全中,您将无法选择 VPC 或可用性区域。选择 photoalbums-rds-group 作为您的数据库安全组。在数据库选项中,您可以命名您的数据库相册。同样,数据库端口、参数组和选项组也不必更改。

A978-1-4842-0653-9_3_Fig10_HTML.jpg

图 3-10。

RDS advanced settings

下一小节的标题是“备份”,从中可以为实例选择自动备份计划。RDS 将自动对您的实例进行每日备份(快照),您可以指定进行这些备份的窗口以及保留备份的时间长度。首先,选择一个备份保留期,默认为 7 天。这是亚马逊将保留备份的天数,从 0 到 35。如果选择 35,您将能够将您的实例恢复到过去 35 天拍摄的任何每日快照。万一发生灾难,有这些备份是很方便的。但是,如果您需要更多的长期备份,您将不得不手动生成它们(或者使用 AWS API 以编程方式)。

您还可以为您的数据库选择一个备份窗口。您可能希望在特定时间创建备份,例如在预期流量激增之前或之后。如果您没有偏好,它们将默认为深夜,此时流量预计会减少。没有首选项也可以,您同样可以将维护设置保留为默认值。单击启动数据库实例,这将带您到一个中间屏幕。单击以返回 RDS 仪表板,在这里您将看到您的数据库首先处于创建状态,然后是修改状态,然后是备份状态,最后是可用状态。

数据库导入

创建 RDS 实例时,让我们准备将本地数据库模式导入到 RDS 实例中。在第一章的中,您看到在/setup/photoalbums.sql有一个 MySQL 数据库与示例项目打包在一起。我们将使用 MySQL Workbench 连接到 RDS 实例,并将该文件导入数据库。如果您喜欢使用另一个接口来连接 MySQL 数据库,那很好。我们使用 MySQL Workbench 只是因为它很简单。

为了唤起您的记忆,我们创建了一个 RDS 安全组,该组只允许访问我们指定的 EC2 实例和您的个人 IP 地址。(如果您在多个地点工作,您必须向安全组添加更多 IP。)我们在该安全组中创建了一个 RDS 实例,我们将确保可以连接到该实例,此时我们将知道一切都按计划进行。打开 MySQL Workbench,或者你正在使用的任何 MySQL 客户端。在左上角,您应该会看到一个+按钮,单击它可以创建一个新的连接。您应该会看到如图 3-11 所示的窗口。将连接命名为相册 RDS。连接方法可以保持为 TCP/IP。我们将不得不参考 RDS 来填写剩余的字段。

A978-1-4842-0653-9_3_Fig11_HTML.jpg

图 3-11。

Setting up a new connection in MySQL Workbench

成功创建实例后,RDS 将生成您的主机名。让我们回到控制台找到它。在 RDS dashboard 中,您应该在表视图中看到您的实例,其状态为绿色的 available。点击该行显示该实例的详细信息,如图 3-12 所示。

A978-1-4842-0653-9_3_Fig12_HTML.jpg

图 3-12。

RDS instance details

在正上方,您应该会看到端点。这是您可以在端口 3306 上连接到实例的 URL。只接受来自你的 IP 地址的连接。复制这个 URL 并将其粘贴到 MySQL Workbench 的 Hostname 字段中(粘贴时记得去掉:3306)。输入您之前选择的用户名和密码,然后单击确定。

MySQL Workbench 将自动打开到数据库的连接。我们现在准备从示例项目中运行 SQL 导出脚本。在文件菜单下,选择运行 SQL 脚本并导航到.sql文件。将出现一个窗口,显示 SQL 脚本的内容,让您选择更改默认模式名或默认字符集。在默认模式名称下拉列表中,选择相册。将字符集留空,然后单击运行。窗口应该将其副标题更改为 Output,输出应该如下所示:

Preparing...

Importing photoalbums.sql...

Finished executing script

Operation completed successfully

单击关闭按钮,并在左侧导航的 SCHEMAS 标题下找到相册数据库。按住 Control 键点按(或在 PC 上右键点按)相册,然后选择“全部刷新”。过一会儿,您应该能够展开 photoalbums 下的表格,并看到您已经在本地使用过的表格:相册、照片和用户(参见图 3-13 )。我们很快就让它运行起来了,不是吗?现在,如果我们的应用也能连接到它就好了!

A978-1-4842-0653-9_3_Fig13_HTML.jpg

图 3-13。

The tables created on your RDS instance

OpsWorks RDS 层

您应该记得,我在前面的第二章中讨论过 RDS 层。现在我们有了一个 RDS 实例,我们将把它注册到应用中。在 AWS 控制台中,返回到 OpsWorks 仪表板。选择您的堆栈,并从导航菜单中选择层。

您应该在列表中只看到一个层:Node.js App Server。下面,点击+层按钮。我们回到添加层视图,这一次,我们必须单击 RDS 选项卡来创建一个 RDS 层。该设置与您看到的应用服务器层略有不同,如图 3-14 所示。

A978-1-4842-0653-9_3_Fig14_HTML.jpg

图 3-14。

Adding the RDS layer

相册实例被自动检测到,因为它在同一区域。你所要做的就是输入你的用户名和密码,然后点击注册堆栈。您将返回到 Layers 视图,现在您将看到在 App Server 层下面的 RDS 层,如图 3-15 所示。但是上面说没有 app 连接!

A978-1-4842-0653-9_3_Fig15_HTML.jpg

图 3-15。

Layers in our stack, with RDS layer added

为了将我们的应用连接到 RDS 层,我们必须将数据库设置为应用服务器的数据源。首先,单击连接应用,这将带您进入堆栈中的应用列表。在相册的操作列中,单击编辑,这将允许您编辑应用的一些设置。

第二个标题是数据源。您会注意到,目前数据源类型被设置为 None。单击 RDS 旁边的单选按钮;选择相册(mysql)作为数据库实例;并输入 photoalbums 作为您的数据库名称。它看起来应该与图 3-16 一模一样。暂时不要离开这个屏幕;我们在这里还有一件事要做。

A978-1-4842-0653-9_3_Fig16_HTML.jpg

图 3-16。

RDS selected as data source for the app

环境和环境变量

如前所述,我们的目标之一是将数据库凭证移出源代码。在第一章的中,我讨论了如果团队成员离开或他/她的电脑被盗,将这些凭证存储在源文件中会带来怎样的安全风险。还有另一个需要考虑的原因,那就是交换我们堆栈中的数据库。到目前为止,您已经看到从头开始创建 RDS 实例是多么容易。将来,您将能够从现有的数据库实例中创建新的数据库实例,这将使您能够在出现严重故障时运行堆栈中的备份数据库。这还将使克隆整个堆栈和维护用于生产和开发的独立堆栈变得非常容易,这是一种强烈推荐的做法。通过遵循这些后续步骤,您将能够维护在所有这些场景下工作的代码,而不必部署代码更改或管理凭据。

到目前为止,我主要讨论了两个环境:本地环境和生产环境。现实情况是,您的工作流可能包括在本地环境中编写和测试代码,然后在开发堆栈上部署和测试,最后部署到生产环境中。这涉及到要管理的三组凭证!如果我们在存储库的不同分支中保存不同的凭证,管理起来会很麻烦,并且很容易出现开发人员错误。相反,我们将使用环境变量来告诉我们的应用它在哪里运行。

如果你已经精通 Node.js,你会知道有一个名为process.env的全局对象,它存储了 Node.js 中关于用户环境的一些信息,你可以在这里找到关于这个的信息: http://nodejs.org/api/process.html#process_process_env 。文档中的属性似乎都没有那么有用。还好我们可以给他们加!

在编辑应用时,向下滚动到环境变量标题,如图 3-17 所示,您会看到您可以在这里添加自己的键值属性。

A978-1-4842-0653-9_3_Fig17_HTML.jpg

图 3-17。

App Environment Variables

添加一个名为ENVIRONMENT的变量,并赋予其值production。如果/当你创建一个开发栈,你将改变这个值。继续并点击右下角的保存。我们现在已经设置了应用的数据源和环境变量ENVIRONMENT

这个环境变量和process.env有什么联系?这是 Chef 的底层技术在幕后工作以使事情变得更简单的微妙方式之一,仅在 Chef 11.10 中可用。当您的应用部署到应用服务器层中的实例时,您设置的环境变量(最多 20 个)将包含在实例的部署脚本中。因为我们的应用是 Node.js,所以在部署过程中会将环境变量添加到process.env中。如果我们运行一个 PHP 应用,环境变量将可以通过getenv($variableName)访问。无论如何,应用设置中的这个接口为我们提供了一个简单的方法来动态设置所有实例中的环境变量,而无需进入服务器配置,也不会破坏我们应用的弹性。

接下来,在耽搁了很久之后,我们不得不回到我们的源代码。目前,源代码被硬编码到本地数据库中。如前所述,我们必须支持三种不同的环境。首先,打开您的代码编辑器到/lib/globals.js。找到database属性,它有本地主机的凭证。我们不想返回一个静态对象,而是想把database变成一个函数(嘿,因为它是 JavaScript,所以并不难!).因为这个文件中还没有其他内容,所以您会希望用清单 3-1 替换整个文件,但是要准备好您的本地凭证,因为我们不会完全丢弃它们。

Listing 3-1. /lib/globals.js

module.exports = {

applicationPort  : 80,

database : function(){

if(process.env.ENVIRONMENT){

var opsworks = require('./../opsworks');

var opsWorksDB = opsworks.db;

var rdsConnection = {

host : opsWorksDB.host,

port : opsWorksDB.port,

database : opsWorksDB.database,

user : opsWorksDB.username,

password : opsWorksDB.password

};

return rdsConnection;

} else {

var local = require('./../config/local');

var localConnection = local.db;

return localConnection;

}

}

}

好了,这里发生了很多事情,我们来分解一下。首先,我们不是返回静态对象,而是检查process.env.ENVIRONMENT是否存在。如果是,我们不关心它是什么,我们将尝试从 OpsWorks 加载数据库凭证,因此出现了行var opsworks = require('./../opsworks');

您会注意到我们的项目中没有这样的文件。在部署期间运行的Configure stack 命令动态编译我们项目根目录中的一个opsworks.js文件。当我们将 RDS 层连接到 Photoalbums 应用时,它会自动开始在该文件中存储数据库的凭据。这意味着,即使我们更改了该层使用的数据库实例,我们也可以通过在堆栈上运行Configure命令来更新凭证,稍后我们将回到这个问题。在opsworks.js文件中,数据库凭证存储在一个名为db的公共变量中。事实上,整个文件看起来有点像清单 3-2 。

Listing 3-2. Sample opsworks.js file

exports.db = {"adapter":"mysql",

"database":"ops_db",

"password":"AsdFGh3k",

"port":3306,

"reconnect":true,

"username":"opsuser",

"data_source_provider":"rds",

"host":"opsinstance.ccdvt3hwog1a.us-east-1.rds.amazonaws.com"

}

exports.memcached = {"port":11211,

"host":null}

在理想情况下,db变量的语法应该与我们在mysql Node.js 模块中需要的语法相匹配。可悲的是,这两者略有不同。为此,我们实例化了一个名为rdsConnection的变量,并将值重新映射到它。然后,返回rdsConnection变量。

在没有设置process.env.ENVIRONMENT的情况下,我们希望保留我们的本地数据库凭证。在您的项目中,创建一个名为config的目录和一个名为local.js的空文件。使用您的本地数据库凭据,将以下内容粘贴到其中:

module.exports = {

db : {

host : 'localhost',

port  : 8889,

database : 'photoalbums',

user : 'root',

password  : 'root'j

}

}

我希望这有意义。我们将本地凭证移动到一个单独的文件中,只有在没有找到process.env.ENVIRONMENT的情况下,我们才会从globals.js中读取这个文件。现在,因为我们把globals.database改成了globals.database(),我们将不得不在几个地方修复它。在server.js中,导航到文件的底部,并在这一行中进行更改:

var connection  = mysql.createConnection(globals.database());

保存更改,然后我们必须更改所有三个模型文件顶部的声明:model-usersmodel-photosmodel-albums。在所有三个文件中,将第 3 行改为:

var connection = mysql.createConnection(globals.database());

为了强调这一点,我们所做的只是在database后面加上一个“()”。

现在,我们将对凭据管理进行最后的收尾工作。我们将让我们的代码库忽略我们的本地凭证,这样它们就不会被推送到 repo,不会与其他开发人员共享,也不会被部署到应用服务器。如果您使用 Git,打开文件.gitignore并添加下面一行:

/config/local.js

保存此更改。如果您使用的是 SVN,您同样希望忽略该文件。将所有这些更改提交到您的存储库中,并返回到 OpsWorks。在保存了最后的更改后,您应该会返回到应用的详细视图。您应该会在右上角看到一个蓝色按钮,上面写着部署应用。点击按钮,进入部署应用视图。这里你唯一需要改变的是添加对你有用的注释(见图 3-18 )。

A978-1-4842-0653-9_3_Fig18_HTML.jpg

图 3-18。

The Deploy App view

单击右下角的 Deploy,等待部署完成。如果几分钟后失败,请点按“日志”栏中的“显示”,看看是否能发现错误。如果您的主机名旁边有一个绿色的复选标记,那么您应该有一个应用现在连接到您的数据库。单击 nodejs-app1,向下滚动到 Network Security,找到您的实例的公共 IP。点击它,你应该回到你的 Hello World 屏幕。现在将/users/添加到 URL 中。

如果你看到一对空括号,恭喜你!您请求了所有用户,但收到的都是零。这意味着您的应用服务器能够连接到 RDS 实例,查询数据库,并返回结果。如果您看到的是一条错误消息,那么很遗憾,一定有什么地方出错了。检查打字错误,如果你没有发现,试着追溯你的步骤。

堆栈命令:备份场景

我简要讨论了堆栈命令的概念,这是一个 OpsWorks 工具,用于在堆栈中的所有实例上运行命令。这些命令本质上抽象了 Chef 的底层技术,让我们可以控制所有实例,如果我们必须单独管理实例,这将更加困难。我们将使用一个数据库场景来更深入地探讨这个问题。

RDS 快照

假设您的数据库处于错误状态,并且您无法让它重新联机。随着时间的推移,您的用户无法访问您的应用。您的多 AZ 备份没有按预期工作,从应用服务器到 RDS 层的每个查询都返回一个错误。您和您的团队一致认为,最好的做法是部署一个备份数据库,让您的应用重新上线。在传统的堆栈中,完成这一任务所涉及的管理任务可能是一场噩梦,会导致应用长时间停机。幸运的是,我们可以通过使用 OpsWorks 节省大量的时间和精力。

我们要做的第一件事是获取数据库的快照,我们将用它来生成新的实例。如果实例中的实际数据没有问题,我们现在就可以拍摄快照;否则,您最好使用 RDS 生成的自动快照。导航到 RDS 仪表板并选择 DB Instances 选项卡。从表中选择您的实例,单击“实例操作”按钮以显示“操作”菜单,然后选择“拍摄数据库快照”。

系统将提示您命名快照。自动快照前面有 rds:前缀。将快照命名为 photo albums-Snapshot-[YYYYMMDD],然后单击是,拍摄快照(参见图 3-19 )。

A978-1-4842-0653-9_3_Fig19_HTML.jpg

图 3-19。

Taking an RDS DB snapshot

您将进入快照视图,在该视图中,您的快照将处于正在创建状态。您还可以在此视图中看到一些自动备份。在我们继续本课之前,您需要等待快照完成。

快照完成后,我们将创建一个新实例。从列表中选择快照,然后单击顶部的恢复快照。您将看到一个名为 Restore DB Instance 的视图,尽管您会认出最初创建 RDS 实例时的界面。因为我们只是运行一个练习,所以您可以使用另一个 db.t1.micro 实例并禁用多 AZ 部署。您可以将实例命名为 photoalbums-failover,然后单击启动数据库实例(参见图 3-20 )。

A978-1-4842-0653-9_3_Fig20_HTML.jpg

图 3-20。

Restoring a DB instance from a snapshot

同样,我们需要等待一段时间来创建实例。当状态变为可用时,我们可以开始将其部署到堆栈中。

创建新的 RDS 层

返回 OpsWorks,访问相册堆栈。打开导航菜单并选择层。单击 RDS photoalbums 图层查看该图层的详细信息。在我们将备份数据库添加到堆栈之前,我们将取消注册这一层。在右上角,单击“取消注册”按钮。系统将提示您确认是否要取消注册该层。单击红色的取消注册按钮后,您将被带到 Resources 视图,此时该视图为空。

在 Resources 标题下,有一个按类型筛选资源的子菜单:卷、弹性 IP 和 RDS。单击 RDS,您将在您的资源中看到一个空的 RDS 实例表。在右上角,单击注册 RDS 数据库实例。这似乎与我们之前创建图层的方式相反,但我们只是在 OpsWorks 的指导下通过不同的工作流程完成相同的流程。之前,我们创建了一个 RDS 层,然后添加了一个 RDS 资源。这一次,我们将添加一个 RDS 资源,然后自动生成一个层。

您将在一个表中看到您的两个 RDS 实例。选择 photoalbums-failover,在表格下方的连接细节面板中输入您的数据库凭证,如图 3-21 所示。这些应该与原始数据库的凭据相同,因为该实例是从该实例的快照创建的。然后,单击向堆栈注册。

A978-1-4842-0653-9_3_Fig21_HTML.jpg

图 3-21。

Adding an RDS resource to the stack

将新数据库层连接到应用

再次打开导航菜单,并选择层。您应该会看到一个新的 RDS 层已添加到您的堆栈中,这一次名为 RDS: photoalbums-failover。您会再次注意到,您有一个未连接到任何应用的 RDS 图层。单击连接到应用,这将再次将您带到应用表。在相册旁边的行中,单击编辑。

同样,您必须为您的应用设置数据源。将数据源类型从 None 更改为 RDS。当您这样做时,您应该会看到 photoalbums-failover(mysql)出现在一个下拉列表中,并且有一个数据库名称的空字段。在数据库名称字段中键入 photoalbums,然后单击右下角的保存。

运行堆栈命令

我们从数据库的快照中创建了一个新的 RDS 实例,一个新的 OpsWorks 层,并将其连接到我们的应用。您还记得,在部署的配置阶段,RDS 层的凭证被复制到我们的 EC2 实例中。为了更新我们的凭证,我们必须在实例上再次运行Configure命令,然后部署以将新凭证推送到实例。

打开导航菜单,并选择堆栈。您将返回到堆栈的详细视图。在右上角,您将看到一个标题为“运行命令”的按钮,您可以单击它。在“运行命令”视图中,您可以从命令列表中进行选择,向命令添加注释,并选择在哪些实例上运行命令。从命令下拉菜单中选择配置,如图 3-22 所示。在“注释”字段中,键入给自己的消息,如“已配置的 RDS 故障转移实例凭据”在视图的右下角,单击配置以运行命令。

A978-1-4842-0653-9_3_Fig22_HTML.jpg

图 3-22。

Running the Configure command

您将看到一个标题为运行命令 configure 的视图,在该视图中,当命令正在执行时,您可以在 EC2 实例旁边看到一个活动指示器。一旦命令完成,应用源代码根目录中的opsworks.js文件将会使用新的凭证进行更新。接下来,运行deploy命令将代码更改部署到实例中,并重启应用。

你可以想象这个工作流程有多有用。我们能够在几分钟内将应用移动到备份数据库,而无需修改任何代码。当然,这样做也意味着您的应用要在您要换出的数据库上运行几分钟,因此,如果您的数据库包含时间敏感的信息,这在理想情况下不是您需要使用的过程。

看待该特性的另一种方式是,它为您提供了两种执行数据库维护的方式。如果您必须增加数据库存储、转换存储类型或进行一些其他重大配置更改,您的应用可能会出现一些中断。一方面,您可以对现有数据库进行更改(总是先拍摄快照),并让 AWS 使用多 AZ 特性在维护过程中自动使用故障转移数据库。

或者,您可以创建一个快照,并使用您想要的配置更改从快照创建一个新的数据库。然后,您必须将数据库实例添加到 OpsWorks 堆栈中,然后运行Configuredeploy命令。

既然本教程已经完成,让我们回到原来的数据库。在 OpsWorks 中,导航到图层并单击 RDS 图层的标题。在右上角,单击“取消注册”,并确认从堆栈中分离数据库。再次从层屏幕,点击+层。选择 RDS 选项卡,并从下面的 RDS 实例列表中选择相册。重新输入数据库凭据,然后单击向堆栈注册。当您返回“层”屏幕时,再次点按“连接应用”,然后点按应用服务器旁边的“编辑”。向下滚动到数据源,并将数据源类型更改为 RDS。选择原始数据库实例和数据库名称。单击保存。

从导航菜单中选择堆栈,然后在右上角单击运行命令。将命令更改为 Configure,然后单击右下角的按钮。最后,导航到应用,然后单击相册旁边的部署。然后单击右下角的 Deploy,让部署运行。现在,我们连接回了原始数据库,如果需要,您可以删除故障转移。

弹性负载平衡(ELB)

此时,我们的应用运行在一个 EC2 实例上。我们知道,虽然我们的实例可以扩展到更大的规模,但这可能不足以处理应用的传入流量。此外,更改 EC2 实例的大小会导致服务中断,这是我们希望避免的。

如果您曾经广泛使用 Node.js,您会知道许多服务器端错误会使您的应用崩溃,这需要重新启动应用。虽然 OpsWorks 会自动重启你的应用,但一个错误就让你的整个应用离线,哪怕只有几秒钟,都是一种耻辱。

这两个问题的解决方案是在多个实例上运行我们的应用。用 AWS 术语来说,这可以通过负载平衡器或弹性负载平衡(ELB)来实现。借助 ELB,流量可以在多个 EC2 实例之间和多个可用性区域之间自动重定向。如果一个实例崩溃,或者处于所谓的不健康状态,ELB 将停止向该实例发送流量,直到它恢复健康。这种自动化的便利性不能被夸大。如果一个实例变得不健康,您不必担心手动将其从堆栈中删除,以免流量被路由到处于错误状态的服务器。

在图 3-23 中,您可以看到负载平衡器将如何在我们的应用堆栈中工作。流量将被路由到 ELB,然后它将流量定向到各个实例,并将响应转发回请求者。

A978-1-4842-0653-9_3_Fig23_HTML.gif

图 3-23。

An ELB routing traffic to three EC2 instances Note

ELB 还可以用来调节应用两层之间的内部流量,而不仅仅是 Web 和应用服务器之间的流量。

此外,使用 ELB 为我们提供了另一种评估应用健康状况的有用方法。我将在稍后讨论监控,但目前,我们可以对云中的服务器健康问题进行概念性分析。如果您的应用堆栈中有 10 台服务器,那么随时监控所有实例的健康状况将是一项相当艰巨的任务。如果一个实例离线,是否会立即引起重大关注?如果你在一百台服务器上运行这个应用会怎么样?如您所见,随着移动部件的增多,需要分析的信息也越来越多。我们可以监视 ELB 的各种指标,而不是单独监视每个实例。

创建负载平衡器

ELB 在 AWS 中没有自己的控制面板。相反,它们在 EC2 的一个子部分中进行管理。返回 AWS 控制台并导航到 EC2。在左侧导航中的网络和安全标题下,单击负载平衡器。然后,在屏幕顶部,单击创建负载平衡器。创建负载平衡器向导将在一个模态窗口中打开,如图 3-24 所示。

A978-1-4842-0653-9_3_Fig24_HTML.jpg

图 3-24。

Create Load Balancer wizard

定义负载平衡器

在向导的第一个屏幕上,您可以将负载平衡器命名为 photoalbums-elb。“创建 LB 内部”下拉列表可以保留为 EC2-Classic。只有在虚拟专用云中创建 ELB 时,才需要更改此字段。接下来,您必须打开端口,负载平衡器可以通过这些端口处理流量。默认情况下,端口 80 在选择 HTTP 协议的情况下打开。如果您还记得,这也是您的应用正在侦听的端口。您可以离开并继续,然后单击“继续”继续。

配置运行状况检查

之前,您第一次听到健康和不健康这两个术语来描述实例。这些都不是经验性的术语,事实上,要由我们来定义。在下一步中,我们将确定 EC2 实例在什么条件下可以被认为是健康的,什么时候可以被认为是不健康的。ELB 将对分配给它的每个实例执行常规运行状况检查。

我们将为这些健康检查定义参数,这将包括 ELB 将从每个实例请求的相对路径。您可以在图 3-25 中看到健康检查参数。

A978-1-4842-0653-9_3_Fig25_HTML.jpg

图 3-25。

Configuring an ELB health check

首先,我们配置 URL 请求,或 ping。Ping 协议应该是 HTTP,Ping 端口应该是 80,和我们的 app 监听的一样。最后,Ping 路径是在每个实例上发送请求的路径。将此更改为/,这将请求我们应用的 Hello World 页面。/index.html的默认值将从我们的应用返回一个 404 错误。

高级细节是运行状况检查的更主观的参数。首先,我们有响应超时。每次对 EC2 实例执行 pinged 操作时,负载平衡器都会测量从实例接收响应所需的时间。如果实例响应缓慢或完全没有响应,它将无法通过一次运行状况检查。默认响应超时是五秒。对于我们的 Hello World 页面,预计它的生成时间不到五秒钟似乎是合理的。事实上,我们可能会进一步减少,但 5 是安全的。

接下来,我们必须确定运行状况检查的执行频率,或者运行状况检查的时间间隔。默认值为 30 秒,这意味着当实例在线时,运行状况检查将每 30 秒执行一次。

一个健康的实例可能偶尔无法通过一次健康检查。由于多种因素,它可能会响应缓慢。我们没有必要因为一个缓慢的响应就从池中删除一个实例。相反,我们希望设置一个不健康的阈值,在这个点上,实例实际上被认为处于不健康状态。默认情况下,该阈值为 2,健康阈值为 10。这些阈值如此不同的原因是为了谨慎起见。一旦一个实例变得不健康,我们不想过早地认为它是健康的。如果负载平衡器过早地认为实例是健康的,就会有更多用户收到错误响应或根本没有响应的风险。

最后,我们的健康检查规则是这样的:

  • 确定实例不正常最多需要 70 秒。
    • 五秒响应超时,每 30 秒两次
  • 确定实例再次正常运行需要 350 秒。
    • 五秒响应超时,每 30 秒,十次

当然,这些只是默认值。如果您想更积极地响应某个实例的缓慢性能,您可以改为每十秒运行一次健康检查。您还可以在五次而不是十次成功检查后,决定将实例安全地放回池中。决定您的策略是一门独立的科学,由您的应用的性质、您的应用堆栈的架构以及您的应用的操作历史决定,这意味着随着时间的推移评估您的堆栈的性能可以帮助您做出更好的决策。

单击继续进入下一个屏幕,在这里您将向负载平衡器添加实例。

添加 EC2 实例

现在,我们只添加我们之前创建的单个 EC2 实例。选择实例,并确保启用了“启用跨区域负载平衡”。如果启用,您的负载平衡器可以将流量路由到多个可用性区域中的实例。我们已经讨论了跨可用性区域分配资源的优势,因此我们肯定希望跨不同可用性区域中的 EC2 实例平衡流量。单击继续并继续添加标签步骤。

添加标签

到目前为止,您可能已经意识到,您可以在 AWS 中非常快速地积累大量资源,其中一些是由 AWS 自动命名的,另一些则遵循您自己的命名约定。很难跟踪所有的移动部件。AWS 帮助缓解这个问题的一个方法是通过使用标签。标签只是可以在 AWS 中为许多资源创建的键值对。

Note

可标记资源的完整列表可在 http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-restrictions 获得。

每个资源,无论是 EC2 还是 RDS 实例,等等。,最多可以有十个标签。这可以帮助您组织您的资源,并使您的账单更易于组织。例如,如果您在 AWS 帐户上为多个客户端托管基础设施,那么您可以按照客户端名称或客户端项目来标记您的资产,从而为每个客户端生成计费摘要。

也许您正在运行一个生产和开发堆栈,并且您想要确定为长期开发周期生成额外开发环境的成本。您可以用“环境”=“生产”来标记您的生产资源,用“环境”=“开发”来标记您的开发资源,以预测额外开发环境的成本。

这只是几个例子。虽然 AWS 计费管理超出了本书的范围,但是最好解释一下标签是什么,而不是完全忽略它们。为了熟悉它们,让我们给我们的 ELB 添加两个标签。在第一个 tag 字段中,键入 Environment 作为键,Production 作为值,如图 3-26 所示。然后单击 Create Tag 并添加值为 Photoalbums 的密钥堆栈。单击继续进入查看步骤。

A978-1-4842-0653-9_3_Fig26_HTML.jpg

图 3-26。

Add ELB tags

回顾

Amazon 方便地允许我们在创建负载平衡器之前回顾所有的选择(见图 3-27 )。花一点时间检查所有的东西,确保它看起来是正确的。如果你犯了一个错误,有一个编辑按钮,可以带你回到每个单独的步骤来解决问题。完成审阅后,单击创建。几秒钟后,您将看到一条确认消息,表明您的负载平衡器已创建,当您关闭它时,您将在一个表中看到您的负载平衡器。

A978-1-4842-0653-9_3_Fig27_HTML.jpg

图 3-27。

Load balancer details

当您选择负载平衡器时,列表下方会出现一个选项卡式详细视图,如图 3-27 所示。“描述”选项卡提供了负载平衡器的概述。在前面和中间,您会看到一个 Status 字段,它会快速告诉您有多少实例在使用中。它应该显示为“服务中的 1 个实例中的 1 个”如果您的实例变得不健康,这将从 1 变为 0,要点是服务中是健康的委婉说法。您还可以重新访问您对负载平衡器所做的任何定制。您可以添加/编辑标签,打开/关闭端口,以及更改运行状况检查的配置。

您还可以在“实例”选项卡中管理连接到负载平衡器的实例。但是,我们不会在这里这样做。如果我们要在 OpsWorks 堆栈中使用负载平衡器,我们不希望在 EC2 仪表板中手动添加/删除负载平衡器的实例。相反,我们希望像处理数据库一样处理负载平衡:在 OpsWorks 中添加一个 ELB 层。

OpsWorks ELB 层

返回 OpsWorks 并打开相册堆栈。使用导航菜单,转到 Layers 视图并选择 Node.js 应用服务器层。在层子导航中,选择网络。在顶部,您应该会看到一个弹性负载平衡标题,在它下面,您会看到没有添加 ELB。单击添加 ELB 将 ELB 附加到应用服务器图层。

页面将刷新,弹性负载平衡器字段现在将显示一个下拉列表,您可以从中选择 photoalbums-elb。当您这样做时,将出现一个警告,通知您 OpsWorks 配置现在将取代在 EC2 仪表板中所做的更改。这意味着如果您返回 EC2 并添加更多实例,这些更改将被忽略,因为 OpsWorks 命令将优先。单击保存提交您的更改,这将再次重新加载页面。

打开导航菜单并返回到层视图。你会看到一个 ELB 层已经自动生成,现在如图 3-28 所示。

A978-1-4842-0653-9_3_Fig28_HTML.jpg

图 3-28。

ELB, App Server, and RDS layers

点击 ELB 层(ELB:相册-elb),你会看到该层的详细视图(见图 3-29 )。首先,您将在 DNS 名称旁边看到一个公共 URL。如果你点击这个,你将被带到我们应用的 Hello World 页面!您还会看到一条警告,提示您的所有实例都在一个可用性区域中。如前所述,最佳实践是在其他可用性区域中复制资源。在这种情况下,这是一个有争议的问题,因为我们现在在应用服务器层只有一个实例。

A978-1-4842-0653-9_3_Fig29_HTML.jpg

图 3-29。

ELB layer view in OpsWorks

在页面的底部,您将看到一个表,其中列出了向负载平衡器注册的所有实例,这些实例按可用性区域组织,每个实例的右侧都有状态指示器。因为我们在一个可用性区域中只有一个实例(在本例中是 us-east-1a),所以这里没有太多信息。但是,一旦我们添加了更多的实例和可用性区域,这一部分将为您提供应用服务器状态的鸟瞰图,以及评估性能问题的起点。

Note

在过去的几分钟里,我们已经看到 EC2 实例被描述为健康的、服务中的和正在服务中的。这些都是同一个意思的变体。

到目前为止,我们已经通过应用服务器层中 EC2 实例的静态 IP 地址访问了我们的应用。这只是我们应用发展过程中的一个临时步骤,因为我们永远不希望用户在使用我们的应用时能够控制他们连接到哪个实例。接下来,我们将向堆栈中添加一个实例,并通过负载平衡器与两个实例进行交互。

添加新实例

打开导航菜单,单击实例返回实例视图。单击左下角的+Instance 按钮打开实例创建对话框。您可以创建一个新实例或将现有实例添加到该层。后一种情况不允许您在 AWS 帐户中添加任何实例。相反,您只能添加已经属于 OpsWorks 层的现有实例。如果您愿意,您可以在多个层之间共享计算资源,并在一个共享实例上托管两个或更多层。因为我们的堆栈只有一个应用服务器层,所以我们不会探索这个选项,但知道它在那里是很好的。

将新实例大小更改为 t1.micro(或任何较小的大小),并选择不同于您为第一个实例选择的可用性区域(参见图 3-30 )。然后单击添加实例。

A978-1-4842-0653-9_3_Fig30_HTML.jpg

图 3-30。

Adding a new instance to a layer

现在你会在实例表中看到你的新实例,如图 3-31 所示。它不会自动启动,也不会分配 IP 地址,因为公共 IP 仅在实例联机时保留。

A978-1-4842-0653-9_3_Fig31_HTML.jpg

图 3-31。

App Server Instances overview

既然应用中有多个实例,这个视图开始变得更加有用。您可以看到左上角的圆圈实际上是一个圆形图,描述了处于五种可能状态之一的实例的百分比。您还可以快速链接到活动实例的公共 IP 以及 ELB。您可以启动或删除停止的实例,并通过 SSH 停止或连接到在线实例。

如果您回想一下关于 RDS 的课程,您可能会认为在将源代码部署到新实例之前必须运行Configure命令。事实并非如此。因为在连接 RDS 实例之后,您已经在堆栈上运行了Configure命令,所以将部署到新实例的凭证是最新的。你所要做的就是点击开始。

Note

有时,AWS 不允许在特定的可用性区域中分配新的实例。如果 us-east-1b 已满,并且您遇到此错误,只需选择 us-east-1c 或 us-east-1d 即可。

您的实例将在该视图中自动运行多个状态,从 requested 开始,到 online 结束。单击相册-elb 导航回负载平衡器图层。关于您的实例位于单个可用性区域的警告应该已经消失了,现在您应该在底部看到第二列,列出了您在 us-east-1b 中的实例。单击 DNS 名称旁边的 URL,您将看到 Hello World 页面在一个新窗口中打开。您现在直接连接到您的负载平衡器,它将请求路由到您在单独的可用性区域中的两个实例!

既然我们已经走了这么远,让我们继续前进,踢踢轮胎。您应该能够通过向您的负载平衡器 URL 上的注册路径提交一个POST请求来注册一个新用户。打开您的 REST 客户端,粘贴 ELB DNS 名称,添加/users/register。添加用户名、密码和电子邮件POST参数并发送请求。您应该会收到以下响应:

{"message":"Registration successful!"}

您的用户似乎已经成功注册,并且在 RDS 实例上创建了记录。如果你还记得第一章中关于示例应用的一些细节,你会记得在应用中有一个列出注册用户的 API。在您的 REST 客户端中,更改为一个GET请求,并从 URL 中删除register,或者在您的浏览器中打开该 URL。您应该会收到注册用户的 JSON 列表,如下所示:

[{"username":"admin","userID":1}]

摘要

在这一章里,我已经讲了很多内容。您了解了 Amazon RDS 的一些重要特性,创建了一个实例,并从该实例创建了一个 OpsWorks 层。然后,我们将 OpsWorks 配置为在我们的应用服务器实例上打包 RDS 凭证,并修改了访问这些凭证的源代码,而不是使用存储在我们的存储库中的硬编码凭证。我们还运行了几个场景,在这些场景中,我们可以使用 RDS 和 OpsWorks 的功能来妥善处理数据库故障或快速部署备份数据库。

您还了解了弹性负载平衡,这是亚马逊的负载平衡服务。我们创建了第一个负载平衡器,并配置了健康检查来识别不健康的 EC2 实例,并调节健康实例的流量。我们使用这个负载平衡器在 OpsWorks 中生成一个 ELB 层,并在 ELB 的多个可用性区域中添加实例。最后,我们通过在 ELB 的公共 URL 注册一个用户来测试我们的进展。

在某些方面,这是最困难的一章,因为我们终于创建了一个真正基于云的应用,不依赖于单一资源。当然,虽然我们在两个实例上运行我们的应用,但这并不意味着它有很强的容错能力,也不意味着它可以供公众使用。在接下来的几章中,您将了解如何赋予这种基础架构弹性,通过扩展我们的资源来满足需求,从而响应繁忙的流量。您还将了解更多关于我们的应用堆栈中的各种指标和故障点的信息,并了解如何使用其他 AWS 服务来提供缓存和加速内容分发,以减少我们的应用服务器和 RDS 层的工作负载。