本篇文章,记录搭建备份服务器的过程。

写在前面

今年考虑专门搭建一台用于数据备份的机器,一来今年外出的需求比较多,历史的设备已经用了几年了,需要有更新的设备来“接力”;二来也想验证方案的靠谱程度,解决我接触的一些生产环境的需求以及朋友们的问题。

因为之前已经买过好几台群晖了,加上今年群晖新品的 “CPU 升级” 非常“明智”,于是我决定自建一台。

最终成品

如果不计算硬盘和内存升级,整机连带 UPS 差不多 2000 块,除了能够充当文件服务器之外,还能运行虚拟机和 Docker 容器,相比群晖性价比还是高不少的。

需求明确

我希望这台设备能够满足以下几个诉求:

  1. 必须支持 macOS 系统的数据备份(时间机器),如果能够支持 Windows (生产环境中会有)就更好啦。
  2. 至少有一种靠谱的方案来保障数据写入是正确可靠的。
  3. 系统稳定可靠,三年五载的不需要额外的维护打理。
  4. 体积相对小巧,运行起来没有噪音。
  5. 不包含存储设备(硬盘、内存)成本相对较低,能够支持快速替换硬件来解决可用性问题。

除此之外提供的能力,都是“附加分”。

因为核心诉求其实是软件诉求,所以我们来看看软件方案。

操作系统和软件的选择

我倾向的方案是能够持续稳定运行,不会随意重启的 Linux 系统,或者基于 Linux 系统封装的 NAS 系统,最低的底限是我能够知道“samba”、“afp(netatalk)” 的程序版本,未来能够完成迁移。

本篇文章,我们先使用 Ubuntu 来完成基础的服务搭建。

为什么没有选择“白”或者“黑苹果”

在此之前,我分别使用 Mac 设备,和兼容 Mac 的硬件(包括这台 Elite Desk)运行 macOS,开启文件共享,做了“时间机器”的测试。

相比 Linux 协议兼容的开源软件 netatalk ,确实稳定性高了不少,但是,如果选择使用白苹果,想要限制成本,那么容量就无法满足实际使用;如果选择黑苹果,即使优化配置到 99% 可用,总归觉得还有 1% 的不稳定因素,而我们并不知道这个 1% 的问题什么时候会“触发”,影响有多严重。况且,很有可能不确定性远大于 1%。

为什么没有选择 Windows

因为近几年,我没有主力使用 Windows,对于 Windows 的熟悉程度是不够的。并且,在最近一次的试验中,我发现非服务器版的 Windows 的长时间运行还是有一些问题的(我购入的是专业版的 Licenses)。

11 月末的时候,我入手了一台机器(Dell Optiplex),购入了正版的 Windows 11 ,系统上低负载运行了一款程序。在过去的两个月中,我遇到了跨系统“远程桌面”失效、毫无预兆的“自动”升级和重启,可能作为文件服务器来说,可靠性还需要进一步验证。

硬件方案选择

基于上面的需求,我将硬件选择范围锁定在了 HP EliteDesk 和 Dell Optiplex 两个系列里。经过对比,这次没有选择全新的设备,而是选择了二手市场上供给充沛的 Elite Desk。最终选择了一台 2021 年激活过,因为 2022 “环境变化”退出服役的二手的主机:EliteDesk 800G6 SFF(硬件规格)。

除了机箱有二次喷涂掩盖搬运时的暴力磕碰之外,用酒精擦外壳的时候能够看到喷涂的漆在快速挥发,会有点呛之外,基本没有什么槽点。(不消毒的话,不会触发这个事情,机器本身没气味)

我偷了个懒,选择了搭载 i3-10105T 的机器,成本在 1600 元左右,如果你手头有闲置的 CPU,可以入手 700~800 块左右的准系统。

为什么没有选择新款群晖

我连续购入了前两代的群晖产品 DS918+ 和 DS920+,但是对于 DS 923+,这款“升级到” AMD Ryzen 1600 的产品,实在提不起兴趣。

