用户可通过该项目将多个存储设备的文件系统逻辑合并,实现跨设备文件存储与管理。它支持动态添加/移除文件系统、配置文件放置策略、抵抗单点故障,还具备扩展属性、文件属性及运行时配置等功能。【此简介由AI生成】
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 2 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 |
名称
mergerfs - 功能丰富的联合文件系统
概要
mergerfs -o<选项> <分支> <挂载点>
描述
mergerfs 是一款面向简化跨多个商用存储设备上文件存储和管理的联合文件系统。它与 mhddfs、unionfs 和 aufs 类似。
特性
- 可配置的行为 / 文件放置策略
- 按需添加或移除文件系统
- 对单个文件系统故障具有抵抗力
- 支持扩展属性(xattrs)
- 支持文件属性(chattr)
- 运行时配置(通过 xattrs)
- 支持异构文件系统类型
- 当写入时文件系统空间不足时移动文件
- 在创建文件时忽略只读文件系统
- 将只读文件转换为指向底层文件的符号链接
- 硬链接复制写时 / CoW
- 支持 POSIX ACLs
- 其他杂项功能
工作原理
mergerfs 逻辑上合并多个路径。可以将其视为集合的联合。通过 mergerfs 操作或展示的文件或目录取决于为该特定操作选择的策略。关于策略的更多信息请见下文。
A + B = C
/disk1 /disk2 /merged
| | |
+-- /dir1 +-- /dir1 +-- /dir1
| | | | | |
| +-- file1 | +-- file2 | +-- file1
| | +-- file3 | +-- file2
+-- /dir2 | | +-- file3
| | +-- /dir3 |
| +-- file4 | +-- /dir2
| +-- file5 | |
+-- file6 | +-- file4
|
+-- /dir3
| |
| +-- file5
|
+-- file6
mergerfs 不 支持aufs和overlayfs中的写时复制(CoW)或白化行为。您不可以将只读文件系统挂载并进行写入。然而,在创建新文件时,mergerfs将会忽略只读文件系统,因此您可以混合使用读写和只读文件系统。它也不会在文件系统之间分割数据。它不是RAID0/条带化。它仅仅是对其他文件系统的合并。
术语
- branch:池中使用的基路径。
- pool:mergerfs挂载点。branches的联合。
- relative path:相对于branch和挂载点的池中的路径。
- function:文件系统调用(open、unlink、create、getattr、rmdir等)。
- category:基于基本行为(操作、创建、搜索)的函数集合。
- policy:在执行函数时选择文件的算法。
- path preservation:某些策略的一个方面,包括检查将要创建文件的路劲。
基本设置
如果您不知道自己有特殊用例,只需从以下选项集之一开始。
如果您需要mmap(由rtorrent和许多基于sqlite3的软件使用)
cache.files=partial,dropcacheonclose=true,category.create=mfs
如果您不需要mmap
cache.files=off,dropcacheonclose=true,category.create=mfs
命令行
mergerfs -o cache.files=partial,dropcacheonclose=true,category.create=mfs /mnt/hdd0:/mnt/hdd1 /media
/etc/fstab
/mnt/hdd0:/mnt/hdd1 /media mergerfs cache.files=partial,dropcacheonclose=true,category.create=mfs 0 0
systemd挂载
https://github.com/trapexit/mergerfs/wiki/systemd
[Unit]
Description=mergerfs service
[Service]
Type=simple
KillMode=none
ExecStart=/usr/bin/mergerfs \
-f \
-o cache.files=partial \
-o dropcacheonclose=true \
-o category.create=mfs \
/mnt/hdd0:/mnt/hdd1 \
/media
ExecStop=/bin/fusermount -uz /media
Restart=on-failure
[Install]
WantedBy=default.target
查看 mergerfs 维基上的实际部署案例,以获取对比和灵感。
选项
以下选项无论是在使用 mergerfs 命令行程序、fstab 中还是在配置文件中都保持相同。
挂载选项
- 配置文件路径: 配置文件的路径。采用 key=val/ini 风格的相同参数。
- 分支: 用冒号分隔的分支列表。
- 最小空闲空间=大小: 用于创建策略的最小空间值。可以被特定分支的选项覆盖。理解 'K'、'M' 和 'G' 分别表示千字节、兆字节和吉字节。(默认: 4G)
- 空间不足时移动=布尔值|策略: 启用时,如果写入操作由于 ENOSPC(设备空间不足)或 EDQUOT(磁盘配额超出)失败,选择的策略将运行以找到文件的新位置。尝试将文件移动到该分支(保持所有可能的元数据),如果成功,原始文件将被取消链接并重试写入。(默认: false,true = mfs)
- inode计算=透传|路径哈希|设备ino哈希|混合哈希: 选择 inode 计算算法。(默认: 混合哈希)
- 关闭时清理缓存=布尔值: 当请求关闭文件时,首先对其调用
posix_fadvise指示内核我们不再需要数据,它可以丢弃缓存。当 cache.files=partial|full|auto-full|per-process 时推荐使用,以限制双重缓存。(默认: false) - 符号链接化=布尔值: 启用时,如果一个文件不可写且其 mtime 或 ctime 早于 symlinkify_timeout,文件将被报告为指向原始文件的符号链接。在使用前请阅读下面的更多信息。(默认: false)
- 符号链接化超时=无符号整数: 激活 symlinkify 行为等待的时间,以秒为单位。(默认: 3600)
- 空读写=布尔值: 将读和写操作转为无操作。请求将成功但不执行任何操作。对于 mergerfs 基准测试很有用。(默认: false)
- 延迟卸载挂载点=布尔值: 在挂载自身之前,mergerfs 将尝试“延迟卸载”挂载点。在进行 mergerfs 的实时升级时很有用。(默认: false)
- 重命名忽略路径保持=布尔值: 在重命名时忽略路径保持。通常,重命名和链接的行为根据
create的策略(见下文)有所不同。启用此选项将导致重命名和链接始终使用非路径保持行为。这意味着在重命名或链接文件时,文件将保持在相同的文件系统上。(默认: false) - 导出支持=布尔值: 设置一个低级 FUSE 功能,表示文件系统可以通过 NFS 导出。(默认: true)
- 安全属性能力=布尔值: 如果为 false,当查询 xattr security.capability 时返回 ENOATTR。(默认: true)
- 扩展属性=透传|无属性|无系统属性: 运行时控制扩展属性。默认是透传 xattr 请求。'noattr' 将短路,好像什么也不存在。'nosys' 将以 ENOSYS 响应,好像 xattrs 不受支持或已禁用。(默认: 透传)
- 链接写时复制=布尔值: 启用时,如果打开了一个链接计数大于 1 的常规文件,它将复制到临时文件并重命名覆盖原始文件。打破链接并提供类似于 cow-shell 的基本写时复制功能。(默认: false)
- statfs=基本|完整: 控制如何进行 statfs。'基本' 表示它总是使用所有分支进行 statfs 计算。'完整' 表示路径保持,并且只包括路径存在的分支。(默认: 基本)
- statfs忽略=无|只读|无创建: 'ro' 将导致 statfs 计算忽略标记为 '只读' 或 '无创建' 的分支的可用空间。'nc' 将忽略标记为 '无创建' 的分支的可用空间。(默认: 无)
- NFS打开修复=关闭|git|全部: 一个针对通过 NFS 导出 mergerfs 时创建文件的模式设置为只读时的问题的修复。(默认: 关闭)
- 分支挂载超时=无符号整数: 启动时等待分支挂载为除挂载点文件系统之外的其他挂载的时间,以秒为单位。(默认: 0)
- 跟随符号链接=从不|目录|常规|全部: 将符号链接转换成它们指向的内容。(默认: 从不)
- 链接EXDEV=透传|相对符号链接|绝对基础符号链接|绝对池符号链接: 当链接操作失败并返回 EXDEV 时,可选择创建指向文件的符号链接。
- 重命名EXDEV=透传|相对符号链接|绝对符号链接: 当重命名操作失败并返回 EXDEV 时,可选择将文件移动到特殊目录并创建符号链接。
- 预读=无符号整数: 设置 mergerfs 和分支的预读(以千字节为单位),如果大于 0。(默认: 0)
- POSIX ACL=布尔值: 启用 POSIX ACL 支持(如果内核和底层文件系统支持)。(默认: false)
- 异步读=布尔值: 异步执行读操作。如果禁用或不可用,内核将确保每个文件句柄最多有一个挂起的读请求,并尝试按偏移量排序请求。(默认: true)
- FUSE消息大小=无符号整数: 设置每个 FUSE 消息的最大页面数。仅在 Linux >= 4.20 上可用,否则将被忽略。(最小: 1;最大: 256;默认: 256)
- 线程数=整数: 要使用的线程数。当单独使用(
process-thread-count=-1)时,它设置用于读取和处理 FUSE 消息的线程数。当一起使用时,它设置从 FUSE 读取的线程数。当设置为零时,它将尝试发现并使用逻辑核心数。如果线程数设置为负数,它将查找核心数然后除以绝对值。例如,在 8 核机器上使用 threads=-2 将导致 8 / 2 = 4 线程。线程数至少为 1。如果与process-thread-count结合设置为 -1,则它将尝试根据 CPU 线程数选择合理的值。注意:线程数越多,并行度越高,但通常吞吐量会降低。(默认: 0) - 读线程数=整数:
threads的别名。 - 处理线程数=整数: 启用单独的线程池以异步处理 FUSE 请求。在此模式下,
read-thread-count指的是读取 FUSE 消息的线程数,这些消息被分发给处理线程。-1 表示禁用,否则与read-thread-count的行为相同。(默认: -1) - 处理线程队列深度=无符号整数: 设置任何单个处理线程可以排队等待的处理请求的数量。这意味着队列的总内存使用量是队列深度乘以处理线程数加上读线程数。0 设置深度与处理线程数相同。(默认: 0)
- 线程绑定策略=字符串: 选择将线程绑定到 CPU 的策略(默认: 未设置)
- 关闭时刷新=从不|总是|打开用于写入: 在文件关闭时刷新数据缓存。主要用于写入回执已启用或合并网络文件系统时。(默认: 打开用于写入)
- 调度优先级=整数: 设置 mergerfs 的调度优先级。有效值范围从 -20 到 19。有关更多详细信息,请参阅
setpriority手册页面。(默认: -10) - 文件系统名称=字符串: 设置在 mount、df 等中看到的文件系统名称。默认为源路径列表,去除了最长公共前缀后的组合。
- 功能.功能=策略: 设置特定 FUSE 函数的策略。请参见下文的价值类型。例如:func.getattr=newest
- 目录读取策略=顺序|一致性顺序|一致性随机|一致性顺序:整数|一致性随机:整数: 设置
readdir策略。整数值设置用于并发的线程数。(默认: 顺序) - 类别.操作=策略: 设置操作类别中所有 FUSE 函数的策略。(默认: epall)
- 类别.创建=策略: 设置创建类别中所有 FUSE 函数的策略。(默认: epmfs)
- 类别.搜索=策略: 设置搜索类别中所有 FUSE 函数的策略。(默认: ff)
- 缓存打开=无符号整数: 'open' 策略缓存超时时间,以秒为单位。(默认: 0)
- 缓存statfs=无符号整数: 'statfs' 缓存超时时间,以秒为单位。(默认: 0)
- 缓存属性=无符号整数: 文件属性缓存超时时间,以秒为单位。(默认: 1)
- 缓存条目=无符号整数: 文件名查找缓存超时时间,以秒为单位。(默认: 1)
- 缓存负条目=无符号整数: 负文件名查找缓存超时时间,以秒为单位。(默认: 0)
- 缓存文件=libfuse|关闭|部分|完整|自动完整|每进程: 文件页缓存模式(默认: libfuse)
- 缓存文件进程名称=列表: 当
cache.files=per-process时启用页缓存的进程 comm 名称的管道 | 分隔列表。(默认: "rtorrent|qbittorrent-nox") - 缓存写回=布尔值: 启用内核写回缓存(默认: false)
- 缓存符号链接=布尔值: 缓存符号链接(如果内核支持)(默认: false)
- 缓存目录读取=布尔值: 缓存目录读取(如果内核支持)(默认: false)
- 并行直接写入=布尔值: 允许内核为以
cache.files=per-process(如果进程不在process-names中)或cache.files=off打开的文件分派多个、并行的(非扩展)写请求。(这需要内核支持,并在 v6.2 中添加) - 直接IO: 已废弃 - 绕过页缓存。使用
cache.files=off代替。(默认: false) - 内核缓存: 已废弃 - 文件打开时不使数据缓存无效。使用
cache.files=full代替。(默认: false) - 自动缓存: 已废弃 - 如果文件 mtime 或大小更改,使数据缓存无效。使用
cache.files=auto-full代替。(默认: false) - 异步读: 已废弃 - 异步执行读操作。使用
async_read=true代替。 - 同步读: 已废弃 - 同步执行读操作。使用
async_read=false代替。 - 拼接读: 已废弃 - 无操作。
- 拼接写: 已废弃 - 无操作。
- 拼接移动: 已废弃 - 无操作。
- 允许其他: 已废弃 - 从 mergerfs v2.35.0 和更新的版本开始,如果以 root 用户运行,将自动设置此 FUSE 选项。
- 使用inode: 已废弃 - mergerfs 应始终控制 inode 计算,因此此选项始终启用。
注意: 选项将按照列出的顺序进行评估,因此如果选项为 func.rmdir=rand,category.action=ff,则 action 类别的设置将覆盖 rmdir 设置。
注意: 请始终查看您正在使用的 mergerfs 版本的文档。不是所有功能在旧版本中均可用。请使用 man mergerfs 或查找与版本关联的文档链接。
值类型
- BOOL = 'true' | 'false'
- INT = [MIN_INT,MAX_INT]
- UINT = [0,MAX_INT]
- SIZE = 'NNM';NN = INT, M = 'K' | 'M' | 'G' | 'T'
- STR = 字符串(可能指代一个枚举值,参见参数细节)
- FUNC = 文件系统函数
- CATEGORY = 函数类别
- POLICY = mergerfs 函数策略
branches
'branches' 参数是一个由冒号(':')分隔的路径列表,这些路径将被集中在一起。路径是否位于同一文件系统上或不同的文件系统上并不重要,文件系统类型(在合理范围内)也不重要。对于同一文件系统上的路径,使用和可用空间不会重复,任何底层文件系统不支持的功能(如文件属性或扩展属性)将返回相应的错误。
目前 branches 有两个可以设置的选项。一个类型影响分支是否包含在策略计算中,另一个是个别 minfreespace 值。这些值通过在分支指定末尾添加一个 = 并使用逗号作为分隔符来设置。示例:/mnt/drive=RW,1234
分支模式
- RW:(读写)- 默认行为。将在所有策略类别中有效。
- RO:(只读)- 将被排除在
create和action策略之外。与只读挂载的文件系统相同(但处理速度更快)。 - NC:(不可创建)- 将被排除在
create策略之外。不能在该分支上创建,但可以更改或删除。
minfreespace
与全局选项具有相同的目的和语法,但特定于分支。如果未设置,将使用全局值。
花括号展开
为了更容易包含多个分支,mergerfs 支持globbing。在使用 shell 时,花括号展开令牌必须转义,否则 shell 本身会应用 glob。
# mergerfs /mnt/hdd\*:/mnt/ssd /media
上述命令将会使用 /mnt 目录下所有以 hdd 和 ssd 为前缀的挂载点。
若需在启动时挂载存储池或使其可通过相关工具访问,请使用 /etc/fstab 文件。
# <file system> <mount point> <type> <options> <dump> <pass>
/mnt/hdd*:/mnt/ssd /media mergerfs minfreespace=16G 0 0
注意: 路径匹配是在挂载时或使用运行时 API 更新时完成的。如果在事后添加了一个符合路径匹配的新目录,它将不会自动被包含。
注意: 要通过 fstab 进行挂载,您必须安装 mount.fuse。在 Ubuntu/Debian 系统中,它包含在 fuse 软件包中。
inodecalc
inode(st_ino)是文件系统内的唯一标识符。每个挂载的文件系统还具有设备 ID(st_dev),它们共同可以在整个系统中唯一标识一个文件。相同设备上具有相同 inode 的条目实际上是引用相同的底层文件。在名称和 inode 之间是一种多对一的关系。然而,由于目录增加的复杂性,在大多数系统中,目录不会有多个链接。
FUSE 允许服务器(mergerfs)设置 inode 值,但不能设置设备 ID。在 mergerfs 的情况下,创建 inode 值有些复杂,因为文件并不真正受其控制。如果策略更改了要选择的目录或文件,或者带外发生更改,就会不清楚应该使用什么值。大多数软件并不关心这些值是什么,但那些确实关心这些值的软件,如果值意外更改,通常会出错。find 工具如果在目录遍历中看到目录 inode 发生变化,将会终止。如果 inode 带外更改,NFS 可能会返回陈旧的句柄错误。文件去重工具通常会利用设备 ID 和 inode 作为搜索重复文件的快捷方式,如果找到不同的 inode 值,则会转而进行完整的文件比较。
mergerfs 提供了多种计算 inode 的方法,以覆盖不同的使用场景。
- passthrough:传递底层 inode 值。主要用于测试,因为这种方法并没有解决上述任何问题,并且可能会混淆文件去重软件,因为不同文件系统的 inode 可以相同。
- path-hash:对所涉及条目的相对路径进行哈希。完全忽略底层文件的价值。这意味着对于该文件路径,inode 值将始终相同。这在使用 NFS 并且在带外进行更改(例如在分支之间复制数据)时非常有用。这也意味着指向同一文件的条目将无法通过 inode 进行识别。但这不意味着硬链接不起作用。硬链接是有效的。
- path-hash32:path-hash 的 32 位版本。
- devino-hash:对底层条目的设备 ID 和 inode 进行哈希。如果策略选择了不同的文件或文件带外移动,这不会防止 NFS 出现问题,但会对底层文件呈现相同的 inode。
- devino-hash32:devino-hash 的 32 位版本。
- hybrid-hash:对目录执行
path-hash,对其他文件类型执行devino-hash。由于目录不能有硬链接,所以静态值不会产生影响,文件将获得用于查找重复文件的有用值。如果不使用 NFS,这可能是最好的选择。因此,它是默认设置。 - hybrid-hash32:hybrid-hash 的 32 位版本。
提供 32 位版本是因为有些软件不能很好地处理 64 位 inode。
尽管在数百万条目的测试中存在哈希冲突的风险,但实际上没有发生任何冲突。与典型文件系统不同,FUSE 文件系统可以重用 inode 而不引用相同的条目。用于在 FUSE 中引用文件的内部标识符与呈现给客户端的 inode 值不同。前者是 nodeid,实际上是一个由两个 64 位值组成的元组:nodeid 和 generation。这个元组不对客户端公开。呈现给客户端的 inode 是通过内核未解释传递的。
来自 FUSE 文档中关于 use_ino 的描述:
Honor the st_ino field in the functions getattr() and
fill_dir(). This value is used to fill in the st_ino field
in the stat(2), lstat(2), fstat(2) functions and the d_ino
field in the readdir(2) function. The filesystem does not
have to guarantee uniqueness, however some applications
rely on this value being unique for the whole filesystem.
Note that this does *not* affect the inode that libfuse
and the kernel use internally (also called the "nodeid").
自版本 2.35.0 起,已移除 use_ino 选项。mergerfs 应始终管理 inode 值。
pin-threads
关于绑定读取和/或处理线程的简单策略。如果未启用处理线程,策略将仅在读取线程上工作。无效值将被忽略。
- R1L:所有读取线程绑定到单个逻辑 CPU。
- R1P:所有读取线程绑定到单个物理 CPU。
- RP1L:所有读取和处理线程绑定到单个逻辑 CPU。
- RP1P:所有读取和处理线程绑定到单个物理 CPU。
- R1LP1L:所有读取线程绑定到单个逻辑 CPU,所有处理线程绑定到(如果可能)不同的逻辑 CPU。
- R1PP1P:所有读取线程绑定到单个物理 CPU,所有处理线程绑定到(如果可能)不同的物理 CPU。
- RPSL:所有读取和处理线程分散绑定到所有逻辑 CPU。
- RPSP:所有读取和处理线程分散绑定到所有物理 CPU。
- R1PPSP:所有读取线程绑定到单个物理 CPU,同时处理线程分散绑定到其他所有物理 CPU。
fuse_msg_size
FUSE 应用程序通过特殊字符设备 /dev/fuse 与内核通信。与 FUSE 相关的大部分开销是与用户空间和内核空间之间的往复通信成本有关。一般而言,需要的往复次数越少,性能越好。减少往复次数可以通过多种方式实现,内核级别缓存和增加消息大小是两个重要的方法。当涉及到读取和写入时,如果消息大小加倍,往复次数大约减半。
在 Linux 4.20 中添加了一个新特性,允许协商最大消息大小。由于大小以页面为单位,该特性被称为 max_pages。max_pages 的最大值为 256(1MiB),最小值为 1(4KiB)。Linux >=4.20 的默认值为 32(128KiB),而 4.20 之前的硬编码值为 32(128KiB)。在 mergerfs 中,它被称为 fuse_msg_size,以便明确指出其影响并提供一定程度的抽象。
由于增加 fuse_msg_size / max_pages 几乎没有负面影响,除了由于消息缓冲区增大导致的内存使用略微增加,因此 mergerfs 默认将值设为 256。在 4.20 之前的内核上,该值无效。之所以提供可配置的选项是为了便于实验和基准测试。请参阅基准测试部分以获取示例。
follow-symlinks
启用此功能后,mergerfs 将根据模式将符号链接解释为其目标。
当文件有 getattr/stat 请求时,mergerfs 会检查文件是否为符号链接,并根据 follow-symlinks 设置将符号链接的信息替换为其指向的对象的信息。
当解除链接或删除符号链接时,它将删除符号链接本身,而不是它所指向的对象。
- never:正常行为。符号链接被视为符号链接。
- directory:仅解析指向目录的符号链接。
- regular:仅解析指向普通文件的符号链接。
- all:解析所有符号链接到它们指向的对象。
不指向任何内容的符号链接保持不变。
警告:此功能可行,但可能还有边缘情况尚未发现。如果您发现任何异常行为,请在 github 上提交一个工单。
link-exdev
如果在使用路径保留且 link 操作失败并返回 EXDEV,则调用 symlink,其中 target 为 oldlink,linkpath 为 newpath。target 的值由 link-exdev 的值决定。
- passthrough:正常返回 EXDEV。
- rel-symlink:相对于
newpath的相对路径。 - abs-base-symlink:使用基础分支的绝对值。
- abs-pool-symlink:使用 mergerfs 挂载点的绝对值。
注意:某些应用程序可能会检查它们链接的文件。在这种情况下,可能会出现错误或抱怨。
rename-exdev
如果在使用路径保留且 rename 操作失败并返回 EXDEV:
- 将文件从 /branch/a/b/c 移动到 /branch/.mergerfs_rename_exdev/a/b/c。
- 创建一个从 rename 的
newpath到已移动文件的符号链接。
target 的值由 rename-exdev 的值决定。
- passthrough:正常返回 EXDEV。
- rel-symlink:相对于
newpath的相对路径。 - abs-symlink:使用 mergerfs 挂载点的绝对值。
注意:某些应用程序可能会检查它们重命名的文件。在这种情况下,可能会出现错误或抱怨。
注意:abs-symlink 没有像 link-exdev 那样拆分,是因为在存在多个 oldpath 的情况下管理绝对基础符号链接的复杂性。
symlinkify
由于 mergerfs 和底层技术 FUSE 引入的间接级别,可能会出现不同程度的性能下降。此功能将非目录且不可写的文件转换为指向 readlink 策略找到的原始文件的符号链接,条件是 mtime 和 ctime 旧于超时。
警告: 当前实现存在一个已知问题,即如果文件在转换为符号链接时打开并被使用,则打开该文件的应用程序将收到错误。这种情况在实际中不太可能出现,但需要注意。
警告: 一些备份解决方案(如 CrashPlan)不备份符号链接的目标。如果使用此功能,将需要将任何备份软件指向原始文件系统或配置软件以跟随符号链接(如果提供此类选项)。或者创建两个挂载点。一个用于备份,另一个用于一般使用。
nullrw
由于 FUSE 的工作方式,所有对 FUSE 文件系统的请求都会产生一些开销,这些开销在内核文件系统中不存在。这意味着即使是简单的传递也会有一些减速。然而,与底层 I/O 成本相比,这种开销通常是微不足道的。通过禁用底层 I/O,我们可以测试理论上的性能边界。
启用 nullrw 后,mergerfs 将像往常一样工作,但 是所有读取和写入都将是空操作。写操作将成功(返回的写入大小将像它是成功的一样),但 mergerfs 不会对给出的数据做任何事情。类似地,读取将返回请求的大小,但不会触摸缓冲区。
请参阅基准测试部分以获取测试建议。
xattr
运行时扩展属性支持可以通过 xattr 选项进行管理。默认情况下,它将传递任何 xattr 调用。由于 xattr 支持很少使用且可能具有重大性能影响,mergerfs 允许在运行时禁用。性能问题主要出现在启用文件缓存时。内核会在每个写入之前发送一个 getxattr 请求 security.capability。它不会缓存任何 getxattr 的响应。这可能在未来得到解决,但现在 mergerfs 只能提供以下两个 workaround。
noattr 将导致 mergerfs 短路所有 xattr 调用,并在适当的时候返回 ENOATTR。mergerfs 仍然接收到所有请求,但它们不会被转发到底层文件系统。在此模式下,运行时控制仍然有效。
nosys 将导致 mergerfs 对任何 xattr 调用返回 ENOSYS。与 noattr 的区别在于,内核将缓存此事实,并自身短路未来的调用。这比 noattr 更有效,但会导致 mergerfs 的运行时控制通过隐藏文件停止工作。
nfsopenhack
NFS 并非完全符合 POSIX 标准,历史上某些行为(例如以 O_EXCL 模式打开文件)没有得到支持或支持不佳。当 mergerfs(或任何 FUSE 文件系统)通过 NFS 导出时,由于 NFS 和 FUSE 之间的交互,会出现一些问题。
这个补丁解决了以只读模式创建文件但同时带有读/写或只写标志的问题。通常这完全有效,但 NFS 会将一个 open 调用拆分为多个调用。具体如何转换取决于 NFS 服务器和客户端的配置和版本,但最终会导致权限错误,因为普通用户不允许将只读文件作为可写文件打开。
尽管这是一个较为特殊的情况,这个补丁还是打破了正常的安全和行为,因此默认设置为 off。如果设置为 git,它将仅在路径包含 /.git/ 时执行补丁。设置为 all 时,如果打开一个只读且为空的文件进行写入,它将应用此补丁。
export-support
理论上,这个标志不应暴露给最终用户。它是一个低级 FUSE 标志,用于指示内核是否能够发送某些类型的消息给它,以便与 NFS 一起使用。mergerfs 支持这些消息,但由于内核和 mergerfs 中的缺陷和怪异行为,提供了这个选项以便在调试时可能需要。
由于这个标志是在首次启动 FUSE 连接时设置的,因此无法在运行时更改。
FUNCTIONS, CATEGORIES and POLICIES
POSIX 文件系统 API 由许多函数组成。例如 creat、stat、chown 等。为了便于在 mergerfs 中配置,大多数核心函数被分组为 3 个类别:action、create 和 search。这些函数和类别可以被分配一个策略,该策略决定了执行该函数时选择哪个分支。
某些函数,以下列出的 N/A 类别,不能分配正常策略。这些函数使用文件句柄而不是文件路径,文件句柄由 open 或 create 创建。尽管如此,当前的 FUSE 内核驱动程序在客户端调用 fgetattr、fchown、fchmod、futimens、ftruncate 等时,并不总是提供文件句柄。这意味着它将调用常规的、基于路径的版本。statfs 的行为可以通过其他选项进行修改。
当使用基于分支可用空间的策略时,使用的基本路径是提供的。而不是所讨论文件的全路径。这意味着分支内的挂载不会在空间计算中考虑。原因是它对于非路径保留策略来说确实不太有效,并且可能导致不明显的行为。
注意:尽管任何策略都可以分配给函数或类别,但在实践中有些可能不太有用。例如:rand(随机)对于文件创建(create)可能很有用,但如果文件有多个副本,用于 chmod 时可能会产生非常奇怪的行为。
函数及其类别分类
| 类别 | FUSE 函数 |
|---|---|
| action | chmod, chown, link, removexattr, rename, rmdir, setxattr, truncate, unlink, utimens |
| create | create, mkdir, mknod, symlink |
| search | access, getattr, getxattr, ioctl (directories), listxattr, open, readlink |
| N/A | fchmod, fchown, futimens, ftruncate, fallocate, fgetattr, fsync, ioctl (files), read, readdir, release, statfs, write, copy_file_range |
在可能需要搜索某物的场合(例如克隆的路径),通常使用 getattr。
策略
策略是用于选择函数工作的分支或分支的算法,或者通常是函数的行为。
任何 create 类别的函数都将在需要时克隆相对路径。其他一些函数(rename、link、ioctl)有特殊要求或行为,您可以在下面阅读更多相关信息。
过滤
大多数策略基本上是搜索分支并为函数创建一个文件/路径列表。策略负责过滤和排序分支。过滤器包括 minfreespace、分支是否以只读方式挂载,以及分支标签(RO、NC、RW)。这些过滤器适用于大多数策略。
- 没有 search 函数策略的过滤器。
- 所有 action 函数策略过滤出以只读方式挂载或标记为 RO(只读)的分支。
- 所有 create 函数策略过滤出以只读方式挂载、标记为 RO(只读)或 NC(不可创建),或可用空间小于
minfreespace的分支。
策略可能有其自己的额外过滤条件,例如需要存在路径。
如果所有分支都被过滤,将返回错误。通常根据最近过滤分支的原因返回 EROFS(只读文件系统)或 ENOSPC(设备上没有空间)。如果找不到符合条件的分支,则返回 ENOENT。
如果 create、mkdir、mknod 或 symlink 因 EROFS 或其他基本错误而失败,则 mergerfs 将标记任何找到的分支为只读(即设置模式 RO)并重新运行策略重试。这主要是针对遇到错误时突然变为只读的 ext4 文件系统。
路径保留
如以下所述,策略分为两种基本类型:路径保留 和 非路径保留。
所有以 ep 开头的策略(epff、eplfs、eplus、epmfs、eprand)都是 路径保留。ep 代表 existing path。
路径保留策略只考虑已访问的相对路径已存在的分支。
使用非路径保留策略时,如必要,会将路径克隆到目标分支。
使用 msp 或 most shared path 策略时,它们被视为 路径保留 以控制 link 和 rename 的行为,因为 ignorepponrename 可以禁用该行为。
策略描述
如上所述,策略的行为根据它所用的函数而有所不同。有时可能真的没有必要提供某些策略,因为它们实际上与其他策略相同,但这样可以使事物更加统一。
| 策略 | 描述 |
|---|---|
| all | 搜索:对于 mkdir、mknod 和 symlink,它将应用于所有分支。create 的工作方式与 ff 相同。 |
| epall (现有路径,全部) | 对于 mkdir、mknod 和 symlink,它将应用于所有找到的分支。create 的工作方式与 epff 相同(但成本更高,因为它在找到有效分支后不会停止)。 |
| epff (现有路径,第一个找到) | 根据挂载时或运行时配置的分支顺序,选择第一个找到的分支,其中相对路径存在。 |
| eplfs (现有路径,最少空闲空间) | 在所有存在相对路径的分支中,选择空闲空间最少的分支。 |
| eplus (现有路径,最少使用空间) | 在所有存在相对路径的分支中,选择使用空间最少的分支。 |
| epmfs (现有路径,最多空闲空间) | 在所有存在相对路径的分支中,选择空闲空间最多的分支。 |
| eppfrd (现有路径,百分比空闲随机分布) | 类似于 pfrd,但限于现有路径。 |
| eprand (现有路径,随机) | 调用 epall 然后随机化。返回 1 个分支。 |
| ff (第一个找到) | 根据挂载时或运行时配置的分支顺序,对找到的第一个分支进行操作。 |
| lfs (最少空闲空间) | 选择可用空闲空间最少的分支。 |
| lus (最少使用空间) | 选择使用空间最少的分支。 |
| mfs (最多空闲空间) | 选择可用空闲空间最多的分支。 |
| msplfs (最共享路径,最少空闲空间) | 类似于 eplfs,但如果找不到分支,它将尝试使用父目录。继续此模式直到找到分支。 |
| msplus (最共享路径,最少使用空间) | 类似于 eplus,但如果找不到分支,它将尝试使用父目录。继续此模式直到找到分支。 |
| mspmfs (最共享路径,最多空闲空间) | 类似于 epmfs,但如果找不到分支,它将尝试使用父目录。继续此模式直到找到分支。 |
| msppfrd (最共享路径,百分比空闲随机分布) | 类似于 eppfrd,但如果找不到分支,它将尝试使用父目录。继续此模式直到找到分支。 |
| newest | 选择具有最大 mtime 的文件/目录。 |
| pfrd (百分比空闲随机分布) | 根据分支的可用空间相对于总数的可能性随机选择一个分支。 |
| rand (随机) | 调用 all 然后随机化。返回 1 个分支。 |
注意: 如果您使用的是保留块的底层文件系统,例如 ext2、ext3 或 ext4,请注意 mergerfs 通过使用 f_bavail(非特权用户的可用块数)而不是 f_bfree(可用块数)来尊重保留,以进行策略计算。df 不使用 f_bavail,它使用 f_bfree,因此直接比较 df 输出和 mergerfs 策略是不恰当的。
默认值
| 类别 | 策略 |
|---|---|
| action | epall |
| create | epmfs |
| search | ff |
func.readdir
示例:func.readdir=seq、func.readdir=cor:4
readdir 有策略来控制它如何管理读取目录内容。
| 策略 | 描述 |
|---|---|
| seq | "顺序":按照定义的顺序遍历分支。这是在引入 readdir 策略之前的默认和传统行为。 |
| cosr | "并发打开,顺序读取":使用线程池并发打开分支目录,并按定义顺序处理它们。这可以保持较低的内存和CPU使用率,同时减少等待分支响应的时间。线程数默认为逻辑核心数。可以通过语法 func.readdir=cosr:N 覆盖,其中 N 是线程数。 |
| cor | "并发打开并读取":使用线程池并发打开分支目录并立即开始读取其内容。这会导致稍高的内存和CPU使用率,但延迟减少。特别是在使用高延迟/低速度网络文件系统分支时。与 seq 和 cosr 不同,由于线程池的异步性质,文件顺序可能会发生变化。线程数默认为逻辑核心数。可以通过语法 func.readdir=cor:N 覆盖,其中 N 是线程数。 |
请记住,readdir 主要只提供目录中的文件名列表,可能还有一些关于这些文件的基本元数据。要了解文件的详细信息,如 find 或 ls 命令所示,需要调用 stat,由 fuse.getattr 控制。
ioctl
当 ioctl 与打开的文件一起使用时,它将使用在原始 open 调用时创建的文件句柄。然而,当使用 ioctl 与目录合并时,mergerfs 会使用 open 策略来找到要操作的目录。
重命名和链接
注意: 如果在移动/重命名/链接文件时接收到软件错误,则应考虑将创建策略更改为不保持路径的策略,启用 ignorepponrename,或联系有问题的软件作者,请求正确处理 EXDEV(跨设备/不正确链接)。
在联合文件系统中,rename 和 link 是棘手的功能。rename 只能在单个文件系统或设备内工作。如果由于源路径和目标路径位于不同的挂载点而导致无法原子性地重命名,它将返回 -1 并带有 errno = EXDEV(跨设备/不正确链接)。因此,如果 rename 的源和目标位于池内的不同文件系统上,就会产生问题。
最初,mergerfs 在任何形式的跨目录重命名请求时都会返回 EXDEV。这使得代码简单,并且从技术上符合 POSIX 要求。然而,许多应用程序完全无法处理 EXDEV,将其视为普通错误或处理不当。这样的应用程序包括:gvfsd-fuse 版本 1.20.3 及之前的版本、Apple OSX 10.9+ 中的 Finder / CIFS/SMB 客户端、NZBGet、Samba 的回收站功能。
因此,为了使大多数软件能够工作,同时在遵守 mergerfs 的策略的前提下,做了一些折中。以下是基本逻辑。
- 如果使用尝试保持目录路径的 创建 策略(epff、eplfs、eplus、epmfs)
- 使用 重命名 策略获取要重命名的文件列表
- 对每个文件尝试重命名:
- 如果失败并返回 ENOENT(找不到文件或目录),则运行 创建 策略
- 如果创建策略返回与当前正在评估的分支相同的分支,则复制路径
- 重新尝试重命名
- 如果 任何 重命名成功,则将更高级别的重命名视为成功
- 如果 没有 重命名成功,则返回遇到的第一个错误
- 成功后:
- 从所有无源文件的分支中删除目标
- 从所有未能重命名的分支中删除源
- 如果使用不尝试保持目录路径的 创建 策略
- 使用 重命名 策略获取要重命名的文件列表
- 使用 获取属性 策略获取目标路径
- 对每个文件尝试重命名:
- 如果源分支 != 目标分支:
- 从目标分支将目标路径复制到源分支
- 重命名
- 如果源分支 != 目标分支:
- 如果 任何 重命名成功,则将更高级别的重命名视为成功
- 如果 没有 重命名成功,则返回遇到的第一个错误
- 成功后:
- 从所有无源文件的分支中删除目标
- 从所有未能重命名的分支中删除源
移除操作受正常权限检查的约束。
以上行为将有助于最小化返回 EXDEV 的可能性,但仍然有可能。
链接 使用相同的策略,但不进行删除。
statfs / statvfs
statvfs 根据片段大小标准化源文件系统,并累加调整后的块和索引节点数量。这意味着你将看到所有源的总空间,使用空间和可用空间。但是,基于同一硬盘的多个源不会导致空间重复计算。分支树中更下层的其他文件系统在检查挂载统计信息时不会被包括。
可以使用 statfs 和 statfs_ignore 选项修改 statfs 的行为。
关闭时刷新
https://lkml.kernel.org/linux-fsdevel/20211024132607.1636952-1-amir73il@gmail.com/T/
默认情况下,FUSE 会在文件描述符释放之前发出刷新。这被认为有些激进,添加了一个功能,允许 FUSE 服务器选择何时发生。
选项:
- always
- never
- opened-for-write
现在默认为 "opened-for-write",这比添加此功能之前的行为要温和。这应该没问题,因为刷新实际上只与写入文件相关。考虑到对于许多文件系统来说刷新是不相关的,将来可能会添加一个特定分支的标志,以便只有在特定分支上打开的文件才会在关闭时刷新。
错误处理
POSIX 文件系统函数提供一个返回码,这意味着在处理多个分支时会有一些复杂性,正如 mergerfs 所做的那样。它试图以一种通常为特定函数返回有意义值的方式处理错误。
chmod, chown, removexattr, setxattr, truncate, utimens
- 如果没有错误:返回 0(成功)
- 如果没有成功:返回第一个错误
- 如果操作的一个文件与相关搜索函数相同:返回其值
- 返回 0(成功)
这样做虽然增加了错误处理的复杂性和成本,尤其是第 3 步,但可能提供了最合理的返回值。
unlink, rmdir
- 如果没有错误:返回 0(成功)
- 返回第一个错误
mergerfs 的旧版本会在任何成功发生时返回成功,但对于 unlink 和 rmdir,存在下游假设,尽管不可能发生,但可能会使某些软件感到困惑。
其他
对于搜索函数,总是只有一个操作对象,因此返回单个函数调用的返回值。
对于创建函数 mkdir、mknod 和 symlink,由于它们不返回文件描述符,因此可以有 all 或 epall 策略。如果其中任何一个调用成功,它将返回成功,否则返回错误。
安装指南
请访问以下链接获取最新版本:https://github.com/trapexit/mergerfs/releases
如果您的发行版的软件包管理器中已包含mergerfs,请检查版本是否为最新。如果版本过旧,建议使用发布页面上提供的最新版本。以下是一些常见发行版的详细说明。
Debian
大多数Debian安装都是稳定分支,因此不包含最新软件。虽然可以通过apt安装mergerfs,但建议用户安装发布页面上提供的最新版本。
预编译的deb包
wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs_<ver>.debian-<rel>_<arch>.deb
dpkg -i mergerfs_<ver>.debian-<rel>_<arch>.deb
高级包装工具(Advanced Package Tool,简称APT)
sudo apt install -y mergerfs
Ubuntu
大多数Ubuntu安装都是稳定分支,因此并不具备最新版本的软件。虽然可以通过apt安装mergerfs,但建议用户从发布页面安装最新版本的mergerfs。
预编译deb包
wget https://github.com/trapexit/mergerfs/releases/download/<version>/mergerfs_<ver>.ubuntu-<rel>_<arch>.deb
dpkg -i mergerfs_<ver>.ubuntu-<rel>_<arch>.deb
高级包装工具(Advanced Package Tool,简称APT)
sudo apt install -y mergerfs
树莓派操作系统
实际上与 Debian 或 Ubuntu 相同。
Fedora
wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs-<ver>.fc<rel>.<arch>.rpm
sudo rpm -i mergerfs-<ver>.fc<rel>.<arch>.rpm
CentOS 与 Rocky
CentOS 是一款基于 Red Hat Enterprise Linux(RHEL)构建的开源企业级操作系统,以其稳定性和安全性著称。Rocky Linux 则是 CentOS 的一个分支,旨在继续提供与 CentOS 相似的功能和体验,同时保持独立发展。两者均为 IT 管理员和开发者提供了强大的平台支持。
wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs-<ver>.el<rel>.<arch>.rpm
sudo rpm -i mergerfs-<ver>.el<rel>.<arch>.rpm
ArchLinux
- 配置 AUR
- 安装
mergerfs
其他
为满足无法获取本地软件包的情况,我们提供了静态二进制文件。
wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs-static-linux_<arch>.tar.gz
sudo tar xvf mergerfs-static-linux_<arch>.tar.gz -C /
构建
注意: 预编译的软件包可以在以下链接找到,并且推荐给大多数用户使用:https://github.com/trapexit/mergerfs/releases
注意: 仅支持标记的版本。master 以及其他分支应被视为开发中的版本。
首先从 GitHub 获取代码。
$ git clone https://github.com/trapexit/mergerfs.git
$ # or
$ wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs-<ver>.tar.gz
德比安 / 乌班图
$ cd mergerfs
$ sudo tools/install-build-pkgs
$ make deb
$ sudo dpkg -i ../mergerfs_<version>_<arch>.deb
红帽企业级Linux / CentOS / Rocky Linux / FedoraLinux
$ su -
# cd mergerfs
# tools/install-build-pkgs
# make rpm
# rpm -i rpmbuild/RPMS/<arch>/mergerfs-<version>.<arch>.rpm
通用要求
已安装 git、g++、make 和 python。
$ cd mergerfs
$ make
$ sudo make install
构建选项
$ make help
usage: make
make USE_XATTR=0 - build program without xattrs functionality
make STATIC=1 - build static binary
make LTO=1 - build with link time optimization
升级指南
mergerfs 支持实时升级,只需将新版本挂载到旧版本之上。安装新版本的 mergerfs 后,遵循以下步骤即可。
重新运行 mergerfs,或者如果使用 /etc/fstab,请调用其重新挂载。现有的打开文件等将正常工作,但它们不会看到运行时变更,因为任何此类变更都将应用于新的挂载点。如果您打算在新的挂载上更改设置,应在挂载新版本之前应用这些设置。
$ sudo mount /mnt/mergerfs
$ mount | grep mergerfs
media on /mnt/mergerfs type mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
media on /mnt/mergerfs type mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
此方法的一个问题是,即使使用它的软件停止或重新启动,底层的实例仍会继续运行。为了解决这个问题,您可以使用“懒惰卸载”。在用新的mergerfs实例覆盖挂载点之前,请执行以下命令:umount -l <mergerfs挂载点>。或者,您可以让mergerfs自动执行此操作,通过设置选项lazy-umount-mountpoint=true。
运行时接口
运行时配置
.mergerfs 伪文件
<mountpoint>/.mergerfs
在挂载点处存在一个伪文件,允许运行时修改某些 mergerfs 选项。该文件不会在 readdir 中显示,但可以通过 {list,get,set}xattrs 调用进行 stat 查询和操作。
运行时所做的任何更改 不会 被持久化。如果您希望值保持持久,则必须将它们作为选项包含在您配置挂载 mergerfs 的位置(例如 /etc/fstab)。
键值
使用 getfattr -d /mountpoint/.mergerfs 或 xattr -l /mountpoint/.mergerfs 查看所有支持的键值。有些键值是信息性的,因此是只读的。setxattr 在尝试修改只读键值时将返回 EINVAL(无效参数)。
值
与命令行相同。
user.mergerfs.branches
用于查询或修改分支列表。修改时,有几个快捷方式可以轻松地操作列表。
| 值 | 描述 |
|---|---|
| [list] | 设置 |
| +<[list] | 前置 |
| +>[list] | 追加 |
| -[list] | 移除提供的所有值 |
| -< | 移除列表中的第一个元素 |
| -> | 移除列表中的最后一个元素 |
xattr -w user.mergerfs.branches +</mnt/drive3 /mnt/pool/.mergerfs
=NC、=RO、=RW 语法与命令行上的用法相同。
示例
[trapexit:/mnt/mergerfs] $ getfattr -d .mergerfs
user.mergerfs.branches="/mnt/a=RW:/mnt/b=RW"
user.mergerfs.minfreespace="4294967295"
user.mergerfs.moveonenospc="false"
...
[trapexit:/mnt/mergerfs] $ getfattr -n user.mergerfs.category.search .mergerfs
user.mergerfs.category.search="ff"
[trapexit:/mnt/mergerfs] $ setfattr -n user.mergerfs.category.search -v newest .mergerfs
[trapexit:/mnt/mergerfs] $ getfattr -n user.mergerfs.category.search .mergerfs
user.mergerfs.category.search="newest"
文件/目录扩展属性(xattrs)####
尽管在使用 getfattr 命令时不会显示,但 mergerfs 提供了一系列特殊的扩展属性(xattrs),以便查询有关所服务的文件信息。要访问这些值,您需要执行以下之一的 getxattr 操作:
- user.mergerfs.basepath:根据当前的 getattr 策略,文件的基准挂载点
- user.mergerfs.relpath:从挂载点视角出发的文件相对路径
- user.mergerfs.fullpath:根据 getattr 策略的原始文件的完整路径
- user.mergerfs.allpaths:以空字符('\0')分隔的所有找到的文件的完整路径列表
信号
- USR1:这将导致 mergerfs 向内核发送所有文件的无效化通知。这将导致所有未使用的文件从内存中释放。
- USR2:触发当前未使用内存的全面清理。这是每约15分钟发生一次的更彻底版本的清理。
I/O 控制命令
在 fuse_ioctl.cpp 中找到:
typedef char IOCTL_BUF[4096];
#define IOCTL_APP_TYPE 0xDF
#define IOCTL_FILE_INFO _IOWR(IOCTL_APP_TYPE,0,IOCTL_BUF)
#define IOCTL_GC _IO(IOCTL_APP_TYPE,1)
#define IOCTL_GC1 _IO(IOCTL_APP_TYPE,2)
#define IOCTL_INVALIDATE_ALL_NODES _IO(IOCTL_APP_TYPE,3)
- IOCTL_FILE_INFO:与上文提到的“文件/目录扩展属性”相同。使用4096字节的缓冲区大小。传入“basepath”、“relpath”、“fullpath”或“allpaths”字符串。在相同缓冲区中接收详细信息。
- IOCTL_GC:触发对过剩内存的彻底垃圾回收。与SIGUSR2相同。
- IOCTL_GC1:触发对过剩内存的简单垃圾回收。与正常情况下每15分钟发生的垃圾回收相同。
- IOCTL_INVALIDATE_ALL_NODES:与SIGUSR1相同。向内核发送对所有文件的无效通知,导致未使用的文件从内存中释放。
工具
preload.so
实验性功能
一段时间以来,一直在努力实现FUSE的透传IO。透传IO将允许在读取和写入方面实现接近原生性能(以牺牲某些mergerfs功能为代价)。然而,有几个问题一直阻碍这个特性进入主流Linux内核。在该特性可用之前,有两种方法可以提供类似的功能。一种方法是使用动态链接器的LD_PRELOAD功能。另一种方法是利用ptrace拦截系统调用。每种方法都有其缺点。目前只有基于preload的工具可用。如果需要,以后可能会开发基于ptrace的工具。
/usr/lib/mergerfs/preload.so
这个可预加载的库覆盖了文件的创建和打开,以模拟透传文件IO。它捕获open/creat/fopen调用,让mergerfs执行调用,查询文件所在的分支,然后在底层文件系统上重新打开文件并返回。这意味着您将获得原生读写性能,因为mergerfs不再是工作流程的一部分。请记住,这也意味着某些通过中断读写工作流程来实现功能的mergerfs特性,例如moveonenospc,将不再工作。
还需要了解的是,这仅在动态链接软件上工作。任何静态编译的软件都不会工作。许多GoLang和Rust应用程序都是静态编译的。
该库不会干扰非mergerfs文件系统。该库编写为在出错时始终返回mergerfs打开的文件。
虽然该库是为了处理许多边缘情况而编写的,但可能还有一些未考虑到的情况,因此请报告任何异常情况。
感谢nohajc对这一想法的原型设计。
一般用法
LD_PRELOAD=/usr/lib/mergerfs/preload.so touch /mnt/mergerfs/filename
Docker 的使用
假设 /mnt/fs0 和 /mnt/fs1 已经通过 mergerfs 在 /media 上进行聚合。
所有 mergerfs 的分支路径必须以同样的路径绑定挂载到容器中,这样 preload 库才能访问它们。
docker run \
-e LD_PRELOAD=/usr/lib/mergerfs/preload.so \
-v /usr/lib/mergerfs/preload.so:/usr/lib/mergerfs/preload.so:ro \
-v /media:/data \
-v /mnt:/mnt \
ubuntu:latest \
bash
或者更明确地说
docker run \
-e LD_PRELOAD=/usr/lib/mergerfs/preload.so \
-v /usr/lib/mergerfs/preload.so:/usr/lib/mergerfs/preload.so:ro \
-v /media:/data \
-v /mnt/fs0:/mnt/fs0 \
-v /mnt/fs1:/mnt/fs1 \
ubuntu:latest \
bash
systemd 单元
利用 Environment 选项来设置 LD_PRELOAD 环境变量。
- https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html#Command lines
- https://serverfault.com/questions/413397/how-to-set-environment-variable-in-systemd-service
[Service]
Environment=LD_PRELOAD=/usr/lib/mergerfs/preload.so
杂项
- https://github.com/trapexit/mergerfs-tools
- mergerfs.ctl:一种工具,用于在运行时查询和配置mergerfs
- mergerfs.fsck:提供权限和所有者审计功能,并能够修复它们
- mergerfs.dedup:帮助识别并可选地删除重复文件
- mergerfs.dup:确保在存储池中至少有N个文件副本
- mergerfs.balance:通过将文件从最满的文件系统移动到最空的文件系统来重新平衡文件
- mergerfs.consolidate:将单个mergerfs目录中的文件移动到具有最多空闲空间的文件系统
- https://github.com/trapexit/scorch
- scorch:一种工具,用于发现文件的静默损坏并跟踪文件
- https://github.com/trapexit/bbf
- bbf(坏块查找器):一种扫描并“修复”硬盘坏块并找到使用这些块的工具
缓存
页面缓存
https://en.wikipedia.org/wiki/Page_cache
- cache.files=off:禁用页面缓存。底层文件被缓存,mergerfs文件不被缓存。
- cache.files=partial:启用页面缓存。底层文件被缓存,打开时mergerfs文件被缓存。
- cache.files=full:启用页面缓存。底层文件被缓存,跨打开时mergerfs文件被缓存。
- cache.files=auto-full:启用页面缓存。底层文件被缓存,如果自上次打开以来mtime和大小未更改,则跨打开时mergerfs文件被缓存。
- cache.files=libfuse:遵循传统的libfuse
direct_io、kernel_cache和auto_cache参数。 - cache.files=per-process:仅为进程名称匹配
cache.files.process-names中定义的值的进程启用页面缓存(相当于cache.files=partial)。如果名称不匹配,文件打开相当于cache.files=off。
FUSE(mergerfs所使用)提供了多种页面缓存模式。mergerfs试图通过cache.files选项来简化它们的使用。它应该可以并应该替代direct_io、kernel_cache和auto_cache的使用。
由于mergerfs使用FUSE,因此作为用户态进程代理现有文件系统,内核将双重缓存通过mergerfs读取和写入的内容。一次来自底层文件系统,一次来自mergerfs(它将它们视为两个独立的实体)。使用cache.files=off将通过禁用mergerfs的缓存来防止双重缓存的发生,但这会有副作用,即所有的读取和写入调用都将传递给mergerfs,这可能会比启用缓存更慢,你会失去共享mmap支持,这可能会影响如rtorrent等应用程序,并且不会进行预读取。内核仍然会缓存底层文件系统数据,但鉴于mergerfs将处理所有请求,这只能帮助到一定程度。
如果你启用了文件页面缓存,cache.files=partial|full|auto-full,你也应该启用dropcacheonclose,这将导致mergerfs指示内核在文件关闭时刷新底层文件的页面缓存。此行为与rsync fadvise / drop cache补丁和Feh的nocache项目相同。
如果大多数文件是通过读取一次然后关闭(如媒体文件),则最好启用dropcacheonclose,无论缓存模式如何,以减少缓冲区膨胀。
平衡内存使用、缓存膨胀和性能是很困难的。理想情况下,mergerfs应该能够禁用其读写文件的缓存,同时允许对自己的页面缓存。这将限制FUSE的开销。然而,没有好的方法来实现这一点。这将需要以O_DIRECT模式打开所有文件,这限制了支持的底层文件系统的类型并使代码复杂化。
内核文档:https://www.kernel.org/doc/Documentation/filesystems/fuse-io.txt
条目和属性缓存
由于FUSE较高的成本,因为涉及到内核与用户空间的往返,所以有针对文件条目和属性的内核侧缓存。条目缓存限制了lookup调用到mergerfs,该调用询问文件是否存在。属性缓存限制了需要向mergerfs发出getattr调用,以提供文件属性(模式、大小、类型等)。与页面缓存一样,如果同时操作底层文件系统,则不应使用这些缓存,因为这可能导致异常行为或数据损坏。设置这些缓存的选项是cache.entry和cache.negative_entry用于条目缓存,cache.attr用于属性缓存。cache.negative_entry指的是对查找(不存在的文件)的负响应的超时。
写回缓存
当启用cache.files时,默认行为是执行写入穿透缓存。这种行为不会帮助提高性能,因为每个写入仍然一对一地通过文件系统。通过启用FUSE写回缓存,小的写入可以被内核聚合,然后作为一个较大的请求发送给mergerfs。这可以显著提高对文件进行低效写入的应用程序的吞吐量。内核可以聚合的量受FUSE消息大小限制。阅读fuse_msg_size部分了解更多细节。
启用写回缓存会有一个小副作用。底层文件永远不会以O_APPEND或O_WRONLY模式打开。前者是因为内核管理追加模式,后者是因为内核可能请求mergerfs的文件数据以填充写入缓存。O_APPEND的更改意味着如果文件在mergerfs外部被修改,可能会导致损坏,因为内核不知道文件的末尾已更改。也就是说,任何时候使用缓存,都应该避免同时在mergerfs外部使用相同的文件。
请注意,如果应用程序正确地设置写入大小,则写回缓存将几乎没有效果。它只会帮助写入大小低于FUSE消息大小的写入(旧内核上为128K,新内核上为1M)。
statfs缓存
在mergerfs使用的策略中,statfs / statvfs调用可能是最昂贵的。它用于查找文件系统的可用空间和它是否以只读方式挂载。根据设置和用法模式,这些查询可能相对昂贵。当启用cache.statfs时,策略中的所有statfs调用都将缓存为其设置的时间。
例如:如果创建策略是mfs,超时设置为60,那么在那60秒内,由于可用空间不会更新,所以将为创建返回相同的文件系统。
符号链接缓存
从版本4.20开始,Linux支持符号链接缓存。在大量使用符号链接的工作负载中,性能可以显著提高。设置cache.symlinks=true将仅在有支持时请求内核进行符号链接缓存。因此,即使是在4.20之前的系统上,也可以安全地启用它。目前默认是禁用的。你可以通过查询xattr user.mergerfs.cache.symlinks来查看是否启用了缓存,但由于必须在启动时请求,因此无法在运行时更改。
目录遍历缓存
从版本4.20开始,Linux支持目录遍历缓存。这对于目录遍历尤其重要,特别是与条目(cache.entry)和属性(cache.attr)缓存结合使用时。设置cache.readdir=true将在每次opendir时请求内核进行目录遍历缓存。如果内核不支持目录遍历缓存,设置该选项为true没有效果。此选项可以通过xattr user.mergerfs.cache.readdir在运行时配置。
分层缓存
某些存储技术支持所谓的“分层”缓存。通常是将较小、较快的存储作为较大、较慢存储的透明缓存。例如,NVMe、SSD、Optane位于传统HDD之前。
mergerfs原生不支持任何形式的分层缓存。大多数用户不需要此功能,其加入会复杂化代码。然而,在某些情况下,缓存文件系统可能会帮助典型的mergerfs设置。
- 快速网络,慢速文件系统,众多读取者:你有一个10+Gbps的网络,许多读取者,而你的常规文件系统跟不上。
- 快速网络,慢速文件系统,较小爆发性写入:你有一个10+Gbps的网络,并希望快速传输小于你的缓存文件系统但希望快速完成的数据量。
对于#1,你甚至可能会质疑是否应该使用mergerfs。RAID可能是一个更好的解决方案。如果你要使用mergerfs,还有其他策略可能有所帮助:将数据分布到不同的文件系统(参见mergerfs.dup工具)并设置func.open=rand,使用symlinkify,或使用dm-cache或类似技术为底层设备添加分层缓存。
对于#2,你也可以使用dm-cache,但也有另一个只需要mergerfs和cron作业的解决方案。
创建两个 mergerfs 池。一个只包含慢速设备,另一个同时包含快速设备(SSD、NVME 等)和慢速设备。
- 'cache' 池应首先列出缓存文件系统。
- 对于 'cache' 池,最佳的
create策略可能是ff、epff、lfs或eplfs。后两者基于假设缓存文件系统远小于基础文件系统。如果使用路径保留策略,请记住您需要手动创建您希望缓存的路径的核心目录。确保权限同步。使用mergerfs.fsck来检查/修正它们。您也可以将慢速文件系统的模式设置为NC,尽管这意味着如果缓存文件系统满了,您会遇到“空间不足”错误。 - 启用
moveonenospc并适当地设置minfreespace。为了确保在“慢速”池中有足够的空间,您可能希望将minfreespace设置为至少与最大缓存文件系统大小一样大,甚至更大。这样在最坏的情况下,整个缓存文件系统可以移动到其他驱动器上。 - 设置您的程序使用缓存池。
- 保存下面其中一个脚本或创建您自己的脚本。
- 使用
cron(作为 root 用户)来按适合您工作流的频率安排命令。
基于时间的过期
仅根据文件最后一次访问时间从缓存移动到基础池。如果想要以分钟而不是天为单位,请将 -atime 替换为 -amin。可能想要使用 fadvise / --drop-cache 版本的 rsync,或者运行带有 "nocache" 工具的 rsync。
注意: 这些脚本的参数包括缓存文件系统本身。而不是带有缓存文件系统的池。如果源是缓存池,可能会导致数据丢失。
基于满度的过期
将最旧的文件从缓存移动到基础池。直到低于百分比阈值。
注意: 这些脚本的参数包括缓存文件系统本身。而不是带有缓存文件系统的池。如果源是缓存池,可能会导致数据丢失。
性能
mergerfs 在其核心上只是一个代理,因此其理论最大性能与底层设备相同。然而,由于它是一个在用户空间工作的 FUSE 文件系统,与基于内核的解决方案相比,开销相对增加。尽管如此,性能可以达到理论最大值,但很大程度上取决于系统的配置。特别是当网络文件系统加入时,有许多变量可能影响性能。设备速度和延迟、网络速度和延迟、一般并发性、读写大小等。不幸的是,由于变量众多,很难找到一组提供最佳性能的设置。如果您遇到性能问题,请查看下面的建议(包括基准测试部分)。
注意:在更改这些设置之前,一定要阅读有关这些特性的内容,以了解它们可能影响的行为
- 禁用
security_capability和/或xattr - 增加缓存超时
cache.attr、cache.entry、cache.negative_entry - 启用(或禁用)页面缓存(
cache.files) - 启用
parallel-direct-writes - 启用
cache.writeback - 启用
cache.statfs - 启用
cache.symlinks - 启用
cache.readdir - 更改工作线程数
- 禁用
posix_acl - 禁用
async_read - 使用
nullrw或挂载 ram 磁盘来测试理论性能 - 如果您的数据主要是静态和只读的,请使用
symlinkify - 使用分层缓存设备
- 使用 LVM 和 LVM 缓存将 SSD 置于 HDD 前面
- 增加预读:
readahead=1024
如果您发现某个设置显著影响性能,请联系 trapexit 以便他进一步调查。请在您的正常设置、单个分支以及 nullrw=true 下进行测试。
基准测试
文件系统很复杂。它们执行许多操作,其中许多是相互关联的。此外,操作系统、驱动程序、硬件等都可以影响性能。因此,在基准测试时,测试必须尽可能集中。
对于大多数情况,吞吐量是关键基准。要测试吞吐量,dd 很有用,但必须使用正确的设置,以确保实际测试文件系统或设备。操作系统可以并且会缓存数据。如果不强制同步读写或禁用缓存,返回的值将不代表设备的真实性能。
在通过 mergerfs 进行基准测试时,确保只使用 1 个分支,以去除策略复杂化的可能性。首先对底层文件系统进行基准测试,然后在它之上挂载 mergerfs 并再次测试。如果您的速度低于预期,您需要精确缩小导致减速的组件。最好按以下顺序(但不要组合)测试以下内容。
- 启用
nullrw模式,使用nullrw=true。这将有效地使读写操作不执行任何操作。移除底层设备/文件系统。这将给我们最高的理论速度。 - 在
tmpfs上挂载mergerfs。tmpfs是一个 RAM 磁盘。速度极高且延迟极低。这是一个更现实的最佳情况。例如:mount -t tmpfs -o size=2G tmpfs /tmp/tmpfs - 在本地设备上挂载
mergerfs。NVMe、SSD、HDD 等。如果您有多个,我建议测试每一个,因为驱动器和/或控制器可能影响性能。 - 最后,如果您打算将
mergerfs与网络文件系统一起使用,无论是作为数据源还是通过mergerfs与另一个合并,单独测试每一个。
一旦找到有性能问题的组件,您可以进行更多测试以查看不同的选项是否影响性能。对于读写,最相关的是:cache.files、async_read。使用 NFS 或特定文件系统时,不太可能但相关的是 security_capability、xattr 和 posix_acl。如果您发现某个特定的系统、设备、文件系统、控制器等性能不佳,请联系 trapexit 以便他进一步调查。
有时问题实际上是应用程序通过 mergerfs 访问或写入数据。某些软件使用较小的缓冲区大小,这可能导致更多的请求,从而增加开销。您可以通过在下面的示例中将 bs=1M 替换为 ibs 或 obs 并使用 512 而不是 1M 的大小来自己测试这一点。在一个使用 nullrw 的测试示例中,当从 1M 移动到 512 时,写入速度从 4.9GB/s 下降到 69.7MB/s。读取测试也有类似的结果。小写入开销可能通过利用写入缓存来改善,但在非正式测试中几乎找不到增益。需要进行更多测试,此功能才会可用。如果您发现某个应用程序在 mergerfs 上运行缓慢,可能是因为这个原因。请联系 trapexit 以便他进一步调查。
编写性能基准测试
$ dd if=/dev/zero of=/mnt/mergerfs/1GB.file bs=1M count=1024 oflag=nocache conv=fdatasync status=progress
读取性能基准
$ dd if=/mnt/mergerfs/1GB.file of=/dev/null bs=1M count=1024 iflag=nocache conv=fdatasync status=progress
其他基准测试
如果您试图对其他行为进行基准测试,您必须在每次运行前确保清除内核缓存。实际上,在读写基准测试之前执行此操作也是一个很好的做法,以防万一。
sync
echo 3 | sudo tee /proc/sys/vm/drop_caches
提示 / 备注
- 本文档内容详尽且准确。如果某个疑似功能未提及,则表示该功能不存在。如果未列出某些 libfuse 参数,那么很可能不应使用它们。
- 请确保您使用的是最新版本。
- 以
root用户身份运行 mergerfs。mergerfs 被设计为以root用户身份运行,如果以其他用户身份运行可能会出现不正确的行为。 - 如果您没有看到预期的某些目录和文件,策略似乎跳过了分支,或者遇到了奇怪的权限错误等,请确保底层文件系统的权限都相同。使用
mergerfs.fsck来检查文件系统的权限是否同步。 - 如果您仍然遇到权限问题,请确保您使用的是符合 POSIX ACL 的文件系统。mergerfs 通常不对 FAT、NTFS 或其他非 POSIX 文件系统做出例外。
- 如果您希望应用程序(如 rtorrent)使用 mmap 文件,不要 使用
cache.files=off。在 FUSE 中禁用页面缓存时不支持共享 mmap。当cache.files=partial|full|auto-full时,建议启用dropcacheonclose。 - Kodi、Plex、Subsonic 等程序可以使用目录 mtime 更有效地判断是否需要扫描新内容,而不是简单地执行完整扫描。如果使用默认的 getattr 策略为 ff,则这些程序可能会因为返回的是第一个找到的目录的 stat 信息而错过更新,而这个目录是另一个挂载点上的目录,其 mtime 最近被更新。为了解决这个问题,您需要将 func.getattr 设置为 newest。不过请记住,这仅仅是 stat。如果文件稍后被 open 或 unlink,而这些操作的策略不同,则可能会作用于完全不同的文件或目录。
- 某些策略与某些功能的组合可能会导致奇怪的行为。尽管这些行为和竞争条件可能在 mergerfs 之外发生,但由于尝试合并多个可能不同步的数据源,这些情况更有可能发生。
- 为了保持一致性,通常最好设置 category 范围的策略,而不是单独的 func。这将有助于限制如 rsync 等工具的混淆。然而,如果需要,灵活性仍然存在。
已知问题 / Bug
内核问题与 Bug
https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs
目录 mtime 未更新
请记住,getattr 的默认策略是 ff。将返回找到的第一个目录的信息。如果该目录不是最近更新的目录,它将显示为过时。
之所以将此作为默认策略,是因为任何其他策略都会更耗费资源,并且对于许多应用程序来说这是不必要的。要始终返回具有最近 mtime 的目录或基于所有找到的值伪造一个值,将需要对所有文件系统进行扫描。
如果您始终希望从具有最新 mtime 的目录获取目录信息,请为 getattr 使用 newest 策略。
'mv /mnt/pool/foo /mnt/disk1/foo' 删除了 'foo'
这不是一个 Bug。
以详细模式运行以更好地理解发生了什么:
$ mv -v /mnt/pool/foo /mnt/disk1/foo
copied '/mnt/pool/foo' -> '/mnt/disk1/foo'
removed '/mnt/pool/foo'
$ ls /mnt/pool/foo
ls: cannot access '/mnt/pool/foo': No such file or directory
在跨设备工作时,mv 命令的行为是先将源文件复制到目标位置,然后删除源文件。由于在这种情况下源文件就是目标文件,所以根据不同的卸载策略,它会删除刚刚复制的文件以及分支上的其他文件。
如果你想要将文件移动到某个文件系统中,只需将它们复制到那里,然后使用 mergerfs.dedup 清理旧路径,或者直接从分支中手动删除。
缓存内存看起来比应有的要大
使用 cache.files=off 和/或 dropcacheonclose=true。查看页面缓存的章节。
NFS 客户端返回 ESTALE / 陈旧文件句柄
NFS 通常不喜欢带外变更。查看 #remote-filesystems 部分的 NFS 章节了解更多详情。
rtorrent 在尝试使用 ENODEV(没有这样的设备)时失败
确保设置 cache.files=partial|full|auto-full|per-processe 或者关闭 direct_io。rtorrent 和其他一些应用程序使用 mmap 读写文件,并不提供回退到传统方法的功能。在使用 direct_io 的情况下,FUSE 当前不支持 mmap。关闭 direct_io 可能会导致写入性能下降,以及双重缓存的问题,但这是在此类应用程序上唯一可行的方法。如果其他应用程序的性能损失太高,你可以挂载两次 mergerfs。一次启用 direct_io,一次不启用。如果不使用 direct_io,请确保设置 dropcacheonclose=true。
Plex 无法与 mergerfs 一起工作
它可以。如果你试图将 Plex 的配置/元数据/数据库放在 mergerfs 上,你不能设置 cache.files=off,因为 Plex 使用启用了 mmap 的 sqlite3。当页面缓存被禁用时,Linux 的 FUSE 实现不支持共享 mmap。要解决这个问题,可以将数据放在其他位置(首选)或者启用 cache.files(使用 dropcacheonclose=true)。Sqlite3 不需要 mmap,但开发者需要回退到标准 IO,如果 mmap 失败。
这适用于其他软件:Radarr、Sonarr、Lidarr、Jellyfin 等。
我建议联系你遇到问题的软件的开发者,并请求他们在 mmap 不可用时添加回退到常规文件 IO 的功能。
如果问题是扫描似乎无法检测到媒体,那么请确保设置 func.getattr=newest,尽管通常完整扫描会检测到所有媒体。
当程序尝试移动或重命名文件时失败
请阅读上面的关于 重命名 & 链接 的章节。
问题是许多应用程序没有正确处理 EXDEV 错误,尽管 rename 和 link 可能返回这些错误,但它们在设备、文件系统或操作系统错误方面是完全有效的情况。如果使用路径保留策略,mergerfs 将只返回错误,如上面策略部分所述。如果你不关心路径保留,只需将 mergerfs 策略更改为非路径保留版本。例如:-o category.create=mfs。理想情况下,有问题的软件应该被修复,如果你遇到这个问题,建议你联系软件作者并请求正确处理 EXDEV 错误。
我的 32 位软件有问题
一些软件在处理 64 位 inode 值时存在问题。症状可能包括在尝试列出文件时出现 EOVERFLOW 错误。你可以通过将 inodecalc 设置为基于 32 位的算法来解决此问题,具体方法请参阅相关章节。
Samba:移动文件/目录失败
解决方法:复制文件/目录,然后删除原始文件,而不是移动。
这不是 Samba 的问题,而是某些 SMB 客户端的问题。GVFS-fuse v1.20.3 及之前版本(在 Ubuntu 14.04 等系统中发现)未能正确处理某些错误代码。特别是 STATUS_NOT_SAME_DEVICE,它来自在跨挂载点调用 rename 时返回的 EXDEV。当一个程序接收到 EXDEV 时,它需要显式采取另一种操作来达成目标。在 mv 或类似情况中,它尝试 rename,并在 EXDEV 时回退到在两个位置之间手动复制数据并取消链接源文件。在这些较旧的 GVFS-fuse 版本中,如果它接收到 EXDEV,它会将其转换为 EIO。这会导致 mv 或大多数尝试在该 SMB 共享中移动文件的应用程序失败,并显示 IO 错误。
GVFS-fuse v1.22.0 及以上版本修复了这个问题,但许多系统仍在使用较旧的版本。在 Ubuntu 中,可以通过运行 apt-cache showpkg gvfs-fuse 来检查版本。大多数在 2015 年发布的发行版似乎都有更新版本,可以正常工作,但较旧的系统可能不行。升级 gvfs-fuse 或整个发行版通常可以解决这个问题。
在 Apple 的 MacOSX 10.9 中,他们用自己的产品替换了 Samba(客户端和服务器)。看起来他们新的客户端也无法处理 EXDEV,并且与 Linux 上的旧版本 gvfs 响应类似。
偶尔无法将文件移至回收站
这与 Samba 的问题相同。rename 返回 EXDEV(在我们的情况下,这实际上只会发生在路径保留策略如 epmfs 的情况下),软件未能很好地处理这种情况。这是软件在移动文件时常见的失败。标准指出实现可能选择支持非用户主目录的文件删除(这是必须的)。实现可能还支持“顶级目录回收站”,许多软件可能这样做。
要创建标准中定义的 $topdir/.Trash 目录,请使用 mergerfs-tools 工具 mergerfs.mktrash。
补充用户组
由于 getgroups/setgroups 的开销,mergerfs 使用缓存。这个缓存是机会主义的,并且是线程级别的。每个线程在需要更改凭据时查询用户的补充组,并将这些数据保留在线程的生命周期内。这意味着如果一个用户被添加到一个组,可能需要重启 mergerfs 才能检测到。然而,由于高级 FUSE API(至少是标准版本)的线程池动态增长和缩小,因此可能随着时间的推移,一个线程将被终止,稍后一个新的无缓存线程将启动并查询新数据。
gid 缓存使用固定存储来简化设计并与可能没有 C++11 编译器的旧系统兼容。有足够的存储空间来存储 256 个用户的补充组。每个用户允许最多 32 个补充组。Linux >= 2.6.3 允许每个用户最多 65535 个组,但大多数其他 *nix 系统允许的组数要少得多。NFS 仅允许 16 个。系统会优雅地处理溢出。如果用户有超过 32 个补充组,只会使用前 32 个。如果发现未缓存用户时系统中超过 256 个活动用户,系统将随机删除一个现有用户的缓存。只要不超过 256 个活动用户,这应该没问题。如果这些值对于你的需求来说太低,你将不得不修改 gidcache.hpp 来增加这些值。注意,这样做会增加每个线程所需的内存。
虽然这不是一个错误,但一些用户在使用容器时发现,在容器内定义的补充组在权限方面不能正常工作。这是预期的,因为 mergerfs 位于容器外部,因此查询的是宿主机的组数据库。可能有一个技巧可以解决这个问题(使 mergerfs 读取容器中的 /etc/group 文件),但尚未实现,并且将限于 Linux 和 /etc/group 数据库。最好是用户将宿主机上的组文件挂载到容器中,或者使用标准共享用户和组的技术,如 NIS 或 LDAP。
远程文件系统
许多用户询问关于远程文件系统的兼容性。本节将描述在使用 mergerfs 与常见远程文件系统时已知的任何问题或特殊行为。
请记住,就像缓存一样,在远程文件系统上更改内容【带外】(https://en.wikipedia.org/wiki/Out-of-band) 是不是一个好主意。这意味着你真的不应该更改基础文件系统或托管远程文件系统的服务器上的 mergerfs 的内容。这样做可能会导致奇怪的行为、不一致、错误,甚至如果多个程序尝试同时写入或读取相同数据,可能会导致数据损坏。这并不是说你不可以这样做,或者说数据损坏很可能会发生,但是它 可能 会发生。最好始终使用远程文件系统。即使在提供服务的机器上也是如此。
NFS
NFS 是 Unix/POSIX 系统上常见的远程文件系统。由于 NFS 的工作方式,需要设置一些参数才能使 mergerfs 与其协同工作。
应该注意的是,由于 FUSE(mergerfs 使用的技术)的某些设计选择,NFS 和 FUSE 之间并不能完美地协同工作。由于这些问题,通常推荐在可能的情况下使用 SMB,直到情况有所变化。尽管如此,mergerfs 通常可以作为 NFS 导出使用,发现的问题应该仍然报告。
为确保 mergerfs 与 NFS 之间的兼容性,请使用以下设置。
mergerfs 设置:
- noforget
- inodecalc=path-hash
NFS 导出设置:
- fsid=UUID
- no_root_squash
noforget 是必需的,因为 NFS 使用 name_to_handle_at 和 open_by_handle_at 函数,这些函数允许程序在不真正以典型意义上的“打开”文件的情况下保持对文件的引用。问题是 FUSE 无法知道 NFS 是否有一个将来用于再次打开文件的句柄。因此,如果内核告诉 mergerfs 忘记该节点,那么如果 NFS 以后询问该节点的详细信息,它将无话可说。永远保持节点是不理想的,但目前在管理这种情况的唯一方法是这样做。
inodecalc=path-hash 是必需的,因为 NFS 对带外更改敏感。FUSE 不关心文件 inode 值的变化,但是作为有状态的 NFS 则会。所以如果你改变了文件或更新了目录,mergerfs 将使用的文件可能会在不同的分支上,因此 inode 会改变。这不是一个理想的解决方案,其他方案正在考虑中,但在大多数情况下它是有效的。
fsid=UUID 是必需的,因为 FUSE 文件系统没有不同的 st_dev 值,这可能会导致导出时出现问题。最容易的做法是将每个 mergerfs 导出的 fsid 设置为某个随机值。生成随机值的一个简单方法是使用命令行工具 uuid 或 uuidgen,或者通过诸如 uuidgenerator.net 的网站。
no_root_squash 并不是严格必要的,但如果启用了 root 降级,可能会导致权限和所有权的混乱问题。
SMB / CIFS
SMB 是一种主要由 Microsoft Windows 系统用于共享文件共享、打印机等的协议。然而,由于 Windows 的普及,它在包括 Linux 在内的许多其他平台上也得到了支持。Linux 上支持 SMB 的最流行方式是通过 Samba 软件。
Samba 和其他通过 SMB 服务的 Linux 文件系统方法应该与 mergerfs 配合良好。这些服务通常不使用 NFS 所使用的相同技术,因此不会有相同的问题。使用 Samba 时不需要任何特殊设置。然而,CIFSD 和其他程序尚未经过广泛测试。如果你将 mergerfs 与 CIFSD 或其他 SMB 服务器一起使用,请提交你的体验,以便这些文档可以更新。
SSHFS
SSHFS 是一个利用 SSH 作为连接和传输层的 FUSE 文件系统。虽然与 NFS 或 Samba 相比,设置通常更简单,但性能可能不足,且该项目处于维护模式。
使用 sshfs 与 mergerfs 没有已知问题。你可能想要使用以下参数来提高性能,但效果因机器而异。
-o Ciphers=arcfour-o Compression=no
更多详情可以在 这里 找到。
其他
还有其他远程文件系统,但没有广泛用于为 mergerfs 提供服务。如果你使用了上面未列出的东西,欢迎提出,我会将其添加到列表中。
常见问题
mergerfs 的扩展性如何?它“准备好用于生产环境”了吗?
用户报告说,他们在从 Raspberry Pi 到双插座 Xeon 系统(>20 核)的各种设备上运行 mergerfs。我至少知道有几家公司在生产环境中使用 mergerfs。Open Media Vault 将 mergerfs 作为其唯一的文件系统池化解决方案。mergerfs 的作者曾经在一个机器上运行了 300 多天,管理着 16+ 设备,并进行了相当重的 24/7 读写使用。只有在机器的电源损坏后才停止运行。
大多数严重问题(崩溃或数据损坏)都是由 内核错误 引起的。所有这些问题都在稳定版本中得到了修复。
如果文件系统已经有数据/正在使用中,能否使用 mergerfs?
可以。mergerfs 实际上只是一个代理,并且 不会 干扰它管理的文件系统/挂载点/路径的正常形式或功能。它只是另一个作为中间人运行的用户空间应用程序。它不能做任何其他随机软件不能做的事情。
mergerfs 不是 控制底层块设备的传统文件系统。mergerfs 不是 RAID。它 不 操作通过它的数据。它 不 在文件系统之间分片数据。它只是分片一些行为并聚合其他行为。
可以随意从池中移除驱动器/文件系统吗?
可以。参见上一个问题的答案。
不影响数据的情况下可以移除 mergerfs 吗?
可以。参见上一个问题的答案。
可以将驱动器/文件系统移动到另一个池中吗?
可以。参见上一个问题的答案。
当添加/移除驱动器/文件系统时,如何将数据迁移到池中或从池中移除?
你不需要这样做。参见上一个问题的答案。
如何移除驱动器/文件系统但保留池中的数据?
不需要做任何特殊操作。从 mergerfs 的配置中移除分支,并使用 rsync 将移除的文件系统中的数据复制到池中。实际上就像是将数据从一个文件系统转移到另一个文件系统。
如果你想在传输数据时继续使用池,只需创建一个不包含所涉及文件系统的另一个临时池,然后复制数据。在执行复制之前,将分支设置为 RO 可能是一个好主意,以确保在复制过程中不会向文件系统写入新内容。
应该使用哪些策略?
除非你在做一些比较特别的事情,否则普通用户最好使用 mfs 作为 category.create 的策略。它会根据可用空间将文件分散到你的各个分支上。如果你想尝试更多地局部化数据,可以使用 mspmfs。如果你有不同的文件系统大小混合,你可能想要使用 lus 以获得稍微不同的数据分布。一般来说,mfs、lus 或甚至是 rand 都适合一般用例。如果你从一个不平衡的池开始,可以使用 mergerfs.balance 工具重新分配池中的文件。
如果你真的想根据目录尝试局部化文件,可以将 func.create 设置为 epmfs 或类似,并将 func.mkdir 设置为 rand 或 eprand,具体取决于你只是想大致局部化还是特定分支。无论如何,需要局部化的情况是很少的。例如:如果你想要定期移除设备并希望数据可预测地位于该设备上,或者如果你根本不使用备份并希望不逐个替换数据。在这种情况下,使用路径保留可能会有所帮助,但需要一些手动关注。局部化之后可以使用 mergerfs.consolidate 工具来完成。如果你不需要 ep 策略提供的严格局部化,那么你可以使用基于 msp 的策略,它会遍历路径直到找到一个有效的分支。
最终,没有正确的答案。这是一个偏好问题,或者基于某些特定需求。mergerfs 非常容易测试和实验。我建议创建一个测试环境并尝试一下,以了解你的需求。
epmfs 是默认的 category.create 策略,因为 ep 策略不会改变分支的一般布局。它不会将文件/目录放置在没有相对分支的分支上。因此,它保持了系统的已知状态。停止使用 epmfs 或在文件系统中重新分配文件要比将它们合并回去容易得多。
应该使用哪些设置?
这取决于你想要的功能。一般来说,没有“错误”的设置。所有设置都与性能或功能相关。最好的做法是阅读可用的选项,并选择适合你情况的选项。如果文档中的某些内容不清楚,请提出,文档将会改进。
也就是说,对于普通人来说,以下设置应该没问题:
cache.files=off,dropcacheonclose=true,category.create=mfs
为什么我所有的文件都结束在了一个文件系统上?!
您是否从空文件系统开始?您是否明确配置了 category.create 策略?您是否使用了 existing path / path preserving 策略?
默认的创建策略是 epmfs。这是一个路径保留算法。使用这样的策略,对于 mkdir 和 create,在一系列空文件系统上,当第一个目录被创建时,它将只选择一个文件系统。在此目录中创建的任何内容,无论是文件还是目录,都将放置在同一个分支上,因为它保留路径。
这会让许多新用户感到意外,但更改默认设置将会破坏许多现有用户的设置,此策略是最安全的策略,因为它不会更改现有文件系统的总体布局。如果您不关心路径保留,并希望您的文件分布在所有文件系统上,请更改为 mfs 或上述类似的策略。如果您确实需要路径保留,您需要在将数据传输之前,手动在您希望数据驻留的文件系统上创建路径。设置 func.mkdir=epall 可以简化 create 的路径保留管理。或者使用 func.mkdir=rand,如果您只是想按文件系统组合目录内容。
硬链接是否有效?
是的。也请查看 inodecalc 选项了解如何计算 inode 值。
mergerfs 不做的是在分支间伪造硬链接。请阅读“重命名 & 链接”部分了解其工作原理。
请记住,硬链接不能跨设备工作。这包括原始文件系统与 mergerfs 池之间、两个相同底层文件系统的独立池之间,或者在 mergerfs 池内的路径的绑定挂载之间。后者在使用 Docker 或 Podman 时很常见。多个卷(绑定挂载)到同一底层文件系统被视为不同的设备。无法在这些之间链接。如果您希望链接能工作,您应该挂载在包含所有所需路径的 mergerfs 池中的最高目录。
FICLONE 或 FICLONERANGE 是否有效?
不幸的是,不行。mergerfs 所基于的技术 FUSE 不支持 clone_file_range 功能,这是它工作的必要条件。mergerfs 甚至不知道有这样的请求。内核将简单地返回一个错误给发出请求的应用程序。
如果 FUSE 获得了这种能力,mergerfs 将更新以支持它。
我能否在不使用 SnapRAID 的情况下使用 mergerfs?或者在不使用 mergerfs 的情况下使用 SnapRAID?
是的。它们是完全不相关的软件。
mergerfs 能否通过 Docker、Podman、Kubernetes 等运行?
是的。使用 Docker 时,您需要包含 --cap-add=SYS_ADMIN --device=/dev/fuse --security-opt=apparmor:unconfined 或类似的选项,其他容器运行时也是如此。您还应该以 root 用户运行,或者给予足够的能力以更改用户和组标识,以及具有类似 root 的文件系统权限。
请记住,在使用容器时必须考虑身份。例如:补充组将从容器中获取,除非您通过共享相关的 /etc 文件或通过其他方式在容器之间共享身份来适当管理用户和组。同样,如果您使用“无根”容器和用户命名空间进行 uid/gid 转换,您必须在管理共享文件时考虑这一点。
另外,正如 hotio 所提到的,使用 Docker 时,您应该将 bind-propagation 设置为 slave。
mergerfs 是否支持 CoW / 拷贝写入 / 写入只读文件系统?
不支持 BTRFS 或 ZFS 这样的文件系统,也不支持 overlayfs 或 aufs 的方式。但它确实提供了一个类似于 cow-shell 的硬链接断裂(复制到临时文件然后重命名覆盖原始文件),这在希望通过硬链接复制的文件来节省空间时非常有用。
如果您想要写入只读文件系统,您应该查看 overlayfs。您始终可以将 overlayfs 挂载包含在 mergerfs 池中。
为什么我看不到我的文件 / 目录?
几乎总是权限问题。与 mhddfs 和 unionfs-fuse 不同,后者以 root 用户运行并尝试以 root 权限访问内容,mergerfs 始终将凭证更改为调用者。这意味着如果用户无法访问文件或目录,则 mergerfs 也无法访问。然而,由于 mergerfs 创建了路径的联合,它可能在一个文件系统上读取某些文件和目录,而在另一个文件系统上则不然,导致不完整的结果。
每次您遇到分裂权限问题(看到一些但不是所有文件)时,都尝试使用 mergerfs.fsck 工具来检查并修复不匹配。如果您根本看不到任何内容,请确保基本权限正确。用户和组值正确,目录具有执行位设置。Linux 新用户常见的错误是在应该使用 chmod -R u=rwX,go=rX 时使用 chmod -R 644。
如果使用网络文件系统,如 NFS 或 SMB(Samba),请务必密切关注与权限和用户相关的所有内容。例如,root 压缩和用户转换已经困扰了一些 mergerfs 用户。其中一些也影响了从容器平台(如 Docker)使用 mergerfs。
为什么使用 FUSE?为什么不使用基于内核的解决方案?
任何问题的解决方案都有其优点和缺点。
基于 FUSE 的解决方案具有 FUSE 的所有缺点:
- 由于进出内核空间的往返,IO 延迟较高
- 由于进出内核空间的往返,总体开销较高
- 使用页面缓存时的双重缓存
- 由于 FUSE 设计的种种限制
但 FUSE 也有许多优点:
- 更容易提供跨平台解决方案
- 更容易实现前后兼容性
- 用户更容易更新
- 更容易且更快的发布周期
- 在设计和功能方面有更大的灵活性
- 整体而言,更容易编写、更安全、更易于维护
- 进入门槛较低(将代码放入内核需要很多时间和努力)
选择 FUSE 是因为上述所有优点。FUSE 的负面影响并未超过其正面影响。
我的操作系统是否需要 libfuse 才能使 mergerfs 工作?
不需要。通常 mount.fuse 是用来让 mergerfs(或任何 FUSE 文件系统)使用 mount 命令挂载的,但在引入 libfuse 库时,mount.fuse 应用已被重命名为 mount.mergerfs,这意味着在 fstab 中的文件系统类型可以简单地设为 mergerfs。尽管如此,安装它并继续使用 /etc/fstab 中的 fuse.mergerfs 类型应该没有坏处。
如果 mergerfs 作为类型不工作,可能是由于 mount.mergerfs 工具的安装方式。必须位于 /sbin/ 并且具有正确的权限。
为什么移除了 splice 支持?
经过多年的大量测试,splice 操作的表现始终是在最好的情况下提供等效性能,而在某些情况下性能更差。其他平台不支持 splice,因此需要提供传统的读写回退。splice 代码被移除以简化代码库。
mergerfs 不应该用于什么?
- 数据库:即使数据库将数据存储在单独的文件中(否则 mergerfs 没有多少作用),间接的高延迟也会破坏性能。如果它是一个使用较少的 SQLITE 数据库,那可能是可以的,但您需要测试。
- 虚拟机镜像:与数据库的原因相同。虚拟机镜像被频繁访问,而 mergerfs 会引入过多的延迟(如果它 überhaupt 工作)。
- 作为 RAID 的替代:mergerfs 只用于池化分支。如果您需要那种类型的设备性能聚合或高可用性,您应该坚持使用 RAID。
是否可以直接写入文件系统?在池化时从池外写入?
是的,但不建议在池内外同时使用相同的文件(尤其是写入)。特别是如果使用任何形式的缓存(cache.files、cache.entry、cache.attr、cache.negative_entry、cache.symlinks、cache.readdir 等),因为可能会缓存数据与未缓存数据之间发生冲突。
为什么我得到“空间不足”/“设备上没有空间”/ENOSPC 错误,即使看起来还有很多空间可用?
首先确保您已经阅读了关于策略、路径保留、分支过滤以及选项 minfreespace、moveonenospc、statfs 和 statfs_ignore 的部分。
mergerfs 只不过是多个分支内容的联合。报告的可用空间是池内可用空间的汇总(由 statfs 和 statfs_ignore 修改的行为)。它不表示连续空间。就像只读文件系统、具有配额的文件系统或保留空间的文件系统报告完整的理论空间一样。
由于路径保留、分支标记、只读状态和 minfreespace 设置,返回 ENOSPC /“空间不足”/“设备上没有空间”是完全有效的。它正在按照要求执行:根据这些设置过滤可能的分支。只有一个错误可以返回,如果过滤分支的一个原因是 minfreespace,则它将以此类方式返回。moveonenospc 仅与写入当前文件系统上的文件太大相关。
还可能是所选文件系统已用尽 inodes。使用 df -i 列出每个文件系统的总 inodes 和可用 inodes。
如果您不关心路径保留,只需将 create 策略更改为非路径保留的任何一个即可。mfs 可能是大多数人要寻找的。之所以不默认设置是因为它最初设置为 epmfs,现在更改将会更改人们的设置。此类设置更改可能会在 mergerfs 3. 中发生。
为什么合并文件系统(mergerfs)中显示的总可用空间与外部显示的不一样?
您是否正在使用 ext2/3/4 文件系统并为 root 保留了空间?mergerfs 使用可用空间进行 statfs 计算时,如果为 root 预留了空间,则这部分空间将不会显示。
您可以通过运行以下命令来移除预留空间:tune2fs -m 0 <设备名>
我注意到在启用 cache.files 时写入速度大幅下降。
当以任何形式启用文件缓存(cache.files!=off)时,它将在每次写入之前发出 getxattr 请求以获取 security.capability。这通常会导致性能下降,尤其是在使用网络文件系统(如 NFS 或 SMB)时。不幸的是,目前内核不会缓存响应。
为了解决这种情况,mergerfs 提供了以下几种解决方案:
- 设置
security_capability=false。这将绕过任何调用并返回ENOATTR。这意味着尽管 mergerfs 在每次写入前都会接收到请求,但至少不会被传递到底层文件系统。 - 设置
xattr=noattr。与上述相同,但应用于对getxattr的所有调用,而不仅仅是security.capability。内核也不会缓存,但 mergerfs 的运行时配置系统仍将正常工作。 - 设置
xattr=nosys。这将导致 mergerfs 返回ENOSYS,而内核会缓存它。之后的 xattr 调用不会再转发给 mergerfs。缺点是这也意味着基于 xattr 的配置和查询功能也不会工作。 - 禁用文件缓存。如果您没有使用需要
mmap的应用程序,那么最简单的方法可能是完全禁用它。当缓存禁用时,内核不会发送请求。
有提到 mhddfs 存在一些安全问题。是什么问题?mergerfs 如何解决这些问题?
mhddfs 通过调用 getuid() 管理以 root 用户身份运行,如果返回 0,则它会使用 chown 修改文件所有者。这不仅是一个竞争条件问题,而且它还没有处理其他情况。正确的做法是使用 seteuid 和 setegid 来模拟原始调用用户的行为,而不是尝试模拟 POSIX ACL 行为。这是 mergerfs 所做的事情,也是为什么 mergerfs 应始终以 root 用户身份运行的原因。
在 Linux 中,setreuid 系统调用只适用于单个线程。GLIBC 通过使用实时信号来通知所有线程更改凭据来隐藏这个事实。mergerfs 仿效 Samba,使用 syscall(SYS_setreuid,...) 仅设置调用者的线程凭据。在需要提升权限时(例如:在不同文件系统之间复制路径),会跳转回 root。
对于非 Linux 系统,mergerfs 使用读写锁并在必要时更改凭据。如果有多个线程需要成为用户 X,则只有第一个线程需要更改进程的凭据。只要其他线程需要作为用户 X 运行,它们将获取读锁,从而允许多个线程共享凭据。一旦有请求作为用户 Y 运行,该线程将尝试获取写锁并在能够时更改到 Y 的凭据。如果支持给予写入者优先权,则会使用该标志,以便尝试更改凭据的线程不会饿死。这不是最好的解决方案,但在用户数量较少的情况下应该能够合理地工作。
mergerfs 与 X 的比较
mhddfs
mhddfs 已经有一段时间没有维护了,并且存在一些已知的稳定性和安全问题。mergerfs 提供了 mhddfs 功能的超集,并且应该提供相同或更好的性能。
下面是一个使 mhddfs 和 mergerfs 设置相似的示例。
mhddfs -o mlimit=4G,allow_other /mnt/drive1,/mnt/drive2 /mnt/pool
mergerfs -o minfreespace=4G,category.create=ff /mnt/drive1:/mnt/drive2 /mnt/pool
aufs
aufs 已经基本被放弃,并且在大多数 Linux 发行版中不再可用。
虽然 aufs 可以提供更好的峰值性能,但 mergerfs 提供了更多的配置选项且通常更容易使用。然而,mergerfs 不提供 aufs 所具有的覆盖/写时复制(CoW)功能。
unionfs-fuse
unionfs-fuse 与 aufs 类似,它提供了覆盖/写时复制(CoW)功能。如果您只是希望创建文件系统的联合并希望灵活放置文件/目录,那么 mergerfs 提供了这种功能,而 unionfs 更适合在只读文件系统上覆盖读写文件系统。
overlayfs
overlayfs 与 aufs 和 unionfs-fuse 类似,主要用于将读写文件系统层叠到一个或多个只读文件系统上。它没有在多个文件系统间分散文件/目录的能力。
RAID0、JBOD、驱动器串联、条带化
使用简单的 JBOD/驱动器串联/条带化/RAID0 时,单个驱动器故障将导致整个池的故障。mergerfs 执行类似的功能,但不会出现灾难性故障,并且恢复难度较低。驱动器可能会故障,但所有其他文件系统和它们的数据将继续可用。
mergerfs 与其他技术的主要实用区别在于,实际上您没有像使用那些其他技术一样大的连续空间。这意味着您不能在 2TB 的文件池中创建一个 2TB 的文件,如果使用的是两个 1TB 文件系统的池。
当与类似 SnapRaid 的工具和/或离站备份解决方案结合使用时,您可以在没有单一故障点的条件下享有 JBOD 的灵活性。
UnRAID
UnRAID 是一个完整的操作系统,据我所知,其存储层是专有的且闭源的。有经验的用户经常表示,他们更喜欢 mergerfs 提供的灵活性,对于某些用户来说,mergerfs 是开源的这一点也很重要。
还有一些 UnRAID 用户也在使用 mergerfs,尽管我对使用场景不太熟悉。
对于半静态数据,mergerfs + SnapRaid 提供了类似的解决方案。
ZFS
mergerfs 与 ZFS 截然不同。mergerfs 旨在提供对任意文件系统(本地或远程)、任意大小和任意类型的灵活池化管理。适用于“写一次,读多次”的场景,如大量媒体存储。在这种情况下,数据完整性和备份以其他方式管理。在这些场景中,ZFS 会引入如 这里、这里 和 这里 描述的成本和限制。
StableBit's DrivePool
DrivePool 仅在 Windows 上运行,因此不像其他 Linux 解决方案那样常见。如果您想使用 Windows,则 DrivePool 是一个好的选择。这两个项目的功能工作方式略有不同。DrivePool 总是写入拥有最大空闲空间的文件系统,然后重新平衡。mergerfs 不提供重新平衡功能,而是在文件/目录创建时选择一个分支。DrivePool 的重新平衡可以在任何目录中以不同的方式进行,并且具有文件模式匹配来自定义行为。由于 mergerfs 没有重新平衡功能,因此没有这些特性,但这些特性计划在 mergerfs v3 中提供。DrivePool 有内置的文件复制功能,这是 mergerfs 未原生支持的(但可以通过外部脚本实现)。
这两个项目之间有很多其他杂项差异,但 DrivePool 的大多数功能都可以通过结合使用外部工具和 mergerfs 来复制。
此外,DrivePool 是一个闭源的商用产品,而 mergerfs 是一个遵循 ISC 许可的开源项目。
支持
文件系统复杂且难以调试。mergerfs 虽然仅仅是一种代理形式,但由于其可能设置的众多选项以及它可以运行的各种环境,对其进行调试可能会很困难。在报告疑似问题时,请 尽可能提供以下信息,否则诊断可能会变得困难或不可能。此外,请阅读上述文档,因为它提供了许多之前遇到的问题/问题的详细信息。
请确保您正在使用 最新版本 或已经进行了比较。旧版本(通常包含在如 Debian 和 Ubuntu 等发行版中)将永远不会更新,您遇到的问题可能已经被解决。
如需商业支持或功能请求,请 直接与我联系。
虫害报告应包含的信息
- 关于更广泛问题的信息以及尝试的解决方案。
- 已经排除的解决方案及其原因。
- mergerfs 版本:
mergerfs --version - mergerfs 设置/参数:来自 fstab、systemd 单元、命令行、OMV 插件等。
- 操作系统版本:
uname -a和lsb_release -a - 问题发生前后的分支列表、其文件系统类型和大小:
df -h - 所有 与相关路径和文件有关的信息:权限、所有者等。
- 所有 关于发起请求的客户端应用程序的信息:版本、用户 ID/组 ID
- 运行环境:
- mergerfs 是否在容器内运行?
- 客户端应用程序是否在运行 mergerfs 的容器内运行?
- 问题应用程序的
strace:strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>
- 当程序尝试进行其无法完成的操作时,mergerfs 的
strace:strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt
- 精确 的重现问题的说明。不要遗漏任何内容。
- 尝试使用标准程序(如
ln、mv、cp、ls、dd等)以最简单的方式重现问题。
联系方式 / 问题提交
- github.com: https://github.com/trapexit/mergerfs/issues
- discord: https://discord.gg/MpAr69V
- reddit: https://www.reddit.com/r/mergerfs
捐赠
https://github.com/trapexit/support
开发和支持像 mergerfs 这样的项目需要大量的时间和精力。该软件在非常自由的 ISC 许可下发布,因此个人或商业用途都是免费的。
如果您是个人用户,发现 mergerfs 及其支持很有价值,并愿意从财务上支持该项目,将会非常感激。
如果您在商业环境中使用 mergerfs,请考虑赞助该项目,以确保其持续得到维护和更新。如果需要自定义功能,请随时 直接与我联系。