☰
RHEL 9软件源配置:本地源与国内镜像源实战排错
2026/10/27 3:34:23 网站建设 项目流程

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」这种怪事。另外还有一个crb(CodeReady 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 -m

RHEL 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目录下面还有一层repodata,baseurl要指到repodata的父目录,也就是.../BaseOS,而不是.../BaseOS/repodata。这个规则对所有类型的仓库都成立:baseurl指向含repodata的那个目录。

3.1 手写 repo 文件,字段逐个拆开讲

新建/etc/yum.repos.d/local-dvd.repo,内容如下:

[local-baseos] name=Local DVD BaseOS baseurl=file:///mnt/dvd/BaseOS enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release cost=1000 [local-appstream] name=Local DVD AppStream baseurl=file:///mnt/dvd/AppStream enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release cost=1000

逐项说。[local-baseos]是这个仓库的 ID,全局唯一,dnf --enablerepo=后面跟的就是它,别用中文、别带空格,否则脚本里引用会很难受。name=是给人看的描述,随便写,但建议写清楚是本地还是镜像,出问题时一眼分辨。baseurl=file:///mnt/dvd/BaseOS是核心,file://后面跟三个斜杠——前两个是协议分隔符,第三个才是根目录的斜杠,写成file://mnt/dvd会被解析成主机名mnt,报错信息还挺误导人。

enabled=1控制默认是否启用,0就是不启用但保留配置,需要时用dnf --enablerepo=local-baseos临时打开。gpgcheck=1开启签名校验,gpgkey=指向公钥文件。本地介质的公钥就在系统里,路径/etc/pki/rpm-gpg/下能找到,文件名各发行版略有差异,ls一下挑对应的那个。如果你用的是兼容发行版的安装介质,公钥文件名会是另外一套(比如带rocky、alma字样),别照抄,看着改。

cost=1000这个字段值得单独说。dnf 默认cost=1000,数值越小优先级越高。给本地源设一个较大的值,意味着「有别的源能用时先别用我」,这就实现了前面说的兜底逻辑。如果你的机器压根没有外网,这个值设不设都无所谓。

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 文件,baseurl=file:///opt/myrepo即可。以后往里加新包,重新跑一次createrepo_c --update /opt/myrepo,--update只处理变化的包,比全量重建快得多。

注意:往目录里加包之后必须重新生成元数据,否则 dnf 看不到新包。这个步骤太容易忘,我的习惯是写一个addpkg.sh,把「拷贝 + 重新生成 + 清缓存」三步串起来,减少手滑。

还有一个小细节:createrepo_c生成的元数据默认带校验和,客户端gpgcheck=0时也能正常用,但如果你自己签了包,记得在 repo 文件里配上对应的gpgkey,否则内网分发链路就等于没有完整性保护了。

4. 国内镜像源配置:域名、路径与清缓存三板斧

本地源解决了「有没有」的问题,国内镜像源解决的是「快不快、新不新」。配置思路和本地源一模一样,都是写.repo文件,区别只在baseurl从file://换成了https://,以及要处理系统自带的那些订阅仓库文件。

下面的示例里,镜像站域名统一用占位形式mirrors.<站点域名>表示,实际使用时替换成你手头那个镜像站的真实域名即可——路径规则是通用的,认准目录结构比记住域名更重要。

4.1 先礼后兵:把系统自带仓库文件挪走

/etc/yum.repos.d/下通常有redhat.repo,这个文件由订阅管理工具维护,里面每个仓库都带enabled=0或者干脆没有可用地址。它的存在本身不致命,但它会干扰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] name=Mirror BaseOS baseurl=https://mirrors.<站点域名>/<发行版目录>/9/BaseOS/x86_64/os/ enabled=1 gpgcheck=1 gpgkey=https://mirrors.<站点域名>/<发行版目录>/RPM-GPG-KEY-<发行版> cost=100 metadata_expire=6h

几个高频错误点。第一,末尾斜杠:建议带上,不带也能用,但某些镜像站的 302 跳转会让不带斜杠的地址落到错误页面。第二,os/这一层:有的镜像站同时提供os/(正式发布)和kickstart/、debug/之类的子目录,认准os。第三,版本号位:写9而不是9.2,写死小版本号,等系统小版本升级之后元数据就对不上了。第四,架构:x86_64和aarch64要跟uname -m一致。

gpgkey建议用https://而不是file://,因为镜像源里的包可能是兼容发行版签的,本地/etc/pki/rpm-gpg/下不一定有对应公钥。从镜像站对应位置拉公钥最直接。拉下来之后 dnf 会自动缓存,不用手动导入。

AppStream 照葫芦画瓢,换个仓库名和路径层级。crb仓库默认不开,需要编译源码包时再加一条,enabled=0放着,用的时候dnf --enablerepo=临时打开。

