这篇是阶段性总结,记录一下这一个多月拆解飞牛fnOS的全过程,以及踩过的坑、发现的好东西。

写在前面

事情的起因是导师给了一台飞牛NAS,让我先把它的底层摸清楚,为后面的工作做准备。

一开始我以为就是个"开箱体验"级别的任务——装个系统、点点Web界面、看看有什么功能。结果SSH进去之后越挖越深,一搞就是一个多月,出了十几万字的文档。

今天就把这30天的工作做个总结,也算给自己一个交代。


第一阶段:摸底——从哪开始挖?

先搞清楚这是什么系统

刚SSH上去的时候,第一步当然是认系统:

cat /etc/os-release
uname -a

结果发现是Debian 12改的,内核版本6.18,还带了个 .c1032-trim 的后缀——一看就是飞牛自己编译的内核。

这就有意思了:它不是拿个现成的Linux发行版改个皮肤,是连内核都自己重新编译过的。

定个计划,不然容易乱

系统是Debian、内核是自研的,那能挖的东西就多了。我列了个4天的学习计划:

  • 第一天:系统基础+启动流程+存储架构
  • 第二天:文件服务+用户权限
  • 第三天:Web后端+应用生态
  • 第四天:整体梳理

事实证明这个计划定早了——光存储架构这一块,就挖了整整两天。


第二阶段:存储层——最大的发现来了

意外发现 /fs

第一天挖到存储的时候,我在根目录下看到一个不认识的挂载点:/fs。

df -h /fs
Filesystem     Size  Used Avail Use% Mounted on
trimafs        1.9T   40G  1.8T   3% /fs

trimafs?这是什么文件系统?不是ext4,不是ZFS,不是Btrfs,我从来没听过。

继续挖:

lsmod | grep trim
trimafs2               77824  1

一个叫 trimafs2 的内核模块,77KB。再看 /proc/filesystems 里的注册信息,标了 nodev——说明它不是直接管磁盘的,是个联合文件系统。

它到底联合了什么?

往下看底层:

mount | grep vol
trim_6c71e3b3-... on /vol2 type zfs (rw,noatime,xattr,trimacl,casesensitive)
/dev/mapper/trim_d1c5b5d5-... on /vol3 type ext4 (rw,relatime)

结果让我有点惊讶:

  • /vol2 是 ZFS
  • /vol3 是 ext4

两个完全不同的文件系统,被 trimafs2 在内核层拼成了一个统一的 /fs。

为什么要这么做?

我后来想了很久这个问题。传统NAS的存储池是物理绑定的——你建一个RAID5,就是一个独立的池,想加盘就得往这个池里加,想合并两个池?不行,得拷数据。

但真实用户的情况是:硬盘是陆续买的。第一年一块盘,第二年加两块,第三年又加一块。不同时期的盘,文件系统可能不一样,RAID可能不一样,接口可能也不一样。

飞牛这个方案的思路就是:底层各自独立,上层统一视图。ZFS还是ZFS,ext4还是ext4,各自的快照、校验、容错都在。但用户看到的就是一个大目录,存文件不用关心落在哪块盘上。

加新盘?直接挂进来就行,不用数据迁移。

和开源方案比怎么样?

现在开源圈做联合文件系统,最常用的是 mergerfs。但 mergerfs 是用户态FUSE实现的——每次文件操作都要在用户态和内核态之间来回拷贝,性能开销不小。

飞牛这个是内核态自研的,直接在VFS层处理,没有额外的上下文切换,性能好很多。但代价是开发难度大——内核模块有bug,直接系统panic,数据都可能丢。

77KB的代码量,看起来不多,但内核级代码的复杂度,懂的都懂。

还有个配套的:trimacl

ZFS的挂载参数里还有个 trimacl——原生ZFS没这个参数,是飞牛打了补丁的。

这个补丁的作用是:统一不同文件系统的权限模型。ZFS和ext4的ACL本来就不一样,打了这个补丁之后,不管你底层是什么文件系统,权限行为都是统一的。

联合文件系统 + 统一权限补丁,这两个加起来,才构成了飞牛存储层的完整自研技术栈。


第三阶段:后端架构——30个微服务全走Unix Socket

挖完存储,开始看Web后端。

我先看Nginx配置,结果发现一个很特别的地方:所有后端服务都是Unix Socket,没有一个用TCP端口的。

upstream trim_cgi {
    server unix:/run/trim_cgi.socket;
}
upstream accountsrv {
    server unix:/var/run/trim.accountsrv.sock:;
}
# ... 一共30多个

账户服务、文件服务、快照服务、docker管理……每个都是独立的进程,独立的systemd服务,全部通过本地socket和Nginx通信。

为什么要这么设计?

我觉得好处很明显:

  1. 安全:所有服务都不暴露端口,外面只能访问Nginx那几个端口,攻击面小
  2. 解耦:每个服务独立重启,不影响其他服务
  3. 性能:本地socket比TCP快,没有网络栈开销
  4. 开发友好:每个服务可以用不同语言写,只要跑个HTTP server监听socket就行

另外还有个全局的安全访问码机制——用Nginx的 auth_request 做的,所有请求先过一次校验,再转发到后端。相当于全系统的二次验证。


第四阶段:文件服务——SMB里的四个自研VFS模块

再往下看文件服务,SMB的配置里也有惊喜:

vfs objects = trim_perms full_audit shadow_copy2 trim

四个自研的VFS模块:

  • trim_perms:权限控制
  • full_audit:操作审计
  • shadow_copy2:快照恢复
  • trim:飞牛自己的一套逻辑

相当于SMB这一层也不是原生Samba,是深度定制过的。FTP、WebDAV同理,都是改了的版本。


第五阶段:输出——从碎片到系统文档

挖了这么多东西,如果不整理,过两天就忘了。所以我边挖边写,最后出了一套系列文章:

  1. 系统基础与启动流程
  2. 存储架构深挖
  3. 系统目录结构与技术栈
  4. 文件服务与用户权限体系
  5. Web后端架构深度解析
  6. 系统识别方法论与二次开发指南

另外还单独写了一份给老师看的正式报告,把最核心的技术发现都整理进去了。


这一个多月的感受

1. 飞牛的技术实力比我想的强

一开始我以为就是个"Debian改了改"的NAS,结果挖下来发现,从内核模块到VFS补丁,从微服务架构到SMB定制,自研的东西比我想象的多得多。

官方宣传里几乎不提这些——你去看飞牛官网,全是AI相册、智能影视这些应用层功能。真正硬的东西,全藏在底下。

2. 拆解的方法比结果重要

这一个多月最大的收获,不是发现了几个技术点,而是学会了一套"怎么拆解一个陌生系统"的方法:

  • 先认系统,再摸架构
  • 从 /proc、lsmod 这种零成本的地方入手
  • 看到不认识的东西,顺藤摸瓜往下挖
  • 边挖边记,不然第二天就忘了

这套方法,后面拆奥瑞克的机器也能用。

3. 写博客是最好的学习方式

挖完只是第一步,能把它写成文章讲清楚,才是真的懂了。

写的过程中会发现很多"我以为我懂了,其实没懂"的地方——比如联合文件系统的设计动机,一开始我只觉得"挺酷",写文章的时候逼自己想"为什么要这么做",才想明白它解决的是渐进式扩容的痛点。


接下来做什么

  1. 继续写系列文章,目前第一篇(联合文件系统)已经写完了,后面还有trimacl、微服务架构这几篇
  2. 开始拆奥瑞克的机器,做对比分析
  3. 把拆解的内容整理成正式的技术文档,供项目使用

慢慢来,不着急。


本文记录了拆解飞牛fnOS的全过程,基于fnOS 1.2.0505版本实际分析,仅供技术学习交流。