PingCAP R&D云原生混沌工程专家周志强带来《云原生混沌工程平台Chaos Mesh原理与实践》精彩议题,演讲结合PingCAP近几年来在混沌工程方向的探索,从混沌工程的概念出发,讲述如何从零开始构建云原生混沌工程平台Chaos Mesh以及云原生对混沌工程带来的机遇和挑战。下方是亚马逊和NETFLIX服务依赖的一个结构图,正常人都不可能完全去了解这么复杂的系统。同时这么复杂系统后面肯定会有非常薄弱的环节。而且这些薄弱的环节随时都会都有可能发生故障。定义:混沌工程是一门新兴的技术学科,他的初衷是通过实验性的方法,让人们建立对于复杂分布式系统在生产中抵御突发事件能力的信心。
混沌工程和这些传统测试方法的主要区别在于:混沌工程是发现新信息的实践过程,而故障注入则是对一个特定的条件、变量的验证方法。
定义:云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展 的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API。这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。
但是其中就会有一个问题:我们如何验证这种为失败设计和优雅降级,它同时是符合预期工作的呢?这个时候我们就需要我们可以运用混沌工程来验证这一点。比较简单的一句话来说,混沌工程就是通过失败来避免失败。混沌工程还有故障演练,我自己认为混沌过程和故障演练存在区别的。故障演练是一段时间去做一次,但是混沌工程应该是一个不断持续的闭环。首先我们说我们建立一个稳态,然后在稳态上面做一个假设,比如说我们一个系统中的几个机器,我们直接把它去掉,那么这个系统它会不会 down掉或者说它是否还能按照预期正常工作,或者是降级到一个预期的降级状态。以上是我们做的假设,然后我们进行混沌实验。混沌实验执行后,对假设进行验证,在验证以后可能得到一个积极或者消极的结果,根据这个结果我们再去提升我们当前的应用。这也就意味着后面工程,并其实不是说大家凑在一块,做一次大规模的故障演练就结束了,而是需要测试运维以及开发全部一起来完成一项非常繁重的工程。我们平台在开始之初就是非常注重混沌工程。因为我们做的是分布式的一个数据库,最开始是也是人肉直接拔网线的方式模拟网络故障。后来我们内部也已经出了一个叫做薛定鄂的系统。薛定鄂的系统其实是跑了一堆测试在上面,在跑这些测试的同时给它注入各种故障,来验证这个系统它真的是work的。再到后来,从薛定鄂中我们孵化出了Chaos Mesh。Chaos Mesh是一个开源的云原生混沌工程平台,它其实目前的主要功能是是支持混沌实验的注入与编排,同时易用,可视化操作。可以发现和刚才提到应用层面的混沌注入不太一样,我们这边主要是注重于 note级别的一些比如说 pod-kill.然后网络还可以提到文件系统、时间的偏移、cpu-burn 的一些压力等等。现在还在加入一些对于云厂商的支持;还有也在支持JVMChaos,到了比较硬核的环节。我们看到加红色的部分提到了几个点,就是容器不可变,基础设施和声明式的API,这三点我觉得是目前我们开始在设计和实践中比较沾光的几个地方。- 容器:容器它是用来资源的隔离,然后我们也可以用它来限制爆炸半径
- 其实在 API的设计上下了很大的功夫,可以让用户真正描述出真正想要的东西。
这是Chaos Mesh的一个整体架构,最上面是user,然后最下面是我们真正一个节点,可以看到用户通过 kubectl apply、 client apply还有Kubernetes API server可以输入他预期想要的case,然后也可以通过这两个组件来观测它当前的case的状态。往下是Chaos Controller Manager,他监听资源的变化,然后决定现在或者说以后要不要进行故障的注入以及故障的恢复。然后再往下一层是Chaos Daemon,负责在具体节点上进行故障注入。2.6 使用 Kubernetes API/Client2.7 Chaos Dashboard和资源变化的监听友好的用户界面、接入 RBAC 的权限管控、方便管理和观察已有的错误。Reconcile 以 ReplicaSet 为例
下一个比较难做的其实是如何进入 namespace, namespace会影响查询的过程。mnt namespace:影响 path resolutionpid namespace:影响 process lookupnet namespace:影响 network device lookup举个例子,如果mnt namespace不一样的话,我们在里面看到的文件是不一样的;pid namespace不一样的话,我们看到的进程是不一样的。net namespace不一样的话,我们看到的那些网卡那些网络设备是不一样的。3.2 Linux namespace and Go Don’t Mix而且还有一个问题就是Linux namespace和Go其实兼容性不是很好。你会发现,明明已经把goroutine已经绑定了,但是被绑定的模式还是去克隆了另外一个thread出来, thread继承了以前namespace,这样就会造成 Namespace的泄露。Namespace 泄漏会导致其他线程的 Namespace 难以追踪。如果禁止,Go进程将会变得十分不可控,所以这样是不可行的。那么有没有其他的方式实现?当然是有的。信号处理较为随意,难以跟踪子进程;难以应对 mnt namespace 的情形:nsenter --mnt /proc/xxx/ns/mnt catnsenter: failed to execute cat: No such file or directory,这一情形在 Distroless 容器逐渐流行的今天更加普遍了。nsexec --net=/proc/xxx/ns/net iptables -A …..nsexec --net=/proc/xxx/ns/net tc qdisc add ….nsexec --pid=/proc/xxx/ns/pid stress-ng …nsexec --mnt=/proc/xxx/ns/mnt inject ...我们举一个以网络流量入方向的限流为例,这个名字叫traffic control,FIFO。它是提供了一些策略,我们可以简单的把它理解为一些管道给它提供了先进先出,然后带优先级的先进先出以及令牌统姓。我们其实要用的就是令牌桶这个功能,使用iptables和ipset,把我们想要被限制的流量给它筛选出来,然后转到特定的 qdisc中就完成了网络的限流。如果一个用户的应用程序想读取一个文件的话,它其实是通过一连串的操作来完成的。我们需要从这一连串的操作中找出一个我们可以注入的点。做几个假设,我们可以从EXT4这边注入,然后我们可以从FUSE这边注入,当然也可以从提供一个自定义的驱动来注入,也甚至可以我们做一个case硬件来注入,当然越往下的话我们的效果越好,但是成本越高。这个图是当一个进程被注入的时候一个情况,可以看到 application是被注入的进程。`clock_gettime` syscall (228 on x86_64);`clock_gettime` vDSO call;Language/Runtime specific function call- C: `clock_gettime` in glibc
- Java: `System.currentTimeMillis()` or `Instant.now()`
4.5 language/runtime 专门的函数调用由kernel 提供在用户态运行的函数的机制:clock_gettime、getcpu、gettimeofday、time;一段由 kernel 在程序启动时分配的内存区域;这部分内存的内容和动态链接库的 ELF 格式一致。`PTRACE_ATTACH` 容器中的每一个进程`PTRACE_POKEDATA` 修改 vDSO 中时间相关函数的实现- 使用 Chaos Mesh:测试监控告警系统;模拟低网络延迟情况;
- 通过使用 Chaos Mesh 减少路试 成本 90%。
- 测试组件(redis rabbitmq scheduler) 3+;
关注本公众号,回复 “Chaosmesh” 获取本次分享的、完整的PPT。