BlueStore checksum机制 1.checksum的计算 2.checksum的验证

BlueStore 磁盘静默数据损坏分析

  1. fsck过程中发现磁盘数据静默错误
  2. clone操作
  3. read操作
  4. write操作
  5. Gc操作

实际场景 1.腾讯场景问题确认 2.rbd mirror 3.rbd export import 4.集群扩容

BlueStore checksum机制

Bluestore的checksum算法默认采用的是crc32, 算法的逻辑很简单,即以固定大小的数据块为单位进行计算checksum,数据块的大小一般为4K,同设备的块大小。 Checksum的结果作为bluestore_blob_t结构体序列化的一部分存放在数据库中,假设一个blob的长度为64KB,进行checksum的数据块大小为4KB, 则初始化该blob时,对于crc32,每个数据块的checksum用4字节描述,将分配(64KB/4KB)* 4 = 64字节用于存储该blob数据的checksum。

1.checksum的计算

写数据会进行checksum的计算,对于对齐写和非对齐写,都会以数据所在的4KB对齐的数据块为单位进行计算。比如0-256区间的数据更新,会将257-4095区间的数据读出来,然后以4KB为单位进行计算,如果257-4095原来不存在数据,则用0进行填充后进行计算,计算完将checksum值存储在该blob中。

2.checksum的验证

读数据时,bluestore中会首先查找所读取数据区间所对应的checksum数组,然后用读取的数据计算checksum,判断两者是否一致。举例如下:假设blob的大小为64KB, 本次读取的数据区间为8192~16383, 则分别计算8192~12287和12288-16383两个数据块的checksum,然后读取blob中之前写数据时计算的checksum,这两块数据对应的checksum值为checksum数组偏移为2和3的两个4字节变量。

BlueStore 磁盘静默数据损坏分析

BlueStore中如下五种操作会读取对象数据,其中目前只有对象数据read操作能主动触发对象修复操作,且对上层用户不感知。

结论:读数据的时候依靠checksum机制可以发现数据不一致的情况,其中read可以自动修复,其他的读取操作如果发现存在静默错误,会及时报错,所以可以取消定期deep-scrub操作。

1. fsck过程中发现磁盘数据静默错误

==>> int BlueStore::_mount(bool kv_only, bool open_db) ====>> fsck(cct->_conf->bluestore_fsck_on_mount_deep) 如果bluestore_fsck_on_mount_deep为真,bluestore的fsck过程中将会读取整个文件,以此将会检查该文件对应的磁盘块是否存在数据损坏现象,如果文件损坏,会导致mount失败,从而导致该osd启动失败。

//bluestore_fsck_on_mount_deep 默认为true
    Option("bluestore_fsck_on_mount_deep", Option::TYPE_BOOL, Option::LEVEL_DEV)
    .set_default(true)
    .set_description("Run deep fsck at mount"),
2. clone操作

Clone操作是否从磁盘读数据取决于bluestore_clone_cow配置,如果bluestore_clone_cow为真,则直接进行extent的共享,不会有实际的数据读写操作。如果为假,读取被clone对象的数据,写入新的对象,如果此时读取过程中,发生磁盘静默错误,则直接assert,osd进程down掉。

//bluestore_clone_cow 默认为true
     Option("bluestore_clone_cow", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
    .set_default(true)
    .set_safe()
    .set_description("Use copy-on-write when cloning objects (versus reading and rewriting them at clone time)"),
3. read操作

读操作过程中,如果发现磁盘静默数据错误,是会主动对该对象进行修复的,代码调用流程如下:

==>> do_osd_ops
====>> do_read
======>> objects_read_sync
======>> rep_repair_primary_object

将对象加入待修复列表,进行修复,读操作 会重新发起,用户不感知。

4. write操作

Write操作的rmw写,需要首先从磁盘读取数据进行覆盖写,如果此时发生磁盘静默错误,代码中直接assert,代码调用流程如下: ==>> _do_write_small ====>> _do_read 触发( assert(r >= 0 && r <= (int)tail_read);)

通常ext4类的本地文件系统会直接abort事务,将文件系统挂载为只读,因此此处直接assert osd,导致osd进程down也可以理解。

5. Gc操作

==>> _do_write ====>> _do_gc ======>> _do_read 触发assert(r == (int)it->length); 此处也会导致osd down,策略我理解同write操作。

实际场景

1.腾讯场景问题确认

1.1 cache?1.2 no crc? 1.3 migration?
ceph社区邮件”Copying without crc check when peering may lack reliability”提到的磁盘静默数据损坏没有及时发现的问题,根据发件人的测试步骤没有复现,数据都能自动修复,目前正在请求发件人提供完整复现脚本。现阶段调查结果如下:
1, ceph-objectstore-tool修改对象数据时,如果checksum使能,会同步用新的数据计算checksum,如果修改对象主副本,则新数据迁移到新的crush root下,此处是合理的,因为checksum检查通过,osd会认为数据是正确的,如果修改对象的从副本,由于迁移是根据主副本的数据,新的crush root下的数据仍然是主副本数据。 如果对该osd做deep-scrub, 如果对象所属的pg在该osd上是主pg,是能检测到数据变动的,因为会和从pg上的副本做对比,如果对象所属的pg在该osd上为从pg, deep-scrub将不会针对该pg做检查的。
2,如果对象数据的主副本存在cache,即使对应的底层硬件存在静默错误,正常的读操作和deep-scrub都不会检测到磁盘静默错误(我已经发送patch让deep-scrub能检测出此种损坏,patch已经被merge)。
3,数据迁移在ceph master分支上验证,在三副本测试中,破坏一个副本的数据,然后让数据迁移到另一个crush root下,ceph会自动进行数据修复的,用户不感知。

2.rbd mirror

测试步骤:
破坏journal对象的主副本数据,然后进行replay,待replay完成后检查两个rbd设备的数据是否一致。 测试结果:
暂时没有找到测试方法来控制上述流程,但理论分析是可以修复的,用户不感知道。Rbd-mirror回放日志数据,也就是读取日志对象主副本数据,具体流程同”3. read操作”。

3.rbd export import

测试步骤:
创建一个rbd设备,向其偏移0处写入4MB随机数据,然后通过dd破坏其中一段数据,注意破坏是主副本的数据。破坏完成后,将主副本所在的osd重启以消除cache的影响,然后进行rbd的export 和import测试,读取两个rbd设备的前4MB数据MD5值与初始的写入rbd设备的随机数据文件md5值是否一致。

测试结果:
损坏的rbd对象数据会自动进行修复,用户不感知,具体原理参见”3. read操作”。

4.集群扩容

测试步骤:
创建3 osd集群,用rados创建一个对象,损坏对象主副本数据,注意同样需要重启osd以清除bluestore层cache,加入3个新的osd,待集群稳定后,用rados读取对象数据,验证数据md5值是否一致。

测试结果:
集群rebalance成功,损坏的数据会自动进行修复,用户不感知,具体原理参见”3. read操作”。