
这事儿在运维圈其实特别常见手上一台跑了好几年的 CentOS 7.9.2009 服务器内核还停留在 3.10.0-1160 那个老版本想升级内核又碰上个内网隔离环境没外网、没 yum 源、甚至没本地镜像只能靠手动搞。折腾过几次之后我把这套离线手动升级内核的完整流程和中间踩过的坑整理出来给同样被困在内网里的兄弟们一个参考。先说清楚这套东西解决什么问题。CentOS 7.9 默认的内核版本是 3.10.0-1160 系列这个版本本身很稳定但放到今天就有点力不从心了新的硬件驱动认不全、容器网络性能上不去、某些文件系统特性不支持甚至有些新出的数据库和中间件会直接要求内核版本不低于某个值才让装。离线手动升级内核本质上就是用你手里已有的内核 RPM 包或者源码包在没有网络依赖的情况下把系统内核从 3.10 推到 4.x、5.x 甚至更高。我这边实测过好几台机器最典型的是把内核从 3.10.0-1160 升到 5.4.270 系列整体跑了半年多没出过问题。这套方案适合谁两类人最需要一类是既要卡合规又要上新功能的内网运维工程师另一类是被某些业务软件“强制要求内核版本”的部署实施同学。不管你是刚接触 Linux 的小白还是老运维这篇文章里的每一步我都是按最笨也最不容易出错的方式来写的照着做基本都能成。1. 离线升级的整体思路与方案选型1.1 为什么必须走离线手动这条路很多时候不是我们想手动是真的没得选。政务云、金融内网、军工项目这类环境安全管控等级高服务器物理上就在隔离网里yum 源指向的镜像站根本连不通。你敲一句yum install kernel等来的只有超时和报错。就算有些环境开了白名单能访问个别域名下载个几百兆的内核包也要审批半天不现实。还有一个常见场景是生产环境变更管控严格。即便是能上外网的机器因为涉及生产业务连续性变更窗口就那么一两个小时你不可能在服务器上开着 yum 慢慢解析依赖。手动下载好包、传到指定目录、本地安装、改引导、重启这一条龙控制在 20 分钟内搞定才是生产环境能接受的操作模式。另外有些软件对内核版本有硬性要求。比如某公司自研的数据库组件安装文档里明确写了“内核版本须高于 4.18”否则直接拒绝安装。这种场景下离线手动升级内核就是一个绕不开的环节。1.2 方案选型RPM 包升级 vs 源码编译离线升级内核主流有两个路线一个是搞到 RPM 包本地安装另一个是拿源码包在自己机器上编译。我个人的强烈建议是能用 RPM 就用 RPM别碰源码编译。RPM 包的好处是快、干净、可管理。装完之后rpm -qa | grep kernel能看到完整的包列表卸载也方便。最关键的是 RPM 包自带编译好的模块比如网卡驱动、文件系统模块这些装上就能用不用等编译。而源码编译的话运气好一个小时能编完运气不好依赖缺一堆、编译报错连环坑搞三五个小时都正常。编译过程中还容易把系统搞出问题比如/boot分区空间不够、gcc 版本不满足要求等。那 RPM 包去哪搞渠道有这几个官方 elrepo 仓库的 kernel-lt 和 kernel-ml 系列这是最常见的来源。kernel-lt 是长期支持版稳定优先kernel-ml 是主线最新版功能多但激进。官方 CentOS 仓库里的 kernel 包版本比较保守但胜在兼容性有保障。第三方镜像站比如某些高校的镜像源下载速度不错。我这边用的最多的是 elrepo 的 kernel-lt 系列。原因很简单长期支持版本维护周期长不会出现升完级没过几个月就没人管的情况。生产环境求稳5.4 LTS 版本是我个人认为最合适的选择性能比 3.10 提升明显同时稳定性经过了大规模验证。1.3 依赖问题的处理逻辑离线环境最头疼的就是依赖。内核 RPM 包不像普通软件依赖那么复杂但也不是完全无依赖。我踩过最典型的一个坑是安装新版内核时提示需要更高版本的elfutils-libelf而系统自带的版本太旧本地又没这个包。解决思路是这样的在能上网的机器上先用 yum 的downloadonly插件把内核包和它的依赖一起拉下来然后用rpm -Uvh本地安装。比如你可以执行yum install --downloadonly --downloaddir/root/kernel-rpms kernel-lt-5.4.270这样会把内核包和依赖一次性下载到指定目录再把整个目录传到内网机器上本地安装时就不会缺依赖了。如果目标机器连 elrepo 仓库都访问不了那就得在另一台能上网的同版本系统上把这个流程跑一遍然后把下载好的东西全部拷过去。这招我屡试不爽尤其是面对各种“哦豁又缺包了”的场景。2. 升级前的准备工作与核心细节2.1 版本选择内核不是越新越好很多人有个误区觉得升级内核就是要上最新的 6.x、7.x。但在生产环境我奉劝一句够用就好。内核版本越高对配套的用户态工具、驱动模块的要求也越高越容易出现兼容性问题。做版本选型时先看业务方的要求。如果业务方没明确要求那就看驱动兼容性。比如你的机器上有某厂商的 RAID 卡或者 HBA 卡升级前一定要去厂商官网确认新版内核是否还带对应驱动或者说驱动模块能不能正常加载。我之前就遇到过一次升级完内核后 RAID 卡驱动模块加载失败磁盘直接找不到了吓得冷汗直流还好有备份内核可以回滚。选版本时另一个参考指标是官方的维护周期。Linux 内核社区对每个版本的支持周期不一样有些是 2 年有些是 6 年。选一个维护周期长的 LTS 版本省得以后三天两头又要升级。我常用的推荐组合是 CentOS 7.9 kernel-lt 4.4 或 5.4这两个版本维护周期长社区反馈多出问题的概率低。2.2 硬件兼容性预检升级内核之前先把硬件信息摸一遍。这种检查花不了几分钟但能帮你规避大部分“升完起不来”的悲剧。# 查看当前内核版本 uname -r # 查看 CPU 信息 lscpu # 查看 PCI 设备列表重点关注网卡、RAID 卡、HBA 卡 lspci | grep -E Ethernet|RAID|Serial Attached SCSI # 查看已加载的内核模块 lsmod特别是网卡驱动。很多服务器用的是板载网卡芯片型号各不相同。升级完内核后最怕的就是新内核不带老网卡的驱动模块导致 SSH 断了就连不回去只能物理接显示器进机房排查。我一般会提前把lsmod | grep -E e1000|igb|ixgbe|bnx2|tg3的输出记下来升级完新内核后对比一下确保关键驱动模块都在。另外/boot分区空间也要检查。内核 RPM 包装完会在/boot下生成 vmlinuz、initramfs 等文件通常每个版本占用 30~50MB 左右。如果/boot分区当初划分得很抠门只有 200MB 空间那装新内核前就得分区扩容或者清理旧内核文件。这个坑特别隐蔽等到安装时报“No space left on device”才知道就晚了。2.3 备份与安全网设置升级内核这种事胆子大没错但该有的退路一定要留好。我每一次升级前都会做以下三件事第一件事是确认现有内核的引导项完好。执行grub2-editenv list查看当前的默认引导项记下来。如果里面显示saved_entryCentOS Linux (3.10.0-1160.el7.x86_64)这样的内容说明现在走的还是老内核这个信息后面有用。第二件事是把当前的内核 RPM 包保留好不要删。万一新内核有问题直接rpm -ivh装回去或者用 grub 选择老内核启动就行。另外/boot下的 vmlinuz 和 initramfs 文件也千万别手贱去清理。第三件事是检查 grub 配置的完整性。执行grub2-mkconfig -o /boot/grub2/grub.cfg重新生成一下引导配置确保无论怎么折腾 grub.cfg 文件都是最新的。这一步实际上是一个兜底操作防止系统之前的引导配置就处于半损坏状态到时候升级完内核重启引导不了你都分不清是升级导致的还是原本就有问题。3. 离线手动升级的完整实操过程3.1 准备内核 RPM 包与依赖包假设你已经在能联网的机器上下载好了所需的内核 RPM 包。这里我以 kernel-lt-5.4.270 为例通常你会拿到以下几个文件kernel-lt-5.4.270-1.el7.x86_64.rpm kernel-lt-devel-5.4.270-1.el7.x86_64.rpm kernel-lt-headers-5.4.270-1.el7.x86_64.rpm有人问 devel 和 headers 包要不要装。我的建议是如果只是为了跑业务系统只装 kernel 主包就够了如果后续还要编译第三方内核模块那 devel 和 headers 也得装。保险起见我一般全装反正这几个包加起来也就一两百 MB不差这点空间。把这些文件上传到目标服务器的/root/kernel-upgrade目录下。传输手段就看你的环境了能 scp 就 scp能挂载 U 盘就挂 U 盘什么都不行就刻光盘。内网环境总有它的办法。3.2 本地安装内核包进入目录后先做一个依赖校验cd /root/kernel-upgrade rpm -Uvh *.rpm --test这个--test参数太重要了。它会模拟安装告诉你有没有缺依赖但不会真正写入系统。如果这一步能顺利通过后面就放心多了。如果报缺依赖按照 1.3 里的思路去补齐不要硬装。确认依赖没问题后正式安装rpm -Uvh *.rpm安装完成后确认一下内核版本是否已经写入系统rpm -qa | grep kernel正常情况下你会看到新旧两个版本的内核包同时存在。这是好事情说明系统里现在有两个可选内核出问题还能回退。3.3 配置 GRUB 引导项新内核装好了但不代表重启后就会自动用它。Linux 的 grub2 引导默认会从/boot/grub2/grub.cfg读取菜单并按照预设的默认项来启动。我们需要把默认引导项指向新内核。先看一下当前默认引导项是什么grub2-editenv list输出的saved_entry通常是一长串如CentOS Linux (3.10.0-1160.el7.x86_64) 7 (Core)的菜单项。上面讲过我们要把它改成新内核对应的菜单项。获取新内核对应的菜单项编号或者名称可以执行grep -E menuentry|submenu /boot/grub2/grub.cfg | cut -d -f2这里会列出 grub.cfg 里所有的引导项名称。找到包含你新装内核版本的那一行比如CentOS Linux (5.4.270-1.el7.x86_64) 7 (Core)。接下来有两种方式配置默认引导项。方式一是直接按标题名称设置grub2-set-default CentOS Linux (5.4.270-1.el7.x86_64) 7 (Core)方式二是按菜单序号设置序号从 0 开始计数。注意 grub.cfg 里可能有 submenu 嵌套的情况比较麻烦我建议优先用第一种方式字符串名称不容易弄错。设置完以后再次执行grub2-editenv list确认saved_entry已经更新。这里再补充一个技巧。如果你希望新内核只在本次启动生效生产环境灰度验证常用手法可以用grub2-reboot CentOS Linux (5.4.270-1.el7.x86_64) 7 (Core)与grub2-set-default不同的是grub2-reboot只修改下一次启动的引导项重启完又恢复默认。这种设计适合那种“我还没想好要不要长期切换先试一把”的场景。3.4 重启并验证新内核配置都做完后就是激动人心的重启环节了。执行reboot之前我习惯再检查一遍grub2-editenv list ls -l /boot/vmlinuz-5.4.270-1.el7.x86_64 ls -l /boot/initramfs-5.4.270-1.el7.x86_64.img确认 vmlinuz 和 initramfs 文件都在、默认引导项已经指向新内核才敢动手重启。重启后第一件事就是确认内核版本uname -r如果输出的是5.4.270-1.el7.x86_64恭喜你主流程已经走通了。但还没完接着还要检查系统关键服务状态systemctl status network systemctl status sshd df -h mount | grep -E ext4|xfs之所以要检查这些是因为新内核可能会改变设备命名规则从 eth0 变成 eno1、ens33 这种导致网络配置失效。如果发现 SSH 连不上多半就是这个问题。解决办法是修改/etc/sysconfig/network-scripts/ifcfg-*配置文件中的 DEVICE 名称或者把网卡配置里的HWADDR和实际 MAC 地址对齐。不过按照我实测的经验CentOS 7 的 systemd 网卡命名规则在 5.4 内核下基本保持稳定出现问题的概率不大但检查这件事不能省。3.5 清理旧内核非必需但推荐新内核确认稳定运行一周后旧内核就可以考虑清理了。直接卸载旧内核 RPM 包之前先看一遍当前系统里有哪些内核包rpm -qa | grep kernel卸载旧内核的命令是yum remove kernel-3.10.0-1160.el7.x86_64注意千万不要用rpm -e删除正在使用的内核包。在 CentOS 7 上yum remove kernel会自动识别当前正在运行的内核并拒绝删除它比 rpm 直接操作安全得多。清完旧包再执行一次grub2-mkconfig -o /boot/grub2/grub.cfg更新引导菜单就大功告成了。4. 常见问题与排查技巧实录4.1 重启后默认还是旧内核这是最多人遇到的问题。辛苦装完新内核重启后uname -r一看还是老的 3.10.0-1160。排查步骤很简单先执行grub2-editenv list看saved_entry到底是什么。如果发现它还是指向旧内核说明你前面设置的默认项没生效重新执行grub2-set-default并确认即可。如果saved_entry已经指向新内核但启动的还是旧内核那就要检查 grub.cfg 文件本身。执行grub2-mkconfig -o /boot/grub2/grub.cfg重新生成配置后再试一次。有时候之前系统做过双系统引导或者手工改过 grub.cfg会导致默认菜单项错乱重新生成通常都能解决。还有一种可能性你的机器是 UEFI 引导模式。这种模式下 grub 配置文件路径不是/boot/grub2/grub.cfg而是/boot/efi/EFI/centos/grub.cfg。在 UEFI 环境下执行grub2-mkconfig虽然也会提示成功但实际生效的配置是 EFI 分区里那份两条路径别搞混了。查引导模式很简单执行ls /sys/firmware/efi或查看磁盘分区表即可。4.2 boot 分区空间不足这个坑我在 2.2 里提过这里展开讲。CentOS 7 默认安装时/boot分区通常只分 200MB 或 500MB。如果之前系统更新频繁旧内核一直没清理boot 分区很容易就满了。新内核 RPM 安装时vmlinuz 文件写到一半就报No space left on device。解决办法是分两步走。第一步先清理不再用的旧内核文件。第二步检查/boot下是否有残留的无用文件比如System.map-*如果确定没用直接删。有一个常用命令是查看/boot下各文件大小ls -lh /boot/看看哪些内核文件占用大头确认不是当前运行内核后就能清理。如果空间极度紧张也可以手工删除旧版本 vmlinuz 和 initramfs 文件但记得要同步修改 grub.cfg不然到时候引导菜单里还留着指向已删除文件的条目。4.3 新内核启动后网卡消失了这是所有升级内核操作里最让人崩溃的问题没有之一。新内核起来了SSH 却连不上机房值班同学打电话说屏幕上有报错。多数情况是网卡驱动没有加载成功或者接口名发生了变化。如果新内核下ip link输出里找不到原来的 eth0不用慌。先看看是不是驱动模块没加载lsmod | grep 原来的网卡驱动模块名如果确认模块缺失先临时加载一下试试modprobe 模块名如果能加载成功说明内核里其实带了驱动只是没有自动加载。这种情况需要修改网卡配置文件让系统在启动时主动加载该模块。在/etc/modules-load.d/下新建一个配置文件填入模块名即可。如果modprobe都提示找不到模块那问题就比较棘手了。说明这个网卡芯片太新或者太偏门新内核里没有对应驱动。这种场景下有两个出路一是换一张主流芯片的网卡二是在旧内核下把驱动编成模块然后在新内核启动时手动 insmod。第二个方案操作门槛高生产环境一般不建议在现场折腾直接回滚旧内核更现实。我自己的习惯是升级前先看lsmod输出把网卡驱动模块名记下来再在新内核包里用modinfo或find /lib/modules/确认一下模块文件是否存在。这个预检动作只需要一分钟能省掉后面一晚上的折腾。4.4 文件系统挂载异常、服务启动失败等兼容性问题升级内核后偶尔会出现某个服务起不来的现象。比如数据库实例启动时报错、容器引擎 disables 了、NFS 挂载不上。这种问题大多和新内核的某些行为变化有关。我的经验是遇到这类问题先不要急着回滚。先查/var/log/messages和/var/log/dmesg看看有没有明显的模块加载报错或者文件系统相关异常。如果是 NFS 这类网络文件系统的问题检查一下rpcbind和nfs-utils版本是否需要同步升级。有时候内核升级了配套的用户态工具还停留在旧版本两者之间会有协议差异导致功能异常。如果日志里根本看不出端倪那就换个思路确认一下业务服务是否依赖了某个内核模块。比如 Docker 依赖overlay和nf_nat等模块如果新内核里这些模块默认没加载Docker 可能就起不来。手动modprobe加载后能解决就解决不能解决还是走回滚路线别硬撑。4.5 问题排查速查表整理一个我平时常用的排查速查表方便大家遇到问题时直接对照现象可能原因处理方式重启后版本未变化默认引导项未设置成功用 grub2-set-default 重新设置并确认 saved_entry启动时报找不到 vmlinuzgrub.cfg 与文件不匹配执行 grub2-mkconfig 重新生成配置启动后 SSH 连不上网卡驱动未加载或接口名变化检查 lsmod修 ifcfg 配置必要时 modprobe 或回滚安装时提示缺依赖离线依赖不全在联网机器上用 downloadonly 拉全依赖再传过来boot 分区空间不足旧内核文件过多清理 /boot 旧文件卸载旧内核 RPM新内核下服务起不来内核模块未加载或工具版本过旧检查模块与日志同步升级配套工具5. 实操心得与后续扩展5.1 亲测有效的几个小技巧这套离线手动升级流程我前后在十几台机器上验证过有几个细节是常规教程里不会讲的但对成功率影响很大。第一个技巧是在联网机器下载 RPM 包时建议选和 target 机器完全相同的 CentOS 小版本。什么意思比如目标机器是 CentOS 7.9.2009那下载环境也尽量用 7.9.2009不要用 7.6 或者 7.8。内核 RPM 包一般对 CentOS 小版本不敏感但依赖包可能会有差异能统一就统一。第二个技巧是装完内核后先别急着删旧内核。至少保留新旧两个内核并行跑一到两周让业务完整跑过一轮发版或者批处理之后再考虑清理。很多人怕麻烦装完就删了旧内核结果新内核某个隐藏问题两周后才暴露那时候想回滚都没得回。第三个技巧是把/var/log/dmesg里关于新内核的警告信息存一份。新内核首次启动时可能会有各种 deprecation 警告或者模块缺失提示。这些信息当时看着不起眼但等后面出现问题排查时它们往往就是定位问题的关键线索。5.2 升级完成之后还能做什么内核升级只是第一步。升到 5.4 之后你会发现很多以前用起来别扭的东西变得顺手了。比如我用新版内核重新编译了一遍性能监控工具配合新版内核的ebpf特性能看到以前看不到的细粒度系统指标。再比如容器场景下新内核的 overlayfs 和网络命名空间性能好了不少以前并发稍高就出现延迟的情况改善明显。还有一点值得说的是如果内网环境有多台同配置机器建议在一台验证机上完整跑通这套流程后再对剩余机器批量操作。批量操作时可以直接用 Ansibl、SaltStack 这类自动化工具把 RPM 包分发下去但 grub 引导项的修改千万要单独确认不能靠命令行一把梭。自动化的确能省很多事但 grub 这层出错的代价太大手动确认一次更稳妥。5.3 最终的一点心里话我接触过不少刚入行的运维朋友面对离线环境升级内核这件事第一反应是“这不就是个 yum install 的事吗怎么这么麻烦”。但真正扑进这个坑里的人才知道内网环境做任何变更核心难点从来不是技术本身而是如何在资源受限、信息不全的情况下保证变更可控、可回退、可追溯。离线手动升级内核本质上练的就是这个基本功。如果你正准备在自己负责的服务器上做类似操作我的建议是找一个不重要的测试机先完整跑一遍把每一步的输出都截图或记录下来形成一套自己的操作手册。有了这套手册真到了生产环境操作时你会从容得多。内核升级这件事本身不难难的是沉着冷静地应对意外情况。多准备一分就少一分狼狈。希望这份实操记录能帮到你。如果你在实际操作中遇到了本文没覆盖到的问题也欢迎对照上面的排查思路自己顺一遍很多时候答案就在日志里。最后再分享一个小经验每次升级前把你找 RPM 包的具体来源、传包的方式、安装的版本号、grub 的修改前后状态全部记录下来哪怕只是写在记事本里。这一份记录在以后复盘或者出问题时比任何技术文档都管用。我自己吃过不少次“当初好像就是这么操作的但细节想不起来了”的亏现在养成了每台机器一份变更记录的习惯确实省心很多。