利用混沌工程在Prime Video构建弹性服务

2020-08-24 17:03:04

大型分布式软件系统由几个独立的子系统(如CDN、负载均衡器和数据库)及其交互组成。这些交互有时会因不可预见的动荡事件(例如,网络故障)而导致不可预测的结果。这些事件可能会导致系统范围的故障。

混沌工程是在分布式系统上进行实验以建立对系统承受动荡事件能力的信心的学科。混沌工程需要采用实践来主动识别分布式系统中的相互作用和相关故障,还需要实施和验证对策。混沌工程的关键是以受控的方式注入故障。

在这篇文章中,我们介绍了一种在使用Amazon Elastic Compute Cloud(Amazon EC2)和Amazon Elastic Container Service(Amazon ECS)的系统中注入故障的简单方法,以及它与负载测试套件的集成,以验证为防止依赖和资源耗尽故障而采取的对策。典型的混乱实验可能是针对系统生成基线负载(流量),增加对底层数据库的所有网络调用的延迟,然后验证超时和重试。我们将解释如何注入此类失败(增加数据库调用的延迟),为什么在负载下验证对策(超时和重试)非常重要,以及如何在基于Amazon EC2的系统中执行。

我们将从简单介绍混沌工程开始,然后使用AWS Systems Manager深入研究故障注入。然后我们将展示我们的开源库AWSSSMChaosRunner。这是受到Adrian Hornsby的博客文章“使用AWS系统管理器给Amazon EC2注入混乱”的启发。

最后,我们将提供一个集成示例,并说明Prime Video如何使用此库来防止潜在的客户影响中断。

软件测试通常涉及实现和自动化单元测试、集成测试和端到端测试。虽然这些测试很重要,但它们并不涵盖分布式系统中可能出现的更广泛的中断(例如,可用区中断、依赖项故障、网络中断等)。

一般来说,软件系统对这些场景的行为仍然是未知的。例如,如果服务群中的Amazon EC2实例持续高CPU消耗,会发生什么情况?出现这种情况的原因可能是通信量意外增加或代码中的循环实现不正确。在没有压力的情况下,很难建立对软件系统的信心。要考虑的问题:

当底层实例出现持续的CPU峰值时,您是否测试了系统的行为?

有没有实施什么对策?例如,是否设置了自动伸缩,其行为是否符合预期?超时和重试是否合适?

如前所述,混沌工程需要采用实践来主动识别分布式系统中的交互和相关故障,还需要实现和验证对策。这些都可以通过混沌工程实验来实现。

资源耗尽:例如,CPU、虚拟内存、磁盘空间等耗尽。这些故障经常发生,通常是由失败的部署、内存泄漏或意外的流量高峰引起的。控制资源耗尽的混沌实验验证了有足够的监控来检测此类故障和适当的对策(例如,自动缩放、自动重启等)。以便系统自动恢复。

网络依赖失败或缓慢:例如,通过网络访问的数据库响应速度较慢,或者故障率较高。当网络出现间歇性问题或依赖项处于降级状态时,可能会发生这些故障。超时、重试策略和断路器是这些故障的典型对策;然而,它们很少经过充分测试,因为单元或集成测试通常不能很可靠地验证它们。在依赖代码路径中注入延迟或故障的混沌实验很好地证明了对策的有效性-超时、重试和断路器。

有关混沌工程的更深入的回顾,请参阅本文末尾的参考资料。

接下来,让我们回顾一下AWS Systems Manager的基本概念:AWS Systems Manager代理(SSM代理)、SendCommand API和AWS Systems Manager文档。

AWS Systems Manager是一项服务,用于查看来自多个AWS服务的运营数据,并自动执行您的AWS资源中的运营任务。可以在用户指南中找到Systems Manager功能的完整列表。

对于Amazon EC2实例,AWS Systems Manager提供SSM代理来在实例或服务器内部执行操作。此功能通常在大多数Amazon EC2实例上用于操作系统修补和管理SSH会话。

SSM Agent是开源Amazon软件,根据Apache License 2.0发布,可以在Amazon EC2实例上安装和配置。SSM代理使Systems Manager能够更新、管理和配置这些资源。默认情况下,SSM代理预安装在从以下Amazon机器映像(AMI)创建的实例上:2016年11月发布的Windows Server 2008-2012 R2 AMI、Windows Server 2016和2019年、Amazon Linux、Amazon Linux 2、Ubuntu Server 16.04、Ubuntu Server 18.04和Amazon ECS优化。

AWS SSM SendCommand API允许通过SSM代理以编程方式在一个或多个实例上运行命令。

指定的实例instanceid=I-1234567890abcdef0将运行“echo Hello,World!”作为shell脚本。目标可用于通过使用实例标签(例如,弹性伸缩组)指定单个实例或实例组。

