编辑
2026-10-05
Linux
0

目录

1. 背景与环境
ME-MINI 的存储架构
几个关键配置(升级前的状态)
2. 前传:先在另一台 NAS 上踩的坑
3. 升级前准备(ME-MINI)
3.1 记录现状,确认当前系统健康
3.2 记下 eMMC 的寿命基线
3.3 检查有没有数据"漏"进了挂载点底下的 eMMC 目录
3.4 做备份:全部写到 RAID 上,不写 eMMC
3.5 一个认知坑:?obsolete 提前预测不了会被移除什么
3.6 把 24.04 更新到最新
3.7 升级前的最后确认
4. 为什么没有用无人值守模式
5. 升级当天
5.1 在 tmux 里启动
5.2 实际遇到的提示和选择
6. 升级完成后、重启之前的检查
6.1 eMMC 写入量
6.2 RAID、挂载、swap、LVM
6.3 逐个对比关键配置有没有被动过
6.4 内核和 initramfs
6.5 服务、Docker、Samba
7. 重启后的验证
8. 问题一:fancontrol 起不来(hwmon 编号互换)
现象
原因
修复
9. 问题二:第三方 apt 源被禁用,需要手动恢复
10. 收尾清理
10.1 看有没有桌面组件被装回来
10.2 autoremove 之前先预览并确认三件事
10.3 手动标记的遗留包
10.4 备份什么时候删
11. 结果对照
12. 经验总结

Ubuntu 24.04 → 26.04 原地升级实录:

把 Ubuntu 24.04.5 LTS 原地升级到 26.04.1 LTS(代号 resolute),目标机器的系统盘是 eMMC,日志、Docker、swap、数据全部放在 NVMe RAID6 的 LVM 上。升级前先做足基线和备份,升级中在 tmux 里交互式完成,升级后逐项核对"eMMC 保护设置"有没有被破坏。结果:存储布局、Docker、Samba 全部保留,eMMC 寿命估计无变化;只遇到两个小问题(fancontrol 的 hwmon 编号互换、第三方 apt 源被禁用),都已解决。备份环节还踩了一个 rsync 加 bind mount 的坑,文中也记录了。

Screenshot 2026-10-05 at 12.25.06.png

1. 背景与环境

  • 升级路径:Ubuntu 24.04.5 LTS → 26.04.1 LTS(resolute),使用官方的 do-release-upgrade
  • 这台机器(下称 ME-MINI)的特点是为了保护 eMMC 寿命做了大量优化,所以最担心的是:升级之后这些设置还在不在、能不能无缝迁移
  • 之前先升级了另一台 NAS(tnas),踩了不少坑,这些经验直接用在了 ME-MINI 上(见第 2 节)

ME-MINI 的存储架构

用途位置说明
系统 / 引导eMMC(/dev/mmcblk0,/ 56G + /boot/efi 1.1G)以读为主,EFI 启动
日志RAID → LVM → /var/log(lv_logs,1G)journald 持久化到这里
DockerRAID → LVM → /mnt/docker(lv_docker,50G)data-root 指向此处
swapRAID → /mnt/data/swapfile(16G)放在数据卷上
数据RAID → LVM → /mnt/data(lv_main,约 7.2T)

底层是 6 块 NVMe 组成的 md0(RAID6),之上是卷组 vg_data。

几个关键配置(升级前的状态)

  • /etc/fstab 里三个 LVM 挂载都是 defaults,没有 nofail。这是有意为之:RAID/LVM 没起来时系统会进入 emergency 模式,Docker 不会被启动,也就不会在 eMMC 的空目录 /mnt/docker 上写数据。代价是出问题时需要有人到场或有远程控制台
  • /etc/docker/daemon.json:data-root=/mnt/docker、overlay2、json-file 日志轮转(10m × 3)
  • /etc/systemd/journald.conf:Storage=persistent、SystemMaxUse=512M、SystemMaxFileSize=50M
  • /etc/mdadm/mdadm.conf:没有 ARRAY 行,数组靠 udev 增量组装
  • 第三方 apt 源:Docker(deb822 格式的 docker.sources)、twingate、netbird,另有 Ubuntu Pro 的 ESM 源

