搭建局域网 YUM 源:createrepo 制作仓库,OpenResty 发布(Rocky Linux 9)
内网服务器访问不了公网 yum 源,打补丁、装软件就没了着落。解法是把 RPM 包集中到一台服务器,用 createrepo 生成仓库元数据,再用 OpenResty 以 HTTP 方式发布出去——客户端只需要一个 repo 文件指向这台服务器,dnf install 的体验和公网源完全一致。本文在 Rocky Linux 9.3 容器上从头到尾跑通这套流程,CentOS 7 时代的差异单独列出。
整体思路
整个方案分三步:
- 把 RPM 包集中到服务器的一个目录
- 用 createrepo_c 命令创建仓库
- 配置 OpenResty 发布 YUM 源
需要准备两个软件:
| 软件 | 用途 | 版本与来源 |
|---|---|---|
| createrepo_c | 制作仓库元数据 | Rocky 9 AppStream 自带,dnf install 即得 |
| OpenResty | 以 HTTP 发布仓库 | 1.31.1.1,官方 yum 源安装 |
OpenResty 的官方下载页在这里,源码编译的安装步骤可以参考这篇文章;本篇直接用官方 yum 源安装,比编译省事得多。
发布层选 HTTP 而不是 FTP 或直接共享目录,理由有三个:dnf 原生支持 http baseurl,客户端零配置;HTTP 单向、无状态,防火墙上只需要开一个端口;OpenResty 顺手还能干别的——同一台 nginx 上再挂个内部静态站点毫无压力。仓库元数据本身只有几 MB,哪怕几百台客户端来拉,缓存命中率也足够高。
部署
准备 RPM 包
RPM 包有两个来源:
- 系统安装 ISO 镜像里自带的包,挂载后把 Packages 目录拷出来
- 同步公网镜像到本地
零散补包也可以在能上网的机器上用 dnf download --resolve 包名 直接拉取,--resolve 会连依赖一起下载。这个参数不能省——实测只 dnf download jq 不带 --resolve,包放进仓库后客户端安装直接报 nothing provides libonig.so.5()(64bit):仓库里只有 jq 自己,依赖 libonig 没跟进门。自建源最常见的坑就在这,收集包时务必连依赖一起收。
把收集到的包统一放进仓库目录(本文用 /data/yum-repo/)。ISO 里的包拷出来时记得用 mount -o loop CentOS-7-x86_64-DVD.iso /mnt/iso 这类方式挂载, Packages 目录整个拷贝即可;目录组织上建议按系统版本分开(比如 /data/yum-repo/9/、/data/yum-repo/7/ 各建各的 repodata),混在一起会出现 9 的客户端解析到 7 的包元数据的怪事。
安装 createrepo
Rocky 9 上 createrepo 已经由 C 重写的 createrepo_c 接替,AppStream 源直接装:
1 | dnf install createrepo_c |
装完系统里同时存在 createrepo 和 createrepo_c 两个命令,前者是指向后者的兼容软链,用法一致。el7 时代那串 deltarpm、python-deltarpm、libxml2-python 的 rpm 依赖链在 el9 上不需要了,一个包解决。
创建仓库
1 | createrepo_c -d /data/yum-repo/ |
这条命令扫描 /data/yum-repo/ 下的所有 RPM 包,在目录里生成 repodata/ 元数据,客户端就是靠这份索引来查包的。-d 表示同时生成 sqlite 格式索引(el9 上默认也会生成)。

