编程 Ubuntu 24.04 升到 26.04 之后:三处被静默改掉的配置与逐项验证命令

2026-11-11 00:04:34

Ubuntu 24.04 升到 26.04 之后:三处被静默改掉的配置与逐项验证命令

do-release-upgrade 跑完、重启、lsb_release -a 显示 26.04,只说明版本号变了,不说明系统完好。升级过程有三条默认行为:第三方源被注释掉,标了 remove-on-upgrade 的 conffile 直接删除,新版组件只安装不迁移数据。这三件事在升级过程中都不会报错,要等某个服务半夜挂掉才会暴露。

官方升级说明见 Upgrade Ubuntu Desktop,各版本支持周期见 Ubuntu 发布周期。

一、两道闸门:能不能升,升之前要清什么

闸门一:通道是否已打开。 do-release-upgrade 只在 /etc/update-manager/release-upgrades 里 Prompt=lts 时才提供 LTS→LTS 升级;Prompt=normal 会把短期支持的过渡版本也列出来。

grep -i prompt /etc/update-manager/release-upgrades
sudo sed -i 's/Prompt=.*/Prompt=lts/' /etc/update-manager/release-upgrades

LTS→LTS 要等第一个点版本发布后才开放。26.04(Resolute Raccoon)于 2026-04-23 发布,但 26.04.1 到 2026-08-27 才发布;Canonical 又为 backport rust-coreutils 的回归修复推迟了数周,24.04→26.04.1 的常规通道 2026-09-29 才真正开放。点版本不是另一个 Ubuntu,只是把四个月的修复整合进安装介质。等待期间可以 do-release-upgrade -d 走开发版通道,不受支持,可能损坏系统。

也不支持跳版本:22.04 必须先升到 24.04,升级工具永远只提供下一个 LTS。

闸门二:当前系统必须干净。 所有更新装完、最新内核已生效、没有半配置状态的包:

sudo apt update && sudo apt full-upgrade && sudo apt --purge autoremove
sudo dpkg --audit          # 无输出 = 没有半配置状态的包
apt-mark showhold
df -h / /boot
[ -f /var/run/reboot-required ] && sudo reboot

有待重启的内核时,升级器会先要求重启。另外,unattended-upgrades 会抢 /var/lib/dpkg/lock-frontend,升级程序会因 Could not get lock 直接停止,升级前先 sudo systemctl stop unattended-upgrades。注意 apt-mark showhold 不会显示 /etc/apt/preferences.d/ 里的 APT pin。

二、三处静默失效

1. /etc/sysctl.conf 被删掉,内核参数全部回退

现象是某些服务开始零星失败,fs.inotify.max_user_instances 从 8192 掉回内核默认的 128。验证:

dpkg-query -W -f='${Conffiles}\n' procps | grep sysctl.conf
#  /etc/sysctl.conf 72b7c827a9636cda7b3b371091ff2dce remove-on-upgrade
sudo grep -i obsolete /var/log/dist-upgrade/apt-term.log
# Obsolete conffile /etc/sysctl.conf has been modified by you.

根因是 26.04 的 procps 把 /etc/sysctl.conf 标记为 remove-on-upgrade,升级时直接删除,改过也一样。任何把参数写进这个文件的 Ansible 任务,升级后都会静默失效。

后果不止 inotify:fwupd.service / fwupd-refresh.service 每小时失败一次,报 Could not initialize inotify, check /proc/sys/fs/inotify/max_user_instances;KubeVirt 的 virt-handler 在 inotify 用量高的节点上 CrashLoopBackOff,报 Failed to create an inotify watcher ... too many open files;net.ipv6.conf.all.forwarding 从 1 回 0、accept_ra 从 2 回 1,IPv6 转发和路由通告一起断。

