ChaosBlade源码(一)blade命令行

前言

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开发。

构建脚本:

  1. pre_build:下载sandbox的release包解压并缓存;

  2. 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记录如下:

  1. uid:由blade生成,同create;

  2. programType:jvm场景;

  3. process:通过--process传入;

  4. pid:通过--pid传入或--process匹配进程得到;

  5. port:jvm-sandbox的http端口,默认随机端口,可通过--port指定;

  6. status:状态;

cli/cmd/prepare_jvm.go:151,核心逻辑在于attachAgent。

exec/jvm/sandbox.go:43,attachAgent分为三步

  1. attach:启动sandbox;

  2. active:激活chaosblade模块;

  3. 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角度来看,执行结果如下:

  1. 启动sandbox三个核心模块,包括sandbox-info、sandbox-control、sandbox-module-mgr,后面会用到control和mgr模块;

  2. 加载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资源,资源名是实验idStatus.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。

相关推荐
枫叶落雨2221 天前
ShardingSphere 介绍
java
GetcharZp1 天前
告别 TCP 握手延迟!让你的 Go 服务瞬间拥抱 HTTP/3 时代
后端
花花鱼1 天前
Spring Security 与 Spring MVC
java·spring·mvc
oak隔壁找我1 天前
SpringBoot 将项目打包成 Fat JAR(肥包),核心原理
后端
言慢行善1 天前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星1 天前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟1 天前
操作系统之虚拟内存
java·服务器·网络
Tong Z1 天前
常见的限流算法和实现原理
java·开发语言
凭君语未可1 天前
Java 中的实现类是什么
java·开发语言