2. 前传:先在另一台 NAS 上踩的坑

在 tnas 上升级时遇到过这些问题,后来都成了 ME-MINI 的操作规范:

  1. 配置冲突提示(logind.conf、udevil.conf、smb.conf 等):自己改过的文件一律选"保留本地版本"(N / keep local)
  2. Remove obsolete packages?:升级会把第三方源禁用,于是 Docker 全家桶被判成"过时",列表里有 129 个包。选 n,之后再手动把源恢复
  3. 升级中途 sshd 会短暂不可用:22 端口一度 Connection refused,备用的 1022 端口也没连上。我还手滑关掉了原来的终端窗口,最后靠机器上已有的 Portainer 才进去(建一个 privileged + pid: host 的 alpine 容器,再 nsenter -t 1 -m -u -i -n -p -- /bin/bash 进入宿主机)。教训:升级前准备好一条不依赖 sshd 的进入通道,而且不要关原来的窗口
  4. tmux 包在升级中被替换:之后 tmux attach 报 open terminal failed: not a terminal。我的判断是旧版 tmux 服务端和新版客户端不匹配(没有深入验证),但不影响使用:用 tmux send-keys 和 tmux capture-pane 从另一个 SSH 窗口照样能回答提示
  5. send-keys 别叠发:连发几次 d 再发 n,输入行变成了 dddn。每发一次键就 capture-pane 看一眼,卡住时用 Ctrl+U 清空输入行
  6. 要求回车的提示:Continue [yN] 这种是行输入,需要回车才提交,只发一个字母不够

3. 升级前准备(ME-MINI)

3.1 记录现状,确认当前系统健康

bash
df -h swapon --show docker info | grep "Docker Root Dir" cat /proc/mdstat cat /etc/fstab cat /etc/docker/daemon.json cat /etc/systemd/journald.conf systemctl cat docker | grep -iE 'RequiresMountsFor|^After=' grep -v '^#' /etc/mdadm/mdadm.conf grep -v '^#' /etc/default/grub

要点:/proc/mdstat 里 6 块盘是 [UUUUUU],没有在重建或降级;grub 配置是原样,没有自定义内核参数(升级时它可能被直接换成新版)。

3.2 记下 eMMC 的寿命基线

bash
sudo apt install -y mmc-utils sudo mmc extcsd read /dev/mmcblk0 | grep -E 'LIFE_TIME|PRE_EOL' awk '{printf "%.1f GiB written since boot\n", $7*512/2^30}' /sys/block/mmcblk0/stat

升级前的基线:寿命估计 A、B 都是 0x01(已用 0–10%),Pre-EOL 是 0x01(正常),开机以来累计写入 4.1 GiB。

3.3 检查有没有数据"漏"进了挂载点底下的 eMMC 目录

挂载点之下可能残留挂载之前写入的文件,被挂载盖住后看不见,但一直占着 eMMC。用 bind mount 看一眼:

bash
sudo mkdir -p /mnt/rootcheck && sudo mount --bind / /mnt/rootcheck sudo du -sh /mnt/rootcheck/var/log /mnt/rootcheck/mnt/docker /mnt/rootcheck/mnt/data sudo umount /mnt/rootcheck && sudo rmdir /mnt/rootcheck findmnt /mnt/rootcheck # 必须没有任何输出,确认已经卸载干净

结果:/mnt/docker 和 /mnt/data 都是空的挂载点;/var/log 底下有 43M,最新的文件时间是 5 月 9 日,是日志迁到 LVM 之前遗留的旧文件,之后再没变过,不用处理。

