Linux 有很多面孔:如何在任何发行版上工作

Linux 有很多面孔:如何在任何发行版上工作

创建适用于任何发行版的备份应用程序并不是一件容易的事。 为了确保 Veeam Agent for Linux 在 Red Hat 6 和 Debian 6、OpenSUSE 15.1 和 Ubuntu 19.04 等发行版上运行,您必须解决一系列问题,特别是考虑到该软件产品包含内核模块。

本文根据会议演讲材料撰写 Linux 彼得 2019.

Linux 不仅仅是最流行的操作系统之一。 从本质上讲,这是一个平台,您可以在此基础上制作一些独特的、属于您自己的东西。 因此,Linux 有许多发行版,它们的软件组件集各不相同。 这里出现了一个问题:为了使软件产品能够在任何发行版上运行,您必须考虑每个发行版的功能。

包管理器。 .deb 与 .rpm

让我们从跨不同发行版分发产品的明显问题开始。
分发软件产品的最典型方法是将软件包放在存储库中,以便系统内置的软件包管理器可以从那里安装它。
但是,我们有两种流行的包格式: и DEB。 这意味着每个人都必须支持。

在 deb 软件包的世界中,兼容性水平是惊人的。 相同的软件包在 Debian 6 和 Ubuntu 19.04 上安装并运行得同样好。 旧的 Debian 发行版中制定的构建软件包和使用它们的过程标准在新奇的 Linux Mint 和elementary OS 中仍然适用。 因此,对于 Veeam Agent for Linux,每个硬件平台一个 deb 包就足够了。

但在 rpm 包的世界里,差异是巨大的。 首先,由于Red Hat和SUSE这两个完全独立的发行商,完全不需要兼容。 其次,这些分销商有来自这些分销商的分销套件。 支持和实验。 它们之间也不需要兼容。 原来el6、el7、el8都有自己的包。 Fedora 的单独包。 SLES11 和 12 的软件包以及 openSUSE 的单独软件包。 主要问题是依赖关系和包名称。

依赖问题

不幸的是,相同的包通常在不同的发行版中以不同的名称出现。 以下是 veeam 软件包依赖项的部分列表。

对于 EL7:
对于 SLES 12:

  • libblkid
  • 库标准++
  • ncurses 库
  • 保险丝库
  • 文件库
  • veeamsnap=3.0.2.1185
  • libblkid1
  • libgcc_s1
  • + + 6
  • libmagic1
  • 库熔丝2
  • veeamsnap-kmp=3.0.2.1185

因此,依赖项列表对于该发行版来说是唯一的。

更糟糕的是,当更新的版本开始隐藏在旧的包名称下时。

示例:

该软件包已在 Fedora 24 中更新 诅咒 从版本 5 到版本 6。我们的产品是使用版本 5 构建的,以确保与旧版本的兼容性。 要在 Fedora 5 上使用旧的第 24 版库,我必须使用该包 ncurses 兼容库.

因此,Fedora 有两个具有不同依赖项的软件包。

进一步更有趣。 下一次发行版更新后,该软件包 ncurses 兼容库 对于该库的版本 5,它已不可用。 对于发行商来说,将旧库拖入新版本的发行版中的成本很高。 一段时间后,该问题在 SUSE 发行版中再次出现。

结果,一些发行版不得不放弃对 ncurses 库,并修复该产品,使其可以与任何版本的库一起使用。

顺便说一句,在 Red Hat 8 版本中不再有元包 蟒蛇,它指的是好旧的 python 2.7。 有 python2 и 蟒蛇3.

包管理器的替代方案

依赖关系的问题由来已久,而且长期以来一直很明显。 只要记住依赖地狱即可。
将各种库和应用程序结合起来,使它们都能稳定工作且不发生冲突——事实上,这是任何 Linux 发行商都试图解决的任务。

包管理器试图以完全不同的方式解决这个问题。 活泼的 来自规范。 主要思想:应用程序在与主系统隔离和保护的沙箱中运行。 如果应用程序需要库,它们会随应用程序本身一起提供。

Flatpak 还允许您使用 Linux 容器在沙箱中运行应用程序。 也使用了沙盒的思想 AppImage.

这些解决方案允许您为任何发行版创建一个包。 的情况下 Flatpak 即使管理员不知情,也可以安装和启动应用程序。

主要问题是并非所有应用程序都可以在沙箱中运行。 有些人需要直接访问该平台。 我什至没有谈论内核模块,它们严格依赖于内核并且不适合沙箱概念。

