虚拟存储(Linux挂载点合并)

简介

在 Linux 中你有 3 块硬盘(或分区)分别为 100G、200G 和 300 G,如何在一个统一的路径下访问 600G 的视频文件夹呢?并且任何一个磁盘数据的损坏是不能影响其他两个磁盘中的数据,也不影响其他磁盘的正常访问。

相信大多数用户会想到几种解决办法:

        买个更大的硬盘 ̄□ ̄||

        使用 LVM

        组 RAID ......

今天我要介绍 mergerfs 工具,它可以将多个 Linux 分区(挂载点)或硬盘组合成一个虚拟驱动器。这样我们就可以将文件丢到 mergerfs  创建的虚拟驱动器中,它会自动将文件分布到不同的挂载点中,而用户看起来就像是放到了一个统一的路径。

该项目来自https://github.com/trapexit/mergerfs

README重要描述如下:

% mergerfs(1) mergerfs user manual

# NAME

mergerfs - a featureful union filesystem


# SYNOPSIS

mergerfs -o<options> <branches> <mountpoint>


# DESCRIPTION

**mergerfs** is a union filesystem geared towards simplifying storage
and management of files across numerous commodity storage devices. It
is similar to **mhddfs**, **unionfs**, and **aufs**.

mergerfs 是一个面向简化跨多个通用存储设备的文件存储和管理的联合文件系统。它类似于 mhddfs、unionfs 和 aufs

# FEATURES

* Configurable behaviors / file placement
* Ability to add or remove filesystems at will
* Resistance to individual filesystem failure
* Support for extended attributes (xattrs)
* Support for file attributes (chattr)
* Runtime configurable (via xattrs)
* Works with heterogeneous filesystem types
* Moving of file when filesystem runs out of space while writing
* Ignore read-only filesystems when creating files
* Turn read-only files into symlinks to underlying file
* Hard link copy-on-write / CoW
* Support for POSIX ACLs
* Misc other things
可配置的行为/文件放置
任意添加或移除文件系统的能力
抵抗单个文件系统故障
支持扩展属性(xattrs)
支持文件属性(chattr)
运行时可配置(通过 xattrs)
与异构文件系统类型兼容
写入时在文件系统空间不足时移动文件
创建文件时忽略只读文件系统
将只读文件转换为底层文件的符号链接
硬链接写时复制/CoW
支持 POSIX ACLs
其他杂项功能

# HOW IT WORKS

mergerfs logically merges multiple paths together. Think a union of
sets. The file/s or directory/s acted on or presented through mergerfs
are based on the policy chosen for that particular action. Read more
about policies below.
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 does **not** support the copy-on-write (CoW) or whiteout
behaviors found in **aufs** and **overlayfs**. You can **not** mount a
read-only filesystem and write to it. However, mergerfs will ignore
read-only filesystems when creating new files so you can mix
read-write and read-only filesystems. It also does **not** split data
across filesystems. It is not RAID0 / striping. It is simply a union of
other filesystems.

mergerfs 不支持在 aufs 和 overlayfs 中发现的写时复制(CoW)或白出行为。你不能将只读文件系统挂载并对其进行写操作。然而,当创建新文件时,mergerfs 将忽略只读文件系统,因此你可以混合使用读写和只读文件系统。它也不会在文件系统之间分割数据。它不是 RAID0 / 切割。它只是其他文件系统的联合。
# TERMINOLOGY

* branch: A base path used in the pool.
* pool: The mergerfs mount. The union of the branches.
* relative path: The path in the pool relative to the branch and mount.
* function: A filesystem call (open, unlink, create, getattr, rmdir, etc.)
* category: A collection of functions based on basic behavior (action, create, search).
* policy: The algorithm used to select a file when performing a function.
* path preservation: Aspect of some policies which includes checking the path for which a file would be created.
branch: 池中使用的基本路径。
pool: mergerfs 挂载点。分支的联合。
相对路径(relative path):相对于分支和挂载点在池中的路径。
function: 文件系统调用(打开、取消链接、创建、获取属性、删除目录等)。
category: 基于基本行为(动作、创建、搜索)的一组函数集合。
policy: 执行功能时用于选择文件的算法。
path preservation: 一些策略的方面,包括检查将要创建文件的路径。

