刚给一台跑了五年多的CentOS 8.5服务器做完内核离线升级,这台机器在内网隔离区,从来就没连过公网。需求也很明确:把内核从4.18升到6.6稳定系列,因为新装的采集卡驱动在内核5.15以下编译不过,后面还准备上实时补丁,对igc网卡和EtherCAT这类实时工业总线的支持也有硬性要求。
整个过程从准备rpm包到重启验证,前后花了一整天,时间主要耗在依赖整理和引导项确认上。网上关于CentOS在线升级内核的教程一大堆,但离线场景的完整流程反而不多,很多文章只给命令不讲为什么,碰到报错就抓瞎。这篇我按实际操作的顺序,把拿包、装包、切引导、验证回滚的完整链路写清楚,适合所有内网环境、弱网环境或对软件包有合规管控要求的服务器场景。
1. 为什么离线升级内核会成为CentOS 8运维的必修课
CentOS Linux 8在2021年底停止维护后,官方仓库的内容基本冻结,内核版本长期停留在4.18系列。这个内核本身不算不能用,但时间一长,问题会非常现实:新硬件的网卡、存储控制器没有对应驱动,某些工业采集卡或加密卡要求内核版本下限,安全漏洞的修复补丁也不再同步更新。
在线环境的升级路径很成熟:装ELRepo、启用elrepo-kernel仓库、yum update kernel、重启时在grub菜单选一下,十几分钟就能搞定。但离线环境是完全另一套逻辑。内网机房、安全隔离区、涉密网段,这些机器从物理上就不允许访问外部yum源,所有软件包必须提前在外部准备好,通过U盘、内网FTP或资产管理系统传进去。内核这种东西又偏偏是牵一发动全身的系统组件,不是简单拷贝一个文件就行。
离线升级的难点集中在三块:
第一是依赖链。内核rpm包拆得很细,kernel-core、kernel-modules、kernel-modules-extra、kernel-devel,包之间有严格的依赖关系,还牵扯到系统基础组件。在线环境这些依赖由dnf自动解决,离线环境一个libelf版本不够就能让整个安装卡住。
第二是驱动与模块配套。新内核的initramfs必须在安装阶段正确生成,需要哪些驱动模块被提前识别进ramdisk,fs模块、磁盘控制器驱动、网卡驱动缺一不可。很多人第一次离线装内核,装完不识别网卡,十有八九是kernel-modules或者kernel-modules-extra漏装了。
第三是回滚设计。内网服务器的重启窗口通常要跟业务方申请,不能随便试错。如果新内核起不来,你要有办法在grub菜单一键回退到旧内核,或者至少能通过单用户模式把系统救回来。这个后路必须在操作前就设计好,而不是等翻车了再想。
这篇文章的定位就是给你一条可以直接照做的离线升级路径,更重要的是让你知道每一步背后的原因。我用的系统是CentOS 8.5,目标内核是6.6 LTS系列,下面按照实际操作顺序逐步展开。
2. 提前备好弹药:在联网机器上准备内核rpm包
离线环境没有网络,安装包必须在外网或另一台可联网的机器上下载好,再传进内网。这一步做得越扎实,后面安装越顺利。
2.1 内核rpm来源怎么选:ELRepo、kernel.org与发行版官方仓库对比
常见的离线内核rpm来源有几种,实际用下来差别不小,先看对比。
| 来源 | 内核版本类型 | 拆分包 | 适配CentOS 8 | 适合场景 |
|---|---|---|---|---|
| 发行版官方BaseOS仓库 | 4.18固定 | 正常 | 完美 | 不敢升版本、只求打补丁 |
| ELRepo kernel-lt | 最新长期支持版(当前为6.6系列) | 拆分为core/modules/modules-extra | 好 | 追求稳定为主的生产环境 |
| ELRepo kernel-ml | 最新主线版 | 拆分为core/modules/modules-extra | 好 | 需要新功能、能接受一定风险 |
| kernel.org手工构建 | 任意版本 | 自己生成 | 需要自行打包 | 需要特定版本或RT实时补丁 |
从CentOS 8的角度看,最省事的是ELRepo的kernel-lt。我这次就是通过kernel-lt拉取的6.6系列,这个系列目前仍在积极维护,对igc这类Intel 2.5G网卡驱动支持完整,EtherCAT主站所需的实时性环境也可以通过后续RT补丁叠加。需要提醒的是,不同时间点kernel-lt指向的版本不一样,如果你确实需要某个具体的6.6小版本(比如当前社区讨论比较多的6.6最新稳定点),下载前一定先用命令确认仓库里已经同步到了哪个版本,避免传到内网后才发现版本不对。
2.2 用dnf download批量拉取内核rpm包
在可联网的CentOS 8机器上,先装ELRepo的release配置。以8.3版本为例:
# 导入ELRepo的GPG密钥 rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org # 安装elrepo-release dnf install -y https://www.elrepo.org/elrepo-release-8.3-1.el8.elrepo.noarch.rpm装完以后,先看仓库里到底有哪些内核版本可选,重点确认kernel-lt当前是否已经同步到你想要的6.6版本:
dnf --enablerepo=elrepo-kernel list --showduplicates kernel-lt确认版本可用后,用dnf download把内核相关rpm一次性拉取到本地目录。注意在CentOS 8上dnf download来自dnf-plugins-core,如果提示找不到命令,先执行dnf install -y dnf-plugins-core。
mkdir -p /root/kernel-rpm cd /root/kernel-rpm dnf download --enablerepo=elrepo-kernel \ kernel-lt \ kernel-lt-core \ kernel-lt-modules \ kernel-lt-modules-extra \ kernel-lt-devel有的教程只让你下载kernel和kernel-devel,这是个大坑。ELRepo从EL8开始已经把内核拆分成多个包,modules-extra里包含大量不常用但关键时刻要命的驱动,比如部分网卡驱动、文件系统模块、硬件监控驱动。我在这次升级前特意对比过,igc网卡驱动就位于kernel-modules中,但如果你要支持更多板载网卡或者某些RAID卡,modules-extra基本是必须的。devel包则是给后续编译第三方驱动模块用的,离线环境下想编译外部模块时才发现没带devel包,那就真的麻烦大了。
如果你需要RT实时补丁内核,ELRepo仓库通常没有现成的kernel-rt,CentOS Stream 8的AppStream源里倒是有,但需要把对应repo的rpm一起打包带走。RT内核包的拆分包命名类似kernel-rt-core、kernel-rt-modules,准备方式与上面类似。
2.3 校验与归档:安全地把rpm包转移进内网
下载完成后,先做SHA256校验,防止传输过程中文件损坏或被人为替换。校验值可以从ELRepo仓库的repodata或者页面上获取,也可以用sha256sum *.rpm自己留存一份基线:
sha256sum *.rpm > SHA256SUMS.txt然后把所有rpm和校验文件打包成一个tar包,这样传输时不容易漏文件:
cd /root tar czf kernel-lt-6.6-rpms.tar.gz kernel-rpm/接下来就是把tar包传进内网服务器。常见渠道有:通过审批U盘拷贝、内网FTP/SFTP上传、或者资产管理系统自带文件分发功能。这一步没有统一命令,遵循你所在环境的安全规范就好。唯一要强调的是:传输完在内网机器上先解压并做一次sha256sum -c SHA256SUMS.txt校验,这样能除尘校验问题。
3. 安装前的环境摸底:这几项不确认就动手,后面全是坑
拿到rpm包后不要急着装,先把目标服务器的现状摸清楚。这一步的价值在于:提前发现问题,而不是等安装中报错再去查。
3.1 内核版本、系统架构与发行版信息核对
上执行第一组命令:
cat /etc/redhat-release uname -r uname -m我这次看到的结果是CentOS Linux release 8.5.2111、内核4.18.0-348.el8.x86_64、x86_64架构,说明目标环境与准备的x86_64 rpm包是对应得上的。如果内网机器是aarch64或ARM架构,同样的步骤要下载对应架构的rpm包,这个在准备阶段就要想清楚。
另外看一眼当前使用的文件系统类型和磁盘控制器驱动,lsblk看磁盘结构,lspci -nn | grep -i raid看RAID卡型号,并记录一下fstab里挂载了哪些分区。这些信息在新内核不识别磁盘时,是你排查initramfs是否缺模块的重要依据。
3.2 /boot分区空间与引导方式的检查
内核升级后/boot目录要存放新的vmlinuz、initramfs、System.map,一套文件加起来大约80到120MB。如果/boot是独立分区且空间吃紧,可能连解压都完不成。
df -h /boot检查引导方式是BIOS还是UEFI,这个决定了grub.cfg的生成路径:
# 有输出就是UEFI,没有输出就是BIOS [ -d /sys/firmware/efi ] && echo "UEFI" || echo "BIOS"同时确认当前grub版本和默认引导项设置:
grub2-editenv list cat /etc/default/grub | grep GRUB_DEFAULT如果GRUB_DEFAULT=saved表示系统会使用grubenv中保存的saved_entry作为默认启动项,这正好是后续我们要利用的机制。如果是数字索引,后续需要改成saved或者直接通过grubby指定默认内核。
3.3 Secure Boot、模块签名和内核参数现状
很多内网机器在BIOS里开启了Secure Boot,而新内核rpm如果没有通过对应密钥签名,Secure Boot状态下会直接拒绝启动,表现为选完内核后卡住或者报Verification failed: (0x1A) Security Violation。
检查当前Secure Boot状态:
mokutil --sb-state如果输出SecureBoot enabled,强烈建议你在升级前先跟负责硬件的同事确认是否能临时关闭,或者准备好在shim/MOK层导入密钥。日常运维中图省事直接关了Secure Boot的也不少,但这一步最好留在升级前做决定,别等到重启起不来才排查。
还要记录当前内核的启动参数,后续新内核要尽量保持一致。查看方式:
cat /proc/cmdline比如机器上有特殊的isolcpus、nmi_watchdog、或者针对实时内核的nohz_full参数,这些都要记下来,在grub菜单或者/etc/default/grub里给新内核补上。我这次就在/etc/default/grub的GRUB_CMDLINE_LINUX里加上了与业务相关的irqaffinity配置,确保新旧内核行为一致。
4. 离线安装内核rpm:正确的安装顺序与依赖问题排查
环境摸完,进入正题。离线安装内核时,命令不复杂,但背后的依赖关系和执行逻辑值得说清楚。
4.1 安装顺序为什么重要:core、modules、devel的依赖关系
从CentOS 8开始,RHEL系内核从单一包拆成了多个子包,这是为了降低最小化安装时内核体积、加快启动速度。但拆包也带来了安装时的依赖顺序问题。
关系基本是这样的:kernel-lt是元包,依赖kernel-lt-core,同时附带dracut配置;kernel-lt-modules依赖kernel-lt-core;kernel-lt-modules-extra依赖kernel-lt-core和kernel-lt-modules;kernel-lt-devel依赖kernel-lt-core。如果单独装core,系统能启动但没有像样的驱动模块;单独装modules又装不上,因为依赖不满足。
实际安装时不需要一个一个rpm手动装,一次把命令写完整就够了。rpm会在单次执行中解析所有包之间的依赖,只要包都齐了,顺序问题就自动解决。
4.2 使用rpm直接安装的实操命令与常见报错
进入rpm包所在目录,执行安装:
cd /root/kernel-rpm rpm -ivh \ kernel-lt-*.x86_64.rpm \ kernel-lt-core-*.x86_64.rpm \ kernel-lt-modules-*.x86_64.rpm \ kernel-lt-modules-extra-*.x86_64.rpm \ kernel-lt-devel-*.x86_64.rpm这里特意用-ivh而不是-Uvh。对于普通软件包,-U是升级(移除旧版),-i是全新安装;对于内核RPM,两种方式的差异并没有普通软件包那么绝对,但用-i更符合“新增一个内核而不是替换”的逻辑。内核rpm设计上允许新旧版本共存,这也是我们保留回滚能力的基础。
正常情况下,安装完成后/boot目录会多出新内核文件,/lib/modules下也会多出对应版本目录:
ls -l /boot/vmlinuz-6.6* /boot/initramfs-6.6* ls /lib/modules/看到vmlinuz、initramfs、System.map都齐了,基本说明install阶段成功了。
不过离线环境里rpm -ivh最容易遇到的报错是依赖不满足,典型长这样:
error: Failed dependencies: elfutils-libelf(x86-64) >= 0.178 is needed by kernel-lt-core-6.6.x这种报错发生在系统已有的elfutils-libelf版本低于新内核要求时。处理思路很简单:把这个依赖rpm也准备好,一起装进去。在前面的准备阶段,如果你不确定目标机器的基础组件版本,最稳妥的做法是在联网机器上先把这些系统库也下载好。实际操作中我会建议直接准备一套kernel叠加依赖的组合包,比如:
cd /root dnf download --enablerepo=elrepo-kernel --resolve kernel-lt加上--resolve参数后,dnf会把所有依赖按当前系统版本拉下来,包虽然多一些,但离线现场的成功率大大提升。如果你当时没这么做,现在就只能根据报错去补包,虽然多一步但也能搞定。
4.3 安装完成后的文件检查
安装完不要急着改grub,先做一次完整性检查,确认新内核的文件都落到了正确位置。
rpm -qa | grep "^kernel-lt"这会列出所有已安装的kernel-lt相关包,应该至少包含下列内容:
kernel-lt-6.6.x-1.el8.elrepo.x86_64 kernel-lt-core-6.6.x-1.el8.elrepo.x86_64 kernel-lt-modules-6.6.x-1.el8.elrepo.x86_64 kernel-lt-modules-extra-6.6.x-1.el8.elrepo.x86_64 kernel-lt-devel-6.6.x-1.el8.elrepo.x86_64再看一下新内核的initramfs是否已生成:
ls -l /boot/initramfs-6.6*.img如果这个文件不存在,说明安装过程中dracut没有正常执行,需要手动重建,命令是:
dracut -f /boot/initramfs-6.6.x.img 6.6.x手动重建initramfs是离线升级里的保命技能,后面验证环节如果发现新内核找不到根文件系统,大部分情况就是initramfs缺驱动模块,到时候还是靠这条命令解决。
5. grub引导配置:让新内核成为默认启动项
装完rpm只是完成了第一步,系统重启后选哪个内核启动,取决于grub配置。这一步做错,新内核可能永远不会被引导。
5.1 重新生成grub.cfg:BIOS和UEFI路径不同
先重新生成grub菜单,让新内核出现在启动项里。BIOS机器与UEFI机器使用不同的配置文件路径,一条命令不能覆盖两种情况,分开操作:
# BIOS引导 grub2-mkconfig -o /boot/grub2/grub.cfg # UEFI引导 grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg如果一开始没确认引导方式,现在通过之前的[ -d /sys/firmware/efi ]判断即可,切忌两条命令都执行,特别在EFI机器上把grub.cfg写到/boot/grub2/,虽然大多数情况下也能用,但遇到特殊分区布局时会出问题。
生成完成后,查看菜单中是否出现了新内核条目:
grep menuentry /boot/grub2/grub.cfg正常情况下应该既有旧的4.18条目,也有新的6.6条目。这里注意确认新内核条目的顺序,不是按版本号排的,而是按安装时间,新内核通常是最后几个之一。
5.2 用grub2-editenv确认默认启动项指向
CentOS 8默认的GRUB_DEFAULT一般是saved,意思是grub从grubenv文件读取上一次保存的默认启动项。要把默认启动项切换到新内核,有两条路:grub2-set-default指定菜单标题,或者用grubby直接指定内核文件。
我推荐用grubby,因为它按内核文件而非菜单顺序定位,更不容易搞错:
grubby --info=ALL | grep -E "^kernel|^index" # 找到新内核的启动文件路径后,执行 grubby --set-default /boot/vmlinuz-6.6.x然后验证一下当前默认配置:
grub2-editenv list输出中kernelopts是启动参数,saved_entry应该是刚才grubby设置的内核对应的菜单条目。如果GRUB_DEFAULT配置的不是saved,grub2-editenv list里就没有saved_entry字段,这时候需要编辑/etc/default/grub,把GRUB_DEFAULT=saved写入,再重新生成grub.cfg。
5.3 保留旧内核:别手欠删4.18
直到这里,也先别删除旧内核。新旧内核共存是我们在离线环境里最大的保险。删除旧内核是在新内核稳定运行至少一到两周、确认业务无异常后才考虑的事。
另外还要记录一下旧内核的菜单条目名,通常类似:
CentOS Linux (4.18.0-348.el8.x86_64) 8 CentOS Linux (6.6.x-1.el8.elrepo.x86_64) 8如果新内核启动出问题,在grub界面直接选择旧内核条目回车,就能回到熟悉的4.18环境,这是最简单的回滚手段。
6. 重启后的验证:能开机只是开始,完整检查要逐项做
重启服务器这一步需要提前申请窗口,和业务方确认好停机时间后执行。
reboot重启后如果系统顺利进入登录界面,先别高兴太早,很多隐藏问题是在业务跑起来后才暴露的。
6.1 内核版本、启动参数与系统服务的基础检查
登录后立刻确认当前是不是新内核:
uname -r再看一下实际生效的启动参数是否与旧内核一致:
cat /proc/cmdline对比之前记录的旧内核参数,看isolcpus、nohz_full这类关键参数有没有带过来。如果发现GRUB_CMDLINE_LINUX里的自定义参数没有生效,需要检查/etc/default/grub配置和grubby的引导项参数。
接着检查系统主要服务的运行状态:
systemctl status NetworkManager --no-pager systemctl status sshd --no-pager journalctl -b -p err新内核下有些服务因为内核模块或库的兼容问题启动失败,journalctl -b -p err能直接筛出本次启动周期的错误日志,比挨个查服务高效得多。
6.2 网卡、文件系统、驱动模块的兼容性验证
离线升级内核最常翻车的就两件事:网卡掉线、根文件系统挂载失败。
网卡验证先看IP是否正常:
ip addr show ip route如果IP没起来,大概率是网卡驱动模块没加载进新内核。确认一下对应网卡的驱动模块状态:
lspci -nn | grep -i ethernet modinfo igc | head -20 lsmod | grep igc以igc为例,如果你用的Intel I225/I226网卡,新内核模块应正常加载。如果模块存在但未加载,考虑检查是否有硬件被另一个驱动占用,或者需要向/etc/modules-load.d里加白名单。这一步在准备阶段专门确认过igc模块的用户会很省心。
文件系统方面重点验证系统盘和业务数据盘都能正常读写:
mount | grep -E "ext4|xfs" df -h touch /data/test_write && rm /data/test_write如果根文件系统挂载异常,一般会在启动时直接进emergency mode,这种情况多半是initramfs缺失指定的文件系统模块,处理方式是用旧内核启动后手动重建新内核的initramfs。
另外建议跑一遍dmesg,重点看Storage、SCSI、usb相关报错:
dmesg | grep -iE "error|fail|scsi|usb" | head -50有些非致命错误不影响当前启动,但记录下来可以和旧内核做对比,提前发现潜在驱动问题。
6.3 实时补丁与特殊硬件模块的补充说明
如果离线升级的目标是支撑实时工业应用,比如EtherCAT主站、运动控制卡,那么仅仅切换到6.6稳定内核还不够。标准内核虽然有RT调度相关的改进,但不是完整的PREEMPT_RT实时内核。
凡是想获得较低调度延迟、稳定的周期通信抖动控制,建议在6.6稳定版基础上继续打RT补丁。离线环境下的做法通常是:
- 在联网机器上把对应版本的内核源码包和RT补丁文件(从内核官网或相关镜像获取)一起打包;
- 在内网机器上基于新内核源码打补丁,生成RT内核rpm;
- 安装RT内核,再走grub切换流程。
这一步工作量明显比纯离线升级大,补丁版本与内核版本必须严格对应,比如6.6.119这个版本对应的RT补丁通常发布在同一维护周期内。如果你确实需要RT环境,准备阶段就要把源码和补丁一起带上,否则内网机器上根本没地方下载这些文件。
对于EtherCAT场景,还要确认新内核里EtherCAT主站需要的模块(比如ec_master相关模块或用户态主站的依赖库)与新内核头文件匹配。很多主站方案需要编译内核模块,这时候就需要之前准备的kernel-lt-devel包和gcc工具链,缺一个都编不过去。
7. 翻车挽救手册:新内核起不来时的回滚与应急恢复
没有哪次内核升级敢说自己百分之百不会翻车,尤其在离线环境,机器重启失败意味着业务完全停摆。所以最后这一节我专门讲回滚。
7.1 最轻量回滚:grub切换回旧内核
如果重启后新内核直接启动不了,或者卡在某个阶段,最直接的办法就是在grub菜单出现时,用方向键选择旧的4.18内核条目,直接回车启动。
实际操作中的情况往往没这么舒服:很多服务器设置了快速启动,grub菜单一闪而过。解决方法是在看到grub界面时迅速按Esc键(UEFI模式)阻止倒计时,让菜单停留等待选择。如果连菜单都没看到就进系统了,可以在重启前用grub2-set-default指定旧内核,或者编辑grub界面。
如果系统能进入紧急模式(emergency mode),可以在单用户环境下修复:
grub2-editenv list grub2-set-default 0 rebootgrub2-set-default 0的意思是默认启动第一个菜单项,通常就是旧内核排在第一个,但不同镜像版本排序不一定相同,稳妥做法还是先grub2-editenv list看当前saved_entry,再决定设哪个。
7.2 彻底删除新内核并回退到原状态
如果确认新内核问题无法修复,或者业务压力要求立即回到升级前状态,那就把新内核包卸载掉。
离线环境下卸载内核不建议直接用rpm -e单独删其中一个包,因为kernel-modules和kernel-modules-extra都依赖core,直接强删会引发依赖问题。推荐的方式是通过yum/dnf按通配符删除:
dnf remove "kernel-lt-*"这会一次性把所有kernel-lt相关包清干净。注意使用--disablerepo='*'避免dnf在删除时尝试访问已不可用的外部仓库:
dnf --disablerepo='*' remove "kernel-lt-*"删除完成后重新生成grub.cfg,让菜单里彻底移除新内核条目:
grub2-mkconfig -o /boot/grub2/grub.cfg grub2-editenv list最后检查确认默认项回退到了旧内核。
7.3 连grub都进不去的极端救援思路
最坏的情况是新内核导致系统在引导早期崩溃,连grub菜单都出不来,或者出现Secure Boot安全报错。这时候只能依靠外部救援介质。
内网服务器通常有IPMI带外管理,可以通过虚拟光驱挂载CentOS 8安装ISO启动到rescue模式。进入rescue后,系统会尝试发现并挂载根文件系统,然后chroot进去修复。chroot后可以执行以下操作:
# 重新设置默认内核为旧内核 grub2-set-default 0 # 重新生成grub配置 grub2-mkconfig -o /boot/grub2/grub.cfg # 如果怀疑initramfs损坏,重建当前默认内核的initramfs dracut -f /boot/initramfs-$(ls /lib/modules | tail -1).img $(ls /lib/modules | tail -1)这里有个前提:救援模式能识别你的根文件系统和磁盘控制器驱动、需要文件系统支持ext4/xfs。如果磁盘控制器驱动缺失,那在救援环境里同样要先加载对应驱动。
离线环境里,我的建议是把CentOS 8的ISO镜像也提前保存在内网可访问的位置(比如带外管理存储或公司共享存储),以备不时之需,不要等到翻车了才去找安装镜像。绝大多数内网环境的CentOS 8 ISO都能在公司的软件资产库中找到,提前备份一份不费事。
最后再提醒一个容易被忽略的点:任何时候内核升级,都保留至少两个版本的内核,绝不保留单个内核。我见过有同事为省空间把旧内核删得干干净净,结果新内核在运行几天后暴露驱动bug,想回旧内核都没得回。内核这种系统组件,不是你装完验证一下没问题就一劳永逸,它和硬件驱动、业务应用之间的兼容性问题可能要过很长时间才暴露。离线环境的服务器更是如此,能多个后路就尽量多留一条。