模拟 JVM 应用故障
Chaos Mesh 通过 Byteman 模拟 JVM 应用故障,主要支持以下类型的故障::
- 抛出自定义异常
- 触发垃圾回收
- 增加方法延迟
- 指定方法返回值
- 设置 Byteman 配置文件触发故障
- 增加 JVM 压力
同时,Chaos Mesh 支持对常用的服务或其 Java 客户端注入上述的故障。比如,当 MySQL Java 客户端执行指定类型的 SQL 语句(SELECT
,UPDATE
,INSERT
,REPLACE
或 DELETE
)时,你可以使用 JVM 故障注入功能在该客户端注入延迟或抛出异常。
本文主要介绍如何创建以上故障类型的 JVM 实验。
Linux 系统内核必须为 4.1 及以上版本。
使用 Dashboard 方式创建实验
-
单击实验页面中的“新的实验”按钮创建实验:
-
在“选择目标”处选择 “JVM 故障”,然后选择具体行为(如
RETURN
),最后填写具体配置:具体配置的填写方式,参考字段说明。
-
填写实验信息,指定实验范围以及实验计划运行时间:
-
提交实验。
使用 YAML 方式创建实验
下面将以指定返回值为例,展示 JVMChaos 的使用方法与效果。以下内容中涉及的 YAML 文件均可在 examples/jvm 中找到,以下步骤默认的工作路径也是在 examples/jvm
中。 默认 Chaos Mesh 安装的命名空间为 chaos-mesh
。
第 1 步:创建被测应用
Helloworld 是一个简单的 Java 应用,此处作为被测应用。被测应用定义在 example/jvm/app.yaml
中,内容如下 :
apiVersion: v1
kind: Pod
metadata:
name: helloworld
namespace: helloworld
spec:
containers:
- name: helloworld
# source code: https://github.com/WangXiangUSTC/byteman-example/tree/main/example.helloworld
# this application will print log like this below:
# 0. Hello World
# 1. Hello World
# ...
image: xiang13225080/helloworld:v1.0
imagePullPolicy: IfNotPresent
-
创建应用所属的 namespace:
kubectl create namespace helloworld
-
建立该应用 Pod:
kubectl apply -f app.yaml
-
执行
kubectl -n helloworld get pods
,预期能够观察到命名空间helloworld
中名为helloworld
的 Pod。kubectl -n helloworld get pods
预期结果如下:
kubectl get pods -n helloworld
NAME READY STATUS RESTARTS AGE
helloworld 1/1 Running 0 2m
等待 READY
成为 1/1
后,可以进行下一步。
第 2 步:观测未被注入时的行为
在注入前你可以先观测应用 helloworld
未被注入时的行为,例如:
kubectl -n helloworld logs -f helloworld
输出如下所示:
0. Hello World
1. Hello World
2. Hello World
3. Hello World
4. Hello World
5. Hello World
可以看到 helloworld
每隔一秒输出一行 Hello World
,每行的编号依次递增。
第 3 步:注入 JVMChaos 并验证
-
指定返回值的 JVMChaos 内容如下:
apiVersion: chaos-mesh.org/v1alpha1
kind: JVMChaos
metadata:
name: return
namespace: helloworld
spec:
action: return
class: Main
method: getnum
value: '9999'
mode: all
selector:
namespaces:
- helloworldJVMChaos 将
getnum
方法的返回值修改为数字9999
,也就是让helloworld
的每行输出的编号都设置为9999
。 -
注入指定返回值的 JVMChaos:
kubectl apply -f ./jvm-return-example.yaml
-
查看
helloworld
的最新日志:kubectl -n helloworld logs -f helloworld
日志如下所示:
Rule.execute called for return_0:0
return execute
caught ReturnException
9999. Hello World
字段说明
参数 | 类型 | 说明 | 默认值 | 是否必填 | 示例 |
---|---|---|---|---|---|
action | string | 表示具体的故障类型,支持 latency 、return 、exception 、stress 、gc 、ruleData 。 | 无 | 是 | return |
mode | string | 表示选择 Pod 的方式,支持 one 、all 、fixed 、fixed-percent 、random-max-percent 。 | 无 | 是 | one |
关于 action
的取值的含义,可参考以下内容:
取值 | 含义 |
---|---|
latency | 增加方法延迟 |
return | 修改方法返回值 |
exception | 抛出自定义异常 |
stress | 提高 Java 进程 CPU 使用率,或者造成内存溢出(支持堆、栈溢出) |
gc | 触发垃圾回收 |
ruleData | 设置 Byteman 配置触发故障 |
mysql | 对 MySQL Java 客户端注入故障 |
针对不同的 action
的值,有不同的配置项可以填写。
latency
相关参数
参数 | 类型 | 说明 | 是否必填 |
---|---|---|---|
class | string 类型 | Java 类的名称 | 是 |
method | string 类型 | 方法名称 | 是 |
latency | int 类型 | 增加方法的延迟时间,单位为 ms | 是 |
port | int 类型 | 附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程 | 否 |
return
相关参数
参数 | 类型 | 说明 | 是否必填 |
---|---|---|---|
class | string 类型 | Java 类的名称 | 是 |
method | string 类型 | 方法名称 | 是 |
value | string 类型 | 指定方法的返回值,目前支持数字和字符串类型的返回值,如果为字符串,则需要使用双引号,例如:"chaos"。 | 是 |
port | int 类型 | 附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程 | 否 |
exception
相关参数
参数 | 类型 | 说明 | 是否必填 |
---|---|---|---|
class | string 类型 | Java 类的名称 | 是 |
method | string 类型 | 方法名称 | 是 |
exception | string 类型 | 抛出的自定义异常,例如:'java.io.IOException("BOOM")' | 是 |
port | int 类型 | 附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程 | 否 |
stress
相关参数
参数 | 类型 | 说明 | 是否必填 |
---|---|---|---|
cpuCount | int 类型 | 增加 CPU 压力所使用的 CPU 核的数量,cpuCount 和 memType 中必须配置一个 | 否 |
memType | string 类型 | 内存 OOM 的类型,目前支持 "stack" 和 "heap" 两种类型,cpuCount 和 memType 中必须配置一个 | 否 |
port | int 类型 | 附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程 | 否 |
gc
相关参数
参数 | 类型 | 说明 | 是否必填 |
---|---|---|---|
port | int 类型 | 附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程 | 否 |
ruleData
相关参数
参数 | 类型 | 说明 | 是否必填 |
---|---|---|---|
ruleData | string 类型 | 指定 Byteman 配置数据 | 是 |
port | int 类型 | 附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程 | 否 |
当编写规则配置文件时,你需要根据具体的 Java 程序,并参考 byteman-rule-language。例如:
RULE modify return value
CLASS Main
METHOD getnum
AT ENTRY
IF true
DO
return 9999
ENDRULE
将配置中的换行转换为换行符 "\n",将转换后的数据设置为参数 "ruleData" 的值,如上的配置转换为:
\nRULE modify return value\nCLASS Main\nMETHOD getnum\nAT ENTRY\nIF true\nDO return 9999\nENDRULE\n"
mysql
相关参数
参数 | 类型 | 说明 | 是否必填 |
---|---|---|---|
mysqlConnectorVersion | string 类型 | 使用的 MySQL 客户端 (mysql-connector-java) 的版本,对于 5.X.X 版本设置为 "5" ,对于 8.X.X 版本设置为 "8" 。默认值为 "8" 。 | 否 |
database | string 类型 | 匹配的指定的库名称,默认值为 "" ,即匹配所有的库。 | 否 |
table | string 类型 | 匹配的指定的表名称,默认值为 "" ,即匹配所有的表。 | 否 |
sqlType | string 类型 | 匹配的 SQL 类型,可选值为 "select" 、"update" 、"insert" 、"replace" 、"delete" ,默认值为 "" ,即匹配所有类型的 SQL。 | 否 |
exception | string 类型 | 抛出的自定义异常信息,如 "BOOM" 。exception 和 latency 中必须配置一个。 | 否 |
latency | int 类型 | 执行 SQL 的延迟时间,单位为 ms,如 1000 。exception 和 latency 中必须配置一个。 | 否 |
port | int 类型 | 附加到 Java 进程 agent 的端口号,通过该端口号将故障注入到 Java 进程。 | 否 |