模拟 JVM 应用故障

Chaos Mesh 通过 Byteman 模拟 JVM 应用故障,主要支持以下类型的故障::

  • 抛出自定义异常
  • 触发垃圾回收
  • 增加方法延迟
  • 指定方法返回值
  • 设置 Byteman 配置文件触发故障
  • 增加 JVM 压力

本文主要介绍如何创建以上故障类型的 JVM 实验。

模拟 JVM 应用故障 - 图1注意

Linux 系统内核必须为 4.1 及以上版本。

使用 Dashboard 方式创建实验

  1. 单击实验页面中的“新的实验”按钮创建实验:

    创建实验

  2. 在“选择目标”处选择 “JVM 故障”,然后选择具体行为(如 RETURN),最后填写具体配置:

    JVMChaos 实验

    具体配置的填写方式,参考字段说明

  3. 填写实验信息,指定实验范围以及实验计划运行时间:

    实验信息

  4. 提交实验。

使用 YAML 方式创建实验

下面将以指定返回值为例,展示 JVMChaos 的使用方法与效果。以下内容中涉及的 YAML 文件均可在 examples/jvm 中找到,以下步骤默认的工作路径也是在 examples/jvm 中。 默认 Chaos Mesh 安装的命名空间为 chaos-testing

第 1 步:创建被测应用

Helloworld 是一个简单的 Java 应用,此处作为被测应用。被测应用定义在 example/jvm/app.yaml 中,内容如下:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: helloworld
  5. namespace: helloworld
  6. spec:
  7. containers:
  8. - name: helloworld
  9. # source code: https://github.com/WangXiangUSTC/byteman-example/tree/main/example.helloworld
  10. # this application will print log like this below:
  11. # 0. Hello World
  12. # 1. Hello World
  13. # ...
  14. image: xiang13225080/helloworld:v1.0
  15. imagePullPolicy: IfNotPresent
  1. 创建应用所属的 namespace:

    1. kubectl create namespace helloworld
  2. 建立该应用 Pod:

    1. kubectl apply -f app.yaml
  3. 执行 kubectl -n helloworld get pods,预期能够观察到命名空间 helloworld 中名为 helloworld 的 Pod。

    1. kubectl -n helloworld get pods

预期结果如下:

  1. kubectl get pods -n helloworld
  2. NAME READY STATUS RESTARTS AGE
  3. helloworld 1/1 Running 0 2m

等待 READY 成为 1/1 后,可以进行下一步。

第 2 步:观测未被注入时的行为

在注入前你可以先观测应用 helloworld 未被注入时的行为,例如:

  1. kubectl -n helloworld logs -f helloworld

输出如下所示:

  1. 0. Hello World
  2. 1. Hello World
  3. 2. Hello World
  4. 3. Hello World
  5. 4. Hello World
  6. 5. Hello World

可以看到 helloworld 每隔一秒输出一行 Hello World,每行的编号依次递增。

第 3 步:注入 JVMChaos 并验证

  1. 指定返回值的 JVMChaos 内容如下:

    1. apiVersion: chaos-mesh.org/v1alpha1
    2. kind: JVMChaos
    3. metadata:
    4. name: return
    5. namespace: helloworld
    6. spec:
    7. action: return
    8. class: Main
    9. method: getnum
    10. value: '9999'
    11. mode: all
    12. selector:
    13. namespaces:
    14. - helloworld

JVMChaos 将 getnum 方法的返回值修改为数字 9999,也就是让 helloworld 的每行输出的编号都设置为 9999

  1. 注入指定返回值的 JVMChaos:

    1. kubectl apply -f ./jvm-return-example.yaml
  2. 查看 helloworld 的最新日志:

    1. kubectl -n helloworld logs -f helloworld

    日志如下所示:

    1. Rule.execute called for return_0:0
    2. return execute
    3. caught ReturnException
    4. 9999. Hello World

字段说明

参数类型说明默认值是否必填示例
actionstring表示具体的故障类型,支持 latencyreturnexceptionstressgcruleDatareturn
modestring表示选择 Pod 的方式,支持 oneallfixedfixed-percentrandom-max-percentone

关于 action 的取值的含义,可参考以下内容:

取值含义
latency增加方法延迟
return修改方法返回值
exception抛出自定义异常
stress提高 Java 进程 CPU 使用率,或者造成内存溢出(支持堆、栈溢出)
gc触发垃圾回收
ruleData设置 Byteman 配置触发故障

针对不同的 action 的值,有不同的配置项可以填写。

latency 相关参数

参数类型说明是否必填
classstring 类型Java 类的名称
methodstring 类型方法名称
latencyint 类型增加方法的延迟时间,单位为 ms
portint 类型附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程

return 相关参数

参数类型说明是否必填
classstring 类型Java 类的名称
methodstring 类型方法名称
valuestring 类型指定方法的返回值,目前支持数字和字符串类型的返回值,如果为字符串,则需要使用双引号,例如:”chaos”。
portint 类型附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程

exception 相关参数

参数类型说明是否必填
classstring 类型Java 类的名称
methodstring 类型方法名称
exceptionstring 类型抛出的自定义异常,例如:’java.io.IOException(“BOOM”)’
portint 类型附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程

stress 相关参数

参数类型说明是否必填
cpuCountint 类型增加 CPU 压力所使用的 CPU 核的数量,cpuCountmemType 中必须配置一个
memTypestring 类型内存 OOM 的类型,目前支持 “stack” 和 “heap” 两种类型,cpuCountmemType 中必须配置一个
portint 类型附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程

gc 相关参数

参数类型说明是否必填
portint 类型附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程

ruleData 相关参数

参数类型说明是否必填
ruleDatasrting 类型指定 Byteman 配置数据
portint 类型附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程

当编写规则配置文件时,你需要根据具体的 Java 程序,并参考 byteman-rule-language。例如:

  1. RULE modify return value
  2. CLASS Main
  3. METHOD getnum
  4. AT ENTRY
  5. IF true
  6. DO
  7. return 9999
  8. ENDRULE

将配置中的换行转换为换行符 “\n”,将转换后的数据设置为参数 “ruleData” 的值,如上的配置转换为:

  1. \nRULE modify return value\nCLASS Main\nMETHOD getnum\nAT ENTRY\nIF true\nDO return 9999\nENDRULE\n"