该命令的所有日志都将发送到名为test的CloudWatch日志组。

AWS SSM SEND-COMMAND\--Document-Name";AWS-RunShellScript";\--Parameters';Commands=[";ECHO Hello,World!";]';\-Targets";key=instanceids,Values=I-1234567890abcdef0";\--COMMENT";ECHO,World!";--超时秒数10--Cloud-Watch。

AWS Systems Manager文档(SSM文档)可用于指定要在一个或多个实例上执行的shell脚本形式的复杂命令。您可以通过AWS Systems Manager控制台或SendCommand API运行SSM文档。

示例:用于在给定UDP或TCP端口上路由所有传出流量的黑洞的SSM文档。

-schemaVersion:';2.2';描述:黑洞实例上的协议/端口参数:protocol:type:string描述:指定要黑洞的协议。选项:tcp,udp(必填)allowed值:-tcp-udp port:type:string描述:指定要黑洞的端口。(必选)持续时间:类型:字符串描述:黑洞的持续时间(秒)。默认:";60";主要步骤:-操作:AWS:runShellScript名称:ChaosBlackholeAttack输入:runCommand:-iptables-A output-p{{protocol}}--dport{{port}}-j drop-sleep{{uration}}-iptables-D output-p{{protocol}}--dport{{port}}-j drop

假设在Amazon EC2实例上安装了SSM代理并配置了正确的权限,则可以通过以下方式将AWS Systems Manager用于Amazon EC2实例上的故障注入:

1.通过AWS Systems Manager控制台或AWS CLI创建SSM文档。SSM文档中包含的shell脚本必须可以在底层实例上执行。

2.通过AWS系统管理器控制台或AWS CLI调用SSM SendCommand API。

可以通过对目标参数使用适当的标记来定义Amazon EC2机队。

必须指定底层shell脚本的参数(上例中的持续时间/端口/协议)。

必须配置和指定CloudWatch日志组,才能在单个位置查看整个Amazon EC2机队的日志。

如果上述步骤成功,则所有指定的Amazon EC2主机都将注入故障。例如,EC2主机将黑洞将传出流量发送到给定的UDP/TCP端口。但是,可能没有请求命中您要向其中注入故障的服务;要么是流量低的时期,要么是开发团队。在这种情况下,故障注入的影响可能微乎其微,或者更糟,根本察觉不到。因此,不可能验证已实施的对策。需要第三步。

3.使用负载生成器生成到服务的流量,以模拟系统上的真实高流量。

手动运行上述步骤容易出现配置错误,风险很大,而且很耗时。这些步骤可以使用最近发布的AWSSSMChaosRunner库自动执行,如下图所示。

这个库抽象了SSM文档的创建和调用SSM SendCommand,并为您的混沌实验提供了经过测试和测试的SSM文档。该库在Apache-2.0许可下是开源的,可以在GitHub和Maven Central上获得。

AWSServiceLatencyAttack:使用ip-ranges.amazonaws.com返回的CIDR范围增加AWS服务的延迟。这对于Amazon Simple Storage Service(AmazonS3)或Amazon DynamoDB等服务是必需的,在这些服务中,解析的IP地址在混沌实验期间可能会更改。

AWSServicePacketLossAttack:使用ip-ranges.amazonaws.com返回的CIDR范围将数据包丢弃到AWS服务。这对于像Amazon S3或Amazon DynamoDB这样的服务是必要的,在这些服务中,解析的IP地址在混沌实验期间可能会更改。

MultiIPAddressLatencyAttack:将延迟添加到IPAddress列表中的所有调用。这对于路由器→主机类型的设置可能很有用。

MultiIPAddressPacketLossAttack:从IPAddress列表的所有调用中丢弃数据包。这对于路由器→主机类型的设置可能很有用。

例如,以在Amazon EC2中运行的服务为例。(为简化起见,省略了CDN、负载均衡、私有网络等常用推荐组件)。

此服务接收客户端请求、应用业务逻辑并访问数据库(或任何外部依赖项)。让我们了解如何将AWSSSMChaosRunner库应用于此服务。

服务的测试是用Java、Kotlin或Scala编写的。AWSSSMChaosRunner库仅适用于这些语言。

服务运行状况和行为必须使用指标或日志进行检测和监控。没有监测,无法观察失败注射的效果。

在执行混沌实验时,会从测试中向服务生成一些基线流量(负载)。生成流量将有助于验证实验假设。

虽然可以通过不同的方式实施此部分,但此处介绍的方法会在每次运行测试时为AWS Systems Manager生成临时凭据。

首先,您必须创建IAM用户及其可以承担的IAM角色。必须将以下IAM策略附加到此角色。

