第 6 章:测试云原生基础架构

基础架构是用来支撑应用程序的。可信任的软件对于工程成功至关重要。如果每次在终端输入 ls 命令,都会发生随机动作,那么你将永远也不会相信 ls,而是去找另一种方式来列出目录中的文件。

我们的基础架构必须值得信任。本章旨在建立信任和验证基础架构的意识形态。我们将描述的实践旨在增加对应用程序和基础架构工程的信心。

软件测试在当今的软件工程领域非常普遍。然而,如何测试基础架构还没有很明确的最佳实践。

这意味着本书中的所有章节中,这一节应该是最令人兴奋的!在该领域像您这样的工程师有充分的空间发挥出色的影响力。

软件测试是一种证明软件可以正常工作的有效做法,证明软件在各种特殊情况下软件仍然可以正常运行。因此,如果我们将相同的范例应用于基础架构测试,测试目标如下:

  1. 证明基础架构按预期运行。
  2. 证明基础架构不会失败。
  3. 证明这两种情况在各种边缘情况下都是正确的。

衡量基础架构是否有效需要我们先定义什么叫有效。现在,您应该对使用基础架构和工程代表应用程序的想法感到满意。

定义基础架构 API 的人应该花时间构思一个可以创建有效基础架构的理智的 API。例如,如果创建了一个定义虚拟机的 API 但里面没有使之可以运行的网络信息,这种做法就很愚蠢。您应该在 API 中创建有用的抽象,然后使用第 3 章和第 4 章中提出的想法来确保 API 创建出正确的基础架构组件。

我们开始开发一个心智模型,通过定义 API 对基础架构的健全性进行检查。这意味着我们可以翻转逻辑并想象出相反的情况,这将是除了原始心智模型之外的所有东西。

很值得为基础架构定义基本的完整性测试。测试基础架构的第一步是证明您的基础架构是按预期存在的,并且没有任何东西的违背原意而存在。

在本章中,我们将探索基础架构测试,这将为新的测试工具奠定基础。

我们该测试什么?

在开始编写代码之前,我们首先必须确定要测试的内容。

使用测试驱动开发是测试优先的常见做法。测试是为了证明测试点而编写的,从一开始就隐含失败。软件的开发周期都需要通过测试;也就是说,软件是为了满足测试中定义的每个要求而开发的。这是一个强大的实践,可以帮助软件保持专注,帮助工程师建立对软件的信心。

这是一个可以用多种方式回答的哲学问题。建立值得信赖的基础架构是必不可少的。例如,如果存在依赖基础架构的业务问题,则应该对其进行测试。更重要的是,许多业务不仅依赖基础架构,还会在出现问题时自行修复。

确定基础架构需要填补的问题空间代表了将需要编写的第一轮测试。

远景规划是基础架构测试的另一个重要方面,但应该谨慎。在足够的前瞻性和过度工程化之间有一条看不见的界限。如果有疑问,坚持最少量的测试逻辑。

在我们完全了解需要的测试之后,就可以考虑实施测试套件。

编写可测试代码

调节器模式不仅旨在创建整洁的基础架构应用程序,而且还鼓励编写可测试的基础架构代码。

这意味着,在应用程序的每一个重要步骤中,我们总是会重新创建一个相同类型的新对象,也就是说基础系统的每个主要组件都会使用相同的输入和输出。使用相同的输入和输出可以更轻松地以编程方式测试软件的小型组件。测试将确保您的组件按预期工作。

然而,在编写基础架构测试代码时,还有许多经验值得借鉴。我们将在看看在一些虚拟情景测试基础架构的具体示例。通过浏览场景,您将学到测试基础架构代码的经验教训。

我们还会给出工程师一些规则,应在编写可测试基础架构代码时遵守这些规则。

验证

采用非常基础的基础架构定义,如示例 6-1。

例 6-1. infrastructure.json

{
    "virtualMachines": [{
        "name": "my-vm",
        "size": "large",
        "localIp": "192.168.1.111",
        "subnet": "my-subnet"
    }],
    "subnets": [{
        "name": "my-subnet",
        "cidr": "10.0.100.0/24"
    }]
}

