跳转至

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 位数字完全一致的名称才会被删除。安全快照、您自己的快照和自动快照从不会被触碰。手动删除某个快照:

zfs destroy -r cache/appdata@bombvault-20260924021500

异常

一个被清空的子数据集几乎不会改变一棵大树的总量,所以异常检测会单独关注对象中的每个数据集:大小、文件数、新数据和 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 内查找它,然后再将其删除。这是在第一次计划运行前验证整条路径的最快方法。

如需自己查看,请在服务器上运行:

docker exec BombVault grep zfs /proc/self/mountinfo

每一行是容器内的一个挂载。数据集所在的行显示它在容器内的路径(位于 /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 以外的用户需要对对象最上层数据集拥有权限,这些权限随后覆盖其下的每个数据集:
zfs allow <user> snapshot,destroy,mount <dataset>

还原到新数据集还需要对上级数据集拥有 create 权限,设置保存的属性则需要这些属性的权限。

在 TrueNAS 上,非 root 的 SSH 会话路径中没有 /usr/sbin;这时 BombVault 会直接调用 /usr/sbin/zfs。 - 应用的 Host Data 必须是数据集之上的主机路径,例如 /mnt/tank,而不是 ixVolume。使用主机路径时,应用会把主机的新挂载传给 BombVault(rslave),而快照访问需要的正是这一点。