Frood:基于 Alpine Linux 的轻量级 Initramfs NAS 方案
速览
Frood 是一个创新的 NAS(网络附加存储)项目,其核心特点在于基于 Alpine Linux 发行版构建。它利用 Initramfs(初始内存文件系统)技术,实现了极致的轻量化和快速启动。这种设计使得系统资源占用极低,适合对性能和存储有特定需求的用户或边缘计算场景。
AI 深度解读
Frood:一个基于 Alpine initramfs 的 NAS 系统
背景
作者构建了一个名为 Frood 的个人网络附加存储(NAS)系统。这个系统的配置相当独特:它本质上是一个巨大的 initramfs(初始内存文件系统),其中包含了一个完整的 Alpine Linux 系统。作者认为这种架构非常令人愉悦,且不确定为何它没有变得更加普及。
传统上,运行从内存中的系统(即系统启动后主要在 RAM 中运行)有几个显著优势:速度快,并且能防止系统存储设备(通常是质量一般的 SD 卡)因频繁写入而磨损,因为好的驱动器通常被专门用于 ZFS 存储池。
然而,从内存运行系统面临一个核心挑战:如何持久化配置更改。Alpine Linux 提供的解决方案是“无盘模式”(diskless mode),即将所有自定义更改保存在一个覆盖文件(overlay file)中。系统启动后,会查找匹配 *.apkovl 的文件并应用它,然后从本地缓存安装缺失的 apk 包。
作者发现这种传统方式存在两个主要问题:
- 复杂性:生成和管理
apkovl的工具lbu(1)虽然不错,但涉及太多环节(查找文件、应用覆盖、挂载新文件系统、安装缺失包等)。在过去一年中,作者多次遇到启动失败的问题,要么是因为找不到文件系统,要么是因为包未安装。启动过程竟然依赖于包管理器,这增加了脆弱性。 - 缺乏声明式追踪:作者希望系统状态能在 Git 中进行版本控制。引用 Graham Christensen 在《Erase your darlings》中的观点:“我在每次启动时都会擦除我的系统。” 传统系统在根分区上积累状态(如
/etc和/var中的文件),这些状态往往由未文档化或顺序混乱的启动步骤组成(例如手动下载证书、触摸特定文件以修复隧道等)。这些“小修补”容易丢失,导致未来升级(如从 RHEL 7 到 RHEL 8)时重复历史错误。
虽然可以使用 Ansible 等工具解决,但这会导致多层配置(Ansible -> 部署 -> lbu 保存到 apkovl),依然复杂。其他声明式系统如 NixOS、gokrazy、buildroot 或 u-root 要么不符合作者口味,要么功能未就绪。作者喜欢 Alpine Linux 的简洁、轻量和无 GNU 特性,但不喜欢其初始化和持久化机制。因此,作者决定直接利用 initramfs 本身来承载整个系统。
核心内容
工作原理
Linux 启动时期望加载一个 initramfs 镜像,这通常是一个包含启动初期所需文件的简单 cpio 归档。通常,initramfs 的任务是加载足够的模块以挂载真正的根文件系统并切换过去(pivot root)。但作者指出,没有任何东西阻止我们将整个系统放入 initramfs 中。
构建 initramfs
构建过程基于 alpine-make-rootfs,这是一个约 500 行的脚本,原本用于构建容器镜像,但几乎涵盖了所需的所有功能。
- 准备环境:使用
alpine-make-rootfs脚本,禁用mkinitfs触发器,设置环境变量(如ALPINE_BRANCH=edge),并指定根目录骨架(FS_SKEL_DIR=root)。 - 生成根文件系统:脚本会复制
root目录下的文件,安装packages文件中指定的包,并在 chroot 环境中运行setup.sh脚本。 - 打包:将生成的根文件系统中的
boot目录移出,其余部分打包为 initramfs 归档(initramfs-lts)。
这一过程展示了 Alpine Linux 对此类自定义的高度适应性,几乎不需要任何黑客手段。
安装的软件包
除了常规服务器软件包外,几个关键包的选择体现了设计的精简:
- alpine-base:元数据包,安装
apk、busybox、openrc及少量配置文件。 - linux-lts:内核及其模块。作者考虑过精简模块以节省空间,但认为为了节省几百 MB 而进行大量黑客操作得不偿失。值得注意的是,这里没有使用
modloop,模块始终可用。 - linux-firmware-i915:Intel i915 显卡固件。必须安装至少一个提供
linux-firmware-any(包括linux-firmware-none)或linux-firmware的包,否则所有固件都会被安装。 - intel-ucode:微码更新。它在
/boot中安装文件,可作为 pre-initramfs 使用,比在大型系统上设置更容易。 - syslinux:引导加载程序。比 GRUB 简单得多,安装在文件系统分区上,并从该分区引导内核。这形成了一个闭环:只要引导正确的分区,除了我们的系统外,没有其他东西能加载。引导过程不需要发现或命名任何文件系统。
- openrc-init:初始化系统。虽然 Alpine 实际上使用
busybox的 init,但作者发现 OpenRC 更容易设置。注意,它不能与busybox的shutdown/reboot/poweroff命令配合使用,必须使用openrc-shutdown。 - agetty:如果计划连接键盘和屏幕,则需要此包。
设置脚本 (setup.sh)
setup.sh 脚本负责声明式地指定系统配置,主要包括:
- 链接 init:将
/sbin/openrc-init链接到/init。 - 配置运行级别:通过
rc-update添加各种服务到sysinit、boot、shutdown或default运行级别。例如,添加devfs、dmesg、networking、sshd、tailscale等服务。 - 设置密码:通过
chpasswd -e直接设置 root 密码哈希。
根骨架 (Root Skeleton)
根骨架是系统特定的,其优势在于只需在 root 目录下创建文件即可将其包含在镜像中。例如,若要在启动时运行某个脚本,只需将文件添加到 root/etc/local.d/。
作者还展示了一个具体的 ACPI 电源按钮处理脚本示例:
#!/bin/sh
openrc-shutdown -p now
该脚本位于 root/etc/acpi/PWRF/00000080,用于处理电源按钮事件。
系统优势
- 干净的启动:只要引导加载程序能找到内核和 initramfs,机器就能干净地启动。
- A/B 部署与回滚:只需选择不同的启动选项即可实现。
- 声明式定义:系统在构建 initramfs 的 Git 仓库中以声明式方式定义。
- 非复杂 DSL:配置不像某些复杂领域特定语言(DSL)。如果希望文件存在于
/etc/example.conf,只需将其放在root/etc/example.conf,其余由几百行可读脚本完成。 - 常规配置体验:配置方式与配置任何常规 Alpine 系统无异。
- 易于测试:可以使用一行
qemu命令测试下一次部署。 - 极少变动部分:系统架构极其简单。
关键要点
- 架构创新:Frood 将整个 Alpine Linux 系统打包进 initramfs,而非传统的根文件系统,实现了“每次启动即擦除”的声明式理念。
- 解决持久化痛点:摒弃了 Alpine 传统的
lbu和apkovl机制,避免了包管理器依赖导致的启动脆弱性。 - Git 驱动配置:所有系统状态和配置变更均在 Git 仓库中管理,实现了版本控制和可重现性,解决了传统 Linux 系统“状态漂移”和“黑盒操作”问题。
