本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2023年01月15日 统计字数: 10971字 阅读时间: 22分钟阅读 本文链接: https://soulteary.com/2023/01/15/building-a-home-storage-server-at-low-cost-part-one.html ----- # 低成本搭建一台家庭存储服务器:前篇 本篇文章,记录搭建备份服务器的过程。 ## 写在前面 今年考虑专门搭建一台用于数据备份的机器,一来今年外出的需求比较多,历史的设备已经用了几年了,需要有更新的设备来“接力”;二来也想验证方案的靠谱程度,解决我接触的一些生产环境的需求以及朋友们的问题。 因为之前已经买过好几台群晖了,加上今年群晖新品的 “CPU 升级” 非常“明智”,于是我决定自建一台。 ![最终成品](https://attachment.soulteary.com/2023/01/15/finial.jpg) 如果不计算硬盘和内存升级,整机连带 UPS 差不多 2000 块,除了能够充当文件服务器之外,还能运行虚拟机和 Docker 容器,相比群晖性价比还是高不少的。 ### 需求明确 我希望这台设备能够满足以下几个诉求: 1. 必须支持 macOS 系统的数据备份(时间机器),如果能够支持 Windows (生产环境中会有)就更好啦。 2. 至少有一种靠谱的方案来保障数据写入是正确可靠的。 3. 系统稳定可靠,三年五载的不需要额外的维护打理。 4. 体积相对小巧,运行起来没有噪音。 5. 不包含存储设备(硬盘、内存)成本相对较低,能够支持快速替换硬件来解决可用性问题。 除此之外提供的能力,都是“附加分”。 因为核心诉求其实是软件诉求,所以我们来看看软件方案。 ## 操作系统和软件的选择 我倾向的方案是能够持续稳定运行,不会随意重启的 Linux 系统,或者基于 Linux 系统封装的 NAS 系统,最低的底限是我能够知道“samba”、“afp(netatalk)” 的程序版本,未来能够完成迁移。 本篇文章,我们先使用 Ubuntu 来完成基础的服务搭建。 ### 为什么没有选择“白”或者“黑苹果” 在此之前,我分别使用 Mac 设备,和兼容 Mac 的硬件(包括这台 Elite Desk)运行 macOS,开启文件共享,做了“时间机器”的测试。 相比 Linux 协议兼容的[开源软件 netatalk](https://netatalk.sourceforge.io/) ,确实稳定性高了不少,但是,如果选择使用白苹果,想要限制成本,那么容量就无法满足实际使用;如果选择黑苹果,即使优化配置到 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([硬件规格](https://h20195.www2.hp.com/v2/getpdf.aspx/4aa7-7943enuc.pdf))。 除了机箱有二次喷涂掩盖搬运时的暴力磕碰之外,用酒精擦外壳的时候能够看到喷涂的漆在快速挥发,会有点呛之外,基本没有什么槽点。(不消毒的话,不会触发这个事情,机器本身没气味) 我偷了个懒,选择了搭载 i3-10105T 的机器,成本在 1600 元左右,如果你手头有闲置的 CPU,可以入手 700~800 块左右的准系统。 ### 为什么没有选择新款群晖 我连续购入了前两代的群晖产品 DS918+ 和 DS920+,但是对于 DS 923+,这款“升级到” AMD Ryzen 1600 的产品,实在提不起兴趣。 上文中提到的相对廉价的 [i3-10105t CPU 规格](https://www.intel.com/content/www/us/en/products/sku/201890/intel-core-i310105t-processor-6m-cache-up-to-3-90-ghz/specifications.html)可以在 Intel 官网查询到,性价比显然高出不止一头。 ### 为什么没有选择 Apple Time Capsule(时间胶囊) ![苹果的时间胶囊](https://attachment.soulteary.com/2023/01/15/time-capsule.jpg) 从外观来说,苹果的时间胶囊([技术规格](https://support.apple.com/kb/SP679?viewlocale=zh_CN&locale=zh_HK))还是很好看的。但是,产品在 2018 年停产,如果硬件出现问题,通过购置相同硬件来完成服务恢复其实有点麻烦。加上产品即使停产,现在的价格也是小贵的,换起来心疼。 更关键的是,设备只能插一颗磁盘,设备本身算力也非常有限。 ### 为什么没有选择阵列硬盘盒 ![常见品牌的硬盘阵列盒](https://attachment.soulteary.com/2023/01/15/disk-box.jpg) 如果我们已经有一台设备,并且不介意通过 USB 或者雷电数据线来进行数据交互的话,使用外置阵列硬盘盒未见得不是一个好的方案。这个方案最好的一点是,消费市场中硬盘盒的外观的可选性非常多。 不过,多数硬盘阵列盒不能直接提供服务,需要借助“宿主机”来提供服务,更不能运行一些我们想要运行的软件。而且,既然选择搭建一台设备,并且能够使用更靠谱直连主板的 SATA 线以及更可靠的主机电源,那么硬盘盒的方案的性价比也就不是很高了。何况这类设备的“溢价”非常高。 因为文章开头就“谢绝”了群晖,所以群晖的 “Expansion Unit” 也自然就不在讨论之列了啦。(而且也更贵) ### 为什么没有选择网络硬盘盒 不论是采用 ARMbian 还是 OpenWRT 的“盒子”,只要硬件靠谱,稳定性都是靠谱的,而且普遍成本低廉,能效比也非常高。 之所以没有选择这个方案,主要的顾虑在硬件可靠性方面,包括三部分:散热、IO能力、供电。 盒子类的设备一般是被动散热,所以如果和硬盘“同处一室”,难以保证有合理的温度,我不想这个备份服务器出现“散装 Style”;盒子类的产品考虑能耗和成本,所以 CPU 相对孱弱,IO 能力自然也就比较弱,跑满硬盘 IO 带宽基本不可能;盒子类的产品供电是带不动多块硬盘的,所以需要独立供电,或者需要和前文提到的能够独立供电的硬盘阵列盒组合使用,容易出现“小马拉大车”的情况。 成品的盒子,几年前我也买过 WD My Cloud 和群晖早些时候的 ARM 产品,就传输效率体验来说,个人体验来说真的不算太好。 ## 准备工作 理论结束,轮到实践啦。 ![购置配件](https://attachment.soulteary.com/2023/01/15/components.jpg) 我购置了上面这些硬件素材,分别有: 1. 一台搭载了 CPU 的“准系统”,用于到手后测试“开箱即用”的测试主板和 CPU。 2. 一台新款的 APC UPS。 3. 4 根国产的光威 32G DDR4 内存。 4. 2 块 4T 的西数紫盘,用于数据存储和数据校验。 5. 一根国产的京造硬盘,用于存放操作系统,以及避免机械硬盘因为有程序运行无法休眠。 6. 一些配件:数据线和硬盘螺丝。 成本总计 5000 出头,工作功耗 27 瓦左右,总的来说还是比较爽的。当然,你也可以省掉内存升级和固态硬盘的部分,只选购硬件和 UPS ,只需要 2000 出头。 ![高扩展性的主板](https://attachment.soulteary.com/2023/01/15/motherboard.jpg) 主板的扩展性还不错,默认支持 3 块 SATA 硬盘和 2 块 NVMe 硬盘,4 根内存条,以及两个 PCI-e 插槽。完整的扩展能力,可以阅读上文中提到的“规格资料”。 ### BIOS 升级 机器到手后,推荐先做两件事,第一件是进入 BIOS ,重置选项为默认选项。第二件则是给机器插上网线,然后使用 BIOS 中的软件升级工具,将 BIOS 升级到最新,避免软件 BUG 和兼容性问题。 ![BIOS 升级过程](https://attachment.soulteary.com/2023/01/15/bios-upgrade.jpg) 过程中,只有一点注意事项,不要断电,避免造成 BIOS 损坏。 ### 插满内存 考虑到后续想在服务器上运行一些“空间换时间”的程序和虚拟机,我购置了 4 条 32G 的内存,减少分配资源时不必要的“小心翼翼”。 ![国产内存](https://attachment.soulteary.com/2023/01/15/ram.jpg) 插上之后,进入 BIOS ,能够看到正常识别。不过想要确认硬件正常,只是看信息是不够的,还需要进行硬件测试。 ![BIOS 识别信息](https://attachment.soulteary.com/2023/01/15/bios-info.jpg) ### 硬件测试 测试硬件需要使用多种方式,如果你的 BIOS 中有测试工具,将能省不少事。 ![硬件测试](https://attachment.soulteary.com/2023/01/15/hardware-test.jpg) 如果你不放心,可以多测试几次,以及使用 PE 或者在完成系统安装之后,使用测试工具进行更持续的测试。 当然,除了一次性测试之外,在使用之前还要进行一些必要的稳定性测试,在实际使用之前,这台设备我通电运行了一周,没有出现意外的异常。 ## 系统的安装和配置 ![系统待机状态](https://attachment.soulteary.com/2023/01/15/ubuntu-stats.jpg) 关于基础的系统安装、配置,可以参考这篇文章[《在笔记本上搭建高性价比的 Linux 学习环境:基础篇》](https://soulteary.com/2022/06/21/building-a-cost-effective-linux-learning-environment-on-a-laptop-the-basics.html)。 完成系统安装之后,我们还需要一些额外的配置,来让系统性能表现更好。 ### 禁用 Swap 在 Ubuntu 中完全禁用和清理 Swap 需要三步,首先是关闭 Swap 功能: ```bash sudo swapoff -a ``` 接着,编辑分区表文件 `/etc/fstab`,去掉 Swap 相关的记录。 然后,重启机器之后,删除掉“残余”文件即可。 ```bash rm -rf /swapfile ``` 当你完成上面的动作之后,我们使用 `free -g` 查看系统资源,就能够看到 Swap 可用量变为零啦: ```bash # free -g total used free shared buff/cache available Mem: 125 1 123 0 1 123 Swap: 0 0 0 ``` ## 创建 Raid 1 镜像阵列 先使用 `mdadm --examine` 来检查机械硬盘的状态,检查两块目标硬盘是否容量一致。 ```bash # 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` 将两个磁盘的分区表进行一致化处理: ```bash sudo sgdisk /dev/sda -R /dev/sdb ``` 复制完分区表之后,需要对其中一个磁盘的分区 UUID 进行刷新,避免操作系统在使用的时候出现问题。 ```bash sudo sgdisk /dev/sdb -G ``` 然后,使用 `mdadm --create --level=mirror` 创建次盘阵列: ```bash # 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` 来检查阵列构建进度: ```bash # 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: ``` 当阵列构建完成,我们再次检查 `mdstat` 的时候,能够看到类似下面的日志输出: ```bash # 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: ``` 我们也可以使用 `mdadm --detail` 来查看阵列的详细信息: ```bash # 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` 来临时暂停任务。 ```bash echo idle > /sys/block/md0/md/sync_action echo frozen > /sys/block/md0/md/sync_action ``` 如果你希望完全重新建立阵列,可以使用下面的命令来恢复阵列前的状态: ```bash sudo mdadm -Esv sudo mdadm --stop /dev/md* ``` ## 在系统中使用磁盘阵列 完成阵列构建之后,想要使用磁盘阵列,首先得完成磁盘的挂载和磁盘的初始化(格式化)。 ```bash # 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` 这个路径,我们可以这样做: ```bash sudo mkdir /data sudo mount /dev/md0 /data ``` 然后使用 `df` 来检查磁盘是否就绪: ```bash # df -h /data Filesystem Size Used Avail Use% Mounted on /dev/md0 3.6T 28K 3.4T 1% /data ``` 实际使用的时候,比如我们在这个分区上创建了一个文件后,磁盘会自动完成文件的镜像动作,提升文件的访问可靠性。 ```bash # 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` 的配置: ```bash # 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: ```bash # sudo dumpe2fs /dev/md0 |grep UUID dumpe2fs 1.46.5 (30-Dec-2021) Filesystem UUID: f7014dd5-b7b1-466c-ae42-2b49c43cbb57 ``` 然后将下面的内容添加到 `/etc/fstab` 中: ```bash UUID=f7014dd5-b7b1-466c-ae42-2b49c43cbb57 /dev/md0 /mnt/mirror ext4 defaults 0 0 ``` 搞定之后,更新 `initramfs`,然后重启设备,检查系统是否能够在启动时,正常挂载阵列分区到 `/data` 路径即可。 ```bash # sudo update-initramfs -u update-initramfs: Generating /boot/initrd.img-5.15.0-57-generic ``` ## 安装文件服务 安装文件服务的方案有很多种,比如之前文章[《装在笔记本里的私有云环境:网络存储篇(中)》](https://soulteary.com/2021/11/09/private-cloud-environment-installed-in-a-notebook-storage-part-2.html)提到的 S3 替代品 MinIO 或者 Syncthing、NextCloud。 不过,为了让 Mac 、Windows 设备能够直接“原生”使用服务,并完成备份,我们可以考虑选择使用 samba 或者 netatalk 提供 `smb` 或 `afp` 协议的文件访问能力。如果你希望直接使用本地的 samba 或者 netatalk 程序,直接使用 `apt install` 即可: ```bash sudo apt-get install samba -y ``` 如果你觉得这样做不够环保,使用 `docker` 版本的 samba 或者 netatalk 也未尝不可。比如,[Stanback/alpine-samba](https://github.com/Stanback/alpine-samba)。 当然,用户管理(增加、停用、删除、改密)可能比较麻烦,不过基本都是一次性成本,或许还好。(也可能,再单独展开一篇文章更为合适) 当我们完成服务启动之后,就能够再网络中找到这台“存储服务器”了。 ![网络服务发现](https://attachment.soulteary.com/2023/01/15/network-discovery.jpg) 以及使用 Samba 协议连接服务器。 ![使用 Samba 访问数据](https://attachment.soulteary.com/2023/01/15/network-samba.jpg) 接下来,如果你愿意的话,就可以直接使用系统自带的“时间机器”,来备份你的设备数据啦。 ## 最后 因为最近比较忙,本篇文章“战线较长”大概经历了两周左右,过程中可能有一些疏漏,如果你有疑问或者建议,欢迎反馈。 下一篇文章中,我们来聊聊如何解决“管理资源”不方便的问题。 --EOF