掘金 后端 ( ) • 2024-05-15 18:55

highlight: a11y-dark

1、概述

1.1 什么是混沌工程

混沌工程(Chaos Engineering)是一种测试和验证分布式系统的稳定性和可靠性的实践方法。它源自云计算和大规模分布式系统的发展,目的是帮助组织识别和解决系统中的潜在故障,并确保系统在各种异常条件下仍能正常运行

混沌工程是一种持续改进和演化的实践,有助于构建更健壮的分布式系统,提高系统的可用性和可靠性。很多大型科技公司,如Netflix、Amazon、Uber等,都采用混沌工程作为其系统可靠性工程的一部分。

1.2 混沌工程的好处

混沌工程的主要思想是有目的地引入混乱和故障条件到系统中,以测试系统的抗故障能力,包括硬件故障、网络问题、软件错误等。通过模拟这些故障,可以带来以下好处:

  1. 提前发现问题:通过模拟故障情况,可以在实际生产环境之前发现潜在的问题,从而提前修复或改进系统。
  2. 增强可靠性:混沌工程有助于识别系统中的薄弱点,帮助团队改进系统的设计和实现,从而提高系统的可靠性。
  3. 降低系统维护成本:通过混沌工程,可以减少系统中的未预期故障,降低了维护和修复的成本。
  4. 提高团队的响应能力:混沌工程要求团队具备应对故障的能力,包括监测和自动化应急响应,从而提高了团队的整体能力。

2、混沌工程工具 - ChaosBlade

目前业界有多种混沌工程工具,例如ChaosMesh、ChaosMonkey、ChaosToolkit、ChaosBlade等等。

经过比较,我们最终选择ChaosBlade作为我们混沌工程平台的底层工具。

2.1 ChaosBlade介绍

ChaosBlade 是阿里巴巴 2019 年开源的混沌工程工具,旨在通过混沌工程帮助企业解决云原生过程中高可用问题,它支持 3 大系统平台,4 种编程语言应用,共涉及 200 多的实验场景,3000 多个实验参数,可以精细化的控制实验范围。

ChaosBlade 具有以下核心优势特性:

  1. 灵活性和可扩展性:Chaosblade 提供了丰富的故障注入模块,可以模拟各种故障场景,如延迟、异常、资源耗尽等。它还支持自定义的故障注入,使用户可以根据自己的需求创建新的故障模拟。
  2. 多语言支持:Chaosblade 不仅支持 Java 应用程序的混沌工程实验,还支持其他编程语言,如 Go、Python、Node.js 等。这使得它可以适用于多种技术栈的应用程序。
  3. 易于使用:Chaosblade 提供了直观的命令行工具,用户可以轻松创建和管理混沌实验。它还有详细的文档和示例,使用户可以迅速上手。
  4. 社区支持:Chaosblade 拥有一个积极的开源社区,不断更新和改进工具,用户可以获取来自社区的支持和建议。
  5. 与容器技术集成:Chaosblade 可以与容器编排系统(如 Kubernetes)集成,以实现容器级别的混沌工程实验。这使得它适用于现代容器化应用的环境。
  6. 开源和免费:Chaosblade 是一个开源项目,用户可以免费使用,并根据自己的需求进行定制和扩展。

总的来说,Chaosblade 是一个强大而灵活的混沌工程工具,可以帮助用户在生产环境中测试和改进系统的稳定性和弹性。它的多语言支持、容器集成、监控和度量支持等特点使其成为一个受欢迎的选择,特别是在容器化和微服务架构的应用程序中。

2.2 ChaosBlade模型

在进行混沌实验之前,需要明确以下问题,以建立一个清晰的实验模型:

  1. 对什么做混沌实验?
  2. 混沌实验的实施范围是什么?
  3. 具体要执行什么样的实验?
  4. 实验生效的匹配条件是什么?你需要定义在何种情况下实验会触发,这可能包括了触发条件、目标组件、请求类型等。

明确以上内容,就可以抽象出ChaosBlade的模型定义,Chaosblade的模型定义可以简化为以下几个要点:

image.png

1. Target(实验靶点) :这是混沌实验的主要对象,通常是系统中的一个组件,如容器、应用框架(例如Dubbo、Redis、Zookeeper)等。实验会针对这些组件执行。

2. Scope(实验范围) :Scope确定了实验的执行范围,通常是特定的机器或机器集群。在特定范围内触发混沌实验。

3. Matcher(实验规则匹配器) :Matcher是实验规则的匹配器,根据所配置的Target来定义相关的实验匹配规则。可以配置多个Matcher,每个Matcher可以针对特定的目标组件。例如,在RPC领域,可以匹配服务提供者和服务消费者,而在缓存领域,可以匹配不同的操作。

4. Action(实验场景) :Action定义了具体的实验场景,这取决于所选的Target。例如,在磁盘实验中,可以有磁盘满、磁盘IO读写高、磁盘硬件故障等场景。对于应用,可以模拟延迟、异常、返回指定值(如错误码或大对象)、参数篡改、重复调用等。

举一个例子:使用ChaosBlade对一台IP为192.168.1.1的机器上注入网络故障,要目标IP为xxx的机器访问192.168.1.1时网络延迟3s

  • 目标:网络组件
  • 范围:单台机器,IP为192.168.1.1
  • 实验:模拟网络延迟3s
  • 匹配条件:目标IP为xxx

2.3 chaosblade-exec-jvm自定义插件

chaosblade-exec-jvm 是 ChaosBlade 的一个子项目,它专注于在Java虚拟机(JVM)层面执行混沌实验,以验证与Java应用程序可靠性

官方(1.7.2)默认提供了23个插件,包括MySQL、HBase、ES、Servlet等等,使用默认插件我们可以实现MySQL操作延迟、MySQL操作异常。

尽管官方提供的插件已经能满足大部分实验场景,但是如果我们需要修改一些参数的匹配条件或者想要演练一些官方不覆盖的实验场景,就需要我们改造已有插件或者自己实现一个插件。

接下来就以实现腾讯云COS插件为例,来介绍如何自定义一个chaosblade-exec-jvm插件:

1、新建一个插件模块

克隆chaosblade-exec-jvm项目,并在chaosblade-exec-plugin目录下,新建一个子模块,例如:chaosblade-exec-plugin-cos

2、自定义Enhancer

CosEnhancer继承BeforeEnhancer,重写doBeforeAdvice方法,在doBeforeAdvice方法中,主要是通过反射获取操作的bucketName。

创建MatchModel,将获取到的参数放到enhancerModel中进行自定义bucketName匹配,返回 EnhancerModel,Inject 阶段会与输入的参数做比对。

public class CosEnhancer extends BeforeEnhancer {

    @Override
    public EnhancerModel doBeforeAdvice(ClassLoader classLoader, String className, Object object, Method method, Object[] methodArguments) throws Exception {
        if (methodArguments == null || methodArguments.length == 0) {
            return null;
        }
        // 获取bucketName
        Object request = ReflectUtil.invokeMethod(methodArguments[0], "getOriginalRequest", new Object[] {},  false);
        String bucketName = ReflectUtil.invokeMethod(request, "getBucketName", new Object[] {}, false);
        LOGGER.info("originRequest: {}, bucketName: {}", request, bucketName);
        MatcherModel matcherModel = new MatcherModel();
        EnhancerModel enhancerModel = new EnhancerModel(classLoader, matcherModel);
        enhancerModel.addCustomMatcher(COS_BUCKET_NAME, bucketName, CosBucketNameMatcher.getInstance());
        LOGGER.info("matcherModel: {}", JsonUtil.writer().writeValueAsString(matcherModel));
        return enhancerModel;
    }

}

3、自定义Matcher

在上面的Enhancer中,我们往EnhancerModel中添加了一个自定义Matcher

如果想要使用默认的参数匹配规则,只需要enhancerModel.add(参数名,反射获取到的实际参数),这样会做一个等值匹配

