复制逻辑(AFR)
AFR(Automatic File Replication)是 glusterfs 中的模块(translator),它提供了同步复制系统的所有功能,具体如下:
-
当客户端修改数据时,同步更新所有该数据的副本。
-
当复制集的一个 brick 出现故障时,为客户提供持续的数据可用性。
-
在 brick 挂掉时,自动进行 self-heal 修复,一旦 brick 恢复正常,确保副本在所有 brick 上的数据一致性。
每个 gluster translator 都实现了所谓的文件操作 (FOP),这些文件操作对应到具体 I/O 系统调用。 例如,AFR 有 afr_writev,当应用程序执行 write 时会调用它。 所有 FOP 都属于以下两种类型之一:
i) 基于读取的 FOP,仅从文件中获取信息,不以任何方式修改文件。即:afr_readdir、afr_access、afr_stat、afr_fstat、afr_readlink、afr_getxattr、afr_fgetxattr、afr_readv、afr_seek
ii) 基于写入的 FOP,可更改文件或其属性。即:afr_create, afr_mknod,afr_mkdir,afr_link, afr_symlink, afr_rename, afr_unlink, afr_rmdir, afr_do_writev, afr_truncate, afr_ftruncate, afr_setattr, afr_fsetattr, afr_setxattr, afr_fsetxattr, afr_removexattr, afr_fremovexattr, afr_fallocate, afr_discard, afr_zerofill, afr_xattrop, afr_fxattrop, afr_fsync.
读取事务
对于复制集中的每个文件,AFR 都有一个称为“readable”的数组在内存中,它指示复制集中的每个 brick 是好副本还是坏(需要修复的)副本。在健康状态下,所有 brick 都是可读的,并且将从任何一个可读 brick 中提供读取的 FOP。 read-hash-mode 选项决定了选择哪个 brick。
如果 brick 上的文件是坏的(即它正在等待修复),那么它不会被标记为 readable。 在文件查找期间,根据文件的 AFR xattrs 属性填充 readable 数组。 这些 xattrs 表明哪些 brick 是好的,哪些是坏的。 我们将在下面的写事务部分看到更多关于这些 xattrs 的信息。 如果 FOP 在选定的 readable brick 上失败,AFR 会在下一个 readable brick 上尝试它,直到整个复制集都被访问。 如果 FOP 在任何可读数据上都没有成功,那么应用程序会收到一个错误。
写入事务
每个基于写入的 FOP 都采用由 5 个阶段组成的写入事务模型:
-
Lock 阶段 锁定正在修改文件,以便其他客户端的 AFR 在尝试同时修改同一文件时被阻止。
-
Pre-op 阶段 在所有参与的 brick 上将 xattr (trusted.afr.dirty) 增加 1,作为即将发生 FOP 的指示
-
FOP 阶段 在所有 brick 上执行实际的 FOP(比如 setfattr)
-
Post-op 阶段 在 FOP 成功的 brick 上将脏 xattr (trusted.afr.dirty) 减 1。 此外,还要增加成功 brick 上的“待处理”xattr (trusted.afr.$VOLNAME-client-x) xattr,以识别 FOP 失败的 brick
-
Unlock 阶段 释放在阶段 1 中获得的锁。任何竞争客户端现在都可以继续自己的写入事务。
扩展属性 xattr
xattr 是由一个24位的十六进制数字构成,被分为三个8位十六进制段:
1-8位是数据内容(data)changelog
9-16位是元数据(metadata)changelog
17-24位是目录项(entry)changelog
下面是两副本文件写入属性变化示意图:
Pre-OP前
数据写入中
写入成功
副本b写入失败
自愈逻辑(self-heal)
我们已经知道 AFR 在事务的不同阶段会增加和/或减少 trusted.afr.dirty 和 trusted.afr.$VOLNAME-client-x 的 xattr 值。 对于给定的文件(或目录),这些 xattrs 的全零值或副本的所有 brick 上完全不存在这些 xattrs 意味着文件是健康的并且不需要修复。 如果这些 xattr 中的任何一个 brick 上不为零,则该文件是修复的候选文件。(非零值可能是瞬态的,比如正在进行写入事务的过程中,但这并不需要特别关注,只需要通过 gluster vol heal info 就可以查看需要恢复的文件列表)
self-heal 分为 3 类:
-
数据修复:仅对文件发生。 文件的内容从 source 复制到 sink。
-
条目修复:仅对目录发生。 如果 source 中不存在给定目录下的条目(即文件和子目录),则它们将从 sink 中删除。 同样,如果条目不存在于 source 中,则会在 sink 上创建条目。
-
元数据修复:文件和目录都会发生。 文件所有权、文件权限和扩展属性从 source 复制到 sink。
对于给定的文件,一组 brick 可能是数据修复的 source,而另一组 brick 可能是元数据修复的 source。 这完全取决于哪些 FOP 在哪些 brick 上失败。
如果 self-heal 观察到具有非零 xattrs 的文件,它会执行以下步骤:
-
获取 afr xattrs,检查哪一组 8 个字节是非零的,并确定文件所需的相应修复 - 即数据修复/元数据修复/条目修复。
-
通过 xattr 值来判断哪些 brick 是好的(sources)和哪些是坏的(sinks)。
-
选择 source brick,并以它来修复 sink brick。
-
如果修复成功,则将 afr xattrs 重置为零。
这是一个相当简化的描述,省略了每个步骤需要采取的各种锁的详细信息,因为自我修复和客户端 I/O 可以在文件上并行发生。 甚至多个自我修复守护进程可以尝试修复同一个文件。
什么时候会发生自愈
从客户端侧发起
当从客户端(挂载)访问文件时,会触发客户端修复。 AFR 使用单调递增的编号来跟踪 translator 与 brick 的断开/连接。 当这个编号发生变化时,文件的 inode 被标记为刷新的候选。 当下一个 FOP 出现在此类 inode 上时,会触发 self-heal 自愈以更新 readable 数组(如果 AFR xattrs 需要修复时)。 这种自愈发生在后台,它不会阻止实际的 FOP,它将在刷新后照常继续。 可以通过禁用 3 个相应的 volume 选项来关闭特定的客户端修复:
cluster.metadata-self-heal
cluster.data-self-heal
cluster.entry-self-heal
后台发生的客户端自愈次数可以通过以下 volume 选项进行调整:
background-self-heal-count
heal-wait-queue-length
Name heal 名称修复:名称修复只在访问文件/目录名称时对其进行修复。 例如,假设一个文件在某个离线的 brick 上创建并写入,并且所有 3 个客户端自愈参数都被禁用。 当下一个 I/O 出现时,文件名将在 brick 上创建,但它的内容/元数据没有被修复。 名称修复无法禁用。 一旦文件被访问,名称修复可以确保所有 brick 的命名空间都是一致的。
由 self-heal daemon 发起
有一个自愈守护进程 (glutershd) 在受信存储池的每个节点上运行。 它是一个轻量级的客户端进程,主要由 AFR 和 translator 组成。 它可以与池中所有 brick 对话。 它定期爬取(默认每 10 分钟一次;可通过 heal-timeout 选项调整)需要修复的文件列表并进行修复。 客户端修复是在文件访问时完成的,但 glustershd 会主动处理修复积压。
索引修复
虽然上面叙述了 AFR 写入事务的五个阶段,但还有一个细节:
-
在 pre-op 阶段,除了标记 trusted.afr.dirty 之外,每个 brick 还将文件的 gfid 字符串存储在其 .glusterfs/indices/dirty 目录中。
-
同样,在 post-op 阶段,它会从其 .glusterfs/indices/dirty 中删除 gfid 字符串。如果另外,如果在某些 brick 上写入失败,则成功的 brick 会将 gfid 字符串存储在 .glusterfs/indices/xattrop 目录中。
因此,当文件上没有发生 I/O 并且在 .glusterfs/indices/dirty 中存在 brick 的 gfid 时,这意味着 brick 在 post-op 操作阶段之前发生了故障。 如果在 .glusterfs/indices/xattrop 中找到 gfid,则意味着在其他 brick 上发生了写入失败。
glustershd 只是读取 .glusterfs/indices/* 中的条目列表并在它们上触发修复。 这称为索引修复。 虽然这种情况会在每 heal-timeout 秒内自动发生,但我们也可以通过命令行使用 gluster volume hea $VOLNAME 手动触发它。
完全修复
从命令行中使用 gluster volume repair $VOLNAME full 可触发完全修复,它不处理索引修复中的特定条目列表,而是从 root 开始爬取整个 gluster 文件系统,检查文件是否具有非零 afr xattrs 并在它们上触发修复。
关于 xattrs 缺失和脑裂
AFR 很大程度上依赖 xattr 值,但是如果文件的数据/元数据不一致,还可能存在 xattr 值缺失 xattr 值不一致(也就是脑裂)的情况,解决方法如下:
-
xattr 缺失:AFR 选择本地(针对特定 glustershd 进程)brick 、选择更大的文件、选择具有最新 ctime 的文件等,然后进行修复。
-
脑裂:需要使用 gluster split-brain resolution 命令行或设置 favourite-child-policy 选项来选择一个好的副本并触发修复。