openSUSE:原子和镜像更新的打包要求
本文档描述了软件包符合 transactional-update、systemd-sysext 以及 openSUSE 的快照和回滚功能的打包要求。
使用 transactional-update 的更新是原子的,这意味着,更新要么完全应用且没有错误,要么对系统不作任何更改。 此外,这种更新不应影响当前运行的进程。 如果更新失败,系统将看不到更新尝试,因此不会进行任何可见的更改,并且带有损坏更新的快照将被删除。 更新和所有更改仅在下次重新启动后可见,并且您始终可以重新启动到旧的快照以回滚更新所做的更改。 systemd-sysext 将带有额外软件(sysext-system 扩展)的镜像添加到正在运行的系统,这些镜像无需重新启动即可立即可见。 它们临时修改正在运行的系统,直到下次重新启动。
在所有情况下,RPM 格式的软件包都是起点。 这些软件包可以直接安装在系统中,或者可以从它们创建 OCI 镜像、文件系统镜像或 systemd-sysext 镜像,然后用于安装或更新系统。
定义
- 镜像:如果本文档中使用“镜像”,则不是完整的磁盘原始镜像,而是分区的内容。 例如,镜像可能仅包含 /usr 的内容,如果 /usr 是自己的分区或 btrfs 子卷。 格式可以是原始分区、OCI 容器、tar 归档文件或类似格式。 镜像通常由 RPM 池构建。
- hermetic-usr:操作系统资源(代码、数据文件等)应在不可变的 /usr 中保持hermetic。 这意味着 /usr 树应携带设置 /usr 之外的最小目录和文件集以使系统正常工作的一切。 然后可以将此 /usr 树以只读方式挂载到可写根文件系统,该文件系统最终将携带 /etc、/var 和 /home 中的本地配置、状态和用户数据。 有关所有详细信息和背景,请参阅 Fitting Everything Together
- hermetic OS 在 /usr 中全面定义:将 /usr 树与空的、否则未填充的根文件系统结合起来,它将成功启动,自动添加启动所必需的严格必要的文件和资源。
要求
对于 transactional-update,有两种方法可以安装系统和更新
- 通过 RPM
- 通过 OCI 容器(镜像)
OCI 镜像的要求比 RPM 更严格:RPM 可以具有 %pre/%post 脚本。 与在 Tumbleweed 或纯 SLES 上安装/更新时相比,它们的功能有限,但可以在快照内进行一些更改。 镜像(例如 OCI,但不仅限于此)没有 %pre/%post 脚本。 RPM 的脚本在构建镜像时在构建环境中运行,而不是在最终系统上运行。 因此,根本无法修改旧镜像的配置文件。 检查硬件的特殊功能是没有用的,因为最终系统将在不同的硬件上运行。
配置文件处理应遵循 配置文件的规范。
用户数据和应用程序
用户数据和应用程序需要严格分离。 用户数据应位于主根子卷之外的另一个子卷中,应用程序应始终位于主根子卷中,而不是其他子卷中。 使用 /srv 在这方面是一个真正的问题:它包含应用程序、用户数据和配置文件混合在一起。 因此,它仅应由 ISV 和第三方供应商使用,而不应由分发商使用。
与 transactional-update 兼容的 RPM 的要求
- 所有文件必须存储在快照内,在我们的例子中是 /etc 和 /usr,而不是 /var、/opt、/srv、/usr/local 或其他任何位置。
- (重新)启动守护进程是不可能的。
- 修改 /usr 和 /etc 之外的文件是不可能的。
- 遵循 配置文件的规范 是可选的,但首选,因为这也可以解决配置文件的几个更新问题(参见 openSUSE:Packaging UsrEtc)
- 必须通过 systemd-tmpfiles 和 systemd 服务进行快照外部的修改。
- 不允许软件在任何时候修改系统只读部分中的文件。
基于镜像更新的要求
- 与 RPM 相同的规则
- 没有 %pre/%post 脚本用于更新,它们仅在构建镜像时在构建环境中运行。
- 软件包内容限制为 /usr(不包括 /usr/local),对于 systemd-sysext,/opt 也可以使用。
- 遵循 配置文件的规范 配置文件的规范是强制性的,或者安装/调整配置文件必须通过 systemd 服务的黑客手段完成。
- 只能在下次启动时创建新的系统用户和组,所有文件应由 root:root 拥有。
由于 systemd-sysext 使用镜像,因此适用于基于镜像的更严格的规则(hermetic-usr)没有任何例外。
完美的软件包
对于 transactional-update 和构建镜像而言,完美的软件包是遵循 hermetic-usr、没有 %pre/%post 或类似脚本且所有文件都由 root:root 拥有的 RPM。 系统用户和组仅在运行时或在首次启动后存储数据时需要,因此,如果用户、组和 /usr 之外的目录/文件仅通过 sysusers.d 和 tmpfiles.d 配置文件在下次重新启动时创建,则就足够了。
虽然许多软件包已经遵循 hermetic-usr 原则,但没有 %pre/%post 脚本会使许多 RPM 无法被 openSUSE Tumbleweed 或 SUSE Linux Enterprise Server 使用,因此无法用于传统的 Linux 发行版。 在这里,在软件包安装期间重新启动 systemd 服务、创建系统用户和执行 systemd-tmpfiles 的可能性是必要的。 为此,应使用 用于 systemd 的标准 RPM 宏,我们知道它们与 transactional-update 兼容。
一致性测试
- 检查 RPM 文件列表,所有内容必须位于 /usr(和 /etc)中
- 安装 RPM,检查 %pre/%post 脚本访问了哪些文件
可能的解决方案
/var 中的文件和目录
RPM 不应将文件打包到 /var 中,因为这些文件无法在软件包的安装、更新或删除期间创建、更新或删除。 更好的解决方案是使用 systemd-tmpfiles 创建这些文件
- 将文件存储在 /usr/share 或 /usr/lib 下的目录中
- 创建一个 /usr/lib/tmpfiles.d/<package>.conf 文件,该文件将文件复制或链接到 /var
一个示例(摘自 ypserv)如下所示
ypserv.conf
d /var/yp 0755 - - - L /var/yp/Makefile - - - - ../../usr/lib/yp/ypMakefile L /var/yp/securenets - - - - ../../usr/lib/yp/securenets.example
ypserv.spec
%post %tmpfiles_create ypserv.conf
有关更多信息,请阅读 tmpfiles 配置文件格式的文档
pam-config 和 /etc/pam.d
pam-config 由几个 RPM 在 %post 脚本中调用,以将 PAM 模块添加到 PAM 配置。 这在镜像情况下是不可能的,可能的解决方案如下
- 将 /etc/pam.d 移动到 /usr/share/factory/pam.d
- 创建一个 /usr/share/tmpfiles.d/pam.d.conf 文件
pam.d.conf
L /etc/pam.d
这将通过 systemd-tmpfiles 创建一个 /etc/pam.d 到 /usr/share/factory/pam.d 的符号链接。
fillup 和 /etc/sysconfig
有两种可能的解决方案:1. 将您的 sysconfig 文件转换为“默认”配置文件,这些文件也采用 shell 可解析的 KEY=VALUE 格式。 /usr/etc/default/<example> 包含 RPM/发行版提供的文件,/etc/default/<example> 可以由管理员创建并覆盖单个值。
2. 像在“/var 中的文件和目录”示例中使用 /usr/share/factory 和 systemd-tmpfiles 一样。
fillup 不适用于 systemd-sysext,也不适用于 systemd-tmpfiles 的上述解决方法。 因此,最好根本不要使用 fillup。
未解决的问题和其他限制
存在一些未解决的问题
- 虽然 %set_permissions 和类似的宏在构建镜像时有效,但该功能在运行系统中不起作用,因为根文件系统是只读的,并且无法更改权限。