很多人一听到“内核升级”四个字,第一反应就是:这玩意儿能不动就不动,万一起不来就是事故。这种谨慎我完全理解,毕竟内核是整台操作系统的地基,地基出了问题,上面跑什么都是白搭。但现实情况是,我们经常不得不去动它——安全漏洞要补、新硬件要认、文件系统要支持新特性,甚至有些软件直接要求内核版本高于某个阈值才肯工作。我这边管理的一批Anolis龙蜥服务器,就多次面临过这种“不得不升”的局面。
Anolis OS作为国内主流的服务器操作系统发行版,和CentOS生态保持了高度兼容,很多朋友是从CentOS迁移过来的,装好之后第一件事就是问:怎么查内核版本?怎么升内核?怎么配新内核默认启动?这些问题看似零散,串起来其实是一条完整链路:先摸清现状,再配置源,然后装包,接着改引导,最后做验证。这篇文章我就按这条链路,把这几年在龙蜥上动内核的完整流程、踩过的坑、总结出的套路全部写出来,方便你照着操作。
1. 升级前的内核现状摸底:先搞清楚为什么动内核
1.1 一个反直觉的事实:升级内核不是更新软件包
很多人第一次在Anolis上执行dnf update,发现内核版本纹丝不动,就开始怀疑是不是源有问题。其实这是正常现象,因为绝大多数发行版的策略是:内核属于“独立更新”的软件包类别,普通update不会主动把内核从一个大版本拉到另一个大版本,这是怕系统自动升级内核后出现兼容性问题,影响正在运行的业务。
所以你在龙蜥上要做的第一件事,不是急着找新内核,而是先想清楚:我到底为什么升内核?我见过不少人是看到别人升了5.10自己也要升,最后升完发现业务依赖的某个内核模块没了,折腾半天又降回来。内核升级不是追求最新,而是追求“当前业务场景下最合适”。
1.2 查询当前内核版本的四种路径
升级前先把现状摸清楚。查询当前内核版本,我常用的有四种方式,各有侧重:
| 命令 | 输出内容 | 适用场景 |
|---|---|---|
uname -r | 当前运行内核的完整版本号 | 最常用,确认“正在用的”内核 |
uname -a | 内核版本+主机名+架构+编译时间 | 需要看编译信息时 |
hostnamectl | 内核版本、操作系统版本、架构 | 一次性确认系统和内核 |
cat /proc/version | 内核版本+GCC版本+编译时间 | 需要确认编译器信息时 |
我个人的习惯是uname -r配合cat /etc/os-release一起看,因为内核版本和系统版本是两回事。比如你现在跑的是4.19.132-anolis,只能说明内核是4.19这条线,不代表系统是某个具体版本。/etc/os-release里能看到PRETTY_NAME字段,那才是操作系统的具体版本信息。
uname -r cat /etc/os-release还有一个容易被忽略的点:uname -r显示的是当前正在运行的内核,而不是机器上已经安装的内核。如果你之前升过内核但一直没重启,这里显示的还是老版本。要看你机器上一共装了哪些内核,需要用下面这条命令:
rpm -qa | grep -E '^kernel'这条命令会列出所有和内核相关的安装包,包括kernel、kernel-devel、kernel-headers、kernel-tools等。其中不带后缀的kernel-版本号才是可引导的内核本体,有几个就说明你的GRUB菜单里应该有几个启动项。
1.3 判断“该不该升”的四个信号
不是任何时候都需要升级内核。结合这些年在龙蜥上处理过的实战场景,我认为出现下面四种信号之一,才真正到了该动手升内核的时候:
- 安全漏洞公告:社区或官方发布了针对当前内核版本的安全通告,涉及你正在使用的服务或内核模块。这种情况优先级最高,必须尽快升。
- 新硬件无法识别:新加了网卡、磁盘阵列卡、GPU等硬件,系统识别不到,或者识别到了但加载固件报错。通常是内核太老,缺少对应驱动。
- 应用对内核版本有硬性要求:某些数据库、虚拟化平台、容器运行时会在启动时检测内核版本,不满足就直接拒绝运行。
- 文件系统或内核特性需要新版本支持:比如要用上较新的Btrfs特性、io_uring高性能IO、或者某些TCP拥塞控制算法,这些往往需要较新内核才能完整支持。
如果以上信号一个都没出现,我的建议是:保持现状。内核升级有收益也有风险,没必要为了“新”而“新”。尤其是生产环境,稳定压倒一切。
2. 软件源是一切的起点:repo配置排查与可用版本确认
2.1 Anolis OS的yum源长什么样
在Anolis OS 8.x上,软件源是通过/etc/yum.repos.d/目录下的repo文件管理的。和CentOS 8类似,它会拆成多个仓库:
| repo名称 | 主要内容 | 内核相关程度 |
|---|---|---|
| AnolisOS-BaseOS | 基础软件包,包含内核、核心工具 | 内核本体在这里 |
| AnolisOS-AppStream | 应用程序和运行时 | 关系不大 |
| AnolisOS-PowerTools | 扩展包,部分编译依赖 | 部分开发依赖包 |
| AnolisOS-Extras | 额外组件 | 关系不大 |
| AnolisOS-Plus | 增强包 | 部分驱动相关 |
升级内核主要依赖BaseOS仓库。所以我排查问题时的第一站,就是确认BaseOS仓库有没有被禁用或者地址配置错误。
查看当前所有启用的仓库,可以用:
dnf repolist如果输出里没有看到BaseOS,或者状态是禁用状态,可以手动查看repo文件内容确认一下:
cat /etc/yum.repos.d/AnolisOS-BaseOS.repo正常情况下的关键配置大概是这样的:
[AnolisOS-BaseOS] name=Anolis OS $releasever - BaseOS baseurl=http://mirrors.aliyun.com/anolis/$releasever/BaseOS/$basearch/os/ enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-ANOLIS这里有个非常容易踩的坑:$releasever变量。如果你系统的releasever解析异常,镜像源地址就会拼错,导致明明配置没问题却显示404。排查时可以用下面这个命令看看变量实际解析成了什么:
dnf --releasever=8 repolist如果加了--releasever=8之后仓库就正常了,说明是系统版本变量出了问题,需要检查/etc/dnf/vars/releasever文件是否存在或者内容是否正确。
2.2 源配置异常时的排查链路
遇到过好几次朋友发截图给我,说dnf makecache报错,一堆Mirror URL无法连接。这种问题90%都不是源本身挂了,而是环境或配置的问题。我自己在龙蜥上排查源故障,遵循的固定链路是:
- 先确认网络连通性:用
curl -I直接测试镜像地址是否可访问。拿阿里云镜像举例:
curl -I http://mirrors.aliyun.com/anolis/8/BaseOS/x86_64/os/确认DNS能解析:如果curl提示无法解析主机名,检查
/etc/resolv.conf里的DNS配置。有些内网环境的机器DNS配置不对,但之前一直没暴露,直到需要联网安装软件包时才暴露出来。确认能访问但dnf仍报错:这种情况通常是证书或gpgkey的问题。私人内网镜像、自建源经常有这种情况,解决办法是先把repo文件里的
gpgcheck=1临时改成0验证一下。如果改成0就好了,说明是gpgkey路径配置错误或导入有问题。清理缓存重试:这是最基础但也是最常被忽略的一步。有时候repo文件改了,但dnf还在用旧缓存:
dnf clean all dnf makecache这条链路走完,90%的源故障都能定位到具体环节。剩下那10%,一般就是镜像源本身在同步或维护,换个时间再试就行。
2.3 查看源里到底有哪些内核版本可装
在确认源可用之后,下一步就是在源里搜索有哪些内核版本可供安装。这一步的关键命令是加--showduplicates参数:
dnf list available kernel --showduplicates不加这个参数的话,dnf只显示一个“最新版本”,加了之后会把所有仓库里存在的不同版本全部列出来。这个细节很关键,因为有时候你并不想升到最新版,而是想升到某个特定修复版本,比如从5.10的早期版本升到某个中期版本,此时就必须靠这个参数把候选版本看清楚。
也可以加上--allowerasing预览依赖变化,但这个时候还没有真正安装,你也可以用下面这条命令来只做模拟,不做真实变更:
dnf install kernel-5.10.xxxx --assumeno--assumeno选项会让dnf把解析好的依赖关系打印出来,然后自动回答“否”,从而不实际执行操作。我每次装内核前都会先跑一次这个命令,看看它会引入哪些新依赖、更新哪些旧包,心里有个底。
3. 正式安装内核:装对了版本才能少踩坑
3.1 用“安装”而不是“更新”的方式引入新内核
这是很多内核升级事故的根源所在。在Anolis上装内核,正确做法是用dnf install,而不是dnf update。
dnf install kernel的行为是“在现有内核之外,再装一个新内核”。它不会动你当前正在运行的内核,而是让你机器上多出一个可选的启动项。这样即使新内核引导失败,重启时还能在GRUB菜单里选择旧内核进入系统,安全性大大提高。
而dnf update kernel这类操作,在某些情况下会把旧内核替换掉或者做更多不可控的变更,操作风险明显更高。所以我强烈建议:升级内核永远用install,不用update。
具体安装命令:
dnf install -y kernel如果你已经通过--showduplicates选定了具体版本,也可以直接指定版本号安装:
dnf install -y kernel-5.10.190-2.5.an8.x86_64安装过程中dnf会检查依赖关系,正常情况下会拉取内核本体、生成initramfs。这个initramfs是系统启动时加载驱动用的临时根文件系统,安装内核时它会被自动生成,不需要你手动执行dracut。但如果你发现安装完成后/boot/initramfs-版本号.img不存在,那就要留意了,说明安装过程可能出了问题。
3.2 开发包和头文件为什么必须一起装
只装kernel本体,对绝大多数纯运行环境是够的。但如果你的机器上要编译内核模块,比如安装某些第三方驱动、DKMS(Dynamic Kernel Module Support)管理的模块,那么kernel-devel和kernel-headers几乎是必须的。
这里要特别强调一个坑:这两个包必须是“对版本”的。也就是说,你机器上跑的是哪个版本的内核,就要装哪个版本的kernel-devel。如果版本对不上,去编译模块时大概率会报类似 “Kernel preparation unnecessary” 之类的问题,甚至直接告诉你找不到/lib/modules/版本号/build目录。
所以在安装内核的同时,我一般会把配套的devel和headers一起装上:
dnf install -y kernel kernel-devel kernel-headers装完开发包后,可以用下面这个命令确认对应的构建目录确实存在:
ls /usr/src/kernels/正常输出里会有一个以完整内核版本号命名的目录,比如:
/usr/src/kernels/5.10.190-2.5.an8.x86_64每次重启之后,我都会习惯性地检查这个目录是否与uname -r的输出一致。如果发现不一致,赶紧找原因,因为这意味着你接下来想编译任何内核模块都会受阻。
3.3 安装完成后的即刻检查
安装结束后,不要着急重启。先花一分钟确认三件事情:
第一,检查/boot目录下的引导文件是否齐全:
ls /boot/重点关注新版本对应的vmlinuz-版本号、initramfs-版本号.img、System.map-版本号这三个文件是否存在。如果initramfs缺失,后面重启极大概率直接失败,进不了系统。
第二,确认内核启动项已经出现在GRUB配置里:
grubby --info=ALL | grep -E '^kernel|index'输出里应该能看到新内核对应的kernel=/boot/vmlinuz-版本号条目,并且前面带有index=编号。
第三,确认一下/boot分区的剩余空间。如果/boot空间不足,比如只剩几十MB,dnf install过程中有可能报“No space left on device”,导致内核虽然下载了但无法写入。我自己就遇到过两次,都是因为旧内核堆太多没清理,新内核的 initramfs 又比较大(有些带完整驱动的能到300MB以上),结果卡在这一步:
df -h /boot如果空间确实紧张,先把旧的、不再使用的内核清理掉(具体方法见第6章),再回来装新的。
4. 重启之后的新世界:切换默认内核是最后一道关卡
4.1 通过grubby精确切换默认内核
装好新内核后,系统并不会自动用新内核启动,除非你是从头安装系统时选的新内核。新内核装好后,默认启动项通常还是旧的那个。此时你想让新内核成为默认启动项,最推荐的工具是grubby,这也是RHEL系发行版里最主流的引导项管理工具。
查看当前默认内核:
grubby --default-kernel这个命令直接输出当前默认启动项对应的内核完整路径,比如/boot/vmlinuz-4.19.132-1.an8.x86_64。
把默认内核切换成新装的版本:
grubby --set-default=/boot/vmlinuz-5.10.190-2.5.an8.x86_64执行完可以用grubby --default-kernel再确认一次,看看默认内核是不是已经变过来了。除了用完整路径,也可以用grubby --set-default-index配合之前grubby --info=ALL查到的index序号来设置,效果是一样的。
这里要注意一个细节:grubby --set-default修改的是GRUB默认启动项,但它内部操作的实际是/etc/default/grub以及已有的GRUB配置文件。在大多数RHEL系系统上,改完之后是即时生效的,不需要再手动执行grub2-mkconfig。不过有部分特殊环境(比如某些UEFI引导场景)可能需要额外手动生成配置,遇到的话看下面一节。
4.2 手动生成GRUB配置的方法
如果你用的是UEFI启动模式,或者执行完grubby --set-default之后发现重启并没有进入预期内核,那可能需要手动重新生成GRUB配置。
先判断系统是BIOS还是UEFI启动模式:
[ -d /sys/firmware/efi ] && echo "UEFI" || echo "BIOS"如果是BIOS启动,生成的配置文件路径一般是:
grub2-mkconfig -o /boot/grub2/grub.cfg如果是UEFI启动,路径则可能是:
grub2-mkconfig -o /boot/efi/EFI/anolis/grub.cfg不同版本、不同发行版的EFI分区挂载路径可能略有不同,最稳妥的办法是先用find或df -h找到EFI分区挂载在哪里,再确认grub.cfg的实际路径。
手动生成配置的操作本身很简单,但执行之后一定要检查输出里是否出现了新内核对应的菜单项:
grep -E 'menuentry' /boot/grub2/grub.cfg | grep 5.10如果配置生成成功且包含新内核菜单项,那说明GRUB层面已经准备好了。
4.3 重启后的验证清单
切换完默认内核,接下来就是重启验证了。但重启不是随便敲个reboot就完事,我一般会在重启前后对个清单。
重启前:
- 检查是否有正在写入的重要数据,生产环境尽量在业务低峰期操作。
- 用
sync强制刷新文件系统缓存。 - 确认无正在进行的软件包安装或其他事务性操作。
重启后优先执行的验证命令:
uname -r这个输出是最直接的证据,证明你的系统当前「真正运行」的内核版本。如果输出的版本和你预期的一致,说明切换成功。如果还是旧版本,优先回顾上一步的GRUB配置是否正确。
接下来还要看一下系统这次的启动信息里有没有异常:
dmesg -T | grep -iE 'error|fail|warn'-T选项会把时间戳转换成可读格式。这条命令只是初步过滤,输出里可能会有些无关紧要的WARN信息,重点看有没有跟“CPU”“内存”“块设备”相关的高危错误。真正出大问题的时候,dmesg里往往有大段红色报错和Call Trace(内核调用栈)。
再做一个系统层面的状态确认:
systemctl status如果有一个或多个服务处于失败状态,别急着下结论,先逐一查看服务的日志。比较常见的情况是:某些服务在旧内核上依赖的某个模块名变了,在新内核里改名或合并了,导致服务起不来。这种问题不是内核本身的毛病,但需要你去适配服务配置。
5. 内核升级后真正的坑:模块、驱动与回滚
5.1 用dmesg和lsmod排查驱动加载
内核升级后最容易出问题的,其实是驱动层。特别是那些依赖内核模块的硬件,比如网卡、RAID卡、GPU,一旦模块加载失败,直接影响业务。
排查模块加载是否正常,第一步看系统启动日志:
dmesg -T | grep -iE 'module|driver|firmware'重点找有没有 “Failed to load module”、“Direct firmware load failed” 之类的关键词。这些提示通常意味着某个模块或固件文件没找到,或者因为内核版本API变化导致旧的二进制模块无法加载。
第二步确认关键模块确实在运行:
lsmod | grep <模块名>比如你的机器用的是Intel网卡,就查一下i40e或者ixgbe这些模块是否已经加载。如果模块没加载,可以手动尝试加载:
modprobe <模块名>手动加载如果报错,错误信息会直接告诉你原因,比如 “Invalid module format” 表示模块版本不匹配,“Unknown symbol” 表示依赖的某个符号在内核里不存在。前者说明你用的是旧版驱动模块,后者说明驱动版本太老或太新,需要换适配新内核的驱动版本。
5.2 第三方驱动的兼容性处理
在内核升级场景里,第三方驱动一直都是大头。尤其是GPU驱动和某些存储厂商提供的闭源驱动,它们和内核的耦合度极高,内核一换,驱动基本就得重装。
处理这类问题,最规范的方式是走DKMS机制。DKMS可以把驱动源码注册到系统里,每次内核更新后自动重新编译驱动模块,从而匹配新内核。如果你的第三方驱动源码支持DKMS,安装步骤通常是:
# 以网卡驱动为例,假设驱动源码在 /opt/driver 目录 cd /opt/driver make clean make make install如果驱动包本身支持DKMS,则:
dkms add -m <模块名> -v <版本号> dkms build -m <模块名> -v <版本号> dkms install -m <模块名> -v <版本号>没有DKMS支持的驱动就比较折腾了,需要手动确认它的构建脚本能否在当前内核下通过编译。这也是为什么我在前面强调:装内核的同时一定要装匹配版本的kernel-devel,没有头文件和构建目录,第三方驱动编译这一步根本走不下去。
对于从旧内核升级上来的机器,我的建议是:升级前先把当前使用的第三方驱动版本记录下来,去官网查一下它对新内核的支持情况,确认有对应适配版本再动手升级内核。否则新内核起来后发现驱动不支持,又得回滚,来回折腾非常不值当。
5.3 新内核翻车时的快速回滚方案
新内核引导失败怎么办?这种场景不需要慌张,因为你当初是用install方式装的新内核,旧内核妥妥还在。重启后GRUB菜单里会有多个启动项,分别是旧内核和新内核,只要选择旧内核的菜单项进入系统,就完成了“回滚”。
但如果GRUB菜单没有出现,或者你人在机房没法看屏幕,那就需要有非交互式的应对方案。
方案一:在GRUB界面按e进入编辑模式,找到linuxefi或linux开头的行,把里面的内核路径临时改成旧内核路径,然后按 Ctrl+X 启动。这种方法是一次性生效,下次重启还是走默认配置。
方案二:如果你能进入单用户模式或通过某种方式挂载根文件系统,可以直接用grubby --set-default把旧内核重新设为默认,再写回GRUB配置:
grubby --set-default=/boot/vmlinuz-旧内核版本 grub2-mkconfig -o /boot/grub2/grub.cfg方案三:如果连GRUB菜单都进不去,只能用安装介质进入救援模式,chroot到原系统根目录后再执行上述命令。
回滚之后系统会回到旧内核环境,此时要清楚一点:你机器上仍然留着那个有问题的新内核包。下次重启时如果默认配置没有改回来,可能又会进入错误内核。所以回滚后第一件事就是确认默认启动项,必要时直接把出问题的新内核包卸载掉,防止误入:
dnf remove kernel-问题版本号6. 旧内核清理与长期维护策略
6.1 清理旧内核的正确打开方式
内核升级了几次之后,/boot分区里的vmlinuz、initramfs会越来越多,/usr/lib/modules下的模块目录也越来越多。这时候就该做清理了。但清理旧内核有一定风险,不能直接rm -rf /boot/vmlinuz-旧版本,因为GRUB配置、rpm数据库、模块目录都需要同步处理。最规范的方式是用包管理器清理。
在Anolis上,推荐用下面两种方式之一:
方式一,直接指定旧内核版本号卸载:
dnf remove kernel-4.19.132-1.an8.x86_64 kernel-devel-4.19.132-1.an8.x86_64注意头文件和本体要一起清理,不然留下一个孤零零的开发包,下次编译模块时容易混淆。
方式二,使用dnf自带的保留策略自动清理:
dnf remove --oldinstallonly这个命令会删除所有超过保留数量的旧内核,比较省事,但注意它只对标记为installonly的包生效,也可能把你想留的某个次新版本删掉,所以执行前建议先加--assumeno做个模拟:
dnf remove --oldinstallonly --assumeno我已经把“清理前先模拟”这件事当成铁律了,因为内核清理误操作导致系统无法启动的案例,比内核升级本身翻车还多。
6.2 长期维护的“铁律”
内核维护不是一次性工作,需要形成习惯。我在龙蜥上长期管理多台服务器,总结出几条自己一直遵守的原则,供你参考。
第一条:机器上永远保留两个可用内核。新内核确认稳定运行一两周后,再考虑清掉旧内核。这个策略能保证在任何时刻,GRUB菜单里都有一个已知可用的备胎启动项。常用配置是把installonly_limit设置成2或3:
# /etc/dnf/dnf.conf 中添加以下配置 installonly_limit=3这个参数意思是最多同时保留几个installonly包,限制后,系统会自动滚动清理最旧的版本,降低手动管理的负担。
第二条:升级前做好记录。我是习惯在维护文档里记下这么几项内容:当前内核版本、目标内核版本、为什么升、第三方驱动是否有对应适配版本、回滚方案是什么。这份记录在出问题时能帮你快速判断是回滚还是继续排查。
第三条:关注安全公告,但不要盲目跟风。内核修复安全漏洞的公告出来之后,不用当天就急着升,但要把这个事纳入近一周的工作计划。等第一批用户升级反馈稳定之后,再在自己的环境里做测试升级,风险会小很多。毕竟那些被历史反复验证的“升级事故”,大部分都是抢跑抢出来的。
第四条:升级前把业务验证脚本准备好。重启进新内核后,快速跑一遍核心业务路径或健康检查脚本,确认CPU、内存、磁盘IO、网络连接一切正常。宁可脚本多写两行,也别等业务报警了才意识到内核升级可能影响了什么。我自己管理的那批机器上,就放着一个简单的检查脚本,开机会自动记录关键指标,一旦内核升级后异常,我能第一时间从数据里看到端倪。
我在实际操作中的体会是,内核升级这件事,最大的风险其实不在技术本身,而在于准备不足和操作姿势不对。只要把前面说的查询、配置、安装、切换、验证这条链路走熟,并且始终坚持“留一个可回滚的旧内核”的底线,Anolis龙蜥的内核升级完全可以做到从容不迫。最后再分享一个小技巧:如果你经常需要在多台龙蜥机器上做内核同步升级,可以考虑把dnf install kernel kernel-devel kernel-headers这个命令和grubby --set-default写成一个shell脚本,参数化目标版本号,配合自动化批量工具一次性分发到所有目标机器上。这样既保证了操作的一致性,也减少了手动敲命令出错的概率。