上文中提到的相对廉价的 i3-10105t CPU 规格可以在 Intel 官网查询到,性价比显然高出不止一头。

为什么没有选择 Apple Time Capsule(时间胶囊)

苹果的时间胶囊

从外观来说,苹果的时间胶囊(技术规格)还是很好看的。但是,产品在 2018 年停产,如果硬件出现问题,通过购置相同硬件来完成服务恢复其实有点麻烦。加上产品即使停产,现在的价格也是小贵的,换起来心疼。

更关键的是,设备只能插一颗磁盘,设备本身算力也非常有限。

为什么没有选择阵列硬盘盒

常见品牌的硬盘阵列盒

如果我们已经有一台设备,并且不介意通过 USB 或者雷电数据线来进行数据交互的话,使用外置阵列硬盘盒未见得不是一个好的方案。这个方案最好的一点是,消费市场中硬盘盒的外观的可选性非常多。

不过,多数硬盘阵列盒不能直接提供服务,需要借助“宿主机”来提供服务,更不能运行一些我们想要运行的软件。而且,既然选择搭建一台设备,并且能够使用更靠谱直连主板的 SATA 线以及更可靠的主机电源,那么硬盘盒的方案的性价比也就不是很高了。何况这类设备的“溢价”非常高。

因为文章开头就“谢绝”了群晖,所以群晖的 “Expansion Unit” 也自然就不在讨论之列了啦。(而且也更贵)

为什么没有选择网络硬盘盒

不论是采用 ARMbian 还是 OpenWRT 的“盒子”,只要硬件靠谱,稳定性都是靠谱的,而且普遍成本低廉,能效比也非常高。

之所以没有选择这个方案,主要的顾虑在硬件可靠性方面,包括三部分:散热、IO能力、供电。

盒子类的设备一般是被动散热,所以如果和硬盘“同处一室”,难以保证有合理的温度,我不想这个备份服务器出现“散装 Style”;盒子类的产品考虑能耗和成本,所以 CPU 相对孱弱,IO 能力自然也就比较弱,跑满硬盘 IO 带宽基本不可能;盒子类的产品供电是带不动多块硬盘的,所以需要独立供电,或者需要和前文提到的能够独立供电的硬盘阵列盒组合使用,容易出现“小马拉大车”的情况。

成品的盒子,几年前我也买过 WD My Cloud 和群晖早些时候的 ARM 产品,就传输效率体验来说,个人体验来说真的不算太好。

准备工作

理论结束,轮到实践啦。

购置配件

我购置了上面这些硬件素材,分别有:

  1. 一台搭载了 CPU 的“准系统”,用于到手后测试“开箱即用”的测试主板和 CPU。
  2. 一台新款的 APC UPS。
  3. 4 根国产的光威 32G DDR4 内存。
  4. 2 块 4T 的西数紫盘,用于数据存储和数据校验。
  5. 一根国产的京造硬盘,用于存放操作系统,以及避免机械硬盘因为有程序运行无法休眠。
  6. 一些配件:数据线和硬盘螺丝。

成本总计 5000 出头,工作功耗 27 瓦左右,总的来说还是比较爽的。当然,你也可以省掉内存升级和固态硬盘的部分,只选购硬件和 UPS ,只需要 2000 出头。

高扩展性的主板

主板的扩展性还不错,默认支持 3 块 SATA 硬盘和 2 块 NVMe 硬盘,4 根内存条,以及两个 PCI-e 插槽。完整的扩展能力,可以阅读上文中提到的“规格资料”。

BIOS 升级

机器到手后,推荐先做两件事,第一件是进入 BIOS ,重置选项为默认选项。第二件则是给机器插上网线,然后使用 BIOS 中的软件升级工具,将 BIOS 升级到最新,避免软件 BUG 和兼容性问题。

BIOS 升级过程

过程中,只有一点注意事项,不要断电,避免造成 BIOS 损坏。

插满内存

考虑到后续想在服务器上运行一些“空间换时间”的程序和虚拟机,我购置了 4 条 32G 的内存,减少分配资源时不必要的“小心翼翼”。

