Ubuntu 22.04 内核锁定与版本回退 速查
锁定内核防自动升级、回退到指定版本。核心两件事:apt-mark hold 冻结包(含元包),GRUB 默认项指向目标内核。所有操作不含重启,切换内核才需重启。
铁律(血泪教训):在用
grub-reboot一次性引导实测目标内核能正常起来之前,绝不固定GRUB_DEFAULT、绝不设GRUB_TIMEOUT=0+hidden。否则目标内核若缺驱动(如根盘在 SAS/RAID 卡上却少了modules-extra)会卡在 initramfs 起不来,且无菜单可选、无法自救,只能靠 BMC/救援盘。正确顺序见第 4 节。
目录
1. 状态速查
uname -r # 当前运行内核,如 5.15.0-25-generic
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 # 某版本能否从源装回2. dpkg 包状态与内核包组成
2.1 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 的包会停在此态,apt policy显示Installed: (none)。必须重新安装才能补回,否则该版本内核可能缺驱动模块。
2.2 内核包组成(4 类组件、5 个 DEB 包)
| 包名 | 内容 | 缺失后果 |
|---|---|---|
linux-image-<ver>-generic | 内核镜像 vmlinuz | 无法引导该版本 |
linux-modules-<ver>-generic | 基础模块(常用驱动) | 基本功能缺失 |
linux-modules-extra-<ver>-generic | 额外模块(少见网卡/存储/文件系统) | 特定硬件驱动丢失,网络/磁盘可能不可用 |
linux-headers-<ver> | 通用头文件 | headers 不完整,DKMS 无法正常编译 |
linux-headers-<ver>-generic | generic flavor 专用头文件 | 第三方驱动无法针对该内核编译 |
4 个还是 5 个? image、modules、modules-extra、headers 是四类组件,但 headers 实际拆成两个 DEB 包。
linux-headers-<ver>-generic硬依赖linux-headers-<ver>,所以 APT 会自动补齐后者;显式列出五个包更适合离线下载、核验和锁定。
适用边界:image 和基础 modules 是启动核心;modules-extra 是否必需取决于硬件;两个 headers 不参与正常启动,主要用于 DKMS。作为可靠的回退环境,建议五个全部安装。
提示:
linux-modules-extra最易被忽略。基础linux-modules里已含drivers/net等常用驱动,但特殊硬件依赖 extra 包。补装 extra 会自动触发update-initramfs把额外驱动打进 initrd(initrd 体积会明显增大)。
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 内核才有,独立于标准元包,需单独锁
3. 锁定 5.15.0-25 内核
以下 5 件套必须全部安装并 hold。先安装再锁定,避免对未安装或只剩配置残留的包执行 hold:
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
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
# 同时锁元包,阻止 APT 继续拉取更新版本内核
sudo apt-mark hold linux-image-generic linux-headers-generic # 锁标准元包
sudo apt-mark hold linux-generic-hwe-22.04 2>/dev/null # 有 HWE 才需要验证 5 件套(第一列必须全部为 hi,即 hold + installed):
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再检查元包锁定结果:
apt-mark showhold | grep -E '^(linux-image-generic|linux-headers-generic|linux-generic-hwe-22\.04)$'验收标准:5 个版本化包缺少任何一个、显示
ii(已安装但未 hold)或hc(已 hold 但包体已删除),都不算锁定完成。
纵深防御(可选):hold 已能阻止 unattended-upgrades 升级。额外在 /etc/apt/apt.conf.d/50unattended-upgrades 的 Package-Blacklist 加内核黑名单,可在有人误 unhold 时兜底:
Unattended-Upgrade::Package-Blacklist {
"linux-image";
"linux-headers";
"linux-modules";
"linux-generic";
};
提示:仅手动装回旧内核并不能阻止 unattended-upgrades 再次清理它。默认策略通常只保留当前运行及较新的内核,其余旧内核可能被自动 purge。要让某旧内核长期留存,应对它的 5 个版本化包都打 hold(hold 的包不会被 autoremove 清理)。
4. 回退到指定内核(安全流程)
安全顺序,不可跳步:引导前预检 → 保留可见菜单 → grub-reboot 一次性实测 → 验证通过后才固定默认项。
4.1 引导前预检(重启前必做,验证目标内核能否找到根盘)
若根文件系统在 LVM/RAID/SAS/NVMe 上,引导依赖的存储驱动必须在目标内核的 initrd 里,否则起不来。逐项确认:
V=5.15.0-25-generic
# ① 看根盘挂在什么控制器上,记下它的驱动名(如 smartpqi / megaraid_sas / mpt3sas / nvme)
lsblk -o NAME,MOUNTPOINT | grep -w / # 找到根设备
lspci -k | grep -iA3 'RAID\|SAS\|SCSI\|NVME' # 看控制器的 Kernel driver in use
# ② 确认该驱动 + LVM hook 已进目标内核 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 引导,根在 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),可用grep -E 'ext4|dm-mod' /lib/modules/$V/modules.builtin确认。真正需要在 initrd 里的是smartpqi这类外挂存储驱动。若预检缺smartpqi,按第 5 节场景 3 补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会让菜单完全不显示。一旦固定的默认内核起不来,本地无从选择,只能靠 BMC/IPMI console 或救援盘。验证阶段绝不能用这套配置。
4.3 一次性引导测试(核心,不改默认项)
grub-reboot 让下次且仅下次重启进目标内核;若起不来,再次重启会自动回到 GRUB_DEFAULT 指向的项。这是零风险验证方式。
回落落点铁律:回落点 =
GRUB_DEFAULT当前指向的内核,所以测试前它必须停在一个已知能正常启动的内核上。Ubuntu 默认GRUB_DEFAULT=0(菜单第一项,绑定当前最新已安装内核),只要最新内核能起,保持0即为正确落点,无需改动。两个坑:① 测试前绝不能把GRUB_DEFAULT先改成目标(如 -25),否则回落点也是起不来的 -25,安全网失效;②GRUB_DEFAULT不能是saved,否则落点绕到 grubenv,语义不直观。数字0或名称字符串都不受影响,grub-reboot用next_entry一次性覆盖。
# 先查菜单精确条目名(子菜单名>菜单项名,> 前后无空格)
grep -E "menuentry '|submenu '" /boot/grub/grub.cfg | grep -v recovery
# 确认 GRUB_DEFAULT 停在能起的内核(默认 0 即可,勿在测试前改指向目标)
grep '^GRUB_DEFAULT=' /etc/default/grub # 期望 GRUB_DEFAULT=0
# 设置仅下次生效的引导项
sudo grub-reboot "Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-25-generic"
sudo grub-editenv list # 确认 next_entry=Advanced options...5.15.0-25-generic
sudo reboot重启后验证是否成功进入目标内核:
uname -r # 应输出目标版本,如 5.15.0-25-generic- 起来了且
uname -r正确 → 进入 4.4 固定默认项。 - 没起来 → 再次重启(或 BMC 触发),系统回落到原默认内核,回到 4.1 排查 initrd 缺什么。
4.4 验证通过后,才固定默认项
只有 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 # 回显应含目标内核的 image 与 initrdupdate-grub 回显应包含:
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.5 清理多余内核
先问一句:真的需要清吗? 多余内核只占 /boot 空间(每个版本约 100–200 MB),不影响运行,留着还是一条现成的回退后路。df -h /boot 用量不高就别动——清理动作本身的风险高于收益。
确实要清时只有一条铁律:至少保留两个能引导的内核(当前运行的 + 一个备用)。
df -h /boot # 空间充裕就不必清
uname -r # 当前运行内核,绝不能删
apt autoremove --purge --dry-run # 预览会删什么,逐行确认不含当前内核
apt autoremove --purge # 确认无误后执行
ls /boot/vmlinuz-* | wc -l # 删完必须 >= 2autoremove 不删当前运行的内核,也不碰被 hold 的包。删包时 dpkg 的 postrm 钩子会自动调 update-grub 与 update-initramfs,不需要手动补。
若 autoremove 不肯删(通常是元包仍依赖该版本),手工指定时只列内核自身的 5 个包:
V=5.15.0-124-generic # 替换为实际要删的版本
# 被 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| 避坑点 | 说明 |
|---|---|
| 只写内核包 | 绝不把 linux-libc-dev 并进 purge 列表——它会连带删掉 libc6-dev、build-essential、g++ 整条工具链 |
| 别删元包绕依赖 | 为删旧内核而 purge linux-image-generic,代价是系统从此不跟踪内核更新,安全补丁不再自动安装 |
| 别删到只剩一个 | 唯一内核起不来时菜单无第二项可选,只能进 recovery 或挂救援盘 |
| 已 hold 的先解锁 | hold 状态下 apt 拒绝删除,需先 apt-mark unhold |
5. 常见场景
场景 1:新装机锁定初始内核 5.15.0-25
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 节安装完整 5 件套;执行后必须确认五项均为
hi。
场景 2:内核被意外升到 185,回退到 25
# 若 25 已被自动清理,先装回全部 5 个包
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
# 设默认项并锁定
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
sudo apt-mark hold 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
# 重启后生效场景 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场景 4:查内核为何被删(溯源)
grep -E "Commandline|Remove:" /var/log/apt/history.log* | grep -B1 "linux-image"Commandline: /usr/bin/unattended-upgrade + Remove: linux-image-... 即自动更新的旧内核清理,非人为删除。
场景 5:根在 LVM/SAS 上,切到旧内核后卡在 initramfs 起不来
典型于配 Adaptec/LSI 阵列卡(smartpqi/megaraid_sas)+ LVM 根盘的服务器。目标内核 initrd 缺存储驱动 → 找不到根盘 → 卡 initramfs。若已固定默认项且菜单不可见,只能走 BMC。
# 从 BMC console 或救援模式进入后,为目标内核补驱动并重建 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
lsinitramfs /boot/initrd.img-5.15.0-25-generic | grep smartpqi # 确认已进 initrd
# 预防:先临时把默认切回能起的内核,待补全后再用 grub-reboot 实测(见 4.3)教训:这类机器切内核前,务必先按 4.1 预检 initrd 里有存储驱动,并用 4.3 的
grub-reboot一次性测试,切勿直接hidden+timeout=0固定默认项后 reboot——起不来时毫无本地退路。
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 |
回退后启动黑屏/卡 Loading initial ramdisk | initrd 未正确生成或驱动不兼容 | recovery 模式进入后 sudo update-initramfs -u -k 5.15.0-25-generic && sudo update-grub |
| 切内核后卡 initramfs、找不到根盘(LVM/SAS) | 目标内核 initrd 缺 smartpqi/megaraid_sas 等存储驱动 | 补 modules-extra 重建 initrd(场景 5);重启前先按 4.1 预检 |
| 固定默认内核起不来、本地无菜单可选 | GRUB_TIMEOUT_STYLE=hidden+GRUB_TIMEOUT=0 且未先实测 | BMC/IPMI console 进入救援;改回可见菜单;今后先用 grub-reboot 实测 |
| 特定网卡/磁盘在旧内核下失效 | 缺 linux-modules-extra | 装回 extra 包,自动重建 initrd |
/boot 空间不足 No space left | 旧内核占满 /boot | sudo apt autoremove --purge && sudo update-grub |
update-grub 未列出目标内核 | /boot 下缺 vmlinuz 或 initrd | 重装该内核包,确认 ls /boot/*-<ver>-* 齐全 |
| GRUB_DEFAULT 名称法不生效 | 子菜单名/条目名不精确匹配 | 用 grep menuentry /boot/grub/grub.cfg 复制准确名,> 前后无空格 |
autoremove 不肯删旧内核 | 元包 linux-image-generic 仍依赖该版本 | 手工 purge 该版本 5 个包;不要为此 purge 元包 |
| 删完只剩一个内核 | 清理过度,无备用引导项 | 装回一个可用内核作 fallback,保持 /boot/vmlinuz-* 数量 ≥ 2 |