先说结论:这批服务器的最终解决方案,就是题目里写的那套——“离线配置yum源”。当时我接手这个环境,一台崭新的机器要装二十多个依赖包,外网不通,光盘里又没有想要的软件版本,折腾到半夜才彻底理顺。今天这篇文章,就是把那次经历里踩过的坑、总结出的步骤、以及不同场景下该选哪条路,一次性说清楚。
如果你也是运维、实施工程师,或者在学校机房/客户内网里干活,遇到“能联网的机器装不了包”这种问题,建议把这篇收藏起来。全文以CentOS 7为主,RHEL 7、CentOS 8/9、以及衍生的OpenEuler等发行版,思路完全通用,区别只在命令细节上。
1. 离线yum源的核心逻辑:yum到底在“找”什么
想要配置离线源,先搞明白yum的工作机制。很多新手卡住,不是命令敲错,而是不知道yum源里必须有什么文件。
1.1 仓库元数据:yum的“购物清单”
yum本质上是个包管理器,它做的事情很像你逛超市:有一个清单告诉你货架上有哪些商品、价格多少、放在哪个位置;你勾选商品,系统自动帮你把商品连同配套零件一起搬回家。
这个“清单”在yum的世界里就是repodata元数据目录。yum在下载软件包之前,会先去访问仓库地址下的repodata/repomd.xml,解析出该仓库包含哪些rpm包、每个包的依赖关系、版本号、校验和。如果你配置了一个源,但目录下只有一堆散落无序的rpm文件,没有repodata,yum照样报错——这就是很多初次配置者百思不得其解的地方。
1.2 三种离线源的形态对比
根据手头的资源和网络环境,离线源通常有三种做法,各有适用场景:
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 本地ISO挂载 | 只需要装该发行版基础包/内核/工具集 | 最快、零依赖准备 | 软件老、范围窄 |
| 有网机器下载rpm包 | 要安装特定版本软件及依赖 | 灵活、可定制 | 需要一台能联网的同架构机器 |
| 整仓镜像同步 | 内网集群规模大、长期维护 | 完整、可持续增量 | 耗流量、耗时间、需要大存储 |
我当时的环境是:内网有一台连外网的跳板机,但业务服务器禁止访问公网。选择的就是“有网机器下载rpm包 + 本地仓库目录”这条路。如果你连一台能联网的同系统机器都没有,那只能靠ISO光盘方案起步,后面再慢慢扩展。
2. 第一个可行方案:挂载ISO做临时本地源
在正式折腾离线rpm包之前,我强烈建议先做一遍ISO本地源。它能在半小时内解决70%的“基础包缺失”问题,而且不需要额外的机器。
2.1 准备介质与挂载
CentOS 7的完整版DVD ISO镜像里,包含了Packages/目录,里面全是rpm包,还自带了repodata,这意味着把ISO挂载出来就能直接当仓库用。操作如下:
mkdir -p /mnt/cdrom mount -o loop /path/to/CentOS-7-x86_64-DVD-2207.iso /mnt/cdrom注意:
mount -o loop挂载需要root权限,而且如果ISO文件在普通文件系统上,不要加ro以外的多余参数。挂载成功后,先检查/mnt/cdrom/Packages/目录是否存在,并且/mnt/cdrom/repodata/repomd.xml能否读取到。
如果不想占用一个挂载点,也可以直接把ISO解压到某个数据盘目录,比如/data/iso/,效果一样。区别是解压后占空间更大一些(完整版ISO解压大约4.5GB),挂载则几乎不占额外空间。
2.2 写repo文件并清缓存验证
在/etc/yum.repos.d/下新建一个local.repo,注意这个目录下原有的.repo文件建议临时改后缀禁用,比如mv CentOS-Base.repo CentOS-Base.repo.bak,否则yum会先尝试访问这些外网地址,然后因为不通卡在超时上。
[local-iso] name=Local CentOS ISO Repository baseurl=file:///mnt/cdrom enabled=1 gpgcheck=0保存后执行:
yum clean all yum makecache yum repolist all如果repolist显示local-iso仓库有包数量,说明源已经通了。此时yum install vim lrzsz等常见工具都能装。这里有两个坑说说:
gpgcheck=0是偷懒,生产环境建议设为1并配好gpgkey,但离线环境很容易出现“公钥不在本机”导致所有包被拒,后面的章节我会专门讲怎么处理。baseurl=file:///mnt/cdrom有可能会因为路径末尾少一个斜杠而报错,确保路径能直接访问到repodata父级目录。
2.3 ISO源的真实边界
用过一轮后你会发现,ISO源的“天花板”很明显。一方面光盘里自带的多半是2~3年前的版本;另一方面很多业务软件的中间依赖,比如某个php扩展需要libxml2-devel,ISO里可能没有,也可能版本不匹配。那时候你就需要转移到下一步:在联网机器上按需收集rpm包。
3. 在“有网机器”上收集RPM包:三条路线的取舍
这是整个离线配置里最讲究技巧的环节。目标很明确:一台能联网且操作系统架构与内网机器一致(都是x86_64)的机器,把需要的rpm包和所有依赖一次性拉下来,打包搬到内网。我试过三条路线,按推荐程度说。
3.1 yumdownloader:最轻量,但是眼力活
yumdownloader需要先安装yum-utils:
yum install -y yum-utils yumdownloader --resolve --destdir=/root/rpms install vim-enhanced--resolve参数很关键,它会自动把依赖包一起拉下来。但这个工具有两个问题:
- 它只解决直接依赖,有时候某些rpm的
post-install脚本需要额外工具,它不会管。 - 它不会自动把已有的老版本依赖升级到所需版本,导致你拿到一批包后,在目标机器安装时又说依赖冲突。
我后来更多把它用在“确认某个包存不存在、拿单包”的场景。拿全套依赖时容易漏。
3.2 repotrack:我首选的依赖收集工具
同样是yum-utils包里的工具,但repotrack是专门为离线仓库设计的。
repotrack -p /root/rpms vim-enhanced它会把vim-enhanced及其完整依赖树都拉进/root/rpms,而且默认会包含所有被依赖的版本,不会出现“拉下来的包自己之间相互版本不一致”的问题。它的缺点也直观:包数量多,一个vim可能会带出三十多个依赖,仓库目录体积不小。
如果你要装的是nginx、php、python3这种依赖较多的软件,直接repotrack最省心。我在这台客户的机器上,就是用repotrack nginx把45个rpm全拉到一起,后面丢进仓库就再没为缺依赖发过愁。
3.3 reposync:整仓镜像,规模场景用
如果需要维护的服务器多、应用杂,甚至以后还要加机器,那建议走整仓镜像。
reposync --repoid=base --download_path=/data/mirror/centos7这会把整个base仓库的内容同步下来,一次就是几十GB。适合搭建内网大仓库的场景,但不适合快速解决“我就装个nginx”这种问题。实际操作上,我通常把前两条路线混着用:大仓库用reposync只同步常用基础包,具体业务包用repotrack拉取。
3.4 架构必须确认,否则全盘皆输
无论选哪条路,打死也不能忽略架构。在x86_64机器上repotrack收集到的包,一定包含大量x86_64和noarch包,但绝不会有aarch64的。如果目标机器是ARM架构,必须在ARM架构源上重新跑,不能直接拷贝过来用。我曾经见过有同事把x86_64的源放到ARM机器上,yum解析到一半就说“not compatible”,排查了一上午。
4. 在离线仓库里建“货架”:createrepo与repo文件的关键细节
通过上述方式把几十个rpm丢到一个目录后,这个目录还不能直接被yum使用。它缺少repodata。这一步用createrepo来解决。
4.1 生成与更新元数据
在存放rpm的目录下执行:
yum install -y createrepo createrepo --update -v /data/yumrepo/这里有两个关键参数:
--update:当目录里已经有repodata时,只做增量更新,不用全量重建,速度快很多。我当时第一次忘了加--update,上千个包重建了十几分钟,第二次加上了,几秒搞定。- 如果你没有
--update,就必须删除旧的repodata目录,否则会报“existing repodata conflicts”。这个坑出现得很频繁。
生成完毕,/data/yumrepo/下会出现repodata/repomd.xml,仓库本身算建成了。
4.2 repo文件的完整格式与解释
生成好元数据后,把整个目录打包传进内网服务器,放在比如/data/yumrepo/,然后写repo文件。以/etc/yum.repos.d/local.repo为例:
[local-offline] name=Offline Custom Repository baseurl=file:///data/yumrepo enabled=1 gpgcheck=0 priority=1上面配置里有几个容易出问题的点:
baseurl的写法:file://后面跟的是绝对路径,三层斜杠,千万不要写成file:///data/yumrepo/以外的东西,注意第三个斜杠是路径根标记,很多人在这里漏了。gpgcheck=0在离线私有仓库里是最常见的设置,因为大多数从公网拉下来的包没有目的机需要的签名公钥。要是你设置的源里同时有官方源和私有源,并且私有源gpgcheck=1又没配gpgkey,yum会在校验时无差别拒绝。我在后面会专门讲。priority=1是yum-plugin-priorities插件里的设置,数字越小优先级越高,适合内网服务器同时配置了多个源时使用。没装插件的话写这个字段会被忽略,不会报错。
4.3 内网机器上的同步与启用
打包传文件的方式有很多,最省事的是tar:
tar czf yumrepo.tar.gz /data/yumrepo/解压到内网目录后,执行:
cd /etc/yum.repos.d/ && ls yum clean all yum makecache yum repolist如果repolist里看到local-offline仓库的数字不是0,就说明通了。如果数字是0,再看有没有报repomd.xml找不到这类错。
5. 离线源使用中的高频坑:我的排查车辙
这个环节可以说是我最想分享的部分。配置源的过程本身不难,难在排查各种奇奇怪怪的报错。下面列出的四个问题,我在这几次实操中全部遇到过,按出现频率排序。
5.1 坑一:缓存与baseurl的“假性”不对应
现象:yum makecache成功,repolist能看到包数量,但一旦yum install,立马报“Cannot find a valid baseurl for repo”。甚至有些源上一秒还好好的,下一秒就报同样错误。
排查思路:先在本地用curl或file访问一下baseurl对应的路径:
curl -I file:///data/yumrepo/repodata/repomd.xml ls -l /data/yumrepo/repodata/repomd.xml如果文件存在但curl报错,多半是目录权限——/data/yumrepo的父目录没有执行权限(x权限),导致yum解析路径时无权限读取。解决:chmod o+x /data /data/yumrepo或者用chown调整属主。
还可能是baseurl写错了路径层级,比如rpm包实际在/data/yumrepo/Packages/子目录而repo文件指到了/data/yumrepo,就只能看到repodata没看到rpm包,最终在安装时报“package not found”。
这种问题一旦发生,先看路径,再看权限,不要第一件事就删缓存。
5.2 坑二:gpgcheck=1的连锁拒绝
现象:如果在内网机同时留下了官方源(.bak后缀但没彻底禁用)和私有源,而官方源依然指向外网地址,yum makecache会长时间卡住,最终超时。更隐蔽的情况是私有源开gpgcheck=1但没指定本机信任的公钥,导致所有包的签名校验失败。
排查思路:一旦报Public key for xxx.rpm is not installed,首先rpm --import你需要导入的公钥,但在私有离线仓库场景下,最直接的办法就是把gpgcheck改成0。
注意:生产环境不建议长期关闭签名校验,如果必须保留校验,请在打包的时候把公钥文件一起放进仓库,并在repo文件里用
gpgkey=file:///data/yumrepo/RPM-GPG-KEY-CentOS-7指向它。这需要你在联网机器上先把公钥复制过去。
我还遇到过一个细节:某些软件在安装后脚本里会独立调用rpm --checksig,即使repo文件里关闭了gpgcheck也会报错。这种属于软件自身行为,只能临时用rpm -ivh --nosignature过渡,不影响服务启动。
5.3 坑三:依赖缺失的“半安装失败”
现象:yum install执行到一半,报Error: Package: xxx requires: libfoo.so.1()(64bit),然后把刚下载的包全部回滚。这说明你的rpm包目录里缺少某个依赖,或者版本太老。
排查思路:缺依赖时,切忌瞎猜。按下面顺序操作:
- 在联网机器上执行
yum deplist 包名 | grep dependency,拿到完整依赖列表。 - 对比离线rpm目录里的文件,找出缺失项。
- 在联网机器上用
yumdownloader --resolve 缺失包名把对应rpm补拉回来。
我就是这样补了三轮才把所有依赖补齐。当时卡在libpcre2和openssl11上,原因是我直接repotrack nginx漏掉了部分子依赖,而补包时又没注意版本号。所以现在养成的习惯是:拉完包以后,先在存储目录里执行rpm -qR --requires $(ls *.rpm) | sort -u做全量依赖核验,把缺失项提前暴露出来。
另外,yum只在安装时才解析依赖,所以可能出现makecache正常、repolist正常、一安装就报缺依赖。这不是源坏了,是收集环节的不完整。
5.4 坑四:旧缓存残留导致源切换失败
现象:把网络源切换成离线源后,yum还是去访问原来的外网地址,几次yum install都卡在超时。
排查思路:这是最常见的症状,但根因通常是**/var/cache/yum/下的缓存没有彻底清空**。yum clean all只能清理包缓存索引,不能删除已加载的仓库元数据缓存。更稳妥的方式是:
yum clean all rm -rf /var/cache/yum/*而且,/etc/yum.repos.d/里如果还留着带外网URL的repo文件(即使后缀改成.bak也一样会被加载),yum会同时探测所有enabled的仓库。所以最保险的动作是先把.bak文件的enabled=1改成enabled=0,或者干脆移动出该目录。
说到底,离线源的绝大部分坑,都集中在“路径”“权限”“仓库元数据不匹配”“依赖不全”这四个维度。按照上面这四条车辙去排查,基本能覆盖95%的现场报错。这个是我多次内网环境配置中反复验证过的稳定排查顺序。
6. 完整实操复盘:一次离线nginx安装的现场记录
这一节把整个流程串起来,假设你面前正好有一台内网CentOS 7服务器,需要离线安装nginx + php-fpm + mysql客户端。跟着我的步骤走一遍,顺便了解每一步的目的是什么。
| 步骤 | 操作 | 用途 |
|---|---|---|
| 1 | 在联网机器装yum-utils createrepo | 准备工具 |
| 2 | repotrack -p /home/soft/rpms nginx php-fpm mysql-client | 拉取完整依赖 |
| 3 | createrepo /home/soft/rpms | 生成repodata |
| 4 | tar czf /home/soft/yumrepo.tar.gz /home/soft/rpms | 打包传输 |
| 5 | 在内网机器解压到/data/yumrepo | 放置仓库 |
| 6 | 写/etc/yum.repos.d/local.repo,禁用其他repo | 声明源 |
| 7 | yum clean all && rm -rf /var/cache/yum/* && yum makecache | 清缓存建索引 |
| 8 | yum install -y nginx php-fpm mysql-client | 安装软件 |
这里要特别叮嘱第2步:repotrack拉一个nginx确实是45个包,但如果你要装php-fpm,它会再拉约60个包,还有mysql客户端,总包数轻松上百。所以这个目录占盘可能在300MB以上,给目标机器规划磁盘时要留够空间。我当时没注意,把包传到根分区,结果后来发现/只剩几百MB,又折腾着改目录。
第6步写repo文件的细节前面已经讲过,补充一个场景:如果内网机同时有多台,可以把打包好的tar.gz和repo文件一起放在文件服务器上,内网各机器wget下来解压到同一路径,然后自行启用。这样就不用每台机器单独拉包。
最后,安装完成后验证:
nginx -v php-fpm -v看到版本号正常输出,这个离线源的使用就算闭环了。之后如果还要更新版本,重复第2、3、4步,把新rpm包丢进仓库目录,再执行createrepo --update /data/yumrepo,内网机器yum update即可。
7. 我的习惯与风险提示
几次离线源配置之后,我养成了几个固定习惯,分享出来供参考。
第一,所有离线源配置一定写文档。包括源路径、包数量、同步日期、用了哪台联网机器的系统版本、架构。原因很简单:三个月后你去排查问题,完全想不起来这个仓库是哪台机器、哪次拉取的。把README放在仓库目录里,花两分钟,省后面半天。
第二,打包传输前先做du -sh。上面那个300MB的例子不是编的,是真事。tar打包本身还要占额外空间,传到内网时还得留tmpfs空间,这些都要計画好。
第三,不同系统的仓库源不要混用。CentOS 7的包绝不能放到CentOS 8机器的仓库目录里,虽然repodata结构相似,但依赖链完全不同,轻则装不上,重则把系统的glibc覆盖成不兼容版本。除非你是红帽系的版本兼容专家,否则别做这种实验。
第四,源做成只读。在生产库中,我会用mount -o bind,ro /data/yumrepo /mnt/repo或者直接chown到一个只有yum进程可读的账户,防止误删repodata或rpm包。误删一个包,等于整个仓库失效,还要重新同步。
这些习惯不一定适合所有人,但如果你是一个人在管一堆内网机器,照着来能省掉大量重复劳动。
最后再分享一个题外话:其实很多所谓“离线装不了包”的问题,本质不是包的数量问题,而是对yum元数据机制不熟悉。只要你理解了repodata和baseurl的关系,离线源只是把“买东西的超市”从公网搬到内网,思路通了,命令就那些。希望这篇记录能帮你少走几趟弯路。