这些数据的目的显而易见:确保一个名为my-vm的虚拟机的大小为large,IP 地址为192.168.1.111。这些数据也暗示确保一个名为my-subnet的子网将容纳虚拟机my-vm

希望您发现了这个数据的问题。虚拟机的 IP 地址超出了子网的可用 CIDR 范围。

应用程序在运行此数据时应该会失败,因为虚拟机设置了无效的网络。如果将我们的应用程序构建为盲目地允许部署任何数据,那么我们将创建一个可以联网的基础架构。尽管我们应该编写测试以确保新的虚拟机能够在网络上路由,但是我们可以做一些其他事情来帮助加强我们的应用程序并使测试更加容易。

在应用程序处理输入之前,我们可以首先尝试验证输入。这在软件工程中是很常见的做法。

想象一下,如果不是盲目部署这个基础架构,我们首先会尝试验证输入。在运行时,应用程序可以很容易的检测到虚拟机的 IP 地址在虚拟机所连接的子网中无效。这将阻止输入到达基础架构环境。由于知道应用程序将故意拒绝无效的基础架构表示,我们可以编写 happy 和 sad 测试来确保实现此行为。

Happy 测试可以对条件进行正面处理。换句话说,它是一种向应用程序发送有效 API 对象并确保应用程序接受有效输入的测试。Sad 测试,可以对相反的情况或负面情况进行分析。例 6-1 是一个 sad 测试的例子,它将一个无效的 API 对象发送给应用程序,并确保应用程序拒绝无效输入。

这种新模式使测试基础架构非常快速,而且通常不用费什么力气。一个工程师就可以开发大量的 happy 和 sad 测试,即使是最奇怪的应用程序输入也是如此。此外,测试集合可以随着时间的推移而增长;在欺骗 API 对象流入环境场景中时,工程师可以快速添加测试以防止其再次发生。

输入验证是测试最基本的事情之一。通过在我们的应用程序中编写简单的验证来检查理智的值,我们可以开始过滤应用程序的输入。这也给了我们一个很容易定义错误并快速返回错误的途径。

验证提供信心,而不会让您等待基础架构发生变异。这为面向 API 开发的工程师创建了更快的反馈循环。

输入代码库

编写易于测试的代码非常重要。经常出错可能会导致成本上升,因此需要围绕专有输入设计应用程序。专有输入是仅与程序中的一个点相关的输入,获得所需输入的唯一方法是线性执行程序。以这种方式线性编写代码对于人类大脑来说是有意义的,但这也是有效测试的最难的模式之一,特别是当涉及到测试基础架构时。

专有输入陷入困境的例子如下:

  1. 函数 DoSomething () 的调用返回 Something {}
  2. Something {} 传递给函数 NextStep (Something) 并返回 SomethingElse {}
  3. SomethingElse {} 被传递给函数 FinalStep (something else),返回 true 或 false。

这里的问题是,为了测试 FinalStep () 函数,我们首先需要遍历步骤 1 和 2。在测试的情况下,这会引入复杂性和更多的失败点;甚至可能不会在测试执行的环境中工作。

更优雅的解决方案是以这样一种方式构造代码,即可以在程序的其余部分使用相同的数据结构上调用最后的 step ():

  1. 代码初始化 GreatSomething {} 实现了方法 great.DoSomething ()
  2. GreatSomething {} 实现 NextStep () 方法。
  3. GreatSomething {} 实现了 something.FinalStep () 方法。

从测试的角度来看,我们可以为我们希望测试的任何步骤填充 GreatSomething {},并相应地调用这些方法。这个例子中的方法现在负责处理它们扩展的对象中定义的内存。这与最后一种方法不同,在这种方法中,特殊的内存中的结构被传递到每个函数中。

这是一种更为优雅的设计,因为测试工程师可以轻松地在整个过程中的任何一步合成内存,而不必担心学习相同数据的一种表示形式。 这是更加模块化的,如果可以迅速发现任何故障,我们可以介入。

