Ubuntu 22.04 内核锁定与安全回退:从原理到实战全指南

在 Linux 服务器运维中,内核(Kernel)升级通常由自动更新(unattended-upgrades)静默触发。这在稳定性优先的生产环境里是隐患:新内核可能与阵列卡驱动、DKMS 模块或特定应用不兼容,导致服务异常甚至无法启动。更棘手的是,自动更新在拉取新内核的同时会清理旧内核——一旦出问题,想回退却发现旧内核已被删掉。

apt-mark holdGRUB 内核选择机制是 Ubuntu 内置的内核版本控制方案:前者冻结内核包不被升级或清理,后者在升级发生后仍能回退到历史版本。但真正的难点不在”怎么锁”,而在”怎么安全地切回去”——尤其当根文件系统建在 LVM/SAS/RAID 卡上时,一次盲目的 reboot 就可能让服务器卡死在 initramfs 里再也起不来。

内核版本控制的核心价值:

  • 锁定可靠apt-mark hold 精确冻结指定版本,不受 apt upgrade 与自动更新影响。
  • 防清理:对目标内核的全部包打 hold,可阻止 unattended-upgrades 的旧内核自动回收。
  • 可回退:GRUB 保留多版本内核条目,配合 grub-reboot 可零风险试引导。
  • 可自救:即使新内核无法启动,可见菜单 + 带外管理(BMC/IPMI)仍能挽回。

本文将从内核管理机制、包状态识别、锁定操作、安全回退流程、引导故障原理及排错等方面,帮助你完整掌握内核版本控制的全流程,并重点讲透”切内核为什么会起不来、如何提前预检、失败后如何回落”这一实战核心。


目录


1. 引言:内核自动升级与清理的双重风险

Ubuntu 22.04 LTS 初始搭载内核 5.15.0-25-generic(可用 uname -r 确认)。随着 SRU(Stable Release Update)与 HWE(Hardware Enablement,硬件支持扩展)栈推进,系统会持续引入更高版本内核(如 5.15.0-185)。

对服务器而言,这带来两层风险:

  1. 升级风险unattended-upgrades 静默拉取新内核,新内核可能与硬件/驱动不兼容。
  2. 清理风险:自动更新在装新内核后会执行”旧内核回收”,默认只保留”当前运行 + 最新”两个内核,其余旧内核(包括你精心保留的出厂 5.15.0-25)会被 purge

真实案例:一台出厂为 5.15.0-25 的服务器,因某次执行 apt install linux-generic 装入元包,此后被自动更新逐步追到 5.15.0-185,而出厂的 -25 内核在某次 unattended-upgrade 的旧内核清理中被删除。运维人员手动装回 -25 后直接 reboot,结果卡在 initramfs 起不来——因为根盘在 Adaptec SAS 阵列卡上,而装回的 -25 缺少 smartpqi 驱动模块(属于 linux-modules-extra 包)。

这个案例浓缩了本文要解决的全部问题:如何锁定、如何防清理、如何在切换前预检驱动、如何零风险验证、失败后如何自救


2. Ubuntu 内核管理机制

为什么要理解机制? 仅锁具体版本包是不够的——元包会绕过限制拉新内核,自动清理会删掉旧内核,缺驱动的 initrd 会让引导失败。理解这四条机制,才能避免”锁了还被升、装了还被删、切了起不来”。

2.1 内核包组成(4 类组件、5 个 DEB 包)

Ubuntu 通用内核可按 image、基础 modules、extra modules、headers 四类组件理解;其中 headers 拆成通用部分和 flavor 专用部分,因此作为完整回退环境安装后实际有五个版本化 DEB 包:

包名内容缺失后果
linux-image-<ver>-generic内核镜像 vmlinuz无法引导该版本
linux-modules-<ver>-generic基础模块(常用驱动,如 drivers/net基本功能缺失
linux-modules-extra-<ver>-generic额外模块(冷门网卡/存储/文件系统驱动)特定硬件驱动丢失,网络/磁盘可能不可用
linux-headers-<ver>与 flavor 无关的通用头文件headers 不完整,DKMS 无法正常编译
linux-headers-<ver>-genericgeneric flavor 专用头文件第三方驱动无法针对该内核编译

为什么有的资料只写 4 个? linux-headers-<ver>-generic 硬依赖 linux-headers-<ver>,通过 APT 安装前者时会自动拉取后者。因此安装命令只显式写四个包通常也能得到完整的五个包,但将其描述为“四个 DEB 包”并不准确。为便于离线下载、逐包核验和锁定,本文后续命令显式列出五个包。

是否必须五个都装? 仅从启动角度看并非一概如此:image 和基础 modules 是核心;modules-extra 是否必需取决于根盘控制器、网卡等硬件;两个 headers 不参与正常启动,主要供 DKMS/外部模块编译使用。但要制作可适配硬件、可重建 DKMS 驱动的可靠回退内核,建议五个全部安装并核验。

提示linux-modules-extra 是最易被忽略、也是最致命的一个。阵列卡驱动 smartpqi(Adaptec)、megaraid_sas(LSI/Broadcom)、mpt3sas 等都在这个包里。若根盘挂在这类控制器上,缺 extra 包会导致引导时找不到根盘。补装 extra 包会自动触发 update-initramfs,把额外驱动打进 initrd(initrd 体积会明显增大,如从 55MB 增至 112MB)。

查看某版本已装了哪些包:

dpkg -l | grep -E "^(ii|hi)" | grep 5.15.0-25   # 查看 -25 版本已装的内核包

2.2 dpkg 包状态码识别

dpkg -l 输出第一列是两字母状态码,是排障的关键——能一眼看出包是真装好、只剩残留、还是被锁定:

含义说明
iiinstalled正常已安装
hihold + installed已安装且被 apt-mark hold 锁定(正常锁定态)
hchold + config-files被 hold 但包体已删、仅剩配置(异常,需补装)
rcremoved + config-files已卸载、仅剩配置残留(无害,可忽略)
ununknown / not-installed未安装

提示hc 是隐蔽的坑。若某包曾被自动清理删除、之后又被打了 hold,就会停在 hc 态,apt policy 显示 Installed: (none)。此时该内核实际缺失对应模块,必须重新安装才能补回。前述 SAS 案例中,linux-modules-extra-5.15.0-25-generic 正是 hc 态,导致 initrd 缺 smartpqi

2.3 元包机制

元包(Meta-Package)本身不含任何文件,只是一个指向当前仓库最新内核的依赖指针

dpkg -l | grep -E 'linux-image-generic|linux-headers-generic|linux-generic-hwe'
  • linux-image-generic / linux-headers-generic:标准内核元包。
  • linux-generic-hwe-22.04:HWE 栈元包,安装了 HWE 内核才存在,独立于标准元包,需单独锁定

执行 apt upgrade 或自动更新时,元包会解析到仓库最新内核并拉入系统。这就是”锁了具体版本包但内核仍被升级”的根因——必须同时锁住元包。案例中那次 apt install linux-generic 正是把元包装入,从此开启了内核追新之路。

2.4 unattended-upgrades 的升级与清理行为

unattended-upgrades(无人值守更新)对内核有两个独立行为,容易混淆:

  1. 升级:通过元包拉取并安装新内核。
  2. 清理(autoremove):安装新内核后,自动移除”非当前运行、非最新”的旧内核,释放 /boot 空间。

从日志可溯源这两个行为:

grep -E "Commandline|Remove:" /var/log/apt/history.log* | grep -B1 "linux-image"

若看到 Commandline: /usr/bin/unattended-upgrade 后跟 Remove: linux-image-...,即自动更新在清理旧内核,并非人为误删

关键结论apt-mark hold 既能阻止升级,也能阻止清理——被 hold 的包不会被 autoremove 移除。因此要让某个旧内核长期留存,应对它的 5 个版本化包全部打 hold,而不只是锁 image。

2.5 GRUB 多内核启动与 initramfs

GRUB 在每次安装/移除内核时,通过 update-grub(实为 grub-mkconfig)扫描 /boot,为每个内核生成菜单项。旧版本条目保留,是回退的基础。

ls /boot/vmlinuz-*    # 所有可引导内核镜像

但”菜单里有条目”不等于”能起来”。每个内核配套一个 initramfs/boot/initrd.img-<ver>)——一个精简的临时根文件系统,负责在真正的根分区挂载前加载必要驱动。引导链是:

GRUB 加载 vmlinuz + initrd → initrd 里的驱动识别存储控制器
→ 找到并挂载根文件系统 → 切换到真正的根 → 启动 systemd

如果根盘在 SAS/RAID 卡或 LVM 上,而 initrd 里没有对应的存储驱动或 LVM 引导脚本,这条链就会断在”找不到根盘”,卡死在 initramfs shell。这正是切内核起不来的技术根源,也是第 4.1 节必须预检的原因。


3. 锁定 5.15.0-25 内核,防升级也防清理

本指南只针对 Ubuntu 22.04 的 5.15.0-25-generic。锁定的完整闭环是:安装 5 件套 → 对 5 件套全部执行 hold → 锁定内核元包 → 验证 5 件套全部为 hi。不能只锁 image,也不能对尚未安装或只剩配置残留的包直接执行 hold。

3.1 查看当前状态

uname -r                                          # 当前运行内核
ls /boot/vmlinuz-*                                # /boot 下可引导内核
dpkg -l | grep -E '^(ii|hi|hc|rc)' | grep linux-  # 已装/hold/残留的内核包
apt-mark showhold | grep linux                    # 当前被 hold 的包
apt policy linux-image-5.15.0-25-generic          # 某版本能否从源装回

3.2 安装并锁定 5 件套与元包

第一步:安装 5.15.0-25 完整 5 件套

sudo apt install \
  linux-headers-5.15.0-25 \
  linux-headers-5.15.0-25-generic \
  linux-image-5.15.0-25-generic \
  linux-modules-5.15.0-25-generic \
  linux-modules-extra-5.15.0-25-generic

即使 APT 会通过 linux-headers-5.15.0-25-generic 的依赖自动安装通用 headers,这里仍显式列出 linux-headers-5.15.0-25,确保在线安装、离线准备和后续核验使用同一份 5 件套清单。

第二步:对 5 件套全部执行 hold

sudo apt-mark hold \
  linux-headers-5.15.0-25 \
  linux-headers-5.15.0-25-generic \
  linux-image-5.15.0-25-generic \
  linux-modules-5.15.0-25-generic \
  linux-modules-extra-5.15.0-25-generic

第三步:锁定元包,阻断新内核拉取路径

sudo apt-mark hold linux-image-generic linux-headers-generic   # 标准元包
sudo apt-mark hold linux-generic-hwe-22.04 2>/dev/null         # 有 HWE 栈才需要

提示linux-generic-hwe-22.04 仅在装了 HWE 内核栈的系统存在。可先 dpkg -l | grep hwe 确认。它独立于标准元包,漏锁它会导致内核仍被升级。

第四步:逐包验证 5 件套

dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' \
  linux-headers-5.15.0-25 \
  linux-headers-5.15.0-25-generic \
  linux-image-5.15.0-25-generic \
  linux-modules-5.15.0-25-generic \
  linux-modules-extra-5.15.0-25-generic

预期 5 行第一列全部为 hi(hold + installed):

hi  linux-headers-5.15.0-25
hi  linux-headers-5.15.0-25-generic
hi  linux-image-5.15.0-25-generic
hi  linux-modules-5.15.0-25-generic
hi  linux-modules-extra-5.15.0-25-generic

缺少任何一行、显示 ii(已安装但未 hold)或 hc(已 hold 但包体已删除),都不算锁定完成。再单独检查元包:

apt-mark showhold | grep -E '^(linux-image-generic|linux-headers-generic|linux-generic-hwe-22\.04)$'

此后 apt upgrade 会跳过这些包,且旧内核清理不会删除已 hold 的 -25 内核包。

3.3 unattended-upgrades 黑名单(纵深防御)

apt-mark hold 已能阻止 unattended-upgrades 升级与清理内核,因此本节在功能上是冗余的纵深防御层——当有人误执行 apt-mark unhold 解锁后,黑名单仍能兜底。

编辑 /etc/apt/apt.conf.d/50unattended-upgrades,在 Package-Blacklist 块内加入:

Unattended-Upgrade::Package-Blacklist {
    "linux-image";
    "linux-headers";
    "linux-modules";
    "linux-generic";
};

保存后自动更新将跳过所有内核相关包。安全补丁照常安装,仅内核保持稳定。


4. 安全回退到 5.15.0-25 内核

