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。

相关推荐
IT学长编程5 分钟前
计算机毕业设计 Java酷听音乐系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·音乐系统·计算机毕业设计选题
IT学长编程23 分钟前
计算机毕业设计 基于协同过滤算法的个性化音乐推荐系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·毕业论文·协同过滤算法·计算机毕业设计选题·个性化音乐推荐系统
小小娥子27 分钟前
Redis的基础认识与在ubuntu上的安装教程
java·数据库·redis·缓存
几何心凉35 分钟前
已解决:org.springframework.web.HttpMediaTypeNotAcceptableException
java
华农第一蒟蒻37 分钟前
Java中JWT(JSON Web Token)的运用
java·前端·spring boot·json·token
两点王爷39 分钟前
使用WebClient 快速发起请求(不使用WebClientUtils工具类)
java·网络
计算机学姐1 小时前
基于SpringBoot+Vue的高校运动会管理系统
java·vue.js·spring boot·后端·mysql·intellij-idea·mybatis
平凡的小码农1 小时前
JAVA实现大写金额转小写金额
java·开发语言
一直在进步的派大星1 小时前
Docker 从安装到实战
java·运维·docker·微服务·容器
老华带你飞1 小时前
公寓管理系统|SprinBoot+vue夕阳红公寓管理系统(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·spring boot·课程设计