news 2026/10/20 7:07:23

离线环境CentOS内核rpm本地升级完整指南与回退策略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
离线环境CentOS内核rpm本地升级完整指南与回退策略

去年有一批内网服务器被安全扫描标记,原因是内核版本过旧,某个已知漏洞需要升级内核才能封堵。问题在于这批机器都在隔离网段,不能连外网yum源,也不允许为了升级内核就顺手做一次全量系统更新——那会把生产环境改到不可控。当时网上查了很多资料,大部分教程只写了yum update kernel或者源码编译,搬过来根本走不通。最后我选的路子,就是标题里写的这种:提前把内核rpm包准备好,拷进内网,用rpm本地更新的方式升级CentOS内核。这篇就把整个过程,包括选型理由、操作细节和踩坑教训,完整记录下来,给同样在离线环境里折腾内核的运维一个可落地的参考。

我默认看这篇文章的读者已经有一台CentOS机器需要动内核,知道rpm、yum的基本用法,但可能没系统梳理过"本地更新内核"这条路到底怎么走、哪些地方容易翻车。下面所有步骤都是我在实际环境里验证过的,按顺序来就行。

1. 为什么偏要选rpm本地更新这条路

1.1 三种常见内核升级方式的取舍

拿到"升级内核"这个需求的时候,大多数人的第一反应是直接yum update kernel。这种方式本身没有错,它会自动解析依赖、自动生成引导项、自动更新grub菜单,体验确实顺畅。但它有个硬前提:机器必须能访问可用的yum源。而且yum update kernel在操作时往往会把与kernel关联的其他软件包一并带起来,比如kernel-tools、systemd的某些组件,升级面比预期大很多。对于"我只想换内核、其余一律不动"的运维场景,这条路径的副作用偏大。

源码编译是另一条路。下载内核源码、make menuconfig配置、make -j编译、make modules_install、make install,一套下来时间成本很高,对机器上的编译工具链也有要求。更麻烦的是回退逻辑:源码安装的新内核会覆盖到/boot和/lib/modules,一旦新内核出了问题,你没有一个rpm记录可以快速删除,只能手动清理文件、手动修grub,生产环境里这是很大的负担。

rpm本地更新恰好站在两者中间。它不要求目标机器有外网,只要求你手里有对应的内核rpm包;它不会自动把无关软件一起升级,变更面非常小;它由rpm包管理器统一记录,装了什么、删了什么一清二楚,出了问题可以像卸普通软件一样把内核卸掉,回退路径做得比别人干净得多。

1.2 本地方案的适用边界:什么情况下该选它

从我自己的实践看,rpm本地更新并不适合所有场景,它有明确的适用区间。最典型的情况就是内网隔离环境——机器上不了外网,yum源不可用,但安全通告又催着必须换内核。这个时候,提前在外网机器上把内核rpm包下载下来,拷贝到目标机器上用rpm安装,几乎是唯一兼顾效率和可控性的方式。

还有一种场景是版本锁定。有些业务软件对内核版本有隐式依赖,比如某个数据库或者某个硬件驱动只在本版本内核上验证过,贸然升级到最新内核会带来不必要的兼容性风险。rpm本地更新允许你精确指定某个内核版本安装,而不是被动接受源里的最新版。

如果你的机器明明有完整外网源、也没有版本锁定要求,那直接用yum update kernel就好,没必要绕一圈做本地安装。本地方案最大的价值在于"离线"和"精确"两个词,缺一个它的优势就不明显了。

2. 动手前先摸清楚现状:环境确认与内核包准备

2.1 确认系统引导模式和分区空间

很多人刚拿到机器就急着下载内核包,结果装到一半才发现/boot分区满了或者引导模式判断错误。我的习惯是先花五分钟把机器现状摸一遍,命令不多,但每一条都是在为后面的操作排雷。

# 当前运行的内核版本 uname -r # 系统发行版版本 cat /etc/redhat-release # 已安装的内核包一览 rpm -qa kernel # /boot 分区剩余空间 df -h /boot # 判断是UEFI还是BIOS启动 [ -d /sys/firmware/efi ] && echo UEFI || echo BIOS

uname -r告诉你现在跑在哪个内核上,rpm -qa kernel告诉你机器上一共装了哪些内核。后面做回退方案时,这两条信息直接决定了你要保留什么、可以删除什么。df -h /boot这条很多人容易漏,后面我会专门讲为什么它那么重要,这里先记住一个原则:/boot里每多装一个内核,大约要占用 100MB 到 200MB 的空间,分区太小的话第二个内核还没装完就没地方了。

