thundersnap v0.01:一个可以撤销所有操作的按钮

Lobsters Hottest 工具

摘要

Thundersnap 是 Tailscale 开发的一个实验性分布式文件系统,它利用 btrfs 快照和 Tailscale 网络来提供类似撤销的功能,并在计算机之间高效复制。目前它只是一个实验品,尚未达到生产就绪状态。

<p><a href="https://lobste.rs/s/d7mfza/thundersnap_v0_01_undo_button_for">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/07/04 18:44

tailscale/thundersnap 源代码:https://github.com/tailscale/thundersnap

THUNDERSNAP

Thundersnap 是一个分布式文件系统设计中的新旧想法集合。它的命名源自这个早期版本可能失败的两种主要方式:惊群效应(其网格复制协议)和灭霸响指(很可能导致大约一半的数据丢失)。总之,现在不要在生产环境中使用它!它只是一个玩具。但作为玩具,它给了我们发挥创造力的机会。

想象一下,如果我们去掉生产级分布式系统的所有要求:不必无限扩展,不必有完全可预测的性能,不必严格将持久化存储与运行时分离,不必在执行前构建一个幂等的容器。你可以做有趣的事情,而不是谨慎的事情。

如果你的整个电脑有一个“撤销”按钮会怎样?如果你在一台电脑上运行着完整的 Debian 系统和容器,然后用几条命令就能把它迁移到另一台电脑上会怎样?如果你有一个类似 Nix 的、内容可寻址的操作系统,但不需要 Nix,因为任何内容都可以被内容寻址?这就是我们追求的目标。它可能没用,但很有趣。

快速开始

要开始使用 Thundersnap,安装其中一个发行版包或从源代码构建。(TODO:将发行版包放到某个地方。)

注意:你需要一个基于 btrfs 的根文件系统,或者给 thundersnapd 提供一个挂载了 btrfs 的 --data-dir 参数。也许有一天我们不再依赖 btrfs,但今天依然依赖它。

