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



创建可在任何发行版上运行的备份应用程序并非易事。 为了确保Veeam Agent for Linux在RHEL 6和Debian 6的发行版上运行,openSUSE Leap 15.1和Ubuntu 19.04必须解决一系列问题,尤其是当您认为内核模块是软件产品的一部分时。

本文基于LinuxPiter 2019大会上的演讲。

Linux不仅是最受欢迎的操作系统之一。 实际上,这是一个平台,您可以在此平台上做一些独特的事情,也可以自己做。 因此,Linux具有许多发行版,它们在一组软件组件中有所不同。 这里出现了问题:为了使软件产品能够在任何发行版上运行,您必须考虑每种软件的特性。

包管理器。 .deb和.rpm


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

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

但是在rpm软件包的世界中,差异很大。 首先,由于存在Red Hat和SUSE的两个完全独立的发行商,因此绝对不需要兼容性。 其次,这些分销商具有这些分销商的分销。 支持和实验。 它们之间也不需要兼容性。 事实证明,对于el6,el7和el8,他们拥有自己的软件包。 Fedora的单独包装。 SLES11和12的软件包,以及针对openSUSE的软件包。 主要问题是依赖关系和程序包名称。

依赖问题


las,相同的软件包通常以不同的发行版的不同名称结尾。 以下是veeam软件包依赖关系的部分列表。
对于EL7:对于SLES 12:
  • libblkid
  • libgcc
  • libstdc ++
  • ncurses-libs
  • 保险丝库
  • 文件库
  • veeamsnap = 3.0.2.1185
  • libblkid1
  • libgcc_s1
  • libstdc ++ 6
  • libmagic1
  • libfuse2
  • veeamsnap-kmp = 3.0.2.1185

结果,依赖项列表对于分发是唯一的。

当更新的版本开始隐藏在旧软件包名称下时,情况将变得更糟。

一个例子:

Fedora 24已将ncurses软件包从版本5更新到版本6。我们的产品是使用版本5构建的,以确保与较早发行版的兼容性。 要在Fedora 24上使用旧的库的第5版,我必须使用ncurses-compat-libs软件包。

结果,出现了两个针对Fedora的软件包,它们具有不同的依赖性。

更有趣。 在发行包的下一次更新之后,具有的第5版的ncurses-compat-libs包不可用。 发行商将旧图书馆拉到新的发行版本中是无利可图的。 一段时间后,该问题在SUSE发行版中再次出现。

结果,对于某些发行版,我不得不放弃对ncurses-libs的明确依赖,并修复产品,使其可以与任何版本的库一起使用。

顺便说一句,在Red Hat的第8版中,不再有引用旧的python 2.7python meta-package。 有python2python 3。

包经理的替代品


依赖关系的问题已经很久很久了。 只要回想一下Dependency地狱。
组合各种库和应用程序,以使它们都能稳定运行并且不会发生冲突-实际上,任何Linux发行商都在尝试解决此问题。

规范软件包管理器Snappy试图以不同的方式解决此问题。 主要思想:应用程序在与主系统隔离并受到保护的沙箱中运行。 如果应用程序需要库,则它们与应用程序本身一起提供。

Flatpak还允许您使用Linux容器在沙箱中运行应用程序。 还有AppImage ,它允许您创建程序的可移植图像。

这些解决方案使您可以为任何发行版创建一个程序包。 对于FlatpakAppImage,即使没有管理员的知识也可以安装和启动应用程序。

主要问题在于,并非所有应用程序都可以在沙箱中运行并且没有root特权。 有些需要直接访问平台。 我不是在谈论内核模块,这些模块高度依赖于内核并且不适合沙盒的概念。

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

在这方面,Veeam Agent for Linux既不在snapcraft.io上,也不在flathub.org上

在有关软件包管理器的问题的结尾,我注意到有一个选项可以通过组合二进制文件和脚本以将它们安装在一个软件包中来完全放弃软件包管理器。

这样的捆绑包使您可以为不同的发行版和平台创建一个通用软件包,以执行交互式安装过程,并进行必要的自定义。 我从VMware偶然发现了此类Linux软件包。

更新问题



即使解决了所有依赖关系问题,该程序也可能在相同的发行版上以不同的方式工作。 关键在于更新。

共有3种升级策略:

  • 最简单的是永不更新。 配置服务器并忘记了。 如果一切正常,为什么要更新? 问题是在您第一次联系支持人员时开始的。 发行版的创建者仅支持更新的发行版。
  • 您可以信任分发者并设置自动更新。 在这种情况下,更新失败后可能会立即寻求支持。
  • 仅在测试基础架构上运行后才进行手动更新的选项是最可靠的,但昂贵且耗时。 并非每个人都能负担得起。

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

