☰
内网离线升级Linux内核完整指南:依赖处理、引导配置与安全回滚
2026/10/14 23:29:11 网站建设 项目流程

1. 为什么内网环境下的内核升级值得单独拿出来讲

很多人第一次遇到"离线升级内核"这个需求,都是在一种比较被动的场景里:机房里的服务器跑着某个关键业务,突然发现当前内核版本对某款新上的硬件支持不全,或者某个安全补丁必须通过升级内核才能覆盖,而这台机器又完全没有外网出口。这时候你手里只有一台能上网的笔记本、一个U盘,以及一台等着你把它救活的生产机。

这件事听起来简单——不就是下载几个包然后装上去吗?但真正动过手的人都知道,离线升级内核的坑远比在线升级密集得多。在线环境下yum update kernel或者apt install linux-image-xxx一条命令就能搞定的事,一旦断了网,依赖关系、包版本匹配、引导配置、固件缺失、回滚预案,每一个环节都可能让你在机房里耗到凌晨。

我写这篇东西的目的很明确:把"离线升级内核"这件事从"碰运气"变成"有章法"。不管你是运维、嵌入式开发,还是单纯维护几台内网服务器的技术负责人,只要你的机器不能直接连外网,这套思路都能直接拿去用。文章会覆盖从版本确认、依赖梳理、包下载、介质制作,到实际安装、引导修复、失败回滚的完整链路,并且重点讲清楚每一步"为什么这么做",而不是只丢给你一堆命令。

需要先说明一点:内核升级本身是一个对系统影响极大的操作,它直接关系到机器能不能正常启动。所以下面所有内容,我都会把"可回滚"作为一条贯穿始终的底线来对待。你可以在心里先记住一句话——升级内核的第一原则不是升成功,而是升失败之后还能退回来。

2. 动手之前必须锁定的几个关键信息

2.1 先搞清楚当前系统到底跑的是什么

离线升级最忌讳的就是"我以为"。你以为机器是 CentOS 7,结果人家是 Rocky Linux;你以为内核是 3.10,结果uname -r出来一个带自定义后缀的版本。所以第一步永远是确认现状,而且要确认得足够细。

uname -r cat /etc/os-release uname -m

这三条命令分别告诉你:当前运行的内核版本、发行版信息、CPU 架构。为什么要三个都看?因为内核包是强绑定发行版和架构的。同样是 5.4 内核,给 Ubuntu 用的.deb和给 CentOS 用的.rpm完全不是一回事;同样是 x86_64,某些发行版还会细分x86_64和amd64的命名差异。

除了这三条,我建议再补两条:

ls /boot rpm -qa | grep kernel # RedHat 系 dpkg -l | grep linux-image # Debian 系

ls /boot能让你看到当前/boot分区里已经有哪些内核和 initramfs 文件,这直接决定了你后面有没有空间放新内核。我见过/boot只给了 200MB 结果新内核装到一半空间不足的案例,最后引导直接损坏。rpm -qa或dpkg -l则是为了搞清楚当前装了哪些内核相关的包,避免升级后出现新旧包混装的混乱局面。

2.2 目标内核版本怎么选,不是越新越好

确定要升到哪个版本,是离线升级里最需要克制的一步。在线环境下你可以随便试,反正大不了重装;离线环境下你每换一个版本,就意味着重新走一遍下载和介质制作流程。

我的经验是遵循"够用且稳定"原则,具体分三种情况:

  • 为了硬件兼容:比如新上的网卡、RAID 卡在旧内核里识别不了,那就去查这款硬件的驱动是从哪个内核版本开始合入主线的,选那个版本或者稍高一点的稳定版即可,没必要追最新。
  • 为了安全补丁:这种情况优先选发行版官方仓库里为你的系统版本提供的最新内核,因为发行版会把安全补丁回移到他们维护的内核版本上,兼容性最有保障。
  • 为了新特性:比如需要某个 cgroup v2 的新行为或者某个文件系统的新功能,那就得看该特性具体在哪个版本引入,同样选引入版本附近的稳定版。

