服务器适合使用不可变 Linux 发行版吗?
了解服务器镜像模式如何通过重启回滚,以及 bootc、Fedora CoreOS、Flatcar 和 Talos 在 VPS 上带来的运维成本与限制。
不可变 Linux 发行版是什么
不可变 Linux 发行版将操作系统作为一个镜像发布,因此更新系统时会替换整个系统,而不是直接修改现有系统。运行中的主机不会在 /usr 下重写文件,也不会执行 apt upgrade。您需要构建或拉取新镜像。主机会将新镜像暂存到当前运行镜像旁边,并在下一次重启时切换活动镜像。旧镜像仍保留在磁盘上,因此如果更新出现问题,只需重启即可回滚。
“不可变”这个名称并不完全准确。root 仍然可以向磁盘写入内容。这类系统实际采取的做法是以只读方式挂载系统目录,并将这些目录的所有权交给镜像。持久化数据存放在 /var。特定于机器的配置存放在 /etc。/usr 下的所有内容都属于镜像。因此,运行相同镜像标签的两台服务器会包含完全相同的系统文件。
Red Hat 对这两种模式的称呼最清晰:软件包模式和镜像模式。软件包模式是一个正在运行的系统,以及用于修改该系统的软件包管理器。镜像模式是在其他位置执行构建步骤并生成制品,然后由服务器启动您指定的制品。以下内容都源于这一差异。
为什么只读系统在服务器上更重要
一台运行了两年的服务器,往往积累着无人记录的历史。一次匆忙操作留下的 make install。为安装某个软件包而添加的第三方软件源。一次故障期间编辑过、之后却没有恢复到配置管理中的配置文件。这就是配置漂移。正因如此,根据笔记重建“一模一样”的服务器后,系统行为却经常不同。笔记记录的是预期状态。磁盘保存的才是实际状态。
Image mode 移除了配置漂移累积的空间。运行时,/usr 以只读方式挂载,因此手动执行的安装要么直接失败,要么会被记录为一个层,并可通过一条命令列出。这样,两台机器之间的差异会直接显示出来,而不必像考古一样追查。常规 Linux 服务器维护清单通过规范化操作解决的是同一个问题,而这里则由文件系统负责处理。
回滚就是重启,这就是全部要点
该模型针对的故障,正是我们已经记录过的故障:内核更新后无法启动的 VPS。在软件包模式下,您需要通过服务商的救援控制台进行恢复。挂载磁盘、进入 chroot,然后手动删除内核软件包。之所以可行,是因为 bootloader 会保留旧内核,但只有内核以这种方式进行版本管理。同一事务中应用的 glibc 更新和 systemd 变更不会自动回退,也没有单个命令可以将它们一起回退。
在镜像模式下,整个系统就是回滚单元。在 bootc 主机上:
sudo bootc status
sudo bootc rollback
sudo systemctl rebootbootc rollback 会将 bootloader 的顺序切换回上一个启动项,也就是您一小时前运行的镜像,内核和用户空间一起回退。不会下载任何内容,也不会重新构建任何内容,因为旧镜像始终保留在磁盘上。如果您已启用下文所述的自动更新 timer,请先停止它:bootc 文档警告,执行自动升级的 agent 可能会撤销回滚。
Fedora CoreOS 也能执行相同操作,只是名称不同:
sudo systemctl stop zincati.service
sudo rpm-ostree rollback -r请先停止 Zincati。Zincati 是让 Fedora CoreOS 机器保持最新版本的 agent。它不会重新安装您刚刚回退的部署,但会持续检查更新,并升级到出现的任何更新版本。因此,如果机器在修复版本发布前应保持当前状态,请运行 sudo systemctl disable --now zincati.service。-r 会在回滚准备完成后重启。要防止您信任的部署被垃圾回收:
sudo ostree admin pin 0
rpm-ostree statusrpm-ostree status 按 bootloader 提供启动选项的顺序列出部署,用一个点标记正在运行的部署,并在您固定的部署上显示 Pinned: yes。
Talos 可从您的工作站通过一次 API 调用完成回滚:
talosctl rollback --nodes 10.20.30.40Flatcar 保留两个 /usr 分区,并在两者之间切换。每个槽位都会在分区表中记录优先级和尝试次数,因此无法成功启动的槽位会耗尽尝试次数,bootloader 随后选择另一个槽位。检查当前使用的槽位,以及它是否已标记为正常:
sudo cgpt show "$(rootdev -s /usr)" | grep successful=1健康运行的槽位会输出包含 tries=0 successful=1 的行。同一行上的优先级数字只表示 bootloader 更偏好的槽位,因此在运行完全正常的机器上也可能显示为 1 或 2。没有匹配的行,表示当前槽位从未被确认;机器在更新完成到首次正常启动之间就处于这种状态。
“安装软件包”由什么替代:bootc 和 Containerfile
bootc 是将这一模式标准化的工具。它将自身描述为:使用 OCI(开放容器计划)容器镜像,以事务方式就地更新操作系统。它也是 CNCF Sandbox 项目。您的服务器会变成一个 Containerfile。截至 2026 年 8 月,Fedora 基础镜像为 quay.io/fedora/fedora-bootc:44,CentOS Stream 基础镜像为 quay.io/centos-bootc/centos-bootc:stream10。
FROM quay.io/fedora/fedora-bootc:44
RUN dnf -y install nginx && dnf clean all
RUN systemctl enable nginx像构建和推送其他镜像一样构建并推送它:
sudo podman build -t registry.example.com/edge/web:2026-08-19 .
sudo podman push registry.example.com/edge/web:2026-08-19然后在服务器上执行:
sudo bootc upgrade --check
sudo bootc upgrade --applybootc upgrade 查询镜像源,并将新镜像加入下一次启动的队列。--check 报告是否有可用更新,但不执行任何更改。--apply 重启并启动到该镜像。bootc switch registry.example.com/edge/web:next 将计算机切换到其他镜像,同时保留 /etc 和 /var。这样无需重新安装系统,即可在不同镜像流之间迁移服务器。
如需无人值守更新,请启用项目提供的计时器:
sudo systemctl enable --now bootc-fetch-apply-updates.timer这相当于镜像模式下的 Ubuntu 无人值守升级,以及 Rocky 和 Alma 上的 dnf-automatic。区别在于最终应用的内容不同。软件包模式的计时器会应用软件仓库当晚提供的版本,因此每台计算机最终得到的软件包集合都会略有不同。镜像模式的计时器则应用一个您已经在其他位置启动过的制品。
这个 Containerfile 还带来两条构建规则。可写数据应放在 /var 下,因此如果软件坚持写入自身的安装目录,就需要在构建时添加符号链接或 systemd BindPaths= 行。/etc 会在更新时进行三方合并。这意味着,您从未修改过的文件会采用镜像中的新版本,而您在本地编辑过的文件会保留。
如果只需在运行中的服务器上使用某个工具进行一次调试:
sudo bootc usr-overlay
sudo dnf -y install strace该命令会在 /usr 上添加一个临时可写覆盖层,并在下次重启时丢弃。它用于检查问题,不用于修复问题。您无法通过这种方式更改内核,而且您安装的所有内容都会按设计在重启后消失。
Fedora CoreOS:一次预配,持续更新
Fedora CoreOS 没有交互式安装程序。您需要编写 Butane YAML 文件,将其转换为 Ignition JSON,然后在机器首次启动时提供给它:
podman run --interactive --rm quay.io/coreos/butane:release \
--pretty --strict < your_config.bu > transpiled_config.ignIgnition 只在首次启动时于 initramfs 中运行。这一点容易让使用过 cloud-init 的人误判。如果配置中没有 SSH key,机器启动后将无法登录,修复方法是从头开始重新预配。在将配置用于重要服务器之前,请先在一次性测试机器上验证。
从 live 环境安装到磁盘:
sudo coreos-installer install /dev/sda \
--ignition-url https://example.com/example.ign更新默认自动执行。您只能控制更新时间,不能禁用更新机制。将 TOML 文件放置到 /etc/zincati/config.d/55-updates-strategy.toml,以选择定期更新策略:
[updates]
strategy = "periodic"使用该策略时,每个 array-of-tables 条目对应一个维护窗口。每个窗口都以双层方括号中的 updates.periodic.window 作为标题,后面包含 3 个键:
days:星期名称列表,例如"Sat"和"Sun"。start_time:窗口开始时间,写作"22:30"。length_minutes:窗口持续时间,例如60。
这些时间均为 UTC。要完全停止更新,请运行 sudo systemctl disable --now zincati.service,并接受之后需要自行负责补丁计划。
Package layering 可作为备用方案:
sudo rpm-ostree install --allow-inactive vim
sudo systemctl reboot该操作会创建一个添加了指定软件包的新部署,但更改只有在重启后才会生效。代价会在之后产生。每个新的基础镜像都会重新应用分层软件包集合,因此如果某个软件包在更新当天已从仓库中消失,该更新就会失败。Fedora 官方文档建议,对于任何较大规模的功能,应优先使用容器;如果确实需要修改操作系统,则应使用 bootc 镜像。
Flatcar Container Linux:完全不提供软件包管理器
Flatcar 是 CoreOS Container Linux 的延续版本,也是通用型选项中限制最严格的一个。系统没有可备用的软件包管理器。您运行的所有内容都必须是容器。配置通过 Ignition 完成,与 Fedora CoreOS 相同。更新使用上文所述的两个 A/B /usr 分区,由 update_engine 执行,并由 locksmithd 决定何时重启。
update_engine_client -status
update_engine_client -check_for_updateUPDATE_STATUS_UPDATED_NEED_REBOOT 表示备用槽已包含新映像,现在只需等待重启。默认重启策略为 reboot,延迟时间为 5 分钟。因此,除非另行配置,单台生产 VPS 会按自身计划自动重启。在 /etc/flatcar/update.conf 中设置重启窗口:
REBOOT_STRATEGY=reboot
LOCKSMITHD_REBOOT_WINDOW_START="Thu 04:00"
LOCKSMITHD_REBOOT_WINDOW_LENGTH=1hREBOOT_STRATEGY=off 会将重启交由您手动执行。同一文件中的 SERVER=disabled 会完全停止更新检查。对于集群,将 REBOOT_STRATEGY=etcd-lock 与 locksmithctl set-max 4 结合使用,可以限制同时重启的节点数,避免更新导致整个节点集群同时中断。
Talos Linux:无 shell、无 SSH、无控制台登录
Talos 是四者中范围最窄、用途最明确的系统。它用于运行 Kubernetes 节点。系统没有 SSH daemon、登录 shell,也不提供控制台登录。物理控制台显示的是用于检查机器状态的仪表板,而不是登录提示符。所有操作都由工作站使用 talosctl 调用 gRPC API 完成,目标是存储在 git 中的机器配置。
talosctl upgrade --nodes 10.20.30.40 \
--image factory.talos.dev/metal-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.14.2从 Talos 1.14 开始,不再发布 ghcr.io/siderolabs/installer 镜像,安装器镜像改由 Image Factory 提供。路径中的长 ID 是未进行任何自定义的机器所使用的空 schematic。请使用安装节点时采用的 schematic,以便一并保留系统扩展,并将标签替换为要升级到的版本。升级采用 A-B 方案,会保留先前的内核和 OS 镜像。因此,如果新版本无法启动,Talos 会自动回滚,无需人工干预。由于没有登录 shell,调试方式也不同:应使用 talosctl logs 和 talosctl dmesg,而不是在节点上使用 journalctl。如果需要在节点上使用工具,talosctl debug 会从你提供的镜像启动一个特权容器,并进入该容器的 shell;退出后容器会被删除。
如果工作负载不是 Kubernetes,Talos 就不是正确选择。如果是 Kubernetes,Talos 可以消除一整类事件,因为“有人登录节点并修改了某些内容”根本没有发生的条件。
VPS 租户实际放弃的内容
在运行中的系统上临时安装软件。 这是最重要的一点。发生故障时,凌晨 2 点无法使用 sudo apt install htop。在 bootc 中,您会获得一个重启后即消失的临时覆盖层。在 Fedora CoreOS 中,您会获得一个需要重启才能生效的分层部署。在 Flatcar 和 Talos 中,您什么也得不到。
之前没有的构建流水线。 添加软件包意味着编辑 Containerfile、构建镜像、将镜像推送到镜像仓库,然后滚动更新服务器。流水线已经存在时,这些操作成本很低。没有流水线时,搭建它需要实际投入,而且服务器必须能够访问镜像仓库;这意味着您还需要运行另一项服务,或支付另一笔费用。
内核模块。 内核来自镜像,因此针对当前运行内核编译的模块无法保留到下一次更新。树外模块和 DKMS(动态内核模块支持)软件包必须针对该镜像的内核构建,并内置到镜像中。凡是需要基础镜像未提供的模块,都会变成构建问题,而不是安装问题。
厂商和服务提供商代理。 监控和备份代理通常以 .deb 或 .rpm 的形式发布,并附带安装脚本,将文件写入 /usr 并启用一个单元。在只读系统上,该脚本会失败。一些厂商会发布容器,或说明如何在镜像模式下安装。许多厂商不会这样做。请在决定采用前确认这一点,因为无法监控的服务器群比配置逐渐偏离的服务器群更糟糕。
镜像本身。 几乎没有 VPS 控制面板会在 Ubuntu 和 Debian 旁边列出 Fedora CoreOS、Flatcar 或 Talos。您需要自行提供磁盘,下一节将介绍这一点。
将其中一个系统部署到租用的 VPS
先向服务提供商确认两点:您拥有带外控制台访问权限,即 VNC 或串行控制台;并且可以启动救援系统。没有控制台时,无法恢复启动的机器只能提交支持工单,而不是在五分钟内修复。
如果服务提供商接受自定义镜像,请上传供应商提供的 raw 或 qcow2 镜像。但仅上传镜像并不能提供可登录的机器。Fedora CoreOS 的 core 用户没有默认密码或 SSH 密钥,因此仍需提供包含您的密钥的 Ignition 配置;如果使用 Afterburn 支持的云平台,也可以通过服务提供商自己的 SSH 密钥功能设置密钥。否则,您需要从救援系统自行写入磁盘。Flatcar 为此提供了一个自包含脚本,可在任意 Linux 系统中运行:
curl -fsSLO https://raw.githubusercontent.com/flatcar/init/flatcar-master/bin/flatcar-install
chmod +x flatcar-install
sudo ./flatcar-install -d /dev/sda -i ignition.json请从救援系统运行该脚本,不要从将要替换的服务器上运行。因为脚本运行时会重新分区目标设备。设备上至少需要 8 GB 可用空间,且救援环境必须提供 bash、bzip2 或 lbzip2、lsblk、wget、udevadm、gpg 和 gawk。您的 ignition.json 必须包含 SSH 密钥,否则安装后的系统将无法让您登录。
Fedora CoreOS 的流程类似,其安装程序以容器形式运行:
sudo podman run --pull=always --privileged --rm \
-v /dev:/dev -v /run/udev:/run/udev -v .:/data -w /data \
quay.io/coreos/coreos-installer:release \
install /dev/vdb -i config.ign运行前使用 lsblk 确认设备名称。向错误的设备写入数据会销毁其中原有的内容,而且不会显示确认提示。
bootc 提供了无需进入救援模式的方案,因为它可以在运行中的 Linux 系统上直接完成转换:
sudo podman run --rm --privileged -v /dev:/dev -v /var/lib/containers:/var/lib/containers -v /:/target \
--pid=host --security-opt label=type:unconfined_t \
quay.io/fedora/fedora-bootc:44 \
bootc install to-existing-root运行前请阅读所用基础镜像的文档,并先在可以丢弃的服务器上进行测试。重启后,机器将运行该镜像,原有的软件包集合也会消失。
哪些人适合运行不可变服务器,哪些人不适合
如果您的服务器属于“牲畜式”基础设施,应考虑采用这种方式。也就是说,您使用同一份配置创建大量机器。持续集成(CI)运行器只运行 1 小时。k3s 或 Kubernetes 节点出现问题时直接替换,而不是修复。对于这类环境,出现故障服务器时,处理方式通常已经确定为“删除它,再创建一台新的”。当您需要向审计人员证明机器上运行的内容时,这种方式也很有价值,因为您可以提供镜像摘要,而不是软件包列表。
如果您只有一台手动维护的 VPS,运行着 3 个服务,需要什么就临时安装什么,而且没有构建流水线,则不应采用这种方式。镜像模式不会减少工作量。它只是将工作从服务器转移到构建流程,并且需要为此维护镜像仓库和流水线。如果您有合适的位置承载这些工作,就能获得配置完全一致的服务器,并且可以通过重启完成回滚。如果没有,您只是在原本运行正常的服务器上增加了更多组件,让凌晨 2 点的故障处理更加困难。
保守的折中方案仍然有效:使用普通发行版,启用自动安全更新,并实际演练过重建流程。选择基础系统本身就是一项独立决策,详见为 VPS 选择要运行的操作系统。镜像模式只是关于软件应如何部署到机器上的一个非常古老争论的最新阶段,而Linux 发行版的历史基本就是这场争论不断重复的过程。
FAQ
真正不可变的 Linux 发行版真的不可变吗?
不是,这个名称容易造成误解。root 仍然可以向磁盘写入数据。实际情况是,运行时将 /usr 以只读方式挂载,并在下次更新时整体替换为新映像,而 /etc 和 /var 仍然可写,并会在更新后保留。您在 /usr 下所做的更改,要么会立即被拒绝,要么会在下一次更新时丢弃。因此,实际效果是,系统目录只会在映像发生变化时改变。
如果 VPS 不提供 Fedora CoreOS 或 Flatcar,可以运行它们吗?
通常可以,前提是提供商提供救援系统和控制台访问权限。您先启动救援系统,将发行版磁盘映像写入块设备,然后重新启动。Flatcar 的 flatcar-install 脚本可从任意 Linux 系统执行此操作,Fedora CoreOS 则将 coreos-installer 作为容器提供,您可以用相同方式运行它。两者都需要包含 SSH 密钥的 Ignition 文件,因为没有首次启动时的密码提示可供使用。如果没有控制台访问权限,请不要尝试:机器无法恢复启动时,您将没有任何途径查看问题。
如何在不可变服务器上安装软件包?
将软件包加入映像,然后重新部署。在 bootc 中,需要在 Containerfile 中添加 RUN dnf -y install ... 行,执行构建和推送,然后在机器上运行 sudo bootc upgrade --apply。在 Fedora CoreOS 中,可以使用 sudo rpm-ostree install 对软件包进行分层安装,然后重新启动;代价是该软件包会在之后的每次更新中重新应用。在 Flatcar 和 Talos 中没有软件包管理器,因此应使用容器。对于 bootc 主机上的一次性调试工具,sudo bootc usr-overlay 会提供一个可写的 /usr,该环境会在下次重新启动时消失。
映像模式能修复内核更新后无法启动的 VPS 吗?
它会将恢复过程从使用救援控制台转变为重新启动。之前的映像、内核和用户空间仍保留在磁盘上,因此使用 sudo bootc rollback 或 sudo rpm-ostree rollback -r 即可切换回之前的版本。Talos 和 Flatcar 更进一步:如果新槽位无法启动,它们会在未收到手动指示的情况下回滚,因为启动项只有在成功启动一次后才会成为默认项。这些机制都不能阻止错误更新,但可以低成本撤销更新。
服务器应选择哪个不可变发行版?
如果您希望使用通用 Linux 服务器,将其像容器映像一样构建,并安装到已有机器上,请选择 bootc。如果您希望采用相同模式,但由系统代为完成构建,并开箱即用地获得自动更新,请选择 Fedora CoreOS。如果您希望使用采用 A/B 更新方案的最小化容器主机,并且不提供软件包管理器供人使用,请选择 Flatcar。只有在机器是 Kubernetes 节点时才选择 Talos,因为它没有登录 shell,也不运行其他服务。