当您开始编写构成应用程序的软件时,请记住,您将需要在传统的运行时间轴上的许多地方跳入代码库。构建代码以使其易于在任何时候输入代码库并在内部进行测试,这是至关重要的。 在这种情况下,您可以成为自己最好的朋友,也可以成为最大的敌人。

自我意识

告诉我你如何衡量我,我会告诉你我的行为。

——Eliyahu M. Goldratt

在编写代码和测试时注意自己的置信度。自我意识是软件工程中最重要的部分之一,也是最容易被忽视的部分之一。

测试的最终目标是增加对应用程序的信心。就基础架构领域来说,我们的目标就是增强对基础架构的信心。

测试基础架构的方法没有对错之分。可以在应用程序中通过代码覆盖率和单元测试来建立信心,但是对于基础架构来说这样做可能会存在误导。

代码覆盖率是以程序化方式衡量代码可以满足预期的行为。这个度量标准可以用作原始数据点,但我们要知道即使是覆盖率达到 100%的代码库仍然可能会出现极端中断,这一点至关重要。

如果你以代码覆盖率来衡量测试结果,那么工程师就会编写更容易被测试覆盖的代码,而不是编写更适应该任务的代码。Dan Ariely 在他刊登于哈弗商业评论的文章 “衡量标准决定一切” :

人们的行为会根据衡量指标作出相应的调整。衡量指标会促使某个人在该指标上做出优化。你想要得到什么,就要去衡量什么。

应该衡量的唯一指标是信心,即我们的基础架构可以按预期工作,并且可以证明这一点。

衡量信心几乎是不可能的。但是有些方法可以从工程师的心理和情绪中抽取有意义的数据集。

问自己以下几个问题,记录下答案:

  • 我担心这行不通吗?
  • 我可以肯定,这将做我认为会做的事吗?
  • 如果有人更改此文件,会发生什么情况?

从问题中提取数据的最强好的方式是比较以前的经验水平。例如,工程师可以做出如下陈述,团队的其他成员很快就会明白他想要传达的内容:

比起上个季度,这次代码发布更令人担忧。

现在,根据团队以前的经验,我们可以开始为我们的信心水平制定一套标准,从 0 开始表示完全没有信心,随着时间的流逝然后增加到非常自信。当我们了解了我们担心应用程序的哪些问题之后,再为了增加信心而制定测试内容就很简单了。

测试类型

了解测试的类型以及测试方式将有助于工程师增加其对基础架构应用程序的信心。这些测试不需要编写,而且没有正确或错误之分。唯一的问题是我们相信应用程序会做我们想要它做的事情。

基础架构断言

在软件工程中,有一个重要的概念是断言,这是一种强制的方式 —— 完全确定条件是否成立。目前已经有许多成功的框架使用断言来测试软件。断言是一个微小的函数,它将测试条件是否为真。这些功能可以在各种测试场景中使用,以证明概念正在发挥作用和增加我们的信心。

在本章的其余部分中,我们将提到基础架构断言。您需要对这些断言的内容以及他们希望完成的内容有基本的了解。您还需要对 Go 语言有基本的了解,才能充分认识这些断言是做什么的。

在基础架构领域需要声明我们的基础架构有效。对于您的项目来说值得练习下使用构建这些断言功能的库。开源社区也可以从这个工具包测试基础架构中受益。

例 6-2 显示了 Go 语言中的断言模式。假设我们想测试虚拟机是否可以解析公共主机名,然后路由到它们。

例 6-2. assertNetwork.go

type VirtualMachine struct {localIp string}
func (v *VirtualMachine) AssertResolvesHostname (hostname string, expectedIp string, message string) error { 
    // Logic to query DNS for a hostname,
    //and compare to the expectedIp
    return nil
}
func (v *VirtualMachine) AssertRouteable (hostname string,r port int, message string) error {
    // Logic to verify the virtualMachine can route
    //to a hostname on a specific port
    return nil
}
func (v *VirtualMachine) Connect () error {
    // Logic to connect to the virtual machine to run the assertions
    eturn nil
}
func (v *VirtualMachine) Close () error {
    // Logic to close the connection to the virtual machine 
    return nil
}

在这个例子中,我们将两个断言作为VirtualMachine {} 结构体上的方法来存储。方法签名是我们将在此演示中关注的内容。