这里有个很多人忽略的点:发行版官方维护的内核,和 kernel.org 上的主线内核,是两条不同的路线。前者经过发行版大量回移和测试,和你的系统其他组件(比如 glibc、systemd、驱动模块)配合得最好;后者虽然版本号新,但可能和你的用户态工具链存在兼容问题。离线环境下我强烈建议优先走发行版官方路线,除非你有明确的理由必须上主线。

2.3 依赖关系是离线升级最大的拦路虎

在线安装时,包管理器会自动帮你解决依赖。离线时,这个"自动"没了,你得自己把依赖树理清楚。内核包的依赖通常包括:

  • kernel-firmware或linux-firmware:各种硬件的固件,缺了它某些设备可能起不来。
  • dracut或initramfs-tools:用来生成 initramfs 的工具,版本太旧可能生成不出新内核需要的镜像。
  • grub2或grub:引导程序,新内核可能需要更新版的 grub 才能正确引导。
  • kmod、binutils、gcc等:某些情况下编译内核模块需要。

在 RedHat 系上,你可以用yum deplist kernel查看依赖(需要能连仓库的机器);在 Debian 系上,apt-cache depends linux-image-xxx是同样的作用。关键技巧是:在一台和离线机系统版本完全一致的联网机器上做依赖分析,把依赖树完整导出来,然后一次性把所有需要的包都下载齐。千万不要在离线机上装一个下一个,那样一旦中途缺包,系统可能处于半升级状态,非常危险。

3. 在一台联网机器上把包准备齐全

3.1 找一台"系统版本完全一致"的联网机

这一步是整个离线升级成败的关键。所谓"完全一致",指的是发行版、大版本号、架构三者都相同。比如离线机是 CentOS 7.9 x86_64,那联网机最好也是 CentOS 7.9 x86_64。小版本号(7.9 里的 .9)如果能一致更好,不一致通常问题不大,但大版本必须一致。

为什么这么强调?因为不同大版本之间的包依赖和库版本差异很大,你在 CentOS 8 上下载的包,拿到 CentOS 7 上很可能因为 glibc 版本不匹配而装不上。我吃过这个亏:当时图省事用了一台 CentOS 8 的机器下载,结果一堆包依赖libc.so.6(GLIBC_2.28),而目标机只有 GLIBC_2.17,全部报错。

如果实在找不到完全一致的机器,退而求其次的办法是用容器。比如用docker run -it centos:7.9.2009 bash起一个和目标机同版本的容器,在容器里做下载。这个方法的优点是环境干净、可复现,缺点是要注意容器里的仓库配置和真实机器可能略有差异。

3.2 用 downloadonly 把依赖树一次性拉全

RedHat 系最省事的做法是用yum的downloadonly插件:

yum install -y yum-plugin-downloadonly yum install --downloadonly --downloaddir=/tmp/kernel-pkgs kernel kernel-devel kernel-headers

如果你还想把依赖也一起下下来,可以配合--resolve(新版 dnf 支持):

dnf download --resolve --alldeps --destdir=/tmp/kernel-pkgs kernel

Debian 系则用apt-get的-d参数配合--print-uris:

apt-get install -d --reinstall linux-image-5.4.0-xx-generic # 包会下载到 /var/cache/apt/archives/

或者更彻底一点,用apt-get download配合apt-rdepends把整棵依赖树都拉下来:

apt-rdepends linux-image-5.4.0-xx-generic | grep -v "^ " | sort -u > deps.txt

这里有个实操心得:下载完之后一定要核对包的数量和完整性。我习惯用ls -l看文件大小,再用md5sum或sha256sum生成校验清单。因为介质在拷贝过程中损坏是很常见的事,尤其是用 U 盘反复插拔的时候。到了离线机上先校验一遍,能省掉很多"装到一半报错"的麻烦。

3.3 别忘了固件和引导相关的包

很多人下载内核时只盯着kernel这一个包,结果装完发现网卡不工作、或者引导菜单里根本没有新内核选项。原因就是漏了固件包和引导工具包。