安装配置mergerfs

初始化磁盘

我是用的系统是debian 12,除去系统盘外,有4块硬盘分别为100G,150G,170G,200G。下来我需要将这四块硬盘组成一个pool进行挂载,对应用程序提供统一对外存储接口。

 目前这些磁盘还没有进行格式化,可以通过fdisk命令或者parted命令进行分区然后格式化为xfs格式。

我这里使用parted进行分区,由于默认没有安装parted,先安装parted工具。

我这里有多块硬盘,如果通过命令一个个初始化在比较花时间,如果硬盘数量更多, 则更为消耗时间,我这里使用脚本创建。大家可以根据自己的需求,修改脚本中的硬盘盘符。

#!/bin/bash

disks=(sdb sdc sdd sde)  # 定义要处理的磁盘列表

for disk in "${disks[@]}"
do
    parted -s /dev/$disk mklabel gpt   # 创建 GPT 分区表

    # 创建一个最大大小的主分区
    parted -a opt /dev/$disk mkpart primary xfs 0% 100%
    sleep 2  # 添加延迟等待系统识别新的分区

    # 格式化分区为 xfs 文件系统(使用 -f 选项强制格式化)
    mkfs.xfs -f /dev/${disk}1
done

 输出结果如下

root@debian:~# ./partition_format.sh
Information: You may need to update /etc/fstab.

meta-data=/dev/sdb1              isize=512    agcount=4, agsize=6553472 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1    bigtime=1 inobtcount=1 nrext64=0
data     =                       bsize=4096   blocks=26213888, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=16384, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
Information: You may need to update /etc/fstab.

meta-data=/dev/sdc1              isize=512    agcount=4, agsize=9830272 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1    bigtime=1 inobtcount=1 nrext64=0
data     =                       bsize=4096   blocks=39321088, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=19199, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
Information: You may need to update /etc/fstab.

meta-data=/dev/sdd1              isize=512    agcount=4, agsize=13107072 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1    bigtime=1 inobtcount=1 nrext64=0
data     =                       bsize=4096   blocks=52428288, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=25599, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
Information: You may need to update /etc/fstab.

meta-data=/dev/sde1              isize=512    agcount=4, agsize=11140992 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1    bigtime=1 inobtcount=1 nrext64=0
data     =                       bsize=4096   blocks=44563968, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=21759, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
root@debian:~# lsblk -f
NAME                  FSTYPE      FSVER            LABEL                 UUID                                   FSAVAIL FSUSE% MOUNTPOINTS
sda
├─sda1                ext4        1.0                                    48541a60-45c9-4b84-b4de-e96b6462b2f2    750.9M    11% /boot
├─sda2
├─sda5                LVM2_member LVM2 001                               eHntBU-4Ajb-VKpF-J7eF-oX4c-m31R-X1f7lj
│ └─vg--swap-lv--swap swap        1                                      07c055d3-bd64-4af1-8ff2-f8bf04040b41                  [SWAP]
└─sda6                LVM2_member LVM2 001                               UEKBAJ-gR23-b1Qy-X6d4-yXDs-4Taz-9TH2Ol
  └─vg-lv             xfs                                                631c94d0-9a71-470f-b144-857d07a3e801     47.7G     4% /
sdb
└─sdb1                xfs                                                60efb5e2-3030-4b33-8721-d395611729c9
sdc
└─sdc1                xfs                                                617cff25-1dd5-465f-b950-eb6bc0a2dfe2
sdd
└─sdd1                xfs                                                6a92643f-be6e-4804-9986-5a0625d5ec57
sde
└─sde1                xfs                                                d7fb2bfc-6a1c-47db-9e83-e4358ba2ed11
sr0                   iso9660     Joliet Extension Debian 12.1.0 amd64 1 2023-07-22-12-15-07-00
root@debian:~# lsblk
NAME                  MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda                     8:0    0   60G  0 disk
├─sda1                  8:1    0  953M  0 part /boot
├─sda2                  8:2    0    1K  0 part
├─sda5                  8:5    0  9.3G  0 part
│ └─vg--swap-lv--swap 254:0    0  9.3G  0 lvm  [SWAP]
└─sda6                  8:6    0 49.8G  0 part
  └─vg-lv             254:1    0 49.8G  0 lvm  /