生成后的 repodata/ 里有 7 个文件:primary、filelists、other 三类信息各有 xml.gz 和 sqlite.bz2 两份,外加总入口 repomd.xml。和 CentOS 7 时代一眼能认出包名的 *-primary.xml.gz 不同,el9 的元数据文件改成了哈希命名,认文件只能看 repomd.xml 里的引用关系,但客户端解析不受影响。
安装并配置 OpenResty
添加 openresty 官方 yum 源再安装:
1 | # /etc/yum.repos.d/openresty.repo |
1 | dnf install openresty |
注:官方配置模板里
repo_gpgcheck=1,实测在容器里报repomd.xml GPG signature verification error: Bad GPG signature,改 0 后正常。生产环境若要保留元数据验签,先确认系统时间准确、GPG 公钥已导入。
OpenResty 装好后配置文件在 /usr/local/openresty/nginx/conf/nginx.conf,在 server 块里加一段 location,把站点根路径映射到仓库目录:
1 | location / { |
autoindex on 让目录可浏览,方便人工核对包是否存在。启动服务并验证:
1 | /usr/local/openresty/bin/openresty |

HTTP 200 且能返回 repomd.xml 内容,发布就通了。客户端可以按路径下载 rpm 和 repodata。
客户端配置
写一个自定义 repo 文件:
1 | # /etc/yum.repos.d/local.repo |
其中 IP:PORT 为 YUM 服务器的地址和对外提供的端口。如果服务器端按系统版本分了目录,baseurl 里可以直接用 dnf 的内置变量拼路径,例如 baseurl=http://IP:PORT/$releasever/$basearch,客户端各自解析成自己的版本和架构,一份 repo 文件全网通用。然后在客户端重建缓存并做一次真实安装——全程禁用其他源,只留 local-yum,装得下来才算通:
1 | dnf clean all |

Installed 与 Complete! 出现,说明元数据解析、依赖判断、包下载整条链路都在自建源上闭环了。
日常维护
更新自建仓库
往目录里追加了新包之后,不用全量重建,增量更新即可:
1 | createrepo_c --update /data/yum-repo/ |
--update 只把新增或变化的包合并进现有索引,仓库大时比全量重建快得多。更新完在客户端 dnf clean all && dnf makecache 一次,新包可见。缓存过期这件事客户端自己也会处理——repomd.xml 带 metadata_expire(Rocky 官方源默认 6 小时),自建源不写这个字段时 dnf 按默认策略刷新,急用的包手动 clean 再 makecache 最稳妥。
下载失败排查
客户端安装时报这个错:
1 | Error: Error downloading packages: |
含义是 repodata 索引里记录的包文件在源上已经不存在了——包被移动或删除后索引没有跟着更新,客户端拿着旧索引去找文件,404 一路试到放弃。yum 时代同类故障的报错是 [Errno 2] Local file does not exist,dnf 的提示换成上面这句,本质是同一个病。
修复路径实测有效:把误删的包补回目录(或确认该包确实该下架),然后删除 repodata/ 重新 createrepo_c -d 重建,或者直接 createrepo_c --update 增量合并,最后客户端清缓存重试。
排查这类问题有个自检三板斧:先在服务器本机 curl -sI http://127.0.0.1:8080/repodata/repomd.xml 确认发布层返回 200;再在客户端 dnf repolist 确认源可见;最后 dnf makecache 看元数据拉取有无报错。三层分别对应发布、寻址、索引,哪层先断,问题就在哪层。

历史版本差异(CentOS 7)
老读者从 CentOS 7 迁过来,主要注意这四处:
- 包名:
createrepo(Python 实现)换成createrepo_c,el7 那套 deltarpm 依赖链不用再手工 rpm -ivh; - 命令:客户端
yum换成dnf,yum makecache对应dnf makecache,yum 在 el9 里只是 dnf 的别名; - 元数据命名:repodata 从可读文件名变成哈希命名,排查看 repomd.xml;
- 收集包:
yumdownloader --resolve换成dnf download --resolve,不解析依赖的坑两代系统一模一样。 - 装 OpenResty:el7 年代基本靠源码编译,如今官方直接提供 Rocky/RHEL 9 的 yum 源,
dnf install openresty一条命令,升级维护都省心。
注意事项
- 依赖必须齐:仓库里只有主包没有依赖,客户端装的时候报 nothing provides;收集包一律
--resolve,必要时把对应版本的整个基础仓库同步进来。 - 仓库目录和 repodata 必须保持同步:包有增删就要
createrepo_c --update,客户端再执行dnf clean all、dnf makecache,否则就是上面那个 Cannot download。 - gpgcheck=0 的边界:跳过签名校验,内网自用没问题,不要把这种源暴露到公网;对安全性有要求的场景给自建源配好自己的签名(rpmsign + 导入公钥)。
- ISO 只覆盖系统自带的包:EPEL 这类第三方源的包要另行收集后再入库,别指望一个 ISO 通吃。
- 防火墙放行:服务器记得放行发布端口(如 8080),
firewall-cmd --add-port=8080/tcp --permanent && firewall-cmd --reload,不然客户端连不上还以为源坏了。
一套局域网 YUM 源,服务端两个软件、客户端一个 repo 文件,成本几乎为零。真正决定它好不好用的,是包收集时有没有带上依赖、增删包之后索引有没有跟着走——这两件事做扎实,内网机器的 dnf install 就和公网一样顺手。等哪天要扩到多机房,把这台 OpenResty 换成上游,再在各个机房用反向代理缓存一份,这套架构还能继续长。
本文由作者 2020-2024 年间的 CSDN 博客文章重构而来,原发布于 CSDN。