首页 / 资讯中心 / 文章详情

离线环境CentOS 7.9内核升级实践:从3.10到5.15

离线环境CentOS 7.9内核升级实践:从3.10到5.15 ★ FEATURED ARTICLE
这个需求如果是在内网环境里处理那确实是个高频问题。很多跑着旧系统的机器因为安全合规或者新硬件兼容性的原因必须升级内核但偏偏连不上外网源不能直接yum update。我把自己在一台 CentOS 7.9.2009 上做离线升级的完整过程、踩过的坑和原理都整理在下面了从选包、传包、安装到处理启动项和驱动问题一条线走完。1. 为什么这台 CentOS 7.9.2009 必须手动离线升级内核先说背景。CentOS 7.x 系列默认自带的 3.10.0 内核年代确实比较久了。它虽然稳定但遇到三类情况就明显吃力一是新出的网卡、NVMe 固态、GPU 直通这类硬件2.6/3.10 里没有驱动二是跑容器和 Kubernetes 时像 overlay2 存储驱动、防火墙规则、网络性能这些内核特性跟不上三是安全扫描总报内核漏洞生产环境又改不了只能靠升级内核打补丁。我这次处理的机器就在某公司内网系统版本是CentOS Linux release 7.9.2009 (Core)内核还是3.10.0-1160.el7.x86_64需要升级到 5.x 系列。这台机器没有外网访问权限内网也没有配置本地 Yum 源镜像所以完全不能走在线安装那条路。更麻烦的是这台机器上还跑着业务不能随便重启也不能因为装了一半缺依赖就回滚不了。所以必须做到“包一次传全”“一次装干净”“启动项可回退”。升级之前先自我确认一下需求到底是不是必须升级如果只是为了解决某个特定模块的 bug先看看能不能通过更新kernel-3.10.0-1160.el7的补丁版本解决而不是直接跳到 5.x。但这次的目标很明确新硬件驱动不识别老内核解决不了升级是唯一路径。2. 离线环境下的内核包获取与版本选型逻辑离线环境最尴尬的不是怎么装而是怎么拿到正确的包。网上很多教程直接让你yum install kernel-ml在内网机器上回车就是Could not resolve host这条路直接断掉。所以核心思路是在一台可以联网、且同样是 CentOS 7.9 的机器上把 rpm 包全部下载好再通过内网传输手段拷贝到目标机器。2.1 ELRepo 源的配置与包列表解析CentOS 7 默认的 base 源里内核版本只到 3.10.0想要 5.x 主线内核最靠谱的是用 ELRepo 源。ELRepo 是专门为 RHEL/CentOS 提供较新内核和驱动模块的第三方仓库它的 kernel 包分两种kernel-mlMainline Stable主线稳定版更新频繁内核大版本迭代快比如 5.15、6.1 这类。kernel-ltLong Term Support长期维护版更新节奏慢更偏向稳定生产环境使用。实操中我优先选kernel-ml因为这台机器需要识别较新的硬件lt 版本在某些最新硬件的支持上会滞后。如果对稳定性要求极其严苛、不想频繁跳大版本可以用kernel-lt逻辑完全一样。在联网机器上先装 ELRepo 的 GPG key 和仓库配置rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org rpm -Uvh https://www.elrepo.org/elrepo-release-7.0-4.el7.elrepo.noarch.rpm装好之后先查看可用的内核版本确认我们要下载的包名和依赖关系yum --enablerepoelrepo-kernel list available | grep kernel-ml输出里通常会有这样几个包kernel-ml.x86_64主内核包kernel-ml-devel.x86_64内核头文件和开发库编译第三方驱动模块时必需kernel-ml-tools.x86_64内核工具集kernel-ml-tools-libs.x86_64工具依赖库有些教程只让你下载一个kernel-ml就完事这对纯跑业务的普通场景也许够用但一旦你后面要编译网卡驱动或装某个需要内核头文件的软件就会发现kernel-ml-devel缺了根本进行不下去。所以这次我把主包和 devel 包一次性全下载。2.2 用 yumdownloader 完整拉取 rpm 包及其依赖直接下载 rpm 用yumdownloader或者repotrack都行。区别在于yumdownloader默认只下载你指定名字的包依赖需要手动再用--resolve参数一起拉repotrack会把所有依赖关系自动分析并下载完整闭环更适合离线安装场景。我这次用的是yumdownloaderyum install -y yum-utils mkdir -p /root/kernel-rpms cd /root/kernel-rpms yumdownloader --enablerepoelrepo-kernel --resolve kernel-ml kernel-ml-devel--resolve参数很关键它会自动把依赖包也下载进来。内核包常见的依赖有kernel-ml-tools-libs、elfutils-libelf等这些在目标机器上不一定有缺一个 rpm 安装就会报错。如果你不放心可以再执行一次repotrack --enablerepoelrepo-kernel -a x86_64 kernel-ml这个命令会把kernel-ml相关的所有架构、所有依赖包全部拉下来放在当前目录里。两者可以结合使用先用repotrack拉完整列表再用yumdownloader查缺补漏。最终目录里应该是干净的几个 rpm 文件不要混入大量无关包。下载完顺便做一下完整性校验rpm -K *.rpm这一步会显示每个包的 GPG 签名是否有效防止 rpm 包被破坏或来源不可信。我看到全部是OK才进入下一步。2.3 跨机器传输的三种方式包拿到了怎么传到目标机器内网环境千差万别我这边提供三种方式按实际情况选如果目标机器有内网 IP且和操作机在同一网段直接用scp传最快。如果中间隔着跳板机或者安全域先传到跳板机指定目录再用内网文件摆渡工具比如某公司的文件安全交换平台推过去。如果机器数量多、路径统一把 rpm 放在内网共享目录或 FTP 服务器上目标机器自行拉取避免一台一台操作。我自己习惯把包名、版本号写进一个文本清单里顺便记录 SHA256 值。这样传到内网后如果出现大小不一致、rpm 损坏的情况第一时间就能比对不用靠猜。3. 离线安装与依赖冲突的完整处理链路这一步是整篇文章的重头戏。rpm 包的安装方式看着简单实际执行时经常遇到依赖不足、版本冲突、架构不对这几类问题每一样都可能导致升级中断。3.1 目标机器安装前的环境检查与快照准备动手之前目标机器一定要做好三件事第一确认当前磁盘空间。内核包解压后会在/boot下写入vmlinuz、initramfs文件/usr/src/kernels下会生成内核源码目录加起来可能占用 1-2GB。检查一下df -h /boot /usr/src第二查看当前内核版本和启动项顺序方便后面核对uname -r grub2-editenv list第三如果机器是虚拟机强烈建议先在虚拟化平台上做一次快照如果是物理机确认备份工具可用。内核升级本来就可以回滚但回滚的前提是旧内核还在如果安装过程异常导致/boot损坏没有备份就会很被动。我之前遇到过一次停电导致/boot分区文件损坏的情况最后靠备份恢复从那之后再也不敢跳过这一步。如果机器上有第三方内核模块比如 NVIDIA 驱动、某厂商的多路径软件需要先记录它们的版本因为内核升级后这些模块通常要重新编译。检查命令lsmod | grep -E nvidia|drbd|multipath3.2 rpm -ivh 还是 rpm -Uvh这个选择影响回滚策略安装命令有讲究。大多数教程用rpm -Uvh kernel-ml*.rpmU表示升级会替换旧内核并安装新内核。但在 CentOS 7 上内核 rpm 的机制比较特殊即使你用-U,它默认也会保留旧内核的引导项不会卸载旧内核。这是因为内核包的%post脚本会调用grub2-mkconfig重新生成引导配置把新旧内核都写进去。不过我还是更推荐用-ivh安装新内核而不是-Uvhcd /root/kernel-rpms rpm -ivh kernel-ml-*.rpm kernel-ml-devel-*.rpm原因很简单-i是纯安装不触发“升级”语义不会对既有内核做任何替换动作回滚逻辑更干净。-U也不是不行但如果你同时传了多个内核版本包它可能会按顺序处理处理完的旧包状态不好预估。分开装、按需装是离线手动升级的基本素养。如果遇到依赖缺失报错比如提示libxxx.so.1()(64bit) is needed不要急着加--nodeps。先记录缺什么在之前的 rpm 下载目录里找对应包单独安装。只有在确定缺失的只是一些无足轻重的辅助库、且手动验证过不影响内核启动时才可以用rpm -ivh --nodeps kernel-ml-*.rpm--nodeps是一把双刃剑。内核主包本身依赖很少跳过依赖大概率能装上但若缺少的是elfutils-libelf这类与模块压缩有关的库后续用dracut生成 initramfs 时就会出现诡异问题。所以尽量别用。3.3 安装完成后的引导文件核对与内核模块确认安装完成后不要急着重启。先确认两件事第一/boot下是否生成了对应 5.x 版本的vmlinuz和initramfs文件ls -l /boot/vmlinuz-* /boot/initramfs-*第二内核模块目录是否存在ls /usr/lib/modules/正常会看到旧的3.10.0-1160.el7.x86_64和新的5.15.x-xxx.el7.x86_64两个目录。只有模块目录完整生成新内核启动时才能加载各类驱动否则就算引导成功了起不来网络、识别不了磁盘照样凉凉。还有一点很容易漏kernel-ml-devel安装后会在/usr/src/kernels下生成一个源码树。如果你后面需要编译第三方模块这个目录必须留着。别为了省空间去删它删了就后悔。4. 引导顺序调整与旧内核回滚机制这一步没做好重启后机器可能直接起不来或者起来还是旧内核白忙活。CentOS 7 用 GRUB2 引导内核选择逻辑由/boot/grub2/grub.cfg和grub2-editenv共同决定。4.1 默认启动内核的切换命令与细节安装完成后查看当前默认启动项grub2-editenv list输出里会出现一行类似saved_entryCentOS Linux (3.10.0-1160.el7.x86_64) 7 (Core)这说明默认启动的还是旧内核。把它切换成新内核grub2-set-default 00表示 grub.cfg 中第一个 menuentry但这里有一个坑新装的内核通常排在第一个但如果你机器上有多套系统或之前手动改过 grub顺序不固定。最稳妥的做法是先列出所有启动项及顺序awk -F\ /menuentry / {print $2} /boot/grub2/grub.cfg找到形如CentOS Linux (5.15.x-xxx.el7.x86_64) 7 (Core)的那一行记下它前面的编号然后grub2-set-default CentOS Linux (5.15.x-xxx.el7.x86_64) 7 (Core)可以用名称精确指定比序号靠谱。执行完再确认grub2-editenv list注意grub2-set-default修改的是saved_entry,它会成为下次启动时的默认项。但我遇到过一种情况执行后saved_entry改了重启后居然还是旧内核。原因是/etc/default/grub里如果设置了GRUB_DEFAULTsaved,且/boot/grub2/grub.cfg里没有正确包含新内核的 menuentry,grub 会退回到第一个可用项。所以重启前一定要检查 grub.cfg 里是否真的写入了新内核项grep 5.15 /boot/grub2/grub.cfg如果没有需要手动执行一次grub2-mkconfig -o /boot/grub2/grub.cfg这一步会把新内核、新 initramfs 完整写进引导配置。不执行的话前面全白搭。4.2 重启验证流程与意外中断时的应急处理切换到新内核后重启sync reboot重启过程中注意观察 GRUB 界面看默认选中的是不是新内核是否出现类似error: file not found的提示。如果一切正常系统会依次加载内核、解压 initramfs、挂载根文件系统最后进入登录界面。等系统起来后立刻验证uname -r看到5.15.x-xxx.el7.x86_64就说明升级成功。但这只是第一步接着要验证关键服务是否正常systemctl status network systemctl status sshd以及磁盘和网络是否正常识别lsblk ip addr如果重启后直接卡在 GRUB 界面或者内核 panic,不用慌。重启机器在 GRUB 菜单里快速按方向键选择旧内核那个 menuentry,回车即可回到原来的 3.10.0 内核。旧内核并没有被移除所以理论上不会丢失系统。如果连 GRUB 菜单都看不到最可能的原因是/boot/grub2/grub.cfg损坏。这种情况物理机需要用系统安装光盘或救援模式修复虚拟机则可以直接挂载 ISO 进 rescue。这里也再次说明为什么安装前备份和确认 grub.cfg 如此重要。5. 升级后必踩的驱动与性能适配坑内核版本跳了接近两个大版本硬件驱动和系统性能参数的兼容性一定会受影响。这一节我专门讲升级后碰到的实际问题提前给大家打预防针。5.1 第三方内核模块重新编译流程如果你的机器装过 NVIDIA GPU 驱动、某厂商的存储多路径软件、或者专门的网卡驱动升级内核后大概率会失效。因为这类驱动通常是 out-of-tree 模块依赖内核源码树编译内核版本一变模块就得重编。以最常见的 NVIDIA 驱动为例升级前先把驱动安装包准备好比如NVIDIA-Linux-x86_64-xxx.run,升级后用新内核重新安装一次sh NVIDIA-Linux-x86_64-xxx.run --kernel-source-path/usr/src/kernels/$(uname -r)/整个过程需要gcc、make、kernel-devel配合。如果你装内核时用了--nodeps导致kernel-devel没装上或者装的是旧版 devel编译就会报各种找不到头文件的错误。这也是我前面反复强调一定要装kernel-ml-devel的原因。验证模块是否加载成功lsmod | grep nvidia如果模块列表里没有说明新内核没有自动加载驱动业务依赖 GPU 的应用会直接报错。5.2 内核参数与业务进程兼容性调整从 3.10 升到 5.15一些内核默认参数变了。比如fs.suid_dumpable的默认值net.ipv4.tcp_congestion_control默认算法vm.swappiness的行为部分 cgroup 版本的默认挂载方式像我们用 Docker 的老机器升级内核后如果发现容器起不来查一下 cgroup 版本mount | grep cgroup5.x 内核默认开了 cgroup v2而 Docker 20.10 之前的版本对 v2 支持不完整可能需要改/etc/default/grub里的GRUB_CMDLINE_LINUX追加systemd.unified_cgroup_hierarchy0强制回退到 v1重新生成 grub.cfg 并重启。这一点很关键我在一台跑着旧版 Docker 的机器上踩过容器全部起不来日志全是 cgroup 相关的报错。另外就是性能监控工具和内核版本匹配的问题。比如某些版本的perf、bpftrace对内核版本敏感升级后需要同步更新工具或者直接用内核源码树自带的工具。5.3 模块缺失时的 udev 规则排查思路如果网卡在旧内核下是eth0新内核下变成ens160或者干脆识别不到多半是 udev 规则和固件缺失的问题。排查分三步第一看硬件是否被识别lspci | grep -i ethernet第二看对应驱动模块是否存在modprobe -l | grep 驱动名第三如果驱动模块不存在检查kernel-ml是否包含了该硬件的驱动。Intel 和 Realtek 的主流网卡基本都内置了但某些小众网卡只提供旧版驱动新内核反而没有。这种情况只能去厂商官网找新版驱动源码用dkms或手动编译安装。另外不要忽略固件文件。内核 5.x 对部分网卡、显卡要求/lib/firmware下有对应固件缺了会报failed to load firmware。这个包一般叫linux-firmware在离线环境下记得也从联网机器拉下来一起装。yumdownloader --resolve linux-firmware rpm -Uvh linux-firmware-*.rpm一个小细节linux-firmware是独立于内核的包装它不会影响当前内核但会影响新内核的硬件支持范围。6. 从这次升级到后续维护的运维建议内核升级不是一次性的动作它只是一个开始。升级完成后后续的维护还需要注意几件事。6.1 新旧内核共存期的日常操作纪律升级后新旧内核会共存一段时间。默认启动新内核但旧内核的 menuentry 保留以便出现问题时能回滚。这个共存期建议至少保持一两周等业务稳定跑过一轮高峰、验证完所有驱动和服务再考虑清理旧内核。清理旧内核时用yum remove而不是手动rmyum remove kernel-3.10.0-1160.el7.x86_64yum remove会同步更新 grub.cfg 和/boot下的文件。手动删除容易出现残留配置或 grub 指向不存在文件的问题。不过要注意别把旧内核全删光至少留一个能引导的内核。我见过有人为了省磁盘空间把新旧内核都删了重启后 GRUB 找不到内核系统直接挂掉最后只能拿安装盘救援。6.2 离线环境后续内核小版本升级的路径规划这次内核升级是从 3.10 跳到 5.15跨度很大。以后再做小版本升级比如 5.15.x 到 5.15.y建议不要再用rpm -ivh,而是用rpm -Uvh因为同大版本内的升级新内核会完全替换旧内核模块和头文件更容易保持一致。同样离线环境下小版本升级也走“联网机器下载 rpm → 传输 → 安装”这条路。下载时用repotrack拉全依赖装完后执行grub2-mkconfig -o /boot/grub2/grub.cfg grub2-set-default 新内核名称这两条命令是离线升级里最稳妥的组合可以形成固定操作清单交给其他人执行也不会出错。6.3 如何把这次升级过程沉淀为团队可复用文档我从这次操作里沉淀出的结论是离线升级内核真正难的不是敲命令而是对依赖关系的梳理、对回滚方案的预演、对驱动兼容性的测试。建议把整个流程写成可复用的检查清单至少包含以下项目目标机器当前内核版本需要升级到哪个版本kernel-ml 还是 kernel-lt联网机器上已经准备好哪些 rpm 包SHA256 是否校验目标机器的磁盘空间、grub 启动项、是否存在第三方模块安装后的引导文件核对结果重启后的内核版本、服务状态、硬件识别情况回滚步骤和旧内核保留策略有了这份清单下一次就算换一个人操作也能按图索骥不用重新踩一遍坑。我在实际执行中还习惯把每一步的日志用script命令记录留存script /var/log/kernel-upgrade-$(date %F).log这样升级完可以回溯每一步的操作和输出出现问题时能快速定位到底哪一步出了问题。这次升级最终顺利把机器跑在了 5.15 内核上新网卡驱动识别正常容器服务恢复安全扫描的内核漏洞项也清零了。整个过程最深的体会是离线环境不可怕可怕的是准备工作不充分。把依赖想全、把回滚想好、把驱动问题留在重启前解决升级这件事其实是个体力活不是技术活。以后如果你们的内网机器也要做同样操作欢迎随时交流具体细节。
阅读完成 · 觉得有帮助?
咨询建站