检查完务必卸载,并用 findmnt 确认。 后面的 rsync 备份会被它坑一次,见 3.4。

3.4 做备份:全部写到 RAID 上,不写 eMMC

先拷关键配置,再补全量备份。备份目录统一放在 /mnt/data/upgrade_backup_2026:

bash
sudo mkdir -p /mnt/data/upgrade_backup_2026 && cd /mnt/data/upgrade_backup_2026 # 关键配置 sudo cp /etc/fstab /etc/mdadm/mdadm.conf /etc/docker/daemon.json /etc/systemd/journald.conf . sudo cp -r /etc/lvm . # /etc 全量(保留权限和属主)和 EFI 分区 sudo rsync -aAXH /etc/ etc-full/ sudo tar czf boot-efi.tgz -C / boot/efi # 系统状态快照,升级后用来对比 sudo sh -c 'lsblk -f > lsblk-f.txt; blkid > blkid.txt; findmnt -A > findmnt.txt; mdadm --detail /dev/md0 > md0-detail.txt; mdadm --detail --scan > mdadm-scan.txt; vgs > vgs.txt; lvs -a > lvs.txt; swapon --show > swap.txt; systemctl list-unit-files --state=enabled > enabled-units.txt; dpkg --get-selections > dpkg-selections.txt; apt list "?obsolete" 2>/dev/null > obsolete-before.txt; docker ps -a --format "{{.Names}}\t{{.Image}}\t{{.Status}}" > containers.txt; sysctl -a 2>/dev/null | grep -E "vm\.(swappiness|dirty)" > sysctl-vm.txt' # 根文件系统整份备份(eMMC 上只有 12G,几分钟就好) # 先确认 eMMC 分区只挂载在 /,不能有 bind mount findmnt -n -o TARGET,SOURCE | grep mmcblk0p2 # 只应该看到 / 一行 sudo rsync -aAXH --one-file-system / /mnt/data/upgrade_backup_2026/rootfs/

几个细节:

  • cp -r 不保留权限和属主,所以另外用 rsync -aAX 备份了整个 /etc
  • dpkg-selections.txt 很有用:升级后再导一份对比,就能看出新装了哪些包
  • 备份完用 du -xsh 对比关键目录(/usr、/var、/boot、/etc、/home)的大小,应该和源一致

一个坑:备份大小是预期的两倍。 我的 rootfs 备份有 23G,而 / 只用了 12G。排查后发现,rootfs/mnt/rootcheck 里多了一份完整的根文件系统拷贝:3.3 节的 bind mount 在 rsync 运行时还挂在 /mnt/rootcheck 上。--one-file-system 是按设备号判断的,LVM 上的三个挂载(/var/log、/mnt/docker、/mnt/data)是另一个设备,被正确跳过,备份里只留下了空目录;但 bind mount 和 / 是同一个文件系统、同一个设备号,不会被跳过,于是整个根又被拷了一遍。顺带还解释了为什么 du 在两次运行里对 rootfs/var 给出 6.2G 和 4.5G:两份拷贝里的硬链接文件共享 inode,同一次 du 只算一次。

排查时用到的几条命令:

bash
sudo ls -la /mnt/data/upgrade_backup_2026/rootfs/mnt sudo du -xh --max-depth=2 /mnt/data/upgrade_backup_2026/rootfs/mnt | sort -h | tail -12 findmnt -R /mnt/data # 确认备份目录下没有活着的挂载

处理:确认 rootfs/mnt/rootcheck 里是根目录的内容后,删掉这份重复拷贝(rm -rf 备份目录里的 rootfs/mnt/rootcheck,路径写完整),rootfs 就降到 12G,和 / 一致。系统本身没有任何影响。

3.5 一个认知坑:?obsolete 提前预测不了会被移除什么

