SBD:我们家里就有文件系统

Hacker News Top 工具

摘要

Fly.io 介绍了 Sprites 块设备(SBD),用一个更简单的块设备方案取代了之前魔改的 JuiceFS 存储栈,以提升 Sprites 的可靠性并消除元数据不一致问题。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/10/22 15:57

# SBD:家里就有文件系统 来源:https://fly.io/blog/sprites-block-device/ 作者Daniel Botha姓名Daniel Botha一位疲惫的鸟厨师,身旁是一个酷酷的意大利面机器人插画作者Annie Ruygt (https://annieruygtillustration.com/)对于那些自己做意大利面的人,我有些不太客气的话想说。不过在这里,我只会拿他们举个例子,说明构建基础设施时不该抱有什么想法。人们太容易误以为,在一件事上花的功夫越多,成果就越好。但基础设施要做的是那袋 1 美元的意大利面:简单、好用,而且不需要你花一周时间把厨房角落里的面粉刮干净。 Sprites 刚开始开发时,它是为一种新产品打造的一种新基础设施。所以,当我们在意大利面货架上找不到合适的文件系统时,就自己做了一个。它很巧妙,而这恰恰是我们不喜欢它的原因。这篇文章介绍它的替代品:Sprites 块设备(SBD)。它就是你从货架上随手拿起的那袋意大利面,而你唯一会察觉到的区别,就是可靠性更好了。 ## https://fly.io/blog/sprites-block-device/#it-was-always-object-storage一直都是对象存储 对于 Sprites 存储的一个核心方面,我们的立场从 v1 起就没变过:Sprite 磁盘的根基是对象存储,而不是 Sprite 恰好运行所在的那台服务器上的 NVMe。正因如此,Sprite 才能休眠、在另一台服务器上唤醒,并且仍然在原处找到自己的文件。一个 Sprite 的持久化状态,差不多就是存储桶里的一个位置。我们曾满怀感情地长篇解释过原因 (https://fly.io/blog/design-and-implementation/)。 对象存储的问题在于,它无法修改对象的一部分。哪怕只改一个字节,也得替换整个对象。这与 Linux 程序的习惯很不合拍,因为它们总是在文件中间重写几个字节。必须有什么东西来处理这种转换。 产品发布时,负责这件事的是一个经过大幅改造的 JuiceFS 版本。原版 JuiceFS 将存储分成两部分:数据以分块形式放在存储桶里,元数据则放在数据库里,记录哪些分块组成哪些文件。不过,我们并不是这样用它的。我们的魔改版运行着重写后的 SQLite 元数据后端,通过 Litestream 实现持久化。它只存储一个文件:一个 100 GB 的磁盘镜像,通过 loop 设备挂载,里面是 ext4。Linux 负责所有常规的 ext4 操作。JuiceFS 只是托管这个镜像。 它运行得还不错。Sprites 靠它上线,用户创建了大量 Sprite,而且检查点创建得很快,因为创建检查点基本上只是挪动元数据。但随着使用量增长,问题开始显现。 ## https://fly.io/blog/sprites-block-device/#manifests-that-lie会撒谎的清单 带有元数据数据库的文件系统有两个密不可分的组成部分:存储桶里的分块,以及说明这些分块如何拼在一起的数据库。两者一致时,你就有文件。两者不一致时,你就完蛋了。 我们在上面构建的每一层,都必须继承这种结构,并继续往上加东西。在 JuiceFS 下,检查点是磁盘镜像的写时复制克隆,因此元数据数据库除了跟踪当前磁盘的分块,还要跟踪每个检查点的分块。另一个 SQLite 数据库 `checkpoints\.db` 则列出检查点本身,以及每个检查点应该将哪个镜像文件挂载到 loop 设备上。 所以,一个 Sprite 的磁盘由分块、说明哪些分块组成各个镜像的元数据数据库,以及说明各个镜像对应哪个检查点的检查点数据库构成。每一部分都单独持久化,所以其中任何一部分都可能是对的,而其他部分却是错的。 ## https://fly.io/blog/sprites-block-device/#what-could-go-wrong能出什么问题呢? 这个故事可能源自亲身经历,也可能不是:某一天,一个 Sprite 无法启动了。JuiceFS 向对象存储请求一个分块,得到的却是 404。我们想要速度快,所以让 JuiceFS 运行在回写模式下。它会先在元数据中记录分块,再上传实际字节。如果机器在这两个步骤之间的任何时刻被终止,元数据就会指向一个不存在的分块。 于是,运行在其上的 ext4 文件系统发现某个目录块未通过校验和检查,便切换为只读。把 Sprite 移到新机器上无济于事,恢复检查点也没用,因为 JuiceFS 的检查点是克隆。它们共享相同的分块,所以每个检查点都会有同一个缺口。 ## https://fly.io/blog/sprites-block-device/#my-agent-ate-my-dogfood我的智能体吃了我的狗粮 促成 Fly.io 史上最密集的一轮“吃自家狗粮”测试的,与其说是这类问题的技术细节,不如说是它们悄无声息的特性。而如果你要为智能体提供计算机,并亲自试用,那最好让智能体来吃这份狗粮。 于是,我们搭建了一个测试框架,让成群的 Sprite 一遍又一遍地执行写入、创建检查点、休眠、唤醒和恢复,并验证读回的每一个字节。我们让它对 400 个 JuiceFS Sprite 跑了大约 5,000 个循环。其中有不止一个永久失去了创建检查点的能力。好吧……操。 有一段时间,我们继续审视自己的配方。增加了更多一致性核对、更多修复工具,以及越来越巧妙的办法,来让三本账保持一致。但一份自带修理包的意大利面,无论怎么做,吃起来都会怪怪的。 ## https://fly.io/blog/sprites-block-device/#just-a-disk就是一块磁盘 现在登场的是:Sprites 块设备(SBD)。它拿一个存储桶,通过 NBD 将其作为块设备呈现给 Linux 内核。Sprite 将这个设备格式化为 ext4 并挂载,就像你在任何 Linux 机器上挂载磁盘一样。 这不是明摆着嘛。 在 SBD 下,元数据与它所描述的块一起存放在存储桶中,因此不再需要同步一个独立的数据库。Sprite 内部的守护进程 `sbd serve` 从自己的本地缓存响应内核的块读写请求,在后台将脏块上传至对象存储,并在需要保证字节已经送出本机时,按需执行刷写。 繁重的工作依然由 ext4 完成。我们只是把它下面的一切大幅简化了:现在只有一个守护进程和一个存储桶,而以前则有一个文件系统、一个数据库和一个磁盘镜像,需要让它们保持一致。 ## https://fly.io/blog/sprites-block-device/#dumber-checkpoints-are-better-checkpoints越笨的检查点,越是好检查点 对于任何旨在为智能体提供安全活动空间的基础设施,检查点都至关重要。在 JuiceFS 下,检查点是磁盘镜像的写时复制克隆,记录在 `checkpoints\.db` 中。在 SBD 下,检查点是整个卷的块快照,带有版本号,记录在 SBD 自己的检查点目录中。啊……单一事实来源带来的安心,真是无与伦比。 恢复也同样朴素。每个检查点都知道自己属于卷的哪个版本,因此恢复时只需卸载文件系统,将卷原地回退,再重新挂载。开销大约相当于重启一次。 当然,事情仍然可能出错。区别在于,现在 Sprite 会停下来并报告问题,而不是继续使用一块它无法保证可靠的磁盘。SBD 会对从对象存储读回的每个块进行检查,确认它与写入时一致。如果 SBD 自身崩溃,整个 Sprite 都会重启,因为只有彻底重启,才能重新获得一块值得信赖的磁盘。 ## https://fly.io/blog/sprites-block-device/#moms-spaghetti妈妈做的意大利面 持续高强度的自用测试、近乎虐待的测试框架,以及几位先行设计合作伙伴的反馈,都表明 SBD 的可靠性比我们自制的意大利面文件系统高出几个数量级。 因此,从现在起,SBD 将成为 Sprites 的文件系统。ext4 运行在块设备上,块设备构建在存储桶上。负责跟踪你文件的,只有每台 Linux 机器都已经在运行的文件系统代码。 *如果你有在 10/6 之前创建的 Sprite,可以按照这些说明 (https://docs.fly.io/sprites/sprite-maintenance#storage-upgrades) 进行迁移。* 上一篇 ↓你的智能体会说 MCP。给它一台计算机。 (https://fly.io/blog/sprites-mcp/)

相似文章

@yifanxu_ephai: 在LayerFS跑一个RUST编写的DB Engine任务: 将 2.50 TB 的快照数据、30 个分支,压缩到仅 4.66 GB, 光是 main 分支就有 8.23 GB。 先吹个牛逼,我怕明天发布翻车,就吹不了了。 半年前我和VC…

X AI KOLs Timeline

Ephemeral AI Lab 宣布 LayerFS 1.0.0 正式发布,这是一款为 AI 智能体设计的文件系统基座,其 Rust 编写的 DB Engine 将 2.50 TB 快照数据和 30 个分支压缩到仅 4.66 GB,主打短暂工作区与共享持久历史。