我的 NAS 方案

很久之前,我就有组一个 NAS 的想法,直到今年才得以实现。

选型

首先就是选择成品 NAS 还是 DIY。我并没有 NAS 的刚需,只是想在折腾中学习,所以自然是选择 DIY。基于战未来的考虑,直接选了个八盘位的机箱,而 CPU 方面目前看来没什么吃性能的应用,所以选了 n100。最终的配置如下:

  • 板U 云星 n100 550
  • m2 转 sata 拓展卡 75
  • 机箱 乔思伯 n3 740
  • 内存 拆机 8G 0
  • 电源 Thermaltake 450W 钢影 SFX 420
  • 系统盘 宏碁 128g 70

合计 1850。这其中电源感觉严重冗余了,因为后面实测待机功率才 30w 左右。但是机箱只支持 SFX 电源,而低功率的 SFX 电源似乎比较少,350w 的 SFX 都挺垃圾的。说句题外话,在很久之前我的 nas 配置单上选的还是 i3-10100,那时候 12 代还没出..

软件同样有成品 NAS 系统和 DIY 可选,DIY 系统(比如装一个普通的 Linux 发行版)太费时费力了,所以还是选择成品的 NAS 系统。网上流行的 NAS 系统大概有:

  • unraid,正如其名字所说的,没有 raid。整体是 JBOD(Just a Bunch of Disks)的思路,有单独的 parity disk。由于 data disk 的容量并不要求一致,所以 parity disk 要求大于等于最大的 data disk 容量。parity 用的也是奇偶校验。非开源,需付费购买授权,当然也有学习版。
  • 黑群晖,优势是能使用群晖生态系统中的软件,但是由于是逆向出来的,难免有各种兼容性问题。使用 btrfs 作为文件系统,mdadm 管理阵列。
  • TrueNAS,基于 zfs,开源。开发商 ixsystems 本身也赞助了一些 zfs feature 的开发。卖企业版,也卖类似一体机的硬件。
  • 飞牛,国产的 NAS 系统。非开源,免费使用。面向小白用户,基本能够开箱即用。似乎也是 btrfs+mdadm 的组合。后面应该会卖硬件。
  • (混沌邪恶) Windows Server
  • OpenMediaVault,相对知名度没那么高的 NAS 系统,是从 FreeNAS(TrueNAS 的前身)fork 出来的。据维基百科介绍,这是因为 OMV 的开发者想要基于 Linux 来开发 FreeNAS。略显讽刺的是,truenas 在 2023 年宣布将全面转向 Linux,原先的 FreeBSD 版本(TrueNAS Core)将只会收到安全更新并被逐渐取代。

在这之上,还可以先安装 PVE 作为虚拟化平台,然后在虚拟机里安装 NAS 系统。由于我很早的时候就看到过很多“鼓吹” zfs 的文章,所以文件系统的选型在一开始就定下了:zfs。并且我比较偏好开源系统,所以最终选择了 truenas。

装机过程

装机过程没碰到什么问题,有一个小问题是机箱附带的两个硬盘风扇感觉声音太大了,但是这两个风扇是 3pin 接口,没法调速(即使是 4 pin,由于这两个接口在硬盘背板,而不在主板上,估计也没法调速)。于是我只好又买了两根减速线插上。

另一个问题是我明明有一块 SSD 了,为什么还要额外买块系统盘呢?因为 truenas 的系统盘存不了数据(官方不支持这种用法)。装系统时碰到一个小问题是我本来是把 truenas 的 iso 放到一个装了 ventoy 的 U 盘上的,没想到居然没法引导。去搜了下 ventoy 的 issue 也有别人碰到这个问题。没办法只能换一个 U 盘直接 dd 写入 iso。

image-20250714153834186 (装完后的效果图)

硬盘方面,我本来有块 4T 的西数红盘,这次打算再添置两块 4T 组 raid-z1。原来的一块 2t ssd 作为备份盘使用。于是购置了一块希捷 ST4000VX015,和一块二手的希捷企业盘。买回来才发现企业盘炒豆子的声音居然这么大(因为 NAS 是放在客厅角落的,所以整个客厅都能听到。并且这个机箱为了通风一点隔音都没有),于是退了又买了一块 VX015,顺便测试了一下 zfs 的阵列重建。重建步骤其实很简单,truenas 中提供了图形界面。如果在命令行下操作,也就是 zpool offline 和 zpool replcae 两个命令。由于数据没存满,重建大概花了 3 个小时就完成了。

未来如果要升级的话,可以直接买 4T 的扩容 pool,也可以买更大的硬盘建新 pool。

系统

truenas 本质上是个 debian 系统,用了一个定制的内核,跑了一个自己的 web 服务(名字叫 middleware),还把 apt 给 ban 了。这个 middleware 其实就是 truenas 本体,提供 web ui,操作 zfs,操作 docker 什么都是它干的。

root@truenas[~]# uname -a
Linux truenas 6.12.15-production+truenas #1 SMP PREEMPT_DYNAMIC Mon May 26 13:44:31 UTC 2025 x86_64 GNU/Linux
root@truenas[~]# cat /etc/os-release 
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
root@truenas[~]# ps aux | grep middleware 
root        1087  5.5  4.9 3598760 1614628 ?     Ssl  May28 3728:31 middlewared
root        1139  0.0  0.4 1670448 147044 ?      Sl   May28   3:06 middlewared (zettarepl)
root     1769577  0.0  0.2 828484 68128 ?        Sl   15:33   0:00 middlewared (worker)
root     1771402  0.1  0.2 901012 67516 ?        Sl   15:35   0:00 middlewared (worker)
root     1778131  0.1  0.2 826684 66088 ?        Sl   15:42   0:00 middlewared (worker)
root     1778135  0.1  0.2 826708 65920 ?        Sl   15:42   0:00 middlewared (worker)
root     1779127  0.2  0.2 827556 67532 ?        Sl   15:43   0:00 middlewared (worker)
root     1785026  0.0  0.0   3744  1464 pts/2    S+   15:48   0:00 grep middleware
root@truenas[~]# apt
Package management tools are disabled on TrueNAS appliances.

Attempting to update TrueNAS with apt or methods other than the TrueNAS web
interface can result in a nonfunctional system.

一个有趣的地方是这个系统是所谓的不可变系统,更新系统的实质是建一个新的 zfs dataset,然后从新的 dataset 启动。从 dataset 上看,配置应该是和系统分离的,存在一个叫 .system 的 dataset 下面:

