最近把一台淘汰下来的戴尔 R610 折腾成了一个对外的边缘接入节点,上面跑的系统就是这个 cloudflare-os。先说明一下,这名字不是 Cloudflare 官方出品,而是一个社区项目:它参照 Cloudflare 边缘节点那套工程思路,做一个极简、只读、可回滚的定制 Linux 发行版,专门用于 CDN 缓存、反向代理、静态托管一类负载。我把它接在老家一条 50M 上行的链路后面,跑了三个月,中间只重启过一次,还是我自己换硬盘拔了电。这个系统给我的直观感受是:它不像一台服务器,更像一台路由器——镜像体积很小,系统分区只读,升级靠整体替换 rootfs,出问题直接回滚到上一版,完全不用伺候包管理器那一大堆依赖关系。
整个项目前后断断续续看了两三周代码,又亲手构建过两遍镜像,把内核裁剪、initramfs 引导、不可变文件系统这些环节都踩过一遍。这篇文章不会对着 README 照本宣科,我想按“为什么这么设计、具体怎么构建、出了故障怎么排查”这个顺序,把 cloudflare-os 从头到脚拆开讲。如果你正在做边缘节点、家庭数据中心,或者只是想搞明白一个精简 Linux 发行版到底是怎么诞生的,这篇内容应该能给你省不少弯路。
1. 项目定位:cloudflare-os 到底在解决什么问题
1.1 通用发行版在边缘场景里的那些别扭
过去我在边缘节点上装系统,第一反应都是拿 Ubuntu Server 或者 Debian 直接灌进去。优点是省事,软件源里什么都有,但真正跑起来才发现问题不少。
首当其冲的是体积和资源占用。一个精简安装的 Ubuntu Server,系统盘也要占 3-4GB,装完以后内存里常驻一堆不明所以的服务进程,unattended-upgrades、snapd、multipathd 全都在后台待命。对一台 8GB 内存的边缘节点来说,这些开销不算致命,但如果节点只有 1GB 内存,或者是一台跑在 ARM 开发板上的小盒子,那系统本身吃掉的内存就有点过分了。cloudflare-os 的思路是完全反着来:没有包管理器,没有自动更新,没有图形环境,整个 rootfs 压缩完不到 200MB,常驻内存可以控制在 400MB 以内。剩下的资源全部留给 CDN 缓存和应用进程。
还有一个很隐蔽的问题是内核配置。通用发行版的内核为了兼容海量硬件,编译进了大量驱动模块,很多默认开启的内核特性在边缘场景里不仅用不到,还会带来安全面和性能损耗。最典型的是无线网卡、蓝牙、声音子系统,以及一大堆你没见过的文件系统驱动。边缘服务器需要的是稳定的网卡驱动、完善的 TCP/IP 栈、高效的 epoll,而不是这些周边功能。cloudflare-os 把内核裁剪这件事提到了非常靠前的位置,默认编译只保留虚拟化、常用 Intel/Realtek 千兆网卡和 VirtIO 驱动,其他全部关掉。
1.2 设计目标:做成一台“网络设备”而不是一台“服务器”
通用云服务器给人的感觉是一个通用计算平台,你可以往里面装任何东西;cloudflare-os 的定位刚好相反,它希望你把它当成一台网络设备来用。什么叫网络设备?就是系统本身对使用者不可变,配置独立于系统之外,升级替代修复,重启替代重新配置。
具体到工程目标上,这个项目有这么几条硬指标:
- 镜像体积限制在 200MB 左右,支持直接从 U 盘、PXE 或 iSCSI 引导启动。
- 系统根分区只读,连 root 都没有权限往系统目录里写文件。
- 所有可变配置集中放在 /data 分区,备份、迁移、重置都只针对这一个目录。
- 升级机制采用双分区 A/B 切换,当前系统跑在 A 区,新版本下载到 B 区,下次启动自动切换。
- 默认只开放 80、443 和 SSH 管理端口,其他端口一律关闭。
这些目标组合起来,就是“不可变基础设施”在单机操作系统层面的落地。好处非常直接:系统永远不会被漂移状态弄脏;如果配置写坏了,把 /data 里对应的目录删掉重来就行;如果升级坏了,启动菜单里选旧内核和旧 rootfs 立刻回滚。做边缘节点的运维,最怕的就是凌晨 3 点发现配置乱掉,你很难在出问题那一刻还记得做过什么改动。只读系统把这类故障直接消灭了。
1.3 适合谁用:从实际场景看用户画像
我不建议所有人都去折腾这个项目。它面向的是非常具体的场景,我用下来感觉比较适合这几类人:
第一类是家里或者小机房有多台设备的人,想统一入口做反向代理、静态文件托管、CDN 缓存。cloudflare-os 的定位和一台 Nginx 服务器没区别,但它把系统维护成本压到了极低,跑起来不用管。第二类是正在折腾虚拟化集群的,控制节点、存储网关这类基础设施节点适合用这种只读系统。第三类是纯粹想研究 Linux 启动流程和发行版构建的玩家,因为它把内核编译、initramfs、rootfs、引导加载器这些过程全部暴露出来了,用来做实验教材非常合适。
它不适合的场景也很明确:如果你想在系统里装 Docker、跑数据库、当日常开发机,那就别碰它。这个系统去掉的东西太多了,硬要用只会折磨自己。把它当作一台路由器或者一组应用集群的前置网关,才是在正确的道路上。
2. 全局设计:为什么这么拆,为什么这么选
2.1 为什么不直接魔改现成发行版
这是我拿到项目后的第一个问题。既然已经有 Alpine、Arch 这类轻量发行版,为什么还要自己搭一套?实际操作之后我理解了:现成发行版的“轻”和边缘节点需要的“窄”不是一个概念。
Alpine 确实小,但它是为通用场景做的。它默认的 OpenRC 启动机制、musl libc 生态、软件包管理方式,都服务于通用的“能装各种软件”这一目标。cloudflare-os 需要的是一个完整可裁剪的构建链路,从内核源码开始控制,到 initramfs 的每一个命令,再到系统服务的守护方式。它本质上是一个自定义 Linux 发行版的框架,而不是一个预装系统的安装包。换句话说,项目真正写的不是“又一个发行版”,而是一套把发行版压缩到极限的工程方法:给定的硬件是什么规格,就只保留能支撑这台硬件跑网络服务的最小集合。
这个选择在运维层面也有明显收益。因为一切都是由构建脚本生成的,架构师可以在 CI 里一键产出镜像;要做 MITM 部署的时候,只需要换构建参数,不用去一台台改已安装的系统。不得不说,这套思路跟 Cloudflare 内部的边缘操作系统理念很像:每台服务器上的软件栈最初就是由几行脚本刻出来的,而不是人工在机器上“酿”出来的。
2.2 文件系统与分区布局
分区布局是 cloudflare-os 最值得讲清楚的部分,它几乎决定了这个系统所有的维护体验。我手头的部署版本是这样的:
/dev/sda1 EFI 系统分区(或 BIOS 引导分区),大小 512MB /dev/sda2 rootfs 的 SquashFS 只读镜像 /dev/sda3 数据分区,ext4,用于保存 /etc、/data、日志、缓存 /dev/sda4 备用 rootfs(B 分区),用于 A/B 升级实际启动时,根文件系统不是直接挂载 SquashFS,而是用 OverlayFS 把 SquashFS 作为 lowerdir,把数据分区里的 upper 目录作为 upperdir,合成一个可读写的根。从用户视角看,系统好像可以写文件,但这些写入只会落到数据分区;系统分区本身还是那个只读的 SquashFS 镜像。这种分层有一个非常大的好处:如果需要恢复出厂设置,清空数据分区上对应的 upper 目录即可,系统镜像根本不会坏。
数据分区里保存的关键目录是:
- /data/overlay/upper:OverlayFS 的写入层,对应系统根目录里其他人写入的内容。
- /data/overlay/work:OverlayFS 正常工作目录。
- /data/etc:我自己用脚本在每次启动时同步到 /etc 里的自定义配置。
- /data/tmp:临时文件与运行时生成的中间文件。
- /data/cache:Nginx 的缓存目录,落盘在这里。
这个布局让我做备份的时候极其舒服。要备份整个节点状态,只需要把 /data 打包走;要用一台新机器迁移,也只需要把 /data 复制过去,然后插上相同版本的安装介质启动即可。系统分区和数据分区完全解耦,是这套设计最核心的收益。
2.3 进程守护:不用 systemd 带来的连锁好处
很多人第一反应是,没有 systemd 的 Linux 还能用吗?实际上对于这种固定角色的系统,systemd 反而是一种负担。cloudflare-os 里负责进程托管的是 s6,加上一小段自己写的守护脚本。
选择 s6 而不是 systemd,有一个非常实际的理由:依赖关系极简单。systemd 的单元管理、socket 激活、日志系统全部不需要,只要“拉起进程、崩溃后重启、按依赖顺序启动”这三件事。s6 在这几个方面的开销接近零,而且它支持基于目录的配置方式,整个服务定义用文本文件就能描述清楚,非常适合做成不可变镜像的一部分。
这个选择直接影响了系统的启动速度。实测从 BIOS 自检结束到 Nginx 开始监听端口,整个系统启动时间在两秒左右。如果开了 PXE 启动,延迟主要体现在网络加载镜像的过程中,真正的内核启动和 init 过程不会超过三秒。这在批量上线边缘节点的时候非常重要,因为几十台机器同时重启,越快进入工作状态,对业务的影响窗口就越小。
2.4 对外接口:为什么只留命令行和简单 Web 面板
项目里并没有做一个复杂的图形管理界面,只提供两个入口:SSH 命令行和 80/443 上挂的一个只读状态页。我一开始也觉得这有点太简陋,可实际用下来反而觉得这是优点。
边缘节点上的管理界面,主要用来做状态查看和快速排障,而不是做复杂变更。命令行足够处理 90% 的问题;状态页显示 CPU、内存、缓存命中率、连接数这些核心指标就够了。不做成完整后台的原因也很直接,完整的 Web 管理面板意味着要在系统里塞进一套 Web 应用框架、数据库、前后端构建产物,体积和复杂度会成倍增加,这会破坏整个项目“越小越好”的初衷。
3. 核心实现拆解:内核、initramfs 与只读根文件系统
3.1 内核裁剪:从标准内核瘦身到 200MB 以下
内核裁剪是这个项目最出彩的部分,也是我花时间最多的地方。cloudflare-os 默认基于 Linux 6.6 LTS 内核做裁剪,源码直接来自 kernel.org。构建时用一个配置文件严格控制选项。
我先说结果:最终生成的 bzImage 约 12MB,压缩后的内核模块目录加起来不到 80MB,整个 rootfs 打包成 SquashFS 后约 180MB。
裁剪遵循三条原则:只留 x86_64 架构,删掉所有不相关架构;只保留边缘场景需要的驱动,能编译成模块的绝不内建;去掉所有装饰性功能,比如各种 LED 灯驱动、无线电、消费级多媒体框架。实际操作中用到的主要配置项大概是这样:
# 核心网络功能保持内建 CONFIG_PACKET=y CONFIG_UNIX=y CONFIG_INET=y CONFIG_IPV6=y CONFIG_NETFILTER=y CONFIG_BRIDGE=y # 必须的文件系统驱动,全部内建 CONFIG_SQUASHFS=y CONFIG_OVERLAY_FS=y CONFIG_EXT4_FS=y CONFIG_PROC_FS=y CONFIG_SYSFS=y CONFIG_TMPFS=y CONFIG_DEVTMPFS=y # 网卡驱动按模块编译,节省体积 CONFIG_E1000E=m CONFIG_IGB=m CONFIG_R8169=m CONFIG_VIRTIO_NET=m CONFIG_VMXNET3=m # 关闭绝大多数不必要功能 CONFIG_WIRELESS=n CONFIG_BT=n CONFIG_SOUND=n CONFIG_HID=n CONFIG_INPUT=y这里有个经验值得分享:SquashFS 和 OverlayFS 两个驱动一定要编进内核而不是编成模块。因为启动早期阶段 initramfs 需要马上挂载根文件系统,如果它们是外部模块,initramfs 里就要额外打包对应的 .ko 文件,任何依赖顺序问题都会导致启动失败。放到内核里,虽然镜像体积会变大几十 KB,但换来的是极高的启动稳定性。
3.2 initramfs 里的启动逻辑
initramfs 是很多人都没太在意的环节,但 cloudflare-os 引导成功与否全看这里。initramfs 本质是一个小型的临时根文件系统,内核启动后先把控制权交给里面的 init 脚本,由脚本负责挂载真正的根分区,然后切根系统。
这个项目里的 init 脚本写得非常简洁,核心流程序列如下:
#!/bin/sh # 提前挂载基础文件系统 mount -t proc proc /proc mount -t sysfs sysfs /sys mount -t devtmpfs devtmpfs /dev mkdir -p /run /sysroot /mnt/squashfs mount -t tmpfs tmpfs /run # 触发 mdev 设备节点生成,用于加载块设备 mdev -s # 等待根设备出现 ROOT_DEV="/dev/sda2" for i in $(seq 1 30); do [ -b "$ROOT_DEV" ] && break sleep 0.1 done [ -b "$ROOT_DEV" ] || panic "root device $ROOT_DEV not found" # 挂载只读 rootfs mount -t squashfs -o loop "$ROOT_DEV" /mnt/squashfs # 挂载数据分区,并构建 OverlayFS 的合并视图 mount /dev/sda3 /mnt/data mkdir -p /mnt/data/overlay/upper /mnt/data/overlay/work mount -t overlay overlay \ -o lowerdir=/mnt/squashfs,upperdir=/mnt/data/overlay/upper,workdir=/mnt/data/overlay/work \ /sysroot # 准备系统根目录的关键挂载点 mount --bind /dev /sysroot/dev mount --bind /proc /sysroot/proc mount --bind /sys /sysroot/sys mount --bind /run /sysroot/run # 切根 exec switch_root /sysroot /sbin/init这里有个坑:OverlayFS 的 upperdir 和 workdir 必须在同一个文件系统上。因为我这里统一放在 /mnt/data 下面,所以 mount overlay 不会报错。如果你自己改成分区,一个 upperdir 在 ext4、workdir 在 xfs 上,内核会直接拒绝挂载,错误信息还不怎么直白,排查起来会比较痛苦。
3.3 只读根文件系统与可写数据分区的同步技巧
系统虽然是只读的,但很多软件运行时还是要写配置、写 PID 文件。这就要依靠启动脚本在每次开机时把 /data/etc 里的内容同步到 /etc 对应位置。
具体实现不复杂,把用户配置目录挂载覆盖系统默认配置即可:
# /etc 系统性只读,但允许将 /data/etc 下同名文件覆盖过去 for f in /data/etc/*.conf /data/etc/nginx /data/etc/ssh; do if [ -e "$f" ]; then cp -a "$f" /etc/ fi done这样带来的好处是,系统管理员对系统的“改动”全部收敛在一个目录里。我上个月想给 Nginx 加一个站点,只需要在 /data/etc/nginx/conf.d 下新增一个配置文件,然后重启 Nginx 服务即可。系统分区里原先的默认配置依然还在,不会因为误操作把默认配置覆盖掉。
从维护角度看,这种设计相当于把“系统目录”剥离了“可变化”的属性,只负责提供代码和默认值;真正的状态全部放在数据分区。这也是它能支持整体升级的基础,因为升级新 rootfs 只需要替换 squashfs 镜像,系统管理员自定义的配置全部还在 /data 里。
3.4 网络栈参数与边缘服务的联动配置
cloudflare-os 的目标是跑网络服务,网络栈参数调优自然不可少。我自己在 /etc/sysctl.conf 里加了一批配置,不能说对所有人适用,但确实是边缘缓存节点普遍需要的基础调优:
# 高并发接受队列 net.core.somaxconn = 65535 net.core.netdev_max_backlog = 65536 # TCP 快速回收与复用,适合短连接密集场景 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 15 # 开启窗口缩放,提升大流量传输效率 net.ipv4.tcp_window_scaling = 1 # 本地临时端口范围放宽 net.ipv4.ip_local_port_range = 1024 65535 # 开启 BBR 拥塞控制,对长肥网络尤其有效 net.ipv4.tcp_congestion_control = bbr # 降低 SWAP 使用倾向,保护闪存寿命 vm.swappiness = 10配合 Nginx 配置时,注意把 worker_processes 设为 CPU 核数、将 worker_connections 提到 2048 以上;同时打开 keepalive 和 gzip,能明显降低边缘节点的资源占用。边缘节点的命中率受文件系统缓存影响很大,因此 On-disk cache 目录放在 /data/cache 上,而文件系统页缓存则依靠系统的默认行为即可。
4. 实操实录:从零构建一套可部署镜像
4.1 构建环境准备
构建 cloudflare-os 镜像的工作量其实不大,问题主要在于“组件版本是否配套”。我推荐在一台 4 核 8GB 内存以上的 Ubuntu 22.04 虚拟机里做构建,尽量避开容器环境,因为内核编译阶段需要大量文件描述符和进程并发,容器里的限制有时候会带来莫名其妙的问题。
宿主机需要装下的工具链大概有这些:
apt install -y build-essential bc flex bison libssl-dev libelf-dev \ xz-utils cpio squashfs-tools grub-efi-amd64-bin dosfstools mtools然后建立工作目录:
export COS_ROOT=/data/cos-builder mkdir -p $COS_ROOT/{rootfs,boot,iso,modules} cd $COS_ROOT从 kernel.org 拉取内核源码,我用的版本是 6.6.45 LTS:
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.45.tar.xz tar -xf linux-6.6.45.tar.xz -C $COS_ROOT mv $COS_ROOT/linux-6.6.45 $COS_ROOT/kernel从 Alpine 官方拉取 minirootfs 作为 rootfs 基础。这一步不需要 chroot 到 Alpine,只需要把它的文件系统目录结构复制出来,腾出空间放自己需要的软件。minirootfs 的下载地址在 Alpine 发布目录里,选择 x86_64 版本即可。
4.2 内核源码与模块构建
这一步是整个过程中最容易出错的地方。首先进入内核源码目录生成基础配置:
cd $COS_ROOT/kernel make defconfig在 defconfig 基础上,我用 scripts/config 命令直接修改关键选项:
scripts/config --disable WIRELESS scripts/config --disable BT scripts/config --disable SOUND scripts/config --disable INPUT_MOUSE scripts/config --disable INPUT_KEYBOARD scripts/config --enable SQUASHFS scripts/config --enable OVERLAY_FS scripts/config --enable DEVTMPFS scripts/config --disable MODVERSIONS然后保存为新配置并开始编译:
make olddefconfig make -j$(nproc) make modules_install INSTALL_MOD_PATH=$COS_ROOT/rootfs cp arch/x86/boot/bzImage $COS_ROOT/boot/vmlinuz编译完成以后,把内核模块复制到 rootfs 的对应目录下,接着通过 depmod 生成依赖关系:
mkdir -p $COS_ROOT/rootfs/lib/modules/$(make -s kernelrelease) cp -a /lib/modules/$(make -s kernelrelease) $COS_ROOT/rootfs/lib/modules/ depmod -b $COS_ROOT/rootfs $(make -s kernelrelease)4.3 根文件系统制作与软件安装
Alpine minirootfs 里已经带了 BusyBox、musl 库和基础目录结构。我在此基础上加装了 OpenResty(Nginx 的增强版)和 dropbear(轻量 SSH 服务)。为了方便从主机管理,还额外放了一个 curl 和 vim 的静态编译版本。
这里有一个小技巧:可以用 Alpine 的 chroot 软包管理来安装软件,而不必在最终 rootfs 里保留 apk。具体做法:
chroot $COS_ROOT/rootfs apk add --no-cache openresty dropbear curl tzdata装完软件之后,清理掉 apk 缓存和多余 doc 文件,然后就能把 rootfs 打包成 SquashFS 镜像:
mksquashfs $COS_ROOT/rootfs $COS_ROOT/iso/rootfs.squashfs \ -comp xz -noappend -processors 4实测压缩后的都会接近 180MB,非常紧凑。SquashFS 压缩等级选择 xz 是为了追求最小体积;如果更看重启动速度,可以选择 gzip,解压开销会小一些。边缘节点如果使用闪存盘,我建议 xz,省空间就是省寿命。
4.4 生成 initramfs 与 ISO
initramfs 实际上就是一个 cpio 归档。先把之前写好的 init 脚本放到一个临时目录里,再补上 BusyBox 和一些必要的工具,然后打包:
mkdir -p $COS_ROOT/initramfs/{bin,sbin,dev,proc,sys,mnt,run} cp $COS_ROOT/initramfs-scripts/init $COS_ROOT/initramfs/init cp /bin/busybox $COS_ROOT/initramfs/bin/busybox ln -s busybox $COS_ROOT/initramfs/bin/sh ln -s busybox $COS_ROOT/initramfs/bin/mount ln -s busybox $COS_ROOT/initramfs/bin/mkdir ln -s busybox $COS_ROOT/initramfs/bin/mknod ln -s busybox $COS_ROOT/initramfs/bin/mdev ln -s busybox $COS_ROOT/initramfs/sbin/switch_root cd $COS_ROOT/initramfs find . | cpio -o -H newc > $COS_ROOT/initramfs.cpio xz -9 $COS_ROOT/initramfs.cpio mv $COS_ROOT/initramfs.cpio.xz $COS_ROOT/boot/initramfs.img因为 6.6 内核默认启用了 lz4 或 xz 格式的 initramfs 压缩支持,所以直接把 xz 压缩后的 cpio 放到 /boot 下,内核就能自动解析,不需要额外处理。
最后生成 ISO。GRUB 需要写一个极简的 grub.cfg:
menuentry "cloudflare-os" { linux /boot/vmlinuz root=/dev/sda2 rootfstype=squashfs quiet initrd /boot/initramfs.img }然后用 grub-mkrescue 把内核、initramfs、rootfs.squashfs 和 grub.cfg 打进一个可启动的 ISO:
grub-mkrescue -o $COS_ROOT/cloudflare-os.iso $COS_ROOT/iso这就是最终交付的安装介质了。
4.5 在物理机上跑通完整启动
ISO 做出来以后,先不要直接上物理机,建议先用 QEMU 做一次完整引导测试:
qemu-system-x86_64 -m 1024 -boot d -cdrom $COS_ROOT/cloudflare-os.iso \ -drive file=/tmp/data.img,format=raw \ -net nic,model=virtio -net user我碰到的第一个问题是 initramfs 里 mdev 没能自动识别 virtio-blk 设备,导致 /dev/vda 不出现,根设备挂载失败。解决方案是在 init 脚本里加上mdev -s,并且提前把参数传给内核来指定根设备,避免依赖自动探测。
等 QEMU 里能看到 Nginx 欢迎页,再把 ISO 写到 U 盘,插到真实服务器上升级引导即可。首次安装时,用 fdisk 手动创建好四分区布局,把 ISO 里的 rootfs.squashfs 写到 /dev/sda2,把空数据分区格式化到 /dev/sda3,设置好 GRUB 引导,然后重启就能进入系统。
5. 常见问题与排查技巧实录
5.1 rootfs 挂载失败,卡在 “VFS: Cannot open root device”
这是我遇到最频繁的启动问题。检查方向有这么几个:首先确认 root 参数是否正确指向了 SquashFS 所在分区;其次确认内核是否内建了 SquashFS,如果它是模块,initramfs 里就得放 squashfs.ko 并在挂载前手动加载;最后看 initramfs 里 mdev 是否执行成功,块设备节点有没有生成。
经验是推荐把根设备写死,比如root=/dev/sda2,而不是用 UUID。因为早期阶段内核和 initramfs 还不一定识别到完整的 UUID 信息,有的文件系统在挂载前也不会读取 superblock;直接用 /dev/sdXn 简单可靠,不会因为多插了一块硬盘导致分区顺序混乱。
5.2 网卡驱动缺失,机器起来但网络不通
出现这种情况,多半是内核模块路径不对或者 depmod 依赖没生成。SSH 进不去系统时,可以用串口终端查看。排查时先确认内核模块文件确实存在于 rootfs/lib/modules/$(uname -r)/ 下,再用 depmod -a 重新生成依赖。如果是网卡驱动被编成了模块而不是内建,检查 initramfs 是否包含了对应 .ko。
最稳妥的办法,是把我最常用的几个网卡驱动编译成模块后,统统塞进 initramfs,并在 init 脚本里加入 modprobe 命令。这样即使 rootfs 损坏,网络也能先通了再去恢复系统。
5.3 只读文件系统上改配置的坑
初上手的人最容易犯的错误,是想直接改 /etc/nginx/nginx.conf,改完发现系统分区只读,根本写不进去。正确做法是把配置放到 /data/etc/nginx/nginx.conf,并在系统初始化脚本里做同步。如果你只是临时调试,可以直接mount -o remount,rw /,但不要养成习惯,否则就失去了不可变系统的意义。
5.4 升级失败,如何安全回滚
A/B 双分区的好处就在这里。假设当前系统跑在 /dev/sda2 上的旧 rootfs,新版本已经被安装程序写入 /dev/sda4,但启动后出现异常。处理步骤:在 GRUB 菜单里选择旧版内核和旧 rootfs 启动,系统恢复原状。如果有异常,先把检测到新 rootfs 的标记位清掉,避免下次启动继续尝试新版本。
这个设计大大降低了升级风险。传统发行版升级到一半如果断电,轻则少几个依赖,重则引导起不来;cloudflare-os 完全没有这种担忧,因为它从来不原生修改正在运行的系统文件,新版本的写入和旧版本的运行天然隔离。
6. 使用体会与后续扩展方向
三个月跑下来,我的体会是这种“只读系统+集中配置+双分区回滚”的思路,确实比通用发行版更适合边缘节点。以前我最大的焦虑是节点上某个包依赖坏掉,现在基本不存在这个问题。每次变更都显式发生,故障可定位性提高了一个量级。要说不足,可能就是初次上手成本略高——如果你对 initramfs 和内核编译不熟,会有一段比较陡的学习曲线。
后续我自己打算继续扩展两个方向。第一个是把 PXE 引导链路做完善,让新机器插上网线就能自动装配镜像,彻底告别 U 盘安装。第二个是在 /data 分区上做一套自动快照策略,这样即使配置写错,也能直接从几分钟前的快照恢复,而不只是回滚到出厂状态。Cloudflare 风格的边缘基础设施,本质上就是要把“出问题后快速恢复”这件事做成系统默认能力,而不是靠运维人员手工救场。
这个项目虽然改动了不少地方,但整体架构非常清晰,非常适合作为个人数据中心的操作系统底座。如果你也想折腾一套自己的边缘系统,建议先按上面的流程在虚拟机里完整跑通一遍,再把构建脚本逐步改成符合自己需求的版本。系统的复杂度在于细节,但也正因如此,它才是值得反复动手实验的好项目。