处理方向是把参数迁到 /etc/sysctl.d/*.conf,这个目录 dpkg 不管,升级不会碰。迁完用命令核对实际生效值,而不是看配置文件:

sysctl fs.inotify.max_user_instances fs.inotify.max_user_watches \
       net.ipv4.ip_forward net.ipv6.conf.all.forwarding net.ipv6.conf.all.accept_ra

2. 第三方 apt 源被清空成一行注释

do-release-upgrade 无法把第三方的一行式 .list 迁移成 deb822 格式的 .sources,于是把原文件清成一行注释,内容挪到同名 .disabled,并在里面留下说明。源没了之后,tailscale 这类包还会被判成「废弃包」一并处理。

ls -l /etc/apt/sources.list.d/     # 找 *.disabled / *.migrate / *.distUpgrade
apt-cache policy tailscale         # Version table 里没有第三方源就是丢了

恢复时 suite 必须用新代号 resolute,并且先确认上游确实发布了这个 suite,否则升级后第一次 apt update 就报 E: The repository '... resolute Release' does not have a Release file.:

# /etc/apt/sources.list.d/tailscale.list
# Tailscale packages for ubuntu resolute
deb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/ubuntu resolute main

默认行为是禁用所有非 Ubuntu 源;--allow-third-party 反过来保留它们,是风险选项。ubuntu-security-status --thirdparty 可以列出没有任何 Ubuntu 软件包存档提供的已安装包。另外 /etc/apt/preferences.d/ 里指定 noble 的 pin 会让包停在旧版本,源恢复了版本也上不去。

3. 残留 conffile 让 logrotate 整体失败

升级时对 sshd_config、logrotate 之类文件选「保留当前版本」之后,被删包留下的 conffile 会和另一个包的 conffile 重复 glob,logrotate 判重复并整体失败(Launchpad #2152107,Noble→Resolute 已知问题):

systemctl --failed
# ● logrotate.service  loaded failed failed  Rotate log files
journalctl -u logrotate -n 3
# logrotate[52282]: error: cloud-init-base:1 duplicate log entry for /var/log/cloud-init.log

处理是把多余的那份挪走再验证,别直接删,先留着对照:

sudo mv /etc/logrotate.d/cloud-init /etc/logrotate.d/cloud-init.disabled
sudo systemctl start logrotate
sudo logrotate -d /etc/logrotate.conf 2>&1 | grep -i error

顺手清点一下升级留下的备份文件,逐个决定采纳还是丢弃:

sudo find /etc \( -name '*.dpkg-dist' -o -name '*.dpkg-old' \
  -o -name '*.ucf-dist' -o -name '*.ucf-old' \) -print

三、三个「看着正常其实没迁」的组件

PostgreSQL 16 → 18。 升级会把 18 装到 16 旁边,但不迁数据。postgresql-common 只在安装服务器软件包时系统中尚不存在任何集群的情况下才创建主集群,已有 16 集群时不会自动为 18 建一个监听 5433 的空集群。应用继续连 5432,一切看起来正常。用 pg_lsclusters 才能看出两个版本并存。

PHP 8.3 → 8.5。 包装到带版本号的路径,且不改写 Web 服务器配置。nginx vhost 里的 fastcgi_pass unix:/run/php/php8.3-fpm.sock; 指向已不存在的套接字,所有 PHP 请求返回 502,错误日志是 connect() to unix:/run/php/php8.3-fpm.sock failed (2: No such file or directory)。改到 php8.5-fpm.sock,nginx -t 通过后 reload。

MySQL 8.0 → 8.4。 配置里含已移除的选项时 mysqld 直接拒绝启动,最常见的是 default_authentication_plugin。journalctl -u mysql -n 50 会指出未知变量,删掉该行再 systemctl start mysql。

四、SSH 自锁与备用通道

升级到 sshd_config 提示时选 Y,会安装维护者版本并丢弃 PermitRootLogin / PasswordAuthentication / AllowUsers / Port。提前把自定义项移到 /etc/ssh/sshd_config.d/99-local.conf:这个文件 dpkg 不管理,24.04 起 sshd_config 顶部就是 Include /etc/ssh/sshd_config.d/*.conf,OpenSSH 对多数关键字保留读到的第一个值,先读先得,这样无论提示怎么答设置都在。

注意 Port 和 ListenAddress 是多值键,drop-in 里写 Port 是追加而不是替换,原有端口仍然生效。

# /etc/ssh/sshd_config.d/99-local.conf
PermitRootLogin no
PasswordAuthentication no
AllowUsers deploy

升级器会在 1022 起第二个 sshd,提示 To make recovery in case of failure easier, an additional sshd will be started on port '1022',但它不替你开防火墙。提前 sudo ufw allow 1022/tcp,升级完成后 sudo ufw delete allow 1022/tcp。整个过程放在 tmux 里跑,断线后 tmux attach -t upgrade 接着看。

五、重启后的核对清单

lsb_release -a                 # 26.04 / resolute
uname -r
systemctl --failed
journalctl -p err -b --no-pager | head -50
sudo apt update && sudo apt full-upgrade

再补三项针对上面几处的抽查:sysctl 关键参数、ls /etc/apt/sources.list.d/ 有无残留 .disabled、pg_lsclusters 和 nginx 的 fastcgi_pass 指向。

六、什么情况下不该原地升级

24.04 的标准安全维护到 2029-05,本身没有硬期限。升级的理由应当是需要 26.04 提供的内容——PHP 8.5、PostgreSQL 18、MySQL 8.4 LTS、OpenSSH 10.2、7.0 内核——而不是版本号变大。

符合以下任一情况,别原地升:从未打开过服务商控制台(VNC 或串行控制台),一旦 SSH 起不来就没有入口;无法承受约 1 小时停机且没有回滚方案;技术栈依赖的第三方源尚未发布 resolute;机器是两年多前手工配置的,没人清楚上面装了什么。

通常更划算的做法是新建一台 26.04,恢复数据、验证通过后切 DNS,回滚只需要把 DNS 改回去。

复制全文 生成海报 LTS升级 Ubuntu 运维 系统升级 Linux

推荐文章

程序员茄子在线接单