第一种方法 AssertResolvesHostname () 演示了一种将用于检查给定主机名是否解析为预期 IP 地址的方法。第二种方法 AssertRouteable () 演示了一种用于检查给定主机名是否可在特定端口上路由的方法。

注意 VirtualMachine {} 结构体是如何定义成员本地 IP 的。另请注意,VirtualMachine {} 结构体具有 Connect () 函数以及 Close () 函数。这是因为断言框架可以在虚拟机的上下文中运行这个断言。测试可以在基础架构环境之外的系统上运行,然后连接到环境中的虚拟机以运行基础架构断言。

在例 6-3 中,我们演示了工程师该如何在本地系统上编写 Go 测试。

例 6-3. network_test.go

func TestVm (t *testing.T) {vm := VirtualMachine {localIp: "10.0.0.17", ""}
	if err := vm.Connect (); err != nil {t.Fatalf ("Unable to connect to VM: % v", err)
	}
	defer vm.Close ()
	if err := vm.AssertResolvesHostname ("google.com", "*",
		"google.com should resolve to any IP"); err != nil {t.Fatalf ("Unable to resolve hostname: % v", err)
	}
	if err := vm.AssertRouteable ("google.com", 443,
		"google.com should be routable on port 443"); err != nil {t.Fatalf ("Unable to route to hostname: % v", err)
	}
}

该示例使用 Go 语言中的内置测试标准,这意味着该函数将作为应用程序中 Go 测试的正常运行测试的一部分执行。测试框架将测试名称以_test.go结尾的所有文件,并使用以TestXxx开头的签名名称测试所有函数。该框架还将* test.T 指针传递给以这种方式定义的每个函数。

这个简单的测试将使用我们之前定义的断言库来完成以下几个步骤:

  1. 尝试连接到应在 10.0.0.17 上可访问的虚拟机。
  2. 在虚拟机上尝试断言虚拟机可以解析 google.com 并且可返回一些 IP 地址。
  3. 在虚拟机上尝试声明虚拟机可以通过端口 443 路由到 google.com。
  4. 关闭与虚拟机的连接。

这是一个非常强大的程序。它为我们的基础架构按预期工作建立了信心。它还引入了一个优雅的脚手架,供工程师定义测试,而不必担心它们将如何运行。

开源社区迫切需要这样的基础架构测试框架。基础架构测试的标准化和可靠方法将成为开发者工具箱中的有益补充。

集成(integration)测试

集成测试也被称为端到端(e2e)测试。这些是长期运行的测试。按照预生产的方式来运行系统,这是证明可靠性和增加信心的最有价值的测试。

编写集成测试套件可能很有趣,也很有意义。在集成测试基础架构管理应用程序的情况下,测试将执行基础架构生命周期的大扫除。

线性集成测试套件的一个简单例子如下:

  1. 定义一个常用的基础架构 API。
  2. 将数据保存到应用程序的数据存储区。
  3. 运行该应用程序并创建基础架构。
  4. 针对基础架构运行一系列断言。
  5. 从应用程序的数据存储中删除 API 数据。
  6. 确保基础架构已成功销毁。

在此过程中的任何一步,测试都可能失败,并且测试套件应该清理发生变异的基础架构。这是测试可以按照预期销毁基础架构重要的原因之一。

测试使我们相信,该应用程序将创建并销毁预期的基础架构,并按预期工作。随着时间的推移,我们可以增加步骤 4 中运行的断言的数量,并继续强化套件。

集成测试工具可能是测试基础架构最强大的环境。没有集成测试工具,运行像单元测试这样的小测试就没有多大价值。

单元(unit)测试

单元测试是测试系统并单独运行其组件的基本部分。单元测试的责任是小而谨慎。单元测试是软件工程中的常见做法,因此将成为基础架构工程的一部分。

在编写基础架构测试的情况下,测试系统的一个组件是困难的。基础架构的大多数组件都建立在彼此的基础之上。相应地测试软件通常需要改变基础架构来测试并查看其是否工作。这个过程通常涉及大部分系统。