root@truenas[~]# zfs list | grep boot                                                       
boot-pool                                                                                              7.85G   106G    96K  none
boot-pool/.system                                                                                      1.90G   106G  1.55G  legacy
boot-pool/.system/configs-ae32c386e13840b2bf9c0083275e7941                                             9.87M   106G  9.87M  legacy
boot-pool/.system/cores                                                                                  96K  1024M    96K  legacy
boot-pool/.system/netdata-ae32c386e13840b2bf9c0083275e7941                                              352M   106G   352M  legacy
boot-pool/.system/nfs                                                                                   124K   106G   124K  legacy
boot-pool/.system/samba4                                                                                560K   106G   312K  legacy
boot-pool/ROOT                                                                                         5.87G   106G    96K  none
boot-pool/ROOT/24.10.2.1                                                                               2.62G   106G   165M  legacy
boot-pool/ROOT/24.10.2.1/audit                                                                          848K   106G   848K  /audit
boot-pool/ROOT/24.10.2.1/conf                                                                          6.83M   106G  6.83M  /conf
boot-pool/ROOT/24.10.2.1/data                                                                           276K   106G   276K  /data
boot-pool/ROOT/24.10.2.1/etc                                                                           7.32M   106G  6.46M  /etc
boot-pool/ROOT/24.10.2.1/home                                                                           108K   106G   108K  /home
boot-pool/ROOT/24.10.2.1/mnt                                                                            112K   106G   112K  /mnt
boot-pool/ROOT/24.10.2.1/opt                                                                             96K   106G    96K  /opt
boot-pool/ROOT/24.10.2.1/root                                                                           152K   106G   152K  /root
boot-pool/ROOT/24.10.2.1/usr                                                                           2.40G   106G  2.40G  /usr
boot-pool/ROOT/24.10.2.1/var                                                                           41.2M   106G  31.3M  /var
boot-pool/ROOT/24.10.2.1/var/ca-certificates                                                             96K   106G    96K  /var/local/ca-certificates
boot-pool/ROOT/24.10.2.1/var/log                                                                       9.55M   106G  8.92M  /var/log
boot-pool/ROOT/24.10.2.1/var/log/journal                                                                644K   106G   644K  /var/log/journal
boot-pool/ROOT/25.04.1                                                                                 3.25G   106G   174M  legacy
boot-pool/ROOT/25.04.1/audit                                                                           1.55M   106G  1.55M  /audit
boot-pool/ROOT/25.04.1/conf                                                                            7.06M   106G  7.06M  /conf
boot-pool/ROOT/25.04.1/data                                                                             260K   106G   260K  /data
boot-pool/ROOT/25.04.1/etc                                                                             7.13M   106G  6.18M  /etc
boot-pool/ROOT/25.04.1/home                                                                             116K   106G   116K  /home
boot-pool/ROOT/25.04.1/mnt                                                                              112K   106G   112K  /mnt
boot-pool/ROOT/25.04.1/opt                                                                               96K   106G    96K  /opt
boot-pool/ROOT/25.04.1/root                                                                             500M   106G   500M  /root
boot-pool/ROOT/25.04.1/usr                                                                             2.53G   106G  2.53G  /usr
boot-pool/ROOT/25.04.1/var                                                                             52.5M   106G  4.28M  /var
boot-pool/ROOT/25.04.1/var/ca-certificates                                                               96K   106G    96K  /var/local/ca-certificates
boot-pool/ROOT/25.04.1/var/lib                                                                         30.5M   106G  27.9M  /var/lib
boot-pool/ROOT/25.04.1/var/lib/incus                                                                   2.18M   106G  2.18M  /var/lib/incus
boot-pool/ROOT/25.04.1/var/log                                                                         16.8M   106G  5.77M  /var/log
boot-pool/ROOT/25.04.1/var/log/journal                                                                 11.0M   106G  11.0M  /var/log/journal
boot-pool/grub                                                                                         8.42M   106G  8.42M  legacy

truenas 的功能还是很全的,我主要用到的有下面几个:

  • Apps,也就是 docker,下面详述

  • Instances,基于 incus 的虚拟机平台,25.04 版本新加的,替代了原来的自己的平台,目前还是试验性阶段。在我加了两个虚拟机(一个跑 clash, 一个跑 homeassistant)之后,不定时会出现 qemu 进程因为 OOM 被 kill,然后服务就挂了(看起来 incus 没有自动重启的策略)… 不得以加到了 32G 内存。

    • 为了简单起见,虚拟机设置了静态的 IP 地址。我一开始担心这会不会造成 IP 地址冲突,搜索后发现 DHCP 中包含了检查分配的 IP 地址是否已被使用的特性:

      When allocating a new address, servers SHOULD check that the offered network address is not already in use; e.g., the server may probe the offered address with an ICMP Echo Request.

      —- RFC 2131

  • scrub task,定时执行 zfs scrub 操作,帮助发现硬盘中潜在的错误。这也是 zfs 相比于其他 raid 方案更安全的原因之一。

  • replication task,实际上是 zfs snapshot 和 zfs send 的结合,用于备份一个 dataset。我把一些重要的数据备份到另一个 pool (ssd)里。

  • 告警,配置了 SMTP 之后,在某些事件发生时(比如 zpool status 中出现了 error)会发邮件通知。

应用

