SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-21

服务器适合使用不可变 Linux 发行版吗?

了解服务器采用 Image mode 后的真实代价: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,再手动删除一个内核软件包。之所以可行,是因为引导加载程序会保留旧内核,但只有内核以这种方式进行版本管理。同一事务中完成的 glibc 更新和 systemd 更改已经生效,没有单个命令可以将它们一起回退。

在镜像模式下,整个系统就是一个单元。在 bootc 主机上:

sudo bootc status
sudo bootc rollback
sudo systemctl reboot

bootc rollback 将引导加载程序的顺序切换回上一个启动项,也就是您一小时前运行的镜像,内核和用户空间一并回退。不会下载任何内容,也不会重新构建,因为旧镜像始终保留在磁盘上。

Fedora CoreOS 也能完成相同操作,只是名称不同:

sudo systemctl stop zincati.service
sudo rpm-ostree rollback -r

先停止 Zincati。Zincati 是让 Fedora CoreOS 计算机保持最新版本的代理。如果不停止它,它会重新部署您刚刚回退的更新。回滚准备完成后,-r 会执行一次重启。要防止您信任的部署被垃圾回收:

sudo ostree admin pin 0
rpm-ostree status

rpm-ostree status 按照引导加载程序提供启动选项的顺序列出部署,为正在运行的部署标记一个点号,并在您固定的部署上显示 Pinned: yes

Talos 可通过工作站发起一次 API 调用完成回滚:

talosctl rollback --nodes 10.20.30.40

Flatcar 保留两个 /usr 分区,并在两者之间切换。每个槽位都会在分区表中记录优先级和尝试次数。因此,某个槽位始终无法成功启动并耗尽尝试次数后,引导加载程序会选择另一个槽位。检查当前使用的槽位,以及该槽位是否已标记为正常:

sudo cgpt show "$(rootdev -s /usr)" | grep successful=1

正常运行的槽位会输出包含 priority=1 tries=0 successful=1 的行。找不到匹配行,表示当前槽位尚未确认成功启动。这正是计算机在更新完成后、首次成功启动前所处的状态。

“安装软件包”的替代方案: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 --apply

bootc 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.ign

Ignition 仅在首次启动时于 initramfs 中运行。这是从 cloud-init 转过来的用户最容易忽略的部分。如果配置中没有 SSH 密钥,机器启动后将无法登录,您只能从头重新配置。将配置部署到重要服务器之前,先在一次性测试机器上验证。

从 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,但之后需要自行负责补丁计划。

软件包分层可作为例外方案:

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_update

UPDATE_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=1h

REBOOT_STRATEGY=off 会将重启留给您手动执行。同一文件中的 SERVER=disabled 会完全停止更新检查。对于集群,将 REBOOT_STRATEGY=etcd-locklocksmithctl set-max 4 结合使用,可以限制同时重启的节点数量,避免一次更新同时停止整个节点集群。

Talos Linux:无 shell、无 SSH、无控制台

Talos 是这 4 个系统中范围最窄的一个,也最明确地限定了用途。它用于运行 Kubernetes 节点。系统没有 SSH daemon、shell,也不提供控制台登录。您在工作站上使用 talosctl 发起 gRPC API 调用,操作由您保存在 git 中的机器配置定义。

talosctl upgrade --nodes 10.20.30.40 \
  --image ghcr.io/siderolabs/installer:v1.10.6

将该标签替换为您要升级到的版本。升级采用 A-B 方案,并保留之前的内核和 OS 映像。如果新版本无法启动,Talos 会自行回滚,无需人工干预。由于没有 shell,调试方式也不同:您应使用 talosctl logstalosctl dmesg,而不是在节点上使用 journalctl

如果您的工作负载不是 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 镜像即可。否则,您需要从救援系统自行写入磁盘。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 可用空间,且救援环境必须提供 bashbzip2lbzip2lsblkwgetudevadmgpggawk。您的 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

运行前请阅读所用基础镜像的文档,并先在可以弃用的服务器上进行测试。重启后,计算机将运行该镜像,原有的软件包集合也会消失。

谁适合运行不可变服务器,谁不适合

如果您的服务器是“牲畜式”管理的,应考虑采用这种方式。也就是使用同一份配方创建多台机器。运行时间只有 1 小时的 CI(持续集成)runner 适合这种方式。k3s 或 Kubernetes 节点通常会被替换,而不是修复,也适合这种方式。凡是遇到故障服务器时,解决方案已经确定为“删除它,再创建一台新的”,都适合采用这种方式。需要向审计人员证明机器上运行的内容时,这种方式也很有价值,因为您可以提供镜像摘要,而不是软件包列表。

如果您只有一台手动维护的 VPS,运行着 3 个服务,需要什么就临时安装什么,而且没有构建流水线,则不应采用这种方式。镜像模式不会减少工作量。它只是将工作从服务器转移到构建过程,并且要求您为此维护 registry 和流水线。如果您有合适的位置承载这些工作,就能获得配置一致的服务器,并可通过重启完成回滚。如果没有,您只是在一台原本运行正常的机器上增加了更多组件,也让凌晨 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 rollbacksudo rpm-ostree rollback -r 即可恢复到旧镜像。Talos 和 Flatcar 更进一步:如果新的启动槽无法启动,它们会在未收到手动指令时自动回滚,因为启动项只有在成功启动一次后才会成为默认项。上述机制都不能阻止错误更新,但可以低成本撤销一次更新。

服务器应选择哪个不可变发行版?

如果您需要通用 Linux 服务器,希望像构建容器镜像一样构建系统,并将其安装到已有机器上,请选择 bootc。如果您希望采用相同模式,但由系统代为完成构建,并默认启用自动更新,请选择 Fedora CoreOS。如果您需要采用 A/B 更新机制的最小化容器主机,并且不希望任何人使用软件包管理器,请选择 Flatcar。只有在机器是 Kubernetes 节点时才应选择 Talos,因为它没有 shell,也不运行其他服务。

#bootc#immutable#atomic#coreos#updates