但这并不意味着为基础架构管理系统编写单元测试是不可能的。事实上,前面例子中定义的大部分断言在技术上将都是单元测试!单元测试只测试一个小组件,但在大型集成测试系统环境中使用时,它们可能非常有用。

在测试基础架构时鼓励进行单元测试,但请记住,它们运行的上下文通常需要相当大的开销。这种开销通常以集成测试的形式出现。将单元测试的小而谨慎的检查与更大的整体测试模式相结合,使基础架构工程师对其基础架构按照预期工作具有高度的信心。

模拟(Mock)测试

在软件工程中,综合系统的常见做法是模拟测试。在模拟测试中,工程师编写或使用旨在欺骗或伪造系统的软件。

一个简单的例子就是使用一个旨在与 API 通信并以 “mock” 模式运行的 SDK。SDK 不会将任何数据发送到 API,而是合成 SDK 认为 API 在各种情况下应该执行的操作。

确保模拟软件准确地反映它正在合成的系统的责任在于开发模拟软件的工程师手中。在某些情况下,模拟软件也是由开发其正在模拟的系统的工程师开发的。

尽管可能有一些模拟工具保持最新并且比其他工具更稳定,但使用模拟系统合成您计划测试的基础架构时存在一个普遍的道理:虚假系统只会给您带来虚假信心。

现在,这条规则可能看起来很苛刻。但它的目的是鼓励工程师不要轻易走出去,并通过构建真正的集成套件来运行测试的实践。虽然模拟系统功能强大,但将它作为基础架构测试的核心(因此也是您的信心)是非常危险的。

大多数公有云提供商对其资源实施配额限制。想象一下与一个对资源有严格限制的系统进行交互的测试。模拟系统可能会尽最大努力限制资源,但是如果不在运行时审核实际系统,模拟系统将无法确定您的基础架构是否实际部署。在这种情况下,您的模拟测试会成功。但是,当代码在真实环境中运行时,它会中断。

这只是许多实例中的一个例子,这些实例证明了为什么变异实际基础架构和发送实际网络数据包比使用模拟系统更可靠。请记住,测试的目标是增强您的基础架构在真实环境中按照预期工作的信心。

这并不是说所有的模拟测试都不好。了解模拟正在测试的基础架构与为了方便而模拟另一部分系统之间的差异非常重要。

工程师需要决定什么时候适合使用模拟系统。我们只是告诫工程师不要对这些系统有太大的信心。

混沌(chaos)测试

混沌测试可能是本书中介绍的所有测试基础架构的方法中中最令人兴奋。混沌测试证明在基础架构中发生不可预知的事件时不会影响基础架构的稳定性。我们通过故意破坏基础架构并衡量系统如何应对灾难来做此演示。与其他的所有测试一样,我们将以基础架构工程师的身份来应对这个问题。

我们将编写旨在以意想不到的方式打破生产系统的软件。建立对系统的信心的一部分是理解它们如何以及为什么会破坏。

Google 如何建立信心

学习如何破解系统的例子可以在谷歌的 DiRT(灾难恢复培训)计划中看到。该计划旨在帮助 Google 的 SRE 熟悉他们所支持的系统。在站点可靠性工程中,他们解释说,DiRT 计划的目的是因为 “长时间与生产脱节可能会导致信心问题,不管是过于自信还是不自信,该计划仅仅是为了发现当事件发生时的认知差距 “。

不幸的是,如果没有系统来衡量影响并从灾难中恢复过来,就不会让工程团队感觉释然。再次,我们将要求运行本章前面定义的基础结构断言。微小的单一责任功能为测量系统随时间的稳定性提供了绝佳的数据点。

测量混沌

我们再来看一下例 6-3 中的 AssertRouteable () 函数。想象一下,我们有一个服务,将连接到虚拟机,并尝试保持连接打开。服务每秒都会调用 AssertRouteable () 函数并记录结果。来自此服务的数据是虚拟机在其网络上路由的能力的准确表示。只要虚拟机可以路由,数据就会在图形上产生一条直线,如图 6-1 所示。

f-6-1
图 6-1. 随着时间推移的 AssertRoutable 测试图

