1. 项目概述:为什么需要离线升级?
最近在负责一个部署在内网环境的生产系统维护,底层操作系统用的是 openEuler 22.03-LTS-SP1。大家都知道,LTS(长期支持)版本主打的就是稳定,但为了修复一些已知的安全漏洞和获取关键的功能更新,我们决定将系统升级到最新的 SP3 版本。麻烦就麻烦在,这套环境是纯离线的,外网连不上,官方源、第三方源统统指望不上。这就意味着,整个升级过程不能像在公网环境那样,一条dnf update命令就自动搞定,所有依赖包都需要我们手动准备、搬运和安装。
这种离线升级的场景其实挺常见的,尤其是在金融、政务、军工等对网络安全有严格要求的领域,或者一些物理隔离的研发、测试环境中。如果你也面临类似情况,那么这篇从实战中踩坑总结出来的全流程指南,或许能帮你省下大量排查问题的时间。整个过程的核心思路,就是在一台能联网的“跳板机”上,模拟目标环境,下载所有必需的 RPM 包及其依赖,然后打包转移到内网,最后在内网目标机上完成升级。听起来简单,但依赖地狱和版本冲突是两大拦路虎,下面我就把每个环节的细节和避坑要点掰开揉碎了讲清楚。
2. 升级整体设计与前期规划
2.1 环境分析与方案选型
接到离线升级任务,第一步不是急着动手,而是先把情况摸清楚。我们需要明确几个关键信息:
- 源版本与目标版本:明确是从 openEuler 22.03-LTS-SP1 升级到 SP3。这属于同大版本下的子版本升级,通常不会涉及核心组件(如内核)的巨变,但包含了大量的安全补丁和累积更新。
- 系统架构:通过
uname -m确认是 x86_64 还是 aarch64。这决定了你需要下载什么架构的软件包,弄错了全盘皆输。 - 已安装软件包:使用
rpm -qa命令导出当前系统所有已安装的 RPM 包列表。这一步至关重要,因为离线升级不是要安装一个全新的系统,而是基于现有软件集合进行更新。你需要确保下载的更新包能覆盖当前系统的大部分软件,特别是关键的系统组件和业务依赖。
方案上,我们选择使用dnf的download命令来下载依赖包。虽然也有yumdownloader或repoquery等工具,但dnf download能更好地处理复杂的依赖关系,并且是 openEuler 推荐的工具链的一部分。整个流程可以拆解为三个主要阶段:
- 阶段一(联网环境):搭建一个与目标机尽可能一致的虚拟机或容器环境,使用工具下载全量升级包。
- 阶段二(介质转移):将下载好的包通过安全方式(如内网文件服务器、移动硬盘)传输到离线环境。
- 阶段三(离线环境):在目标服务器上,使用本地创建的仓库进行升级操作。
2.2 工具准备与跳板机环境搭建
工欲善其事,必先利其器。我们需要准备一台能访问互联网的机器作为跳板机,这台机器最好是一台干净的虚拟机,避免已有环境干扰。
首先,在跳板机上安装一个与目标机版本一致的 openEuler 22.03-LTS-SP1 系统。你可以从 openEuler 官网下载 ISO 镜像进行安装。安装时,建议选择“最小化安装”,这样系统最干净,下载的包也更精确。
系统安装好后,配置 openEuler 22.03-LTS-SP3 的官方 yum 源。编辑/etc/yum.repos.d/openEuler.repo文件,确保源指向正确的 SP3 仓库地址。一个基础的配置示例如下:
[OS] name=openEuler-$releasever - OS baseurl=https://repo.openeuler.org/openEuler-22.03-LTS-SP3/OS/$basearch/ enabled=1 gpgcheck=1 gpgkey=https://repo.openeuler.org/openEuler-22.03-LTS-SP3/OS/$basearch/RPM-GPG-KEY-openEuler [everything] name=openEuler-$releasever - Everything baseurl=https://repo.openeuler.org/openEuler-22.03-LTS-SP3/everything/$basearch/ enabled=1 gpgcheck=1 gpgkey=https://repo.openeuler.org/openEuler-22.03-LTS-SP3/everything/$basearch/RPM-GPG-KEY-openEuler配置完成后,运行dnf clean all && dnf makecache更新元数据缓存。
注意:务必确保跳板机的架构(x86_64/aarch64)与目标机完全一致。另外,如果目标机上安装了一些非官方源(如 EPEL、NVIDIA、MySQL 等)的软件,你需要在跳板机上也添加对应的源,否则无法下载这些软件的更新包。这往往是依赖缺失的罪魁祸首。
3. 核心环节:离线包下载与依赖解析
3.1 使用 DNF Download 下载全量更新包
这是整个流程中最关键也最容易出错的一步。我们的目标不是下载 SP3 的所有包,而是下载“从当前 SP1 系统升级到 SP3 所需”的包。这里就需要用到之前导出的已安装包列表。
假设你将目标机的包列表保存为installed_packages.list并传到了跳板机。首先,我们需要安装dnf-plugins-core,它提供了download命令插件:
sudo dnf install -y dnf-plugins-core接下来,创建一个目录用于存放下载的 RPM 包,例如/opt/offline_upgrade_sp3。
sudo mkdir -p /opt/offline_upgrade_sp3然后,使用dnf download命令进行下载。这里有一个非常重要的技巧:直接对整个列表下载可能会因为个别包的仓库问题而中断。更稳健的做法是编写一个脚本,或者使用xargs配合循环。但更推荐使用dnf的repoquery和download组合来解析依赖。
一个经过实战检验的相对可靠的方法是分两步走:
解析依赖树:使用
repoquery命令生成需要下载的包列表(包含依赖)。# 首先,确保安装了 yum-utils 或 dnf-utils sudo dnf install -y dnf-utils # 生成需要更新的包列表。--releasever 指定目标版本。 sudo dnf repoquery --installroot=/tmp/empty-root --releasever=22.03-LTS-SP3 --qf "%{name}" --upgrades | sort -u > /tmp/upgrade_packages.list这个命令会列出所有可以从当前版本升级到 SP3 的包。
--installroot指定一个空路径,以避免受当前系统已安装包的影响,力求模拟一个干净的环境去解析 SP3 的升级关系。批量下载:根据生成的列表进行下载。
cd /opt/offline_upgrade_sp3 sudo dnf download --resolve --alldeps --destdir /opt/offline_upgrade_sp3 $(cat /tmp/upgrade_packages.list)--resolve:自动解决依赖关系。--alldeps:下载所有依赖包,包括那些可能已经安装但需要更新版本的。--destdir:指定下载目录。
实操心得:
dnf download过程可能会非常漫长,并且网络不稳定可能导致中断。强烈建议使用screen或tmux会话在后台运行此命令。另外,第一次运行时很可能会因为缺少某些依赖而失败,提示找不到某个包。这时需要根据错误信息,检查是否缺少对应的仓库(如 EPEL),或者该包在 SP3 中是否已被改名或废弃。这是一个需要耐心反复迭代的过程。
3.2 处理依赖冲突与异常包
在下载过程中,你几乎一定会遇到类似这样的错误:
No match for argument: some-obscure-package Error: Unable to find a match: some-obscure-package这通常意味着:
- 该软件包来自一个你尚未配置的第三方仓库。
- 该软件包在 SP3 官方源中已被移除或改名。
- 该软件包是一个本地手动编译安装的包,不存在于任何仓库。
应对策略:
- 对于情况1:在跳板机上添加对应的第三方仓库源,然后重新运行下载命令。
- 对于情况2:这是最棘手的情况。你需要判断这个包是否关键。
- 如果不关键(例如某个旧的工具),可以考虑在升级后从目标系统卸载它,或者寻找替代品。可以在下载列表中剔除它。
- 如果关键,需要去软件项目的官方社区查找是否提供了兼容 SP3 的包,或者评估是否必须保留在 SP1 版本(这可能带来安全风险)。
- 对于情况3:这类包无法通过包管理器更新。你需要单独备份该软件的安装目录、配置和数据,并在升级完成后,手动重新编译安装或部署。
一个实用的技巧是,将dnf download的错误输出重定向到文件,然后逐一分析处理:
sudo dnf download --resolve --alldeps --destdir . $(cat list.txt) 2>&1 | tee download.log然后grep -i "no match\|error" download.log来快速定位问题包。
4. 创建本地仓库与升级介质准备
4.1 使用 Createrepo 构建本地 YUM 仓库
所有 RPM 包下载完成后,它们只是一堆散乱的文件。为了让离线环境中的dnf能够识别并处理依赖关系,我们必须将它们组织成一个本地 YUM 仓库。
这就需要用到createrepo_c工具(createrepo的 C 语言实现,效率更高)。首先安装它:
sudo dnf install -y createrepo_c安装完成后,进入存放 RPM 包的目录,执行创建仓库元数据的命令:
cd /opt/offline_upgrade_sp3 sudo createrepo_c .这个命令会扫描当前目录下的所有 RPM 包,生成repodata文件夹,里面包含了仓库的元数据(如 primary.xml, filelists.xml, others.xml 等),dnf正是通过这些元数据来查询包信息和依赖关系的。
注意事项:
createrepo_c .命令末尾的点号代表当前目录,千万别漏了。这个过程可能需要几分钟,取决于 RPM 包的数量和大小。完成后,你可以使用dnf repolist命令(指定本地仓库路径)来测试仓库是否创建成功,但更直接的测试是在下一步。
4.2 打包与完整性校验
仓库创建好后,就可以将整个/opt/offline_upgrade_sp3目录打包了。为了保证传输效率,通常使用tar进行压缩:
cd /opt sudo tar -czvf offline_upgrade_sp3.tar.gz offline_upgrade_sp3/完整性校验是必须的环节!在通过网络或移动介质传输大文件时,数据损坏的风险是存在的。我们需要生成压缩包的校验和:
sha256sum offline_upgrade_sp3.tar.gz > offline_upgrade_sp3.tar.gz.sha256这个sha256文件需要和tar.gz文件一起传输到离线环境。在离线环境解压前,首先进行校验:
sha256sum -c offline_upgrade_sp3.tar.gz.sha256如果显示 “OK”,说明文件完好无损。如果校验失败,必须重新传输,绝对不要使用损坏的包进行升级,否则会导致系统处于不可预测的状态。
5. 离线环境升级操作实录
5.1 前置检查与备份
在离线目标机上执行任何升级操作前,必须完成以下准备工作,这是你的“安全绳”:
- 完整系统备份:如果条件允许,对整机做快照(如果是虚拟机)或使用
dd、rsync等工具对关键分区进行完整备份。这是最彻底的回滚方案。 - 关键数据备份:备份
/etc,/home,/var(特别是/var/lib下的数据库数据,如 MySQL、PostgreSQL),以及任何自定义的服务数据目录。 - 记录当前状态:
rpm -qa > /backup/installed_packages_pre_upgrade.listdf -h记录磁盘空间。ip addr,ss -tulnp记录网络和端口状态。systemctl list-units --type=service --state=running记录运行中的服务。
- 检查磁盘空间:升级过程需要临时空间。确保
/分区和/var分区有至少 5-10GB 的可用空间。解压后的 RPM 包和临时缓存会占用空间。
5.2 配置本地仓库并执行升级
将offline_upgrade_sp3.tar.gz和校验文件传输到目标机,例如放到/opt下。校验并解压:
cd /opt sha256sum -c offline_upgrade_sp3.tar.gz.sha256 sudo tar -xzvf offline_upgrade_sp3.tar.gz解压后,目录结构为/opt/offline_upgrade_sp3/,里面是 RPM 包和repodata文件夹。接下来在目标机上创建一个本地仓库配置文件:
sudo vim /etc/yum.repos.d/local_sp3.repo写入以下内容:
[local-sp3-upgrade] name=Local OpenEuler 22.03-LTS-SP3 Upgrade Repository baseurl=file:///opt/offline_upgrade_sp3 enabled=1 gpgcheck=0 priority=1baseurl使用file://协议指向本地目录。gpgcheck=0是因为我们自建的本地仓库没有 GPG 签名。在生产环境中,如果你有内部签名机制,可以配置gpgcheck=1并指定gpgkey。priority=1设置较高的优先级,确保升级时优先使用本地仓库。
保存后,清除 DNF 缓存并启用新仓库:
sudo dnf clean all sudo dnf makecache现在,可以开始模拟升级了。强烈建议先进行测试升级(dry-run),这能预览将要发生的变化,而不会实际安装任何包:
sudo dnf upgrade --repo local-sp3-upgrade --downloadonly --assumeno或者使用更直观的:
sudo dnf check-upgrade --repo local-sp3-upgrade查看输出,确认将要升级、安装或卸载的包列表是否符合预期,特别注意是否有核心组件(如内核、glibc、systemd)被标记为更新。
如果一切看起来正常,就可以执行真正的升级了。这是一个需要耐心的过程:
sudo dnf upgrade --repo local-sp3-upgrade -y命令执行后,dnf会从本地仓库解析依赖,下载(实际上文件已在本地)并安装所有更新。整个过程不能中断,请确保服务器供电和连接稳定。
5.3 升级后必要操作与验证
升级完成后,系统不会立即重启。需要执行一系列收尾和验证工作:
更新引导配置:如果内核升级了,需要重新生成 initramfs 并更新 grub 配置。
sudo dracut -f sudo grub2-mkconfig -o /boot/grub2/grub.cfg # 对于 UEFI 系统,路径可能是 /boot/efi/EFI/openEuler/grub.cfg重启系统:这是最关键的一步,新内核和核心库将在重启后生效。
sudo reboot重启后验证:
uname -r检查内核版本是否已更新。cat /etc/os-release确认系统版本号变为 22.03-LTS-SP3。rpm -qa | grep -E \"kernel|systemd|glibc\"查看核心组件版本。systemctl list-units --type=service --state=failed检查是否有服务启动失败。- 运行主要业务应用,进行功能性测试。
6. 常见问题排查与回滚方案
6.1 升级过程中遇到的典型错误
即使准备再充分,实际升级时也可能遇到问题。下面是一些常见错误及解决方法:
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
Error: Transaction check error | 包依赖冲突,例如新包 A 需要新版本库 B,但旧包 C 又依赖旧版本库 B。 | 这是最经典的依赖地狱。首先仔细看错误信息,定位冲突的包。尝试dnf remove [包C]移除冲突的旧包(确保它不重要),或者寻找是否有一个兼容的旧包 C 的更新版本在仓库里。操作前务必确认移除的包不影响业务。 |
Package X is already installed | 本地仓库中的包版本与已安装版本相同或更旧。 | 这通常是因为下载的包集合中混入了不需要更新的包。可以暂时在本地仓库配置中排除这个包,或者使用dnf upgrade --exclude=包名跳过它。 |
Failed dependencies: libY.so.Z is needed by X | 动态库依赖缺失。虽然下载了 RPM,但可能某个底层库的版本不对。 | 使用dnf provides */libY.so.Z在本地仓库中查找哪个包提供此库。确保提供该库的包已被正确下载并包含在升级事务中。 |
| 升级后服务无法启动 | 1. 配置文件格式在新版本中不兼容。 2. 服务的依赖路径或环境变量改变。 | 1. 检查服务的日志 (journalctl -u [服务名])。2. 对比备份的旧配置文件与新版本的默认配置文件,进行合并或调整。 3. 查看是否有相关的 selinux策略需要调整。 |
6.2 系统回滚操作指南
如果升级后系统出现严重问题且无法快速修复,回滚是最后的保障。回滚的前提是:你没有在升级后清除 DNF 的历史事务记录和旧版本的 RPM 包。
查看 DNF 历史:
sudo dnf history list找到最近那次升级操作的 ID(例如 10)。
执行回滚:
sudo dnf history undo 10 -y这条命令会尝试逆操作,将系统恢复到升级前的状态。
回滚后重启:
sudo reboot
重要警告:回滚并非 100% 可靠,特别是当升级过程中涉及了复杂的依赖变更或配置文件修改时。它高度依赖于 DNF 事务记录的完整性和旧 RPM 包的保留情况。因此,完整的系统备份才是你最可靠的回滚手段。在升级前制作可启动的备份镜像,在出现不可恢复的错误时,直接从备份还原,这是生产环境最稳妥的做法。
整个离线升级过程,考验的不仅是技术,更是耐心和细致。每一个环节的检查与验证,都是为最终的成功增加砝码。尤其是在处理依赖和冲突时,切忌盲目操作,多查资料,多模拟测试。希望这份详尽的记录能让你在下次面对类似任务时,心中更有底气。