针对我的应用场景(一台放在没有公网IP的家宽下、挂载有大容量硬盘的ARM板用作NAS,另一台有公网IP、带宽较大但硬盘很小的VPS,为了让VPS获得较大的硬盘空间),琢磨出的一套远程挂载硬盘且较为高效的在Linux上挂载硬盘的方案。

(【2021年6月17日 补充说明】使用iSCSI替换NFS和qemu-nbd可以获得更好的性能,且运行更稳定,配置过程可参数局域网配置iSCSI服务端及客户端
实际上,使用tinc构建了虚拟局域网以后,再通过NFS就已经可以挂载硬盘空间了。然而,实际使用时发现,在有大量随机读写的情况下NFS的效率会变得非常糟糕,为了提高性能,有基于文件缓存的cachefilesd方案,然而这种方案只适用于大量的小文件,如果出现大文件则不会被缓存。一个进一步提高性能的方法是创建img文件格式的虚拟硬盘,通过挂载为loop设备,将虚拟硬盘格式化为ext4格式,借用内核的缓存机制对读写性能进行进一步优化,但是这种方法会占用内核的cache空间,对于小内存的VPS提升不够明显,实际使用时在出现大量读写的情况下仍然会导致系统负载率飙升。由于VPS虽然硬盘空间较小,但相对于内存而言其空间仍然较为充裕,且速度显然快于基于公网的网络存储设备。为了进一步提高性能,这里引入了bcache对网络存储进行再进一步的优化。bcache常用于使用SSD加速HDD,由于它是基于块的缓存,这里通过qemu-nbd将虚拟硬盘挂载为块设备。
本文中VPS的系统为Debian 10,NAS的系统为Ubuntu 20.04。VPS上的命令均需要root权限。且这种方法要求家宽NAS与VPS之间具有良好且稳定的网络连接。

将家庭NAS和公网VPS连接起来

Linux上常用的NFS并不适用于公网环境,可以通过任意方法(比如zerotier、tinc等)假设一个虚拟局域网,将公网设备和内网设备连接连接在一个虚拟局域网中。本文已通过《搭建tinc实现异地构建局域网》中所述的方法假设好了虚拟局域网,并且使用《在Debian上使用脚本自动重启崩溃及异常退出的程序或服务》设置了针对tinc的守护脚本,使其能够在tincd或tun设备崩溃的情况下自动恢复服务(注意,ARM设备还包含重新加载tun模块的内容,因为板子和内核各异,这里不做赘述)。
以下假定NAS的内网IP是10.20.20.2,VPS的内网IP是10.20.20.1。

创建和挂载NFS

关于NFS的配置方法在《配置NFS,以及使用NFS进入子系统》中已进行了描述。本文中,ARM的板子(NAS)为NFS的服务器,VPS为NFS客户端。假定NFS服务器暴露了路径/mnt/sda1/Share给VPS,那么VPS通过以下命令创建本地路径/mnt/nfs,并挂载远程共享路径为本地路径

mkdir /mnt/nfs
mount -t nfs 10.20.20.2:/mnt/sda1/Share /mnt/nfs

创建虚拟硬盘

在NAS上,cd到共享目录中,创建一个共享硬盘,此处以100G硬盘空间、文件名为nfs.data.img为例,通过fallocate命令对该部分空间进行预分配,并允许NFS客户端写入。

cd /mnt/sda1/Share
fallocate -l 100G nfs.data.img
chmod ugo+rw nfs.data.img

在VPS上,在本地硬盘创建一个虚拟硬盘,用作缓存硬盘。此处以容量2G、文件名及路径为/mnt/nfs.cache.img为例

fallocate -l 2G /mnt/nfs.cache.img

通过nbd连接硬盘

使用nbd是为了将虚拟硬盘挂载为块设备,以便于通过bcache进行加速。qemu-utils中包含了通过nbd连接虚拟硬盘的相关工具,以下命令通过apt安装和启用nbd设备

apt-get install qemu-utils
modprobe nbd
qemu-nbd --connect=/dev/nbd0 --format=raw /mnt/nfs/nfs.data.img
qemu-nbd --connect=/dev/nbd1 --format=raw /mnt/nfs.cache.img

此时,需要保存数据的硬盘被映射为/dev/nbd0,需要用作缓存的硬盘被映射为/dev/nbd1。可直接对/dev/nbd0进行操作,但为了便于后续扩容、迁移等工作,这里还是对/dev/nbd0进行分区。使用fdisk /dev/nbd0通过fdisk进行分区,输入n添加分区,输入p创建为主分区,之后输入w保存并退出。也可以使用parted、gparted等其他工具对该虚拟硬盘进行分区操作。
创建分区后,会新增一个/dev/nbd0p1的设备(如果有多个分区则会出现多个设备)。

配置缓存

以下命令安装和启用bcache

apt-get install bcache-tools
modprobe bcache

将数据分区和缓存硬盘擦除

wipefs -a /dev/nbd0p1
wipefs -a /dev/nbd1

创建数据区和缓存区

make-bcache -B /dev/nbd0p1
make-bcache -C /dev/nbd1

此时,应该能通过lsblk看到bcache设备。如果没有,则需要手动注册这两个区。

echo /dev/nbd0p1 >/sys/fs/bcache/register
echo /dev/nbd1 >/sys/fs/bcache/register

在运行make-bcache时应当能看到缓存区的uuid,也可以使用bcache-super-show命令查看bcache设备的参数,找到cset.uuid项,将缓存区的uuid记录下来。

bcache-super-show /dev/nbd1

此处以"8a48e761-a817-486c-afd1-5e3a4478e656"为例,将它与数据区绑定。

echo "8a48e761-a817-486c-afd1-5e3a4478e656" >/sys/block/bcache0/bcache/attach

bcache默认采用writethrough模式,即写入数据时会同时向数据区和缓存区写入,直到两者都完毕才返回写入完毕。在本文的应用中,为了提高性能,将其修改为writeback模式,即数据优先写入缓存区便返回写入完毕,之后再写入数据区。

echo writeback >/sys/block/bcache0/bcache/cache_mode

完成这些后,/dev/bcache0变为实际抽象后的存储设备。将其格式化并挂载到/mnt/nfs_data目录上

mkfs.ext4 /dev/bcache0
mkdir /mnt/nfs_data
mount /dev/bcache0 /mnt/nfs_data

至此,便能以较高的性能挂载远程硬盘。
注意,ext4文件系统有一个缓慢初始化的功能(ext4lazyinit),刚格式化的分区会在挂载后慢慢完成剩余初始化数据的写入工作,表现是在挂载后即使没有向内写入数据,后台也会有少量的持续写入。如果需要在格式化时立即完成该过程,mkfs.ext4的步骤可以附加参数:

mkfs.ext4 -E lazy_itable_init=0,lazy_journal_init=0 /dev/bcache0

在挂载时可以启用discard和noatime选项。其中discard选项将在删除文件时释放文件系统占用的数据块,在涉及删除文件的操作时可以减少后备分区的写入。在大多数文件系统中,删除的文件只是被添加了删除的标记,其占用的储存区域直到下次被填入时,才相当于文件被删除,而在bcache中如果一个已写入缓存区而未写入数据区的文件被删除了,这时块设备只需要在缓存区将数据删除掉即可,而不会将删除的数据再写入数据区。noatime则指文件系统不会储存上次访问文件的时间,可以减少硬盘IO。挂载时启用这两个选项的挂载命令如下:

mount /dev/bcache0 /mnt/nfs_data -o discard,noatime

为了进一步提高bcache在该场景下的性能,首先可以将sequential_cutoff设置为0,该参数控制顺序写入的阈值,当顺序写入量超过阈值时则直接写入数据区,设置为0即不启用顺序写入直接进入数据区的选项,默认所有写入数据先进入缓存区:

echo 0 >/sys/block/bcache0/bcache/sequential_cutoff

此外bcache会跟踪每个IO操作,当出现IO时间超过阈值的情况时,数据读写会跳过缓存区,直接在数据区进行。在该场景下,通常远端硬盘IO性能远小于本地硬盘,可以直接将该功能关闭以节省服务器开销:

echo 0 > /sys/fs/bcache/8a48e761-a817-486c-afd1-5e3a4478e656/congested_read_threshold_us
echo 0 > /sys/fs/bcache/8a48e761-a817-486c-afd1-5e3a4478e656/congested_write_threshold_us

卸载该远程硬盘设备

即将上述流程反过来执行一遍。

#卸载bcache抽象的存储设备
umount /dev/bcache0

#停止设备
echo 1 >/sys/block/bcache0/bcache/stop

#取消缓存区和数据区的挂载
echo "8a48e761-a817-486c-afd1-5e3a4478e656" >/sys/block/bcache0/bcache/detach

#反注册缓存区
echo 1 >/sys/fs/bcache/8a48e761-a817-486c-afd1-5e3a4478e656/unregister

#断开nbd设备
qemu-nbd --disconnect /dev/nbd0
qemu-nbd --disconnect /dev/nbd1

#卸载NFS路径
umount /mnt/nfs

之后根据自己的需要选择是否删除挂载目录和缓存文件等。

直接挂载数据区

数据区的原始数据存放在偏移量为8192处,使用losetup将其装载到loop设备后便可直接挂载并读取数据。
在该例子中,将后备数据挂载到/dev/loop0并将内部文件系统挂载到/dev/shm/bcache的例子如下:

losetup -o 8192 /dev/loop0 /dev/nbd0p1
mount /dev/loop0 /dev/shm/bcache

标签: Linux, Debian, Ubuntu

添加新评论