UEFI和BIOS的差异主要体现在grub配置文件的路径上。BIOS机器grub配置文件在/boot/grub2/grub.cfg,UEFI机器一般在/boot/efi/EFI/centos/grub.cfg(发行版不同目录名会有差异)。这个差异不影响rpm包的安装,但影响你后面运行grub2-mkconfig时输出的目标路径,所以现在确认清楚能省不少事。

2.2 内核rpm包从哪里获取

明确要装的内核版本之后,下一步是准备rpm包。最稳妥的做法是在一台能联网、且CentOS版本和目标机一致的机器上,用yumdownloader把内核包连同依赖一起拉下来。

# 安装yum-utils工具包 yum install -y yum-utils # 建立存放目录 mkdir -p /tmp/kernel-rpms # 下载内核及其配套包(--resolve会自动拉取依赖) yumdownloader --resolve --destdir=/tmp/kernel-rpms kernel kernel-devel kernel-headers

如果打算从社区维护的第三方内核仓库获取新版本内核,逻辑也差不多:先在联网机器上安装该仓库rpm包,再用yumdownloader下载对应内核包。这类仓库通常会提供"长期维护版"和"主线最新版"两个系列,生产环境我一般偏向长期维护版本,它稳定性更好、补丁周期长,不至于装上之后没多久就要再次升级。

需要注意一点:kernel、kernel-devel、kernel-headers三个rpm包的版本号必须一致,否则后续编译第三方模块时头文件版本对不上,会报各种奇怪错误。如果你只是想单纯换内核、不打算在新内核上编译任何外部模块,kernel-devel可以不要;但我仍然建议把它一起拷过去,多一个文件不影响什么,真到要用的时候你会发现它无比重要。

2.3 离线依赖梳理:别只搬一个kernel文件回家

rpm工具的设计哲学和yum不同:yum会自动解析并补齐依赖,rpm -ivh遇到缺依赖直接报错退出。这意味着离线环境下,你必须在源机器上就把依赖带全。这里有一个我在实践中碰到的教训:最初我以为内核包只需要kernel一个rpm文件,结果在目标机器上rpm -ivh时报出缺elfutils-libelf的依赖,整个安装流程卡住。后来回到联网机器上重新用yumdownloader --resolve拉完整套文件,才把事情理顺。

# 查看下载目录里的文件清单 ls -lh /tmp/kernel-rpms/

拷贝进内网机器的手段就因地制宜了,能走跳板机scp最方便,不能走就U盘。本质上这就是"本地安装"的字面含义——所有安装动作都基于机器上已经存在的rpm文件,不依赖任何网络通道。如果你需要批量更新多台机器,同一个目录的rpm文件可以直接复制到每台机器上反复使用,这也是本地方案在批量场景下的隐藏红利。

3. 安装与引导变更的完整操作:每一步都讲清楚为什么

3.1 包安装顺序与依赖处理

把所有rpm包拷贝到目标机器,进入存放目录,执行安装。我通常在CentOS 7上这样操作:

cd /tmp/kernel-rpms # 一次传入多个rpm,让rpm一次性解析它们的共同依赖 rpm -ivh kernel-*.rpm

这里的kernel-*.rpm会匹配到kernel、kernel-devel、kernel-headers等所有以kernel开头的包。一次传入多个rpm和逐条执行的区别在于:rpm会在安装前统一分析所有这些包的依赖关系,而不是装完一个再单独看下一个,能减少一部分"装完A才意识到B互相冲突"的情况。

如果执行过程中报依赖缺失,不要拿到--nodeps就硬上。--nodeps会让rpm跳过依赖检查,但缺失的依赖会在系统里埋雷——轻则某个工具运行异常,重则以后每次执行yum操作都会报同样的依赖错误。正确做法是记录下报错信息里的缺失包名,回到联网机器上用yumdownloader --resolve下载对应包,再拷贝过来安装。

在某些离线环境里,还有一个更顺滑的变体:把准备好的所有rpm文件放到一个目录,用createrepo把这个目录做成一个本地文件源,然后写一个repo文件指过去,再用yum localinstall -y kernel-*.rpm完成安装。这种方式的好处是yum仍然在工作,依赖解析和安装日志都由yum统一管理,比裸rpm -ivh的可视化更好。缺点是你需要额外安装createrepo工具并且多几步配置。如果机器数量少、只是临时装一次内核,直接用rpm -ivh就够了,没必要为一次性操作搭建本地源。

安装完成后,立刻检查内核文件是否落位:

# 查看/boot下的新内核文件 ls -lh /boot/vmlinuz-* # 确认新内核出现在rpm列表中 rpm -qa kernel

正常情况下,/boot目录下会多出一整套新版本文件:vmlinuz-新版本、initramfs-新版本.img、System.map-新版本、config-新版本。其中initramfs是安装rpm包时自动生成的,它负责在启动阶段加载根文件系统所需的模块,不需要我们手动干预。如果你发现新内核文件都在但缺了initramfs,那就要警觉,说明安装过程中某一步没走完整,后面我会讲怎么补救。

3.2 引导加载器配置:不改默认启动项等于白装

这是整个升级流程里最容易翻车的地方。原因是很多人想当然地以为"内核rpm装好了,重启自然就进新内核了"。实际完全不是这样。CentOS的多内核机制里,新内核只是被加入到了grub菜单中,系统当前的默认启动项仍然是旧内核。不改默认项,重启一百次都还是老内核。

首先要让grub识别到新内核的菜单项:

# BIOS引导的机器 grub2-mkconfig -o /boot/grub2/grub.cfg # UEFI引导的机器(输出路径根据具体发行版目录调整) grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg

这条命令会扫描/boot里的所有内核文件,重新生成完整的grub启动菜单。执行完之后,新内核的菜单项已经存在了,但系统默认项还没变。接下来用grubby设置新内核为默认启动项:

# 查看当前默认内核 grubby --default-kernel # 查看所有启动菜单项 grubby --info=ALL # 将默认启动项设置为新内核 grubby --set-default /boot/vmlinuz-新内核版本号 # 再次确认默认项是否切换成功 grubby --default-kernel

在CentOS 7上,grub2-set-default也是可用的方案,但grubby在CentOS 7、8、9上都能通用,用一套命令就能覆盖不同版本的环境,我自己更习惯用它。关键判断点是最后一步:grubby --default-kernel输出的路径必须是你刚安装的新内核,否则说明这一步操作没有生效,需要回头检查内核路径是否写对。

这里补充一句,为什么要手动改默认项、为什么发行版不帮我们自动切:因为有相当多运维场景要求"装好内核但暂时不重启",开发者会把默认项留在旧内核上,等业务低峰期再手动切过去。这种设计本身是给运维留余地的,但也意味着如果不主动修改默认项,升级就是无效的。

3.3 重启与归属验证:确保你已经跑在新内核上

确认默认启动项已经指向新内核之后,就可以安排重启了。重启前我习惯再做一次快速检查:grubby --default-kernel的输出、/boot里的新内核文件都在、磁盘空间足够,三个条件满足就执行reboot。

重启回来后,验证动作不多但必须做:

# 确认实际运行的内核版本 uname -r # 查看本次启动的时间 uptime -s # 检查已加载的内核模块数量是否正常 lsmod | wc -l # 查看启动日志里有没有明显报错 dmesg | grep -iE "error|fail" | head -20

uname -r是最直接的验证,输出必须等于你安装的新内核版本号。如果发现重启后还在旧内核上,基本可以断定是上一步默认启动项没设置成功,不用慌,重新执行grubby --set-default再重启一次就好。

这里我要多提醒一句:如果操作的是远程服务器,重启之前务必确认机器有带外管理通道或者可以进机房。因为新内核可能导致某个驱动没有正常加载,网络接口甚至可能起不来,一旦 ping 不通,你唯一能做的就是去物理控制台处理。这个场景我经历过,结论是在远程机器上动内核之前,评估"如果网络掉了怎么办"比评估"新内核有多快"重要一百倍。

4. 回退兜底设计:给生产环境留一条保命的后路

4.1 为什么同时保留两个内核

rpm包安装新内核并不会覆盖旧内核,系统会同时保留新旧两套内核文件,grub菜单里也会同时出现两个入口。这不是bug,是Linux发行版的刻意设计——多内核共存是系统稳定性的最后底线。

我在处理生产机器时,从不在升级当天删除旧内核。至少要等新内核跑几天、确认业务无明显异常、驱动加载无告警之后,再考虑清理。保留多个内核的成本不过是/boot里几百MB空间和grub菜单里多一行项,这点代价换一次失败的保险,太划算了。

你可以用下面命令确认当前系统和已安装内核的状态:

# 列出已安装的内核 rpm -qa kernel # 查看当前运行的内核 uname -r # 查看当前默认启动项 grubby --default-kernel

对比这三个输出,你能清楚看到"当前运行的"和"默认启动项"分别指向哪个版本。正常情况它们应该一致;如果发现不一致,说明之前设置默认项时操作有误,要及时修正。