如果在任何时候连接断开或者虚拟机不再能够路由,那么图形数据会发生变化,并且我们会看到图形上的线条发生变化。随着基础架构自行修复,线路开启该图将再次稳定下来,如图 6-2 所示。

f-6-2
图 6-2. 失败并且随着时间的推移修复 AssertRoutable 测试

这里考虑的重要方面是时间。随着时间的推移,测量混乱将伴随着混沌的测量。

我们可以快速扩展测量。想象一下,名为 AssertRouteable () 的服务现在正在虚拟机上调用一组 100 个基础结构断言。另外,假设我们有 100 台虚拟机正在测量。

这将对我们的基础架构产生大约每秒 1.0×104 个断言。来自我们的基础架构断言的大量数据使我们能够创建强大的图形表示基础架构。以可查询的格式记录数据也可以进行高级混沌调查。

随着混沌的测量,拥有可靠的测量工具和服务非常重要。以有意义的方式存储来自服务的数据也很重要,以便稍后可以引用它们。强烈建议将数据存储在日志聚合器或其他容易索引的数据存储中。

系统的混乱与系统的可靠性成反比。因此,它直接反映了我们正在评估的基础架构的稳定性。这意味着,当事情发生中断或引入变化时,将信息随时间绘制成分析是非常有价值的,以了解是否降低了稳定性。

引入混沌

将混沌引入系统的另一种说法:“故意破坏系统”。我们希望总结出我们可能在野外看到的意想不到的基础架构问题组合。如果我们不会故意注入混沌,那么云提供商、互联网或某个系统会为我们做这件事。

Netflix 的猿猴军队

Netflix 推出了它称之为猿猴军队(Simian Army)的系统,导致其混乱。猴子、猿猴以及猿猴家族的其他动物都以不同的方式造成混乱。Netflix 解释了这些工具之一 Chaos Monkey 的工作原理:

构建 Chaos Monkey 是我们的哲学,它是随机禁用我们的生产实例以确保我们能够在没有任何客户影响的情况下经受这种常见故障的工具。这个名字来自于在数据中心(或云区域)用武器释放野猴以随机击落实例并通过电缆咀嚼的想法 —— 这一切都是在我们不间断的继续为客户提供服务的同时进行。通过在工作日运行 Chaos Monkey,在受到严密监控的环境中,工程师站在一边解决问题,我们仍然可以学习有关系统弱点的教训,并构建自动恢复机制来处理这些问题。所以下一次星期天上午 3 点有一个实例失败的时候,我们甚至不会注意到。

就云原生基础架构而言,Monkey 是基础架构作为软件和利用协调模式的很好例子。主要区别在于它们旨在以意想不到的方式摧毁基础架构,而不是可预测地创建和管理基础架构。

此时,您应该有一个准备好使用的基础架构管理应用程序,或者至少有一个。用于部署,管理和稳定基础架构的基础架构管理应用程序也可用于引入混乱。

想象一下两个非常相似的部署。

第一个示例 6-4 代表有效(或 happy)基础架构。

例 6-4. infrastructure_happy.json

{
    "virtualMachines": [{
        "name": "my-vm",
        "size": "large",
        "localIp": "10.0.0.17",
        "subnet": "my-subnet"
    }],
    "subnets": [{
        "name": "my-subnet",
        "cidr": "10.0.100.0/24"
    }]
}

我们可以使在环境中设置的方式来部署此基础架构。这个基础架构应该部署且运行稳定。就像以前一样,随着时间的推移记录您的基础架构测试非常重要;图 6-3 就是一个例子。理想情况下,您运行的测试数量应该随着时间的推移而增加。

我们决定引入混乱。因此,我们创建了原始基础架构管理应用程序的副本,但这次我们采取了更加险恶的方式部署基础架构。我们利用我们的部署工具的能力来审计基础架构,并对已经存在的基础架构进行更改。

f-6-3
图 6-3. 成功的测试

第二次部署将代表有意故障的基础架构,并仍使用与原始基础架构相同的标识符(名称)。基础架构管理工具将检测现有基础架构并进行更改。在第二个示例(示例 6-5)中,我们将虚拟机大小更改为较小,并且意图将虚拟机的静态 IP 地址 192.168.1.111 分配到 10.0.100.0/24 范围之外。

