资讯动态

RHEL 9软件源配置:本地源、镜像源与内网自建源实战

发布时间:2026/10/27 3:34:37 来源:尧图企业网站定制
1. 先想清楚源这件事为什么它决定了一台机器的可用性装完最小化系统的第一件事绝大多数人都会去敲dnf install然后发现卡住、超时、报 404。这时候问题八成不在网络而在软件源——也就是 repo。本地源和镜像源这两条路看起来只是配置文件里几个 URL 的差别实际决定了后续半年里这台机器是能自己装东西还是每次都要找人拷 rpm 包。我把这两套配置当作交付前的固定动作无论是给测试机装环境、给内网机器做离线部署、还是给一批机器做同版本基线都是先落源、再装包。这篇文章讲的是 RHEL 9 这一类系统上的软件源落地怎么用一张安装 ISO 做出可用的本地源怎么把公共镜像站的仓库接到系统里以及内网里怎么搭一个大家共用的镜像仓库。读者画像大概是三种人刚接手 Linux 的新手被Could not resolve host折磨过做运维的手里有一堆没有外网的内网机还有一种是自己搭实验环境的想省下每次装包找依赖的时间。核心关键词就是本地源、镜像源、repo 配置后面的内容都围绕它们展开。我先把三者的定位摆出来因为选错形态是后面所有问题的根源。本地源的本质是文件路径当仓库它不依赖任何网络稳定性最高缺点是内容固定在一张 ISO 的范围内更新滞后公共镜像源的本质是把仓库搬到离你近的服务器上覆盖面广、能拿到更新缺点是受网络质量和站点可用性影响内网自建源是前两者的折中用一次带宽换长期稳定适合机器数量超过十台的场景。2. 三种源的取舍逻辑以及一个必须对齐的版本号2.1 本地源、公共镜像源、内网自建源的适用边界我一般按机器数量 × 网络条件两个维度来选。单台机器、能上外网直接用公共镜像源五分钟搞定单台机器、完全隔离用 ISO 做本地源镜像挂上就能用五台以上、需要长期维护我会花半天时间做内网源因为后面每台机器都要重装、都要装同样的包一次性投入很快就能回本。这三种方案的对比我整理成了下面这张表选型时直接对照就行。方案依赖网络更新时效初始成本典型场景ISO 本地源否固定在该 ISO 发布时点挂载 写一个 repo 文件隔离环境、装机验证、批量基线公共镜像源是跟随站点同步周期换 baseurl 清缓存开发机、测试机、临时环境内网自建源仅同步时由自己的同步策略决定需一台中转机和存储机房、实验室、多机交付这里有个容易被忽略的判断点本地源不只是离线替代品它还是版本一致性的锚点。你在十台机器上都挂同一张 ISO装出来的基础包版本就完全一致排查问题时不会出现这台是 9.2 那台是 9.4的尴尬。所以在做需要严格复现的实验时我宁可用本地源也不用镜像源去追最新。2.2 版本号对齐$releasever是绝大多数报错的源头repo 文件里常见的baseurlhttps://镜像站/rhel/$releasever/BaseOS/$basearch/os/这两个变量是 dnf 在运行时展开的。$basearch一般是x86_64或aarch64比较好确认$releasever就麻烦一点它取的是系统识别到的版本号在 RHEL 9 上通常解析成9而不是9.0、9.4这种带小版本的写法。问题在于很多公共镜像站的目录结构是9/、9-stream/、9.4/混着放的你写$releasever展开成9结果站点目录叫9.4直接 404。我的做法是在写 repo 文件之前先手工确认目录用浏览器或者curl -I探一下目标路径是否存在确认后再决定是用$releasever还是直接硬编码小版本号。硬编码看起来不够优雅但它换来的是不会因为系统小版本升级而突然断源。提示dnf报错Failed to download metadata for repo xxx时先别急着怀疑网络用浏览器把拼出来的完整 URL 打开看一眼十次里有六次是路径写错了。另一个常见坑是本地源和镜像源混用时的版本错位。比如系统是 9.4ISO 是 9.0 的那么从本地源装的老版本包和从镜像源装的新版本包可能产生依赖冲突。我的处理原则是同一个仓库族BaseOS / AppStream不要在同一个优先级上同时来自两个不同小版本的源要么统一用 ISO 版本要么统一用镜像站的当前小版本别混着来。3. 动手之前三分钟摸清家底3.1 用几条命令确认系统身份和现有源状态别急着写配置文件先确认你面对的到底是什么系统。下面这几条命令我几乎是条件反射地敲cat /etc/os-release uname -m dnf repolist --all ls /etc/yum.repos.d/第一条确认发行版和版本号主要看VERSION_ID第二条确认架构决定$basearch的值第三条列出所有已定义的仓库包括被禁用的能看出系统出厂自带了哪些 repo 文件第四条看目录里有哪些.repo文件方便判断是改造现有的还是新建一个。这里有个实操细节/etc/yum.repos.d/下如果已经有一堆仓库文件绝大多数是官方订阅源在没订阅的机器上它们全是 401 或者超时。这些文件留着会让每次dnf makecache都白等一轮超时。我的做法是先建一个备份目录把这批文件整体移走再按需新建mkdir -p /etc/yum.repos.d/backup mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/backup/移走而不是删除是因为某些场景下还要回头参考原来的配置写法删了就得重新找。3.2 ISO 文件的获取与完整性确认本地源依赖安装介质。如果你拿到的是物理光盘插进去之后用lsblk或blkid找到设备名通常是/dev/sr0如果是下载的 ISO 文件先确认文件完整——sha256sum对一下官方公布的校验值这一步别省我有一次因为 ISO 传输中断导致挂载后目录里少了AppStream排查了快一个小时才想到是文件本身坏了。sha256sum /path/to/rhel-9.0-x86_64-dvd.iso确认无误后再做挂载。另外一个必须提醒的点用 DVD 版 ISO不要用 Boot 版。Boot 版只有引导文件里面没有Packages目录挂上去之后BaseOS里空空如也配置写得再对也装不了包。判断方法很简单看 ISO 大小——完整 DVD 镜像通常是几个 GB 级别。顺带看一眼磁盘空间/var/cache/dnf在首次makecache时会占几十到几百 MB空间太紧张的话先把缓存目录挪到别处或者定期dnf clean packages。4. 本地源落地从挂载到 dnf 能用的完整链路4.1 挂载方式的选择与目录结构确认挂载这一步有两种做法如果是物理光驱直接mount /dev/sr0 /mnt/iso如果是 ISO 文件用 loop 设备mount -o loop。我习惯统一建一个固定挂载点/mnt/iso因为后面写进 repo 文件的路径要长期稳定临时换个目录又得改配置。mkdir -p /mnt/iso mount -o loop,ro /path/to/rhel-9.0-x86_64-dvd.iso /mnt/iso ls /mnt/iso挂载完一定要ls看一眼。RHEL 9 的 DVD 目录下应该能看到BaseOS、AppStream两个目录以及EULA、RPM-GPG-KEY-*之类的文件。这两个目录就是两个独立的仓库需要分别配成两个 repo 段——这是新手最容易漏的地方只配了BaseOS结果dnf install vim一路报缺包因为vim在AppStream里。ro参数别省只读挂载能避免误操作破坏介质内容对 loop 挂载来说也省得文件系统被写脏。注意如果挂载点位于 SELinux 会检查的路径下偶发出现 dnf 读不到目录内容的情况。可以用ls -Z /mnt/iso看一眼上下文必要时用restorecon -Rv /mnt/iso复位。绝大多数默认配置下不会碰到但排障时值得先排除这一项。4.2 repo 文件的关键参数逐条说明在/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 cost100 [local-appstream] nameLocal ISO AppStream baseurlfile:///mnt/iso/AppStream enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release cost100逐条说为什么这么写。baseurl用file://协议加绝对路径三个斜杠不能少——写成file://mnt/iso会被当成主机名mnt报一个很迷惑的解析错误。enabled1表示默认启用本地源我一般保持常开。gpgcheck1是包签名校验很多人为了省事改成 0我不建议本地源的内容虽然来自官方介质但校验开着几乎不增加开销却能在介质被篡改或损坏时给出明确报错。gpgkey指向本机的公钥文件如果这个路径不存在可以从 ISO 里拷一份出来cp /mnt/iso/RPM-GPG-KEY-* /etc/pki/rpm-gpg/cost是 dnf 原生的仓库成本参数默认 1000数值越小优先级越高。把本地源设成 100意味着同样的包在两个仓库里都有时优先从本地源取——这样在内网环境下能最大限度减少对外网的依赖。注意cost只在同一个包多仓库可选时起作用它不是万能的优先级开关。还有个可选参数module_hotfixestrue如果本地源和模块流module混用时出现元数据解析报错加上它经常能绕过去。原理是让 dnf 忽略模块过滤直接把仓库当普通仓库处理。副作用是模块流的管理能力失效所以只在排障时临时用别常态挂着。4.3 让挂载在重启后依然有效mount是临时的重启就没了。要长期可用得写进/etc/fstab。ISO 文件用 loop 挂载的写法/path/to/rhel-9.0-x86_64-dvd.iso /mnt/iso iso9660 loop,ro 0 0物理光驱的写法是/dev/sr0 /mnt/iso iso9660 defaults,ro 0 0。写完之后一定要用mount -a验证一遍别直接重启——fstab写错会导致系统进 emergency mode远程机器上这一下就很难受了。mount -a不报错、df -h能看到挂载点才算过关。如果你的场景是只在装包时需要源平时不需要那fstab里加noauto更合适需要时手动mount /mnt/iso。这样开机不依赖介质在位物理光驱拔掉也不会影响启动。配置完成后走一遍标准验证流程dnf clean all dnf makecache dnf repolistrepolist输出里应该能看到local-baseos和local-appstream且包数量不是 0。如果包数量显示 0说明路径指到了空目录回去核对ls /mnt/iso/BaseOS是不是真有Packages。5. 镜像源配置baseurl 怎么写、缓存怎么管、优先级怎么排5.1 从公共镜像站取仓库的配置模板公共镜像站的用法是把baseurl从file://换成https://其余结构一样。我不会在文章里写死某一个站点的域名因为站点目录结构会变写死了过几个月就失效。给你一个模板把占位符换成你实际确认过的路径即可[mirror-baseos] namePublic Mirror BaseOS baseurlhttps://镜像站域名/rhel/9/BaseOS/$basearch/os/ enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release cost500 skip_if_unavailable1 [mirror-appstream] namePublic Mirror AppStream baseurlhttps://镜像站域名/rhel/9/AppStream/$basearch/os/ enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release cost500 skip_if_unavailable1skip_if_unavailable1是实用参数当这个仓库临时不可达时dnf 跳过它继续处理其他仓库而不是整体失败。在有本地源兜底的情况下这个参数能明显提升体验——外网抖一下dnf install不至于直接挂掉。cost500让它排在本地源100之后。这个组合的实际效果是装包时优先从本地 ISO 取本地没有的再从镜像站补。我在隔离和半隔离环境里常用这个搭配。换源之后必须清缓存。dnf 会缓存仓库元数据不清的话它还在读旧的baseurldnf clean all dnf makecache --refresh--refresh强制忽略缓存有效期重新拉取比单纯makecache更彻底。第一次makecache可能会慢一点因为要下载repomd.xml和各仓库的 primary 元数据正常情况一两分钟内完成。5.2 仓库优先级和包冲突的处理思路多仓库并存时同一个包在不同仓库里有不同版本是常态。dnf 的默认行为是选版本最高的而不是选优先级最高的仓库——这点和很多人的直觉相反。cost影响的是同版本情况下的选择顺序不是版本高低。理解这一点能省下大量困惑。真要控制某个包必须从某个仓库装有两个办法。一是临时指定仓库dnf --disablerepo* --enablerepolocal-appstream install vim二是用dnf repoquery先查清楚包在哪些仓库里有、各自什么版本dnf repoquery --info vim dnf repoquery --show-duplicates vim--show-duplicates特别有用它会把所有仓库里同名包的版本都列出来一眼就能看出冲突来源。我排查为什么装的是老版本这类问题时第一件事就是敲它。另一个容易忽略的东西是模块流。RHEL 9 的AppStream里有大量以模块形式组织的软件如某些语言的运行时模块有自己的启用状态。dnf module list能看到当前启用的流。本地源和镜像源的模块元数据如果不一致可能出现这个包明明在仓库里却提示没有可用版本。这时候先dnf module list 包名看清楚是不是模块流的问题再决定是dnf module reset还是换个流。5.3 GPG 校验失败怎么定位gpgcheck1会带来一类报错Public key for xxx.rpm is not installed或者GPG signature verification failed。前者是gpgkey路径不对或没导入后者是包的签名和公钥不匹配。排查顺序我固定这么走先rpm -qa gpg-pubkey*看本机已导入哪些公钥再确认gpgkey指向的文件存在且可读最后用rpm --checksig 包路径单独验一个包确认是包的问题还是仓库配置的问题。如果确认是公钥缺失手工导入一次rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release还有一种情况是系统时间严重偏差导致签名校验的证书有效期判断出错。容器里、旧虚拟机上经常碰到。date看一眼不对就先把时间同步好再重试——这个坑很隐蔽因为报错信息不会提示你时间问题。6. 内网自建源一次投入换长期稳定6.1 用同步工具做离线仓库机器一多每台都去外网拉包就很不经济尤其是带宽紧张的机房。做法是找一台能访问外网的中转机把仓库同步到本地磁盘再用 HTTP 服务发出去。同步用reposync索引重建用createrepo_c两个工具都在dnf-utils和createrepo_c包里。dnf install -y dnf-utils createrepo_c httpd mkdir -p /var/www/html/repo/{BaseOS,AppStream} reposync --repoidmirror-baseos --download-metadata -p /var/www/html/repo/BaseOS reposync --repoidmirror-appstream --download-metadata -p /var/www/html/repo/AppStream--download-metadata是关键参数它把仓库的元数据一起拉下来省掉后面createrepo重建的步骤。不加的话只有 rpm 包客户端拿不到索引一样用不了。如果确实只拉了包就补一步createrepo_c /var/www/html/repo/BaseOS createrepo_c /var/www/html/repo/AppStream同步是个吃磁盘的活完整同步两个仓库大概要几十 GB。第一次同步前先df -h看看别同步到一半满了留下半套数据。我一般会加个--newest-only只拉最新版本能省不少空间代价是拿不到历史版本的包。同步完启动 HTTP 服务注意防火墙和 SELinuxsystemctl enable --now httpd firewall-cmd --permanent --add-servicehttp firewall-cmd --reload restorecon -Rv /var/www/html/reporestorecon这一步不能省。/var/www/html下的文件如果不是正确的 SELinux 上下文httpd 会返回 403而错误日志里只写 permission denied很容易误判成文件权限问题。6.2 客户端指向内网源的写法客户端这边就是标准 repo 文件baseurl换成内网地址[internal-baseos] nameInternal Mirror BaseOS baseurlhttp://内网服务器地址/repo/BaseOS/ enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release cost200内网源和本地源可以共存cost设成 200 让它在 ISO 之后、公网之前。这样一套优先级链就是ISO100→ 内网源200→ 公网镜像500既保证了离线可用又保证了包的新鲜度。如果你的场景是定期同步写个 systemd timer 或者 cron 就行每周跑一次同步脚本同步完自动createrepo_c --update更新索引。脚本里加个set -e和日志重定向出问题能追溯。7. 常见问题排查实录这些坑我都踩过7.1 高频报错速查表排障时最耗时间的不是修是判断往哪个方向修。我把这些年遇到的高频报错整理成表按报错关键字直接对号入座能省掉大部分试错。报错关键字大概率原因处理动作Failed to download metadatabaseurl 路径错、站点目录变更手工打开完整 URL 验证Cannot find a valid baseurl变量展开后路径不存在查$releasever实际值改硬编码Could not resolve host域名解析问题nslookup确认检查/etc/resolv.confPublic key not installedgpgkey 路径错或未导入rpm --import手工导入GPG signature verification failed包与公钥不匹配、系统时间错校验时间确认公钥版本No available modular metadata本地源缺模块元数据加module_hotfixestrue临时绕过Nothing to do包已被装、或仓库里确实没有dnf repoquery确认包是否存在Error: GPG check FAILED介质损坏重新校验 ISO 的 sha256Permission deniedhttpd 侧SELinux 上下文不对restorecon -Rvcurl error 60证书校验失败检查系统 CA 包、时间这张表覆盖了八九成的场景。剩下的疑难杂症基本都要靠dnf -v或dnf --debuglevel10输出细节来定位日志量很大但信息很全把baseurl展开后的实际 URL 和 HTTP 状态码找出来问题基本就明了了。7.2 几个不在文档里的经验第一先验证 URL 再写配置文件。我现在的习惯是拿到一个镜像站先用curl -I把拼好的repodata/repomd.xml路径探一遍返回 200 才往 repo 文件里写。这一步三十秒能省掉后面十分钟的疑惑。命令长这样curl -I https://镜像站域名/rhel/9/BaseOS/x86_64/os/repodata/repomd.xml注意必须是repodata/repomd.xml而不是BaseOS/目录本身——有些站点目录列表被禁用了根路径返回 403 但仓库实际是好的看repomd.xml才准。第二dnf clean all不是万能的。有些情况下元数据缓存在/var/cache/dnf之外的地方比如libdnf的缓存清了还是读到旧的。这时候dnf makecache --refresh或者干脆把/var/cache/dnf整个目录删掉再重建效果更彻底。第三别在同一个系统上留太多 repo 文件。每多一个仓库每次makecache和每次装包都要多一轮元数据比对机器多了感知很明显。我一般控制在三到四个本地源两个BaseOS AppStream、业务需要的话再加一个内网源或扩展仓库仅此而已。那些以防万一留着的不用的源实际上只贡献了超时时间。第四本地源和镜像源的包版本差异要在交付文档里写清楚。这不算技术问题但很影响后续维护。接手的人不知道你这套源的优先级设计看到某个包版本偏老会觉得是配置错了。我在 repo 文件里会加一段注释说明用途和优先级顺序成本很低收益很高。第五物理光驱挂载时注意介质在位。用fstab自动挂载物理光驱的机器如果开机时光盘不在位可能卡在启动阶段。这类场景我建议noauto需要时手动挂别为了省一次mount冒启动失败的风险。8. 关于重复配置这件事我的实际做法我现在做源配置基本是脚本化的一个脚本负责挂 ISO、建挂载点、写两个本地源 repo 文件、清缓存、验证另一个脚本负责按环境变量替换镜像站域名生成镜像源 repo 文件。两个脚本加起来不到一百行但把每次交付的手工操作压到了两条命令。这么做的原因很实际手写 repo 文件的时候最容易错的地方不是逻辑是拼写。AppStream写成Appstream、file:///少一个斜杠、$basearch写成$baserach这些错误肉眼扫一遍看不出来跑起来才报错。脚本化之后这类错误一次都没有了因为路径是固定的。另外提醒一句本地源和镜像源这两套配置不是二选一的关系实际生产中经常是同时存在的。本地源保证任何情况下都能装基础包镜像源保证能拿到更新的版本和更多的软件。把优先级设计好两者配合起来比单独用任何一个都舒服得多。唯一要守住的原则是版本一致性——同一个仓库族不要跨越两个不同的小版本来源这条守住了剩下的都是参数调整的活。

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

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

免费获取报价 →
↑