对于 RedHat 系,建议至少下这几个:

  • kernel:内核本体
  • kernel-devel、kernel-headers:如果你后续要编译驱动
  • linux-firmware:固件
  • grub2、grub2-tools:引导相关
  • dracut:生成 initramfs

Debian 系对应的是:

  • linux-image-xxx:内核镜像
  • linux-headers-xxx:头文件
  • linux-firmware:固件
  • initramfs-tools:initramfs 工具
  • grub-common、grub-pc或grub-efi:引导

把这些包和内核包放在同一个目录里,后面安装时一起处理,能避免很多"装完起不来"的尴尬。

4. 制作离线介质与传输过程中的细节

4.1 介质选择:U盘、移动硬盘还是内网共享

介质选择看起来是小事,但实际影响很大。我的排序是:内网共享 > 移动硬盘 > U盘。

如果离线机和某台联网机在同一个内网里(只是离线机自己不能出外网),那最省事的方式是在联网机上起一个临时的 HTTP 服务或者 SMB 共享,离线机直接通过内网拉取。这样既避免了物理介质拷贝的损坏风险,速度也快得多。

# 联网机上,在包目录里起一个临时 HTTP 服务 python3 -m http.server 8000 # 离线机上直接下载 wget http://联网机IP:8000/kernel-pkgs.tar.gz

如果必须用物理介质,移动硬盘比 U 盘可靠,因为 U 盘的文件系统(尤其是 FAT32)对单个大文件和权限位支持不好。如果非要用 U 盘,建议格式化成 ext4 或者 exFAT,并且打包成 tar 再拷贝,而不是直接拷散文件。打包的好处是保留了权限和目录结构,也方便校验。

tar czvf kernel-pkgs.tar.gz /tmp/kernel-pkgs/ sha256sum kernel-pkgs.tar.gz > kernel-pkgs.tar.gz.sha256

把.sha256文件也一起拷过去,到离线机上先sha256sum -c校验,确认无误再解压。这一步花不了两分钟,但能帮你排除掉介质损坏这个最常见的故障源。

4.2 挂载和本地仓库的搭建

包传到离线机之后,不要急着直接rpm -ivh或dpkg -i。更稳妥的做法是在离线机上搭一个本地仓库,然后用包管理器来装。这样做的好处是包管理器会自动处理依赖顺序,比手动一个个装可靠得多。

RedHat 系搭本地仓库:

mkdir -p /mnt/localrepo tar xzvf kernel-pkgs.tar.gz -C /mnt/localrepo createrepo /mnt/localrepo cat > /etc/yum.repos.d/local.repo <<EOF [local] name=Local Repo baseurl=file:///mnt/localrepo enabled=1 gpgcheck=0 EOF

Debian 系搭本地仓库稍微麻烦一点,可以用dpkg-scanpackages:

cd /mnt/localrepo dpkg-scanpackages . /dev/null | gzip -9c > Packages.gz echo "deb [trusted=yes] file:///mnt/localrepo ./" > /etc/apt/sources.list.d/local.list apt-get update

搭好本地仓库之后,安装就变成了yum install kernel或apt-get install linux-image-xxx,依赖问题交给包管理器,你只需要盯着输出看有没有报错就行。

注意:搭本地仓库时gpgcheck=0或trusted=yes是为了跳过签名校验,因为离线环境下你没法联网获取 GPG key。这在受控的内网环境里是可以接受的,但你要清楚这意味着放弃了包的来源验证,所以包的来源必须是可信的。

5. 实际安装与引导配置的完整链路

5.1 安装顺序和 initramfs 的生成

包管理器安装内核时,通常会自动触发 initramfs 的生成。但离线环境下,如果dracut或initramfs-tools版本不匹配,这一步可能静默失败,导致新内核没有对应的 initramfs 文件,引导时直接 panic。

所以安装完之后,一定要手动确认 initramfs 是否生成成功:

ls -l /boot/initramfs-$(uname -r 对应的新版本).img # RedHat 系 ls -l /boot/initrd.img-新版本 # Debian 系

如果没有,手动生成:

dracut -f /boot/initramfs-新版本.img 新版本 # RedHat 系 update-initramfs -c -k 新版本 # Debian 系

这里有个细节:dracut生成 initramfs 时会读取/etc/dracut.conf里的配置,如果你的配置里指定了某些模块或者排除了某些模块,新内核的 initramfs 可能和旧的不一样。升级前最好把/etc/dracut.conf和/etc/dracut.conf.d/下的内容看一眼,确认没有针对特定内核版本的硬编码。

5.2 GRUB 配置更新与默认启动项

内核装好、initramfs 生成好之后,接下来是让引导程序认识新内核。RedHat 系通常会自动更新 grub 配置,但离线环境下我建议手动确认一遍:

grub2-mkconfig -o /boot/grub2/grub.cfg # BIOS 引导 grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg # UEFI 引导,路径按发行版调整

Debian 系则是:

update-grub

更新完之后,用grep确认新内核的菜单项确实写进去了:

grep -i "新内核版本" /boot/grub2/grub.cfg

关键的一步是设置默认启动项。默认情况下,grub 会启动菜单里的第一个项,而新装的内核通常排在前面。但为了保险,我建议显式指定默认启动的内核,并且保留旧内核作为备选:

# 查看所有菜单项 grub2-editenv list awk -F\' '$1=="menuentry " {print $2}' /boot/grub2/grub.cfg # 设置默认启动项(按菜单项名称或索引) grub2-set-default "新内核菜单项名称"

Debian 系可以用grub-set-default配合菜单索引,或者直接编辑/etc/default/grub里的GRUB_DEFAULT。

提示:设置默认启动项时,务必确认旧内核的菜单项还在。这样万一新内核起不来,你还能在 grub 菜单里手动选旧内核进系统。这是离线升级最重要的安全网。

5.3 重启前的最后检查清单

重启是"开弓没有回头箭"的操作,重启前我习惯过一遍这个清单:

检查项命令预期结果
新内核包已安装rpm -qa | grep kernel或dpkg -l | grep linux-image能看到新版本
initramfs 已生成ls /boot/initramfs-*或ls /boot/initrd.img-*有新版本对应文件
grub 配置已更新grep 新版本 /boot/grub2/grub.cfg能找到菜单项
旧内核仍在ls /boot/vmlinuz-*旧版本文件还在
/boot 空间充足df -h /boot使用率低于 80%
默认启动项正确grub2-editenv list指向预期内核

这个清单看起来啰嗦,但每一条都对应着我或者身边人踩过的真实坑。尤其是/boot空间和旧内核保留这两条,前者导致过引导损坏,后者导致过"新内核起不来又退不回去"的绝境。

6. 重启失败时的排查与回滚

6.1 新内核起不来,先别慌

新内核启动失败的表现有很多种:卡在 grub、卡在 initramfs、kernel panic、或者起来了但某个硬件不工作。不同的表现对应不同的排查方向。

如果是卡在 grub 阶段,通常是 grub 配置问题。可以在 grub 菜单里按e编辑启动项,手动指定旧内核的vmlinuz和initrd路径,先进系统再说。

如果是卡在 initramfs 阶段,多半是 initramfs 里缺了关键模块(比如根文件系统所在的存储驱动)。这种情况可以在 grub 编辑界面给内核命令行加上rd.debug或break=premount,进入调试模式看具体卡在哪。

如果是kernel panic,注意看 panic 信息里的最后几行,通常会提示是哪个模块或者哪个设备出了问题。常见的是根设备找不到(VFS: Cannot open root device),这往往和 initramfs 里缺存储驱动有关。

6.2 用旧内核回滚的标准流程

只要旧内核还在,回滚其实很简单。在 grub 菜单里选中旧内核的菜单项启动,进系统之后把默认启动项改回旧内核:

grub2-set-default "旧内核菜单项名称" grub2-mkconfig -o /boot/grub2/grub.cfg

然后可以选择把新内核的包卸载掉,或者先留着排查问题。我个人的习惯是先留着,因为卸载内核包会触发 grub 配置更新,万一更新过程出问题,可能把旧内核的引导项也搞乱。等确认新内核彻底不用了,再干净地卸载。