国产内存

插上之后,进入 BIOS ,能够看到正常识别。不过想要确认硬件正常,只是看信息是不够的,还需要进行硬件测试。

BIOS 识别信息

硬件测试

测试硬件需要使用多种方式,如果你的 BIOS 中有测试工具,将能省不少事。

硬件测试

如果你不放心,可以多测试几次,以及使用 PE 或者在完成系统安装之后,使用测试工具进行更持续的测试。

当然,除了一次性测试之外,在使用之前还要进行一些必要的稳定性测试,在实际使用之前,这台设备我通电运行了一周,没有出现意外的异常。

系统的安装和配置

系统待机状态

关于基础的系统安装、配置,可以参考这篇文章《在笔记本上搭建高性价比的 Linux 学习环境:基础篇》

完成系统安装之后,我们还需要一些额外的配置,来让系统性能表现更好。

禁用 Swap

在 Ubuntu 中完全禁用和清理 Swap 需要三步,首先是关闭 Swap 功能:

sudo swapoff -a

接着,编辑分区表文件 /etc/fstab,去掉 Swap 相关的记录。

然后,重启机器之后,删除掉“残余”文件即可。

rm -rf /swapfile

当你完成上面的动作之后,我们使用 free -g 查看系统资源,就能够看到 Swap 可用量变为零啦:

# free -g
               total        used        free      shared  buff/cache   available
Mem:             125           1         123           0           1         123
Swap:              0           0           0

创建 Raid 1 镜像阵列

先使用 mdadm --examine 来检查机械硬盘的状态,检查两块目标硬盘是否容量一致。

# sudo mdadm --examine /dev/sda /dev/sdb

/dev/sda:
   MBR Magic : aa55
Partition[0] :   4294967295 sectors at            1 (type ee)

/dev/sdb:
   MBR Magic : aa55
Partition[0] :   4294967295 sectors at            1 (type ee)

接着,使用 sgdisk -R 将两个磁盘的分区表进行一致化处理:

sudo sgdisk /dev/sda -R /dev/sdb

复制完分区表之后,需要对其中一个磁盘的分区 UUID 进行刷新,避免操作系统在使用的时候出现问题。

sudo sgdisk /dev/sdb -G

然后,使用 mdadm --create --level=mirror 创建次盘阵列:

# sudo mdadm --create /dev/md0 --level=mirror --raid-devices=2 /dev/sda /dev/sdb

mdadm: /dev/sda appears to be part of a raid array:
       level=raid1 devices=2 ctime=Thu Jan  5 20:47:30 2023
mdadm: partition table exists on /dev/sda but will be lost or
       meaningless after creating array
mdadm: Note: this array has metadata at the start and
    may not be suitable as a boot device.  If you plan to
    store '/boot' on this device please ensure that
    your boot-loader understands md/v1.x metadata, or use
    --metadata=0.90
mdadm: partition table exists on /dev/sdb
mdadm: partition table exists on /dev/sdb but will be lost or
       meaningless after creating array
Continue creating array? 
Continue creating array? (y/n) y
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.

创建磁盘阵列需要等待一些时间,完成时间和你够买的磁盘的容量,以及磁盘和主板的数据传输协议,以及你的设备 CPU 算力都有关系。

我们可以随时通过 cat /proc/mdstat 来检查阵列构建进度:

# cat /proc/mdstat

HP-EliteDesk-800-G6-Small-Form-Factor-PC: Thu Jan  5 21:50:56 2023

Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5] [raid4] [raid10]
md0 : active raid1 sdb[1] sda[0]
      3906886464 blocks super 1.2 [2/2] [UU]
      [=>...................]  resync =  9.6% (375538432/3906886464) finish=325.5min speed=180783K/sec
      bitmap: 28/30 pages [112KB], 65536KB chunk

unused devices: <none>

当阵列构建完成,我们再次检查 mdstat 的时候,能够看到类似下面的日志输出:

# sudo cat /proc/mdstat

Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5] [raid4] [raid10] 
md0 : active raid1 sdb[1] sda[0]
      3906886464 blocks super 1.2 [2/2] [UU]
      bitmap: 0/30 pages [0KB], 65536KB chunk

unused devices: <none>

我们也可以使用 mdadm --detail 来查看阵列的详细信息:

# sudo mdadm --detail /dev/md0

/dev/md0:
           Version : 1.2
     Creation Time : Thu Jan  5 21:15:24 2023
        Raid Level : raid1
        Array Size : 3906886464 (3.64 TiB 4.00 TB)
     Used Dev Size : 3906886464 (3.64 TiB 4.00 TB)
      Raid Devices : 2
     Total Devices : 2
       Persistence : Superblock is persistent

     Intent Bitmap : Internal

       Update Time : Fri Jan  6 23:29:38 2023
             State : clean 
    Active Devices : 2
   Working Devices : 2
    Failed Devices : 0
     Spare Devices : 0

Consistency Policy : bitmap

              Name : 0
              UUID : bd87e586:257740d3:5fd50282:ccc8b145
            Events : 6144

    Number   Major   Minor   RaidDevice State
       0       8        0        0      active sync   /dev/sda
       1       8       16        1      active sync   /dev/sdb

中止阵列创建过程

如果你在构建过程中,因为一些原因,想停止构建,可以使用 idle 来中止构建,或者使用 frozen 来临时暂停任务。

echo idle > /sys/block/md0/md/sync_action
echo frozen > /sys/block/md0/md/sync_action

如果你希望完全重新建立阵列,可以使用下面的命令来恢复阵列前的状态:

sudo mdadm -Esv
sudo mdadm --stop /dev/md*

在系统中使用磁盘阵列

完成阵列构建之后,想要使用磁盘阵列,首先得完成磁盘的挂载和磁盘的初始化(格式化)。

# sudo mkfs.ext4 /dev/md0

mke2fs 1.46.5 (30-Dec-2021)
Discarding device blocks: done                            
Creating filesystem with 976721616 4k blocks and 244187136 inodes
Filesystem UUID: f7014dd5-b7b1-466c-ae42-2b49c43cbb57
Superblock backups stored on blocks: 
	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
	4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968, 
	102400000, 214990848, 512000000, 550731776, 644972544

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (262144 blocks): done
Writing superblocks and filesystem accounting information: done   

将磁盘手动挂载到 /data 这个路径,我们可以这样做:

sudo mkdir /data
sudo mount /dev/md0 /data

然后使用 df 来检查磁盘是否就绪:

# df -h /data

Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        3.6T   28K  3.4T   1% /data

实际使用的时候,比如我们在这个分区上创建了一个文件后,磁盘会自动完成文件的镜像动作,提升文件的访问可靠性。

# touch /data/test.go
# sudo mdadm --examine /dev/sda /dev/sdb

/dev/sda:
          Magic : a92b4efc
        Version : 1.2
    Feature Map : 0x1
     Array UUID : bd87e586:257740d3:5fd50282:ccc8b145
           Name : 0
  Creation Time : Thu Jan  5 21:15:24 2023
     Raid Level : raid1
   Raid Devices : 2

 Avail Dev Size : 7813772976 sectors (3.64 TiB 4.00 TB)
     Array Size : 3906886464 KiB (3.64 TiB 4.00 TB)
  Used Dev Size : 7813772928 sectors (3.64 TiB 4.00 TB)
    Data Offset : 264192 sectors
   Super Offset : 8 sectors
   Unused Space : before=264112 sectors, after=48 sectors
          State : clean
    Device UUID : da33683e:54478a8b:cd6fd10e:1a19114c

Internal Bitmap : 8 sectors from superblock
    Update Time : Fri Jan  6 23:29:14 2023
  Bad Block Log : 512 entries available at offset 24 sectors
       Checksum : 5ad089e9 - correct
         Events : 6144


   Device Role : Active device 0
   Array State : AA ('A' == active, '.' == missing, 'R' == replacing)