如果想要更加复杂的匹配逻辑,就需要自定义Matcher。 下面的CosBucketNameMatcher就实现了:如果不传bucketName则全匹配,如果传了多个,则用逗号分隔,多个匹配成功一个则成功。

public class CosBucketNameMatcher implements CustomMatcher {

    public static final Logger LOGGER = LoggerFactory.getLogger(CosBucketNameMatcher.class);

    private static final CosBucketNameMatcher COS_BUCKETNAME_MATCHER = new CosBucketNameMatcher();

    public static CosBucketNameMatcher getInstance() {
        return COS_BUCKETNAME_MATCHER;
    }

    @Override
    public boolean match(String commandValue, Object originValue) {
        LOGGER.info("commandValue: {}, originValue: {}", commandValue, originValue);
        if (StringUtils.isBlank(commandValue)) {
            return true;
        }
        return Arrays.asList(commandValue.split(",")).contains((String) originValue);
    }

    @Override
    public boolean regexMatch(String commandValue, Object originValue) {
        return false;
    }

}

4、自定义PointCut

PointCut的核心就是要定义要拦截的类和方法,如果有多个则需要用or匹配。

在我们的例子中,CosPointCut拦截类,定义拦截com.qcloud.cos.COSClient@invoke方法(因为COS的所有操作都会调用该方法),invoke如果有多个则明确是有2个参数的invoke方法

public class CosPointCut implements PointCut {

    @Override
    public ClassMatcher getClassMatcher() {
        return new NameClassMatcher("com.qcloud.cos.COSClient");
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        AndMethodMatcher invokeMatcher = new AndMethodMatcher();
        invokeMatcher.and(new NameMethodMatcher("invoke"))
                .and(new ParameterMethodMatcher(2, ParameterMethodMatcher.EQUAL));
        return invokeMatcher;
    }
}

5、自定义Spec

这是一条COS的注入命令: ./blade create cos delay --bucketName=xxx --time=3000

ModelSpec即为cos,ActionSpec为delay,MatcherSpec为--bucketName,FlagSpec为–time。对于chaosblade-exec-jvm的插件而言,我们只需要自定义ModelSpec和MatcherSpec,像ActionApec/FlagSpec使用默认即可

ModelSpec:ModelSpec 的 getTarget()方法对于命令中 target 部分的名称,例如cos、mysql等

public class CosModelSpec extends FrameworkModelSpec {

    @Override
    protected List<MatcherSpec> createNewMatcherSpecs() {
        List<MatcherSpec> matcherSpecs = new ArrayList<>(1);
        matcherSpecs.add(new CosBucketMatcherSpec());
        return matcherSpecs;
    }

    @Override
    public String getTarget() {
        return "cos";
    }

    @Override
    public String getShortDesc() {
        return "cos experiment";
    }

    @Override
    public String getLongDesc() {
        return "cos experiment";
    }
}

MatcherSpec

CosBucketMatcherSpec主要是定义 自定义参数匹配的参数名

public class CosBucketMatcherSpec extends BasePredicateMatcherSpec {

    @Override
    public String getName() {
        return "bucketName";
    }

    @Override
    public String getDesc() {
        return "cos bucket name";
    }

    @Override
    public boolean noArgs() {
        return false;
    }

    @Override
    public boolean required() {
        return false;
    }
}

6、自定义Plugin

继承 com.alibaba.chaosblade.exec.common.aop.Plugin,自定义 target 名称,添加 Enhancer、PointCut、ModelSpec 即可

实现类需要全路径名复制到 : resources/META-INF/services/com.alibaba.chaosblade.exec.common.aop.Plugin 挂载 Agent,模块激活后 plugin 自动加载。

public class CosPlugin implements Plugin {
    @Override
    public String getName() {
        return "cos";
    }

    @Override
    public ModelSpec getModelSpec() {
        return new CosModelSpec();
    }

    @Override
    public PointCut getPointCut() {
        return new CosPointCut();
    }

    @Override
    public Enhancer getEnhancer() {
        return new CosEnhancer();
    }
}

3、酷家乐混沌工程平台 - Chaoslab