铁律(血泪教训):在用 grub-reboot 一次性引导实测目标内核能正常起来之前,绝不固定 GRUB_DEFAULT、绝不设 GRUB_TIMEOUT=0 + GRUB_TIMEOUT_STYLE=hidden。否则目标内核若缺驱动(根盘在 SAS/RAID 卡上却少 modules-extra)会卡在 initramfs 起不来,且无菜单可选、无法自救,只能靠 BMC/救援盘。

安全顺序,不可跳步:引导前预检 → 保留可见菜单与带外通道 → grub-reboot 一次性实测 → 验证通过后才固定默认项。

4.1 引导前预检:5.15.0-25 能否找到根盘

若根文件系统在 LVM/RAID/SAS/NVMe 上,引导依赖的存储驱动必须在目标内核的 initrd 里,否则起不来。逐项确认:

V=5.15.0-25-generic
 
# ① 找到根设备,看它挂在什么控制器上,记下驱动名
lsblk -o NAME,MOUNTPOINT | grep -w /              # 定位根设备(如 /dev/mapper/ubuntu--vg-ubuntu--lv)
lspci -k | grep -iA3 'RAID\|SAS\|SCSI\|NVME'      # 看控制器的 "Kernel driver in use"(如 smartpqi)
 
# ② 确认存储驱动 + LVM 引导脚本已进目标内核 initrd
lsinitramfs /boot/initrd.img-$V | grep -E 'smartpqi|megaraid|mpt3sas|nvme'   # 存储驱动,须有输出
lsinitramfs /boot/initrd.img-$V | grep -E 'scripts/local-top/lvm2|sbin/lvm'  # 根在 LVM 时须有
 
# ③ 确认 5 件套全部为 hi(尤其 modules-extra)
dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' \
  linux-headers-5.15.0-25 \
  linux-headers-5.15.0-25-generic \
  linux-image-5.15.0-25-generic \
  linux-modules-5.15.0-25-generic \
  linux-modules-extra-5.15.0-25-generic

提示ext4dm-modmd-mod 在 initrd 里查不到是正常的——它们被编入内核 vmlinuz(builtin),而非可加载模块。可用下面命令确认它们是 builtin:

grep -E 'ext4|dm-mod|md-mod' /lib/modules/$V/modules.builtin

真正需要在 initrd 里的是 smartpqi 这类外挂存储驱动。若预检发现缺 smartpqi,按第 5.1 节场景补 modules-extra(会自动重建 initrd)后再测。

4.2 保留可见菜单与带外通道

先把菜单设成可见,这样万一目标内核起不来,重启时能在菜单里手动选回旧内核:

sudo sed -i 's/^GRUB_TIMEOUT_STYLE=.*/GRUB_TIMEOUT_STYLE=menu/' /etc/default/grub
sudo sed -i 's/^GRUB_TIMEOUT=.*/GRUB_TIMEOUT=10/' /etc/default/grub
sudo update-grub

反面教材GRUB_TIMEOUT_STYLE=hidden + GRUB_TIMEOUT=0 会让菜单完全不显示。一旦默认内核起不来,本地无从选择,只能靠带外管理或救援盘。验证阶段绝不能用这套配置。

确认带外通道可用。远程操作时,若目标内核卡死,SSH 进不去且机器不会自愈(原因见 4.4),唯一的补救手段是带外管理:

ls /dev/ipmi*        # 存在则有 BMC/IPMI(如服务器板载 BMC)

提示:仅确认 /dev/ipmi0 存在还不够,务必提前确认 BMC 的网络地址、账号密码能登进 console。这是远程切内核的最后一道生命线。

4.3 grub-reboot 一次性引导测试

grub-reboot下次且仅下次重启进入目标内核;若起不来,重启后会自动回到原默认项。这是零风险的验证方式,核心在于它不修改 GRUB_DEFAULT

前置条件自检(换台机器前先确认,缺一不可):

# ① grub.cfg 含 next_entry 处理块(grub-reboot 靠它生效)
grep -n 'next_entry' /boot/grub/grub.cfg          # 应看到 set default="${next_entry}" 等行
 
# ② grubenv 可写(grub-reboot 要把选择写进这里)
ls -l /boot/grub/grubenv                          # root 可写即可
 
# ③ grub-reboot 命令存在
which grub-reboot
 