4.3 dnf.conf 里几个值得改的参数

写完.repo文件,顺手看一眼/etc/dnf/dnf.conf。有几个参数调整之后体验提升明显:

[main] gpgcheck=1 installonly_limit=3 clean_requirements_on_remove=True max_parallel_downloads=10 fastestmirror=True skip_if_unavailable=False

max_parallel_downloads默认是 3,调到 8~10,装大包时能明显感觉到差别,但别调太高,有些镜像站会对高并发做限制。fastestmirror让 dnf 在多个镜像之间测速选最快的,多仓库场景有用;如果配的是单个固定镜像站,这项没意义。installonly_limit=3控制保留几个旧内核,默认也是 3,但显式写出来心里有数,别让/boot被内核塞满——这是 RHEL 系统上非常经典的故障,/boot满了之后dnf update直接失败。

skip_if_unavailable=False这个设置是「宁可报错,不要糊弄」。默认值在某些版本里是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_hotfixes=1,让它绕过模块过滤规则,或者干脆把冲突的仓库禁用掉。判断方法:

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 <baseurl>repodata/repomd.xml看返回码。404 说明路径拼错,重点查版本号位、架构位、os/这一层。403 一般是镜像站限制了访问方式。超时则是网络可达性问题。能curl通但 dnf 报错,那大概率是 dnf 缓存脏了,走下一步。

现象二:curl通,dnf 不通。

先清缓存:

dnf clean all dnf makecache

clean 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文件的enabled=1是不是漏写了,或者文件根本没有.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 nginx

reset是必须的,直接从 A 流切到 B 流而不 reset,dnf 会拒绝。第三步,如果模块层没问题,用dnf repoquery --info nginx看Repository字段,确认包是不是来自你以为的那个源。来自错误的源,说明cost或者priority的排序跟预期不符。

5.3 常见问题速查表

报错/现象最可能的原因快速验证处理动作
Cannot download repomd.xmlbaseurl 路径错 / 网络不通curl -I <baseurl>repodata/repomd.xml修正路径层级或换镜像站
no enabled repositoriesrepo 文件未启用或后缀错dnf repolist all补enabled=1,确认.repo后缀
本地源元数据缺失挂载点失效df -h /mnt/dvd重挂载,检查fstab
TLS 握手失败系统时间偏差datetimedatectl set-ntp true
装上的版本偏旧模块流是默认流dnf module list <包名>module reset+module enable
包找不到但源里有仓库被禁用或包在crb里dnf repoquery --info <包名>--enablerepo=临时启用
/boot空间不足导致更新失败旧内核堆积df -h /bootinstallonly_limit=3+ 手动清理
更新后行为异常跨源版本错位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 reposync:

dnf install -y dnf-plugins-core mkdir -p /data/mirror dnf reposync --repoid=mirror-baseos --download-metadata -p /data/mirror dnf reposync --repoid=mirror-appstream --download-metadata -p /data/mirror

--download-metadata是关键参数,它会把repodata一起拉下来,目标目录立刻就是可用的仓库,不需要再跑createrepo_c。不加这个参数,拉下来的只是一堆 RPM,还得手动生成元数据,多一步还容易漏。

只拉元数据不拉包,用--download-metadata-only,适合先看看仓库有多大再决定。拉特定包而不是全量:

dnf reposync --repoid=mirror-appstream --download-metadata \ -p /data/mirror --package-filter=nginx --package-filter=httpd

全量拉一个 AppStream 是几十 GB 的量级,动手前先df -h看看盘。内网分发时,把/data/mirror整个目录打包,拷到内网机器的/opt/mirror,然后按前面的格式写 repo 文件,baseurl=file:///opt/mirror/mirror-baseos之类,就能用了。如果内网有 Web 服务器,把目录挂上去用http://提供服务,多台机器共享一份,比每台都存一份省空间。

6.2 空间、同步频率与仓库瘦身

跑一段时间之后,/data/mirror会肉眼可见地膨胀。几个控制手段。

第一,只同步需要的仓库。crb、debug、source这类仓库对普通环境没用,同步时直接排除。dnf reposync支持--repoid指定,不给它就不会拉。

第二,定期清理旧版本包。reposync加--delete参数会在同步时删掉本地有、远端没有的包(也就是被新版本替换掉的旧包)。这个参数要慎用,它会真的删文件,建议第一次先--newest-only只拉最新版:

dnf reposync --repoid=mirror-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="*" --enablerepo="local-baseos,local-appstream" install -y httpd

故意只用本地源装一个带依赖链的包,能装上,说明 BaseOS + AppStream 都通了;装不上,缺哪个仓库一目了然。这比事后出了问题再排查省事太多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询