我最初以为升级前执行 apt list '?obsolete',就能知道升级时哪些包会被清掉。实际上这条命令列出的是"当前没有任何已启用源能提供"的包,升级前第三方源还是启用的,所以我的输出是空的。Docker 之类要等升级把第三方源禁用之后,才会被判成过时。

正确做法是直接盘点第三方源:

bash
ls /etc/apt/sources.list.d/ grep -rhE '^(deb|URIs)' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null | grep -v -E 'ubuntu.com|你的镜像域名'

ME-MINI 上的结果:Docker、twingate、netbird,这三家的包升级时一定会被列进"过时包",必须选 n。

3.6 把 24.04 更新到最新

bash
sudo apt update apt list --upgradable 2>/dev/null

只显示 Listing... 说明已经是最新。另外看了一下内核符号链接 /boot/vmlinuz 的日期(10 月 2 日,指向 7.0.0-38-generic),和机器上次开机时间吻合:这台机器升级前就已经在跑 7.0 的 HWE 内核,内核对硬件的兼容性已经验证过,这是个让人安心的信息。

3.7 升级前的最后确认

  • 防火墙不会拦住备用 SSH 端口(do-release-upgrade 会额外在 1022 端口起一个 sshd)。我的 ufw 规则里放行了整个局域网网段,所以不用改
  • 准备好一条不依赖 sshd 的进入通道(Portainer 或本机显示器)
  • 如果已有 reboot-required 就先重启一次(本机没有)

4. 为什么没有用无人值守模式

我最初打算用 DEBIAN_FRONTEND=noninteractive do-release-upgrade -f DistUpgradeViewNonInteractive,后来放弃了。

我查到的资料是:这个前端遇到配置冲突会自动选"保留旧文件"(这点很好),但会一路自动确认所有提示。关键风险是 Remove obsolete packages? 这一步:tnas 上的列表里就包含了整套 Docker,没有人回答的话,我推断它会被自动移除,容器全部停掉,得升级完再手动装回来。这一点我没有实测,是按资料加 tnas 的经验推断的。另外它出问题时输出很少,也没法中途用 send-keys 干预。

这台机器的提示本来就不多,交互式没有额外负担,所以还是选了交互式。

5. 升级当天

5.1 在 tmux 里启动

bash
tmux new -s upgrade sudo do-release-upgrade

画面有疑问时,从另一个 SSH 窗口查看和回答:

bash
tmux capture-pane -p -t upgrade | tail -25 tmux send-keys -t upgrade n # 发一个键,然后马上 capture 确认,别叠发

5.2 实际遇到的提示和选择

提示选择说明
发行说明页 Continue [yN]y只是确认已阅读
Foreign Packages Installed:libass9、libde265-0、libsoup-2.4-1、libsoup2.4-common、libsrt1.5-gnutls(Installed from: UbuntuESMApps)y这些包的版本被 Ubuntu Pro 的 ESM Apps 源收录,升级工具标成"非主档案库来源",只是提醒,26.04 里有对应的包会被换成新版
smb.conf 配置冲突保留本地版本(keep the local version)提示里的 /usr/share/samba/smb.conf 是软件包自带的默认模板,生效的仍是 /etc/samba/smb.conf,路径没变
Samba 安装时的 WARNING: ... usershare max shares不用处理Debian 的 samba 不再改这个参数的默认值,安装脚本为了保持兼容,自动往你的 smb.conf 里加了一行 usershare max shares = 100
Remove obsolete packages?(123 个包)n里面有 Docker、netbird、twingate,保住它们
Restart requiredn先在重启之前做检查,再手动重启

中间还有开始升级前的确认等提示,按流程确认即可。

6. 升级完成后、重启之前的检查

升级结束后不要急着重启,先把状态核对一遍。

6.1 eMMC 写入量

bash
awk '{printf "%.1f GiB written since boot\n", $7*512/2^30}' /sys/block/mmcblk0/stat

升级前 4.1 GiB,升级后 16.6 GiB,所以整个升级大约往 eMMC 写了 12.5 GiB,对寿命的影响可以忽略。