酷家乐混沌工程平台Chaoslab是基于阿里巴巴ChaosBlade工具并结合酷家乐业务特色设计并实现的。

Chaoslab能够提供低成本、场景丰富的故障演练服务以及高效便捷的强弱依赖分析能力,帮助业务方及时发现系统隐患、验证预案的有效性,从而提高系统的可用性。

3.1 基础能力

故障注入能力

  • 基于开源的 ChaosBlade 混沌工具,提供了容器系统资源级别、Java 方法级别这两大类故障场景,结合目前公司特定中间件使用做定制,覆盖了目前绝大部分 Java 场景下客户端的基础故障能力
  • 引入 Chaosblade Operator 云原生混沌工具组件,进一步支持 k8s 集群级别的故障注入能力
  • 对于其他高度自由的故障模拟场景,我们也计划提供脚本执行方式、纯文本记录方式来承载,提高混沌工程平台的适用面

image.png

流水线编排能力

Chaoslab平台将故障注入的流程抽象成一个流水线,并支持各种演练场景下的流程编排,可以灵活的拓展和编排任务。

监控配置能力

创建演练时,在演练流程配置页面,支持进行监控配置,可以配置注入对象的黄金指标,例如应用的每分钟请求量(QPM)、平均响应时长(RT)、错误率,以及实例的CPU使用率、实例的内存使用率等。

除了上述说的黄金指标,还支持配置即席查询、警报规则等等。

在演练执行过程中,根据配置,我们提供了实时监控数据,以使用户能够直观地跟踪和评估系统的健康状态,确保演练过程中系统的稳定性和性能。这个功能可以帮助用户更好地了解系统的实时状态,从而更有针对性地调整实验或采取必要的行动。

3.2 业务能力

日常演练

日常演练功能可以根据用户需求执行一次简单的故障注入,是目前用户使用最多的功能。

流程可以简单梳理为:选择故障项 → 演练流程配置 → 流水线执行 → 故障持续 → 故障排查、观察故障现象 → 故障恢复

image.png 如何选择合适动作项?给大家一些参考

场景 推荐动作项 网络故障 网络延迟、网络丢包、网络包损坏 系统基础资源瓶颈 CPU负载、内存负载、磁盘负载 依赖中间件故障 MySQL延迟(异常)、Jedis延迟(异常)、COS延迟(故障)、Hbase延迟(故障) 依赖下游接口故障 Servlet延迟(异常) 、SOA延迟(异常) JVM相关异常 JVM异常、JVM延迟、内存溢出

根据选择的动作项,按需填写动作项参数以及填写演练对象信息,确认后,启动流水线,执行注入流程,当故障注入成功后会进入故障持续中,故障持续的时间在填写配置参数的时候就已经配置好了。

在故障持续期间,可以观察故障现象或者演练预案是否成功,当设置的故障时间到了,会自动执行恢复逻辑。如果想提前结束,则可以点击快速恢复手动恢复。

突袭演练

突袭演练是我们公司不定时会组织的一项演练活动。开始,需要活动的策划人员设计故障演练场景和实施方案,活动的策划人员一般是各个业务线的测试或者SRE。设计好故障实施方案后,使用Chaoslab平台创建好对应的突袭演练,然后执行注入。

和日常演练的区别是,突袭演练可以设置定时执行,而且会在故障注入成功后,创建工单给配置的技术支持。创建工单后,技术支持同学会使用应急平台进行应急响应,拉群、拉会议并同步故障处理进展,和一次真实的工单应急一样。

业务方收到故障通知后,会进行排查,定位问题并解决。故障处理完成后,会编写复盘文档并进行复盘会议,自此一次突袭演练才算是正式结束。

image.png 突袭演练是在日常演练的基础上,加上了创建工单,和技术支持同学一起响应的流程。

其目的是锻炼应急人员应急响应和协作能力,包括检测、分析、应对和恢复。让业务方多参与突袭演练有助于提高他们的安全意识和技能,使他们能够更好地理解和应对潜在的威胁。

经验库