第二个问题是,在企业环境中流行的 Red Hat 和 SUSE 发行版尚未包含对 Snappy 和 Flatpak 的支持。

在这方面,Veeam Agent for Linux 不可用 snapcraft.io. 不开 flathub.org.

为了结束有关包管理器的问题,我想指出,有一种选择可以通过组合二进制文件和用于将它们安装到一个包中的脚本来完全放弃包管理器。

这样的捆绑包允许您为不同的发行版和平台创建一个通用包,执行交互式安装过程,执行必要的自定义。 我只遇到过来自 VMware 的 Linux 的此类软件包。

更新问题

Linux 有很多面孔:如何在任何发行版上工作
即使所有依赖性问题都得到解决,程序在同一发行版上的运行方式也可能有所不同。 这是更新的问题。

有3种更新策略:

  • 最简单的一种就是永远不更新。 我设置了服务器并忘记了它。 如果一切正常为什么要更新? 当您第一次联系支持人员时,问题就开始出现。 发行版的创建者仅支持更新的版本。
  • 您可以信任经销商并设置自动更新。 在这种情况下,更新不成功后可能会立即致电支持人员。
  • 仅在测试基础设施上运行后进行手动更新的选项是最可靠的,但昂贵且耗时。 不是每个人都能负担得起。

由于不同的用户使用不同的更新策略,因此需要同时支持最新版本和所有以前发布的版本。 这使得开发和测试过程变得复杂,并给支持团队带来了麻烦。

多种硬件平台

不同的硬件平台是一个很大程度上特定于本机代码的问题。 至少,您必须为每个受支持的平台收集二进制文件。

在 Veeam Agent for Linux 项目中,我们仍然无法支持像这样的 RISC。

我不会详细讨论这个问题。 我只会概述主要问题:平台相关类型,例如 size_t、结构对齐和字节顺序。

静态和/或动态链接

Linux 有很多面孔:如何在任何发行版上工作
但问题是“如何与库链接——动态还是静态?” 值得讨论。

通常,Linux 下的 C/C++ 应用程序使用动态链接。 如果应用程序是专门为特定发行版构建的,那么这非常有用。

如果任务是用一个二进制文件覆盖各种发行版,那么您必须关注最旧的受支持发行版。 对于我们来说,这是Red Hat 6。它包含gcc 4.4,连C++11标准都不支持 十分.

我们使用 gcc 6.3 构建项目,它完全支持 C++14。 当然,在这种情况下,在 Red Hat 6 上您必须随身携带 libstdc++ 和 boost 库。 最简单的方法是静态链接到它们。

但遗憾的是,并非所有库都可以静态链接。

首先是系统库,例如 库保险丝, libblkid 有必要动态链接以确保它们与内核及其模块的兼容性。

其次,许可证有一个微妙之处。

GPL 许可证基本上允许您仅使用开源代码链接库。 MIT 和 BSD 允许静态链接并允许将库包含在项目中。 但 LGPL 似乎并不矛盾静态链接,而是要求共享链接所需的文件。

一般来说,使用动态链接将使您不必提供任何内容。

构建 C/C++ 应用程序

要为不同的平台和发行版构建C/C++应用程序,只需选择或构建合适的gcc版本并使用针对特定体系结构的交叉编译器并组装整套库就足够了。 这项工作是比较可行的,但是比较麻烦。 并且不能保证所选的编译器和库将提供可行的版本。

一个明显的优势:基础设施大大简化,因为整个构建过程可以在一台机器上完成。 此外,为一种架构收集一组二进制文件就足够了,您可以将它们打包成不同发行版的包。 这就是为 Veeam Agent for Linux 构建 veeam 软件包的方式。

与此选项相反,您可以简单地准备一个构建场,即多台用于组装的机器。 每台这样的机器将为特定的发行版和特定的架构提供应用程序编译和包组装。 在这种情况下,使用发行者准备的手段进行编译。 也就是说,消除了准备编译器和选择库的阶段。 此外,构建过程可以轻松并行化。

然而,这种方法有一个缺点:对于同一架构中的每个发行版,您都必须收集自己的二进制文件集。 另一个缺点是需要维护如此大量的机器,并且必须分配大量的磁盘空间和RAM。

这就是为 Red Hat 发行版编译 veeamsnap 内核模块的 KMOD 包的方式。

打开构建服务

SUSE 的同事试图以编译应用程序和组装包的特殊服务的形式实现一些中间立场 - 开放构建服务.

本质上,它是一个虚拟机管理程序,用于创建虚拟机,在其中安装所有必需的包,编译应用程序并在这个隔离的环境中构建包,然后发布虚拟机。

Linux 有很多面孔:如何在任何发行版上工作

OpenBuildService 中实现的调度程序将确定可以启动多少虚拟机以获得最佳包构建速度。 内置签名机制将对包进行签名并将其上传到内置存储库。 内置的版本控制系统将保存更改和构建的历史记录。 剩下的就是简单地将您的源添加到该系统中。 您甚至不必自己设置服务器;您可以使用开放的服务器。

然而,存在一个问题:这种收割机很难适应现有的基础设施。 例如,不需要版本控制;我们已经拥有自己的源代码。 我们的签名机制不同:我们使用特殊的服务器。 也不需要存储库。

此外,对其他发行版(例如 Red Hat)的支持实施得相当差,这是可以理解的。

此类服务的优点是快速支持下一版本的 SUSE 发行版。 在正式宣布发布之前,组装所需的软件包已发布在公共存储库上。 OpenBuildService 上的可用发行版列表中会出现一个新发行版。 我们选中该框并将其添加到构建计划中。 因此,添加新版本的发行版几乎只需单击一下即可完成。

在我们的基础设施中,使用 OpenBuildService 组装了 SUSE 发行版的 veeamsnap 内核模块的所有 KMP 包。

接下来,我想详细讨论内核模块的具体问题。

内核ABI

Linux 内核模块历来以源代码形式分发。 事实上,内核的创建者并不需要担心支持内核模块的稳定 API,特别是在二进制级别(进一步称为 kABI)。

要为普通内核构建模块,您肯定需要该特定内核的标头,并且它仅适用于该内核。

DKMS 允许您在更新内核时自动执行构建模块的过程。 因此,Debian 存储库(及其许多相关版本)的用户使用来自发行商存储库的内核模块或使用 DKMS 从源代码编译的内核模块。

然而,这种情况并不特别适合企业领域。 专有代码分发商希望将产品作为编译的二进制文件分发。

出于安全原因,管理员不希望将开发工具保留在生产服务器上。 Red Hat 和 SUSE 等企业 Linux 发行商决定为用户提供稳定的 kABI。 结果是适用于 Red Hat 的 KMOD 软件包和适用于 SUSE 的 KMP 软件包。

这个解决方案的本质非常简单。 对于特定版本的发行版,内核 API 被冻结。 分发者声称他使用的是3.10内核,并且只进行了不影响内核接口的修正和改进,并且为第一个内核收集的模块可以用于所有后续内核而无需重新编译。

Red Hat 声称该发行版在其整个生命周期中都具有 kABI 兼容性。 也就是说,rhel 6.0(2010 年 6.10 月发布)的组装模块也应该适用于 2018 版本(8 年 XNUMX 月发布)。 而这一晃,已经快XNUMX年了。 当然,这个任务也是相当艰巨的。
我们记录了几个由于 kABI 兼容性问题导致 veeamsnap 模块停止工作的案例。

在为 RHEL 7.0 编译的 veeamsnap 模块被证明与 RHEL 7.5 的内核不兼容,但它加载并肯定会导致服务器崩溃之后,我们完全放弃了对 RHEL 7 使用 kABI 兼容性。

目前,RHEL 7 的 KMOD 包包含每个发行版本的程序集和加载模块的脚本。

SUSE 更加仔细地处理了 kABI 兼容性的任务。 它们仅在一个服务包内提供 kABI 兼容性。

例如,SLES 12 的发布是在 2014 年 12 月。而 SLES 1 SP2015 已经是在 3.12 年 XNUMX 月,也就是过去了一年多一点。 尽管两个版本都使用 XNUMX 内核,但它们与 kABI 不兼容。 显然,维持 kABI 兼容性一年要容易得多。 每年的内核模块更新周期不应该给模块创建者带来问题。

由于此 SUSE 政策,我们没有在 veeamsnap 模块中记录任何 kABI 兼容性问题。 确实,SUSE 的软件包数量几乎增加了一个数量级。

补丁和向后移植

尽管发行商努力确保 kABI 兼容性和内核稳定性,但他们也尝试提高该稳定内核的性能并消除缺陷。

与此同时,除了他们自己的“修复错误”之外,企业 Linux 内核的开发人员还监视普通内核的变化,并将其转移到他们的“稳定”内核中。

有时这会带来新的结果 错误.

在最新版本的 Red Hat 6 中,其中一项小更新出现了错误。 导致了在快照发布的时候,veeamsnap模块肯定会导致系统崩溃。 比较了更新前后的内核源代码后,我们发现向后移植是罪魁祸首。 普通内核版本 4.19 中也进行了类似的修复。 只是这个修复在普通内核中运行良好,但是当将其转移到“稳定”2.6.32 时,自旋锁出现了问题。

当然,每个人总会有错误,但是值得冒着稳定性的风险将代码从 4.19 拖到 2.6.32 吗?...我不确定...

最糟糕的是营销陷入“稳定”与“现代化”的拉锯战。 营销部门一方面需要更新后的发行版核心稳定,同时性能更好,有新功能。 这会导致奇怪的妥协。

当我尝试在 SLES 4.4 SP12 的内核 3 上构建模块时,我惊讶地发现其中包含 vanilla 4.8 的功能。 在我看来,SLES 4.4 SP12 中的 3 内核的块 I/O 实现与 SLES4.8 SP4.4 中的先前版本的稳定 12 内核相比更类似于 2 内核。 我无法判断 SP4.8 的代码从内核 4.4 转移到 SLES 3 的百分比是多少,但我什至不能将内核称为相同的稳定版 4.4。

最令人不愉快的是,当编写一个在不同内核上同样工作的模块时,您不能再依赖内核版本。 您还必须考虑分布。 有时您可以参与与新功能一起出现的定义,这是件好事,但这种机会并不总是出现。

结果,代码中充满了奇怪的条件编译指令。

还有一些补丁可以更改已记录的内核 API。
我遇到了分布 KDE霓虹灯 5.16 中,非常惊讶地发现该内核版本中的lookup_bdev 调用更改了输入参数列表。

为了将它们组合在一起,我必须在 makefile 中添加一个脚本来检查 Lookup_bdev 函数是否具有掩码参数。

签署内核模块

但让我们回到包分发的问题。

稳定的 kABI 的优点之一是内核模块可以被签名为二进制文件。 在这种情况下,开发人员可以确定模块没有被意外损坏或故意修改。 您可以使用 modinfo 命令检查这一点。

Red Hat 和 SUSE 发行版允许您检查模块的签名,并且仅当相应的证书在系统上注册时才加载它。 证书是用于对模块进行签名的公钥。 我们将其作为单独的包分发。

这里的问题是证书要么可以内置到内核中(发行商使用它们),要么必须使用实用程序写入 EFI 非易失性内存 莫库蒂尔。 公用事业 莫库蒂尔 安装证书时,它要求您重新启动系统,甚至在加载操作系统内核之前,都会提示管理员允许加载新证书。

因此,添加证书需要物理管理员访问系统。 如果计算机位于云中的某个位置或只是位于远程服务器机房并且仅通过网络(例如通过 ssh)进行访问,则无法添加证书。

虚拟机上的 EFI

尽管EFI早已被几乎所有主板厂商支持,但在安装系统时,管理员可能没有想到EFI的必要性,甚至可能将其禁用。

并非所有虚拟机管理程序都支持 EFI。 VMWare vSphere 从版本 5 开始支持 EFI。
Microsoft Hyper-V 也从 Windows Server 2012R2 的 Hyper-V 开始获得了 EFI 支持。

但是,在默认配置中,Linux 计算机禁用此功能,这意味着无法安装证书。

在 vSphere 6.5 中,设置选项 安全启动 只能在通过 Flash 运行的旧版 Web 界面中实现。 HTML-5 上的 Web UI 仍然远远落后。

实验分布

最后,让我们考虑一下实验发行版和没有官方支持的发行版的问题。 一方面,这样的发行版不太可能在严肃组织的服务器上找到。 此类发行版没有官方支持。 因此,提供这些。 此类发行版不支持该产品。

然而,此类发行版成为尝试新实验解决方案的便捷平台。 例如,Fedora、OpenSUSE Tumbleweed 或 Debian 的不稳定版本。 他们相当稳定。 他们总是有新版本的程序和新内核。 一年后,这个实验性功能可能会出现在更新的 RHEL、SLES 或 Ubuntu 中。

因此,如果某些东西在实验发行版上不起作用,这就是找出问题并解决它的原因。 您需要做好准备,因为此功能很快就会出现在用户的生产服务器上。

您可以研究 3.0 版当前官方支持的发行版列表 这里。 但我们的产品可以运行的实际发行版列表要广泛得多。

就我个人而言,我对 Elbrus 操作系统的实验很感兴趣。 完成 veeam 软件包后,我们的产品已安装并运行。 我在 Habré 上写过这个实验 文章.

嗯,对新发行版的支持仍在继续。 我们正在等待4.0版本的发布。 Beta即将出现,敬请关注 什么是新的!

来源: habr.com

添加评论