Glusterfs 碰撞测试。我们的经验
一年前,我们的好伙伴、同事和企业存储专家出现并说:“嗨,伙计们,我有一个很酷的 90 TB 存储,具有所有这些花哨的功能,你知道的。” 我不能说我们太需要它,但拒绝它会相当愚蠢。所以我们配置了几个备份并忘记了它。
不时地,我们使用该设备在主机之间移动大文件或为 Postgre 副本构建 WAL 等。最终,我们将与我们项目相关的所有零散的东西移到那里结束,并配置了 Rotate 和备份通知,都成功和失败的。一年来,这种存储成为我们运营团队的关键基础设施元素之一。
一切都很好......直到大师再次来找我们说他想要他的东西回来。即刻。紧急。现在。
我们有一点选择。事实上几乎没有:要么把我们的东西推到任何地方,要么用我们手头的东西建立我们自己的存储空间。那个时候聪明的老鸟,我们已经看到了很多不完全容错的系统,所以容错已经成为我们的嗡嗡声。
在众多替代方案中,我们选择了 Gluster。它看起来很有希望。于是我们开始了碰撞测试。
什么是 Gluster,我们为什么需要它?
它是一个与 Openstack 兼容并集成在 oVIrt/RHEV 中的分布式文件系统。尽管我们的 IaaS 不是基于 Openstack,但 Gluster 拥有一个庞大而活跃的社区,并且在 libgfapi 接口中支持原生 QEMU。因此,我们用一块石头杀死了两只鸟:
1. 获得一个我们可以支持自己的备份存储,而无需等待供应商提供替换部件
2. 测试新的卷类型以进一步提供给我们的客户
我们检查了是否:
1. Gluster 工作。——是的,确实如此。
2. Gluster 具有容错性:任何节点都可以在集群仍在运行且数据可用的情况下重新启动,甚至可以在不丢失数据的情况下重新启动多个节点。证明。
3. Gluster 是可靠的,不会自行崩溃,没有内存泄漏等——在某种程度上是正确的。花了一些时间才明白问题不在于我们,而是一个条带卷,它在我们构建的所有配置中仍然不稳定(请参阅下面的详细信息)。
一个月后,我们正在试验和构建不同的配置和版本。然后我们在生产环境中尝试将其作为技术备份的第二个目的地。在采用之前,我们在六个月内密切关注它,以确保它运作良好。
我们是怎么做到的?
我们有足够的硬件进行试验:一个带有戴尔 PowerEdge R510 的机架和一些在我们的旧版 S3 之后留下的不太快的 2 TB SATA 磁盘。20 TB 的存储空间似乎绰绰有余,我们花了大约半个小时在两台旧的 Dell PowerEdge R510 中的每台上安装 10 个磁盘,然后再添加一台服务器作为仲裁器,下载软件并部署所有这些东西。它看起来像这样:
我们选择了带仲裁器的条带复制卷,因为它速度快(数据均匀地分布在多个砖块上)、足够可靠(副本 2),并且可以在没有裂脑的情况下在一个节点崩溃中幸存下来。男孩,我们错了吗?
当时的集群配置的一个主要缺点是通道非常狭窄(1G),这对我们来说不是问题,因为这篇文章是关于系统弹性和灾难恢复的,而不是它的速度。虽然我们计划采用带有 RDMA 的 Infiniband 56G 并测试性能,但这将是另一回事。
我不会深入研究集群创建,因为它非常简单:
为砖块制作目录:
for i in {0..9} ; do mkdir -p /export/brick$i ; done
在磁盘上为砖创建 xfs:
for i in {b..k} ; do mkfs.xfs /dev/sd$i ; done
将挂载点添加到 /etc/fstab:
/dev/sdb /export/brick0/ xfs defaults 0 0
/dev/sdc /export/brick1/ xfs defaults 0 0
/dev/sdd /export/brick2/ xfs defaults 0 0
/dev/sde /export/brick3/ xfs defaults 0 0
/dev/sdf /export/brick4/ xfs defaults 0 0
/dev/sdg /export/brick5/ xfs defaults 0 0
/dev/sdh /export/brick6/ xfs defaults 0 0
/dev/sdi /export/brick7/ xfs defaults 0 0
/dev/sdj /export/brick8/ xfs defaults 0 0
/dev/sdk /export/brick9/ xfs defaults 0 0
山:
mount -a
将名为 holodilnik 的卷的目录添加到砖块中:
for i in {0..9} ; do mkdir -p /export/brick$i/holodilnik ; done
Then make cluster hosts peers and create a volume:
Install software packages on all three hosts:
pdsh -w server[1-3] -- yum install glusterfs-server -y
启动 Gluster:
systemctl enable glusterd
systemctl start glusterd
请记住,Gluster 有几个进程:
glusterd = management daemon.
它是一个主守护进程,用于管理卷和其他控制砖块和数据恢复的守护进程。
glusterfsd = per-brick daemon
它为每块砖启动自己的 glusterfsd 守护进程。
glustershd = self-heal daemon
它会在集群节点故障的情况下重建复制卷上的数据
glusterfs = usually client-side, but also NFS on servers
例如,它可能带有 glusterfs-fuse 原生客户端包 Make nodes peers:
gluster peer probe server2
gluster peer probe server3
组装一个卷(砖的顺序在这里很重要,复制的砖必须彼此跟随):
gluster volume create holodilnik stripe 10 replica 3 arbiter 1 transport tcp server1:/export/brick0/holodilnik server2:/export/brick0/holodilnik server3:/export/brick0/holodilnik server1:/export/brick1/holodilnik server2:/export/brick1/holodilnik server3:/export/brick1/holodilnik server1:/export/brick2/holodilnik server2:/export/brick2/holodilnik server3:/export/brick2/holodilnik server1:/export/brick3/holodilnik server2:/export/brick3/holodilnik server3:/export/brick3/holodilnik server1:/export/brick4/holodilnik server2:/export/brick4/holodilnik server3:/export/brick4/holodilnik server1:/export/brick5/holodilnik server2:/export/brick5/holodilnik server3:/export/brick5/holodilnik server1:/export/brick6/holodilnik server2:/export/brick6/holodilnik server3:/export/brick6/holodilnik server1:/export/brick7/holodilnik server2:/export/brick7/holodilnik server3:/export/brick7/holodilnik server1:/export/brick8/holodilnik server2:/export/brick8/holodilnik server3:/export/brick8/holodilnik server1:/export/brick9/holodilnik server2:/export/brick9/holodilnik server3:/export/brick9/holodilnik force
为了获得稳定的 Gluster,我们必须尝试许多参数组合、内核版本(3.10.0、4.5.4)和 Glusterfs 本身的版本(3.8、3.10、3.13)。最终,我们还得出了以下参数值:
gluster volume set holodilnik performance.write-behind on
gluster volume set holodilnik nfs.disable on
gluster volume set holodilnik cluster.lookup-optimize off
gluster volume set holodilnik performance.stat-prefetch off
gluster volume set holodilnik server.allow-insecure on
gluster volume set holodilnik storage.batch-fsync-delay-usec 0
gluster volume set holodilnik performance.client-io-threads off
gluster volume set holodilnik network.frame-timeout 60
gluster volume set holodilnik performance.quick-read on
gluster volume set holodilnik performance.flush-behind off
gluster volume set holodilnik performance.io-cache off
gluster volume set holodilnik performance.read-ahead off
gluster volume set holodilnik performance.cache-size 0
gluster volume set holodilnik performance.io-thread-count 64
gluster volume set holodilnik performance.high-prio-threads 64
gluster volume set holodilnik performance.normal-prio-threads 64
gluster volume set holodilnik network.ping-timeout 5
gluster volume set holodilnik server.event-threads 16
gluster volume set holodilnik client.event-threads 16
其他有用的参数:
sysctl vm.swappiness=0
sysctl vm.vfs_cache_pressure=120
sysctl vm.dirty_ratio=5
echo "deadline" > /sys/block/sd[b-k]/queue/scheduler
echo "256" > /sys/block/sd[b-k]/queue/nr_requests
echo "16" > /proc/sys/vm/page-cluster
blockdev --setra 4096 /dev/sd[b-k]
虽然这些值在我们的情况下是好的,当它全部与备份有关时,即线性操作,随机读/写需要不同的东西。现在让我们谈谈不同Gluster安装类型的优缺点和失败的测试结果。
我们测试了所有基本的卷挂载选项:
具有 backupvolfile-server 参数的 Gluster Native Client (glusterfs-fuse)
有什么不好:
- 要安装的其他客户端软件
- 速度
不好,但还可以:
- 如果一个集群节点发生故障,则长期无法访问数据。要解决此问题,请在服务器端使用 network.ping-timeout 参数。如果我们将其设置为 5,则对共享文件夹的访问将丢失 5 秒。
有什么好的:
- 运行比较稳定,文件损坏问题很少
Gluster Native Client (gluster-fuse) + VRRP (keepalived)
我们配置了一个在两个集群节点之间迁移的 IP 并关闭其中一个。
有什么不好:
- 要安装的附加软件
有什么好的:
- 集群节点故障时可配置的故障转移超时
事实证明,指定 backupvolfile-server 参数或设置 keepalived 是不必要的,因为客户端可以连接到 Gluster 守护程序(使用任何地址),获取其他地址并开始写入所有集群节点。我们观察到从客户端到 server1 和 server2 的对称流量。即使您指定了 VIP 地址,客户端仍然使用 Glusterfs 集群地址。因此,当启动客户端尝试连接到 glusterfs 服务器,发现它无法访问,然后连接到在 backupvolfile-server 中指定的主机时,此参数很有用。
来自白皮书的评论:
FUSE 客户端允许使用 GlusterFS «round robin» 样式连接进行挂载。在 /etc/fstab 中,使用一个节点的名称;但是,内部机制允许该节点发生故障,并且客户端将滚动到受信任存储池中的其他连接节点。性能比基于测试的 NFS 方法稍慢,但不是很明显。收益是自动 HA 客户端故障转移,这通常值得对性能产生影响。
带有 Pacemaker 的 NFS-Ganesha 服务器
如果出于任何原因不想使用本机客户端,建议使用挂载类型
有什么不好:
- 更多附加软件
- 打扰起搏器
- 发现一个错误https://www.spinics.net/lists/gluster-users/msg33510.html
NFSv3 和 NLM + VRRP(保持活动状态)
具有锁定支持和 IP 在两个集群节点之间迁移的经典 NFS
有什么好的:
- 节点故障时的快速故障转移
- keepalived的简单设置
- nfs-utils 默认安装在我们所有的客户端主机上
有什么不好:
- NFS 客户端在通过 RSYNC 将数据复制到挂载点几分钟后挂在 D 状态
- 运行客户端的节点完全崩溃 — BUG:软锁定 — CPU 因 Xs 卡住!
- 许多情况下,文件因«stale file handle»、«Directory not empty at rm -rf»、«Remote I/O error»和其他错误而损坏
这个选项是最糟糕的,甚至在后来的 Glusterfs 版本中被弃用了。
最后,我们选择了没有keepalived和backupvolfile-server参数的glusterfs-fuse,因为尽管速度相对较低,但它是唯一稳定的选择。
除了配置高可用的解决方案外,在生产使用方面,我们还必须能够在灾难后恢复服务。这就是为什么,最终有了一个稳定的集群,我们进行了破坏性测试:
旧重启
服务器重启后,glustershd 守护进程自动运行数据修复,速度急剧下降。
您可以查看节点崩溃后正在修复的文件数:
[16:41]:[root@sl051 ~]# gluster volume heal holodilnik info
...
Brick server2:/export/brick1/holodilnik
/2018-01-20-weekly/billing.tar.gz
Status: Connected
Number of entries: 1
Brick server2:/export/brick5/holodilnik
/2018-01-27-weekly/billing.tar.gz
Status: Connected
Number of entries: 1
Brick server3:/export/brick5/holodilnik
/2018-01-27-weekly/billing.tar.gz
Status: Connected
Number of entries: 1
...
愈合后,计数器归零并恢复写入速度。
磁盘故障和更换
无论是故障还是用砖块更换磁盘都不会减慢写入共享文件夹的速度。也许,这里的瓶颈是节点之间的通道而不是磁盘速度。一旦我们有额外的 Infiniband 卡,我们就会尝试更快的频道。
请注意,故障磁盘和替换磁盘在 sysfs (/dev/sdX) 中必须具有相同的名称。虽然新磁盘经常被分配下一个字母,但不要让它保持原样,否则在下一次重新启动后磁盘会得到它的旧名称,块设备名称会改变,砖块将无法工作。这就是为什么要采取某些行动的原因。
旧的磁盘挂载点很可能遗留在系统的某个地方。所以,做卸载。
umount /dev/sdX
此外,检查哪些进程可能持有该设备:
lsof | grep sdX
并停止它。
然后重新扫描:
检查 dmesg-H 以获取有关故障磁盘位置的更多详细信息。
[Feb14 12:28] quiet_error: 29686 callbacks suppressed
[ +0.000005] Buffer I/O error on device sdf, logical block 122060815
[ +0.000042] lost page write due to I/O error on sdf
[ +0.001007] blk_update_request: I/O error, dev sdf, sector 1952988564
[ +0.000043] XFS (sdf): metadata I/O error: block 0x74683d94 ("xlog_iodone") error 5 numblks 64
[ +0.000074] XFS (sdf): xfs_do_force_shutdown(0x2) called from line 1180 of file fs/xfs/xfs_log.c. Return address = 0xffffffffa031bbbe
[ +0.000026] XFS (sdf): Log I/O Error Detected. Shutting down filesystem
[ +0.000029] XFS (sdf): Please umount the filesystem and rectify the problem(s)
[ +0.000034] XFS (sdf): xfs_log_force: error -5 returned.
[ +2.449233] XFS (sdf): xfs_log_force: error -5 returned.
[ +4.106773] sd 0:2:5:0: [sdf] Synchronizing SCSI cache
[ +25.997287] XFS (sdf): xfs_log_force: error -5 returned.
其中 sd 0:2:5:0 是:
h == hostadapter id (first one being 0)
c == SCSI channel on hostadapter (first one being 2), which is also a PCI slot
t == ID (5), which is also slot number of a failed disk
l == LUN (first one being 0)
重新扫描:
echo 1 > /sys/block/sdY/device/delete
echo "2 5 0" > /sys/class/scsi_host/host0/scan
其中 sdY 是要替换的磁盘的错误名称。
然后,对于砖块更换,创建一个新目录进行挂载,创建文件系统并挂载它:
mkdir -p /export/newvol/brick
mkfs.xfs /dev/sdf -f
mount /dev/sdf /export/newvol/
更换砖块:
gluster volume replace-brick holodilnik server1:/export/sdf/brick server1:/export/newvol/brick commit force
开始治疗:
gluster volume heal holodilnik full
gluster volume heal holodilnik info summary
仲裁失败:
共享文件夹的相同 5-7 秒不可访问以及与仲裁节点的元数据同步导致的 3 秒下垂。
概括
破坏性测试结果鼓励我们将其部分用于生产用途,但这只是短暂的快乐……
问题 1,这也是一个已知的错误:
在删除大量文件和目录(大约 100,000 个)时,我们得到以下信息:
rm -rf /mnt/holodilnik/*
rm: cannot remove ‘backups/public’: Remote I/O error
rm: cannot remove ‘backups/mongo/5919d69b46e0fb008d23778c/mc.ru-msk’: Directory not empty
rm: cannot remove ‘billing/2018-02-02_before-update_0.10.0/mongodb/’: Stale file handle
自 2013 年以来,我已经阅读了大约 30 份类似的用户投诉。这个问题没有解决方案。
Red Hat 建议更新版本,但对我们没有帮助。
https://access.redhat.com/solutions/1264803
我们的解决方法是简单地清除所有节点上的砖块中剩余的损坏目录
pdsh -w server[1-3] -- rm -rf /export/brick[0-9]/holodilnik/<failed_dir_path>
振作起来,最糟糕的还没有到来。
问题2,最糟糕的:
我们试图解压一个包含许多文件的存档,该存档包含在条带卷上的共享文件夹中,结果 tar xvfz 处于“不间断睡眠”状态,只能通过重新启动客户端节点来治愈。
在意识到我们再也无法忍受之后,我们选择了唯一尚未尝试但相当棘手的配置——擦除编码。关于它的唯一困难是理解它的卷创建原理。Red Hat 提供了一个很好的示例手册
在运行所有相同的破坏性测试后,我们获得了同样令人鼓舞的结果。我们编写然后删除了数百万个文件。我们所有打破分散体积的尝试都失败了。我们观察到更高的 CPU 负载,但这对我们来说并不重要,目前还不是。
现在,它存储我们的基础设施段备份,并用作我们内部需求的文件仓库。我们想花一些时间使用它,看看它在各种负载下的表现。到目前为止,很明显条带卷以一种奇怪的方式工作,而其他卷则表现得非常好。我们还计划在具有宽 Infiniband 通道的 6 台服务器上构建一个 50 TB 的分散卷(4 + 2),测试其性能,并继续研究其运行原理。