4.2 新内核启动异常时的回退操作

新内核出问题分不同严重程度,回退方式也分三种情况。

第一种:系统能进、命令行能敲、但业务跑不起来,或者某些驱动报错。这种情况最友好,直接把默认启动项切回旧内核重启即可:

grubby --set-default /boot/vmlinuz-旧内核版本号 # 确认已切回 grubby --default-kernel # 重启进入旧内核 reboot

第二种:新内核启动过程中直接卡住,根本进不了系统。这时重启机器,在grub菜单出现时用方向键选择旧内核的菜单项回车启动。进入系统后,按第一种情况里的方式把默认项改回旧内核,避免下一次开机又自动往新内核里撞。

第三种:grub菜单本身都异常了,比如升级过程中grub配置写坏、启动项全丢。这种情况最麻烦,常见思路是进入故障救援模式,或者用启动介质引导后再修复grub配置。处理逻辑是先恢复一个可用的系统入口,再重新生成grub菜单。这种场景不太常见,但如果你负责的机器很重要,最好提前在测试机上演练一遍救援介质的使用流程,真到用的时候不至于手忙脚乱。

4.3 稳定运行后的旧内核清理时机与操作

新内核稳定运行几天后,清理就提上日程了。清理的目的是释放/boot空间、减少grub菜单里的杂质项。我的习惯是保留当前运行的新内核和一个最旧的备用内核,中间那些历史版本一次性清掉。

# 列出所有已安装的内核 rpm -qa kernel # 以rpm命令删除指定旧内核 rpm -e kernel-旧内核版本号 # 用yum删除也可以 yum remove -y kernel-旧内核版本号

删除时务必仔细核对版本号,我见过有人手滑把当前运行的内核也删了,导致系统虽然还在运行,但rpm库里已没有对应内核记录,下次更新或重装组件时各种连环依赖错误。稳妥起见,删除前先跑一次uname -r确认当前内核,然后和rpm -qa kernel的输出比对,只删那些既不是当前运行、也不是默认启动项的旧版本。

清理完旧内核后,重新执行一次grub2-mkconfig让菜单跟实际文件保持一致。这一步可以避免grub菜单里残留指向已删除内核的灰色启动项。

5. 实操中真正容易踩的坑与自救思路

5.1 /boot分区爆满:最容易翻车的环境问题

前面我反复提到/boot空间不是无限的,因为这是我在实际环境里被绊倒过的点。某台机器原本已经装了三个内核,我下载好新内核包后直接rpm -ivh,结果在写入阶段报no space left on device。一看df -h /boot,分区已经占满。

处理这类问题没有捷径,就是先腾空间再装。把最旧最没用的内核删掉一两个,释放出空间,然后重新执行安装命令。这个坑虽然不难解决,但它发生的时机非常尴尬——通常是在你满心以为马上要完成的时候,所以我把检查/boot空间提到流程最开始,就是希望你不要在最后一步被它卡住。

5.2 驱动模块与新内核匹配:网络掉线的检查思路

一个让人记忆深刻的案例是:升级内核后某台机器的网卡驱动直接失效,网络掉线,因为是远程操作,我只能让现场同事去物理控制台处理。事后排查发现,那台机器用的是某硬件厂商提供的闭源网卡驱动模块,它并没有随新内核自动编译适配。

这个问题的预防思路其实不难:升级前先用lspci -k看看网卡、存储卡这些关键硬件当前用的是哪家驱动,确认是内核自带模块还是厂商闭源模块。升级后进入新内核,再用lsmod检查对应模块是否加载成功:

# 查看内核模块是否加载 lsmod | grep 模块名 # 查看具体硬件走的驱动 lspci -k | grep -A2 -i ethernet

如果发现模块确实没有加载,就要考虑找厂商要适配新内核版本的驱动包重新安装,或者通过dkms机制在新内核上重新编译模块。这块工作最好在升级前就评估好,而不是先升级再发现问题。

5.3 依赖冲突与本地源绕行方案

离线环境下rpm -ivh报依赖错误是家常便饭。常见的是某个.so动态库文件缺失,或者是某个包对版本有硬性要求。这时候不要硬着头皮忽略依赖,最体面的解法是回到联网机器上,用yumdownloader --resolve把报错里提示的依赖包拉下来,按依赖链逐个装好,再回来装内核。

如果机器数量多、以后还要反复装包,更推荐做一个本地文件源:把所有rpm放进一个目录,用createrepo生成索引,写一个repo文件指向该目录,之后所有离线安装都可以走yum localinstall。这个方案的前期工作量稍大,但后期省心不是一点半点。

