事件经过

有位仁兄在服务器上的黑群处于开机状态时,把机柜里的硬盘直接拔了出来……被拔的硬盘是组了RAID0的两块盘之一。当时没有注意,但当晚服务器风扇狂转,阵列寄了。

重启,一块盘显示Foreign,另一块显示Ready。这是不太能恢复阵列的状态——正常情况下如果盘掉了也是显示两块Foreign。Ready显示这块盘上的配置也丢了,可能是硬盘掉电造成的。实际上我推测,在掉盘的几小时里,两块盘处于这样的状态:写到好的盘上的请求能进行,但写到坏的盘上的就被写进了阵列卡的Cache,最终寄了。

而在这样的状态下,导入Foreign设置貌似不可能了,因此我清除了Cache并删除了Foreign设置,重新建了RAID0(不要初始化,否则会被抹掉),开了一个新的系统并把硬盘挂上去。结果没法在系统中挂载,同时各种btrfs的各种工具都没法恢复。具体的尝试过程就不写了,不论是用-orecovery挂载,用fsck,btrfsck还是用btrfs check都会报 btrfs: Couldn't setup device tree。

同时,在上述过程中,由于对掉的盘上写入请求(一部分进了Cache里)被丢了,两块盘中的文件系统处于严重的不统一状态。

恢复方式

btrfs的Wiki上写到了“最后的恢复方式”分为两步:

  • 通过btrfs-find-root找到一个可用的root(这个应该和btrfs储存文件的结构有关,还要进一步研究一下)
  • 通过btrfs restore进行离线恢复,即将可恢复的内容拷到别的地方

先用 btrfs-find-root /dev/sdX 命令(自行替换设备路径),找到最新的root位置。在输出中会显示为:

Well block xxxxxxxxxx(一串数字) seems great, but generation doesn't match, have=XXX, want=XXX

注意这条命令可能会花很久才返回有效的信息。找到最新的代(have、want后的数字最大的),记录下前面的一长串数据(Well block后的)。然后运行 btrfs restore -t xxxxxxxxxx(记录的数字) /dev/sdX(自行替换) /mnt/restore(恢复的目标路径),就可以把数据恢复到/mnt/restore处。

但我们的数据有大概5TB,而服务器上已经没有这样多盘位放用来恢复的盘了。同一机柜还有一台服务器,上面有对3T的盘组着RAID1放着一堆虚拟机。因此我的步骤是:

  • 把RAID1上的虚拟机全部迁移到别的地方
  • 把两块3T盘的RAID1拆开组RAID0
  • 在另一台的服务器上建一虚拟机(VM1),挂这个RAID0阵列建立好文件系统,但不挂载
  • 在VM1上建一个iSCSI Target
  • 在坏阵列的服务器上建一虚拟机(VM2),挂上坏的RAID0阵列
  • 在VM2上连接上iSCSI Target,挂载上好的RAID0阵列
  • 用btrfs restore恢复进去

用iSCSI而不是NFS或SMB/CIFS的原因是,在这种大规模处理的场景下,iSCSI会更快(我猜的)。加上iSCSI其实也只需要很少的配置。

至于具体的步骤,这里放上iSCSI建立的步骤,以供参考(来自教程1教程2):

targetcli # 进入 LinuxIO iSCSI Target配置的 Shell. 这是一个很类似普通 Shell 的 CLI 界面
# 只是 cd ls 等操作的对象是 LinuxIO 对象而不是文件
# 如果用过 PowerShell 的自定义 PSDrive 功能应该熟悉这个

在其中:
    cd backstores/block                 # 先建 iSCSI Target 的 Backstore,即用来实际存放数据
    create name=rescue dev=/dev/sdc     # 用的 block-based backstore 把整个设备作为 backstore,而不是通过 fileio 用映像文件作 backstore,性能好一点
    cd /iscsi
    create iqn.2021-12.cn.edu.nju.itxia:rescue          # 换成自己的 IQN
    cd iqn.2021-12.cn.edu.nju.itxia:rescue/tpg1/luns
    create /backstores/block/lun1                       # 换成上面步骤建立的 LUN 路径
    cd ..
    set attribute generate_node_acls=1 authentication=0 demo_mode_write_protect=0
    cd /
    saveconfig
    exit
systemctl restart target

在客户端:
iscsiadm --mode discovery -t sendtargets -p 服务器IP
iscsiadm --mode node --targetname iqn.2021-12.cn.edu.nju.itxia:rescue --login -n node.startup
用 fdisk -l 找到新的硬盘,挂上即可

注意上面的命令关了iSCSI的身份验证。这是因为在工作室的相对封闭的网络环境没有必要过度配置这些,但在实际的生产环境上,应该配置好CHAP验证!

我们花了大概20个小时跑btrfs restore(大文件大概110MB/s,但中途有很多小文件拖慢速度)。

重启服务

在恢复数据后自然要重新启用NAS服务。第一步自然是格式化好NAS的硬盘,用Synology的系统初始化好卷。但将恢复的数据导入NAS中又是个问题。

我最开始想的是直接把原来的所有文件/目录全部复制进新的盘,覆盖掉默认的数据。但花了几个小时这样做之后,Synology并没有找到原来的共享文件夹(群晖里一个卷会有一堆叫“共享文件夹”的东西,是卷以下最大的管理单元,所有的文件需要存在这些文件夹里),用相同的名字创建新的共享文件夹会报错。

我怀疑是权限配置的问题,尝试模仿了一下群晖的默认权限(主要是@eaDir @database 等以@开头的文件夹)和Owner。但并没有解决问题。且因为黑群的Linux实在是残废的原因,我是通过反复在虚拟机中创建、删除虚拟硬盘(这种虚拟硬盘只是一种Link,用来映射整个硬盘到一个虚拟机),先在满血的Ubuntu中进行操作,再接到群晖上开机测试的,可以说是极为痛苦了。

最后在各种尝试失败后,我们手动创建了所有的共享文件夹(大概有10个?),一个文件夹一个文件夹地拷了所有数据。另一个问题在于我们的群晖上有很多插件,这些插件全部需要被重新配置。最终历时不知道多少天,终于(部分)恢复了工作室的NAS服务。

注意事项

在运行btrfs restore时,我用的版本会产生大量”We have looped trying to restore files in“的提示,要让我们选Yes/No/Abort。这样的提示多半指出这些文件没法恢复了,因此我的选择是n。同时遇到别的错误若没有加ignore errors,还可能会直接退出。因此我实际用的命令是:

yes n | btrfs restore --ignore-errors -t 1495059005440 /dev/md2 /mnt/target ls 2>&1 | tee output.txt

其中的一串数字、/dev/md2和/mnt/target都根据实际情况来。

教训 - Lessons Learned

这次的恢复还是有点困难的,可能还是工作室资源太少了()。幸好NAS上存的数据都是不太重要的从网上下的资源等,因此没有什么大的压力。在”注意事项“中提到的显式丢失的文件大多集中在Transmission的下载/做种任务中,因此损失不重。但还是折射出了工作室安全管理的不足:

  • 没有备份
  • 没有建立足够的实验室安全意识——没有培训基本的安全知识

这次如果有其他同学重装系统时备份的个人文件在NAS上,那就是真的大寄了。


不想被自己的惰性打败。