# ④ GRUB_DEFAULT 指向已知能起的内核(回落落点)
grep '^GRUB_DEFAULT=' /etc/default/grub           # =0 指向菜单第一项;确认第一项能起

提示GRUB_DEFAULT=0(非 saved不影响 grub-reboot。它用的是优先级更高的 next_entry 机制,只覆盖下一次启动,用完即弃,无需把 GRUB_DEFAULT 改成 saved

执行测试

# 查菜单精确条目名(子菜单名>菜单项名,> 前后无空格)
grep -E "menuentry '|submenu '" /boot/grub/grub.cfg | grep -v recovery
 
# 设置仅下次生效的引导项
sudo grub-reboot "Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-25-generic"
 
# 确认已写入 grubenv
sudo grub-editenv list        # 应看到 next_entry=Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-25-generic
 
# 重启(远程务必确保 BMC 可用)
sudo reboot

重启后验证:

uname -r        # 期望目标版本,如 5.15.0-25-generic
  • 是目标内核 → 进入 4.5 固定默认项。
  • 仍是旧内核或进了救援 → 系统已自动回落,人没丢;回到 4.1 排查 initrd 缺什么。

4.4 回落机制原理与前提

必须准确理解:这个”自动回落” 不是”系统检测到内核起不来后自己重启换旧内核”,而是”一次性选择用完即弃”。看 grub.cfg 的核心逻辑:

if [ "${next_entry}" ] ; then
   set default="${next_entry}"    # 把默认临时设成目标内核
   set next_entry=                # 清空内存中的 next_entry
   save_env next_entry            # ★ 在交棒给内核前,就把 grubenv 里的 next_entry 擦除
   set boot_once=true
fi

关键在于:GRUB 在把控制权交给内核之前,就已把 next_entry 从 grubenv 擦掉了。 因此这个一次性选择在 GRUB 阶段就被”消费”,与内核之后起不起得来无关。三种结局:

  1. 目标内核正常起来uname -r 为目标版本,成功。
  2. 目标内核卡在 initramfs(找不到根盘) → 机器卡死在原地,不会自己重启。GRUB 已交棒,此刻帮不了你,必须由人 power-cycle(本地电源键或 BMC 强制重启)。
  3. power-cycle 之后next_entry 上一次已被擦除,GRUB 找不到一次性项,回落到 GRUB_DEFAULT(如 =0 的旧内核)→ 自动进旧内核。

一句话:回落保证你”不会被永久锁死在起不来的内核”,但不保证你不用动手——卡死时仍需人工重启一次。

由此得出必须满足的前提:

必备条件为什么必须
GRUB_DEFAULT 指向已验证能起的内核这是回落的落点,落点也起不来就彻底失联
BMC/IPMI 提前确认可登录卡死时机器不自愈,带外重启是唯一远程手段
菜单可见(menu + timeout>=5卡死重启后能手动干预,不必再赌
测试前 grub-editenv list 确认已写入没写进 grubenv 等于没设
验证通过前不用 grub-set-default / GRUB_DEFAULT=saved用 saved 会持久化选择,失去”一次性”保护

关于回落落点与 GRUB_DEFAULT 的准确理解:回落点就是 GRUB_DEFAULT 当前指向的那一项,所以测试前它必须停在一个已知能正常启动的内核上

  • Ubuntu 装完默认就是 GRUB_DEFAULT=0(这是出厂值),0 指菜单第一项,Ubuntu 会把第一项绑定到当前最新已安装的内核。简化理解为”保持 0 即可”在绝大多数情况成立——前提是最新内核能正常起(正常都成立;唯一例外是最新内核本身也是坏的,此时 0 才不安全)。
  • 坑 ①:测试前绝不能把 GRUB_DEFAULT 先改成目标内核(如 -25)。 否则一次性测试失败后,回落落点也是起不来的 -25,安全网直接失效。测试阶段 GRUB_DEFAULT 必须停在已验证能起的内核(通常就是默认的 0)。
  • 坑 ②:GRUB_DEFAULT 不能是 saved 数字 0 或名称字符串都不影响 grub-reboot(它用优先级更高的 next_entry 一次性覆盖);但 saved 会让落点绕到 grubenv 的 saved_entry,语义不直观、易误判。

一句话:不是”必须让 GRUB_DEFAULT=0”,而是”必须让 GRUB_DEFAULT 停在能起的内核上”。Ubuntu 默认 0 恰好指向能起的最新内核,所以保持 0 即可,只要别在测试前把它改指向目标内核、也别改成 saved

4.5 验证通过后固定默认项

只有 4.3 实测成功后才执行这步。用名称法(子菜单名>菜单项名> 前后无空格):

sudo sed -i 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-25-generic"/' /etc/default/grub
sudo update-grub

update-grub 回显应包含目标内核的 image 与 initrd:

Found linux image: /boot/vmlinuz-5.15.0-25-generic
Found initrd image: /boot/initrd.img-5.15.0-25-generic

提示:固定默认项后,建议仍保留 4.2 的可见菜单(GRUB_TIMEOUT=10)作为长期后路。确认长期稳定运行前,不要改回 hidden + 0

4.6 清理多余内核

4.6.1 先判断是否需要清理

多余内核只消耗 /boot 空间,每个版本约占 100–200 MB(vmlinuz + initrd + System.map + config)。除此之外它不影响任何运行时行为,反而是一条随时可用的回退后路。

df -h /boot   # 查看 /boot 实际用量

判断标准:

  • /boot 用量低(< 60%):不需要清理,留着作备用引导项,风险为零、收益为正。
  • /boot 接近写满:必须清理,否则下次 update-initramfs 会报 No space left on device,同样危险。
  • 无论何种情况:删完必须保留至少两个能引导的内核(当前运行的 + 一个备用)。

4.6.2 安全清理流程

首选:apt autoremove(最不容易出错)

uname -r                           # 确认当前运行内核,这个绝不能删
apt autoremove --purge --dry-run   # 预览会删哪些包,逐行确认
apt autoremove --purge             # 执行
ls /boot/vmlinuz-* | wc -l         # 必须 >= 2

autoremove 由元包的依赖关系推导删除范围,不删当前运行的内核,也不碰被 hold 的包,适合大多数情况。

删包时 dpkg 的 postrm 钩子会自动执行 update-initramfsupdate-grub无需手动补跑。实际回显类似:

/etc/kernel/postrm.d/initramfs-tools:
update-initramfs: Deleting /boot/initrd.img-5.15.0-124-generic
/etc/kernel/postrm.d/zz-update-grub:
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-5.15.0-25-generic
Found initrd image: /boot/initrd.img-5.15.0-25-generic
done

次选:手工指定(autoremove 不肯删时)

autoremove 不肯删通常是元包仍依赖该版本。此时手工 purge,只写内核自身的 5 个包

V=5.15.0-124-generic               # 替换为实际要删的版本
# 被 hold 的先解锁,未 hold 的这步无害
sudo apt-mark unhold linux-image-$V linux-modules-$V linux-modules-extra-$V \
                     linux-headers-${V%-generic} linux-headers-$V 2>/dev/null
# 只删内核包
sudo apt purge linux-image-$V linux-modules-$V linux-modules-extra-$V \
               linux-headers-${V%-generic} linux-headers-$V

删后核对:

ls /boot/*${V}* 2>/dev/null        # 应无输出
grep "$V" /boot/grub/grub.cfg      # 应无输出
ls /boot/vmlinuz-* | wc -l         # 应 >= 2
dpkg --audit                       # 应无输出

4.6.3 常见误操作速查

误操作后果
purge 时多写了 linux-libc-dev连带删掉 libc6-devbuild-essentialg++ 整条工具链
为绕开依赖而 purge linux-image-generic 元包系统失去内核更新跟踪,安全补丁不再自动安装
清到只剩一个内核该内核出问题时 GRUB 菜单无第二项可选,只能进 recovery
忘记先 unhold 就 purgeapt 报错拒绝执行,包未被删

5. 常见场景与最佳实践

5.1 常见使用场景

场景 1:新装机后立即锁定初始内核

先按 3.2 安装完整 5 件套,再执行:

sudo apt-mark hold \
  linux-headers-5.15.0-25 \
  linux-headers-5.15.0-25-generic \
  linux-image-5.15.0-25-generic \
  linux-modules-5.15.0-25-generic \
  linux-modules-extra-5.15.0-25-generic
sudo apt-mark hold linux-image-generic linux-headers-generic linux-generic-hwe-22.04

执行后必须按 3.2 的验收命令确认 5 件套全部为 hi

场景 2:内核被意外升到 185,需回退到 25

# ① 若 -25 已被自动清理,先装回全部 5 个包(含两种 headers 和 modules-extra)
sudo apt install linux-image-5.15.0-25-generic linux-modules-5.15.0-25-generic \
     linux-modules-extra-5.15.0-25-generic linux-headers-5.15.0-25 \
     linux-headers-5.15.0-25-generic
 
# ② 装回后立即锁住 5 件套,避免再次被自动清理
sudo apt-mark hold \
  linux-headers-5.15.0-25 \
  linux-headers-5.15.0-25-generic \
  linux-image-5.15.0-25-generic \
  linux-modules-5.15.0-25-generic \
  linux-modules-extra-5.15.0-25-generic
 
# ③ 按 3.2 验证五项均为 hi,再做引导前预检(见 4.1)
 
# ④ 保留可见菜单(见 4.2),确认 BMC 可用
 
# ⑤ grub-reboot 一次性实测(见 4.3),确认 uname -r 为 5.15.0-25-generic
 
# ⑥ 仅在实测通过后固定默认项
sudo sed -i 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-25-generic"/' /etc/default/grub
sudo update-grub

场景 3:补装被删的 modules-extra(hc 态修复)

被 hold 的包无法直接 apt install,需先解锁:

sudo apt-mark unhold linux-modules-extra-5.15.0-25-generic
sudo apt install linux-modules-extra-5.15.0-25-generic     # 自动重建 initrd
sudo apt-mark hold linux-modules-extra-5.15.0-25-generic
dpkg -s linux-modules-extra-5.15.0-25-generic | grep Status  # 应为 hold ok installed
lsinitramfs /boot/initrd.img-5.15.0-25-generic | grep smartpqi  # 确认驱动已进 initrd

场景 4:根在 LVM/SAS 上,切内核后卡死救援

典型于配 Adaptec/LSI 阵列卡(smartpqi/megaraid_sas)+ LVM 根盘的服务器。目标内核 initrd 缺存储驱动 → 找不到根盘 → 卡 initramfs。若已固定默认项且菜单不可见,只能走 BMC console:

# 从 BMC console 或 recovery 模式进入后,为目标内核补驱动并重建 initrd
sudo apt-mark unhold linux-modules-extra-5.15.0-25-generic
sudo apt install linux-modules-extra-5.15.0-25-generic     # 含 smartpqi,自动重建 initrd
sudo apt-mark hold linux-modules-extra-5.15.0-25-generic
sudo update-initramfs -u -k 5.15.0-25-generic              # 如仍缺,手动重建
sudo update-grub

场景 5:溯源内核为何被删

grep -E "Commandline|Remove:" /var/log/apt/history.log* | grep -B1 "linux-image"

若见 Commandline: /usr/bin/unattended-upgrade + Remove: linux-image-...,即自动更新清理旧内核,非人为。

5.2 最佳实践

1. 始终同时锁具体版本包与全部元包

仅锁 linux-image-5.15.0-25-generic 而漏锁元包是常见失误。三个元包——linux-image-genericlinux-headers-genericlinux-generic-hwe-22.04——都会绕过具体包的 hold 继续拉新内核。

2. 保留内核的全部 5 个包,特别是 modules-extra

对要长期保留的内核,务必让 image / modules / modules-extra / 两个 headers 包全部处于 hi 态。缺 modules-extra 时系统日常运行可能正常(当前内核已加载所需模块),但重启到该内核时会因 initrd 缺驱动而失败

3. 切内核前必做 initrd 预检

重启到新内核前,按 4.1 确认目标内核 initrd 含根盘所需存储驱动与 LVM 引导脚本。这一步能拦截绝大多数”切了起不来”的事故。

4. 永远先 grub-reboot 实测,再固定默认项

不要直接改 GRUB_DEFAULTreboot。用 grub-reboot 一次性试引导,失败可自动回落,验证成功后再固定。远程操作时这条尤为关键。

5. 验证期保留可见菜单与带外通道

验证阶段用 GRUB_TIMEOUT_STYLE=menu + GRUB_TIMEOUT=10,并确认 BMC/IPMI 能登录。切勿在未实测时使用 hidden + timeout=0

6. 定期检查 hold 状态

dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' \
  linux-headers-5.15.0-25 \
  linux-headers-5.15.0-25-generic \
  linux-image-5.15.0-25-generic \
  linux-modules-5.15.0-25-generic \
  linux-modules-extra-5.15.0-25-generic

系统升级或人为操作可能改变状态,每月确认 5 项第一列仍全部为 hiapt-mark showhold 只能证明包名在 hold 列表中,无法单独排除包体已删除的 hc 状态。

7. 内核回退后验证 DKMS 模块

回退到旧内核后,通过 DKMS 编译的驱动(如 NVIDIA、VirtualBox)可能不兼容:

dkms status           # 查看模块状态
sudo dkms autoinstall # 为当前内核重新编译缺失模块

6. 故障排除

现象原因解决
apt-mark showhold 已锁,仍被升级HWE 元包 linux-generic-hwe-22.04 未锁sudo apt-mark hold linux-generic-hwe-22.04
手动装回的旧内核又被删unattended-upgrades 清理旧内核对该内核 5 个版本化包全部 apt-mark hold
dpkg -l 显示 hcapt policy(none)包被删仅剩配置unholdapt install → 重新 hold(场景 3)
切内核后卡 initramfs、找不到根盘(LVM/SAS)目标内核 initrd 缺 smartpqi/megaraid_sas 等存储驱动modules-extra 重建 initrd(场景 4);重启前先按 4.1 预检
固定默认内核起不来、本地无菜单可选hidden + timeout=0 且未先实测BMC/IPMI console 进救援;改回可见菜单;今后先 grub-reboot 实测
grub-reboot 设了却未生效grub.cfg 无 next_entry 块或 grubenv 不可写按 4.3 前置自检;确认 /boot/grub/grubenv 可写
回退后启动黑屏/卡 Loading initial ramdiskinitrd 未正确生成或驱动不兼容recovery 进入后 sudo update-initramfs -u -k 5.15.0-25-generic && sudo update-grub
特定网卡/磁盘在旧内核下失效linux-modules-extra装回 extra 包,自动重建 initrd
/boot 空间不足 No space left旧内核占满 /bootsudo apt autoremove --purge && sudo update-grub
update-grub 未列出 -25 内核/boot 缺 vmlinuz 或 initrd重装 5 件套,确认 /boot/vmlinuz-5.15.0-25-generic/boot/initrd.img-5.15.0-25-generic 存在
GRUB_DEFAULT 名称法不生效子菜单名/条目名不精确匹配grep menuentry /boot/grub/grub.cfg 复制准确名,> 前后无空格
autoremove 不肯删旧内核元包 linux-image-generic 仍依赖该版本手工 purge 该版本 5 个包(见 4.6.2);不要为此 purge 元包
清理后只剩一个内核清理过度,无备用引导项装回一个可用内核作 fallback,确保 ls /boot/vmlinuz-* | wc -l ≥ 2

7. 总结

Ubuntu 22.04 的 5.15.0-25-generic 版本控制依赖两大机制:apt-mark hold 固定完整 5 件套并防止其被清理,GRUB 多内核条目 + grub-reboot 提供可验证的回退通道。但真正决定成败的,是对”引导链依赖”与”回落机制”的准确理解——切内核不是改个默认值那么简单,尤其当根盘在 LVM/SAS/RAID 卡上时。

掌握本文后,你可以:

  • 安装并 apt-mark hold 5.15.0-25 的完整 5 件套,同时锁住全部相关元包,既阻断新内核拉取,也防止 -25 被自动清理。
  • 在切换内核前,通过 lsinitramfs 预检 initrd 是否含根盘存储驱动,提前拦截”起不来”事故。
  • grub-reboot 做零风险的一次性引导测试,失败自动回落,验证通过后再固定 GRUB_DEFAULT
  • 准确判断卡死时的回落行为,配合可见菜单与 BMC/IPMI 带外通道,确保远程操作有退路。
  • 通过 dpkg 状态码与 apt 历史日志,快速定位 hc 态缺包与自动清理导致的内核丢失。

建议先在有带外管理的测试机上完整演练一遍”锁定 → 预检 → grub-reboot 实测 → 固定 → 清理”全流程,确认无误后再应用于生产服务器。


8. 参考资料