我们从过去出现的故障中,筛选出了可演练、有演练价值的故障,并根据故障的出现原因,抽象成了一个个演练模板,组成了经验库。之后想要演练历史故障,就可以使用经验库中的模板,一键创建演练。

这么做目的是复用历史演练中使用的演练场景,包括配置的故障项、故障项参数、监控策略、探测策略等信息,提高演练效率和合作难度。

3.3 强弱依赖验证

随着分布式微服务的发展,系统正在变得越来越复杂,一个普通的应用也可能依赖了很多其他的服务。在没有明确强弱依赖关系的前提下,系统很难进行限流、降级、优化改造等操作

强弱依赖验证就是通过科学的手段检测和管理应用之间的依赖关系验证依赖的强度提前发现因为依赖问题可能导致的故障,确保系统的各个部分按照预期的方式协同工作,避免依赖异常导致的故障影响用户体验,从而持续推进系统稳定性提升。

强依赖、弱依赖怎么区分?

下游依赖发生异常时,如果不影响上游的核心业务流程和系统可用性,则为弱依赖,反之为强依赖。

1、服务维度依赖验证

Chaoslab服务维度依赖验证的原理: 让下游依赖出现故障(长时间延迟或者直接报错),观察对上游的影响

通过Chaoslab给服务A的下游依赖 注入故障,导致下游依赖B和下游依赖C不能 正常给A提供服务(tip:注入的故障是在服务A本身,通过模拟实现的下游报错或长时间延迟,不会对下游服务本身产生影响

这时使用A服务 手动调用或接口自动化测试调用下游依赖,然后用户需要根据响应结果和上游表现进行依赖强度判定

image.png

2、接口维度强弱依赖验证 & 依赖强度自动判定

用户想要验证下游某接口依赖强度(可能依赖某个下游的多个接口),以前只能是通过操作整个服务级别的降级,间接操作接口降级,这样即麻烦又不能保证验证的准确性

现在支持了针对 下游某服务接口(中间件)进行故障注入,实现 上游接口 → 下游接口(中间件)依赖强度的验证

在接口自动化平台针对核心接口增加一些 强弱依赖验证场景的自动化Case后,平台即可根据预期依赖强度&测试用例结果 → 直接判定依赖强度(不用用户去参与)

流程图:

image.png 3、强弱依赖 - 自动演练

系统支持了配置cron表达式后,定时去验证配置好的下游依赖的依赖强度,并给用户发送通知&验证报告

image.png

4、未来发展方向 & 展望

1、线上演练

接下来我们要做的是将线上演练的流程嵌入到Chaoslab混沌工程平台,建设线上演练平台,最主要的事保证演练流程的正确和安全。

以下几点是我们需要重点关注的:

1、演练前:业务方预约线上演练日期 → 编写线上演练文档(影响面、验证流程、紧急恢复逻辑等) → 线下预演练 → 文档评审

  • 规范演练流程,确保所有研发按照相同的标准进行演练。这样可以消除不同演练之间的差异,确保演练结果的可量化

2、演练中:应用号拉群 → 实时推送演练关键节点信息 → 推送关联警报信息等

3、演练后:系统生成演练文档 → 组织复盘 → 改进Action

做好以上这些点,可以使得线上演练平台成为一种高效、安全、可度量,可以有效地提升演练质量和效果。

2、线下全面自动化演练

目前我们支持了强弱依赖的自动化演练,接下来的目标是,用户可以任意添加一个自动演练计划,针对某个服务配置多个演练动作项,系统会自动取注入并生成报告。

对此需要我们做到以下几点:
1、接入录制流量平台,在注入故障后,需要回放流量取验证效果

2、收集该服务的监控数据,包括错误日志、系统指标、执行接口RT、错误率等

3、将验证结果聚合后生成报告并发送给用户


参考文章:

ChaosBlade官方文档

干货 | 阿里巴巴混沌测试工具ChaosBlade两万字解读

去哪儿旅行混沌工程落地实践

主动发现系统稳定性缺陷:混沌工程 | 京东云技术团队

字节跳动混沌工程之云原生场景实践