{";Version";:";2012-10-17";,";语句";:[{";Action";:[";sts:AssumeRole";,";SSM:CancelCommand";,";SSM:CreateDocument";,";SSM:DeleteDocument";,";,";SSM:DescribeDocumentParameters";,";SSM:DescribeInstanceProperties";,";SSM:GetDocument";,";SSM:ListTagsForResource";,";SSM:ListDocuments";,";SSM:ListDocumentVersions";,";SSM:SistTagsForResource";,";SSM:ListDocumentVersions";,";SSM:SistDocumentVersions";,";:";ALLOW";},{";Action";:[";EC2:DescribeInstances";,";IAM:PassRole";,";IAM:ListRoles";],";Resource";:[";*";],";Effect";:";Allow";},{&#。:[";SSM:StopAutomationExecution";,";SSM:StartAutomationExecution";,";SSM:DescribeAutomationExecutions";,";SSM:GetAutomationExecution";],";Resource";:[";*";],";Effect";:";Allow"。

此代码应在测试初始化期间调用(即,无论在何处创建单例)。

//kotlin@Beanopen Fun awsSecurityTokenService(redentialsProvider:AWSCredentialsProvider,awsRegion:String):AWSSecurityTokenService{Return AWSSecurityTokenServiceClientBuilder.standard().with Credentials(RedentialsProvider).with Region(AwsRegion).build()}@Beanopen Fun awsSimpleSystemsManagement(securityTokenService:AWSSecurityTokenService,awsAccountId:).with StsClient(SecurityTokenService).build()返回AWSSimpleSystemsManagementClientBuilder.standard().with Credentials(RedentialsProvider).build()}

Step3.开始测试前启动故障注入攻击,测试结束后停止故障注入攻击。

//kotlin@BeforeOverride Fun Initialise(args:Array){if(shouldExecuteChaosRunner()){ssm=applicationContext.getBean(AWSSimpleSystemsManagement::class.java)ssmAttack=getAttack(ssm,attackConfiguration)command=ssmAttack.start()}}@TestFun`给定的故障注入生成对服务的调用`(int:时长){//该测试应该调用服务的端点,并在测试期间不断重复。//可以添加额外的日志记录,也可以监控服务仪表板以查看概览。Val startTime=LocalDateTime.now()While(getElapsedSeconds(StartTime)<;=Duration){serviceClient.callEndpoint()}}@AfterOverride Fun Destroy(){ssmAttt.stop(Command)}。

注意:AWSSSMChaosRunner也可以用于基于EC2+ECS的服务,只需在上述步骤之前执行一个设置步骤。有关更多详细信息,请参阅Github自述文件。

2020年3月,Prime Video推出了Prime Video Profile,它允许Prime Video用户访问单独的推荐、季度进度和观看列表,因为这些内容基于个人简档活动。这种新的客户体验需要使用Amazon EC2设计和实施新服务。

这些服务是分布式系统的一部分,它们通过网络调用其他内部Amazon服务。测试此服务使用的超时、重试和断路器配置被认为至关重要,因为:

当需要这些对策(超时、重试和断路器)时,通常会在停机期间发现配置中的问题。

Prime Video使用AWSSSMChaosRunner的DependencyLatency攻击,并针对服务生成负载,从而在依赖项表现出高延迟时模拟流量,从而实现了此混沌工程实验。

观察了服务到服务呼叫指标,并因此验证了超时、重试和断路器配置。

现在,让我们回顾其中一个混沌实验的结果,了解它如何帮助我们主动发现一个潜在影响客户的问题。

实验假设:服务→ElastiCache调用超时设置为40毫秒。这将通过在实验期间观察服务→ElastiCache延迟度量来验证。

故障注入:使用AWSSSMChaosRunner将两秒的延迟添加到服务AWSSSMChaosRunner的服务→ElastiCache调用中。

针对服务生成基准负载:每秒针对服务生成1000个请求。如前所述,在加载系统时运行混沌工程实验是至关重要的。

上图显示服务→弹性缓存延迟超过了配置的40ms超时。因此,ElastiCache超时配置失败。

根据这些结果,我们修复了超时配置中的一个错误。为了验证我们的修复,我们随后重新运行了相同的实验。

该图显示服务→弹性缓存延迟的最大上限为配置的超时值40毫秒。尽管实验将两秒的额外延迟注入到此调用路径中,但仍会发生这种情况。此结果验证了如果ElastiCache响应较慢或该网络路径出现问题,服务将很快超时。

运行此混乱实验导致发现依赖项降级对策中的错误(即ElastiCache超时)。错误修复防止了潜在影响客户的故障发生。

测试服务依赖超时、重试和断路器配置至关重要。在本帖子中,我们介绍了使用AWS Systems Manager在Amazon EC2上进行故障注入的开源方法,并演示了Prime Video如何将其与负载测试相结合以实现更高级别的恢复能力。此Prime Video案例研究展示了混乱工程如何帮助防止使用传统测试方法难以确定的潜在影响客户的问题。