卸载新内核包:

yum remove kernel-新版本 # RedHat 系 apt-get remove linux-image-新版本 # Debian 系

卸载后同样要确认 grub 配置和 initramfs 都回到了正常状态。

6.3 如果旧内核也被删了怎么办

这是最坏的情况,但也不是完全没救。如果/boot里旧内核的vmlinuz和initrd文件还在,只是 grub 菜单项没了,可以手动在 grub 命令行里引导:

grub> linux /boot/vmlinuz-旧版本 root=/dev/sda1 ro grub> initrd /boot/initramfs-旧版本.img grub> boot

如果连文件都没了,那就只能通过救援模式(rescue mode)或者 LiveCD 启动,挂载原系统分区,重新安装旧内核包。这也是为什么我一直强调升级前备份/boot目录:

tar czvf /root/boot-backup-$(date +%Y%m%d).tar.gz /boot

这个备份不大,但关键时刻能救命。有了它,即使 grub 配置全乱,你也能从 LiveCD 里把/boot恢复回去。

7. 几个容易被忽略但很致命的细节

7.1 内核模块和第三方驱动

如果你的系统里装了第三方内核模块(比如某些 RAID 卡驱动、虚拟化增强驱动、或者自己编译的模块),升级内核后这些模块需要针对新内核重新编译或重新安装。离线环境下,这意味着你不仅要下载内核包,还要下载对应的驱动包或者准备好编译环境。

判断有没有第三方模块,可以看:

ls /lib/modules/$(uname -r)/extra/ ls /lib/modules/$(uname -r)/updates/

这两个目录里如果有东西,基本就是第三方模块。升级前一定要确认这些模块有没有对应新内核的版本,否则新内核起来了但硬件用不了,等于白升。

7.2 Secure Boot 对内核签名的要求

如果机器开启了 Secure Boot,未签名的内核是无法引导的。发行版官方仓库的内核通常是签过名的,但如果你用的是自己编译的内核或者第三方仓库的内核,就可能因为签名问题起不来。

检查 Secure Boot 状态:

mokutil --sb-state

如果显示SecureBoot enabled,那就要确保你装的内核是签过名的。离线环境下没法临时关闭 Secure Boot 再重开(有些机器关了之后需要重新配置),所以升级前就要把这个因素考虑进去。

7.3 时间同步和证书问题

这个坑比较隐蔽。有些内核包在安装时会校验签名或者时间戳,如果离线机的系统时间偏差太大,校验可能失败。升级前顺手确认一下时间:

date timedatectl status

如果时间偏差大,先手动校准。另外,如果本地仓库用了 HTTPS,还要注意证书问题,离线环境下建议直接用file://协议,省去证书的麻烦。

8. 我个人的操作习惯和几点体会

做了这么多次离线内核升级,我慢慢形成了一套自己的固定动作。每次动手前,我会先在测试机上完整走一遍流程,确认没问题再上生产。测试机不需要和生产机配置完全一样,但系统版本和架构必须一致。这一步多花一两个小时,能避免生产环境出问题时的手忙脚乱。

另一个习惯是永远保留至少一个可用的旧内核。哪怕新内核已经稳定运行了一两个月,我也不会急着删旧内核。磁盘空间不够就扩/boot,反正现在硬盘便宜,为这点空间冒引导失败的风险不值得。

还有一点是关于文档。每次离线升级,我都会把这次用的包版本、下载来源、安装命令、遇到的问题和解决办法记下来。下次再遇到类似需求,直接翻记录就行,不用重新踩一遍坑。内网环境下的操作往往缺乏在线文档支持,自己积累的这份记录就是最宝贵的资料。

最后说个心态上的事。离线升级内核这件事,技术难度其实不算特别高,真正考验人的是细心和耐心。在线环境下你可以快速试错,离线环境下每一次操作都要想清楚后果。把准备工作做足,把回滚路径留好,剩下的就是按部就班执行。急不得,也慌不得。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询