还有一个容易忽略的坑是版本锁。有些系统管理员为了防止意外升级,在yum里配置了版本锁定,把kernel包固定在某个版本号上。这种情况下即使rpm包就摆在你面前,yum localinstall也可能被版本锁拦截。排查时可以用yum versionlock list看一眼,有锁就先解除,装完再重新锁上。

5.4 特殊引导环境带来的隐藏问题

最后说两个相对低频、但遇到了会很头疼的问题。

第一个是UEFI环境下开启了Secure Boot。如果新内核rpm包没有在机器信任密钥里注册,启动时会被固件拒绝。处理办法是用mokutil导入对应公钥,或者在确认安全的前提下暂时关闭Secure Boot。这方面不同的主板、不同的发行版表现差异很大,处理时建议以硬件厂商的文档为准,不要照搬通用步骤。

第二个是根文件系统使用了LVM或磁盘加密的机器。正常情况下rpm包安装内核时会自动生成对应的initramfs,但特殊存储配置偶尔会让initramfs不包含必要的持久化模块,导致新内核启动时找不到根设备。如果你怀疑是这种情况,可以手动重新生成当前运行内核的initramfs:

dracut -f /boot/initramfs-$(uname -r).img $(uname -r)

我处理这类问题时的经验是:先用文档记录、再用测试机复现,最后才动生产机器。尤其是涉及Secure Boot、磁盘加密这类和环境强相关的特性,生产环境和测试环境不一致,往往就是踩坑的根源。

这批机器后来都顺利完成了内核切换。我个人的习惯是,内网环境升级内核,心思花在"留后路"上比花在"装得快"上有价值得多。每次动内核前,我都会确认三件事:rpm包版本清晰、默认启动项改对了、旧内核还留着。只要能随时回退,剩下的故障都是可接受的。如果你也正好在离线环境里折腾CentOS内核,希望这篇能让你少走几个弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 7:06:57

sipjs与FreeSWITCH实现网页呼叫中心:呼入呼出转移保持全解析

简介:这份资源是面向WebRTC语音通信开发者的实战示例包,围绕sipjs与FreeSWITCH搭建网页端电话应用,解决浏览器内实现分机注册、呼入呼出、通话转移与保持等核心功能的落地问题。适合具备一定前端与SIP协议基础、希望快速验证软交换对接效果的…

作者头像 李华
网站建设 2026/10/20 7:04:59

GraphRAG技术全解析:知识图谱如何让大模型检索更智能

先聊一个我自己踩过的坑。前两年给某企业内部知识库做问答系统,用的是当时最主流的向量化RAG方案——文档切片、Embedding、向量召回、拼上下文给大模型。Demo跑起来很唬人,一问一个准,可一上真实业务数据就露馅:问“A项目的负责人…

作者头像 李华
网站建设 2026/10/20 7:03:59

JMeter接口压测实战:从环境搭建到并发测试报告解读

刚上手接口压测那会儿,我第一个遇到的问题是“工具选哪个”。市面上能压测的工具不少,但 JMeter 这个名字在简历和招聘需求里出现频率实在太高。它是 Apache 下的开源工具,纯 Java 写的,主要用来做性能测试、负载测试,…

作者头像 李华
网站建设 2026/10/20 7:03:38

OpenCvSharp C# WinForms 图片显示与处理Demo全解析

简介:OpenCvDemo.rar 是一套基于 OpenCvSharp 的 C# 图片显示与交互控件示例工程,面向需要在 WinForm/WPF 界面中集成 OpenCV 图像处理能力的开发人员与初学者。压缩包共 105 个文件、约 129MB,包含 19 个 .cs 源码、25 个 .dll 运行库、17 个…

作者头像 李华
网站建设 2026/10/20 7:03:18

Unity WebGL播放海康RTSP:转HLS全流程方案与避坑指南

简介:面向Unity/C#开发者的WebGL视频监控方案demo,核心是基于AVProVideo插件拉取并播放海康摄像头实时画面,解决WebGL平台下的视频解码与渲染问题。作者重写了Http请求模块,并加入无水印AVProVideo的发布配置指导文件;…

作者头像 李华
网站建设 2026/10/20 7:02:40

Web自动化测试流程详解:从用例筛选到脚本维护的完整实践

做Web自动化测试有些年头了,从最早写那种“打开浏览器、点两下、断言一下”的练手脚本,到后来在项目里搭起能跑几百条用例的完整测试体系,整个过程走下来,最大的感受是:自动化不是写代码难,而是流程难。网上…

作者头像 李华