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 第一列)

排障关键:能一眼看出包是真装好了、还是只剩残留、还是被锁定。

含义说明
iiinstalled正常已安装
hihold + installed已安装且被 apt-mark hold 锁定(正常锁定态)
hchold + config-files被 hold 但包体已删、仅剩配置(异常,需补装)
rcremoved + config-files已卸载、仅剩配置残留(无害,可忽略)
ununknown/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>-genericgeneric 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-upgradesPackage-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-rebootnext_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 与 initrd

update-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         # 删完必须 >= 2

autoremove 不删当前运行的内核,也不碰被 hold 的包。删包时 dpkg 的 postrm 钩子会自动调 update-grubupdate-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-devbuild-essentialg++ 整条工具链
别删元包绕依赖为删旧内核而 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 显示 hcapt policy(none)包被删仅剩配置unholdapt install → 重新 hold
回退后启动黑屏/卡 Loading initial ramdiskinitrd 未正确生成或驱动不兼容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旧内核占满 /bootsudo 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