分布式文件系统典型异常场景剖析及模拟(一)

阿凡达2018-07-04 13:48

本文记录了分布式文件系统从上层到底层,从软件到硬件的故障模拟场景。

首先介绍一下分布式文件系统NEFS的架构:

 用户从客户端FSI(client)进行文件写入、删除、读取等操作,NEFS底层会根据传入参数进行多副本的文件存储。每个副本放在不同的zone内的partition上,以保证高可靠性,即即使有一个zone内的partition所在的机器下线或断链,也不会影响其他副本的可读性。

NEFS文件系统的主要使用场景主要有三类:

1.      N个副本写入【一般N=3,低于3副本的ps(partition Server)(可以理解为管理partition的server进程,负责写入逻辑)存活则写入失败】。

2.      读取文件【只要单副本的ps存活即可读取成功】。

3.      删除文件【无论任何情况都返回成功,底层再异步删除】。

可以看出场景较简单并且单一,但是真正涉及到写入和读取有可能会失败的场景却很多,大家知道一个写入或读取流程涉及到内存pagecache、VFS、具体的ext3/ext4等文件系统、通用块层、disk设备等层级,其中任何一个环节出错则这个写入或读取就会失败。当然我们的测试是基于相信内核可靠的前提下来做的,因此内核内部的一些异常导致的IO错误我们暂时不去mock或模拟。我们首要的先考虑我们nefs这个分布式文件系统正常运转过程中有可能发生的一些人为、环境、应用程序异常等导致的一些故障场景下我们的分布式文件系统是否还能够正常的运转,或者至少能够正确的应对这些异常。

 

这些场景一个是依靠经验值提取文件读写过程中可能会出现的软硬件故障场景,另外是参考之前分布式文件系统的一些线上BUG来分析一些容易出问题的故障点。

梳理过程不阐述,经典场景如下:

1.      不知道为什么,运行了一段时间后发现多个ps上面的文件副本不一致了,可能会导致多次读出来的数据不一致,ps进程重新启动后检查不一致后死掉等严重问题。分析后的场景构造:人为注入多副本写入的文件不一致的数据,查看这种情况下分布式文件系统能否处理。

2.      OOM(out of memory)或其他原因导致的进程异常退出。

3.      运维错误操作-文件系统强制卸载。

4.      硬件故障:磁盘部分块写入失败、磁盘整块故障、整机掉电重启等。

梳理出来这些场景,则测试任务完成了10%,剩下的90%是需要结合我们的业务部署及业务逻辑来设计场景,并且通过一些手段来模拟并且执行,任重而道远。

由于故障场景很多,而且有些是比较容易模拟的,例如进程异常退出的场景,因此不在这里过多阐述,下面举几个不太好模拟,然而能够发现问题的一些例子分享给大家:

场景一:人为构造多副本的文件不一致的场景:

实例:写入一个2副本1M小文件,然后人为修改其中1个副本的文件,导致其数据跟之前的不一致。然后查看是否可以从另外一个副本上读取成功。以下是测试的状态图:

 

具体执行步骤如下

写入文件:调用写入接口。

Modify data:两种方式:1 直接修改文件(不方便自动化)。2fwrite写入修改(编写代码对文件尾部倒数n个字符进行替换写入)。

代码使用的函数示例(C语言):

FILE *fp;

fp=fopen("***.log","a+");

fseek(fp,-10,SEEK_END);

fwrite("bbbbbbbbbb",1,strlen("bbbbbbbbbb"),fp);

fclose(fp);

附注:为什么要改最后10个字符,因为小文件log是集合了n个小文件的,而且包含一些元数据信息,如果不修改最尾部的数据,而改头的话容易导致元数据信息不一致,这样的异常就不接近于真实场景,而且可能会导致一些无法处理的错误。

读取文件并检查结果:调用读取接口。

预期结果:读取成功。但是需要加入定期检测数据不一致的监控进程(目前还没有做),一旦出现数据不一致则报警然后自动拉起该文件所在partition(一个磁盘)的数据恢复任务,将其各个副本上的数据恢复成正确的数据。

测试过程中发现2个BUG,一个是改完数据后,ps进程重启异常退出,另一个是改完数据后,客户端读取的时候提示partition不存在,不往另外一个副本上读。

场景二:磁盘部分块写入失败场景

看了上面的例子,肯定有些童鞋在想,不就是改一个文件么,有多大难度,就算用fwrite写也是最基础的用法,并没有什么技术含量。那模拟磁盘部分块写入失败这个场景就不像刚才那么容易了。为什么要模拟块写入失败,因为现在线上一些机器硬盘配置老旧,可能会存在某块盘故障,或者盘上面出现一些坏道导致IO error。这样也会导致某个副本的ps写入的时候失败。那这种场景如何模拟呢。别说去找一个有坏道的磁盘了,就算能找到写入文件的时候也不一定能刚好踩到那个坏道上,因为文件系统已经进行重映射了,上层写入的文件逻辑位置与磁盘的真实位置在外部是不可见的。

那如何来模拟磁盘块写入失败呢。之前blog发过一个用dmsetup工具来模拟error设备的用法,在此不重复阐述。有兴趣的麻烦移步至:http://qa.blog.163.com/blog/static/1901470022015328324283/

就是用软件的手段来模拟一个可以导致IO error的逻辑设备并切换原设备。

下面描述一下场景、构造脚本和预期结果:

 

首先配置好的disk写入试验是否可以成功:

将挂载点的磁盘配置成可用的/mnt/sdc(将sdc映射成mydisk设备)。

脚本内容如下

sudo dmsetup create mydisk okdisktable

sudo mount /dev/mapper/mydisk /mnt/cxqloop

sudo chown -R nefs:nefs /mnt/cxqloop/

其中okdisktable的文件内容如下

0 1024000 linear /dev/sdc 0

写入成功后将mydiskmock成error设备(脚本内容):

sudo dmsetup suspend mydisk

sudo dmsetup reload mydisk ./errordisktable

sudo dmsetup resume mydisk

其中errordisktable文件内容如下

0 20480 linear /dev/sdf 0

20480 1024000 error

Mock成功后在进行写入,主副本ps写入成功以后推到从ps副本上发现写入报IO error:

resp={PeerID:1 PartitionID:1 Epoch:1 PreparePoint:2 CommitPoint:1 ReturnCode:3 Reason:input/output error} err=<nil>

客户端写入失败,与预期结果一致。

 测试过程中发现的bug:1.从ps写入失败,但是fsi客户端显示成功;2.主上写入成功后推送到从ps频率太快,日志打印过多。

可以看到,两个场景均能发现不少潜在的系统问题,但构造异常是一个比较繁琐的过程,尤其是这种精确的异常模拟,一定要与业务逻辑相结合才能分析出来有价值的异常注入点。同样的异常注入,不同的场景价值点也不一样,例如多个副本上磁盘都注入IO error,则不会出现主从不一致的情况,这种异常虽然也需要构造,但是意义不如其中一个副本IOerror的场景大。因此如何在短时间内构造精确而又价值高的场景是一个比较依赖经验的事情。今后测试过程中需要多加积累这方面的经验,来达到这个目的。

 

相关阅读:分布式文件系统典型异常场景剖析及模拟(二)

本文来自网易实践者社区,经作者崔晓晴授权发布。