CephFS快照几个特点:

  • 写时复制
  • client端操作时只能针对目录,不能针对单独文件
  • 从任意文件夹下开始打快照

快照实现

快照通过​​SnapRealm​​​组织成树形结构,每个有快照信息的inode节点都会有对应的​​SnapRealm​​​,没有快照信息的inode使用父节点路径上最近的​​SnapRealm​​​,根节点默认有​​SnapRealm​​​。在client端创建快照时mds会在对应的inode节点新建​​SnapRealm​​​(仅首次创建时),普通文件inode中也会出现​​SnapRealm​​,但都是mds的cow机制创建的,client端无法直接操作。


【ceph】CephFS 内部实现(三):快照_元数据

snaprealm创建及组织

 

要解决的问题

  • 快照究竟是在备份什么?
    快照是对当前目录及以下的子树状态进行保存,创建快照相当于对目录树中(节点以下的子目录树)每个inode进行备份,因为inode承载了文件系统的全部信息。根据MDS的元数据组织关系,对于普通文件,实际上是将dentry在dir的​​items​​中新增一份副本,由​​first​​,​​last​​两个值指明对应的snap范围,并且还会对inode进行备份,备份的inode最终以omap val形式存在。对于目录,并不会在其父目录​​items​​中新增dentry,而只是在自己的inode中新增一份inode备份,最终以meta pool中的RADOS对象形式存在(不是新增对象,每个目录只有一个对象)。
  • 快照的元数据如何存在?
    每个快照都有全局唯一的整数id标识,通过向​​MDSTableServer​​申请来保证id唯一性。每个元数据都有​​first​​,​​last​​标识,用于标识元数据对应的snap,​​last​​为​​CEPH_NOSNAP​​时标识元数据为head数据。
  • 在目录树中的某个节点打快照后,快照信息如何向上传递?
    快照节点以上的部分和本次快照无关,因此元数据不受影响。但如果本节点是第一次打快照,则snaprealm的组织关系会发生变化。
  • 在目录树中的某个节点打快照后,快照信息如何向下传递?
    当父节点创建快照后,子节点是需要知道的,这样子节点才会知道去备份dentry和inode。这个通知机制是通过​​SnapRealm​​关系树来完成的。父节点遍历自己的child snaprealms,逐个清空child snaprealm cached_seq,并向client端发送信息。
    清空cached_seq可以保证在下次需要读取snap信息时snaprealm重新进行​​build_snap_set()​​操作,进而读取到父节点的最新snap信息。
    向client端发的信息主要包括三方面:
  1. snaprealm组织关系的变化。如果是新创建的snaprealm,则涉及继承关系调整。
  2. 面向client的inode cap组织关系变化。每个inode cap都属于一个snaprealm管理(通过xlist结构),如果是新建的snaprealm,则涉及管理关系的移动。比如初始状态下所有cap都在根节点snaprealm中,新建snaprealm后,快照节点以下的inode cap将被移动到新snaprealm中。
  3. split信息。如果不是新建snaprealm,即在已有快照的节点上继续创建快照。这种情况下快照节点的子节点sanprealm需要知道父节点快照更新的消息,在mds端是通过写时复制(CopyOnWrite)的方式先invalidate cache seq,再在需要时build seq实现,但是对于client端无法这样做,client端维护的snap信息需要及时更新,没有cow,因此这些信息作为split信息传递给client。

写时复制

创建快照时只更新节点的snaprealm,并invalidate子节点的snaprealm cache,同时通知client最新的snap信息。整个过程并没有涉及元数据的备份,​​first​​​,​​last​​​的修改以及对于普通文件的数据进行的备份,因为这些修改是在下次对节点进行修改时才会发生的事件,因此叫做写时复制。
举两个🌰:

  1. 创建快照后,在目录下新建文件,这时目录发生cow:通过​​journal_dirty_inode()​​​将目录inode进行复制(​​CInode::cow_old_inode()​​),但并不会将目录的dentry在父目录中进行备份。
  2. 创建快照后,在目录下truncate一个已有的文件,这时文件发生cow:通过​​journal_dirty_inode()​​​对文件目录进行复制(​​MDCache::cow_inode()​​​),且会在目录的​​items​​​中新增一个snaped的dentry,和​​cow_inode()​​​ new出来的inode关联。对于文件数据部分的备份则是通过RADOS层的快照机制完成(mds会将文件之前的快照号传给​​Filer​​,最终传到pg层的OSDOp中,这些快照号对应的数据将被保留)。

快照对stats的影响

由于stats信息都是根据inode统计得出的,而从client发起的请求,要么是在非快照目录下,要么是快照目录下,在非快照目录下,对于mds就是一次snapid为​​CEPH_NOSNAP​​的请求,在快照目录下发起的请求对于mds就是一个有具体snapid的请求。因此只能分开统计快照和非快照空间使用量。如果计费的话这里会有个问题,就是快照的实际使用空间是无法从client端得到的,除非根据inode去datapool中遍历对象计算出快照的实际使用空间。

作者:宋新颖