6.2 RAID、挂载、swap、LVM

bash
cat /proc/mdstat df -hT /var/log /mnt/docker /mnt/data /boot/efi swapon --show sudo vgs; sudo lvs

结果:RAID 仍是 [UUUUUU];三个挂载、/boot/efi 和 swap 都在;vgs/lvs 与备份一致。/proc/mdstat 里当时有一个 check = 6.9% 的例行一致性检查(只读,不是重建),重启会打断它。我重启后看 /proc/mdstat 没有再出现 check 行,没有确认它会不会自动续跑。想补跑的话可以:

bash
echo check | sudo tee /sys/block/md0/md/sync_action # 只读检查,大约两三个小时 cat /sys/block/md0/md/mismatch_cnt # 跑完后看,应该是 0

小坑:我最初用的是 findmnt /var/log /mnt/docker /mnt/data,结果什么也没输出。findmnt 一次只接受一个目标,传三个会无输出。换成 df -hT 就能同时看多个挂载点。

6.3 逐个对比关键配置有没有被动过

bash
cd /mnt/data/upgrade_backup_2026 diff fstab /etc/fstab && echo fstab-same diff mdadm.conf /etc/mdadm/mdadm.conf && echo mdadm-same diff daemon.json /etc/docker/daemon.json && echo daemon-same diff journald.conf /etc/systemd/journald.conf && echo journald-same sudo diff -r lvm /etc/lvm

fstab、mdadm.conf、daemon.json、journald.conf 都没有变。/etc/lvm 里 lvm.conf 有差异:升级前我没有修改过它,所以被新版本直接替换了,差异几乎全是注释说明,唯一的非注释变化是 report { 和 } 变成启用的空段,以及 vdo-small.profile 里去掉了三个 VDO 参数。我没用 VDO,没有影响。

6.4 内核和 initramfs

bash
ls -l --time-style=long-iso /boot/initrd.img-* /boot/vmlinuz-* lsinitramfs /boot/initrd.img | grep -E 'mdadm|raid456' | head -5 uname -r

两个内核(7.0.0-34、7.0.0-38)的 initrd 都是升级当天重新生成的,里面有 raid456 模块和 mdadm 脚本,说明新版 mdadm/lvm2 的钩子已经打进去了。/boot/efi/EFI 下有 ubuntu 和 BOOT。

6.5 服务、Docker、Samba

bash
systemctl --failed systemctl status ssh ssh.socket | grep -E 'Loaded|Active' sudo docker ps | wc -l testparm -s > /dev/null diff /mnt/data/upgrade_backup_2026/etc-full/samba/smb.conf /etc/samba/smb.conf
  • systemctl --failed 为空
  • ssh.service 显示 inactive (dead),但 ssh.socket 是 active (listening):这是 socket 激活的正常状态(有新连接时才拉起 sshd)。重启前建议从另一台机器新开一个 SSH 连接,验证确实能登录,万一登不上,重启后就进不来了
  • Docker 仍在运行(此时还是 24.04 的构建),23 个容器全部在跑(docker ps 输出 24 行含表头)
  • Samba:testparm 通过;diff 里只多了前面提到的 usershare max shares = 100

7. 重启后的验证

bash
uname -r cat /proc/mdstat df -hT /var/log /mnt/docker /mnt/data /boot/efi swapon --show sudo docker info | grep "Docker Root Dir" sudo docker ps | wc -l systemctl --failed sudo mmc extcsd read /dev/mmcblk0 | grep -E 'LIFE_TIME|PRE_EOL' awk '{printf "%.1f GiB written since boot\n", $7*512/2^30}' /sys/block/mmcblk0/stat cat /sys/block/md0/md/mismatch_cnt

