1. 离线升级前的准备:先把环境摸清楚
1.1 先确认当前内核版本和系统版本
离线升级内核这件事,听起来就是“下载几个包、装上、重启”三连,实际上坑都藏在你对当前环境的认知盲区里。我在接到这类需求时,第一件事永远是先确认两样东西:当前内核版本和系统小版本。
uname -r cat /etc/centos-release这两条命令的输出决定了很多东西。比如你当前跑的是4.18.0-193.el8.x86_64,说明这是 CentOS 8.0/8.1 时代的内核;如果系统小版本是 8.5,那么仓库地址、安装包依赖都会指向 8.5 的 BaseOS/AppStream,离线下载的 RPM 包也最好跟小版本对齐。
还有一个容易被忽略的点:uname -r只显示正在运行的内核,不代表boot分区里只有这一个内核。有时候你升级过,但重启后没切到新内核,这种情况很常见。所以还得看已安装的内核包有哪些:
rpm -qa | grep kernel这一步能帮你搞清楚当前系统里到底装了几个 kernel 包、有没有 kernel-core、kernel-modules 这些拆分后的子包。CentOS 8 从 4.18 开始,内核 RPM 被拆成了 kernel、kernel-core、kernel-modules、kernel-modules-extra、kernel-devel 等好几个包,离线升级时一个都不能漏,漏了可能能开机,但某些驱动模块就是加载不上。
1.2 解决仓库失效:CentOS 8 的遗留问题
CentOS 8 正式停止维护后,mirrorlist.centos.org这个地址已经不能用了,默认的 yum/dnf 源全部失效。这是一个非常现实的问题,离线机器尤其明显:即使你有网络,直接dnf update也会报一堆 404 或者找不到 mirrorlist。
所以离线升级内核的第一步,不是去下内核包,而是先把系统自带的 repo 文件处理掉,否则后续操作会一直被仓库报错干扰。
cd /etc/yum.repos.d/ mkdir backup mv *.repo backup/然后把需要的 repo 文件写进去。如果你只是临时装内核,不打算长期用 dnf 拉包,最简单的方案是直接建一个本地目录 repo,把下载好的 RPM 包放进去,用createrepo生成索引,或者干脆只依赖rpm -ivh本地安装。说实话,升级内核这种操作,用 rpm 本地安装就够了,不一定要走 dnf,依赖关系用rpm -Uvh逐个安装完全可控。
注意:CentOS 8 系列的内核 RPM 包之间是有依赖关系的,安装顺序一般是从 kernel-core 开始,再到 kernel-modules、kernel-modules-extra、kernel,顺序错了容易报依赖错误。不过实际用
rpm -Uvh *.rpm一次性丢给 rpm 处理,它自己会解析顺序,通常不会出问题。
2. 离线安装包准备:从哪下载、下哪些、怎么校验
2.1 内核版本怎么选
离线升级内核,最核心的决策就是选版本。我见过很多人不管三七二十一直接上最新 mainline,结果某个业务用的内核模块没有适配,开机直接崩。生产环境选内核的原则就一句话:选 LTS(长期支持)版本,选你业务依赖的驱动已经合入的版本。
比如你跑 EtherCAT 主站、IgH 或者 Acontis 这类工业实时控制方案,就得特别关注igc驱动的支持情况。igc 是 Intel I225/I226 系列网卡的驱动,EtherCAT 应用里经常拿它做实时以太网。这些驱动合入主线内核的版本比较晚,老内核根本不认识这块网卡。6.6 这个 LTS 系列的 igc 驱动已经足够成熟,而且对应的实时补丁(PREEMPT_RT)也比较齐备,所以 6.6.119 这种版本在工控圈子里热度很高,属于“既能长期维护、又带实时能力”的版本。
如果只是常规服务器升级,不想追求新功能,选当前使用的内核系列里最新的小版本更稳妥。比如原来用 4.18,那升到 4.18 系列最后一个版本,风险最低,驱动兼容性几乎为零迁移成本。但我个人建议 CentOS 8 至少升到 5.4 以上的长期支持版本,因为 4.18 默认对 NVMe、新网卡、新 RAID 卡的支持已经跟不上硬件发展了。
2.2 需要下载哪些 RPM 包
离线环境下,你没法在有网机器上直接对着目标机器操作,所以得在有网的机器上先把 RPM 包准备好,再拷贝到离线机器上。CentOS 8 的内核包可以从两个渠道获取:
- 官方源(vault.centos.org):包含 CentOS 8 自带的 4.18 系列内核。适合只想升到同系列最新小版本的场景。
- ELRepo:提供 kernel-lt(长期支持版)和 kernel-ml(主线最新版)。如果你的目标是 6.6.119 这种新 LTS,通常走 ELRepo 的 kernel-lt 或者自己从 kernel.org 编译。
以 ELRepo 为例,在有网的机器上执行:
# 安装 ELRepo 仓库 rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org dnf install https://www.elrepo.org/elrepo-release-8.3-1.el8.elrepo.noarch.rpm # 查看可用内核版本 dnf --enablerepo=elrepo-kernel list --showduplicates kernel-lt # 下载内核包和依赖包 dnf --enablerepo=elrepo-kernel download --downloaddir=/opt/kernel-rpms \ kernel-lt kernel-lt-core kernel-lt-modules kernel-lt-modules-extra注意,ELRepo 的包名跟 CentOS 自带的不一样,是kernel-lt、kernel-lt-core这种格式。下载时建议把kernel-lt-devel也带上,因为后续如果要编译第三方内核模块(比如 EtherCAT 主站),必须要有跟运行内核完全匹配的kernel-lt-devel。
如果你要的是具体某个小版本,比如 6.6.119,可以用dnf download kernel-lt-6.6.119-1.el8这种精确指定版本号的方式。
官方 vault 源的下载方式类似:
dnf --repofrompath=baseos,https://vault.centos.org/8.5.2111/BaseOS/x86_64/os/ \ --enablerepo=baseos download kernel kernel-core kernel-modules kernel-modules-extra \ --downloaddir=/opt/kernel-rpms还有一个小技巧:如果目标机器上已经装了某些依赖包,比如elfutils-libelf、linux-firmware,那它们其实不用重复下载。不确定的话,把 kernel 系列的几个包都下载好带到现场,用 rpm 安装时看报错再决定。
2.3 密钥校验与依赖检查
离线环境最怕的就是 RPM 包不完整或者被篡改。下载完 RPM 后,我习惯先做两件事:
# 校验 GPG 签名 rpm -K /opt/kernel-rpms/*.rpm # 检查依赖完整性 rpm -qpR /opt/kernel-rpms/kernel-lt-core-*.rpmrpm -K会输出每个包的签名状态,出现OK才说明完整。rpm -qpR会列出这个包依赖的库和包,你可以逐一对比目标机器上是否满足。常见的内核包依赖无非是elfutils-libelf、systemd、linux-firmware、perl这些,CentOS 8 基础安装一般都有。
有一个包特别提醒:linux-firmware。新内核往往需要新固件,比如 igc 网卡可能依赖e1000e或者igc微码,这些固件都在linux-firmware包里。如果离线机器上的固件包太老,新内核能装上但硬件可能工作不正常。建议也把linux-firmware的新版下载好,一起带到现场。
3. 离线安装实操:从 rpm 安装到 grub 配置的完整流程
3.1 rpm 安装新内核
把 RPM 包拷贝到目标机器后,我习惯放在/opt/kernel-rpms下,然后直接执行:
cd /opt/kernel-rpms rpm -Uvh *.rpm这里有个选择:-U是升级,-ivh是全新安装。如果你现在跑的是 4.18,要装 6.6,用-U没问题,因为旧内核和新内核是不同的包版本,rpm 会保留多个内核并存,不会把旧的删掉。如果你尝试用-i全新安装,效果其实一样,因为内核包的特性和名字与旧内核不冲突。
安装过程如果顺利,会看到一串Preparing...和Running scriptlet输出。注意观察scriptlet阶段有没有报错,因为内核安装时会触发dracut重新生成 initramfs、更新 grub 引导配置,这些步骤卡住会导致新内核不可引导。正常情况下,装完后/boot下会出现新内核的vmlinuz-*、initramfs-*.img和System.map-*文件。
安装完成后先别急着重启,马上做一次启动项检查:
grubby --info=ALL | grep -E "^kernel|^index"grubby --info=ALL会列出 grub 配置里所有可引导的内核,index字段就是每个内核对应的启动项编号。确认新内核出现在列表里之后,再设置默认启动项。
如果安装时报依赖错误,比如提示缺少elfutils-libelf-devel或者python3-perf,不要急着rpm -ivh --nodeps。先看清楚缺的是运行时依赖还是安装脚本依赖。如果只是构建 initramfs 时需要某个辅助工具,可以先用--nodeps装上,但事后要尽快补装缺失的依赖,避免系统组件之间状态不一致。
3.2 设置默认启动项
新内核装好后,系统的默认启动项通常还是旧内核,必须手动切换。用 grubby 是最快的方式:
# 设置默认启动项为新内核 grubby --set-default /boot/vmlinuz-6.6.119-1.el8.elrepo.x86_64 # 验证默认启动项 grubby --default-kernelgrubby --default-kernel输出应该指向新内核的 vmlinuz 路径。如果 grubby 不好用,或者你更习惯传统方式,可以编辑/etc/default/grub文件,把GRUB_DEFAULT=saved改成GRUB_DEFAULT=0(前提是第一个启动项就是新内核),然后重建 grub 配置:
grub2-mkconfig -o /boot/grub2/grub.cfg这里有个细节:如果机器是 UEFI 启动,配置文件路径是/boot/efi/EFI/centos/grub.cfg,需要用对应的路径:
grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg不确定用哪个就直接ls /boot/grub2/和ls /boot/efi/EFI/centos/看哪个存在。
3.3 重启与验证
重启前最后一个动作,是确认initramfs已经生成。可以手动执行一次:
dracut -f /boot/initramfs-6.6.119-1.el8.elrepo.x86_64.img 6.6.119-1.el8.elrepo.x86_64dracut -f会强制重新生成 initramfs,-f参数很关键,不加可能因为文件已存在而跳过。这一步的目的是确保新内核的根文件系统驱动、LVM、加密、multipath 等模块都被正确打进 initramfs。
然后重启:
reboot重启后第一件事就是确认内核版本:
uname -r如果还在旧内核上,别慌,大概率是默认启动项没生效。重启时手动在 grub 菜单里选新内核,进入系统后再检查grub2-editenv list,看saved_entry指向哪个内核。
我再提醒一次验证驱动加载,这一步在很多场景下比内核版本本身更重要:
# 查看当前内核加载了哪些模块 lsmod | grep igc dmesg | grep -i igc如果你升级内核就是为了 igc 驱动的 EtherCAT 应用,一定要确认新内核下igc模块成功加载,网卡链路起来了,才可以继续后面的应用部署。
4. 升级后的收尾:多内核管理、/boot 空间与实时补丁的补充说明
4.1 清理旧内核与 /boot 空间管理
新内核跑稳之后,旧内核还占着/boot空间。CentOS 8 默认/boot分区只有大约 1GB,而一个内核加 initramfs 差不多要 100~200MB,装三四个内核没问题,装多了就会撑爆。
查看当前/boot使用情况:
df -h /boot rpm -qa | grep kernel清理旧内核,我一般保留最近两个版本:一个当前在跑的,一个备用的。命令如下:
dnf remove kernel-4.18.0-193.el8.x86_64 \ kernel-core-4.18.0-193.el8.x86_64 \ kernel-modules-4.18.0-193.el8.x86_64 \ kernel-modules-extra-4.18.0-193.el8.x86_64如果你当初是用 ELRepo 装的,包名就是kernel-lt-*这种格式。删除时注意别把当前内核删了,保守的做法是先用uname -r确认当前版本,再核对要删除的包名。
有一个常见坑:删除旧内核后/boot/grub2/grub.cfg里可能残留失效的启动项。执行一次grub2-mkconfig -o /boot/grub2/grub.cfg重新生成一下就好。如果清理完/boot还是吃紧,检查一下/boot下有没有积累大量initramfs-*.img或者vmlinuz-*旧文件,手动删掉也行,但更推荐走 rpm 删除,避免系统状态不一致。
4.2 内核参数与实时补丁的补充说明
如果你升级内核是为了跑工业实时应用,那还得关注PREEMPT_RT补丁。常规发行版内核默认都是PREEMPT_DYNAMIC,不是全实时模式。ELRepo 有单独的kernel-rt包,带-rt后缀,比如kernel-rt-6.6.119-1.el8.elrepo.x86_64,这才是打了实时补丁的内核。
离线场景下,RT 内核的 RPM 包同样是下载好后拷进去安装,流程跟普通内核一样。装完后注意确认实时特性真的生效:
uname -r输出里带rt字样才说明是 RT 内核。另外可以检查内核配置:
zcat /proc/config.gz | grep PREEMPT或者:
grep PREEMPT /boot/config-$(uname -r)如果看到CONFIG_PREEMPT_RT=y,说明实时补丁已经生效。
如果你用的是 IgH EtherCAT 主站这类第三方模块,升级内核后一定要重新编译模块,因为内核模块是强绑定内核版本的。编译前先确认kernel-devel或kernel-lt-devel版本和新内核完全一致:
rpm -q kernel-devel uname -r两个输出不一致,编译必报错。这一点在离线环境尤其麻烦,因为 devel 包也要提前下载好。
4.3 Secure Boot 与内核签名问题
还有一个经常被忽略的坑:Secure Boot。如果机器开启 UEFI Secure Boot,而新内核不是由发行版签名的,引导时会被拒。ELRepo 的内核包自带签名,一般没问题;但如果你是从 kernel.org 自己编译的内核,没有签名,Secure Boot 模式下是起不来的。
检查 Secure Boot 状态:
mokutil --sb-state输出SecureBoot enabled就需要处理。最简单的办法是进 BIOS 关掉 Secure Boot。如果没法进 BIOS,可以用mokutil导入自定义密钥,但这个过程比较繁琐,而且离线环境下需要提前准备好密钥文件,不建议现场临时折腾。生产环境升级内核前,确认 Secure Boot 状态是我的必查项。
5. 常见问题与排查记录
5.1 依赖错误:装内核时 rpm 报依赖不足
这是离线升级内看到最多的问题。典型报错长这样:
error: Failed dependencies: xxx is needed by kernel-lt-core-6.6.119-1.el8.elrepo.x86_64处理思路:先看缺失的是什么。
- 如果是
linux-firmware这种固件包,直接下载新版linux-firmware一起装。 - 如果是
elfutils-libelf、zlib、perl这种基础库,目标机器上应该有,只是版本太老,可以下载对应版本装上。 - 如果是
kernel-headers或者kernel-devel版本冲突,多半是因为你想装的集成包版本不一致,检查一下下载的 RPM 包是不是同一个版本号。
排查命令很有用:
# 查看所有已安装的内核相关包 rpm -qa | grep -E "kernel|elfutils|linux-firmware" # 查看某个 rpm 的依赖 rpm -qpR kernel-lt-core-*.rpm我记得有一次安装一直失败,排查了半天发现是/etc/yum.repos.d里残留的失效仓库干扰了 rpm 的依赖解析。清掉之后,直接rpm -Uvh *.rpm一次过。所以离线安装时,先处理 repo 文件是个好习惯。
5.2 新内核启动后默认内核还是旧版
装完内核重启进系统,发现uname -r还是旧版。这个问题通常是 grub 默认启动项没设置成功。
先检查当前 grub 环境:
grub2-editenv list输出里如果有saved_entry=...,且指向旧内核,直接用 grubby 重新设置:
grubby --set-default $(ls /boot/vmlinuz-6.6.119* | head -1) grub2-editenv list再确认一遍saved_entry是否变成新内核。如果 grubby 无用,手动改 grub 配置:
编辑/etc/default/grub,把GRUB_DEFAULT改成新内核在 grub 菜单里的序号,重建 grub.cfg。
其实还有一类情况:新内核引导时直接 panic 了,系统自动回退到旧内核。这种时候要去查/var/log/messages或者开机画面里的报错信息,常见原因是/boot空间不足导致 initramfs 没写完整,或者内核参数里指定的根分区路径不对。
5.3 新内核下某块网卡不工作
这个现象很典型:新内核起来了,网卡没起来。用ip link看不到网卡,或者链路是 down 状态。
排查步骤:
# 1. 确认驱动模块是否存在 modprobe igc dmesg | tail -50 # 2. 查看模块加载是否报错 lsmod | grep igc # 3. 确认固件是否存在 ls /lib/firmware/igc/如果模块加载报Invalid argument,大概率是固件版本不匹配。解决思路就是升级linux-firmware包。如果modprobe成功但网卡还是没出现,检查 BIOS 里网卡是否被禁用,或者是不是需要设置 SR-IOV 等特殊参数。
我遇到过最隐蔽的情况是:新内核默认把网卡命名规则改了,ens33变成了enp3s0,导致业务脚本里写死的网卡名失效。这种情况你自己心里有数就好,升级内核前先记录一下当前网络配置,升级后对得上号。
5.4 系统无法启动的应急方案
升级内核导致系统起不来,这事虽然不常发生,但真遇到了别慌,有两条路可以走。
第一条:启动时手动选择旧内核。grub 菜单在开机时会出现,用上下键选中旧内核回车启动。如果在当前版本内核下还能进系统,进去后用 grubby 把默认内核改回旧版,然后再排查新内核的问题。
第二条:如果 grub 菜单都进不去,或者旧内核也没了,需要进单用户模式或救援模式。
以单用户模式为例:开机进入 grub 菜单后,按e编辑当前内核的启动项,在linux开头的那一行末尾加上rd.break,然后按Ctrl+x启动。这会进入一个 emergency 环境,可以用chroot /sysroot切到真实根文件系统,修复内核配置或重新安装内核。
建议任何生产环境升级内核前,把当前 grub 配置备份一份:
cp /boot/grub2/grub.cfg /boot/grub2/grub.cfg.bak.$(date +%F)出问题还能快速恢复。
5.5 常见问题速查表
| 问题 | 可能原因 | 排查/解决 |
|---|---|---|
| rpm 安装报依赖不足 | 基础包版本过老、repo 文件干扰 | 下载对应依赖包,清理失效 repo |
| 重启后还是旧内核 | grub 默认启动项没设置成功 | 用grubby --set-default重新设置,重建 grub.cfg |
| 新内核 panic 回退 | /boot 空间不足、initramfs 未生成 | 清理 /boot,手动执行dracut -f |
| 网卡不工作 | 驱动模块未加载、固件太老 | modprobe加载模块,升级 linux-firmware |
| Secure Boot 阻止启动 | 新内核无签名 | 关闭 Secure Boot 或导入自定义密钥 |
| EtherCAT 模块编译失败 | kernel-devel 版本与新内核不一致 | 下载匹配版本的 kernel-devel/kernel-lt-devel |
写在最后
离线升级内核这件事,操作难度本身不高,真正考验人的是前期的准备和异常情况的处理。我个人这几年下来最深的一个体会是:离线环境里,备份和预案比操作技能更重要。每一次升级前,我都会确保当前内核可用、grub 配置有备份、手头有旧内核的安装介质,这样哪怕新内核出了问题,也能在五分钟内回滚,不耽误业务。
还有一个实用的小建议:把下载过的内核 RPM 包和配套的 devel、firmware 包长期归档在一个专门的目录里,最好再写一份版本清单。离线环境不像联网服务器,随时可以重下包,一旦源失效或者版本被官方下架,你手头的归档就是你唯一的救命稻草。在我这边,每台离线服务器的/opt/upgrade-pkgs目录都保留着历次升级用到的所有包,已经成了标准习惯。
另外,如果你升级内核是为了特定应用,比如 EtherCAT 实时控制,建议升级完成后第一时间跑一遍应用的完整测试流程,而不是只确认内核版本号对了就收工。内核是底层基础,应用跑通了才叫真的升级成功。