一个 NAS 只有存储功能当然是不够的,应用才是核心。如今的 NAS 系统基本都围绕着 docker 来构建应用生态了,这也是合理的,毕竟 docker 的出现就是为了解决应用部署的问题。truenas 在几个版本之前选择了极为先进的 k3s+helm 的组合,据说因为使用难度被广受诟病,于是现在回归了 docker+docker compose 的组合。

官方有个 apps 仓库,虽然说是社区维护的,但是大部分 commit message 都是无意义的 update xxx,也没有良好的文档。我大概看了一下,差不多是用一堆模板渲染出一个给 compose 用的 yaml,然后喂给 compose。不过作为用户来说,这些都无所谓,能用就行。官方支持的 app 数量还是很多的,常用的基本都有。可以用 docker compose ls 看到渲染出来的 yaml 长什么样。

image-20250714163725960

应用配置里有许多 app specific 的问题,这里就不展开了。吐槽一下 jellyfin 的字幕真是万年难题,直至今日某些字幕在 jellyfin android tv 客户端上显示的依旧是豆腐..

几个 turenas 导致的问题:

  • 一开始我的 app 安装在 backup pool 中,因为我想让 tank pool 在空闲时休眠。但是几天观察下来 tank 并不会休眠,原因未知。休眠本身也没那么重要,于是我就又把 app 迁移到 tank pool 中。这一步需要手动完成, 参考论坛中的这篇文章
  • TrueNAS 的 temporary storage 似乎会在重启容器时清空(参考)。而 immich 的 ml-cache 用的这个类型的 storage,导致重启的时候会重下模型。但模型又很大,浪费流量.. 而且由于 huggingface 的限流策略,下载模型有五分之四的概率是失败的。truenas 页面上限制了把 storage 从 temporary 改成 ixVolume,于是我只能手动建了个 dataset 然后把 storage 改成 hostPath。

备份

为了不让 NAS 成为数据火葬场,备份当然是很重要的。同时,我的几个 VPS 上的数据之前也一直没有定时备份,在有了 NAS 之后正好统一规划一下备份方案。目前的备份方案如下图:

  graph LR;
	subgraph VPS
	athena
	shanghai
	end
	subgraph NAS
		subgraph pool tank
			personal
			apps
		end
		
		subgraph pool backup
			backup/personal["personal"]
			backup/apps["apps"]
			webdav["webdav (provided by Caddy)"]
		end
		
		personal -- zfs snapshot, replication task --> backup/personal
		apps -- zfs snapshot, replication task --> backup/apps
	end
	
	subgraph "Moo's NAS (异地灾备)"
		moo/webdav["webdav (provided by OpenList)"]
	end
	
	athena -- kopia --> webdav
	shanghai -- kopia --> webdav
	phone["Android Phone"] -- SeedVault --> webdav

    backup/personal -- cloud sync --> moo/webdav
    backup/apps -- cloud sync --> moo/webdav

tank pool 中的数据实现了三份副本,一份异地。VPS 上的数据则实现了两份副本,一份异地。所有备份任务每天执行一次。一开始想的是每周执行一次,但是实验了之后发现,增量备份还是挺快的,所以改成了一天一次。Moo 老师提出了上传各自备份到对方 NAS 的方案,于是我就拥有了一份免费的异地备份。

选型方面,VPS 的备份一开始看了 borg/kopia/restic 三个工具,感觉它们之间的差别不是很大,就随便选了一个。NAS 的备份由于 TrueNAS 自带了工具,就直接用了。TrueNAS 的 cloud sync 实际调用的是 rclone, 严格来说并不是备份而是同步。目前 rclone 跑一次没有修改过文件的同步需要半个小时,不知道瓶颈在哪里,是否有优化的空间。

TrueNAS apps 中的 webdav 应用感觉性能非常差,看了下镜像是 httpd,也就是 apache,不知道是否是配置的问题,但我也不想学习它的配置。于是干脆换成 Caddy。Caddy 本身没有 webdav 的支持,但是社区有,只需自己 build 一个镜像就可以用了。

所有跨局域网的网络访问均通过 tailscale 进行,以减小攻击面。


最后修改于 2025-08-31