结果:

  • 内核 7.0.0-38-generic;RAID [UUUUUU];三个挂载和 swap 全部正常;Docker Root Dir 仍是 /mnt/docker,23 个容器都起来了
  • eMMC 寿命估计仍是 0x01;重启后写入量 0.0 GiB:开机过程几乎不往 eMMC 写数据,日志全落在 RAID 上,说明保护设计在新系统上依然有效
  • mismatch_cnt 是 0(那次一致性检查被重启打断了,所以这个 0 只代表目前没发现问题)
  • 唯一的失败项:fancontrol.service

8. 问题一:fancontrol 起不来(hwmon 编号互换)

现象

text
fancontrol[2993]: Device path of hwmon7 has changed fancontrol[2993]: Device path of hwmon8 has changed fancontrol[2993]: Device name of hwmon7 has changed fancontrol[2993]: Device name of hwmon8 has changed fancontrol[2993]: Configuration appears to be outdated, please run pwmconfig again

先看散热有没有风险:sensors 里 NVMe 约 49–50°C、CPU 48°C,远低于告警线,fan2 在转(fancontrol 停下时会把风扇还给主板自动控制),所以不着急。

原因

/etc/fancontrol 里写死了 hwmon7 = it8628(风扇芯片)、hwmon8 = coretemp(CPU 温度),而这次启动系统分配成了:

text
hwmon7: coretemp hwmon8: it8628

两个编号对调了。hwmon 编号是按驱动加载顺序分配的,不稳定。fancontrol 发现名字和路径对不上,就拒绝启动,这是它的保护机制。对比备份确认 /etc/fancontrol 本身没被改过,升级前一次启动的日志(journalctl -u fancontrol -b -1)里编号还是对的,所以是升级之后加载顺序变了。

修复

先确认设备路径:

bash
readlink -f /sys/class/hwmon/hwmon8/device /sys/class/hwmon/hwmon7/device # 预期分别是 /sys/devices/platform/it87.2608 和 /sys/devices/platform/coretemp.0

把配置里的 7 和 8 对调,清除失败状态后启动:

bash
sudo cp /etc/fancontrol /etc/fancontrol.bak-20261005 sudo sed -i -e 's/hwmon7/TMPA/g' -e 's/hwmon8/hwmon7/g' -e 's/TMPA/hwmon8/g' /etc/fancontrol cat /etc/fancontrol sudo systemctl reset-failed fancontrol # 不能省,否则会报 Start request repeated too quickly sudo systemctl start fancontrol systemctl status fancontrol --no-pager | head -12 sensors | grep -E 'fan2|pwm2'

修复后 fancontrol 变成 active (running),pwm2 变成 MANUAL CONTROL,占空比 88%,这是按配置里 MINTEMP=40、MAXTEMP=60 的曲线算出来的。之后又重启了好几次,各项检查都正常。

需要知道的是:hwmon 编号并不保证稳定。如果以后又对不上,fancontrol 还是会拒绝启动,后果是风扇保持主板自动控制,不会停转。想一劳永逸的话,可以用 systemd drop-in 在启动前按设备名动态生成配置,我没有做这一步。

9. 问题二:第三方 apt 源被禁用,需要手动恢复

升级会把 Docker、twingate、netbird 的源全部禁用,升级后的状态是:

  • docker.sources(deb822 格式):Suites: noble,并多了一行 Enabled: no
  • twingate.list、netbird.list:被改名成 .list.disabled,里面的 deb 行被注释掉了

恢复过程:

bash
cd /etc/apt/sources.list.d sudo sed -i -e 's/^Suites: noble/Suites: resolute/' -e '/^Enabled: no/d' docker.sources sudo sed 's/^# *deb /deb /' twingate.list.disabled | sudo tee twingate.list sudo sed 's/^# *deb /deb /' netbird.list.disabled | sudo tee netbird.list cat docker.sources sudo apt update apt list --upgradable 2>/dev/null | grep -E 'docker|containerd|netbird|twingate'

