触发场景
目前,块存储服务是Ceph存储中被使用的最普遍的服务之一,通过块存储服务,可以向客户端以使用块设备一样访问Ceph集群。然而,目前在使用块存储服务时,尤其是OpenStack与Ceph对接时,如果没有严格的控制Ceph端的对象大小(使用 >= 8MB对象时),将有可能导致严重的数据不一致情况,该异常由于XFS文件系统本身对Fiemap的支持特性导致的。
XFS使用fiemap时,当extents数量大于1364时,通过ioctl的FS_IOC_FIEMAP接口,获取的extents数量上限为1364,导致超出部分extents数据获取不到。这将导致,在开启fiemap时,Ceph集群进行recovery与backfill之后,产生大量数据不一致的情况,也是迄今为止Ceph中影响最大的,最严重的数据不一致BUG。
由于fiemap BUG,碎片化对象在recovery与backfill之后(fiemap 获取不正确的数据),从而使的恢复的对象数据与原对象数据不一致。而恢复后的副本若成为主副本,则可能发生静默读错误,并且如果使用Ceph策略自动修复对象(repair object之后),可能将错误数据覆盖至正确数据,数据将永远损毁。在某些情况下,还会触发对象永远处于inconsistent状态。
复现方式
初始化大量extents碎片文件test,使其成为一个拥有3999个extents的碎片文件。
通过fiemap系统调用,获取这些extents
编译g++ do_fiemap.cc -o do_fiemap
通过该程序可以打印出可以获取的到fiemap extents, do_fiemap test,通过xfs_bmap test 打印出真正的extents数量后,进行比较。
可以看到,我们最先写了2000个extents,并通过xfs_bmap获取到了 2000个有内容的extents,但是通过fiemap系统调用,只获取到了1364个有数据的extents,所以在ceph中使用fiemap系统调用在某些情况下导致数据一致性BUG。
修复方案
1)目前,在使用块存储使用场景时,通常情况下,默认使用4MB对象。在无特殊情况下,不用使用大于4MB 对象的RBD 镜像。
2)在I版以后,通过新的系统调用,seek_data, seek_hole,可以避免触发该BUG,防止extents过多时使用fiemap调用产生的问题
这组系统调用,可以让使用者通过while循环,反复的发现文件中的data 与 hole,从而组织出一个文件中真正存在的数据,能够避免产生与fiemap系统调用类似的BUG,又保证了recovery或者clone时候只复制有用数据,而反复的系统调,应该会带来一定性能上的影响,请读者们自行测试。
目前,在I版本以后的ceph中,提供参数filestore_seek_data_hole,来启用该功能。当filestore_seek_data_hole 与 filestore_fiemap同时设置时,只用文件系统支持seek_data_hole,那么就会先通过seek_data_hole方式来获取文件的extents,所以在I版本以后的ceph中,应启用filestore_seek_data_hole功能来替代filestore_fiemap 功能。
3)对于已经使用8MB,16MB 甚至更大对象大小的RBD镜像,请暂时设置禁用Fiemap功能,并等待后续版本修复。