Ubuntu 22.04 内核锁定与安全回退:从原理到实战全指南
在 Linux 服务器运维中,内核(Kernel)升级通常由自动更新(unattended-upgrades)静默触发。这在稳定性优先的生产环境里是隐患:新内核可能与阵列卡驱动、DKMS 模块或特定应用不兼容,导致服务异常甚至无法启动。更棘手的是,自动更新在拉取新内核的同时会清理旧内核——一旦出问题,想回退却发现旧内核已被删掉。
apt-mark hold 与 GRUB 内核选择机制是 Ubuntu 内置的内核版本控制方案:前者冻结内核包不被升级或清理,后者在升级发生后仍能回退到历史版本。但真正的难点不在”怎么锁”,而在”怎么安全地切回去”——尤其当根文件系统建在 LVM/SAS/RAID 卡上时,一次盲目的 reboot 就可能让服务器卡死在 initramfs 里再也起不来。
内核版本控制的核心价值:
- 锁定可靠:
apt-mark hold精确冻结指定版本,不受apt upgrade与自动更新影响。 - 防清理:对目标内核的全部包打 hold,可阻止
unattended-upgrades的旧内核自动回收。 - 可回退:GRUB 保留多版本内核条目,配合
grub-reboot可零风险试引导。 - 可自救:即使新内核无法启动,可见菜单 + 带外管理(BMC/IPMI)仍能挽回。
本文将从内核管理机制、包状态识别、锁定操作、安全回退流程、引导故障原理及排错等方面,帮助你完整掌握内核版本控制的全流程,并重点讲透”切内核为什么会起不来、如何提前预检、失败后如何回落”这一实战核心。
目录
- 1. 引言:内核自动升级与清理的双重风险
- 2. Ubuntu 内核管理机制
- 3. 锁定 5.15.0-25 内核,防升级也防清理
- 4. 安全回退到 5.15.0-25 内核
- 5. 常见场景与最佳实践
- 6. 故障排除
- 7. 总结
- 8. 参考资料
1. 引言:内核自动升级与清理的双重风险
Ubuntu 22.04 LTS 初始搭载内核 5.15.0-25-generic(可用 uname -r 确认)。随着 SRU(Stable Release Update)与 HWE(Hardware Enablement,硬件支持扩展)栈推进,系统会持续引入更高版本内核(如 5.15.0-185)。
对服务器而言,这带来两层风险:
- 升级风险:
unattended-upgrades静默拉取新内核,新内核可能与硬件/驱动不兼容。 - 清理风险:自动更新在装新内核后会执行”旧内核回收”,默认只保留”当前运行 + 最新”两个内核,其余旧内核(包括你精心保留的出厂
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>-generic | generic 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 输出第一列是两字母状态码,是排障的关键——能一眼看出包是真装好、只剩残留、还是被锁定:
| 码 | 含义 | 说明 |
|---|---|---|
ii | installed | 正常已安装 |
hi | hold + installed | 已安装且被 apt-mark hold 锁定(正常锁定态) |
hc | hold + config-files | 被 hold 但包体已删、仅剩配置(异常,需补装) |
rc | removed + config-files | 已卸载、仅剩配置残留(无害,可忽略) |
un | unknown / 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(无人值守更新)对内核有两个独立行为,容易混淆:
- 升级:通过元包拉取并安装新内核。
- 清理(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提示:
ext4、dm-mod、md-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 阶段就被”消费”,与内核之后起不起得来无关。三种结局:
- 目标内核正常起来 →
uname -r为目标版本,成功。 - 目标内核卡在 initramfs(找不到根盘) → 机器卡死在原地,不会自己重启。GRUB 已交棒,此刻帮不了你,必须由人 power-cycle(本地电源键或 BMC 强制重启)。
- 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-grubupdate-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 # 必须 >= 2autoremove 由元包的依赖关系推导删除范围,不删当前运行的内核,也不碰被 hold 的包,适合大多数情况。
删包时 dpkg 的 postrm 钩子会自动执行 update-initramfs 与 update-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-dev、build-essential、g++ 整条工具链 |
为绕开依赖而 purge linux-image-generic 元包 | 系统失去内核更新跟踪,安全补丁不再自动安装 |
| 清到只剩一个内核 | 该内核出问题时 GRUB 菜单无第二项可选,只能进 recovery |
忘记先 unhold 就 purge | apt 报错拒绝执行,包未被删 |
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-generic、linux-headers-generic、linux-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_DEFAULT 后 reboot。用 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 项第一列仍全部为 hi。apt-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 显示 hc,apt policy 为 (none) | 包被删仅剩配置 | unhold → apt 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 ramdisk | initrd 未正确生成或驱动不兼容 | recovery 进入后 sudo update-initramfs -u -k 5.15.0-25-generic && sudo update-grub |
| 特定网卡/磁盘在旧内核下失效 | 缺 linux-modules-extra | 装回 extra 包,自动重建 initrd |
/boot 空间不足 No space left | 旧内核占满 /boot | sudo 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 hold5.15.0-25的完整 5 件套,同时锁住全部相关元包,既阻断新内核拉取,也防止-25被自动清理。 - 在切换内核前,通过
lsinitramfs预检 initrd 是否含根盘存储驱动,提前拦截”起不来”事故。 - 用
grub-reboot做零风险的一次性引导测试,失败自动回落,验证通过后再固定GRUB_DEFAULT。 - 准确判断卡死时的回落行为,配合可见菜单与 BMC/IPMI 带外通道,确保远程操作有退路。
- 通过 dpkg 状态码与 apt 历史日志,快速定位
hc态缺包与自动清理导致的内核丢失。
建议先在有带外管理的测试机上完整演练一遍”锁定 → 预检 → grub-reboot 实测 → 固定 → 清理”全流程,确认无误后再应用于生产服务器。