提示:twingate 用的 keyring 文件名是 twingate-connector-keyring.gpg,保持原文件里的路径,别照抄别的机器。apt update 里要能看到 download.docker.com ... resolute、pkgs.netbird.io、packages.twingate.com,没有 NO_PUBKEY 或 404。

Docker 官方源已经提供 resolute 通道,所以可以把包从 24.04 的构建换成 26.04 的构建(版本号都是 29.8.2,只是构建不同):

bash
sudo apt install --only-upgrade docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras sudo docker ps | wc -l sudo docker --version apt policy docker-ce | head -4

注意:升级 docker-ce 会重启 dockerd,所有容器会短暂停止再自动恢复,挑个空闲时段做。完成后 docker ps 仍是 24 行,apt policy 里 Installed 带 ubuntu.26.04~resolute。确认没问题后删掉备份的源文件,只删这三家的:

bash
sudo rm /etc/apt/sources.list.d/{twingate,netbird}.list.disabled /etc/apt/sources.list.d/twingate.list.save

这时再看 apt list '?obsolete',里面已经没有 Docker、netbird、twingate 了,剩下的都是 24.04 遗留的旧库和旧内核头文件。

10. 收尾清理

这一节是建议的操作顺序,每一步都应该先预览再执行。

10.1 看有没有桌面组件被装回来

用升级前后的 dpkg --get-selections 对比,只看新增的、名字带桌面相关关键词的包:

bash
dpkg --get-selections > /tmp/after.txt diff <(awk '{print $1}' /mnt/data/upgrade_backup_2026/dpkg-selections.txt | sed 's/:.*//' | sort -u) \ <(awk '{print $1}' /tmp/after.txt | sed 's/:.*//' | sort -u) \ | grep '^>' | grep -Ei 'gnome|gtk|gdm|lightdm|desktop|xorg|wayland'

结果只有 6 个:gnome-accessibility-themes、gnome-themes-extra、gnome-themes-extra-data、gtk2-engines-pixbuf、gir1.2-gtk-4.0、libdecor-0-plugin-1-gtk,都是主题和库,没有 gdm、gnome-shell、xorg 这类桌面环境本体。前四个会被 autoremove 清掉,后两个用 apt rdepends --installed 看是不是有人依赖。

10.2 autoremove 之前先预览并确认三件事

bash
sudo apt autoremove --dry-run

预览结果是 105 个包:24.04 遗留的旧库、gtk2/gnome 主题、旧版 ffmpeg 库、旧 HWE 内核头文件和工具,另外有 samba-ad-provision、samba-dsdb-modules(活动目录域控才用,这台是 ROLE_STANDALONE,不受影响)。里面没有 docker、netbird、twingate、mdadm、lvm2、samba 主包。

执行之前确认:

bash
# 1. 26.04 自己的内核元包在,否则移除 HWE 元包后收不到内核更新 dpkg -l linux-generic linux-image-generic | grep '^ii' # 2. 有没有虚拟环境依赖系统自带的 python3.12(它会被移除) sudo find /home /opt /root /usr/local -name pyvenv.cfg 2>/dev/null # 3. 有没有用 DKMS 编译的驱动(和旧内核头文件有关) dkms status 2>/dev/null

三项都没问题再执行 sudo apt autoremove。

10.3 手动标记的遗留包

?obsolete 里还有几个不在 autoremove 清单里(因为是手动安装标记的):linux-headers-6.17.0-40-generic、linux-hwe-6.17-headers-6.17.0-40、kerneloops、policykit-1、wireless-tools、vdpau-driver-all。先模拟再删:

bash
sudo apt remove --simulate linux-headers-6.17.0-40-generic linux-hwe-6.17-headers-6.17.0-40 kerneloops policykit-1 wireless-tools

模拟结果只涉及这几个包才去掉 --simulate 执行。

10.4 备份什么时候删

