本话题系列文章整理自 PingCAP NewSQL Meetup 第 26 期刘奇分享的《深度探索分布式系统测试》议题现场实录。文章较长,为方便你们阅读,会分为上中下三篇,本文为下篇。html
接中篇:
ScyllaDB 有一个开源的东西,是专门用来给文件系统作 Failure Injection 的, 名字叫作 CharybdeFS。若是你想测试你的系统,就是文件系统在哪不断出问题,好比说写磁盘失败了,驱动程序分配内存失败了,文件已经存在等等,它均可以测模拟出来。java
CharybdeFS: A new fault-injecting file system for software testingsql
Simulate the following errors:数据库
disk IO error (EIO)网络
driver out of memory error (ENOMEM)并发
file already exists (EEXIST)app
disk quota exceeded (EDQUOT)框架
再来看看 Cloudera,下图是整个 Cloudera 的一个 Failure Injection 的结构。dom
一边是 Tools,一边是它的整个的 Level 划分。好比说整个 Cluster, Cluster 上面有不少 Host,Host 上面又跑了各类 Service,整个系统主要用于测试 HDFS, HDFS 也是很努力的在作有效的测试。而后每一个机器上部署一个 AgenTEST,就用来注射那些可能出现的错误。tcp
看一下它们做用有多强大。
Cloudera: Simulate the following errors:
Packets loss/corrupt/reorder/duplicate/delay
Bandwidth limit: Limit the network bandwidth for the specified address and port.
DNSFail: Apply an injection to let the DNS fail.
FLOOD: Starts a DoS attack on the specified port.
BLOCK: Blocks all the packets directed to 10.0.0.0/8 (used internally by EC2).
SIGSTOP: Pause a given process in its current state.
BurnCPU/BurnIO/FillDISK/RONLY/FIllMEM/CorruptHDFS
HANG: Hang a host running a fork bomb.
PANIC: Force a kernel panic.
Suicide: Shut down the machine.
数据包是能够丢的,能够坏的,能够 reorder 的,好比说你发一个 A,再发一个 B,它能够给你 reorder,变成先发了 B 再发了 A,而后看你应用程序有没有正确的处理这种行为。接着发完一次后面再给你重发,而后能够延迟,这个就比较简单。目前这个里面的大部分,TiKV 都有实现,还有带宽的限制,就好比说把你带宽压缩成 1M。之前咱们遇到一个问题颇有意思,发现有人把文件存到 Redis 里面,但 Redis 是带多个用户共享的,一个用户就能把整个 Redis 带宽给打满了,这样其余人的带宽就很卡,那这种很卡的时候 Redis 可能出现的行为是什么呢?咱们并不须要一个用户真的去把它打满,只要用这种工具,瞬间就能出现我把你的带宽限制到原来的 1%,假设别人在跟你抢带宽,你的程序行为是什么?立刻就能出来,也不须要配很复杂的环境。这极大的提升了测试效率,同时能测试到不少 corner case。
而后 DNS fail。那 DNS fail 会有什么样的结果?有测过吗?可能都没有想过这个问题,可是在一个真正的分布式系统里面,每一点都是有可能出错的。还有 FLOOD,假设你如今被攻击了,整个系统的行为是什么样的?而后一不当心被这个 IP table 给 block 了,该怎么办。这种状况咱们确实出现过。咱们一上来并发,两万个链接一打出去,而后发现大部分都连不上,后来一看 IP table 自动启用了一个机制,而后把大家都 block。固然咱们后面查了半个小时左右,才把问题查出来。但这种实际上应该是在最开始设计的时候就应该考虑的东西。
若是你的进程被暂停了,好比说你们在云上跑在 VM 里面,整个 VM 为了升级,先把你整个暂停了,升级完以后再把你恢复的时候会怎么样?那简单来说,就是若是假设你程序是有 GC 的,GC 如今把咱们的程序卡了五秒,程序行为是正常的吗?五十秒呢?这个颇有意思的就是,BurnCPU,就是再写一个程序,把 CPU 全占了,而后让你这个如今的程序只能使用一小部分的 CPU 的时候,你程序的行为是否是正常的。正常来说,你可能说我 CPU 不是瓶颈啊,我瓶颈在 IO,当别人跟你抢 CPU,把你这个 CPU 压的很低的时候,到 CPU 是瓶颈的时候,正常你的程序的这个行为是否是正常的?还有 IO,跟你抢读的资源,跟你抢写的资源,而后 filedisk 把磁盘写满,写的空间不多。好比说对数据库而言,你建立你的 redo log 的时候,都已经满了会怎么样?而后我忽然把磁盘设为只读,就你忽然一个写入会出错,可是你接下来正常的读写行为是否是对的?很典型的一个例子,若是一个数据库你如今写入,磁盘满了,那外面读请求是否就能正常响应。 Fill memory,就是瞬间把这个 memory 给压缩下来,让你下次 malloc 的时候可能分布不到内存。这个就和业务比较相关了,就是破坏 HDFS 的文件。其它的就是 Hang、Panic,而后还有自杀,直接关掉机器,整个系统的行为是什么样的?
如今比较痛苦的一点是你们各自为政,每一家都作一套,可是没有办法作成一个通用的东西给全部的人去用。包括咱们本身也作了一套,可是确实没有办法和其余的语言之间去 share,最先提到的那个 libfu 库其实是在 C 语言写的,那全部 C 相关的均可以去 call 那个库。
Distributed testing
Namazu
ZooKeeper:
Found ZOOKEEPER-2212, ZOOKEEPER-2080 (race): (blog article)
Etcd:
Found etcdctl bug #3517 (timing specification), fixed in #3530. The fix also resulted a hint of #3611, Reproduced flaky tests {#4006, #4039}
YARN: Found YARN-4301 (fault tolerance), Reproduced flaky tests{1978, 4168, 4543, 4548, 4556}
而后 Namazu。你们确定以为 ZooKeeper 很稳定呀, Facebook 在用、阿里在用、京东在用。你们都以为这个东西也是很稳定的,直到这个工具出现了,而后轻轻松松就找到 bug 了,全部的你们认为的这种特别稳定的系统,其实 bug 都还挺多的,这是一个毁三观的事情,就是你以为东西都很稳定,都很 stable,其实不是的。从上面,咱们能看到 Namazu 找到的 Etcd 的几个 bug,而后 YARN 的几个 bug,其实还有一些别的。
How TiKV use namazu
Use nmz container / non-container mode to disturb cluster.
Run container mode in CI for each commit. (1 hour)
Run non-container mode for a stable version. (1 week+)
Use
extreme
policy for process inspector
Pick up some processes and execute them with SCHED_RR scheduler. others are executed with SCHED_BATCH scheduler
Use [0, 30s] delay for filesystem inspector
接下来讲一下 TiKV 用 Namazu 的一些经验。由于咱们曾经在系统上、在云上面出现过一次写入磁盘花了五十几秒才完成的状况,因此咱们须要专门的工具模拟这个磁盘的抖动。有时候一次写入可能确实耗时比较久,那这种时候是否是 OK 的。你们若是能把这种东西通通用上,我以为还能为不少开源系统找出一堆 bug。
稍微介绍一下咱们如今运行的基本策略,好比说咱们会用 0 到 30 秒的这个 delay (就是每一次你往文件系统的交互,好比说读或者写,那么咱们会给你产生随机的 0 到 30 秒的 delay ),但咱们正常应该仍是须要去测三十秒到几分钟的延迟的状况,是否会让整个系统崩掉了。
How TiKV simulate network transport
Drop/Delay messages randomly
Isolate Node
Partition [1, 2, 3, 4, 5] -> [1, 2, 3] + [4, 5]
Out of order messages
Filter messages
Duplicate and send redundant messages
怎么模拟网络呢?假设你有网络,里面有五台机器,那我如今想作一个脑裂怎么作?不能靠拔网线对吧?好比在 TiKV 的测试框架中,咱们就能够直接经过 API 把 5 个节点脑裂成两部分,让 1, 2, 3 号节点互相联通,4, 5 号节点也能联通,这两个分区彼此是隔离的,很是的方便。其实原理很简单,这种状况是用程序本身去模拟,假如是你发的包,自动给你丢掉,或者直接告诉你 unreachable,那这个时候你就知道这个网络就脑裂了,而后你怎么作?就是只容许特定类型的消息进来,把其余的都丢掉,这样一来你能够保证有些 bug 是必然重现的。这个框架给了咱们极大的信心用来模拟并重现各类 corner case,确保这些 corner case 在单元测试中每次都能被覆盖到。
How to test Rocksdb
Treat storage as a black box.
Three steps(7*24):
Fill data, Random kill -9
Restart
Consistent check.
Results:
Found 2 bugs. Both fixed
而后说说咱们怎么测 RocksDB。 RocksDB 在你们印象中是很稳定的,但咱们最近发现了两个 bug。测的方法是这样的:咱们往 RocksDB 里面填数据,而后随机的一段时间去把它 kill 掉,kill 掉以后咱们重启,从新启动以后去检测咱们刚才 fail 的 data 是否是一致的,而后咱们发现两个可能形成数据丢失的 bug,可是官方的响应速度很是快,几天就都 fix 了。但是你们广泛运行的是这么 stable 的系统,为何还会这么容易找到 bug?就说这个测试,若是是一直有这个测试的 cover,那么这两个 bug 可能很快就可以被发现。
这是咱们一个基本的,也就是当成一个纯黑盒的测。你们在测数据库的时候,基本也是当黑盒测。好比说 MySQL 写入数据,kill 掉,好比说我 commit 一个事务,数据库告诉咱们 commit 成功,我把数据库 kill 掉,我再去查我刚才提交的数据同样能查到。这是一个正常的行为,若是查不到,说明整个系统有问题。
More tools
american fuzzy lop
其实还有一些更加先进的工具,你们平时以为特别稳定的东西,都被摧残的不行。Nginx 、NGPD、tcpdump 、LibreOffice ,若是有用 Linux 的同窗可能知道,还有 Flash、sqlite。这个东西一出来,当时你们很兴奋,说怎么一会儿找了这么多 bug,为何之前那么稳定的系统这么不堪一击,会以为这个东西它还挺智能的。就好比说你程序里面有个 if 分支,它是这样的,假如你程序有一百条指令,它先从前面一直走,走到某条分支指令的时候,它是一直持续探索,一个分支走不下去,它会一直在这儿持续探索,再给你随机的输入,直到我探索进去了,我记下来了下次我知道我用这个输入能够进去特定的分支。那我能够再往下走,好比说你 if 分支进去以后里面还有 if ,那你传统手段可能探测不进去了但它能够,它记录一下,我这个能够进去,而后我重来,反正我继续输入这个,我再往里面走,一旦我探测到一个新的分支,我再记住,我再往里面走。因此它一出来的时候你们都说这个真厉害,一下发现这么多 bug。但最激动的不是这些人,最激动的是黑客,为何?由于忽然有不少栈溢出、堆溢出漏洞被发现了,而后就能够写一堆工具去攻击线上的这么多系统。因此不少的技术的推动在早期的时候是黑客作出来,可是他们的目的固然不必定是为了测试 bug,而是为了怎么黑一个系统进去,这是他们当时作的,因此这个工具也是很是强大、很是有意思的,你们能够拿去研究一下本身的系统。
你们印象里面各类文件系统是很稳定的,但是当用 American fuzzy lop 来测试的时候,被惊呆了。 Btrfs 连 5 秒都没有坚持到就跪了,你们用的最多的 Ext4 是最坚挺的,也才抗了两个小时!!!
再来讲说 Google,Google 怎么作测试对外讲的很少,最近 Chrome team 开源了他们的 Fuzz 测试工具 OSS-Fuzz,这个工具强大的地方在于自动化作的极好:
发现 bug 后自动建立 issue
bug 解决后自动 verify
更惊人的是 OSS-Fuzz 集群一周能够跑 ~4 trillion test cases 更多细节你们能够看这篇文章:Announcing OSS-Fuzz: Continuous Fuzzing for Open Source Software
另外有些工具能让分布式系统开发人员的生活变得更美好一点。
Tracing tools may help you
Google Dapper
Zipkin
OpenTracing
还有 Tracing,好比说我一个 query 过来,而后通过这么多层,通过这么多机器,而后在不一样的地方,不一样环节耗时多久,实际上这个在分布式系统里面,有个专门的东西作 Tracing ,就是 distribute tracing tools。它能够用一条线来表达你的请求在各个阶段耗时多长,若是有几段,那么分到几个机器,分别并行的时候好了多长时间。大致的结构是这样的:
这里是一个具体的例子:
很清晰,一看就知道了,不用去看 log,这事其实一点也不新鲜,Google 十几年前就作了一个分布式追踪的工具。而后开源社区要作一个实现叫作 Zipkin,好像是 java 仍是什么写的,又出了新的叫 OpenTracing,是 Go 写的。咱们如今正准备上这个系统,用来追踪 TiDB 的请求在各个阶段的响应时间。
最后想说一下,你们研究系统发现 bug 多了以后,不要对系统就丧失了信心,毕竟bug 一直在那里,只是从前没有发现,如今发现得多了,整体上新的测试方法让系统的质量比之前好了不少。好像有点超时了,先聊到这里吧,还有好多细节无法展开,下次再聊。(本系列完结)