各种硬件平台


各种硬件平台都是主要针对本机代码的问题。 至少,您必须为每个受支持的平台收集二进制文件。

在Veeam Linux代理项目中,我们仍然至少不支持类似RISC的功能。

我不会在这个问题上详细介绍。 我仅概述主要问题:与平台有关的类型,例如size_t ,结构的对齐方式和字节顺序。

静态和/或动态链接



这是一个问题“如何动态或静态链接到库?” 值得讨论。

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

如果任务是用一个二进制文件覆盖各种发行版,则您必须专注于支持最早的发行版。 对我们来说,这是Red Hat6。它包含gcc 4.4,即使C ++ 11标准也不完全支持。

我们正在使用完全支持C ++ 14的gcc 6.3构建项目。 自然,在这种情况下,在Red Hat 6上,必须将libstdc ++和boost库拖在一起。 链接到它们的最简单方法是静态方式。

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

首先,需要动态链接系统库,例如libfuselibblkid,以确保它们与内核及其模块兼容。

其次,许可证有些微妙。

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

通常,使用动态链接将避免提供某些东西。

构建C / C ++应用程序


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

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

与该选项相比,您可以简单地准备构建场,即准备组装多台机器。 每台这样的机器都会为特定的发行版和特定的体系结构提供应用程序的编译和程序包的组装。 在这种情况下,编译是通过分发者已准备好的方式进行的。 也就是说,不再需要编译器准备阶段和选择库。 另外,组装过程可以容易地并行化。

但是,这种方法有一个缺点:对于同一体系结构中的每个发行版,您都必须汇编自己的二进制文件集。 还有一个缺点是需要维护如此多的计算机,以分配大量的磁盘空间和RAM。

这样,就组装了用于Red Hat发行版的veeamsnap内核模块的KMOD软件包。

开放式构建服务


SUSE同事试图将某种中间立场作为一种特殊服务来编译应用程序和构建软件包-openbuildservice

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



OpenBuildService中实现的调度程序将确定为达到最佳程序包生成速度,可以运行多少个虚拟机。 内置的签名机制本身将对软件包进行签名,并将其放在内置的存储库中。 内置的版本控制系统将保存更改和程序集的历史记录。 只需将您的源代码添加到该系统即可。 即使服务器本身也没有必要提高,但是您可以使用打开的服务器。

但是,这里存在一个问题:这种联合收割机很难放入现有的基础架构中。 例如,不需要版本控制,我们已经有了自己的源代码。 签名机制不同:使用特殊的服务器。 也不需要该存储库。

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

该服务的优点是可以快速支持SUSE发行版的下一版本。 在正式发布公告之前,将组装所需的软件包上载到公共存储库。 一个新的出现在OpenBuildService的可用发行版列表中。 我们打勾,然后将其添加到装配计划中。 因此,只需单击一下即可添加发行版的新版本。

在我们的基础架构中,我们使用OpenBuildService组装veeamsnap内核模块的所有KMP软件包以用于SUSE发行版。

此外,我想谈谈内核模块特有的问题。

内核ABI


Linux内核模块历来以源代码形式分发。 事实是,内核的创建者不会为维护内核模块的稳定API负担自己的负担,甚至在二进制级别,甚至kABI上也是如此。

要为原始内核构建模块,需要此特定内核的标头,并且该标头仅在此内核上有效。

DKMS允许您在更新内核时自动执行模块的组装过程。 结果,Debian存储库(及其许多亲戚)的用户使用了来自发行者存储库的内核模块,或者使用DKMS从源程序组装了内核模块。

但是,这种情况对于企业部门而言并不是特别舒服。 专有代码分发者希望以编译二进制文件的形式分发产品。

出于安全原因,管理员不想将开发工具保留在生产服务器上。 企业Linux发行商(例如Red Hat和SUSE)已决定他们可以为其用户维护稳定的kABI。 结果,出现了Red Hat的KMOD软件包和SUSE的KMP软件包。

该解决方案的本质非常简单。 对于发行版的特定版本,内核API被冻结。 分发者声明他使用内核(例如3.10),并且仅进行了不以任何方式影响内核接口的更正和改进,并且为第一个内核组装的模块可以用于所有后续内核,而无需重新编译。

红帽宣布其生命周期内的发行版均具有kABI兼容性。 也就是说,用于rhel 6.0(2010年11月发行)的组装模块也应在6.10版(2018年6月发行)上工作。 这将近8年。 自然,任务相当复杂。
我们记录了几种情况,由于kABI兼容性问题,veeamsnap模块停止工作。

事实证明,为RHEL 7.0编译的veeamsnap模块与RHEL 7.5的内核不兼容,但加载并保证了服务器掉线后,我们通常拒绝对RHEL 7使用kABI兼容性。

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

