资讯详情

资讯详情

RHEL 9 本地源与国内源配置实战:双源共存、优先级与排错

1. 先把“源”这件事拆开看RHEL 9 的包管理到底在干什么上个月帮一个做内网交付的朋友收尾机房里三十多台 RHEL 9 的机器全部不允许出外网装个编译工具链都能卡半天。这种场景绕不开的一件事就是把本地源和国内源同时配起来本地源负责确定性和审计国内源负责覆盖面和更新包。这篇文章就把我在 RHEL 9 上搭本地源和国内源的两套流程完整拆一遍包括 repo 文件里每一行为什么这么写、双源共存时优先级怎么排、以及那些报错信息背后真正的原因。先把结论摆前面本地源和国内源解决的是两个完全不同的问题。本地源解决的是“我手上必须有一套确定的、可复现的软件包集合”国内源解决的是“ISO 里没有的包我总得有地方拿”。很多资料把它们当成二选一来讲实际上生产环境里这两个是互补关系只有把优先级和屏蔽规则安排好才不会出现“本地源装一半、远端源又装个更高版本”的混乱。适合谁看手上有一台能连内网的 RHEL 9 或它的社区重建版需要给一批机器做统一软件基线的人也适合刚接手离线环境、对着dnf install报错不知道怎么下手的新手。下面所有路径和参数都是按常见实践给出的主机名、IP、镜像站域名你按自己环境替换就行结构不用改。1.1 仓库、repo 文件与 dnf 的解析链路RHEL 9 的包管理是 dnf 这一套底层还是 rpm。rpm 只管单个包能不能装、依赖满不满足它不知道去哪里找包dnf 负责拿元数据、算依赖、下载、调用 rpm 落地。所谓“源”本质是一份 repodata 元数据加上一堆 rpm 文件dnf 通过 repo 文件里的baseurl找到 repodata 的位置把repomd.xml及其索引文件拉到本地缓存之后所有依赖计算都在本地做。一个仓库目录长这样以 RHEL 9 的 DVD 为例/mnt/iso/ ├── BaseOS/ │ ├── Packages/ # 实际 rpm 文件 │ └── repodata/ # 元数据dnf 真正读的东西 ├── AppStream/ │ ├── Packages/ │ └── repodata/ ├── media.repo # 介质自带的 repo 模板 └── RPM-GPG-KEY-... # 签名公钥BaseOS里放的是系统启动和运行必须的最小集合AppStream里放的是应用、语言运行时和各种版本化的模块流。RHEL 9 把这两个拆开是有意为之BaseOS 的包生命周期跟着整个大版本走AppStream 的包可以按 module stream 给多个版本两者更新节奏不一样混在一个仓库里会互相拖累。搞明白这个拆分后面配 repo 时就不会疑惑“为什么我要写两段而不是一段”。repo 文件放在/etc/yum.repos.d/后缀必须是.repodnf 启动时会把这个目录下所有.repo文件全读一遍再叠加/etc/dnf/dnf.conf里的全局配置。所以排查问题的第一步永远是dnf repolist -v看清楚 dnf 到底认了几段仓库、每段的 baseurl 解析成了什么。1.2 为什么我坚持“本地源 国内源”双轨而不是二选一只用国内源的问题是内网机器根本出不去而且就算能出去远端仓库的包版本是滚动的今天装的 gcc 和下周装的 gcc 可能不是同一个 build出问题时你没法复现。做交付和做审计的团队对这一点特别敏感同一批机器必须能证明“我装的这一套包来自同一个被验证过的介质”。只用本地源的问题是覆盖度不够。DVD 里的包大概几百到一千多个实际业务里要装的容器运行时、监控组件、开发库很多不在里面。而且安全更新是持续发布的ISO 发版那天之后的所有补丁本地介质里一律没有。你要么每季度重新做一次介质要么就得有个远端源兜底。所以我这边的标准做法是三层第一层本地 ISO 源priority1负责基础系统第二层内网自建的同步源用createrepo_c做的priority10负责业务定制包第三层国内镜像源priority99负责兜底和获取更新。三层的数字越小优先级越高dnf 在同一批候选包里会优先从数字小的仓库选只有在高优先级仓库找不到这个包名时才落到下一层。这个分层的关键在于“包名级别”的优先而不是“版本级别”。也就是说如果本地源里有nginx那么即使远端源有更新的nginxdnf 默认还是装本地那个。这正是我们要的确定性。反过来如果本地源里压根没有nginxdnf 会老老实实去远端找不会因为优先级高就报“找不到”。理解了这条规则后面的所有配置才有意义。2. 动手前的盘点RHEL 9 的源有哪几条路可走在敲命令之前先要把自己手上有什么、能拿到什么盘清楚。RHEL 9 这个体系下“源从哪来”其实有三条路每条路的授权边界和技术代价都不一样选错了后面会一直别扭。第一条是官方订阅源。装了订阅的机器subscription-manager会自动在/etc/yum.repos.d/redhat.repo里生成一堆仓库定义baseurl指向官方的 CDN 地址带证书认证。这条路最省事但要求机器能出网、订阅状态正常内网隔离环境下基本走不通。而且这个redhat.repo是自动生成的你手动改它下次刷新订阅就被覆盖这是很多人踩过的坑。第二条是用安装介质当本地源。这是离线环境唯一可靠的路。RHEL 9 的 DVD ISO 里 BaseOS 和 AppStream 都是完整可用的仓库挂上去就能装。缺点是版本固定在介质发版那天后续更新包拿不到。第三条是换成社区重建版的仓库地址。RHEL 的下游 rebuild 发行版在包结构上和 RHEL 9 高度一致它们的仓库地址可以直接被 RHEL 9 的 dnf 消费。这条路在法律和授权上要自己评估清楚商业发行版的订阅权益和技术支持边界是你自己负责的我不在这里替任何人做决定。技术上要注意的是混用之后redhat-release这类包不要被覆盖否则系统版本识别会出问题。2.1 三条路线的取舍对比路线覆盖度更新及时性内网可用主要约束官方订阅源完整跟随官方发布节奏否需要订阅有效且有出网通道ISO 本地源仅介质内包固定在发版日是更新包拿不到需定期换介质社区重建版仓库完整跟随上游重建节奏部分可用授权与兼容性需自行评估实际项目里我基本是“ISO 本地源打底 重建版仓库兜底”这个组合。本地源保证了系统能起来、能装编译工具、能满足审计要求兜底源负责那些 ISO 里没有的业务依赖。两层都用 file 协议或 https 协议dnf 不需要任何额外插件就能处理。2.2 环境与前置条件清单开工前把这几项确认掉能省掉后面一大半的排查时间。系统版本用cat /etc/redhat-release确认内核架构用uname -m确认现在基本都是x86_64ARM 机器上是aarch64两者仓库路径不同混了就是 404。磁盘空间看一眼df -h /vardnf 的缓存目录/var/cache/dnf在大批量装包时会涨到几个 GB空间不够会在解包阶段报No space left on device这个错误和网络无关但很多人第一反应是去查网络。网络方面能出网的话先测一下镜像站的连通性和证书链curl -I https://镜像站域名看返回码。时间同步一定要做timedatectl看System clock synchronized是不是yeshttps 源对时间偏差很敏感偏差超过几分钟就会报证书校验失败而报错信息里写的是SSL certificate problem很容易被误判成镜像站挂了。cat /etc/redhat-release uname -m df -h /var timedatectl | grep -i synchronized dnf --versiondnf --version输出里能看到版本号RHEL 9 上是 dnf4 这一代config-manager子命令需要装dnf-plugins-core才有。这个包如果本地源里没有你就得先想别的办法把它弄进去是个典型的“先有鸡还是先有蛋”问题后面会讲怎么破。3. 本地源实操从 ISO 到可用的 BaseOS/AppStream本地源这块我见过太多人图省事直接把 ISO 里的 rpm 全拷到一个目录里然后createrepo_c重新生成元数据。这个做法能跑但没必要而且容易把 GPG 签名校验搞乱。ISO 里现成的 repodata 是官方签过名的直接挂载复用最干净。3.1 挂载 ISO 并确认仓库目录结构先把 ISO 传到机器上建议放在一个不会被随手清理的目录比如/opt/iso/mkdir -p /opt/iso /mnt/iso # 假设 ISO 已经放在 /opt/iso/ 下 mount -o loop,ro /opt/iso/rhel-9.4-x86_64-dvd.iso /mnt/iso ls /mnt/isoloop,ro这两个参数要写全。loop是把文件当成块设备挂ro是只读。只读这个不是可选项ISO 是只读文件系统写成可写挂载在某些内核版本上会给你一个警告然后照样只读不如一开始就写明白。挂上之后ls应该能看到BaseOS、AppStream、media.repo这几项。如果只看到Packages而没有repodata说明你拿到的是精简版或者被别人重新打包过的介质这种介质不能直接当源用得自己createrepo_c。确认一下 repodata 里的关键文件在不在ls /mnt/iso/BaseOS/repodata/ | head # 应该能看到 repomd.xml 以及一堆 hash 开头的 xml.gz / sqlite 文件repomd.xml是整个仓库的入口dnf 第一步只拉这个文件里面记录了各个索引文件的位置和校验值。这个文件缺失或者损坏dnf makecache会直接报Failed to download metadata for repo。3.2 写 repo 文件每一行参数为什么这么填RHEL 9 的 DVD 里其实自带了一个media.repo模板可以直接拷出来改省得手写。我个人习惯还是自己写一份名字清楚、注释清楚交接给别人的时候不用猜。vi /etc/yum.repos.d/local-iso.repo[local-baseos] nameLocal ISO - BaseOS baseurlfile:///mnt/iso/BaseOS/ enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release metadata_expire-1 cost500 [local-appstream] nameLocal ISO - AppStream baseurlfile:///mnt/iso/AppStream/ enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release metadata_expire-1 cost500逐行说一下为什么。baseurl用的是file:///三个斜杠这个写法坑过很多人。两个斜杠的file://mnt/iso会被解析成主机名mnt加路径/isodnf 会去尝试网络访问然后超时。三个斜杠才是“本机绝对路径”的标准写法。另外结尾那个/建议带上某些版本的 dnf 在拼接相对路径时对结尾斜杠比较敏感。gpgcheck1是必须的。离线环境不等于可信环境ISO 在传输过程中损坏、被人替换都靠 GPG 校验兜住。gpgkey指向的路径是 RHEL 系发行版预置的公钥位置ls /etc/pki/rpm-gpg/看一眼就知道你机器上到底有哪几个 key名字对不上就改成实际的。metadata_expire-1表示元数据永不过期。这一条专门针对静态介质因为 ISO 内容不会变让 dnf 每次去比对时间戳纯属浪费。如果不开这一项dnf 在缓存过期后会尝试重新拉 repodata虽然 file 协议本地读取很快但那种“每次操作都多几百毫秒”的体感很烦。cost500是成本参数数字越小成本越低。它的作用场景是当多个仓库都有同一个包、且包版本相同时dnf 会优先从 cost 低的那个下载。注意 cost 和 priority 是两回事priority 决定“选哪个包”cost 决定“同一个包从哪下”。很多人把它们搞混结果配了个 priority 却发现版本还是被覆盖。3.3 开机自动挂载与 SELinux 上下文手动mount的挂载点重启就没了机器一重启 dnf 就全线报错。这一步必须做持久化两种做法。第一种写/etc/fstabecho /opt/iso/rhel-9.4-x86_64-dvd.iso /mnt/iso iso9660 loop,ro 0 0 /etc/fstab mount -a写完之后一定要mount -a验证一次。fstab 写错最怕的是重启后进 rescue所以改完立刻验证是习惯。mount -a没报错、df -h /mnt/iso能看到挂载点才算过。第二种用 systemd 的 mount unit适合对启动顺序有要求的场景。文件名要按挂载路径编码/mnt/iso对应mnt-iso.mount# /etc/systemd/system/mnt-iso.mount [Unit] DescriptionMount RHEL9 DVD ISO [Mount] What/opt/iso/rhel-9.4-x86_64-dvd.iso Where/mnt/iso Typeiso9660 Optionsloop,ro [Install] WantedBymulti-user.targetsystemctl daemon-reload systemctl enable --now mnt-iso.mount systemctl status mnt-iso.mountSELinux 这块file://方式读本地文件基本不会触发拒绝因为 dnf 是以unconfined_r跑的读iso9660_t上下文没问题。真正出问题的是后面要把这个目录通过 HTTP 发布出去的时候上下文不对会直接 403日志在/var/log/audit/audit.log里能看到avc: denied。这个坑留到 3.4 节细说。3.4 局域网共享用 HTTP 发布本地源只有一台机器用挂载就够了。要给一个机房几十台机器用每台都传一份 ISO 不现实这时候把本地源通过 HTTP 发布出去更省事也更好管理——ISO 换版本只需要动服务端一处。dnf install -y httpd systemctl enable --now httpd mkdir -p /var/www/html/repo mount --bind /mnt/iso /var/www/html/repo semanage fcontext -a -t httpd_sys_content_t /var/www/html/repo(/.*)? restorecon -Rv /var/www/html/repo firewall-cmd --permanent --add-servicehttp firewall-cmd --reload这里有两个必须讲的细节。第一个是mount --bind的 SELinux 上下文。bind mount 默认会继承源目录的上下文而/mnt/iso是iso9660_thttpd 根本读不了。semanage fcontext加restorecon是在策略里登记规则并刷新标签但 bind mount 的标签是在挂载时确定的restorecon对 bind mount 出来的节点可能不生效。更稳的写法是挂载时直接指定上下文mount --bind -o contextsystem_u:object_r:httpd_sys_content_t:s0 /mnt/iso /var/www/html/repo或者干脆别用 bind mount用rsync把 ISO 内容同步到磁盘目录里机器硬盘够的话这是最省心的方案SELinux 和权限都是普通文件路径那一套没有特殊性。第二个是持久化。bind mount 也不进 fstab 就会丢要写的话用echo /mnt/iso /var/www/html/repo none bind,contextsystem_u:object_r:httpd_sys_content_t:s0 0 0 /etc/fstab服务端配好之后客户端只需要把baseurl从file:///mnt/iso/BaseOS/换成http://服务端IP/repo/BaseOS/其余不变。局域网内 HTTP 拉包的速度通常在几百 MB/s 量级比走公网镜像快得多几十台机器同时装包也不会互相拖慢。注意httpd默认的DocumentRoot是/var/www/html如果你把仓库放到别的路径记得改/etc/httpd/conf.d/下的配置或者加个Alias否则访问是 404 而不是 403排查方向完全不一样。4. 国内源实操把远端仓库配稳、配快本地源解决的是“有没有”国内源解决的是“快不快、全不全”。这一节的配置思路和本地源不太一样本地源追求确定性远端源追求可用性和容错。4.1 baseurl 写法与 $releasever/$basearch 的坑一份标准的远端仓库配置长这样[remote-baseos] nameRemote - BaseOS baseurlhttps://mirrors.example.com/repo/rhel9/BaseOS/$basearch/os/ enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release metadata_expire3600 skip_if_unavailable1 [remote-appstream] nameRemote - AppStream baseurlhttps://mirrors.example.com/repo/rhel9/AppStream/$basearch/os/ enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release metadata_expire3600 skip_if_unavailable1$basearch由 dnf 自动替换成x86_64或aarch64这个变量基本不会出问题。$releasever才是真正的雷区。RHEL 9 上$releasever的值来自redhat-release包的版本多数情况下是9但有些环境里会被解析成9.4这种带小版本号的形式。如果镜像站的目录结构是按大版本组织的rhel9/而不是rhel9.4/$releasever一展开就 404。判断方法很简单dnf repolist -v会把每个仓库的 baseurl 展开后的完整地址打出来直接看就行。如果发现展开结果不对两种修法一是把 baseurl 里的$releasever写死成9二是加一行releasever9覆盖[remote-baseos] ... releasever9我更倾向写死少一层变量替换就少一个排查点。代价是将来升级大版本的时候得记得手动改这个写进自己的运维清单里就行。metadata_expire3600给远端源用一小时过期一次。远端仓库内容是滚动的缓存太久会拿不到新包缓存太短又会让每次dnf操作都去拉一遍元数据几十兆的 repodata 拉一次好几秒。一小时是个比较平衡的值批量部署前手动dnf makecache一次后面一小时内的操作都是本地读缓存。skip_if_unavailable1是容错开关。远端镜像站偶尔会有单个仓库同步延迟某些子目录暂时不存在这种情况下 dnf 默认会整个操作失败加上这一项坏掉的仓库会被跳过并给个警告其他仓库照常工作。这个参数在生产环境强烈建议开尤其是把远端源当兜底的时候兜底源本身不稳定不该拖垮整个安装流程。4.2 GPG 校验与密钥导入远端源的签名校验和本地源是同一套机制区别在于你需要确认公钥文件确实来自可信渠道。用发行版预置的/etc/pki/rpm-gpg/下的 key 最稳妥因为那是随系统安装包一起装进去的链路可追溯。如果用的是社区重建版的仓库它们的签名 key 名字不一样需要单独导入rpm --import /etc/pki/rpm-gpg/对应的KEY文件导入之后用rpm -qa gpg-pubkey*能看到已导入的 key 列表。这里有个容易忽略的点rpm --import导入的 key 和 repo 文件里gpgkey指定的 key 是两套机制。rpm --import是把 key 装进 rpm 数据库全局生效gpgkey是让 dnf 在这个仓库的包校验时使用指定 key。两者都配了不冲突但如果只配了gpgkey而指向的文件不存在报错是Public key for xxx.rpm is not installed看起来像签名问题实际是路径写错了。真遇到过签名校验失败又急着装包的情况临时手段是gpgcheck0或者命令行加--nogpgcheck。我的建议是只在自己完全清楚包来源的时候这么干而且干完立刻改回来别留在配置文件里。离线环境里“临时关一下”最后变成“一直关着”的情况太常见了。4.3 关闭 rhsm 仓库管理RHEL 特有这一节是 RHEL 独有的坑用社区重建版的可以跳过。如果你在 RHEL 9 上手动写了 repo 文件指向第三方地址会发现过一段时间配置就“失灵”了dnf repolist里冒出一堆你没见过的仓库或者你改的 baseurl 被改回去了。原因在subscription-manager的rhsm插件它每次执行都会根据订阅状态重新生成/etc/yum.repos.d/redhat.repo这个文件里的仓库定义优先级很高而且会覆盖同名仓库。要彻底避免这个干扰可以把 rhsm 的仓库管理关掉subscription-manager config --rhsm.manage_repos0关掉之后redhat.repo不再被自动生成你手写的 repo 文件就稳定了。如果机器本来就没有有效订阅这一步是必须的否则 dnf 会因为redhat.repo里那些指向官方 CDN 的仓库全部失败而拖慢每一次操作。需要说清楚的是这个操作只是停止自动生成仓库定义不改变订阅状态本身。商业发行版的授权边界和技术支持范围你自己按合同评估我这里只讲技术动作。4.4 加速参数cache、超时、并发远端源慢多数不是带宽问题是参数配得保守。/etc/dnf/dnf.conf里这几项值得调[main] gpgcheck1 installonly_limit3 clean_requirements_on_removeTrue max_parallel_downloads10 timeout30 retries3 keepcache1max_parallel_downloads默认是 3改成 10 之后批量装包的耗时能砍掉三分之一以上尤其是在装桌面环境这种几百个包的场景里差别很明显。timeout30和retries3是给不稳定链路准备的默认值偏保守一遇到瞬时抖动就整批失败重试两次往往就过去了。keepcache1这个选项有争议。开着的好处是同一个包重复装比如给多台机器做镜像不用重复下载/var/cache/dnf里的 rpm 可以直接复用坏处是磁盘会一直涨需要定期dnf clean packages。我这边离线环境是开着的配个 cron 每周清一次包缓存元数据缓存保留。# 每周日凌晨清理包缓存保留元数据 0 3 * * 0 root /usr/bin/dnf clean packages /dev/null 215. 双源共存优先级、屏蔽与模块流配完两套源真正的难点才来怎么让它们按你想要的顺序工作。这一节是整个搭建过程里最容易出问题的地方因为 dnf 的选择逻辑是分层的不同参数作用在不同的层上。5.1 priority 与 cost 的真实差别先把三层逻辑讲清楚。第一层是“仓库里有没有这个包名”。dnf 会先用各仓库的元数据建一个虚拟的包索引然后按包名去找。这一步跟 priority 无关找不到就是找不到。第二层是“同一个包名在多个仓库里都有选哪个版本”。这一层才轮到priority。priority默认值是 99数字越小优先级越高。如果仓库 A 的 priority 是 1、仓库 B 是 99那么即使 B 里有更新版本的同一个包dnf 也会选 A 里的版本。第三层是“选定了版本从哪个仓库下载”。这一层由cost决定cost默认 1000数字越小越优先。它的作用是在包内容和版本完全相同时优先从成本低的源下载比如优先走局域网 HTTP 而不走公网。所以正确的配法是本地源priority1、cost500内网同步源priority10、cost800远端源priority99、cost1000。这样本地源既在版本选择上占优又在下载路径上占优双重保障。[local-baseos] ... priority1 cost500要提醒的是priority这个参数在老版本 yum 上需要插件支持在 RHEL 9 的 dnf4 上是内建支持的直接写就行不用装额外插件。这一点很多老资料没更新还在教人装yum-plugin-priorities在 RHEL 9 上是多余的。5.2 exclude 与 module_hotfixes 的使用边界有些包是必须锁死不动的典型的是kernel、redhat-release、systemd这几个。内核跟第三方仓库的驱动模块有严格版本对应关系被远端源偷偷升上去机器重启就起不来。锁的方式是在远端源里加exclude[remote-baseos] ... excludekernel* redhat-release* systemd* glibc*exclude支持通配符可以写多个用空格分隔。写在哪个仓库里就只对该仓库生效写在[main]段里则全局生效。我倾向写在各仓库里全局的exclude一旦漏了某个包排查起来很麻烦。module_hotfixes1是另一个常被误用的参数。RHEL 9 的 AppStream 里有模块流module streamdnf 在处理有模块元数据的仓库时会把非模块化的包过滤掉只留模块里的。当你的本地源是普通仓库、远端源有模块元数据时混合使用会出现“本地源里的包突然看不到了”或者No available modular metadata for modular package这种报错。module_hotfixes1的作用是让这个仓库里的包绕过模块过滤当成普通包处理。它适合用在你自己用createrepo_c生成的定制仓库上不适合无脑加在远端源上因为远端源的模块依赖关系是官方设计好的绕过去之后 dnf 的依赖解析可能给你拼出一套不兼容的组合。模块这块的日常操作就三条记一下够用dnf module list # 看有哪些模块和流 dnf module list nginx # 看某个包的可用流 dnf module reset nginx # 把模块状态重置回默认 dnf module enable nginx:1.22 # 显式指定版本流注意dnf module reset不会卸载已安装的包它只是把“用户选择的流”清掉。切换流之后要配合dnf distro-sync才会真正把包降到目标流的版本这一步在做之前一定要确认清楚影响范围。6. 常见问题排查实录与速查表下面这些报错基本覆盖了我这几年在离线环境里遇到的九成情况。每条都写清楚“报错长什么样”“真正原因是什么”“怎么验证”“怎么修”按这个顺序排查比漫无目的地改配置快得多。6.1 五类高频报错逐条拆解第一类Failed to download metadata for repo xxx: Cannot download repomd.xml。这是最常见的但原因有四五种。先curl -I baseurl/repodata/repomd.xml看返回码404 说明路径结构不对要么$releasever展开错了要么结尾斜杠多了或少了403 说明服务端有访问控制本地源的话八成是 SELinux 上下文问题超时说明网络不通或者镜像站挂了。dnf repolist -v能看到展开后的完整地址这是最快的定位手段。第二类Public key for xxx.rpm is not installed。这不是网络问题是签名校验没过。分三种情况gpgkey路径写错文件不存在key 文件存在但不是签这个包的 key用错发行版的 key 了包本身被重新打包过签名丢失。用rpm -qp --qf %{SIGPGP:pgpsig}\n -p 包文件看包实际用的 key ID再和rpm -qa gpg-pubkey*里的对比一眼就知道对不对。第三类Error: Cannot find a valid baseurl for repo: xxx。这个和第一类看着像实际是 dnf 连 baseurl 都没解析出来。常见原因是 repo 文件里写了mirrorlist而不是baseurl或者两个都写了但 mirrorlist 先被解析且失败了。把mirrorlist那一行注释掉只留baseurl试试。第四类No available modular metadata for modular package。这就是 5.2 节说的模块问题。临时解法是dnf clean all dnf makecache重建缓存如果还不行看是不是某个仓库缺 modular metadata用dnf module list确认哪些仓库参与模块解析必要的时候用--disablerepo把那个源临时排除。第五类装到一半报No space left on device。这个不用查网络直接df -h。/var/cache/dnf和/var/lib/rpm是两个增长点前者清dnf clean packages后者是 rpm 数据库别乱动。另有种情况是/var单独分区且容量给小了这个只能在规划阶段解决。6.2 一套十分钟定位法每次遇到 dnf 出问题我固定按这五步走一般十分钟内能定位。第一步看仓库状态dnf repolist all列出所有仓库的启用/禁用状态确认你期望启用的那几个确实是enabled很多人写了 repo 文件但里面enabled0自己忘了改。第二步看连接和路径dnf repolist -v把每段的 baseurl 展开结果打出来对着检查路径拼接对不对。这一步能解决掉大半的 404 问题。第三步清缓存重来dnf clean all dnf makecache。缓存脏了会导致元数据和实际仓库对不上表现是“明明仓库里有这个包就是搜不到”。第四步单仓库验证用dnf --disablerepo* --enablerepolocal-baseos install 包名只启用一个源试装。这一步的价值在于排除多源干扰把问题范围从“一堆源”缩到“一个源”。第五步看日志/var/log/dnf.log和/var/log/dnf.rpm.log里有完整的执行记录和错误堆栈比终端上刷过去的那几行信息全得多。SELinux 相关的问题去/var/log/audit/audit.logausearch -m avc -ts today能筛出当天的拒绝记录。现象最可能原因验证命令修复动作404 on repomd.xml路径或版本变量错误dnf repolist -v修正 baseurl写死 releasever403 Forbidden服务端权限或 SELinuxcurl -I url修上下文或 httpd 配置GPG 校验失败key 不匹配或路径错rpm -qp --qf %{SIGPGP:pgpsig}导入正确 key模块元数据冲突模块化仓库混用dnf module listreset 模块或加 module_hotfixes磁盘写满缓存或分区过小df -hdnf clean packages扩容操作极慢仓库过多或缓存配置差dnf repolist all禁用无用源调 parallel downloads7. 我踩过的坑与几条实操心得先说一个最容易被忽略的点copr 那类第三方仓库千万别在生产环境随手加。dnf config-manager --add-repo用起来太方便了一条命令就加上但这些源里的包很多是个人打包的签名策略、依赖声明、构建环境都没保证。我在一个项目里吃过亏为了装一个新版本的某个工具加了第三方源结果它顺带把系统的openssl-libs升了个小版本另一台依赖特定行为的老应用当场起不来。后来我定了个规矩第三方源一律只在测试机用生产环境要么自己用createrepo_c重打包成内网源要么就别装。第二个是dnf clean all之后一定要makecache。这两个命令很多人当成一回事其实clean all只删缓存不重建。删完直接装包dnf 会重新拉元数据但如果那次拉取因为网络抖动失败了一半缓存里就是残缺状态后续操作会报各种奇怪错误。分开执行能让你清楚看到“重建成功”这个信号。第三个是关于ISO 版本的定期更新。本地源最大的问题不是技术难度是容易“配完就不管了”。我见过跑了两年没换过介质的机器系统里装的还是发版那天的包安全补丁一个没有。建议把介质更新写进季度任务换 ISO 的时候流程很简单新 ISO 传到/opt/iso/改/etc/fstab里的文件名umount旧挂载点mount -adnf clean all dnf makecache。五分钟的事但需要有人记得做。第四个是内网 HTTP 源的容量规划。一开始只想着发布 BaseOS 和 AppStream后来业务部门要装一堆开发包又加了 EPEL 类仓库目录越堆越大。建议一开始就把/var/www/html/repo/放在独立的大容量分区上或者挂个 NFS。RHEL 9 的 DVD 解压后大概十几个 GB加上后续同步的仓库预留 100GB 比较从容。用 bind mount 的话注意源目录和挂载点要在同一分区跨分区的 bind mount 在扩容时会有额外的搬运工作。最后分享一个小技巧用于快速确认“某个包到底来自哪个源”dnf repoquery --location nginx dnf repoquery --qf %{name}-%{version}-%{release} from %{repoid}\n nginx第二条命令的输出直接带上 repoid一眼就能看出 dnf 最终从哪个仓库选的这个包。排查“为什么装的是老版本”这类问题时比翻配置文件快得多。如果发现 repoid 不是你期望的那个回到第 5 节检查 priority 配置基本都能对上。这套东西配下来一个小型离线机房从裸机到能批量装包我实际操作的耗时大概在两小时以内其中一小时是等 ISO 传输和第一次makecache。剩下的时间基本花在确认网络路径和 SELinux 上下文上这也是我认为新手最容易卡住的地方——技术本身不难难的是报错信息和真实原因之间隔了一层。把上面那张速查表对着用能省掉很多来回试的时间。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →