前言
chaosblade是一个混沌实验实施工具,其组件可以大致分为两部分:
1)白屏控制台:chaosblade-box和chaosblade-box-agent;
2)黑屏命令行:chaosblade,blade命令行工具,对于不同实验类型底层依赖众多组件,如k8s实验依赖chaosblade-operator,jvm实验依赖chaosblade-exec-jvm;
由于官方文档比较匮乏,而chaosblade组件众多,遇到问题难以定位,也难以入手做改造,所以看源码的方式了解一下细节。
本章基于chaosblade-1.7.4版本分析chaosblade黑屏命令行的实现原理。
后续将分析chaosblade-box白屏控制台,chaosblade-exec-jvm和jvm-sandbox。
一、项目结构
1、构建产物
1)blade:chaosblade-io/chaosblade主项目构建产物,Go编写,命令行工具,为混沌实验提供统一入口,管理所有混沌实验;
2)bin/lib/yaml:chaosblade子项目构建产物
2-1)bin,可执行程序,如chaos_os,由chaosblade-io/chaosblade-exec-os构建,Go编写,命令行工具,封装了基础资源故障场景,支持的组件包括 CPU、内存、网络等;
2-2)lib,复杂的可执行程序,对目录相对路径有一定要求,如sandbox由chaosblade-io/chaosblade-exec-jvm构建,Java编写,基于jvm-sandbox,通过javaagent做故障注入;
2-3)yaml,不同子项目提供的描述文件,blade通过解析yaml匹配需要执行的实验。
如chaosblade-exec-os提供chaosblade-os-spec-1.7.4.yaml,blade通过解析得到cpu满载实验和其相关参数。
2、构建脚本
chaosblade通过Makefile构建:
1)pre_build:创建目录等操作;
2)cli:构建chaosblade主项目;
3)os~kubernetes:构建子项目;
4)package:打压缩包;
主要看子项目构建,以os为例:
1)如果target/cache目录下不存在os子项目,则git clone下载;
2)git pull更新目标分支;
3)make编译chaosblade-exec-os;
4)将构建产物分别放入bin目录和yaml目录;
通过修改project和branch可以指向自己的仓库进行定制开发。
3、工程结构
chaosblade主项目分为三个模块:
1)cli:blade命令入口,cli/cmd子目录对应blade子命令,如prepare、create、status;
2)data:数据存储模块,chaosblade使用****sqlite存储实验数据,experiment表对应create,preparation表对应prepare;
3)exec:底层命令执行模块,比如os执行chaos_os;
4、小总结
chaosblade命令行集成了n个子项目对于混沌实验场景的实现,通过blade命令统一管理,用sqlite本地存储实验状态。
二、os实验-cpu满载
1、案例
通过create子命令创建cpu满载实验。
plain
root@service-e:/opt/chaosblade# ./blade create cpu fullload
{"code":200,"success":true,"result":"b0ad36cdedce219c"}
通过status子命令查询实验信息。
plain
root@service-e:/opt/chaosblade# ./blade status --type=create
{
"code": 200,
"success": true,
"result": [
{
"Uid": "b0ad36cdedce219c",
"Command": "cpu",
"SubCommand": "fullload",
"Flag": "",
"Status": "Success",
"Error": ""
}
]
}
通过top命令查看cpu使用率。
通过destroy子命令删除实验,需要指定实验唯一id。
plain
root@service-e:/opt/chaosblade# ./blade destroy b0ad36cdedce219c
{"code":200,"success":true,"result":{"target":"cpu","action":"fullload","ActionProcessHang":false}}
2、create-创建实验
cli/cmd/create.go#actionRunEFunc:当匹配到create子命令,执行actionRunEFunc定义的回调方法。
Step1,创建实验模型ExpModel,一个普通对象。
target=cpu,actionCommandSpec.Name=fullload,cmd包含执行参数。
Step2,实验模型持久化到sqlite。
在持久化到db前,由blade侧生成uid。
首次获取db连接,会创建两张表(preparation和experiment),数据文件存储在blade命令行所在目录chaosblade.dat文件。
experiment实验记录如下,如设置cpu负载为60%。
Step3,blade根据cpu和fulllload找底层实验实现执行。
exec/os/executor.go:blade实际调用底层chaos_os命令,启动chaos_os进程消耗cpu。
plain
root@service-e:/opt/chaosblade# ps -efww | grep chaos_os
root 680 1 99 01:39 pts/0 00:00:53 /opt/chaosblade/bin/chaos_os /opt/chaosblade/bin/chaos_os create cpu fullload --uid=9eb0176ff57ba279 --cpu-percent=60
exec/cpu/cpu.go:245:chaos_os侧开启cpu数量个协程执行死循环,main线程根据当前系统cpu使用率,调整协程死循环速率。
exec/cpu/cpu.go:333:chaos_os每个gorouting做死循环。
Step4,根据执行情况,更新实验记录。
如果命令执行失败,更新实验记录Error。
如果命令执行成功,校验chaos_os进程是否存在(response.Result是pid)。
如果存在,更新实验记录Success,否则更新Error。
3、status-查询实验
cli/cmd/status.go:71:可根据type类型或根据uid查询实验。
如果type=create/destroy,查询experiment表;如果type=prepare/revoke,查询preparation表。
4、destroy-销毁实验
cli/cmd/destroy.go:70:Step1,根据uid查询实验。
cli/cmd/destroy.go:142:Step2,根据实验模型找到os命令执行器。
cli/cmd/destroy.go:200:Step3,blade执行chaos_os命令,如果执行成功更新实验记录为Destroyed,否则不变。
chaos_os命令如:
plain
./chaos_os destroy cpu fullload --uid=aa3bbfea430b86e1 --cpu-percent=60
exec/exec.go:31:chaos_os根据chaos_os+uid匹配进程号(这也是为什么create要传入uid,只是为了销毁能根据uid匹配进程),执行kill -9杀死chaos_os cpu满载实验进程。
三、jvm实验-自定义异常
1、chaosblade-exec-jvm项目结构
从构建产物来说,chaosblade-exec-jvm完全基于sandbox-jvm开发。
构建脚本:
-
pre_build:下载sandbox的release包解压并缓存;
-
build_java:构建当前chaosblade-exec-jvm项目;
工程结构:
chaosblade-exec-bootstrap-jvmsandbox:依赖sandbox-core基于jvmsandbox开发,依赖和chaosblade-exec-service提供服务实现;
chaosblade-exec-service:提供jvm混沌实验的各方法实现,如create、destroy,依赖common模块;
chaosblade-exec-common:公共模块,包括通用模型定义、基础实验实现(异常、延迟)、插件SPI等,无三方依赖;
chaosblade-exec-plugin:依赖common模块,提供不同插件SPI实现;
2、案例
执行prepare命令,对目标java进程安装jvm-sandbox。(实际上跳过prepare,直接create也可以,这里分开执行作为演示)
plain
root@service-e:/opt/chaosblade# ./blade prepare jvm --pid 1
{"code":200,"success":true,"result":"ef8a0f527a3fcf82"}
执行status命令查询prepare情况,这里的port是jvm-sandbox暴露的http端口。
plain
root@service-e:/opt/chaosblade# ./blade status --type prepare
{
"code": 200,
"success": true,
"result": [
{
"Uid": "ef8a0f527a3fcf82",
"ProgramType": "jvm",
"Process": "",
"Port": "42135",
"Pid": "1",
"Status": "Running",
"Error": ""
}
]
}
执行create命令创建实验,这里创建自定义异常实验。
plain
root@service-e:/opt/chaosblade# ./blade create jvm throwCustomException --exception java.lang.Exception --exception-message "cause by blade" --classname com.xxx.TestJob --methodname test
{"code":200,"success":true,"result":"3da11cd6d3294f4d"}
业务代码:
查看日志:
plain
{"timestamp":"xxx","traceId":"N/A","thread":"scheduling-6","level":"ERROR","loggerName":"org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler","message":"Unexpected error occurred in scheduled task","thrown":{"message":"cause by blade","name":"java.lang.Exception","extendedStackTrace":"java.lang.Exception: cause by blade\n\tat sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)\n\tat sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)\n\tat sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)\n\tat java.lang.reflect.Constructor.newInstance(Constructor.java:423)\n\tat com.alibaba.chaosblade.exec.common.model.action.exception.DefaultThrowExceptionExecutor.instantiateException(DefaultThrowExceptionExecutor.java:127)"}
}
执行destroy命令销毁实验:
plain
root@service-e:/opt/chaosblade# ./blade destroy 3da11cd6d3294f4d
{"code":200,"success":true,"result":{"target":"jvm","action":"throwCustomException","flags":{"classname":"com.xxx.TestJob","exception":"java.lang.Exception","exception-message":"cause by blade","methodname":"test"},"ActionProcessHang":false}}
执行revoke命令,关闭jvm-sandbox。
plain
root@service-e:/opt/chaosblade# ./blade revoke ef8a0f527a3fcf82
{"code":200,"success":true,"result":"success"}
执行status命令,查看prepare状态变为Revoked。
plain
root@service-e:/opt/chaosblade# ./blade status ef8a0f527a3fcf82
{
"code": 200,
"success": true,
"result": {
"Uid": "ef8a0f527a3fcf82",
"ProgramType": "jvm",
"Process": "",
"Port": "42135",
"Pid": "1",
"Status": "Revoked",
"Error": ""
}
}
3、prepare-准备实验
cli/cmd/prepare_jvm.go:85,prepare和create类似,由chaosblade通过sqlite记录preparation数据,底层交给具体实验场景实现,如调用jvm-sandbox。
preparation记录如下:
-
uid:由blade生成,同create;
-
programType:jvm场景;
-
process:通过--process传入;
-
pid:通过--pid传入或--process匹配进程得到;
-
port:jvm-sandbox的http端口,默认随机端口,可通过--port指定;
-
status:状态;
cli/cmd/prepare_jvm.go:151,核心逻辑在于attachAgent。
exec/jvm/sandbox.go:43,attachAgent分为三步
-
attach:启动sandbox;
-
active:激活chaosblade模块;
-
check:校验sandbox启动成功;
3-1、attach
exec/jvm/sandbox.go:90,attach阶段执行了两个命令。
第一个命令,挂载****sandbox-agent ,注意namespace=chaosblade,token由blade自己生成,生成方式是date | head | cksum | sed 's/ //g'
根据日期生成的校验码。
plain
java -Xms128M -Xmx128M -Xnoclassgc -ea -Xbootclasspath/a:{JAVA_HOME}/lib/tools.jar -jar /path/to/chaosblade/lib/sandbox/lib/sandbox-core.jar 95431 "/path/to/chaosblade/lib/sandbox/lib/sandbox-agent.jar" "home=/path/to/chaosblade/lib/sandbox;token=78590791129;server.ip=127.0.0.1;server.port=64455;namespace=chaosblade"
这段逻辑和jvm-sandbox提供的sandbox.sh是一样的。
从jvm-sandbox角度来看,执行结果如下:
-
启动sandbox三个核心模块,包括sandbox-info、sandbox-control、sandbox-module-mgr,后面会用到control和mgr模块;
-
加载chaosblade模块,但处于FROZEN状态,增强插装存在,但不会执行任何逻辑;
plain
./sandbox.sh -p java进程id -n chaosblade -l
sandbox-info ACTIVE LOADED 0 0 0.0.4 luanjia@taobao.com
sandbox-module-mgr ACTIVE LOADED 0 0 0.0.2 luanjia@taobao.com
chaosblade FROZEN LOADED 0 0 1.7.4 Changjun Xiao
sandbox-control ACTIVE LOADED 0 0 0.0.3 luanjia@taobao.com
第二个命令,校验token文件存在。
plain
grep 78590791129 {USER_HOME}/.sandbox.token | grep 命名空间chaosblade | tail -1 | awk -F ";" '{print $3";"$4}'
localhost;64455
与sandbox.sh一致。
如果sandbox挂载成功,会在家目录下生成.sandbox.token文件。
每行格式为:namespace命名空间;token;sandbox http服务绑定ip;sandbox http服务绑定端口。
3-2、active
exec/jvm/sandbox.go:75,active通过http调用sandbox-module-mgr 模块的active 端点,激活chaosblade模块。
实际执行命令类似于。
plain
curl http://127.0.0.1:64455/sandbox/chaosblade/module/http/sandbox-module-mgr/active?ids=chaosblade
等同于</font><font style="color:#333333;">sandbox.sh -p java进程id -n 命名空间chaosblade -a 模块名chaosblade</font><font style="color:#333333;">
。
注:sandbox.sh可以通过.sandbox.token拿到http服务ip和port。
经过active后,查看模块列表,chaosblade被开启。
plain
bin % ./sandbox.sh -p 95431 -n chaosblade -l
sandbox-info ACTIVE LOADED 0 0 0.0.4 luanjia@taobao.com
sandbox-module-mgr ACTIVE LOADED 0 0 0.0.2 luanjia@taobao.com
chaosblade ACTIVE LOADED 0 0 1.7.4 Changjun Xiao
sandbox-control ACTIVE LOADED 0 0 0.0.3 luanjia@taobao.com
total=4
3-3、check
exec/jvm/sandbox.go:60,激活完成后,调用chaosblade/status端点,如果http响应200即成功。
等同于命令:
plain
curl http://127.0.0.1:64455/sandbox/chaosblade/module/http/chaosblade/status?1=1
这是chaosblade-exec-jvm扩展sandbox提供的接口,用于根据jvm实验id查询实验状态。
4、create-创建实验
blade status --type prepare查询prepare状态就是查询preparation表,不过多分析。
创建实验在上面cpu满载实验分析过了,chaosblade负责记录实验状态,只需要分析底层调用实验的不同实现。
如jvm方法异常实验记录:
exec/jvm/executor.go:61,根据SELECT * FROM preparation WHERE program_type = "jvm" and status = "Running"
查询prepare记录。
exec/jvm/executor.go:61,如果没有prepare记录,这里会走一次prepare流程,所以create前没挂载sandbox也可以。
exec/jvm/executor.go:61,prepare如果完成,组装http请求调用sandbox创建实验。
exec/jvm/executor.go:180,请求端点</font>**<font style="color:#333333;">/sandbox/chaosblade/module/http/chaosblade/create</font>**<font style="color:#333333;">
,其中suid即为创建实验uid。
比如请求体为:
SandboxModule#create:chaosblade-exec-jvm提供创建jvm实验实现。
CreateHandler#handle:chaosblade-exec-jvm解析请求为一个Model。
MatcherModel包含需要对哪个类方法进行插装,ActionModel包含具体实验实现方式。
SandboxModule#add:chaosblade-exec-jvm将目标切点(Pointcut) 和增强逻辑(Enhancer) 封装为PluginBean,最终调用jvm-sandbox的ModuleEventWatcher#watch方法。
DefaultModuleEventWatcher#watch:简单看一下sandbox如何对目标方法插装。
sandbox在安装时会将Instrumentation缓存下来,在这个时候能动态执行class增强。
1)创建SandboxClassFileTransformer实现Transformer,用于回调用户增强逻辑;
2)将SandboxClassFileTransformer安装到Instrumentation;
3)根据用户提供的matcher找到需要增强的class;(通过Instrumentation#getAllLoadedClasses扫描所有被加载的class)
4)执行Instrumentation#retransformClasses对目标class触发retransform;
增强后的class如下,这都是jvm-sandbox的实现。
5、destroy-销毁实验
销毁实验在上面cpu满载实验分析过了,chaosblade负责维护实验状态,只需要分析底层调用实验的不同实现。
exec/jvm/executor.go:61,销毁与创建的主要区别仅仅在于调用不同的sandbox接口。
最终调用sandbox的命令如:
plain
curl http://127.0.0.1:26666/sandbox/chaosblade/module/http/chaosblade/destroy?suid=50745cd203a67038
SandboxModule#destroy:chaosblade-exec-jvm接收destroy请求。
SandboxModule#delete:根据suid找到先前的实验模型,调用最终调用jvm-sandbox的ModuleEventWatcher#delete方法,移除增强插装。
DefaultModuleEventWatcher#delete:移除增强插装
1)根据watchId查询SandboxClassFileTransformer;
2)Instrumentation#removeTransformer,移除目标Transformer;
3)和create一样,根据用户提供的matcher找到需要增强的class;(通过Instrumentation#getAllLoadedClasses扫描所有被加载的class)
4)对这些class执行Instrumentation#retransformClasses重新执行所有Transformer;
销毁后目标class恢复原状。
6、revoke-撤销实验
revoke是prepare的反向操作,对目标jvm进程卸载jvm-sandbox。
cli/cmd/revoke.go:50,step1,根据uid查询prepare记录。
cli/cmd/revoke.go:50,step2,根据prepare类型,调用不同实现。
cli/cmd/revoke.go:50,step3,根据调用结果,如果成功,记录更新为Revoked,如果失败,状态不变,仅记录失败原因。
exec/jvm/sandbox.go:302,对于jvm实验,调用sandbox的sandbox-control/shutdown端点。
命令如下:
plain
curl http://127.0.0.1:26666/sandbox/chaosblade/module/http/sandbox-control/shutdown?1=1
ControlModule#shutdown:shutdown端点由jvm-sandbox提供,关闭http服务回收所有资源。
四、 k8s实验
1、 案例
使用k8s实验需要安装chaosblade-operator。
plain
helm repo add chaosblade-io https://chaosblade-io.github.io/charts
helm install chaosblade chaosblade-io/chaosblade-operator --namespace chaosblade
安装后有三个组件:
chaosblades.chaosblade.io:crd,定义chaosblade资源,一个chaosblade代表一个实验;
chaosblade-operator:deployment,根据crd管理k8s实验状态;
chaosblade-tool:daemonset,为operator提供服务,部分实验需要用到,如node cpu满载;
k8s实验支持三种类型:
1)node:k8s节点级别实验;
node cpu满载实验。
names指定node资源名,kubeconfig:通过本地kubeconfig文件构建k8s客户端。
plain
% blade create k8s node-cpu fullload --names k8s节点名 --kubeconfig ~/.kube/config --cpu-percent 80
{"code":200,"success":true,"result":"ef0e8286411c007e"}
2)pod:k8s pod级别实验;
pod cpu满载实验。
plain
% blade create k8s pod-cpu load --kubeconfig ~/.kube/config --names pod名
3)container:k8s container级别实验;
container-jvm自定义异常实验。
plain
% blade create k8s container-jvm throwCustomException --exception java.lang.Exception --classname com.example.controller.DubboController --methodname sayHello --kubeconfig ~/.kube/config --names pod名 --container-index 0 --process 进程名
查询chaosblade资源,资源名是实验id ,Status.Exp Statuses.Res Statuses.Id****是底层blade实验id,Phase=Running,实验正常运行。
plain
% kubectl describe chaosblade ef0e8286411c007e
Name: ef0e8286411c007e
API Version: chaosblade.io/v1alpha1
Kind: ChaosBlade
Metadata:
Finalizers:
finalizer.chaosblade.io
Generation: 1
Resource Version: 4480637
UID: c0995a59-3e57-4b51-be9b-42f039771c91
Spec:
Experiments:
Action: fullload
Desc: created by blade command
Matchers:
Name: cpu-percent
Value:
80
Name: names
Value:
node名
Name: cgroup-root
Value:
/host-sys/fs/cgroup
Scope: node
Target: cpu
Status:
Exp Statuses:
Action: fullload
Res Statuses:
Id: 14c140521e603e19 --- 实际底层blade实验id
Identifier: /node名/
Kind: node
State: Success
Success: true
Scope: node
State: Success
Success: true
Target: cpu
Phase: Running
销毁k8s实验。
plain
% blade destroy ef0e8286411c007e
{"code":200,"success":true,"result":{"target":"node-cpu","action":"fullload","flags":{"cpu-percent":"80","kubeconfig":"{home}/.kube/config","names":"k8s节点名"},"ActionProcessHang":false}}
查询chaosblade资源。
plain
% kubectl describe chaosblade ef0e8286411c007e
Error from server (NotFound): chaosblades.chaosblade.io "ef0e8286411c007e" not found
2、create-创建实验
在blade创建实验记录后,调用k8s实现执行实验创建。
exec/kubernetes/executor.go:121,blade根据kubeconfig创建k8s客户端,调用apiserver创建crd资源。
exec/kubernetes/executor.go:196,blade将命令行实验模型转换为chaosblade crd资源模型,调用apiserver创建chaosblade资源,创建完成后查询chaosblade资源是否创建成功。
后续流程由chaosblade-operator完成。
pkg/controller/chaosblade/controller.go:77,chaosblade-operator监听chaosblade资源。
pkg/controller/chaosblade/controller.go:185,Reconcile循环根据chaosblade资源状态,走不同逻辑处理。
pkg/controller/chaosblade/controller.go:185,一开始chaosblade的status.phase=空,调用apiserver更新chaosblade的****Metadata.Finalizers=finalizer.chaosblade.io,待后续chaosblade资源删除后执行删除逻辑,更新status.phase=Initialized。
pkg/controller/chaosblade/controller.go:185,status.phase=Initialized,循环spec下所有实验定义,创建实验,更新status.phase=Running,至此创建实验流程结束。
实验分为三种。
node、pod、container匹配容器列表ContainerMatchedList逻辑不同。
exec/node/controller.go:48,如node,根据names调用apiserver查询节点。
exec/model/controller.go:63,最终都调用父类Exec方法,匹配实验action执行。
有5种action实现,内部有n多分支逻辑,通过chaosblade-operator的日志来分析吧。
node cpu满载 ,调用apiserver exec,在目标node的chaosblade-tool容器中,执行blade create cpu fullload,会返回底层实验id,最终更新到chaosblade资源的Status.Exp Statuses.Res Statuses.Id属性中。
plain
level=info msg="Exec command in pod" command="[/opt/chaosblade/blade create cpu fullload --cpu-percent=20 --cgroup-root=/host-sys/fs/cgroup]" container=chaosblade-tool podName=chaosblade-tool-shdd2 podNamespace=chaosblade
level=info msg="get output message" command="[/opt/chaosblade/blade create cpu fullload --cpu-percent=20 --cgroup-root=/host-sys/fs/cgroup]" container=chaosblade-tool err= out="{\"code\":200,\"success\":true,\"result\":\"a55f9b1b4545965d\"}" podName=chaosblade-tool-shdd2 podNamespace=chaosblade
pod cpu满载,调用apiserver exec,在pod所在node的chaosblade-tool容器中,执行blade create cri cpu fullload。基于cri(Container Runtime Interface),本文没分析。
plain
msg="Exec command in pod" command="[/opt/chaosblade/blade create cri cpu fullload --cgroup-root=/host-sys/fs/cgroup --container-id 7c498c0776ed --container-runtime docker]" container=chaosblade-tool podName=chaosblade-tool-shdd2 podNamespace=chaosblade
jvm实验,调用apiserver exec,在目标container执行blade create jvm命令。
plain
msg="Exec command in pod" command="[/opt/chaosblade/blade create jvm throwCustomException --methodname=sayHello --classname=com.example.controller.DubboController --exception=java.lang.Exception --process=app.jar]" container=service-e-k2 podName=service-e-6bf785f74d-q927z podNamespace=default
如果目标container没有blade,chaosblade-operator会把自己的blade传输给目标container。
plain
msg="Exec command in pod" command="[test -e /opt/chaosblade/blade]" container=service-e-k2 podName=service-e-6bf785f74d-q927z podNamespace=default
msg="Invoke exec command error" command="[test -e /opt/chaosblade/blade]" container=service-e-k2 err= error="command terminated with exit code 1" out= podName=service-e-6bf785f74d-q927z podNamespace=default
msg="Exec command in pod" command="[tar --no-same-permissions --no-same-owner -xmf - -C /opt/chaosblade]" container=service-e-k2 podName=service-e-6bf785f74d-q927z podNamespace=default
2、 destroy-销毁实验
exec/kubernetes/executor.go:352,blade侧根据uid调用apiserver销毁chaosblade资源。
pkg/controller/chaosblade/controller.go:185,chaosblade-operator侧发现DeletionTimestamp不为空,执行finalize逻辑。
exec/model/executor.go:295,最终调用apiserver通过exec对目标container执行blade destroy命令。
exec/model/executor.go:46,调用销毁后,开协程查询销毁情况。
exec/model/executor.go:46,向chaosblade-tool所在pod执行blade status 实验id查询实验状态,当blade status状态变更为Destroyed,更新chaosblade资源状态为Destroyed。
pkg/controller/chaosblade/controller.go:185,最终Reconcile循环将chaosblade的Finalizer移除,完成chaosblade删除。
总结
blade命令行用于管理混沌实验,使用sqlite存储实验状态,数据文件在blade同级目录的chaosblade.dat文件中。
常用命令包括:
1)blade create,创建实验;
2)blade destroy,销毁实验,与create对应;
3)blade status,查询实验;
4)blade prepare,准备实验,只有jvm和cpp有,在create前需要做准备;
5)blade revoke,撤销实验,与prepare对应;
根据实验类型调用不同实验实现。
1、os实验
os实验由chaosblade-exec-os子项目实现。
chaosblade-exec-os编译后生成chaos_os可执行程序。
create实验,如cpu满载,blade create cpu fullload,会创建chaos_os子进程。
chaos_os子进程根据fullload指令找到实际实现,根据阈值死循环消耗cpu。
plain
/opt/chaosblade/bin/chaos_os /opt/chaosblade/bin/chaos_os create cpu fullload --uid=aa3bbfea430b86e1 --cpu-percent=60
destroy实验,调用chaos_os。
chaos_os根据chaos_os+uid匹配进程号,执行kill -9杀死chaos_os cpu满载实验进程。
plain
/opt/chaosblade/bin/chaos_os destroy cpu fullload --uid=aa3bbfea430b86e1 --cpu-percent=60
2、jvm实验
jvm实验由chaosblade-exec-jvm子项目实现,chaosblade-exec-jvm基于jvm-sandbox扩展。
jvm实验需要经过prepare->create->destroy->revoke流程。
prepare
prepare实验,如blade prepare jvm --pid 1,分为三步:
1)attach:挂载jvm-sandbox,挂载后目标jvm进程会打开sandbox http服务,用于接收后续请求,类似于执行(实际上是把sandbox.sh中相关命令通过blade管理了):
plain
sandbox.sh -p jvm进程号
如果sandbox挂载成功,会在家目录下生成.sandbox.token文件,通过
每行格式为:namespace命名空间;token;sandbox http服务绑定ip;sandbox http服务绑定端口。
2)active:调用sandbox-module-mgr模块的active端点,激活chaosblade模块。
plain
curl http://127.0.0.1:{sandbox端口}/sandbox/{命名空间=chaosblade}/module/http/sandbox-module-mgr/active?ids=chaosblade
3)check:调用chaosblade模块的status端点,这个端点是查询实验状态的端点,这里只判断http响应码200。
plain
curl http://127.0.0.1:{sandbox端口}/sandbox/{命名空间=chaosblade}/module/http/chaosblade/status?1=1
create
create实验,如blade create jvm throwCustomException自定义异常。
blade查询prepare记录得到jvm-sandbox的http端口。
blade调用**/sandbox/chaosblade/module/http/chaosblade/create**端点,请求体如:
chaosblade-exec-jvm侧,根据入参调用jvm-sandbox的ModuleEventWatcher#watch方法,对目标class进行retransform。
jvm-sandbox在prepare安装时会将Instrumentation缓存下来,watch方法逻辑大致是:
1)创建Transformer用于回调用户增强逻辑;
2)Instrumentation#addTransformer,安装Transformer;
3)Instrumentation#getAllLoadedClasses循环所有被加载class,匹配需要增强的class;
4)Instrumentation#retransformClasses对目标class触发retransform;
destroy
destroy实验,如blade destroy create实验id。
blade调用chaosblade模块的destroy端点。
plain
curl http://127.0.0.1:{sandbox端口}/sandbox/{命名空间=chaosblade}/module/http/chaosblade/destroy?suid=create实验id
chaosblade-exec-jvm侧,根据suid找到先前缓存的实验模型,调用最终调用jvm-sandbox的ModuleEventWatcher#delete方法移除class增强。
jvm-sandbox的逻辑大致是:
1)根据id查询对应Transformer;
2)Instrumentation#removeTransformer,移除Transformer;
3)Instrumentation#getAllLoadedClasses循环所有被加载class,匹配需要增强的class;
4)Instrumentation#retransformClasses对目标class触发retransform;
revoke
revoke实验,如blade revoke prepare实验id。
blade调用sandbox-control模块的shutdown端点。
plain
curl http://127.0.0.1:{sandbox端口}/sandbox/{命名空间=chaosblade}/module/http/sandbox-control/shutdown?1=1
jvm-sandbox关闭http服务,回收所有资源。
3、 k8s实验
k8s实验依赖三个组件:
chaosblades.chaosblade.io:crd,定义chaosblade资源,一个chaosblade代表一个实验;
chaosblade-operator:deployment,根据crd管理实验状态;
chaosblade-tool:daemonset,为operator提供服务,部分实验需要用到,如node cpu满载;
blade命令对于k8s实验大致交互如下。
blade命令调用apiserver创建或删除chaosblade资源。
chaosblade-operator监听chaosblade资源变更,维护chaosblade状态,调用目标容器执行真实blade命令。
create
blade create k8s,创建实验。
blade请求apiserver创建chaosblade资源,chaosblade资源名即实验id。
chaosblade资源刚创建,operator对chaosblade增加finalizer用于拦截chaosblade销毁,状态变更为Initialized。
当状态变更为Initialized,operator通过apiserver调用目标容器执行实验,状态变更为Running。
1)node cpu满载,在目标node的chaosblade-tool容器中,执行blade create cpu fullload。
2)pod cpu满载,在pod所在node的chaosblade-tool容器中,执行blade create cri cpu fullload。
3)jvm实验 ,在目标container执行blade create jvm命令。如果目标container没有blade,chaosblade-operator会把自己的blade传输给目标container。
所以整个流程中blade会执行两次,一次是在创建实验的客户端调用blade create k8s创建chaosblade资源,另一次是operator转发到目标容器执行blade create cpu/cri/jvm等真实命令。
destroy
blade destroy 实验id,销毁实验。
blade请求apiserver删除chaosblade资源,资源名即实验id。
operator 发现chaosblade的删除时间不为空,执行finalizer逻辑,通过apiserver调用目标容器中的blade destroy命令销毁底层实验,最后去除finalizer。