sdb                     8:16   0  100G  0 disk
└─sdb1                  8:17   0  100G  0 part
sdc                     8:32   0  150G  0 disk
└─sdc1                  8:33   0  150G  0 part
sdd                     8:48   0  200G  0 disk
└─sdd1                  8:49   0  200G  0 part
sde                     8:64   0  170G  0 disk
└─sde1                  8:65   0  170G  0 part
sr0                    11:0    1  3.7G  0 rom

此时已经全部创建完成并且格式化为xfs格式。

安装mergerfs

下载mergerfshttps://github.com/trapexit/mergerfs/releases,根据自己的系统版本下载对应的软件。

此时已经安装完成
可以查看各参数信息,可以通过man mergerfs查看具体参数

 配置/etc/fstab进行挂载

在mergerfs进行挂载的时候需要先将我们前面初始化的磁盘挂载到本地不同的目录上,然后再将不同的目录进行聚合。

创建4个挂载点,对四块硬盘进行挂载

建议使用uuid进行挂载

修改/etc/fstab文件,通过blkid输出的uuid进行挂载,分别挂载的mnt中的不同目录,使用的是xfs格式,defaults 是一种用于指定文件系统挂载选项的常见参数,通常包含了一组默认的挂载选项。当在 /etc/fstab 文件中使用 defaults 作为挂载选项时,系统会根据默认设置自动应用多个常见选项。这些选项通常包括:
rw:允许读写权限。
suid:允许 set-user-ID 权限。
dev:允许创建设备文件。
exec:允许执行二进制文件。
auto:在启动时自动挂载。
nouser:普通用户无法卸载此文件系统。
async:采用异步 I/O 操作。
当使用 defaults 作为挂载选项时,实际上是告诉系统应该使用这些默认选项来挂载文件系统。如果你想明确指定每个选项,可以将它们逐个列出,而不是使用 defaults。
第五个字段(备份选项):
0:表示不执行备份操作。
1:表示应该备份此文件系统。
2:通常与磁带设备一起使用,表示备份并排除数据。
第六个字段(fsck 检查顺序):
0:表示不进行文件系统检查。
1:表示在启动时首先对根文件系统进行检查。
2:表示在启动时对其他文件系统进行检查,但在根文件系统之后进行。
此时4块硬盘已经挂载成功,需要给四块硬盘写入数据,需要分别到/mnt/sd*对应的四块硬盘进行写入数据。

配置mergerfs挂载

root@debian:~# mkdir /pool  #创建目录pool用于存储统一空间入口
root@debian:~# ls /
bin  boot  dev  etc  home  initrd.img  initrd.img.old  lib  lib32  lib64  libx32  media  mnt  opt  pool  proc  root  run  sbin  srv  sys  tmp  usr  var  vmlinuz  vmlinuz.old
root@debian:~#

root@debian:~# cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# systemd generates mount units based on this file, see systemd.mount(5).
# Please run 'systemctl daemon-reload' after making changes here.
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
/dev/mapper/vg-lv /               xfs     defaults        0       0
# /boot was on /dev/sda1 during installation
UUID=48541a60-45c9-4b84-b4de-e96b6462b2f2 /boot           ext4    defaults        0       2
/dev/mapper/vg--swap-lv--swap none            swap    sw              0       0
/dev/sr0        /media/cdrom0   udf,iso9660 user,noauto     0       0
UUID=60efb5e2-3030-4b33-8721-d395611729c9 /mnt/sdb1     xfs     defaults 0 0
UUID=617cff25-1dd5-465f-b950-eb6bc0a2dfe2 /mnt/sdc1     xfs     defaults 0 0
UUID=6a92643f-be6e-4804-9986-5a0625d5ec57 /mnt/sdd1     xfs     defaults 0 0
UUID=d7fb2bfc-6a1c-47db-9e83-e4358ba2ed11 /mnt/sde1     xfs     defaults 0 0
/mnt/sdb1:/mnt/sdc1:/mnt/sdd1:/mnt/sde1 /pool fuse.mergerfs cache.files=partial,dropcacheonclose=true,category.create=mfs 0 0
root@debian:~#

 以上fstab参数为推荐参数,下面为解释,可以根据自己的具体需求进行对应的修改。

