ZFS 数据集¶
ZFS 页面用于备份 ZFS 数据集。一个对象是一个数据集连同它下面的所有数据集。每次备份时,BombVault 只为整棵树创建一个 ZFS 快照,因此其中每个数据集都记录的是同一时刻的状态。随后它从这个快照中读取每个数据集的文件,像保存文件夹一样用 restic 保存,并在完成后立即删除快照。备份经过去重,每一份都可以浏览,也可以还原单个文件。
BombVault 从不对数据集使用 zfs send,从不回滚数据集,也从不销毁任何数据集。
前提条件¶
- 到这台服务器的 SSH 连接。 ZFS 数据集使用与虚拟机备份相同的密钥、主机和用户。如果虚拟机备份已经可用,这里也可以用。否则请按照 GitHub 上的通过 SSH 备份虚拟机指南操作。模板字段名为 Host SSH: Address、Host SSH: Port 和 Host SSH: User。
- 该主机上的
zfs命令。 Unraid 6.12 及更高版本和 TrueNAS SCALE 都有。 - Host Data 映射为
/mnt,Access Mode 为 Read/Write - Slave。 这是模板的默认值。数据集的快照要在 BombVault 启动之后才会出现在数据集的.zfs/snapshot文件夹中,因此容器必须能收到主机之后创建的挂载。 - 数据集挂载在
/mnt之下。 在 Unraid 上,池位于/mnt/<pool>,所以这一点已经满足。
在 设置,常规 中打开该域(ZFS 数据集)。之后 ZFS 页面会显示 与这台服务器的连接 卡片。它会测试 SSH 连接,说明它连接的用户和主机,缺少什么时会指出缺少什么。主机集成检查(/spike)显示同样的结果。
对象与子数据集¶
在 ZFS 页面打开 添加数据集。列表来自服务器。选择您要备份的内容中最上层的数据集,例如 cache/appdata,对象就会包含它以及它下面的每个数据集。
- 新的子数据集会自动加入。 之后在对象下创建的数据集会在下一次运行时备份,那次运行会把它标为新数据集。它的第一次备份会完整读取一次,之后只读取变化部分。
- 可以排除单个子数据集。 在对象的设置中关闭某个子数据集,它连同其下的一切都会被排除。已排除但服务器上已不存在的子数据集会被这样标记,并可从列表中删除。
- 无法读取的子数据集会被跳过,但绝不会悄悄跳过。 运行会列出它们,对象会显示跳过了多少个,仪表板上的覆盖卡片会把每一个都算作未受保护。运行仍会备份其余所有内容,不会因为跳过的子数据集而失败。原因见原因代码表:数据集未挂载、设置了
canmount=off、挂载点为legacy或没有挂载点、加密密钥未加载、快照访问已关闭,或 BombVault 看不到的挂载点。 - 被跳过的数据集不会连带跳过它的子数据集。 只包含其他数据集的
canmount=off数据集会被跳过(显示为"仅结构"),而它已挂载的子数据集会被备份。密钥未加载的加密数据集会与共用其密钥的子数据集一起被跳过。 - 属于虚拟机磁盘或系统数据的子数据集在添加对话框中默认关闭,原因显示在开关旁边。添加整个池时需要确认,确认窗口会列出池中包含的内容。
卷¶
卷(zvol)包含的是虚拟磁盘而不是文件,ZFS 页面从不备份卷。
- 虚拟机使用的卷会在 虚拟机 页面随该虚拟机一起备份。
- 没有任何虚拟机使用的卷(iSCSI extent、已分离的磁盘)不会被 BombVault 备份。添加对话框和 ZFS 页面会统计这些卷并说明这一点。之后的版本会备份它们。
对象树中的卷在每次运行时都会被跳过并列出名称。
Docker 的存储¶
使用 Docker 的 ZFS 存储驱动时,每个镜像层都是一个挂载点为 legacy 的数据集。添加对话框会把它们按父级合并为一行。包含超过 20 个此类数据集的树不能成为对象:只要它的快照存在,Docker 就无法删除镜像层。请改为添加它下面的数据集,例如 appdata。
对象之间从不重叠¶
一个数据集只能属于一个对象。BombVault 会拒绝位于现有对象内部、或会包含现有对象的新对象。要把多个子对象合并成一个父对象,请先删除子对象并选择保留它们的备份,然后再添加父对象。每个数据集都以自己的名称保留历史,因此下一次备份会从旧对象停下的地方继续,不会重新读取全部内容。
快照前后停止容器与执行命令¶
对正在运行的数据库做快照就像突然断电:数据库通常能恢复,但它必须经过恢复过程。每个对象可以为此做两件事,两者都只作用于快照的那一刻,而不是整个备份。
- 为快照停止这些容器。 BombVault 停止列出的容器,创建快照,然后立即重新启动它们。同一依赖层级的容器并行停止,依赖方先停,因此整个窗口通常只有几秒钟;运行记录会显示具体时长。随后备份在应用已经重新运行时读取被冻结的快照。只有原本在运行的容器才会被停止。
- 快照前后执行的命令。 它在您选择的容器中运行,例如在快照前一刻把数据库导出到数据集中,而无需停止任何东西。如果快照前的命令失败,备份失败,也不会创建快照。快照后的命令失败会显示在运行记录中,但不会导致备份失败。
出现问题时会发生什么:
- 如果某个容器无法停止,BombVault 会启动已经停止的容器,备份失败并指出该容器。它从不退而对运行中的应用做快照。
- 停止操作会等待正在进行的容器备份结束(手动运行最多 30 分钟,计划运行最多到备份的时间上限),这样两者就不会同时停止和启动同一个容器。
- 在停止第一个容器之前,BombVault 会记下它要停止哪些容器。如果 BombVault 在窗口期间被强行终止,它下次启动时会重新启动这些容器并发送通知,对象会为每个无法启动的容器显示一条红色提示。
数据库的自动导出(见功能)随容器在 容器 页面上的自身备份一起运行,而不是随 ZFS 对象运行。如果某个数据库的容器只通过其数据集备份,就不会得到导出,所以请在这里为它设置命令。
一个容器可以同时出现在这个列表和 容器 页面上。这时它的数据会在两个仓库中各存一份,全部备份 也会停止它两次。对象会提示这一点。
还原¶
在对象上打开 备份,选择备份,再选择数据集。默认是对象最上层的数据集。
- 恢复回数据集里。 备份中的文件会写入数据集的挂载点。同名文件会被覆盖,其他文件保持不变。数据集从不会被回滚或替换。BombVault 会在开始前检查一次、在写入前再检查一次,确认数据集已挂载、可见且可写。数据集内部挂载了子数据集的位置不会写入任何内容:子数据集保留它的文件、属主和权限,并从它自己的备份中还原。
- 恢复到某个文件夹。 在
/mnt下选择一个文件夹。BombVault 会检查该文件夹是否位于已挂载的池或共享上,以及可用空间是否足够。这不需要 SSH 连接,也适用于已不存在的数据集。 - 到新数据集。 指定一个尚不存在的数据集。BombVault 会用备份中保存的 ZFS 属性创建它并还原到其中,参见还原为新数据集。
- 挑选文件(高级):只把您选择的文件和文件夹写回数据集。
- 这份备份里的所有数据集(高级):把树中的每个数据集还原到所选文件夹下各自的子文件夹。该备份中被跳过的数据集会列出名称。
- 从另一台服务器: 恢复 页面从另一个 BombVault 的仓库还原,总是还原到文件夹:一份备份的所有数据集,每个放进各自的子文件夹;或者树中的一个数据集,整个还原或只还原所选文件。
还原回数据集时,也会提供对象的待停止容器列表。这些容器在整个还原期间保持停止,其间容器备份会等待。
安全快照¶
在写入数据集之前,BombVault 会只为这个数据集创建一个 ZFS 快照,名称为 bombvault-prerestore-<时间>。它默认开启;关闭它需要第二次确认。如果无法创建快照,就不会还原任何内容。
BombVault 从不自行删除安全快照。对象会列出这些快照及其时长和大小,每个都有 删除 操作;当最旧的一个超过 30 天时会发出警告,因为它会让已删除和已更改的数据继续占用池空间。
还原后若要回退,请从数据集内的 .zfs/snapshot/bombvault-prerestore-<时间> 复制单个文件。zfs rollback <dataset>@bombvault-prerestore-<时间> 只在它仍是该数据集最新快照时有效。zfs rollback -r 会删除所有更新的快照,包括自动快照。
还原为新数据集¶
BombVault 每次备份都会保存每个数据集在本地设置的 ZFS 属性:compression、recordsize、quota、reservation、atime、xattr、acltype、casesensitivity 以及你自己的用户属性。继承的值和只读值会自行恢复,因此不保存。在 BombVault 开始保存之前的备份中没有这些属性。
- 到新数据集会带上所有保存的属性运行
zfs create。casesensitivity、normalization 和 utf8only 只能这样设置。配额和预留会在写入文件之后才设置,因此不会拒绝这些文件。挂载点不带过去,以免副本与原数据集冲突;canmount、readonly和加密设置也不带过去,以便还原可以写入。在加密数据集下创建的新数据集会继承它的加密。上级数据集必须存在。如果创建之后出了问题,新数据集会留在服务器上,因为 BombVault 从不销毁数据集。 - 恢复回数据集里会在还原旁边列出保存的属性。同时设置这些属性 会在写入任何文件之前,设置现有数据集仍然接受的属性。配额和预留会在写入文件之后才设置,因此不会拒绝这些文件。不打开此开关,数据集会保留自己的设置。
备份里包含什么¶
备份中包含:每个已备份数据集的文件和文件夹,以及 restic 保存的属主、权限、时间戳和扩展属性,以及每个数据集在本地设置的 ZFS 属性。
备份中不包含:
- 每个数据集顶层文件夹本身的属主和权限(其下的内容都包含在内)。还原回数据集时保留现有的顶层文件夹不变,还原到文件夹时以
0755权限创建它; - 已有的 ZFS 快照;
- 被跳过或被排除的子数据集;
- 卷。
要还原到新池,请先创建池,再把每个数据集还原为新数据集。TrueNAS 在 SMB 数据集上使用的 NFSv4 ACL 能否按预期恢复,目前尚未验证,所以在依赖它之前,请先用自己的数据试一次还原。
加密的数据集¶
加密数据集只有在其密钥已加载时才会被备份。否则会被跳过并给出警告;请用 zfs load-key 加载密钥并挂载数据集。BombVault 以解密后的形式读取数据,并保存到经过加密的 restic 仓库中。如果您在 BombVault 中关闭了加密,这个仓库就不是加密的。
遗留的快照¶
备份快照的名称为 <dataset>@bombvault-<14 位数字>,例如 cache/appdata@bombvault-20260924021500(UTC)。BombVault 会在备份完成后立即删除它。如果删除失败,例如因为数据集正忙或 BombVault 被停止,BombVault 会在以下时机删除它:
- 该对象的下一次备份之前,
- BombVault 启动时,针对每个对象,即使该域已关闭,
- 您删除该对象时,
- 您在对象上按下 立即移除 时,那里也会显示还剩多少。
只有与 bombvault- 加 14 位数字完全一致的名称才会被删除。安全快照、您自己的快照和自动快照从不会被触碰。手动删除某个快照:
异常¶
一个被清空的子数据集几乎不会改变一棵大树的总量,所以异常检测会单独关注对象中的每个数据集:大小、文件数、新数据和 restic 用时各有自己的历史。这些历史属于数据集的名称,因此之后这棵树由另一个对象备份时,历史仍然保留。
上一次运行备份过、而这次运行无法读取的数据集,只要对象的选择没有变化,就会被视为已清空。这包括密钥未加载、数据集未挂载以及数据集已从树中消失的情况。您自己排除的子数据集会改变选择,因此它的历史会重新开始。只要关于数据丢失的发现仍未关闭,保留策略就只保留该数据集的旧备份,树中其余部分照常清理。
在 异常 页面,每个数据集在对象的面板中都有单独的一行。该面板通过对象卡片上的 监控 打开;对象没有未处理项时,则从 没有未处理项 卡片中它的那一行打开。本页的对象树会在每个数据集旁显示未关闭的发现。发现中的链接会打开对象的还原面板,并定位到该数据集最后一份正常的备份。一次运行是否完成是按整个对象来判断的,因为一次运行作为整体成功或失败。
检查本身在功能中说明。通过 MCP 服务器连接的助手可以列出 ZFS 对象的还原点、启动它的备份并读取发现,但确认发现需要在 异常 页面上进行。
原因代码¶
页面、运行记录和通知会用以下代码之一来说明问题。大多数代码在页面上也会在旁边给出解决方法。
| 代码 | 含义 | 处理方法 |
|---|---|---|
ssh-missing |
此容器中没有设置 SSH 连接。 | 按虚拟机备份的方式设置 SSH 连接。 |
host-placeholder |
Host SSH: Address 仍是示例值,host.docker.internal 也没有响应。 |
把 Host SSH: Address 设为这台服务器的局域网 IP。 |
host-fallback |
Host SSH: Address 仍是示例值,但 host.docker.internal 可用。 |
无需处理,或者设置局域网 IP。 |
ssh-unreachable |
无法通过 SSH 连接到服务器。 | 检查地址和端口,并确认 SSH 已开启。 |
ssh-auth |
服务器拒绝了 BombVault 的密钥。 | 在服务器上运行一次连接卡片上显示的命令。 |
zfs-not-found |
SSH 主机上没有 zfs 命令。 |
把 Host SSH: Address 指向拥有这些池的机器。 |
zfs-permission |
SSH 用户无权运行这条 zfs 命令。 | 使用 root,或参见 TrueNAS SCALE。 |
uri-mismatch |
LIBVIRT_URI 指定的主机或用户与 SSH 字段不同。 |
使二者一致,或清空 SSH 字段,让两者都取自 URI。 |
zfs-error |
zfs 报告了其他错误。 | 详细信息中会显示其消息。 |
propagation-missing |
主机上的新挂载无法到达容器。 | 把 Host Data 的 Access Mode 设为 Read/Write - Slave,然后重启 BombVault。 |
invalid-name |
BombVault 不接受的数据集名称。 | 重命名该数据集。 |
name-too-long |
树中某个数据集的名称用作快照名称太长。 | 重命名它,或把它下面的一个数据集添加为对象。 |
invalid-exclude |
某个排除模式或已排除的子数据集与对象不匹配。 | 修正消息中指出的条目。要排除整个子数据集,请直接关闭它,而不是写模式。 |
not-found |
服务器上不存在该数据集。 | 删除该对象,或重新创建数据集。它的备份仍可还原。 |
not-filesystem |
这是卷,不是文件系统。 | 参见卷。 |
overlaps-item |
该数据集与现有对象重叠。 | 参见对象之间从不重叠。 |
docker-storage |
该树包含 Docker 的镜像存储。 | 参见Docker 的存储。 |
nothing-readable |
对象中目前没有任何数据集可以读取。 | 查看被跳过数据集的代码。 |
snapshot-failed |
无法创建快照。 | 详细信息中会显示 zfs 的消息。 |
containers-busy |
需要停止容器时,还有容器备份在运行。 | 稍后再开始。计划运行会自动等待。 |
consistency-stop-failed |
有容器无法停止,所以没有创建快照。 | 检查该容器,或把它从列表中移除。 |
pre-snapshot-failed |
快照前的命令失败了。 | 运行详细信息中会显示其输出。 |
container-unknown |
列表中的某个容器不存在。 | 把它从列表中移除。 |
container-is-self |
BombVault 无法停止自己的容器。 | 把它从列表中移除。 |
leftover-snapshots |
服务器上还留有 BombVault 无法删除的快照。 | 按 立即移除,参见遗留的快照。 |
zvol |
树中的一个卷,已跳过。 | 参见卷。 |
canmount-off |
从不挂载(canmount=off),已跳过。 |
如果其中有数据,请挂载它,或把数据移到子数据集中。 |
legacy-mount |
legacy 挂载点,已跳过。 | 为它在 /mnt 下设置挂载点。 |
no-mountpoint |
没有挂载点,已跳过。 | 为它在 /mnt 下设置挂载点。 |
not-mounted |
在服务器上未挂载,已跳过。 | 用 zfs mount 挂载,或设置 canmount=on。 |
key-not-loaded |
已加密且密钥未加载,已跳过。 | 运行 zfs load-key,然后挂载。 |
snapdir-disabled |
快照访问已关闭,已跳过。 | zfs set snapdir=hidden <dataset>。.zfs 文件夹仍保持隐藏。 |
not-visible |
BombVault 看不到该数据集的挂载点。 | 把挂载点移到 Host Data 路径下,或以 Read/Write - Slave 将其映射到容器中的相同路径。 |
shfs-only |
该数据集只能通过会隐藏快照的 /mnt/user 看到。 |
把 /mnt 而不是 /mnt/user 映射为 Host Data。 |
snapshot-not-visible |
快照已创建,但没有出现在 BombVault 中。 | 运行 测试快照访问;见下文。 |
snapshot-loop |
由于 Host Data 不传递新挂载,快照没有到达 BombVault。 | 把 Host Data 的 Access Mode 设为 Read/Write - Slave,然后重启 BombVault。 |
backup-failed |
restic 在此数据集上失败。 | 运行详细信息中会说明原因。 |
not-reached |
运行在处理到此数据集之前就结束了。 | 重新运行备份。 |
gone |
该数据集已不在服务器上。 | 无需处理。它的备份仍可还原。 |
read-only-mount |
BombVault 只能读取该数据集,因此无法还原到其中。 | 把映射设为 Read/Write - Slave,或还原到文件夹。 |
destination-not-mounted |
该文件夹不在已挂载的池或共享上。 | 选择池或共享上的文件夹。 |
not-enough-space |
目标位置的可用空间不足。 | 释放空间,或选择其他文件夹。 |
safety-snapshot-failed |
无法创建安全快照,因此没有还原任何内容。 | 详细信息中会显示 zfs 的消息。 |
safety-name-too-long |
数据集名称用于安全快照太长。 | 关闭安全快照,或还原到文件夹。 |
dataset-exists |
已存在同名数据集。 | 换一个新名称,或恢复到该数据集本身。 |
create-failed |
无法创建新数据集。 | 详细信息中显示 zfs 的消息。请检查上级数据集是否存在。 |
new-dataset-not-visible |
新数据集已创建,但 BombVault 看不到它,因此没有恢复任何内容。 | 数据集保留在服务器上。把它挂载到 Host Data 路径下,再恢复到其中。 |
set-properties-failed |
无法设置保存的属性,因此没有恢复任何内容。 | 详细信息中会显示 zfs 的消息。 |
set-limits-failed |
文件已恢复,但无法设置保存的配额或预留。 | 详细信息中显示 zfs 的消息。请用 zfs set 自行设置配额或预留。 |
检查容器能看到什么¶
对象上的 测试快照访问 会为它的树创建一个真实快照,针对每个数据集在 BombVault 内查找它,然后再将其删除。这是在第一次计划运行前验证整条路径的最快方法。
如需自己查看,请在服务器上运行:
每一行是容器内的一个挂载。数据集所在的行显示它在容器内的路径(位于 /host/user 之下)和数据集名称。该行中如果有 master:N 字段,表示这个挂载能收到主机之后创建的挂载,而快照访问需要的正是这一点。如果没有,请把 Host Data 的 Access Mode 设为 Read/Write - Slave,然后重启 BombVault。
TrueNAS SCALE¶
- 设置了
LIBVIRT_URI时(与 TrueNAS 上的虚拟机备份相同),BombVault 会从 URI 中获取其 zfs 命令所用的 SSH 主机、用户和端口中未单独设置的项。如果不做虚拟机备份,请改为设置LIBVIRT_HOST、LIBVIRT_SSH_USER和LIBVIRT_SSH_PORT。在 Additional Environment Variables 下添加这些变量。 - root 以外的用户需要对对象最上层数据集拥有权限,这些权限随后覆盖其下的每个数据集:
还原到新数据集还需要对上级数据集拥有 create 权限,设置保存的属性则需要这些属性的权限。
在 TrueNAS 上,非 root 的 SSH 会话路径中没有 /usr/sbin;这时 BombVault 会直接调用 /usr/sbin/zfs。
- 应用的 Host Data 必须是数据集之上的主机路径,例如 /mnt/tank,而不是 ixVolume。使用主机路径时,应用会把主机的新挂载传给 BombVault(rslave),而快照访问需要的正是这一点。