ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Oracle Linux AppStream模块仓库:从入门到生产运维实战

Oracle Linux AppStream模块仓库:从入门到生产运维实战 直接开写。这篇东西我打算从运维视角讲透Oracle Linux的AppStream模块仓库把它是什么“怎么用”“踩了哪些坑”“生产上怎么管”四块一次讲干净。我不用那种教科书式的引导就说我实际碰到的情况。如果你在Oracle Linux 8/9上装过软件包被No match for argument坑过或者明明装了PostgreSQL却不知道版本为什么和预期不一致那么这篇文章就是给你准备的。1. AppStream到底是什么从一次失败的dnf install说起大概是一年前我在一台Oracle Linux 8服务器上准备部署一套内部应用需要装PostgreSQL。我按以前CentOS 7的习惯敲下dnf install postgresql-server结果终端直接给我吐了一行Error: Unable to find a match: postgresql-server。我当时的第一个反应是仓库配错了检查了半天dnf repolist发现ol8_appstream明明显示正常可包就是装不上。后来同事提了一句你试试模块流我才第一次认真去看AppStream这套东西。所谓AppStream全称是Application Stream它是Oracle Linux 8/9以及对应的RHEL和CentOS Stream从软件仓库层面引入的一套模块化机制。以前在RHEL 7那个时代一个软件包就是固定一个版本比如PostgreSQL永远只有9.2你想装9.6就得去第三方源甚至要手动编译。到了RHEL 8时代红帽和Oracle把仓库拆成了两层BaseOS负责内核、系统库、核心命令行工具这些底层组件AppStream则承载应用、运行时和框架并且同一个应用可以以模块的形式同时保留多个流每个流就是一个大版本。所以postgresql不是普通的包而是一个模块它下面有10、12、13、16等多个stream选哪个流装的就是哪个大版本。把软件分流的思路其实不难理解。你可以把BaseOS想象成房子的地基和承重墙它求稳几年都不大变AppStream则像是家具和家电你可以根据需求选择不同款式而且同一个空间里新家具旧家具可以共存——当然前提是房东不禁止。Oracle Linux正是靠这套机制让企业用户在不升级整个操作系统的前提下单独给某个中间件或数据库升版本这在以前是不可想象的。不过AppStream的意义不只是多版本这么简单它还给运维带来了一套全新的包管理逻辑。以前yum install就是装上就完事现在你多了一步选择流的动作。这一还加不加默认流的问题后面我想重点讲因为我在生产上吃过亏。2. 玩转模块流查看、启用、切换、安装的完整实操2.1 用dnf module list把地皮摸清楚我第一次接触AppStream时最困惑的是不知道系统里到底有哪些模块、每个模块有什么流。其实DNF早就给了明确入口就一条命令dnf module list。这条命令会列出当前仓库中所有模块和对应的流。输出非常长所以建议加个匹配条件比如dnf module list | grep postgres或者直接dnf module list postgresql。我在Oracle Linux 8.8上跑过一次输出大概是这样的Oracle Linux 8 Applications Stream (x86_64) Name Stream Profiles Summary postgresql 10 client, server [d] PostgreSQL server and client postgresql 12 client, server [d] PostgreSQL server and client postgresql 13 client, server [d] PostgreSQL server and client postgresql 16 client, server [d] PostgreSQL server and client注意最后一列有个[d]代表server是这个模块的默认profile也就是默认安装形态。流的名字前面有command的比如postgresql:10表示10这个流当前没有启用但可以启用如果前面出现[e]表示已经启用。执行dnf module enable postgresql:16后你再执行dnf module list postgresql会发现16前面多了[e]。这一步的操作逻辑很像是先声明我要用哪个版本然后再去装包这样DNF在后续依赖解析时就知道该往哪个方向走。2.2 enable、install、switch-to的正确用法模块启用有两种常见路径一种是我前面说的dnf module enable postgresql:16它只是提前声明要使用的流不装包另一种是直接一步到位dnf module install postgresql:16/server这样模块会被自动启用然后安装server profile下的所有包。我个人的习惯是直接使用module install除非我需要先验证这个流是否存在、和现有软件是否冲突。切换模块流是我后来用得特别多的操作场景通常是想从老版本升大版本比如从PostgreSQL 13切到16。命令是dnf module switch-to postgresql:16。注意这条命令并不仅仅是把模块元数据改一下它实际上会执行依赖重新解析必要时安装新包、移除旧包这个过程中很容易触发冲突。我在下文排错部分会专门讲。还有一个每次都被我忘掉的命令是dnf module reset postgresql它的作用是把模块恢复到未启用状态。如果你只是想临时看看其他流或者想要彻底脱离某个模块版本reset是安全操作。比如你误启用了某个流的默认版本导致其他包报module conflict先reset再重新enable很多怪问题能解决掉。2.3 profile和默认流两个容易被忽略的细节dnf module install postgresql:16/server中的server就是profile。AppStream里的模块通常有多个profile比如client、server、minimal、devel默认profile会用[d]标注。不同的profile决定了你安装这组软件包时到底会带多少组件。举个例子如果我只是做开发联调装client就够了没必要把整套数据库服务端也带到机器上。但在生产环境我强烈建议你看清楚默认profile是什么别装完才发现少装了关键组件。至于默认流就更重要了。Oracle Linux给每个模块设定了一个默认流也就是你不指定stream时DNF默认启用的那个版本。比如Oracle Linux 8上的php默认流可能是7.2你的业务可能要PHP 8.0如果你不显式声明直接装php-fpm你会拿到默认流的包代码跑起来才发现版本不对。我建议所有生产环境安装模块化应用时务必把stream写在命令里不要依赖默认值。默认流不是替你选好的最优解它只是发行版发布当刻的最常见版本。3. 排错实战AppStream最容易翻车的三个场景3.1 No match for argument的根因与解决方案前面提到的No match for argument是新手最容易遇到也会最懵的坑。我在网上看到很多求助帖大家都以为是仓库失效实际上九成的原因是这个包名在AppStream里属于某个模块而且这个模块当前没有启用任何流。DNF在没有显式启用模块时默认不会从模块元数据里解析包。解决办法有三条按我推荐的顺序来执行dnf module list 包名前缀确认这个模块存在并看当前有哪些流、默认流是什么。如果你不关心版本只需要能装上可以直接dnf module enable 模块名:默认流然后再dnf install 包名。如果你要特定版本直接dnf module install 模块名:流/profile一步完成。还有一种特殊情况你确实执行了dnf module list发现模块存在但dnf module enable报错说模块不存在那就可能是仓库元数据没刷新。先执行dnf clean all和dnf makecache再重试。我遇到过几次基本都是镜像源同步延迟导致元数据陈旧。3.2 模块流切换后依赖冲突的完整排查链路这是我真实踩过的一个大坑。当时我把一套系统的Node.js从模块流nodejs:14切换到nodejs:18执行dnf module switch-to nodejs:18结果DNF给我了一大段依赖冲突大致是说nodejs-14的某个子包需要http-parser的libhttp_parser.so.2但18流会把http-parser升到一个只提供libhttp_parser.so.3的版本于是两个版本的.so文件在命名空间里打架了。遇到这种冲突我当时的直觉是不装了回滚然后我执行了很多次dnf module reset和dnf distro-sync都没法干净解决。后来我按下面的链路排查才找到真正原因第一步执行dnf module list --enabled确认系统里已经启用的模块流都有哪些这能帮你看到有哪些模块是相互依赖的。第二步执行dnf repoquery --whatprovides 缺失的依赖比如上面这个libhttp_parser.so.2看这个共享库到底由哪些包提供在哪个模块里。第三步检查dnf module provides因为某些模块会提供特定运行时路径模块之间可能存在隐式约束。第四步直接看dnf history找到之前安装模块的时候做了哪些包变更这样你就能判断新流升包时会不会和旧stream的残余包冲突。最终我发现问题在于我第一次启用nodejs:14时模块的serverprofile把所有相关依赖装成了固定版本导致系统里同时存在http-parser的旧版本残留。解决方法是执行dnf module reset nodejs然后dnf remove掉所有残留的nodejs相关包再dnf module install nodejs:18。注意这里有个前提这台机器上没有其他服务依赖旧版本的Node.js运行时否则不能粗暴reset。3.3 模块流和第三方仓库打起来怎么办在企业环境你难免要加EPEL或者Oracle Linux的ol8_developer_EPEL这类第三方仓库。AppStream最麻烦的地方在于它会把某些软件包藏在模块里第三方仓库却可能提供同名同版本的包。这时DNF会按照仓库优先级和模块元数据来决定装哪个但常常出现一个结果是你启用了模块流可装了以后发现包来源是EPEL而不是AppStream。我的处理经验是关键生产环境不要同时启用多个提供同款应用的外部仓库。比如你需要装Redis就只认AppStream的模块流把EPEL的redis子包通过dnf versionlock锁掉或者干脆禁用EPEL里redis相关的组件。如果你发现模块流安装报错说名下的包在某个第三方仓库中已存在最稳妥的办法是指定模块后明确排除第三方仓库的包dnf module install redis:6/server --excluderedis*。这条命令会把EPEL里所有redis开头的包都排除掉避免DNF解析时摇摆。如果你对仓库优先级有较高要求建议在/etc/dnf/dnf.conf中显式配置module_platform_idplatform:el8这个参数能规范模块解析行为防止某些第三方仓库用非标准模块元数据干扰你的安装。4. 生产环境管理AppStream的实战心得4.1 选流策略不是越新越好要看生命周期前面讲了那么多操作和排错这里我特别想聊一聊生产环境选流的策略。很多新手喜欢选最新版模块流我见过有人为了追求PostgreSQL 16硬是把Oracle Linux 8上整套老业务都切过去结果发现应用层有不兼容。我的建议是先看你现有业务依赖的运行时最低版本再去模块列表里找覆盖这个最低版本且已是稳定状态的流。Oracle Linux的AppStream模块流有的生命周期很短有的则跟着整个操作系统生命周期走。优先选择模块说明里标注了[d]或长期支持的版本不要选刚发布的新流新流往往bug多而且周边配套组件还没完全跟上。4.2 测试发布流程中的模块验证生产上一旦决定了要启用某个模块流就把它当作一次正式变更管理不能心血来潮直接在生产装。我自己的标准流程是这样在测试环境执行dnf module install 模块:流/profile确认安装成功且服务能起。在测试环境执行dnf module list --enabled记录当前启用的模块和流把这作为发布清单的一部分。检查dnf history list确认这次模块变改动了哪些包尤其关注是否有系统库被升级成和BaseOS版本不一致的版本。如果测试环境有监控工具观察至少24小时资源消耗、日志报错确认模块流稳定后再推到生产。生产发布时先把操作系统的/etc/dnf/dnf.conf里的assumeyesFalse保留默认这样DNF在解析模块流时会要求你确认包变更给你一个手动回溯的机会。4.3 回滚方案与版本锁定在AppStream世界里回滚比传统yum时代更麻烦因为模块流会牵动一堆包的版本升降。我建议生产环境启用模块流后配合dnf versionlock插件把关键运行时版本锁住比如把postgresql-server锁在指定版本防止日常dnf update后流内包出现意外升级打破业务兼容性。锁定的命令是dnf install python3-dnf-plugin-versionlock然后dnf versionlock add postgresql-server。如果真要回滚到上一个模块流我先做的是记录当前模块元数据状态执行dnf module list --enabled module-backup.txt并保留dnf history的transaction ID。回滚时尽可能用dnf history rollback transaction-id而不是手动dnf module reset和重装。因为模块流和普通包变更往往交织在同一个transaction里历史回滚能一次性恢复整组包的元数据状态比手工拆解命令更可靠。4.4 我最常用的一套查询命令备忘如果只想记住一部分命令我建议优先记住下面这几个。它们基本覆盖了日常运维80%的模块操作需求dnf module list查看所有模块及流dnf module list --enabled只看已启用的模块dnf module info 模块名:流看该流的详细信息和依赖dnf module enable 模块名:流仅启用模块不装包dnf module install 模块名:流/profile启用并安装dnf module switch-to 模块名:流切换模块流dnf module reset 模块名重置模块到未启用状态dnf clean all dnf makecache刷新元数据解决大部分获取不到模块信息的问题把这套命令背下来之后AppStream在你眼里就不那么神秘了。说到底它只是换了一套包管理元数据组织方式新增了一点版本选择逻辑底层还是DNF那套依赖解析引擎。你只要在动手前明确我到底要哪个流的标志版本绝大多数坑都能避开。我在生产环境摸爬滚打久了现在的体会是AppStream最大的价值不是让你装新版本而是让你在不用重启整个操作系统的情况下慢慢跟上应用生态的进化。可它给你自由的同时也把版本管理的责任还给了你。过去发行版替你决定一切现在你一句话不说清楚DNF就可能替你做一个你不想要的决定。所以从今往后在主机的任何一条安装命令里多敲几个字符把模块名、流版本、profile都写全你就已经比大部分运维少走一个季度的弯路了。
返回列表