1. 这个报错到底卡在了哪一步CentOS Linux 8 - AppStream 错误为仓库 appstream 下载元数据失败 : Cannot prepare internal mirrorlist: No URLs——这行报错我见过太多次了尤其是在一些老旧的测试机、内网实验环境、或者放了半年没开机的虚拟机上。很多人第一反应是网络断了然后开始 ping 网关、ping 公共 DNS发现都通于是陷入困惑网络明明是好的为什么 yum 就是装不了东西先把结论摆在前面这个报错的核心不是网络不通而是yum/dnf 在准备仓库元数据时拿不到任何可用的镜像地址mirrorlist 里解析出来的 URL 列表为空。注意报错里的关键词是Cannot prepare internal mirrorlist: No URLs它说的是内部镜像列表准备失败没有 URL。也就是说配置文件里那个mirrorlist指向的地址要么访问不到要么返回的内容里一条有效镜像地址都没有最终 yum 手里空空如也只能报错退出。要理解这件事得先搞清楚 CentOS 8 的仓库配置是怎么组织的。CentOS 8 默认使用dnf作为包管理器yum在 8 上其实是dnf的软链接仓库定义文件放在/etc/yum.repos.d/目录下典型的有CentOS-AppStream.repo、CentOS-Base.repo、CentOS-Extras.repo等。每个.repo文件里一个仓库段落长这样[appstream] nameCentOS Linux $releasever - AppStream mirrorlisthttp://mirrorlist.centos.org/?release$releaseverarch$basearchrepoAppStreaminfra$infra #baseurlhttp://mirror.centos.org/$contentdir/$releasever/AppStream/$basearch/os/ gpgcheck1 enabled1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial关键就在mirrorlist这一行。yum 执行时会先请求这个mirrorlist地址服务器返回一个纯文本列表里面是一堆实际可用的镜像站点 URLyum 再从里面挑一个去下载repomd.xml等元数据。如果这一步返回空、超时、或者返回了 404/HTML 错误页yum 就会抛出No URLs这个错误。那为什么会出现没有 URL结合我这些年的排查经验原因基本集中在下面几类而且它们经常是叠加出现的CentOS 8 已停止常规维护官方把 8 的软件源从主镜像站迁移到了归档站点vault原来mirrorlist.centos.org上针对 8 的条目被清空或不再返回有效地址。这是目前最常见、也最容易被忽略的一条。$releasever变量解析异常导致拼出来的 mirrorlist 请求参数不对服务器返回空列表。DNS 或出网策略限制机器能 ping 通 IP但域名解析不了或者只允许特定域名出网mirrorlist.centos.org被挡在外面。系统时间严重偏差导致 HTTPS 握手失败如果 mirrorlist 走的是 https进而拿不到列表。.repo文件被改坏比如mirrorlist和baseurl同时被注释掉或者enabled1但地址写错。提示看到No URLs先别急着重装系统九成情况改一下仓库配置就能救回来。重装是最后手段不是第一手段。我在一台内网虚拟机上复现过这个场景机器能访问外网curl一个公共网站也正常但dnf makecache就是报No URLs。后来curl了一下那个 mirrorlist 地址返回的是一个空文件——问题瞬间清晰了不是网络的事是源那头没东西了。这个排查动作非常关键后面会详细讲。2. 先分清是源没了还是网断了排查这类问题最忌讳一上来就改配置。你得先判断故障域到底是仓库源本身失效还是本机网络/解析有问题。这两类的修法完全不同搞反了会白折腾半天。2.1 用三条命令快速定位故障域我习惯按下面这个顺序走基本三分钟内能定性# 第一步看仓库配置里 mirrorlist 到底指向哪 grep -r mirrorlist /etc/yum.repos.d/ # 第二步手动请求这个地址看返回什么 curl -v http://mirrorlist.centos.org/?release8archx86_64repoAppStreaminfrastock # 第三步看 dnf 自己怎么说的 dnf repolist -v重点看第二步的返回。会出现几种典型情况curl 返回情况含义故障域返回一堆http://...开头的 URL源正常问题在本机 yum 配置或缓存本机返回空内容0 字节源那头没有该版本条目源失效返回 404 / HTML 错误页地址已废弃源失效Could not resolve hostDNS 解析失败本机网络连接超时出网被限制或路由不通本机网络如果 curl 返回的是空或者 404那基本可以确定是 CentOS 8 源迁移导致的问题跟你的机器没关系。这时候无论你怎么重启网络、改 DNS 都没用必须换源地址。2.2 为什么 CentOS 8 的源会消失这里得说清楚背景不然改配置也是知其然不知其所以然。CentOS 8 的生命周期在 2021 年底就进入了尾声官方后续把 8 的软件包整体搬到了归档站点vault 站点主镜像站上不再保留 8 的常规同步。这意味着mirrorlist.centos.org针对release8的查询返回的列表会越来越空最终变成空文件。原来mirror.centos.org/centos/8/...这类路径也可能 404。但归档站点上8 的包是完整保留的只是路径变了需要显式指向 vault 地址。所以正确的修复方向不是修网络而是把仓库地址从动态 mirrorlist 切换到归档站点的固定 baseurl。这也是为什么很多人搜到教程说把 mirrorlist 注释掉启用 baseurl本质就是这个道理。2.3 一个容易误判的细节$releasever解析还有一种情况容易被误判成源没了$releasever变量没解析成8而是解析成了别的值比如8.5或者空。这时候请求 mirrorlist 带的参数就不对服务器自然返回空。验证方法很简单# 看 dnf 实际用的 releasever 是多少 dnf -v repolist 21 | grep -i releasever # 或者直接问 python3 -c import dnf; print(dnf.conf.Conf().releasever) 2/dev/null || echo 用下面这招 rpm -q --qf %{VERSION}\n centos-release如果$releasever解析异常可以在.repo文件里把它写死成8或者用--releasever8参数临时指定。不过说实话在 CentOS 8 这个场景下$releasever出问题的概率远小于源迁移这个根本原因所以别在这上面钻太久。注意排查时一定要用curl -v看完整响应不要只看能不能连上。能连上但返回空和连不上是两码事。3. 把 mirrorlist 换成归档 baseurl 的完整操作定性为源失效之后修复思路就明确了放弃动态 mirrorlist改用归档站点的固定 baseurl。下面是我实际用下来最稳的一套操作适用于绝大多数 CentOS 8 机器。3.1 动手前先备份这是铁律改仓库配置之前先把原来的.repo文件备份一份。别嫌麻烦我见过太多人改到一半改乱了想回退发现原文件没了最后只能重装。# 建个备份目录把现有仓库配置整体拷过去 mkdir -p /etc/yum.repos.d/backup cp /etc/yum.repos.d/*.repo /etc/yum.repos.d/backup/ ls -l /etc/yum.repos.d/backup/备份完再动手心里踏实。万一改错了cp /etc/yum.repos.d/backup/*.repo /etc/yum.repos.d/一条命令就能还原。3.2 用 sed 批量替换 mirrorlist 为 vault 地址CentOS 8 的归档站点地址是vault.centos.org对应的路径结构是vault.centos.org/8.x.x/...。这里有个坑vault 上的路径带具体小版本号比如8.5.2111而不是笼统的8。所以要么你查清楚机器对应的具体版本要么用8-stream之类的路径。先确认机器的小版本cat /etc/centos-release # 输出类似CentOS Linux release 8.5.2111拿到8.5.2111之后就可以批量替换了。下面这段脚本会把所有.repo文件里的mirrorlist行注释掉并把baseurl指向 vault# 定义版本号按你实际查到的改 VER8.5.2111 # 遍历所有 repo 文件处理 for f in /etc/yum.repos.d/CentOS-*.repo; do # 注释掉 mirrorlist 行 sed -i s/^mirrorlist/#mirrorlist/ $f # 把 baseurl 指向 vault如果原本被注释先取消注释再替换 sed -i s|^#baseurlhttp://mirror.centos.org|baseurlhttp://vault.centos.org| $f sed -i s|^baseurlhttp://mirror.centos.org|baseurlhttp://vault.centos.org| $f # 把 $releasever 替换成具体版本避免解析问题 sed -i s|\\\$releasever|$VER|g $f done # 检查改完的结果 grep -E baseurl|mirrorlist /etc/yum.repos.d/CentOS-AppStream.repo改完之后CentOS-AppStream.repo里应该长这样[appstream] nameCentOS Linux 8.5.2111 - AppStream #mirrorlisthttp://mirrorlist.centos.org/?release8.5.2111arch$basearchrepoAppStreaminfra$infra baseurlhttp://vault.centos.org/8.5.2111/AppStream/$basearch/os/ gpgcheck1 enabled1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial3.3 清缓存并验证配置改完必须清掉旧的元数据缓存否则 dnf 可能还在用之前缓存的坏数据# 清理所有缓存 dnf clean all # 重建缓存这一步会真正去拉元数据 dnf makecache # 看仓库列表是否正常 dnf repolist如果dnf makecache顺利跑完dnf repolist能看到appstream、baseos、extras等仓库那就说明修好了。这时候再装个包验证一下dnf install -y vim-enhanced能装上收工。3.4 如果 vault 也访问不了怎么办有些内网环境完全不允许出网那 vault 也够不着。这种情况下有两个选择搭本地镜像源在一台能出网的机器上把需要的包同步下来放到内网 HTTP 服务器上然后把 baseurl 指向内网地址。这是最正规的做法但工作量大。用光盘/ISO 做本地源如果手头有 CentOS 8 的安装 ISO可以挂载后直接当本地仓库用完全不依赖网络。挂载 ISO 做本地源的配置大概是这样# 挂载 ISO mkdir -p /mnt/cdrom mount -o loop /path/to/CentOS-8.iso /mnt/cdrom # 新建一个本地 repo 文件 cat /etc/yum.repos.d/local-iso.repo EOF [local-iso] nameLocal ISO Repo baseurlfile:///mnt/cdrom/AppStream enabled1 gpgcheck0 EOF dnf clean all dnf makecache提示gpgcheck0只建议在完全可控的本地 ISO 场景下临时使用生产环境还是应该保留签名校验。4. 那些年我踩过的坑和反直觉的细节配置改对了不代表就万事大吉实际操作里还有一堆细节能把人绊住。这一节我把印象最深的几个坑拎出来都是文档里不会写、但实际会遇到的。4.1 改了配置却依然报错缓存和 dnf 进程在作祟最常见的情况是配置明明改对了grep出来 baseurl 也指向 vault 了但dnf还是报No URLs。这时候八成是缓存没清干净或者有残留的 dnf 进程锁着。# 强制清缓存包括元数据和事务缓存 dnf clean all rm -rf /var/cache/dnf/* # 检查有没有卡住的 dnf 进程 ps aux | grep -E dnf|yum | grep -v grep # 如果有确认不是正在运行的任务后清掉锁 rm -f /var/run/dnf.pid /var/cache/dnf/*.pid我遇到过一次是之前一个dnf命令被 CtrlZ 挂起了进程还在后台导致后续所有 dnf 操作都拿不到锁报的错却五花八门其中就包括No URLs。所以看到奇怪的报错先ps一下有没有僵尸 dnf 进程。4.2$basearch和$releasever的替换顺序有讲究用 sed 批量替换的时候如果你先把$releasever替换成8.5.2111再处理 baseurl一般没问题。但如果你脚本里对$basearch也做了替换就要小心别把变量名替换坏了。$basearch通常解析成x86_64一般不需要手动替换保留变量让 dnf 自己解析更安全。另外sed 里的$在双引号中会被 shell 先解释所以替换$releasever时要么用单引号要么转义成\$releasever。我上面脚本里用的是\\\$releasever就是为了防止 shell 提前吃掉。这个细节不注意替换会静默失败配置看着改了其实没改。4.3 时间偏差导致的假网络故障有一次在一台长期没开机的虚拟机上配置全对vault 地址也能 ping 通但dnf makecache就是失败。最后发现是系统时间停在了两年前导致 HTTPS 证书校验失败。虽然我们用的是 http但某些镜像站会做重定向到 https时间不对就断在重定向那一步。# 看系统时间 date # 同步时间如果机器能出网 timedatectl set-ntp true # 或者手动设置 date -s 2024-01-01 12:00:00时间这个问题特别隐蔽因为报错信息不会直接说你时间不对而是表现为各种连接失败。排查网络问题时顺手date一下能省很多事。4.4 别忽略enabled0的仓库有时候appstream报错但baseos是好的。这时候要检查是不是只有appstream这个仓库的配置有问题其他仓库正常。dnf repolist --all能看到所有仓库包括禁用的dnf repolist只看启用的。如果某个仓库被误设成enabled0它不会报No URLs但也不会被使用容易造成明明配了却装不上包的困惑。# 看所有仓库状态 dnf repolist --all # 临时启用某个仓库 dnf --enablerepoappstream install -y 包名4.5 关于换源这件事的取舍网上有很多一键换源脚本把 CentOS 8 的源换成各种第三方镜像。我的建议是优先用官方 vault 归档源因为它是官方保留的、签名完整的、路径稳定的。第三方镜像虽然快但存在同步滞后、签名不一致、甚至被篡改的风险。如果确实要用第三方务必确认它提供了完整的 GPG 校验并且gpgkey指向可信地址。源类型优点风险推荐度官方 vault 归档签名完整、路径稳定速度可能一般高本地 ISO 源完全离线、速度快包版本固定、更新麻烦内网首选第三方镜像速度快同步滞后、可信度参差谨慎自建内网镜像可控、快维护成本高大规模环境首选5. 从根上避免仓库配置的长期维护思路修好一台机器容易但如果手上有几十上百台 CentOS 8一台台改就是灾难。这一节聊聊怎么从管理层面减少这类问题的复发。5.1 用配置管理工具统一仓库文件如果环境里有 Ansible、SaltStack 之类的工具把.repo文件做成模板统一分发是最省心的。核心思路是把版本号、镜像地址做成变量模板渲染后推到所有机器改一次全量生效。一个简化的 Ansible 任务大概长这样- name: 统一分发 CentOS 8 仓库配置 template: src: templates/CentOS-AppStream.repo.j2 dest: /etc/yum.repos.d/CentOS-AppStream.repo owner: root group: root mode: 0644 notify: dnf clean all模板里用{{ centos_minor_version }}这样的变量不同批次机器传不同值。这样即使以后 vault 路径再变改一个变量就能全量更新。5.2 建立源可用性的日常巡检与其等报错了再修不如定期巡检。写个简单的脚本每天跑一次检查关键仓库的元数据能不能拉到#!/bin/bash # check_repo.sh - 仓库可用性巡检 LOG/var/log/repo_check.log DATE$(date %Y-%m-%d %H:%M:%S) if dnf makecache /dev/null 21; then echo $DATE OK: 所有仓库元数据正常 $LOG else echo $DATE FAIL: 仓库元数据异常请检查 $LOG # 可以在这里加告警比如发邮件或写监控 fi配合定时任务每天凌晨跑一次有问题提前发现不至于等到业务装包时才炸。5.3 规划好系统的生命周期说到底CentOS 8 源失效是生命周期结束的必然结果。与其反复打补丁不如提前规划迁移路径。对于还在用 CentOS 8 的环境可以考虑迁移到有长期支持的发行版避免再次遇到源突然没了。如果短期不能迁移就把关键包在本地镜像里固化下来减少对外部源的依赖。建立内部软件包仓库所有机器从内网源装包外部源只作为同步来源。这些是管理层面的决策但技术人应该主动把这些信息同步给团队而不是等出事了再救火。5.4 一个我常用的最小可用修复模板最后给一个我压箱底的、最简化的修复模板。当你只想快速让一台机器恢复装包能力不想折腾太多时直接照这个来# 1. 备份 mkdir -p /etc/yum.repos.d/bak cp /etc/yum.repos.d/*.repo /etc/yum.repos.d/bak/ # 2. 确认版本 VER$(rpm -q --qf %{VERSION} centos-release | cut -d. -f1-2) echo 检测到版本: $VER # 3. 批量改源以 8.5.2111 为例按实际改 for f in /etc/yum.repos.d/CentOS-*.repo; do sed -i s/^mirrorlist/#mirrorlist/ $f sed -i s|^#\?baseurlhttp://mirror.centos.org|baseurlhttp://vault.centos.org| $f sed -i s|\$releasever|8.5.2111|g $f done # 4. 清缓存重建 dnf clean all dnf makecache dnf repolist这套流程我在多台机器上验证过只要 vault 站点可达基本一次成功。唯一需要你手动确认的就是那个具体小版本号别填错。注意8.5.2111只是示例务必用cat /etc/centos-release查到的实际版本替换。填错版本号vault 上找不到对应路径照样报No URLs。我个人在实际操作中的体会是这类源失效问题最考验的不是技术深度而是排查的条理性——先定性故障域再动手改配置改完必清缓存最后验证装包。按这个节奏走再刁钻的No URLs也能顺藤摸瓜解决掉。真正会让人翻车的往往是跳过验证、凭感觉改配置结果越改越乱。
阅读完成 · 觉得有帮助?