我们知道虚拟机上的工作负载不会在小型虚拟机上运行,并且我们知道虚拟机将无法在网络上路由。这是我们将要介绍的混乱情况。

例 6-5. infrastructure_sad.json

{
    "virtualMachines": [{
        "name": "my-vm",
        "size": "small",
        "localIp": "192.168.1.111",
        "subnet": "my-subnet"
    }],
    "subnets": [{
        "name": "my-subnet",
        "cidr": "10.0.100.0/24"
    }]
}

由于第二个基础架构管理应用程序默默地对基础架构进行了更改,因此我们可以预料会看到事态发展。我们图中的数据将开始波动,如图 6-4 所示。

f-6-4
图 6-4. 包含网络故障的图形

如果虚拟机上的任何应用程序未完全中断,则应该缓慢地失败。虚拟机的内存和 CPU 现在已经过载。该 shell 无法 fork 新进程。负载平均值远高于 20。系统正在接近死锁,我们甚至无法访问虚拟机来查看错误,因为没有任何方式可以路由到冒名顶替者的 IP 地址。

正如预期的那样,初始系统将检测到底层基础架构中的某些内容发生了变化,并会相应地进行调整。冒名顶替者系统脱机是非常重要的,否则两个系统之间可能会有永无休止的和解,而这两者将会按照指示的方式相互竞争以纠正基础架构。

这种引入混沌的方法之美在于,我们不需要开发任何额外的工具或花费任何工程时间编写混沌框架。我们以巧妙的方式滥用了原有的基础架构管理工具,引发了一场灾难。

当然,这可能并不总是一个完美的解决方案。与您的生产基础架构应用程序不同,您的混沌应用程序应该有一定的限制,以确保它们有益。一些常见的限制是能够根据标签或元数据排除某些系统,不能在非工作时间运行混沌测试,并将混沌限制在特定的百分比或系统类型。

现在引入随机混沌的主要负担在于基础架构工程师随着时间推移而随机化探索的工作流程的能力。当然,基础架构工程师还需要确保从实验中收集的数据以可消化的格式提供。

监控基础架构

除了测试基础架构外,我们不能忘记监控正在运行的系统。测试和熟悉的失败模式可以让您对基础架构充满信心,但要测试系统可能出现的所有故障是不可能的。

监测可以检测到的,在测试期间未识别的异常并执行正确的操作是非常重要的。通过积极监控站点基础架构还可以增强我们的信心,即当发生的事情认为是不 “正常” 时,我们会收到警报。明确什么时候以及如何提醒人类这些异常是一个很有争议的话题。

在云原生环境中监控基础架构的实践中涌现出许多优秀的资源。我们不会在这里讨论这些主题,但您应该先阅读 Rob Ewaschuk 的 “监控分布式系统:Google 的 SRE 团队的案例研究”(O’Reilly),并观看 MonitoramaConference 上的视频。两者都可以在线免费观看。

无论您实施哪种监控解决方案,都要记住用云原生方法来创建您的监控规则。规则应声明并存储为代码。监控规则应与您的应用程序代码放在一起,并以自助服务的方式提供。当测试和遥测可能满足您的大部分需求时,不要过度补偿监控。

结论

测试可以将强我们对基础架构的信心,我们所支持的应用程序也获得了信心和信任。如果一个测试套件不能提供信心,它的价值应该是有问题的。记住,本章中提出的工具和模式是出发点,旨在激发和吸引在这个领域中工作的工程师。无论测试类型或运行它们的框架如何,最重要的一点是工程师可以开始相信他们的系统。作为工程师,我们通常会通过观察实践证明事情按预期工作的动手演示获得信心。

而且,在生产中进行实验不仅是可以的,而且是值得鼓励的。您需要确保环境是为了进行这种实验而建立的,并且实施了适当的跟踪,以便不会浪费测试!

现实测量是基础架构开发和测试的重要组成部分。能够从工程角度和运营角度来封装现实是运用基础架构的重要组成部分,因此可以确信它能够按预期运行。