在旧金山举行的ChaosConf大会上,Gremlin公司发布了其在“故障即服务”领域的第二款产品——应用级故障注入(ALFI)。最初的SaaS产品旨在帮助工程师在基础设施级创建和运行混沌实验,在此基础上,ALFI通过本地语言库支持实现了应用程序级的故障注入。目前,仅支持Java/JVM平台,但很快就会加入其他语言库。
根据Gremlin的文档介绍,“操作者考虑请求”,除了希望针对基础设施层尝试注入故障外——如重启一个计算实例,增加网络连接延迟或消耗大量的内存——操作者也希望针对目标应用程序请求注入故障,例如,增加延迟或终止请求。
在将ALFI库作为依赖项集成到应用程序之后,工程师可以使用基于Web的Gremlin UI来运行“攻击”,针对ALFI依赖项报告的特定应用程序属性来匹配和限定故障注入的影响。工程师可以创建一个范围精确的破坏实验,例如,只影响特定的客户ID、位置或设备类型。
选择通过ALFI进行故障注入的流量类型(图片来自Gremlin的博客)
Gremlin团队声称,由于ALFI嵌入到了应用程序中,因此,它可以在任何现有的环境中工作,其中也包括所有的无服务器平台,如AWS Lambda、Azure Functions和Google Cloud Functions。Gremlin认为,在使用微服务或函数即服务(FaaS)架构方式构建的系统中,许多事件的发生都是由于上游依赖项的某个地方出现了减速或故障。因此,ALFI可以模拟特定服务、特定RPC调用和外部依赖的延迟或全面故障,使工程师可以重现中断,主动发现未知的故障模式,并为多个组件发生故障的更复杂的场景做好准备。
要使用ALFI,工程师必须将Gremlin语言依赖项集成到应用程序中并重新部署。JVM安装指南针对当前支持的安装过程提供了一个详尽的步骤(目前只提供了一个Gradle依赖示例,很快就会提供一个Maven示例)。应用程序重新部署后,必须通过环境变量或属性文件配置一系列ALFI参数,比如Gremlin团队标识符和凭证。
工程师操作的主要Java类是com.gremlin.GremlinService,该类抽象了向Gremlin SaaS平台API注册、查找和缓存实验以及向Gremlin API报告成功所需要的所有功能。GremlinService类被设计为单例,可以通过依赖注入框架进行管理。文档中提供了将故障注入集成到Java Apache HTTP客户端和Amazon DynamoDBNoSQL数据库客户端的示例。还可以添加自定义扩展。
ALFI的一个重要概念是,每个应用程序都有一组标识属性。这组属性被称为ApplicationCoordinates,用于确定应用程序何时通过基于Web的UI匹配攻击请求。gremlin-core依赖包括在AWS Lambda和Amazon EC2上运行的集成。在AWS Lambda中,将默认设置属性“type=AwsLambda、name和region”。在AWS EC2中,将设置属性“type=AwsEc2、region、az、instanceId”,例如:
{“type”=”AwsLambda”, “region”=”us-west-1”, “name”=”event-handler”} 和{“type”=”MyServiceType”, “region”=”us-east-1”, “service”=”recommendations”, “criticality”=”2”, “userfacing”=”true”}。 通过在抽象类GremlinCoordinatesProvider中实现这两个方法,还可以定义操作者希望针对的应用程序的其他方面或“坐标”。要创建自定义ApplicationCoordinates,工程师必须重写initializeapplicationcoordinate()方法。自动生成的ApplicationCoordinates(如果有的话)作为这个方法的参数提供,这意味着,任何自定义的坐标都可以附加到里面。下面是一个自定义ApplicationCoordinates的示例:
import com.gremlin.ApplicationCoordinates;
import com.gremlin.GremlinCoordinatesProvider;
public class MyCoordinatesProvider extends GremlinCoordinatesProvider {
@Override
public ApplicationCoordinates initializeApplicationCoordinates(Optional<ApplicationCoordinates> autoDiscoveredCoordinates) {
return autoDiscoveredCoordinates.map(c -> {
c.putField("userfacing", "true");
return c;
}).orElseGet(() -> new ApplicationCoordinates.Builder()
.withType("MyServiceType")
.withField("name", "recommendations")
.withField("userfacing", "true")
.build());
}
}
然后,可以使用这组ApplicationCoordinates来匹配攻击。例如,如果操作者创建了匹配userfacing=true的攻击,那么上面示例中列举的应用程序将包含在攻击中。目前,操作者可以指定应该受故障注入影响的请求的百分比,为请求添加延迟或在请求执行线程上导致一个被抛出的异常。
在故障注入领域,也有开源的解决方案,例如(现已退役的)Simian Army和Chaos Toolkit,但是,这些产品需要自托管。运行混沌实验确实需要提前准备和设计,最近有一篇长文“ChaosConf问答:混沌工程的好处、挑战和实践”对这些主题都有涉及。