内网离线YUM源搭建:gpmall-repo中间件部署与排障 简介gpmall-repo.rar 是面向云计算与分布式电商平台运维学习者的离线软件仓库资源包主要服务于 gpmall 服务部署、实验环境搭建及内网依赖安装场景可缓解外网源访问不稳定、组件版本不一致等常见问题。压缩包共收录173个文件约304.19MB其中166个为RPM安装包覆盖Logstash、MongoDB、OpenJDK、Elasticsearch等常用服务端组件另有gz、bz2格式的仓库元数据及1个XML文件便于构建本地YUM源并完成依赖索引。已有187人学习下载适合初中级运维人员和云计算课程实践者参考。使用时可直接获得较完整的离线依赖集合减少逐个查找安装包的重复工作也便于对照组件版本梳理部署流程、复现实验环境并排查依赖冲突提升gpmall相关服务搭建与验证效率。1. 一份压缩包解决的是内网机器装不上中间件这件事上周有人发来一个压缩包说机房的内网机器根本连不上外网yum install一条命令都跑不通一个电商微服务项目卡在装 MySQL 上卡了整整两天。我打开看了看包名叫 gpmall-repo.rar解压出来是成堆的 rpm 包和一批 Maven 依赖 jar。这种资源在云计算方向的课程设计和微服务实训里出现频率很高把一套商城系统跑起来需要的运行时、中间件、依赖包整体离线打包让没有外网的机器也能把环境从零搭起来。它解决的是环境依赖供给的问题并非业务代码层面的问题。适合三类人断网机房里要做部署实训的学生、需要给客户演示一套微服务 Demo 但对方网络封闭的工程师、以及想把一次性环境固化成内网基线的人。下面按我实际的拆包顺序写能照着复现。2. 先拆包再动手gpmall-repo 里到底放了什么拿到一个几十上百兆的 rar最忌讳的动作是直接扔到/usr/local下面一通解压然后rpm -ivh *.rpm一把梭。原因很实际这个包里的组件是有依赖顺序的一口气装完大概率会撞上冲突而且装坏了很难回退。我的习惯是先花十分钟把目录结构和文件清单摸清楚再决定这套东西装到哪个路径、按什么顺序装。2.1 解包、校验与三层目录盘点Linux 上处理 rar 不像 zip 那么顺手unrar未必在源里用7z更稳。解压目标目录单独开一个比如/opt/offline-repo别和系统自带的/usr、/usr/local混在一起后面要撤的时候直接删目录就行不用一个个rpm -e。# 1) 先不解压只看目录树判断包体规模 7z l gpmall-repo.rar | head -40 # 2) 解到独立目录-o 后面不能有空格 mkdir -p /opt/offline-repo 7z x gpmall-repo.rar -o/opt/offline-repo -y # 3) 生成校验清单往后每次搬运或改包都对比一次 cd /opt/offline-repo find . -maxdepth 3 -type d | sort find . -type f -name *.rpm | wc -l md5sum $(find . -type f -name *.rpm) /opt/repo.md57z l只列内容不出文件用来判断包体里是不是还有嵌套压缩-o指定输出路径写成分开的形式会报参数错误最后那份 md5 清单是后悔药跨机器拷贝、U 盘搬运之后跑一遍md5sum -c /opt/repo.md5能立刻发现是不是传坏了。这一步在实际操作里没少救场曾有次内网拷贝后有 3 个 rpm 是半截文件yum报的错却是一句含糊的“文件损坏”排查了两小时才想到校验。目录盘点时重点看三层结构最外层通常是安装脚本和说明中间层是 rpm 包集合可能还有一层独立放 Maven 依赖或者 SQL 初始化脚本。这三层的处理方式完全不同rpm 要走 yum 源Maven 依赖要落到settings.xml指向的本地仓库SQL 脚本要在服务起来之后手工灌。# 按扩展名分类看清包体构成 find /opt/offline-repo -type f | sed s/.*\.// | sort | uniq -c | sort -rn # 看 rpm 包的架构和版本确认和目标机器匹配 rpm -qp --queryformat %{NAME} %{VERSION}-%{RELEASE} %{ARCH}\n \ /opt/offline-repo/**/*.rpm 2/dev/null | sort -u | head -30第二条命令是关键。gpmall-repo这种离线包最常见的问题是架构不对在 x86_64 机器上解出来的却是 aarch64 的包yum install会直接告诉你“没有可用的软件包”而包明明就在目录里。先确认%{ARCH}和目标机uname -m一致能省掉一半的无效排查。2.2 从文件清单反推部署拓扑与端口文件清单本身就是一张部署图。包里出现了zookeeper基本就能断定 Kafka 是跑在它上面的出现了elasticsearch就意味着后面要调内核参数出现了nginx和一堆*-web之类的 war 或 jar说明前端和网关是分开部署的。把这套组件映射成端口表装机的时候才知道该开哪些防火墙规则。组件包里对应物默认端口前置依赖JDKjdk-*.rpm无无最先装MySQLmysql-community-*.rpm一组3306需先清掉mariadb-libsRedisredis-*.rpm或源码目录6379无ZooKeeperzookeeper-*.rpm2181 / 2888 / 3888JDKKafkakafka_*.tgz或 rpm9092ZooKeeper 必须先起Elasticsearchelasticsearch-*.rpm9200 / 9300JDK需调vm.max_map_countNginxnginx-*.rpm80 / 443无这张表不是抄来的模板是从文件清单里逐个核对出来的。如果包里只有 Kafka 没有 ZooKeeper那说明部署方案里用的是 Kafka 内置的 KRaft 模式或者 ZK 在外部队列机器上这时候装完直接起 Kafka 会一直卡在“等待连接”得回头确认。还有一类信息藏在 rpm 的依赖声明里# 查单个包的依赖判断装机顺序 rpm -qpR /opt/offline-repo/rpms/mysql-community-server-*.rpm | head -20 # 反过来查包里哪些 rpm 是给别人当依赖的 rpm -qp --provides /opt/offline-repo/rpms/*.rpm 2/dev/null | grep -i libaio\|ncursesrpm -qpR打印的是这个包需要什么--provides打印的是它能提供什么。两边一对安装顺序基本就自动排出来了。我一般会把这份依赖关系先落到一张纸上装的时候照着走比装一步报一次错再去搜要快得多。3. 把静态目录变成 YUM 源createrepo 与 repo 文件的两端配置rpm 文件放在目录里不等于能用yum装。yum认的是 repodata 元数据也就是一个repodata/子目录里那几份 xml 文件。没有它yum走到这个目录面前会当作不存在。这一步是整个离线部署的分水岭做对了后面一路顺做错了就是无穷无尽的 “Cannot retrieve repository metadata”。3.1 服务端目录发布与 createrepo 元数据生成先明确一件事包里的 rpm 是不是全都堆在一个目录里。如果分了os/、extras/这种子目录那每个子目录都是一个独立的仓库要分别建元数据如果全平铺在一个目录下一次createrepo就够。# 1) 统一 rpm 存放路径避免多层嵌套 mkdir -p /opt/offline-repo/rpms find /opt/offline-repo -name *.rpm -exec mv -n {} /opt/offline-repo/rpms/ \; # 2) createrepo 未必在系统里先用包内自带的装上 rpm -ivh /opt/offline-repo/rpms/createrepo*.rpm \ /opt/offline-repo/rpms/python-deltarpm*.rpm 2/dev/null || \ yum install -y createrepo # 3) 生成元数据-v 看进度--update 用于后续增量 createrepo -v /opt/offline-repo/rpms # 目录内容有变化时用增量重建速度快很多 createrepo --update -v /opt/offline-repo/rpms # 4) 校验元数据确实生成了 ls -l /opt/offline-repo/rpms/repodata/createrepo的路径参数必须指向 rpm 所在的那一层指向上一层会生成一个空仓库repodata里的primary.xml记录数为 0客户端yum list什么都看不到但又不报错这种情况最容易让人以为配置写错了。生成完拿ls看一眼repodata里是不是有repomd.xml、primary.xml.gz、filelists.xml.gz缺哪个都不行。发布方式有两种取决于拓扑。单机自用直接走file://不用起服务# 方式一本地路径注意 file:// 后面是三个斜杠 baseurlfile:///opt/offline-repo/rpms要给内网其他机器用起个 Nginx 更合适还能顺便限速、看访问日志# 方式二HTTP 发布 cat /etc/nginx/conf.d/offline-repo.conf EOF server { listen 8080; server_name _; root /opt/offline-repo; autoindex on; # 方便浏览器直接看目录 location /rpms/ { alias /opt/offline-repo/rpms/; autoindex on; } } EOF nginx -t systemctl reload nginx # 防火墙放行只对内网段开放 firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.0.0/16 port port8080 protocoltcp accept firewall-cmd --reload curl -I http://127.0.0.1:8080/rpms/repodata/repomd.xmlautoindex on不是为了好看是排错时能直接在浏览器里翻目录确认某台机器访问到的路径到底对不对alias和root的区别要留神alias是把 location 前缀整个替换掉写错一个斜杠就会 404。最后那条curl是必做的验证返回 200 才算服务端这一侧过关返回 403 通常是 SELinux 拦了目录读权限返回 404 是路径写错了。3.2 客户端baseurl、gpgcheck 与优先级客户端这边就是写一个.repo文件。看着简单但参数怎么填直接决定后面会不会翻车。# /etc/yum.repos.d/local-offline.repo [local-offline] nameLocal Offline Repo baseurlhttp://192.168.10.20:8080/rpms/ # 单机自用改成baseurlfile:///opt/offline-repo/rpms/ enabled1 # 离线包通常没有签名这里关掉生产环境应导入自建 GPG key gpgcheck0 # 数字越小优先级越高避免公网源的同名包覆盖离线版本 priority1 # 只装仓库里有明确声明的包禁止自动去公网补齐 skip_if_unavailable0gpgcheck0是离线场景的常见妥协但要知道代价包被替换过也发现不了。稳妥做法是把包内附带的 GPG 公钥rpm --import进来然后写gpgcheck1加gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-local。priority需要yum-plugin-priorities插件如果系统没装这个字段会被静默忽略不报错也不生效。配置写完先清缓存再建缓存yum的缓存很顽固改完 repo 不清等于没改yum clean all rm -rf /var/cache/yum/* yum makecache # 验证仓库真的被识别且包数量合理 yum repolist all yum --disablerepo* --enablerepolocal-offline list available | wc -lyum repolist all的输出里local-offline那一行的状态要是enabled包数量不能是 0。数量为 0 而状态正常八成是baseurl指到了rpms的上一级。--disablerepo* --enablerepo...这个组合是用来做隔离测试的能确认某个包到底是从离线源装的还是从别处漏进来的排查版本错乱时非常好用。4. 安装顺序与版本对齐依赖链上先装谁有了可用的源剩下的问题就变成“按什么顺序装”和“怎么防止装错版本”。这两件事在离线环境里比在线环境更敏感因为在线环境装错了yum还能去公网找替代版本离线环境里装错了只有重装系统这一条路。4.1 从 JDK 到 Kafka 的启动依赖顺序顺序的原则只有一条被依赖的先装、被连接的要先起。中间件之间的“依赖”分两种装机依赖rpm 层和运行时依赖进程层两者不是一回事。JDK 是典型的装机依赖没它 Kafka 的 rpm 装都装不上ZooKeeper 对 Kafka 是运行时依赖rpm 装得上但 Kafka 起不来。#!/bin/bash # deploy-offline.sh —— 按依赖顺序离线部署任一步失败即终止 set -euo pipefail REPO--disablerepo* --enablerepolocal-offline echo [1/7] JDK yum install -y $REPO java-1.8.0-openjdk java-1.8.0-openjdk-devel java -version echo [2/7] MySQL先清冲突包再装 rpm -q mariadb-libs /dev/null 21 yum remove -y mariadb-libs || true yum install -y $REPO mysql-community-server mysql-community-client systemctl enable --now mysqld # 首次启动生成的临时密码在日志里 grep temporary password /var/log/mysqld.log | tail -1 echo [3/7] Redis yum install -y $REPO redis sed -i s/^bind 127.0.0.1/bind 0.0.0.0/ /etc/redis.conf sed -i s/^protected-mode yes/protected-mode no/ /etc/redis.conf systemctl enable --now redis redis-cli ping echo [4/7] ZooKeeper yum install -y $REPO zookeeper systemctl enable --now zookeeper echo ruok | timeout 2 nc 127.0.0.1 2181 echo [5/7] Kafka必须在 ZK 之后 yum install -y $REPO kafka systemctl enable --now kafka sleep 10 ss -lntp | grep 9092 echo [6/7] Elasticsearch先调内核参数否则会被 OOM killer 带走 sysctl -w vm.max_map_count262144 echo vm.max_map_count262144 /etc/sysctl.conf yum install -y $REPO elasticsearch systemctl enable --now elasticsearch echo [7/7] Nginx yum install -y $REPO nginx nginx -t systemctl enable --now nginx逐段说明关键点。set -euo pipefail是必须的中间任何一步失败就停不然脚本会带着残缺环境一路跑到底最后你看到的是“七个组件都装了但一个都不通”。--disablerepo* --enablerepolocal-offline把安装来源锁死在离线源避免机器上残留的某个公网 repo 混进来装出半新半旧的组合。MySQL 那一段先删mariadb-libs因为多数发行版自带 MariaDB 的兼容库和 MySQL 官方包的文件路径重叠yum会报file /usr/lib64/libmysqlclient.so.18 conflicts。这个冲突不解决后面装什么都没用。Redis 改bind和protected-mode只是因为内网演示图省事任何可路由环境都该配密码加 ACL。ZooKeeper 用echo ruok | nc验证返回imok说明四字命令通道通畅Kafka 那一步sleep 10后再看9092端口是因为 Kafka 启动要先去 ZK 注册起得太快查端口会误判。Elasticsearch 的vm.max_map_count必须在装之前或启动之前设默认的 65530 会导致 JVM 在初始化 mmap 时被内核拒绝进程直接消失日志里只有一行max virtual memory areas ... is too low。4.2 版本锁定excludes、priority 与手动 rpm 的边界离线环境最怕的不是装不上是装上了不兼容的版本。比如 MySQL 8 的驱动连 MySQL 5.7 的服务端认证插件对不上报的错是Unable to load authentication plugin caching_sha2_password看着像代码问题实际是版本问题。锁定版本有三个层次力度从小到大手段写法适用场景风险指定完整版本yum install mysql-community-server-5.7.36-1.el7单次安装依赖仍可能被升级repo 级排除excludemysql* kafka*防止某类包被误升级需要维护列表versionlock 插件yum versionlock add mysql-community-server长期锁定解锁需显式操作实际做法是先看包里到底有哪几个版本# 列出离线源里某个组件的所有可用版本 yum --disablerepo* --enablerepolocal-offline --showduplicates list mysql-community-server # 查已装的版本和来源 yum list installed | grep -i mysql rpm -qi mysql-community-server | grep -E Version|Release|Vendor--showduplicates这个参数很关键默认yum list每个包只显示最新一条你会以为包里只有一个版本实际上老版本也在只是被隐藏了。部署前先跑一遍把每个组件的可用版本记下来和项目要求的版本对一遍比装完再改省事得多。关于rpm -ivh --nodeps我的态度很明确除了极少数明确知道后果的场景不要用。--nodeps强装跳过了依赖检查装完yum check会报一堆 broken dependency后续任何yum操作都可能被卡住。真遇到 rpm 之间互相依赖成环正确做法是把相关的几个包放在一条命令里一起装让yum或rpm自己排序yum install -y a.rpm b.rpm c.rpmyum会解析这一批文件的内部依赖比逐个rpm -ivh靠谱得多。这个技巧叫 localinstall是离线装第三方包的常规手法。5. 避坑与排查内网源上线后最容易翻车的五个点下面这五条都是实际环境里撞出来的每条按“看到什么、为什么、怎么解”写遇到时可以直接对号入座。5.1 元数据与缓存类问题现象一yum makecache报Cannot retrieve repository metadata (repomd.xml) for repository: local-offline但curl访问那个 URL 明明能通。原因有两个方向。一是baseurl少写了一层元数据在/rpms/repodata/下而baseurl写成了/yum会去/repodata/repomd.xml找自然 404。二是createrepo执行时的工作目录不对元数据生成在了rpms的上一层或者repodata目录权限是 700Nginx 的 worker 用户读不到这时curl从本机 root 跑是通的浏览器访问却是 403。解决先用curl -I精确访问http://host:8080/rpms/repodata/repomd.xml看状态码是 404 还是 403。404 改baseurl层级403 执行chmod -R 755 /opt/offline-repo并检查 SELinuxgetenforce返回Enforcing时补一条chcon -R -t httpd_sys_content_t /opt/offline-repo。改完记得yum clean all再试否则缓存里还是旧的失败结果。现象二仓库状态是 enabled但yum list available出来是 0 条。原因是repodata存在但内容为空createrepo跑的时候目录里还没有 rpm或者跑完之后才把 rpm 挪进去。这种“元数据比文件旧”的状态最阴yum不会报错只会安静地告诉你没东西可装。解决createrepo --update -v /opt/offline-repo/rpms重建一次然后grep -c package /opt/offline-repo/rpms/repodata/*primary.xml*确认条目数和ls *.rpm | wc -l对得上。对不上就是路径搞错了先去核对find / -name *.rpm的实际分布。5.2 依赖与版本类问题现象三装完某个组件后systemctl start报error while loading shared libraries: libaio.so.1: cannot open shared object file。原因是之前用rpm -ivh --nodeps强装过把依赖检查绕过去了运行时才发现缺底层库。libaio、numactl-libs、ncurses-libs是离线包里最容易被漏掉的几个“小依赖”因为它们在在线环境里通常早就装好了打包时不会有人特意想。解决rpm -qpR 包名.rpm把完整依赖列表拉出来逐个用yum install从离线源补齐yum deplist 包名更直观会列出每个依赖当前由哪个包满足。补完之后跑一次yum check和rpm -Va前者看依赖完整性后者看文件是否被改坏。装任何第三方 rpm 之前跑一遍yum localinstall ./xxx.rpm让 yum 自己解依赖比手工rpm -ivh稳。现象四MySQL 能装但起不来systemctl status mysqld显示Failed to start日志里是权限或端口占用。端口占用的排查顺序是ss -lntp | grep 3306看是不是机器上还有个 MariaDB 或者容器占着。权限问题多半出在数据目录如果之前手动初始化过datadir或者从别的机器拷过数据目录mysql用户的属主丢失mysqld会拒绝启动。这种问题日志里往往只有一行[ERROR] InnoDB: Operating system error number 13不熟悉的话很难联想到权限。解决chown -R mysql:mysql /var/lib/mysql并确认datadir在my.cnf里指的确实是这个路径如果是全新安装直接rm -rf /var/lib/mysql/*再mysqld --initialize重新初始化比修权限快。别在还有残留ibdata1的情况下反复重启容易把数据目录搞成半初始化状态。5.3 运行环境类问题现象五Elasticsearch 起来几秒就没了日志只有Killed或max virtual memory areas vm.max_map_count [65530] is too lowKafka 则是启动后一直重连 ZooKeeper。ES 这条前面提过vm.max_map_count默认值不够Lucene 的内存映射被内核拒绝。注意这个参数要在启动之前设设完sysctl -p生效已启动的进程不会自动应用。Kafka 重连 ZK 的原因通常是三种ZK 没起echo ruok | nc验证、server.properties里zookeeper.connect写的主机名解析不了getent hosts验证、或者两台机器时间差超过 session timeoutdate对比配chrony同步。我遇到过一次是主机名大小写不一致hosts里写的是Kafka01配置文件写的是kafka01ping不通但yum一切正常排查了很久。这类问题没有捷径就是按“进程 → 端口 → 域名解析 → 时间”四层依次验。6. 把它固化成内网基线自检脚本与增量更新离线包最大的价值不在第一次装通而在于后面每次重装都能得到一模一样的结果。我一般会把上面这些验证动作收成一个自检脚本装完之后跑一遍输出一份环境快照。这套习惯是从一次教训里来的某次演示前夜重装了机器MySQL 装了 8.0 而应用依赖 5.7 的驱动现场报认证插件错误翻日志翻了半小时才发现是版本漂了。# env_check.py —— 离线环境自检输出结构化报告 import subprocess, shutil, socket CHECKS [ (jdk, [java, -version]), (mysql, [mysql, --version]), (redis, [redis-server, --version]), (nginx, [nginx, -v]), ] PORTS [3306, 6379, 2181, 9092, 9200, 80] def run(cmd): try: out subprocess.run(cmd, capture_outputTrue, textTrue, timeout10) return (out.stdout out.stderr).strip().splitlines()[0] except Exception as e: return fMISSING ({e}) def port_open(p): with socket.socket() as s: s.settimeout(0.5) return s.connect_ex((127.0.0.1, p)) 0 report {} for name, cmd in CHECKS: report[name] shutil.which(cmd[0]) and run(cmd) or NOT INSTALLED report[ports] {p: port_open(p) for p in PORTS} report[ulimit_nofile] subprocess.run( [bash, -c, ulimit -n], capture_outputTrue, textTrue).stdout.strip() report[selinux] subprocess.run([getenforce], capture_outputTrue, textTrue).stdout.strip() for k, v in report.items(): print(f{k:16} {v})这段脚本做三件事查二进制在不在路径上、查关键端口有没有监听、查ulimit和 SELinux 这两个经常被忽略的系统状态。shutil.which判断的是可执行文件是否存在比直接跑命令快connect_ex返回 0 表示端口可连比ss解析文本稳定ulimit -n单独拉出来是因为 Kafka 和 ES 默认的文件句柄上限经常不够日志里报Too many open files时才想起来查就晚了。增量更新这块也有个固定套路。包里新加了 rpm不需要删掉repodata重来# 加入新包后增量重建只处理变化的条目 cp new-component-*.rpm /opt/offline-repo/rpms/ createrepo --update -v /opt/offline-repo/rpms # 客户端侧强制刷新 yum clean metadata yum makecache--update只扫描新增和变化的包包里几千个 rpm 时比全量重建快一个数量级。但要注意它不会清理已删除包留下的旧条目如果是从目录里删了 rpm得用createrepo --update --database配合--skip-stat全量重建或者干脆删掉repodata重来。这个细节不注意会出现“源里明明删了某个包客户端还能装”的怪现象。从那以后我每次拿到这类离线包第一件事都是先跑一遍环境快照把版本号、端口、依赖清单全部落成文件存进包里下次重装直接 diff 对比。版本漂移这种事事后查是黑匣子事前留底就是后悔药。希望帮到你。本文还有配套的精品资源点击获取