然后你需要通过登录 Tailscale 来激活。Thundersnap 从根本上需要 Tailscale 才能工作,这样它的认证和网格复制功能才是安全的。你可以使用 Headscale(开源控制服务器),或者使用 Tailscale 慷慨的免费计划(https://tailscale.com/blog/free-plan)。

总之,一旦 thundersnapd 在后台运行,使用 thundersnapd --activate 并打开链接登录。完成后,你可以使用 SSH 访问你的第一个空容器:ssh root@@thundersnap(是的,双 @ 符号表示容器名为空,给你一个默认的空空间来玩耍)。

持久化快照

Thundersnap 系统的主要属性是持久性——数据库“ACID”术语中的 D。Thundersnap 非常努力地使你的数据持久化。它并不特别追求原子性、一致性或隔离性。在这个版本中,它可能也不是超级持久,但这不是架构的限制。

Thundersnap 的基本论点是,现代分布式存储需要依赖快照而不是实时同步。没有其他东西能给你有用的多用户语义(你其实不想看到其他用户未完成的工作!)。而且,因为真正的全球分布式系统(包括你笔记本电脑或手机上的虚拟机)存在延迟和可靠性问题,始终与所有副本保持连接本身也是一种幻觉。

简而言之,Thundersnap 对文件系统的假设类似于 git 对源代码树的假设。这不是巧合,因为它的结构受到我十多年前的 bup 项目(https://apenwarr.ca/log/20100104)的启发,而 bup 又受 git 启发。

你可以在一次性或低质量的计算机上运行 Thundersnap。与其试图让这些计算机(或它们的磁盘)更高质量或更不易丢弃,我们让创建内容可寻址的快照变得容易,然后有效地将这些快照复制到其他计算机。如果你在足够多的地方有足够多的计算机,你可能就不会丢失数据。

Thundersnap 目前使用 btrfs 来制作瞬间快照,但不使用 btrfs 复制,因为那不使用内容可寻址格式,因此有长期的架构限制。因此,Thundersnap 的实现目前依赖于 btrfs,但它的架构协议并不依赖。也许将来会有更好的快照文件系统,我们可以透明地切换过去。

创建快照使用 ts snap

轻量级执行帧

Thundersnap 默认使用 Linux 命名空间(“容器”)而不是完整虚拟机。其理念是,现代云架构建议将你做的每件事都变成自己独立的微型 VM。虽然这非常安全,但也非常慢,而且不知怎的我们已经习惯了。在 Unix 中,你可以在毫秒内启动和停止一个小程序。Thundersnap 的目标是启动容器时达到相同的速度(虽然目前还没那么快,但已经非常快了)。而且这些容器比 Unix 进程隔离得更好。它并不完美,但速度快。

💡 注意:还有部分成型的基于 VM 的隔离支持;这个想法是,你可以将一个或多个容器放入一个微 VM 中,它们仍然共享同一个底层 btrfs 文件系统。VM 添加了一个抽象层,但仍然非常快。这方面还有更多工作要做,但你现在就可以尝试。

一个执行帧实际上由三个独立的快照组成:

  • root (/):操作系统(你可以从 Docker 容器中提取一个来开始,如果你喜欢的话)
  • /home:包含你个人偏好和工具的 /home 目录
  • /work:存放你正在工作的项目的 /work 目录

你可能会惊讶 /home/work 是分开的。我们这样做是因为你可能想独立地替换这三个部分中的每一个。例如,如果你在 Debian 容器中工作,并想在你的程序在 Red Hat 构建环境中测试,你会替换 root。如果你想把你的玩具给朋友继续开发,他们可以保留 root 和 /work,但使用他们自己的 /home,这样他们所有的个人点文件都完好无损。

要创建一个新的执行帧,使用 ts frame <root-snap>:<home-snap>:<work-snap>。这些字符串中的任何一个都可以为空,表示“保留我已有的那个”。或者它们可以是 nil,表示该组件最初为空。

你可以用 ts go 跳入另一个帧。例如,ts go nil:nil:nil 创建一个新的空帧并在其中启动一个交互式 shell;退出该 shell 会让你回到父帧。ts go redhat:: 创建一个新帧,它是当前帧的完美副本,但根文件系统是 redhat,然后跳进去。

ts go 的一个有点奇怪(目前)的变体是 ts undo。它从你上一个快照创建一个新帧,然后进入该帧,使你回到过去。然后你可以再次 ts undo 回到更早的快照,以此类推。这的语义并不是我想要的(难道不应该替换当前帧或类似操作吗?),但它是一个很好的演示。

引用(Refs):命名帧

帧通常以 uuid 命名,因为我们创建了很多帧,而且它们通常是用于一次性临时目的。每次你想做一个实验,你都可以从当前环境的副本中启动一个帧,做实验,然后扔掉它。这类似于 git 中的“提交”,每个都有自己的乱码名称。(快照在 git 中类似于“树”。)

引用(Refs)也类似于 git 引用(即分支和标签)。你可以将 ref 指向任何你想要的帧 uuid。一旦这样做,你就可以通过 ref 来引用那个确切的帧。多个 ref 可以指向同一个 uuid,但每个 ref 只指向一个 uuid。你可以使用 ts ref 命令或在使用 ts frame 时的 --ref 标志来操作 ref。

Refs 还关联着私有数据,这些数据不会被快照。这旨在用于秘密身份和密钥文件,这些文件应该只存在于一个帧中。例如,Tailscale 的 tsnet(https://tailscale.com/docs/features/tsnet)库维护着唯一标识一个节点的状态信息。给定 ref 的状态位于该 ref 当前指向的帧内的 /id/<refname>/ 目录中。如果你将 ref 移动到另一个帧,该目录也会随之移动(并且旧帧内的所有进程都会被终止,这样就不会有残留的密钥材料在 RAM 中浮动)。

这让你可以实现蓝绿部署,例如:

  • ts frame --ref blue <root>:<home>:<work>
  • ts go blue
  • app --state-dir=/id/blue/ --login # 执行 Tailscale 登录流程
  • …让应用正常工作…
  • ts frame --ref=green :: # 复制我的帧
  • ts go green # 复制我的帧
  • app --state-dir=/id/green --login # 执行 Tailscale 登录流程
  • …修改应用并在 green 模式下测试…
  • ts ref move blue $(ts frame)

总之这部分测试得不多,但你明白了。

轻量级持久化应用

说到应用,有一种方式可以在你本人没有连接到帧时自动运行它们,这很好。我们没有在每个 VM 或容器内运行 /sbin/init,而是只提供一个文件系统,看起来像你想要的,以及一个简单的 ts autorun 命令,让你确保一个给定程序始终在一个容器内运行。

想法是你可以 SSH 进入容器,实时编辑程序,杀死它,然后它会重启。你这样做会感到内疚。这就像 1990 年代在生产服务器上编辑 PHP 网站。但请记住,Thundersnap 不是为生产环境准备的,它是为玩具准备的。你的玩具停机会有什么大不了的吗?如果你搞砸了,ts snap 会根据你的需要经常为旧版本创建快照。

将 Docker 容器转换为快照

众所周知,Docker 容器具有可以更新的“名称”。所以当你下载一个特定的容器时,你可能不完全确定它是否和你昨天下载的那个一样。如果你想拉取最新的安全修复,这很“棒”,但这也意味着你拉入了任意的其他更改,这些更改可能更好或更坏,但至少会让人意外。

Thundersnap 的工作方式不同。如果你使用例如 ts download-docker debian:latest,它会下载名为 debian:latest 的最新 Docker 容器,但随后它会打印出它所创建快照的内容哈希;每次你使用那个内容哈希,你都会得到完全预期的内容。

例如,ts frame --ref=deb (ts download-docker debian:latest):: 会给你一个名为 deb 的新帧,其中包含 debian:latest 操作系统以及你当前的 home 和 work 目录树。然后你可以用 ts go deb 进入它,或者退出你的 SSH 会话并重新进入:ssh root@deb@thundersnap

网格复制

这部分仍然有 bug,但核心框架已经存在。要启用网格模式,你需要在启动 thundersnapd 时传递 --mesh 参数。启用网格模式后,thundersnapd 会定期 ping 你 tailnet 上所有具有相同身份(默认是你的用户名)的机器。这有点 hacky,我们应该修复它,但这是一个开始的好方法。

所以如果你有多台电脑,并在每台上安装了带 --mesh 的 thundersnapd,它们会互相发现。如果你愿意,可以通过访问 http://thundersnap:7575/ 查看它们的网格状态和对等节点列表。(将 “thundersnap” 替换为你分配的主机名。)

一旦你有对等节点,你就可以复制快照。目前唯一的相关命令是 ts download-snap <snapid> —— 只要你知道在一台机器上通过 ts snap 生成的 snapid,你就可以将它下载到另一台机器,然后从中构建帧。

这为未来的自动化留下了很大的空间。例如,我想在本地机器上每次运行 ts snap 时自动将快照复制到另一台机器,自动剪除旧快照,实现帧的一键迁移等等。你可以将这些全部构建为基于 Thundersnap 现有原语的工具。

Thundersnap 与 Tailscale 的关系是什么?

Tailscale 拥有版权,但目前这只是我的个人项目(Avery Pennarun,apenwarr)。它目前并未在 Tailscale 内部用于任何实际用途,也不应该——它是我用于自己一些东西的玩具。Tailscale 还有其他四五个用于启动 VM 或容器的工具,其中一些是开源的,一些很快会开源。其中一些用于生产环境,有些使用频率很高。Thundersnap 不是。

我认为 Thundersnap 是最好玩的一个……但我有偏见。Tailscale 现在是一家“真正的公司”,官方 Tailscale 产品需要有一定质量水平,以便我们能为那些将其用于实际工作的人提供实际支持。但是,为了在家庭实验室中获得真正的乐趣,有时我们必须把边界推得更远。

Thundersnap 将许多不寻常的假设结合到一个包中。它需要一段时间才能成熟。但我很想听听你的想法!我的邮箱是 [email protected],或者在 Bluesky 上叫 @apenwarr.ca,我听说你也可以通过一个叫 bridgy 的工具从 Fediverse 上联系到我。

相似文章

KDE 中的 Btrfs 快照集成

Lobsters Hottest

KIOSnapshot 是一个新发布的 KDE 工具,使用户能够直接从 Dolphin 等文件浏览器访问 Btrfs 快照,为文件和子卷提供时间旅行功能,类似于 Windows 的“以前的版本”或 macOS Time Machine。

Snapr

Product Hunt

Snapr 是一款用于捕捉想法并即时查找的工具。

Rift:Git Worktrees 的更优替代方案

Hacker News Top

Rift 是一个命令行工具,提供比 Git worktrees 更好的替代方案,通过写时复制快照在 Linux 的 btrfs 和 macOS 的 APFS 上实现快速创建工作区。