/dev/sdb:
          Magic : a92b4efc
        Version : 1.2
    Feature Map : 0x1
     Array UUID : bd87e586:257740d3:5fd50282:ccc8b145
           Name : 0
  Creation Time : Thu Jan  5 21:15:24 2023
     Raid Level : raid1
   Raid Devices : 2

 Avail Dev Size : 7813772976 sectors (3.64 TiB 4.00 TB)
     Array Size : 3906886464 KiB (3.64 TiB 4.00 TB)
  Used Dev Size : 7813772928 sectors (3.64 TiB 4.00 TB)
    Data Offset : 264192 sectors
   Super Offset : 8 sectors
   Unused Space : before=264112 sectors, after=48 sectors
          State : clean
    Device UUID : 577ef05f:fa66ff02:8e4085c0:03d798ec

Internal Bitmap : 8 sectors from superblock
    Update Time : Fri Jan  6 23:29:14 2023
  Bad Block Log : 512 entries available at offset 24 sectors
       Checksum : 460982b8 - correct
         Events : 6144


   Device Role : Active device 1
   Array State : AA ('A' == active, '.' == missing, 'R' == replacing)

不过,上面手动挂载的磁盘,如果设备重启,将自动被释放。需要我们再次手动进行设备挂载。

想要解决这个问题,我们需要调整 fstab 中的内容,而 fstab 需要我们先获取阵列的“UUID”,以及更新 mdadm 的配置:

# sudo mdadm --detail --scan --verbose | sudo tee -a /etc/mdadm/mdadm.conf

ARRAY /dev/md0 level=raid1 num-devices=2 metadata=1.2 name=0 UUID=bd87e586:257740d3:5fd50282:ccc8b145
   devices=/dev/sda,/dev/sdb

获取 UUID 需要使用下面的命令,而不能直接使用 mdadm 输出的 ID:

# sudo dumpe2fs /dev/md0 |grep UUID

dumpe2fs 1.46.5 (30-Dec-2021)
Filesystem UUID:          f7014dd5-b7b1-466c-ae42-2b49c43cbb57

然后将下面的内容添加到 /etc/fstab 中:

UUID=f7014dd5-b7b1-466c-ae42-2b49c43cbb57 /dev/md0   /mnt/mirror   ext4   defaults   0   0

搞定之后,更新 initramfs,然后重启设备,检查系统是否能够在启动时,正常挂载阵列分区到 /data 路径即可。

# sudo update-initramfs -u

update-initramfs: Generating /boot/initrd.img-5.15.0-57-generic

安装文件服务

安装文件服务的方案有很多种,比如之前文章《装在笔记本里的私有云环境:网络存储篇(中)》提到的 S3 替代品 MinIO 或者 Syncthing、NextCloud。

不过,为了让 Mac 、Windows 设备能够直接“原生”使用服务,并完成备份,我们可以考虑选择使用 samba 或者 netatalk 提供 smbafp 协议的文件访问能力。如果你希望直接使用本地的 samba 或者 netatalk 程序,直接使用 apt install 即可:

sudo apt-get install samba -y

如果你觉得这样做不够环保,使用 docker 版本的 samba 或者 netatalk 也未尝不可。比如,Stanback/alpine-samba

当然,用户管理(增加、停用、删除、改密)可能比较麻烦,不过基本都是一次性成本,或许还好。(也可能,再单独展开一篇文章更为合适)

当我们完成服务启动之后,就能够再网络中找到这台“存储服务器”了。

网络服务发现

以及使用 Samba 协议连接服务器。

使用 Samba 访问数据

接下来,如果你愿意的话,就可以直接使用系统自带的“时间机器”,来备份你的设备数据啦。

最后

因为最近比较忙,本篇文章“战线较长”大概经历了两周左右,过程中可能有一些疏漏,如果你有疑问或者建议,欢迎反馈。

下一篇文章中,我们来聊聊如何解决“管理资源”不方便的问题。

–EOF