资讯动态

RHEL 9软件源配置:本地源与国内镜像源实战排错

发布时间:2026/10/27 3:34:37 来源:尧图企业网站定制
1. RHEL 9 软件源的三层结构先搞清装完红帽企业版 9后文统一简称 RHEL 9第一次敲dnf install就卡住的人不在少数。屏幕上一行There are no enabled repositories或者更常见的Cannot download repomd.xml。这时候多数人第一反应是网络不通但实测下来八成以上的情况压根不是网络的事而是软件源本身就没配对。本地源和国内镜像源的配置本质上就是把「包从哪里来」这件事说明白让 dnf 知道该去哪个目录或哪个域名底下找元数据。这篇文章围绕本地源和国内镜像源的配置展开把 RHEL 9 这套源结构、repo 文件的每个字段、清缓存与优先级这些容易翻车的点全拆一遍从没碰过 RHEL 的新手能照着抄被订阅提示烦过的老手也能捞到几条省时间的经验。RHEL 9 和早年的 CentOS 7 最大的差别是仓库被纵向切成了几块而且默认那个「订阅源」在没绑账号的情况下是关着的。所以你会看到一个很奇怪的现象系统明明装好了/etc/yum.repos.d/里躺着几个.repo文件dnf repolist却返回一个空列表。这不是 bug是设计。搞不清楚这层关系后面所有配置都是瞎猜。1.1 订阅源、本地源、国内镜像源各管什么把 RHEL 9 的取包渠道分成三层思路立刻就清楚了。第一层是官方订阅源也就是subscription-manager注册之后才会激活的那批仓库走的是官方 CDN包最新、签名最规范但需要账号授权。第二层是本地源来源通常是随系统一起拿到的 DVD 安装介质ISO 文件把它挂载到某个目录直接告诉 dnf「去这个目录找包」。第三层是国内镜像源把baseurl指向就近的镜像站点解决官方源拉取慢、或者压根没有订阅资格的问题。这三层不是互斥的实际生产里经常是混着用本地源作为兜底保证在完全断网的内网环境里也能装基础包国内镜像源作为主力负责日常的更新和新软件安装。我见过不少环境是这样的组合——本地源cost设得很高优先级最低镜像源设得低只有当镜像源连不上时才回落到本地介质。这个思路值得借鉴因为它把「可用性」和「新鲜度」这两件事分开了。源类型典型来源优点局限适用场景订阅源官方账号注册后自动生成包最全、更新最快、签名完备需要授权未注册时全部不可用有正规授权的生产机本地源DVD 安装 ISO / 自建目录完全离线可用、速度取决于磁盘内容冻结在介质制作那一刻内网隔离环境、装机兜底国内镜像源公共镜像站点的 HTTP/HTTPS 路径免授权、速度快、元数据较新依赖外网、路径拼错就全废有外网但订阅不便的机器需要提醒一点RHEL 9 的官方二进制仓库本身是需要订阅的公开镜像站上能直接拿到的多半是与之兼容的下游发行版仓库目录名往往是rocky、almalinux、centos-stream这类。用它来装通用软件包绝大多数情况下没问题但涉及内核模块、专有驱动、需要严格版本对齐的商业软件时兼容性风险得自己评估。我的建议是测试和开发环境随便用核心生产库要谨慎尤其是那些对glibc、openssl版本敏感的程序。1.2 为什么 RHEL 9 把 BaseOS 和 AppStream 拆成两半这个拆分是理解 RHEL 8 之后所有源配置问题的钥匙。RHEL 9 把软件包分进了两个必装仓库BaseOS放的是操作系统的地基——内核、systemd、glibc、基础运行库、dnf自身。这里面的包生命周期跟整个发行版绑定版本基本不动。AppStream放的是应用层的东西——数据库、Web 服务器、各种语言运行时、开发工具。这里的包支持模块流module stream同一个包可以有多个版本并存比如php:8.0和php:8.1两个流你启用哪个就用哪个。拆分带来的直接后果是你至少得配两个仓库条目一个指 BaseOS一个指 AppStream少配一个就会出现「明明装了 DVD却装不上httpd」这种怪事。另外还有一个crbCodeReady Builder在老版本里叫PowerTools放的是开发用的额外库默认不开编译某些源码包时才需要。搞清这三个名字的分工后面写baseurl时就不会把路径拼串。模块流这个机制还埋了一个坑AppStream 里的包默认启用的是「默认流」。你想装某个非默认版本必须先dnf module enable或者dnf module reset再选流。很多人dnf install nginx之后发现版本不对然后到处找镜像源的问题其实源一点毛病没有是模块流没切。2. 动手前的地基系统自检与介质准备配置源这件事最忌讳上来就vi一个文件然后往里面灌内容。先把当前系统状态摸清楚能省掉后面一大半的排查时间。我吃过一次亏在一台别人配过的机器上折腾了四十分钟最后发现是/etc/yum.repos.d/里有个残留的.repo文件baseurl指向一个早就下线的内网地址每次dnf都要等它超时才继续。这种坑两条命令就能提前发现。2.1 三条命令摸清当前仓库底细第一条看仓库列表和状态dnf repolist all加all是关键不加只显示已启用的。输出里每一行会标明enabled还是disabled能一眼看出有几个仓库、分别叫什么 ID。第二条看具体配置来源dnf repoinfo它会打印每个仓库的Repo-id、Repo-baseurl、Repo-expire、Repo-filename。Repo-filename这一项特别有用直接告诉你这个仓库是从哪个.repo文件的第几段读出来的排查「配置改了但没生效」时按图索骥。第三条确认系统版本和架构这决定了baseurl里的版本号和目录层级cat /etc/redhat-release uname -mRHEL 9 的小版本号9.0、9.2、9.4在配镜像源时一般不用写进去仓库路径里的版本位通常就写9小版本靠元数据自然更新。但配本地源时必须对得上因为你的 ISO 是 9.2 的 DVD里面就只有 9.2 那一批包。uname -m输出x86_64还是aarch64直接决定baseurl最后那段是/x86_64/os/还是/aarch64/os/写错就是 404。顺带提一句检查一下/etc/yum.repos.d/目录里到底有什么ls -l /etc/yum.repos.d/如果看到redhat.repo这类由订阅管理工具自动生成的文件先别急着删记下名字后面要用--disablerepo临时绕过它比直接删除安全得多。2.2 安装介质到手后的三种挂载姿势本地源的前提是把 ISO 里的内容「变成」文件系统里的一个目录。有三种做法各有适用场景。第一种临时 loop 挂载最常用mkdir -p /mnt/dvd mount -o loop,ro /root/rhel-9.2-x86_64-dvd.iso /mnt/dvd-o loop让内核把这个文件当成块设备ro只读挂载避免误操作污染介质内容。挂载完ls /mnt/dvd应该能看到BaseOS、AppStream、EFI这几个目录如果只有一堆散文件说明 ISO 结构不对或者挂错了。第二种物理光驱挂载虚拟机里挂了虚拟光驱的场景mount /dev/sr0 /mnt/dvd设备名可能是/dev/sr0也可能是/dev/cdrom软链接lsblk看一眼就清楚。第三种直接把 ISO 内容解压到硬盘适合没有 loop 设备支持或者想改内容的情况mkdir -p /opt/dvdroot tar -xf /root/rhel-9.2-x86_64-dvd.iso -C /opt/dvdroot注意用tar解 ISO 对某些镜像可能不完整尤其带 Joliet/Rock Ridge 扩展的更稳妥的是7z x或者干脆挂载后cp -a拷出来。解压方式的好处是脱离了 ISO 文件本身别人问你baseurl在哪你指一个目录就行不用担心 ISO 被挪走或删掉。提示挂载点不要用/media下自动生成的目录名那个名字带 UUID 之类的后缀重启后会变写进baseurl就是定时炸弹。自己建一个固定的/mnt/dvd最省心。三种方式选哪种看你的环境。临时装几个包loop 挂载足够长期使用的内网机器解压到硬盘 fstab不管最稳虚拟机大批量部署虚拟光驱 自动化脚本更顺手。3. 本地源配置从挂载点到 dnf repolist 亮绿灯介质挂好了接下来就是写.repo文件。这一步的难点不在语法而在baseurl的层级要对。ISO 里BaseOS目录下面还有一层repodatabaseurl要指到repodata的父目录也就是.../BaseOS而不是.../BaseOS/repodata。这个规则对所有类型的仓库都成立baseurl指向含repodata的那个目录。3.1 手写 repo 文件字段逐个拆开讲新建/etc/yum.repos.d/local-dvd.repo内容如下[local-baseos] nameLocal DVD BaseOS baseurlfile:///mnt/dvd/BaseOS enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release cost1000 [local-appstream] nameLocal DVD AppStream baseurlfile:///mnt/dvd/AppStream enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release cost1000逐项说。[local-baseos]是这个仓库的 ID全局唯一dnf --enablerepo后面跟的就是它别用中文、别带空格否则脚本里引用会很难受。name是给人看的描述随便写但建议写清楚是本地还是镜像出问题时一眼分辨。baseurlfile:///mnt/dvd/BaseOS是核心file://后面跟三个斜杠——前两个是协议分隔符第三个才是根目录的斜杠写成file://mnt/dvd会被解析成主机名mnt报错信息还挺误导人。enabled1控制默认是否启用0就是不启用但保留配置需要时用dnf --enablerepolocal-baseos临时打开。gpgcheck1开启签名校验gpgkey指向公钥文件。本地介质的公钥就在系统里路径/etc/pki/rpm-gpg/下能找到文件名各发行版略有差异ls一下挑对应的那个。如果你用的是兼容发行版的安装介质公钥文件名会是另外一套比如带rocky、alma字样别照抄看着改。cost1000这个字段值得单独说。dnf 默认cost1000数值越小优先级越高。给本地源设一个较大的值意味着「有别的源能用时先别用我」这就实现了前面说的兜底逻辑。如果你的机器压根没有外网这个值设不设都无所谓。3.2 开机自动挂载与 fstab 的写法临时mount挂的东西重启就没了baseurl里的路径变成空目录dnf报「repodata 不存在」。解决办法是写进/etc/fstab/root/rhel-9.2-x86_64-dvd.iso /mnt/dvd iso9660 loop,ro 0 0几个要点。文件系统类型写iso9660不要写auto实测auto在某些内核上识别不出来。loop和ro之间用逗号不要空格。最后两个0 0是 dump 和 fsck 选项ISO 不需要检查都写 0。改完执行mount -a验证没报错再用df -h /mnt/dvd确认挂上了。千万别跳过mount -a直接重启fstab写错会导致系统进不了正常启动流程得进救援模式修这个代价太大了。如果用了解压到硬盘的方式fstab这条根本不需要这也是我偏爱解压方式的原因之一——少一个可能出错的环节。3.3 自己搭一个目录型本地仓createrepo_c 的用法有些场景下你的包不是来自 ISO而是自己攒的一批 RPM比如内网统一分发的内部工具包、从别处下载的一堆零散包。这时候需要一个「目录型仓库」——把 RPM 丢进一个目录然后生成repodata元数据让它变成一个 dnf 认得的仓库。mkdir -p /opt/myrepo cp /path/to/*.rpm /opt/myrepo/ dnf install -y createrepo_c createrepo_c /opt/myrepo执行完/opt/myrepo下会多出一个repodata目录。然后照着前面的格式写 repo 文件baseurlfile:///opt/myrepo即可。以后往里加新包重新跑一次createrepo_c --update /opt/myrepo--update只处理变化的包比全量重建快得多。注意往目录里加包之后必须重新生成元数据否则 dnf 看不到新包。这个步骤太容易忘我的习惯是写一个addpkg.sh把「拷贝 重新生成 清缓存」三步串起来减少手滑。还有一个小细节createrepo_c生成的元数据默认带校验和客户端gpgcheck0时也能正常用但如果你自己签了包记得在 repo 文件里配上对应的gpgkey否则内网分发链路就等于没有完整性保护了。4. 国内镜像源配置域名、路径与清缓存三板斧本地源解决了「有没有」的问题国内镜像源解决的是「快不快、新不新」。配置思路和本地源一模一样都是写.repo文件区别只在baseurl从file://换成了https://以及要处理系统自带的那些订阅仓库文件。下面的示例里镜像站域名统一用占位形式mirrors.站点域名表示实际使用时替换成你手头那个镜像站的真实域名即可——路径规则是通用的认准目录结构比记住域名更重要。4.1 先礼后兵把系统自带仓库文件挪走/etc/yum.repos.d/下通常有redhat.repo这个文件由订阅管理工具维护里面每个仓库都带enabled0或者干脆没有可用地址。它的存在本身不致命但它会干扰dnf repolist的输出也会在--enablerepo*这类操作里被误启用。处理方式是挪走而不是删除mkdir -p /etc/yum.repos.d/backup mv /etc/yum.repos.d/redhat.repo /etc/yum.repos.d/backup/保留备份的意义在于万一以后拿到正规授权把文件挪回去就能直接用不用重新注册。如果确定永远不会用订阅源也可以直接禁用subscription-manager repos --disable*这条命令会把所有订阅仓库的enabled改成0但不动文件。两种做法都行看你的管理习惯。我倾向于挪文件因为它的效果是「彻底看不见」排查问题时输出更干净。4.2 baseurl 的路径拼法一步错步步错镜像站的目录结构遵循一套通用约定拆开看是这样https://mirrors.站点域名/发行版目录/版本号/仓库名/架构/os/举例BaseOS 的完整地址大致长这样[mirror-baseos] nameMirror BaseOS baseurlhttps://mirrors.站点域名/发行版目录/9/BaseOS/x86_64/os/ enabled1 gpgcheck1 gpgkeyhttps://mirrors.站点域名/发行版目录/RPM-GPG-KEY-发行版 cost100 metadata_expire6h几个高频错误点。第一末尾斜杠建议带上不带也能用但某些镜像站的 302 跳转会让不带斜杠的地址落到错误页面。第二os/这一层有的镜像站同时提供os/正式发布和kickstart/、debug/之类的子目录认准os。第三版本号位写9而不是9.2写死小版本号等系统小版本升级之后元数据就对不上了。第四架构x86_64和aarch64要跟uname -m一致。gpgkey建议用https://而不是file://因为镜像源里的包可能是兼容发行版签的本地/etc/pki/rpm-gpg/下不一定有对应公钥。从镜像站对应位置拉公钥最直接。拉下来之后 dnf 会自动缓存不用手动导入。AppStream 照葫芦画瓢换个仓库名和路径层级。crb仓库默认不开需要编译源码包时再加一条enabled0放着用的时候dnf --enablerepo临时打开。4.3 dnf.conf 里几个值得改的参数写完.repo文件顺手看一眼/etc/dnf/dnf.conf。有几个参数调整之后体验提升明显[main] gpgcheck1 installonly_limit3 clean_requirements_on_removeTrue max_parallel_downloads10 fastestmirrorTrue skip_if_unavailableFalsemax_parallel_downloads默认是 3调到 8~10装大包时能明显感觉到差别但别调太高有些镜像站会对高并发做限制。fastestmirror让 dnf 在多个镜像之间测速选最快的多仓库场景有用如果配的是单个固定镜像站这项没意义。installonly_limit3控制保留几个旧内核默认也是 3但显式写出来心里有数别让/boot被内核塞满——这是 RHEL 系统上非常经典的故障/boot满了之后dnf update直接失败。skip_if_unavailableFalse这个设置是「宁可报错不要糊弄」。默认值在某些版本里是True意味着某个仓库连不上时 dnf 会静默跳过继续用剩下的仓库表面看起来成功了实际上某些包就是装不上排查起来特别费劲。设成False后仓库不可用会直接报错问题暴露得早。4.4 仓库优先级、模块流与冲突处理多个仓库同时存在时同一个包可能在好几个源里都有版本还不一样。这时候谁说了算第一层是cost在.repo文件里数值小的优先。第二层是版本号同cost下 dnf 会选版本高的。第三层是priority插件需要装dnf-plugins-core它比cost更强势数值小的直接锁死不再考虑版本高低。日常用cost就够了priority留给「必须锁定某个源」的严格场景。模块流的冲突更隐蔽。假设你dnf module enable php:8.1之后某个第三方仓库里又提供了php-8.1.x的普通包非模块包两者可能打架。处理办法是给那个仓库加module_hotfixes1让它绕过模块过滤规则或者干脆把冲突的仓库禁用掉。判断方法dnf module list php dnf repoquery --info php前者看模块流状态后者看包到底来自哪个仓库。遇到「包装上了但版本不是想要的」先查这两条命令比盲改源配置高效得多。还有一个容易忽略的点跨仓库混装之后glibc、openssl这类地基包的版本可能出现「新版本在镜像源旧版本在本地源」的错位。建议定期跑一次dnf distro-sync它会把已安装包的版本和当前启用仓库对齐该升的升、该降的降。这条命令有风险会在你不知情的情况下改动很多东西跑之前先--assumeno预览一遍。5. 排查实录那些年 dnf 报过的错配置源的过程里报错信息往往指向不明确一个「元数据下载失败」背后可能有五六种原因。这一节把实际踩过的坑汇总一下按现象倒推成因比看文档快。5.1 元数据下载失败的五种成因现象一Cannot download repomd.xml或Failed to download metadata for repo xxx。成因排查顺序先curl -I baseurlrepodata/repomd.xml看返回码。404 说明路径拼错重点查版本号位、架构位、os/这一层。403 一般是镜像站限制了访问方式。超时则是网络可达性问题。能curl通但 dnf 报错那大概率是 dnf 缓存脏了走下一步。现象二curl通dnf 不通。先清缓存dnf clean all dnf makecacheclean all会清掉元数据、包缓存、事务历史。清完makecache强制重新拉。很多人只跑makecache不清缓存旧元数据还在问题照旧。现象三证书校验失败 / TLS 握手错误。系统时间不对是头号嫌疑。date看一眼偏差超过几分钟HTTPS 握手直接失败。用chronyd校时timedatectl set-ntp true chronyc sources -v如果时间没问题还报错检查/etc/pki/tls/certs/ca-bundle.crt是否存在且完整某些精简安装的镜像会缺这个文件。现象四本地源报「repodata 不存在」。九成是挂载点失效。df -h /mnt/dvd确认ls /mnt/dvd/BaseOS/repodata验证。没挂上就mount -a如果mount -a报错回去看fstab那行有没有拼错。现象五There are no enabled repositories。dnf repolist all看有没有enabled的条目。全是disabled就去查.repo文件的enabled1是不是漏写了或者文件根本没有.repo后缀——dnf 只认这个后缀叫什么名字都行但扩展名必须是.repo。5.2 包装上了但版本不对模块流与优先级作祟这类问题不会报错只会让人怀疑人生。典型场景dnf install nginx成功nginx -v一看是 1.20可你明明记得仓库里有 1.24。第一步dnf module list nginx看有哪些流、哪个是[e]enabled哪个是[d]default。第二步如果想要的版本在非默认流里切过去dnf module reset nginx dnf module enable nginx:1.24 dnf install nginxreset是必须的直接从 A 流切到 B 流而不 resetdnf 会拒绝。第三步如果模块层没问题用dnf repoquery --info nginx看Repository字段确认包是不是来自你以为的那个源。来自错误的源说明cost或者priority的排序跟预期不符。5.3 常见问题速查表报错/现象最可能的原因快速验证处理动作Cannot download repomd.xmlbaseurl 路径错 / 网络不通curl -I baseurlrepodata/repomd.xml修正路径层级或换镜像站no enabled repositoriesrepo 文件未启用或后缀错dnf repolist all补enabled1确认.repo后缀本地源元数据缺失挂载点失效df -h /mnt/dvd重挂载检查fstabTLS 握手失败系统时间偏差datetimedatectl set-ntp true装上的版本偏旧模块流是默认流dnf module list 包名module resetmodule enable包找不到但源里有仓库被禁用或包在crb里dnf repoquery --info 包名--enablerepo临时启用/boot空间不足导致更新失败旧内核堆积df -h /bootinstallonly_limit3 手动清理更新后行为异常跨源版本错位dnf distro-sync --assumeno预览后执行对齐还有一条不在表里但很常见改了.repo文件却感觉没生效。除了dnf clean all还要确认没有别的地方覆盖了配置——/etc/dnf/dnf.conf里的[main]段、/etc/dnf/plugins/下的插件配置、甚至命令行上的--disablerepo参数都可能盖掉你的设置。dnf repoinfo输出的Repo-filename能定位到具体文件顺藤摸瓜就行。6. 离线环境与长期维护的实操心得上面讲的都是「单机怎么配」但真实环境里更麻烦的是一批机器怎么配尤其是完全断网的内网。这时候本地源就不只是兜底了而是唯一选择。这一节讲怎么把镜像源的内容搬进内网以及长期维护时几个不写进文档的细节。6.1 把镜像源「搬」进内网reposync 一条龙思路很直白在外网机器上把仓库内容完整下载下来打包带进内网再按目录型仓库的方式配好。用dnf reposyncdnf install -y dnf-plugins-core mkdir -p /data/mirror dnf reposync --repoidmirror-baseos --download-metadata -p /data/mirror dnf reposync --repoidmirror-appstream --download-metadata -p /data/mirror--download-metadata是关键参数它会把repodata一起拉下来目标目录立刻就是可用的仓库不需要再跑createrepo_c。不加这个参数拉下来的只是一堆 RPM还得手动生成元数据多一步还容易漏。只拉元数据不拉包用--download-metadata-only适合先看看仓库有多大再决定。拉特定包而不是全量dnf reposync --repoidmirror-appstream --download-metadata \ -p /data/mirror --package-filternginx --package-filterhttpd全量拉一个 AppStream 是几十 GB 的量级动手前先df -h看看盘。内网分发时把/data/mirror整个目录打包拷到内网机器的/opt/mirror然后按前面的格式写 repo 文件baseurlfile:///opt/mirror/mirror-baseos之类就能用了。如果内网有 Web 服务器把目录挂上去用http://提供服务多台机器共享一份比每台都存一份省空间。6.2 空间、同步频率与仓库瘦身跑一段时间之后/data/mirror会肉眼可见地膨胀。几个控制手段。第一只同步需要的仓库。crb、debug、source这类仓库对普通环境没用同步时直接排除。dnf reposync支持--repoid指定不给它就不会拉。第二定期清理旧版本包。reposync加--delete参数会在同步时删掉本地有、远端没有的包也就是被新版本替换掉的旧包。这个参数要慎用它会真的删文件建议第一次先--newest-only只拉最新版dnf reposync --repoidmirror-baseos --download-metadata \ --newest-only -p /data/mirror--newest-only只拉每个包的最新版本体积能砍掉一大半。代价是失去了版本回退的能力如果业务上有「必须装某个旧版本」的需求就别用这个参数。第三同步频率别太高。BaseOS 里的包几个月才动一次AppStream 稍微频繁些。每周同步一次完全够用天天同步纯属浪费带宽。真要自动化用systemd timer比cron好日志走journalctl方便查。最后分享一个我踩过的坑。有一次给内网机器配好本地源dnf repolist显示正常dnf install却一直提示某个依赖「没有可用包」。折腾半天发现那个依赖在AppStream里而我只配了BaseOS。配本地源时一定要把 BaseOS 和 AppStream 两个都写上这不是可选项是 RHEL 9 的硬性结构。后来我给自己定了条规矩任何新机器配完源第一件事是跑dnf --disablerepo* --enablerepolocal-baseos,local-appstream install -y httpd故意只用本地源装一个带依赖链的包能装上说明 BaseOS AppStream 都通了装不上缺哪个仓库一目了然。这比事后出了问题再排查省事太多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