SUSE更仔细地处理了kABI兼容性任务。 它们仅在一个Service Pack中提供kABI兼容性。

例如,2014年9月发布了SLES12。SLES12 SP1已于2015年12月发布,也就是说,已经过去了一年多。 尽管两个版本均使用3.12内核,但它们与kABI不兼容。 显然,只需一年就可以保持kABI兼容性。 年度核心模块更新周期不会对模块的创建者造成问题。

由于此SUSE策略,我们没有在veeamsnap模块中解决与kABI兼容性有关的任何问题。 的确,用于SUSE的软件包数量几乎增加了一个数量级。

修补程序和反向端口


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

此外,除了自己的“错误工作”之外,企业linux内核的开发人员还可以跟踪原始内核中的更改并将其转移到其“稳定”状态。

有时这会导致新的错误

Red Hat 6的最新版本在次要更新之一中犯了一个错误。 这导致了一个事实,即在释放快照时,可以确保veeamsnap模块使系统崩溃。 通过比较更新前后的内核源代码,我们发现应归咎于反向移植。 原始内核4.19版中进行了类似的修复。 但是,仅在香草核心中,此修补程序才能正常工作,并且在将其转移到“稳定” 2.6.32时,自旋锁存在问题。

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

最糟糕的是,当营销与拔河活动联系在一起时,“稳定” <->“现代化”。 市场部门一方面需要稳定的更新发行版,另一方面又要具有更好的性能和新功能。 这导致奇怪的妥协。

当我尝试从SLES 12 SP3在4.4内核上构建模块时,我惊讶地发现其中包含Vanilla 4.8的功能。 我认为,与以前的SLES12 SP2稳定的4.4内核发行版相比,SLES 12 SP3的内核4.4的块I / O的实现更像是4.8内核。 我无法判断有多少百分比的代码从4.8内核转移到SP3的SLES 4.4,但是我仍然没有机会将内核称为稳定的4.4。

对此最不愉快的是,当编写在不同内核上同样能很好工作的模块时,您将不再依赖内核版本。 我们还必须考虑分布。 有时候,您可以参与一个随新功能一起出现的定义,这很好,但是此功能并不总是出现。

结果,该代码被用于条件编译的特殊指令所包围。

也有一些补丁可以更改已记录的内核API。
我遇到一个KDE neon 5.16分发工具包 ,很惊讶地看到此内核版本中的lookup_bdev调用更改了输入参数的列表。

为了聚在一起,我们必须在makefile中添加一个脚本,该脚本检查lookup_bdev函数是否具有mask参数。

内核模块的签名


但是回到包分发的问题。

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

Red Hat和SUSE发行版允许您验证模块的签名并仅在系统中注册了适当的证书的情况下下载它。 证书是用于签名模块的公共密钥。 我们将其作为单独的软件包分发。

这里的问题是证书可以内置到内核中(分发者使用它们),或者必须使用mokutil实用程序将它们写入非易失性EFI内存。 安装证书时, mokutil实用程序需要重新启动系统,甚至在加载操作系统内核之前,它都会为管理员提供允许下载新证书的功能。

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

虚拟机上的EFI


尽管几乎所有主板创建者都长期以来都支持EFI,但是在安装系统时,管理员可能不会考虑是否需要EFI,因此可以将其禁用。

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

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

在vSphere 6.5中,您只能在可通过Flash使用的旧版Web界面中设置“ 安全启动”选项。 HTML-5上的Web UI远远落后。

实验分布


最后,考虑实验性发行和没有官方支持的发行的问题。 一方面,在严重组织的服务器上不太可能找到这种分布。 没有官方支持此类发行版。 因此,提供那些。 无法在这种发行版本上提供产品支持。

但是,这样的发行版成为测试新实验解决方案的便捷平台。 例如,Fedora,OpenSUSE Tumbleweed或Debian的不稳定版本。 他们很稳定。 它们始终具有新版本的程序,并且始终具有新的内核。 一年后,此实验功能可能已在更新的RHEL,SLES或Ubuntu中。

因此,如果实验性分发套件无法解决问题,那么可以借此机会来解决问题并加以解决。 您需要为以下事实做好准备:该功能将很快出现在用户的生产服务器上。

可在此处找到版本3.0官方支持的最新发行版列表。 但是,我们的产品可以使用的实际发行版列表要宽得多。

我个人对Elbrus OS的实验感兴趣。 更新veeam程序包后,我们的产品已安装并获得。 关于这个实验,我在哈布雷(Habré)上发表了文章

好吧,继续支持新发行版。 我们正在等待版本4.0的发布。 Beta即将面世,敬请期待最新消息

Source: https://habr.com/ru/post/zh-CN471226/


All Articles