fuse.mergerfs:这说明了使用 FUSE 文件系统来合并这些挂载点

cache.files 参数用于控制文件缓存的行为,支持以下不同的选项:

libfuse:直接使用 direct_io、kernel_cache 和 auto_cache 值。
off:禁用页面缓存。
partial:在打开文件时清除页面缓存。
full:在打开文件时保留缓存。
auto-full:如果修改时间和大小未更改,则保留缓存。
per-process:仅为与 cache.files.process-names 中的值匹配的进程启用缓存。
默认值为 libfuse。
dropcacheonclose=true:当文件关闭时丢弃页面缓存。
category.create=mfs:创建特定的元数据缓存策略

 category.create=POLICY: Sets policy of all FUSE functions in the create category.  (default: epmfs)
What policies should I use?
       Unless you’re doing something more niche the average user is probably best off using mfs for category.create.  It will spread files out across your branches  based  on  available  space.   Use
       mspmfs if you want to try to colocate the data a bit more.  You may want to use lus if you prefer a slightly different distribution of data if you have a mix of smaller and larger filesystems.
       Generally though mfs, lus, or even rand are good for the general use case.  If you are starting with an imbalanced pool you can use the tool mergerfs.balance to redistribute files  across  the
       pool.

       If  you really wish to try to colocate files based on directory you can set func.create to epmfs or similar and func.mkdir to rand or eprand depending on if you just want to colocate generally
       or on specific branches.  Either way the need to colocate is rare.  For instance: if you wish to remove the device regularly and want the data to predictably be on that device or if you  don’t
       use  backup  at all and don’t wish to replace that data piecemeal.  In which case using path preservation can help but will require some manual attention.  Colocating after the fact can be ac‐
       complished using the mergerfs.consolidate tool.  If you don’t need strict colocation which the ep policies provide then you can use the msp based policies which will walk back  the  path  till
       finding a branch that works.

       Ultimately  there  is  no correct answer.  It is a preference or based on some particular need.  mergerfs is very easy to test and experiment with.  I suggest creating a test setup and experi‐
       menting to get a sense of what you want.

       epmfs is the default category.create policy because ep policies are not going to change the general layout of the branches.  It won’t place files/dirs on branches that don’t already  have  the
       relative branch.  So it keeps the system in a known state.  It’s much easier to stop using epmfs or redistribute files around the filesystem than it is to consolidate them back.
通过重新挂载,挂载完成后会看到/pool的统一入口,而filesystem则为多个磁盘的集合,总空间为4块硬盘合在一起的总空间。

目前没有写入任何数据,所有磁盘都是空的。该状态下,可以直接给/pool写入数据,也可以单独给每块磁盘写入数据。

数据写入测试 

 直接给/pool中写入多个文件进行测试

此时给pool中写入了多个文件

在pool中创建目录,并向目录中直接写入数据,然后可以查看每个磁盘里面的数据信息
此时数据值写入到sdd1中,当sdd1数据写满后会写入到其他磁盘中

直接在其他磁盘写入文件

在/mnt/sdc1中创建目录啥sdc1-test,并在其中写入文件
在sdc1中直接写入的文件在pool中也可以直接看到

断掉磁盘sdd1,查看数据和pool的情况

卸载掉磁盘sdd1,此时mount中已经没有sdd1磁盘,df -h中也看不到该磁盘信息
此时在sdd1中存储的数据已经全部丢失,但是在sdc1中的数据还是在的

继续向pool中写入数据

此时数据被写入到sde1,整个过程pool没有变化,写入数据的入口都是/pool

总结

mergerfs整个的工作过程是将多个磁盘进行统一挂在在同一个目录上,数据写入会逐个磁盘写入。没有任何的冗余机制,任何一块磁盘的数据丢失也不会影响其他磁盘的数据和整个pool的工作。该方式适合多种不同规格的磁盘组成统一的存储空间。

注意:使用mergerfs需要自己对数据进行备份,可以备份至其他磁盘或者外置空间云盘等位置。备份方式可以参考我的这篇文章高效的nas备份之路

  • 16
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Monster✺◟(∗❛ัᴗ❛ั∗)◞✺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值