本文诞生于笔者使用 TrueNAS, 群晖 DSM, 飞牛OS 的过程中。文件系统如何实现 CoW 和快照是个技术难题,但是如何利用快照技术实现数据保护,依然需要用户做很多思考。为此,笔者写下了这篇文章。
考虑到飞牛目前的 ZFS 快照/replication 还处于一个非常初级的阶段,因此本文也适合飞牛的开发者参考。
坑边闲话:绝大多数人都听过「3-2-1 备份原则」:至少三份数据,存储在两种不同介质上,其中一份存放在异地。然而,即使在真正理解这一原则的人群中,数据丢失的案例依然层出不穷。原因很简单:他们只有备份意识,却没有备份系统;只有备份习惯,却没有自动化策略。本文以 ZFS 为底层文件系统,讲解备份过程中的内功心法。如果你使用的是其他类型的 CoW 文件系统如 BtrFS,应该也能从中获益。
现实中最危险的情况并不是“没有备份”,而是:
- “我打算做备份,但还没开始。”
- “我上个月备份过一次,应该没问题。”
- “我桌面同步到 NAS 了,也算备份吧?”
- “我准备找时间再备一下。”
一旦出现硬盘损坏、系统崩溃、勒索病毒、用户误删,手动备份都是苍白无力的。只有当备份方案具备以下特征时,数据才真正安全:
- 自动化:不依赖人的意志;
- 低开销:不打扰日常工作,不占用过多资源;
- 可验证:快照、校验机制确保备份有效;
- 可逆转:恢复流程清晰,不需要人临时拼命抢救。
换句话说:
- 可靠的备份不是“记得备份”,
- 而是“就算你忘了,系统也会替你记得”。
1. 备份的内功心法
本来笔者打算花大量篇幅介绍 zrepl 的使用方案,并像往常一样给出备份 yaml 配置文件作为最佳实践。然而,在学习了大量 zrepl 的设计细节之后,笔者发现这样的文章将会非常长,而且阅读难度相当高,非常不利于新用户上手使用 zrepl 这个极为强大的备份工具。为此,笔者打算先从一个基本的 YAML 配置文件入手,帮助你厘清为什么 zrepl 选择了这样的设计。
当你学会使用这个最基本的模板之后,后期可以自行探索更高级的语法。
1.1 备份案例呈现
ZFS 的 replication,即 zfs send | zfs receive 基于标准数据流,因此 zfs send 可以重定向到文件,zfs receive 也可以从文件、网络流中接收。当然,这也给程序员提供了无限的想象空间,我们可以基于高性能传输标准构建更强大、更安全的网络协议,在更短时间内、更安全的隧道中实现可靠的 ZFS replication. 然而,笔者今天不打算介绍这一部分,因为这并不是备份的核心要点。
下面是笔者常用的本机备份配置文件。所谓本机备份,就是从本机的 pool_1 获取数据,然后 发送到 pool_2,这当然不属于「3-2-1 备份原则」,因为数据依然只在一台机器上,没有实现真正的异地备份。不过,这个配置文件可以将:
- source 的快照策略
- source、sink 的快照保留策略
等核心概念讲清楚。
**s:
- type: sink
name: "local_sink"
root_fs: "Solidigm_P44Pro_Stripe"
serve:
type: local
listener_name: localsink
- type: push
name: "gtr7-debian_backup"
connect:
type: local
listener_name: localsink
client_identity: local_backup
filesystems: {
"zroot<": true,
"Samsung_990Pro_Stripe<": true,
}
snapshotting:
type: cron
cron: "*/5 * * * *"
prefix: zrepl_
timestamp_format: "human"
pruning:
keep_sender:
- type: not_replicated
- type: last_n
count: 72
keep_receiver:
- type: grid
grid: 1x1h(keep=all) | 24x1h | 35x1d | 6x30d
regex: "zrepl_.*"
1.2 发送与接收
一次成功的 ZFS replication,本质上由两个动作构成:
- 发送者执行
zfs send
- 接收者执行
zfs receive
听起来简单,但现实情况比这复杂得多,不同工具在“谁负责 send / receive”以及“控制权在哪里”都有完全不同的设计哲学。
1.2.1 TrueNAS:集中化、NAS 主导的 replication 模型
以 TrueNAS 的 Replication Task 为代表,它将所有决策和命令执行权都集中在 NAS 本体上。
TrueNAS 的基本流程:
- 首先根据任务类型判断:
- Pull:NAS 主动去远端拉取快照(NAS 调用远端的 zfs send)
- Push:NAS 主动向远端推送快照(NAS 本地执行 zfs send)
- 根据任务类型,TrueNAS 会通过 SSH 在远端执行必要命令,典型流程是:
- 在远端执行 zfs send
- 在本地执行 zfs receive(或反之)
TrueNAS 的优点:
NAS 就能完成整个备份流程。然而,TrueNAS 的方案也有很多弊端。
TrueNAS 理论上可以远程调用 zfs snapshot <ds>,但它没有实现,原因就在于 SSH 是远程调用,存在延迟与阻塞,无法保证 snapshot 的实时性与一致性。此外,同一台 TrueNAS 对数百个客户端下发快照命令时,由于并发量限制,难以维持精准的实时性。因此,
- 客户端系统必须自行创建 / 管理自己的快照,
- NAS 只能基于已有的快照执行 replication.
这一点,也让 TrueNAS 在“多客户端、大规模环境”下显得不够优雅。
1.2.2 zrepl:分布式、端到端自动化的现代 replication 模型
与 TrueNAS 完全不同,zrepl 采用的是分布式架构。在跨主机 replication 场景中,zrepl 要求参与系统都必须安装 zrepl. 这看似成本更高,但实际上带来巨大的工程优势:
- 每台机器都能自动地
- 创建快照
- 管理快照生命周期
- 执行
send
- 执行
receive
- 所有的 snapshot、pruning、replication 全部通过 zrepl agent 处理
也就是说,zrepl 从 NAS 主导式备份升级为「分布式备份管控系统」。为什么这更强?因为 zrepl 避免了 SSH 远程调用的弊端:
- snapshot 可以由本机 zrepl agent 直接执行(毫秒级)
send/receive 自动根据带宽、快照链、resume token 等自动协调
- prune(保留策略)可在 sender 和 receiver 侧分别执行
- 不会因为网络延迟错失 snapshot,或执行时机不准确
此外,zrepl 几乎为所有 Linux 发行版、甚至 FreeBSD 都提供了官方安装包。这意味着:Debian、Ubuntu、Arch、RedHat 等系统均可原生部署,远比 TrueNAS 的 SSH 远程操控模型更适合集群、分布式备份、实验室、多池架构等场景。
1.3 精简的快照策略
zrepl 的快照策略乍看非常简单:只需要配置一个高频快照任务即可。但这背后依赖的是 zrepl 强大而灵活的快照保留 (pruning) 机制。
与此形成鲜明对比的是 TrueNAS 等系统的快照机制。它们依赖传统的 cron 式定时任务,为了实现“近期稠密、远期稀疏”的快照保留方式,只能采取如下策略:
- 为同一个数据集设置多个快照任务
- 每个任务对应不同的周期与保留时间,例如:
- rule1: 每 5 分钟一次,保留 2 小时
- rule2: 每 1 小时一次,保留 24 小时
然而这种方式带来了许多工程层面的麻烦。如果两个快照任务采用相同的命名规则,那么在时钟的整点,5 分钟策略会触发,1 小时策略也会触发,而创建同名 ZFS 快照会引发系统报错。为避免冲突,TrueNAS 官方建议:
- 方案 A: 为快照任务设置不同的命名规则
- 方案 B: 调整任务执行时间,例如偏移 2 分钟
但这两种做法都不够优雅:
-
方案 A:时间偏移
- 快照时间不再与整点、整分对齐
- 不利于排查时间线
- 不符合大多数人习惯的时间结构
-
方案 B:不同命名规则
- 两个几乎完全相同的快照被重复创建
- 快照数量无意义增加
- 写入性能下降,快照太多会导致 ARC/metadata 压力。ZFS 行为决定了快照越多,写入越慢。无意义的快照应该尽量避免。
zrepl 的做法是让快照生成与保留策略解耦。为此,zrepl 采用了完全不同的理念:只需要一个高频拍摄策略。例如每 5 分钟拍一次:
# periodic 意味着从 zrepl 服务启动开始,周期性执行快照任务
snapshotting:
type: periodic
interval: 5m
timestamp_format: "human" # 时间为 UTC 时区,human 格式更容易读懂
# periodic 有可能不是钟表刻度整分对齐,因此还可以使用更规整的 cron 策略
snapshotting:
type: cron
cron: "*/5 * * * *" # 同样是 5 分钟执行一次
prefix: zrepl_
timestamp_format: "human"
「近期稠密、远期稀疏」原则由 pruning (保留策略) 自动实现。zrepl 提供了比 TrueNAS 先进得多的 pruning 规则,例如 grid:
# 如果不写 (keep=?) 就默认 (keep=1), 下一节会详细介绍
grid: 1x1h(keep=all) | 24x1h | 30x1d | 6x30d
这类策略意味着:
- 最近 1 小时:全保留(每 5 分钟一条)
- 最近 24 小时:保留小时级快照
- 最近 30 天:保留每天一个
- 最近半年:保留每月一个
所有这些都无需额外 snapshot 任务,也不需要不同命名规则,也不会产生时间冲突,更不会产生冗余快照,真正实现了单一任务、多层时间密度、自动演化。
总结:TrueNAS 依赖多个定时快照任务组合策略,复杂且容易冲突;zrepl 依赖单一高频快照加强大的 pruning 策略,简单却极度灵活。
这也是为什么:
- TrueNAS 的快照体系是基于时间的触发机制
- zrepl 的快照体系是基于事件与生命周期的链式机制
两者不仅在技术细节上不同,在架构设计上就完全不是一个时代的产物。