整盘拷贝(rootfs,12G)只在"升级失败、需要回滚"的窗口期有价值。升级后稳定运行几天、经历几次重启、各项检查都正常,就可以把它删掉。 再留着意义不大:升级之后 Docker、日志、/etc 里的内容都已经变了,拿它覆盖回去,回到的是 24.04,反而容易引入新问题。

配置和快照则建议长期留着。它们加起来只有几十 MB,却是以后排查"这个配置以前是什么样"最方便的依据(对比 etc-full 就知道某个行为变化是不是这次升级造成的),lvm/ 里的卷组元数据备份在 LVM 出问题时也用得上。

bash
sudo du -sh /mnt/data/upgrade_backup_2026/* | sort -h # 先看各项占多大 ls -la /home/jackie | head # 确认自己的文件和脚本还在 /home、/root sudo ls -la /root | head sudo rm -rf /mnt/data/upgrade_backup_2026/rootfs # 路径写完整,不要缩写 du -sh /mnt/data/upgrade_backup_2026

另外,备份放在 RAID 上,保留它不影响 eMMC 寿命,删它也不会改善,纯粹是为了整洁。

11. 结果对照

项目升级前升级后
系统版本Ubuntu 24.04.5 LTSUbuntu 26.04.1 LTS(resolute)
内核7.0.0-38-generic(HWE)7.0.0-38-generic(升级后重新生成 initrd)
RAID6 md0[UUUUUU][UUUUUU],mismatch_cnt = 0
挂载 /var/log /mnt/docker /mnt/data在 LVM 上不变,fstab 无改动
swap/mnt/data/swapfile 16G不变
Docker24.04 构建,data-root=/mnt/docker26.04 构建(29.8.2),23 个容器全部运行,data-root 不变
Samba正常testparm 通过,仅多一行 usershare max shares = 100
eMMC 寿命估计(A/B/Pre-EOL)0x01 / 0x01 / 0x010x01 / 0x01 / 0x01
升级过程 eMMC 写入4.1 GiB(开机以来)16.6 GiB(重启前),约 12.5 GiB 来自升级
重启后 eMMC 写入-0.0 GiB
systemctl --failed-空(fancontrol 修复后)

12. 经验总结

  1. 先备份再动手,备份写到 RAID 上:/etc 全量、/boot/efi、dpkg --get-selections、各种状态快照,升级后逐项 diff,比凭感觉检查可靠得多
  2. 交互式升级 + tmux:配合 capture-pane / send-keys 从另一个窗口监控和回答,每次发键后先确认再发下一个
  3. 自己改过的配置文件一律保留本地版本,升级后再对比新版有没有值得吸收的内容
  4. Remove obsolete packages? 选 n:第三方源被禁用后,Docker 等包会被误判成过时
  5. 升级后恢复第三方源:先确认官方源已有对应的发行版通道,再改成 resolute
  6. fstab 不加 nofail 是 eMMC 保护的一部分:挂载失败就进 emergency 模式,宁可不启动也不让 Docker 写 eMMC,但要有进入通道
  7. hwmon 编号不稳定:用 fancontrol 的机器,升级或换内核后记得检查服务状态
  8. 用 mmc extcsd 量化 eMMC 寿命:升级前后各记一次寿命估计和累计写入量,才知道这次升级到底写了多少
  9. 准备一条不依赖 sshd 的进入通道,并且不要关掉原来的终端窗口
  10. 命令也要核对:findmnt 只接受一个目标;apt list '?obsolete' 不能预测升级会移除什么,别照搬
  11. bind mount 检查完要卸载,备份前确认 eMMC 分区只挂在 /:rsync --one-file-system 不会跳过同一设备上的绑定挂载,会把整个根再拷一份
  12. 整盘拷贝等升级稳定运行几天后再删,只留配置和快照:整盘拷贝只在回滚窗口期有用,配置和快照几十 MB,长期留着很划算