中文 English

同一套软件源,一台升到 9.2、一台卡死 9.1:揪出让 PVE 更新“静默失效”的假钥匙

发布时间: 2026-09-13 · 阅读量 --
ProxmoxVE pve 故障排查 运维 Troubleshooting Linux 自动化 Windows 11 macOS Ubuntu 26.04

先说结论

手头有两台 Proxmox VE 9 的物理节点,软件源配置一模一样(同一个国内镜像站的 pve-no-subscription 源)。一台早已升到最新的 9.2.18,另一台却死活只能升到 9.1.9,而且这样一卡就是四个多月,期间没有任何报错提醒。

排查下来,根因既不是网络、也不是源配置,而是那台机器上的 GPG 密钥环文件 /etc/apt/trusted.gpg.d/proxmox-release-trixie.gpg 从一开始就存错了内容——里面装的不是一个"公钥",而是一段"签名",导致 Debian 13 的新一代验签器每次都拒绝刷新 PVE 源的软件包索引,然后安安静静地继续用四个月前的旧索引

修复只需要三步:备份坏文件 → 换上正确的官方公钥 → 重新 apt update。不需要重装系统,不需要动软件源,全程 5 分钟。本文完整复盘追凶过程,并给出 Windows 11 / Ubuntu 26.04 / macOS 26 三个平台的一键修复脚本,以及「人工执行」和「交给 Agent 自动配置」两种用法。

原创封面:一把假钥匙卡住了整台服务器的更新

图 1:原创封面图。同一套软件源,两台机器却活在两个版本世界里,真凶是密钥环里的一把"假钥匙"。

一、问题背景:一次平平无奇的"版本对齐"

我的家庭实验室里跑着两台 PVE 物理节点,下面挂着20来个虚拟机,承担着软路由、数据库、构建机、反向代理等日常角色。两台机器当初都是 PVE 9.0 时代装的系统,软件源统一指向同一个国内镜像站的 pve-no-subscription 免费源——这个配置在之前的文章里也提过,属于家庭实验室的标准操作。

最近打算给两台机器做一次版本对齐,然后顺手把能升的虚拟机都升一遍。结果出现了非常诡异的一幕:

第一反应是"这台机器的源肯定配得不一样"。毕竟两台机器是不同时间装的,人肉配置难免有出入。于是有了下面这场历时不到半小时的追凶。

二、问题表现:同源不同命

先把两台机器的差异可视化一下,方便大家建立直觉:

问题全景:同一个源,两种命运

图 2:问题全景。同样的软件源,节点 B 能看到 9.2.18,节点 A 的天花板却被死死焊在 9.1.9。

2.1 版本差了整整一个系列

两台机器分别执行 pveversion,输出对比一目了然:

pveversion 对比:9.1.9 对 9.2.18

图 3:真实命令输出。节点 A 停在 9.1.9(内核还是 6.17 系列),节点 B 已经是 9.2.18(内核 7.0 系列)。

2.2 apt 认为 9.1.9 就是"最新版"

更关键的是节点 A 上 apt-cache policy pve-manager 的输出:

apt-cache policy 显示候选版本被锁死

图 4:真实命令输出(有删节)。注意 Candidate(候选版本)也是 9.1.9,而且版本列表里连 9.1.10 都不存在。

这里科普一下 apt-cache policy 怎么读:Installed 是已经装了的版本,Candidate 是 apt 认为可以升级到的版本。Candidate 停在 9.1.9,意味着 apt 的"进货清单"里压根没有 9.2 系列的记录——问题不在"装"这个环节,而在"看见"这个环节:apt 根本不知道世界上存在 9.2.18

顺带把常见猜测当场排除:

猜测 验证方式 结论
源配得不一样? diff 两台机器的 sources.listsources.list.d/ 完全相同
镜像站不同步? 两台机器指向同一个镜像站,B 却能看到 9.2.18 排除
版本被 pin 住? apt-cache policy 无 pin 记录,/etc/apt/preferences 干干净净 排除
磁盘满/锁文件? df -h 正常,无 apt 排除

三、问题分析:时间戳是第一案发现场

排除法走到尽头,换个角度:既然 apt"看不见"新版本,那就看看它的"进货清单"最后一次更新是什么时候。apt 的索引缓存都躺在 /var/lib/apt/lists/ 里,文件的时间戳不会说谎:

索引文件时间戳对比:一个停在四月,一个每周新鲜

图 5:真实命令输出(文件名有删节)。节点 A 的 PVE 索引停留在 2026-04-25,节点 B 的则是 9 月刚刷新的。

真相的第一层浮出水面:节点 A 的 PVE 软件包索引,已经四个多月没有刷新成功过了。而 2026 年 4 月下旬,恰好就是 9.1.9 发布的时间点——从那以后 PVE 源对这台机器来说就是"查无此人"。

那么问题来了:索引刷新失败,为什么没人知道?手动跑一次 apt-get update 看看:

apt-get update 的验签失败现场

图 6:真实命令输出。Err: 指向 InRelease 验签失败,缺的钥匙指纹是 24B30F06…,但整条命令只有 W: 警告,“正常"退出。

这段报错的信息量非常大,逐句翻译一下:

最阴险的是最后一行注释里说的事:整个过程只有 W: 开头的警告,没有任何 E: 级别的错误,命令退出一团和气。如果你的 cron 里挂着自动 apt update && apt upgrade,它永远"成功”,永远升不了级,永远不吵不闹。

3.1 那把"缺失的钥匙"到底缺在哪?

apt 核对签名用的公钥,按这台机器的源配置方式,应该住在 /etc/apt/trusted.gpg.d/proxmox-release-trixie.gpg 这个文件里。把两台机器的这个文件并排一验,真凶当场落网:

file 命令对比两台机器的密钥环文件

图 7:真实命令输出。节点 A 的"密钥环"竟然是一段 -----BEGIN PGP SIGNATURE----- 开头的签名块;节点 B 的才是正经的 OpenPGP 公钥。

看到这里整件事就通了:当初给节点 A 配置 PVE 源的时候,下载密钥这一步拿错了文件——把仓库里的"签名文件"当成了"公钥文件"存进了密钥目录。签名块不是公钥,验签器读不懂,自然每次都报"缺钥匙"。而文件的时间戳恰好是 4 月 26 日——也就是说这台机器从 4 月底某次"看起来成功的换源操作"之后,就再也没能从 PVE 源拿到过任何更新。

四、问题根因:一次验签失败,如何静默锁死版本

把整个链条画出来,就是一个"静默降级"的经典案例:

apt 验签流程图

图 8:apt 的一次验签之旅。钥匙环里没有对应公钥时,sqv 拒绝刷新索引,只留下一行警告。

如果觉得 GPG、密钥环这些词太抽象,用一个餐厅的比喻,小学生也能听懂:

餐厅核对公章的比喻

图 9:餐厅比喻。钥匙环 = 官方印鉴样本册;密钥环损坏 = 样本册里塞了废纸,怎么比对都对不上。

对应到技术细节:

  1. 软件源(仓库) 每次更新都会发布一份 InRelease 索引清单,内含全部软件包版本信息,并附带开发商的 GPG 签名——相当于"盖了防伪公章的菜单";
  2. 密钥环(keyring) 是本地存放"官方公章样本"的册子,trusted.gpg.d 目录下的每个文件都会被当作样本收进去;
  3. 验签器(sqv) 每次刷新索引时,拿菜单上的公章和样本册比对,对不上就拒收;
  4. 静默回退:拒收后 apt 不会报错中断,而是继续使用上一次成功时的旧索引——这就是"四个多月无人察觉"的全部秘密。

根因一句话总结:节点 A 的 PVE 密钥环文件内容错误(签名块冒充公钥)→ 每次索引刷新验签失败 → apt 长期使用 4 月的陈旧索引 → 版本天花板被锁死在 9.1.9。

五、如何解决:三步换钥匙,5 分钟搞定

修法的思路极其朴素:把正确的公钥放回密钥环,然后重新刷新索引。关键是"正确的公钥"从哪来。实测两条路都通,按优先级推荐:

5.1 修法一(推荐):用官方密钥包恢复,不依赖任何外网直连

PVE 源里自带一个官方包 proxmox-archive-keyring,里面装着全套现行有效签发公钥。而且 apt 安装软件包时只做校验和验证(用的是本地已有索引),不依赖即时联网到国外官方站——这对访问 download.proxmox.com 困难的网络环境非常友好:

确认官方密钥包里就是缺的那把钥匙

图 10:真实命令输出。官方密钥包内的指纹清单里,赫然躺着报错信息点名要的 24B30F06…

apt-get install --reinstall -y proxmox-archive-keyring
install -m 0644 /usr/share/keyrings/proxmox-archive-keyring.gpg \
                /etc/apt/trusted.gpg.d/proxmox-release-trixie.gpg
apt-get update

5.2 修法二(备选):从同版本健康节点复制

如果局域网里有一台源配置相同的健康机器(我的场景恰好就有),直接把它的密钥文件拷过来也是完全等价的——两台机器用的是同一个源,公钥必然相同:

修复全过程实录

图 11:真实修复过程。备份坏文件 → 从健康节点拷贝正确密钥 → 刷新索引 → 候选版本立刻从 9.1.9 跳到 9.2.18。

两条修法共同的注意点:

顺带一提:Proxmox 官方 Wiki 现在推荐的新姿势是 deb822 格式的 .sources 文件 + /usr/share/keyrings/proxmox-archive-keyring.gpg(在源的 Signed-By: 里显式指明钥匙路径),比 trusted.gpg.d 这种"全局钥匙串"更可控。这次先按最小改动修复,换源迁移留作后续优化。

六、一键自动修复脚本

懒得逐条敲命令?我把上面的逻辑封装成了三平台一键脚本,全部只用 SSH + 官方仓库,不依赖任何第三方服务,并且都内置了"体检 → 备份 → 修复 → 验证"的完整流程,密钥环本来就健康时会自动跳过、不做任何改动:

修复脚本的四步逻辑

图 12:脚本流程。先体检、再备份、后修复、终验证,幂等可重复执行。

方法一:人工一键执行

Ubuntu 26.04 / PVE 节点本地(fix-pve-keyring.sh——既能在 PVE 节点上直接跑,也能传一个 root@目标节点 参数远程修另一台:

#!/usr/bin/env bash
# fix-pve-keyring.sh — 修复 PVE 源验签失败导致的版本天花板
# 本机修复:  sudo bash fix-pve-keyring.sh
# 远程修复:  bash fix-pve-keyring.sh root@<目标节点IP>
set -euo pipefail

PAYLOAD=$(cat <<'EOS'
set -euo pipefail
KEY="/etc/apt/trusted.gpg.d/proxmox-release-trixie.gpg"
PKG="/usr/share/keyrings/proxmox-archive-keyring.gpg"

echo "== [1/5] 修复前版本 =="
pveversion || true
apt-cache policy pve-manager | sed -n '1,4p'

echo "== [2/5] 密钥环体检 =="
if file "$KEY" 2>/dev/null | grep -q "OpenPGP Public Key"; then
  echo "OK:密钥环格式正常。若仍无法升级,请另行排查软件源与网络。"
else
  echo "BAD:密钥环不是有效的公钥文件,需要修复。"
fi

echo "== [3/5] 备份原文件 =="
if [ -e "$KEY" ]; then
  cp -a "$KEY" "$KEY.bak.$(date +%Y%m%d%H%M%S)"
  echo "已备份:$KEY.bak.*"
fi

echo "== [4/5] 用官方密钥包恢复公钥 =="
apt-get install --reinstall -y proxmox-archive-keyring \
  || echo "重装失败,尝试使用本机已有的官方密钥"
if [ ! -s "$PKG" ]; then
  echo "未找到官方密钥文件 $PKG,请参考文章手动方案。"; exit 1
fi
install -m 0644 "$PKG" "$KEY"

echo "== [5/5] 刷新索引并复核 =="
apt-get update
echo "== 修复后版本信息(Candidate 应变为最新版)=="
apt-cache policy pve-manager | sed -n '1,4p'
EOS
)

if [ "${1:-}" != "" ]; then
  ssh "$1" 'bash -s' <<<"$PAYLOAD"
else
  bash -c "$PAYLOAD"
fi

Windows 11(fix-pve-keyring.ps1——用系统自带的 OpenSSH 客户端远程修复,PowerShell 里执行 .\fix-pve-keyring.ps1 -Target "root@<目标节点IP>"

# fix-pve-keyring.ps1 — Windows 11 一键远程修复 PVE 密钥环
param(
  [Parameter(Mandatory = $true)][string]$Target,  # 例如 root@<目标节点IP>
  [int]$Port = 22
)

$payload = @'
set -euo pipefail
KEY="/etc/apt/trusted.gpg.d/proxmox-release-trixie.gpg"
PKG="/usr/share/keyrings/proxmox-archive-keyring.gpg"
echo "== [1/5] 修复前版本 =="
pveversion || true
apt-cache policy pve-manager | sed -n '1,4p'
echo "== [2/5] 密钥环体检 =="
file "$KEY" 2>/dev/null | grep -q "OpenPGP Public Key" \
  && echo "OK:密钥环格式正常" || echo "BAD:密钥环损坏,开始修复"
echo "== [3/5] 备份原文件 =="
[ -e "$KEY" ] && cp -a "$KEY" "$KEY.bak.$(date +%Y%m%d%H%M%S)"
echo "== [4/5] 用官方密钥包恢复公钥 =="
apt-get install --reinstall -y proxmox-archive-keyring \
  || echo "重装失败,尝试使用本机已有的官方密钥"
[ -s "$PKG" ] || { echo "未找到 $PKG"; exit 1; }
install -m 0644 "$PKG" "$KEY"
echo "== [5/5] 刷新索引并复核 =="
apt-get update
apt-cache policy pve-manager | sed -n '1,4p'
'@

$payload | ssh -p $Port $Target "bash -s"
if ($LASTEXITCODE -eq 0) {
  Write-Host "修复流程执行完成,请确认上方 Candidate 已更新到最新版。" -ForegroundColor Green
} else {
  Write-Host "执行出错,请检查 SSH 连接与上方输出。" -ForegroundColor Red
}

macOS 26(fix-pve-keyring-macos.sh——终端里执行 bash fix-pve-keyring-macos.sh root@<目标节点IP>

#!/usr/bin/env zsh
# fix-pve-keyring-macos.sh — macOS 一键远程修复 PVE 密钥环
set -euo pipefail
TARGET="${1:?[用法] bash fix-pve-keyring-macos.sh root@<目标节点IP>}"

ssh "$TARGET" 'bash -s' <<'EOS'
set -euo pipefail
KEY="/etc/apt/trusted.gpg.d/proxmox-release-trixie.gpg"
PKG="/usr/share/keyrings/proxmox-archive-keyring.gpg"
echo "== [1/5] 修复前版本 =="
pveversion || true
apt-cache policy pve-manager | sed -n '1,4p'
echo "== [2/5] 密钥环体检 =="
file "$KEY" 2>/dev/null | grep -q "OpenPGP Public Key" \
  && echo "OK:密钥环格式正常" || echo "BAD:密钥环损坏,开始修复"
echo "== [3/5] 备份原文件 =="
[ -e "$KEY" ] && cp -a "$KEY" "$KEY.bak.$(date +%Y%m%d%H%M%S)"
echo "== [4/5] 用官方密钥包恢复公钥 =="
apt-get install --reinstall -y proxmox-archive-keyring \
  || echo "重装失败,尝试使用本机已有的官方密钥"
[ -s "$PKG" ] || { echo "未找到 $PKG"; exit 1; }
install -m 0644 "$PKG" "$KEY"
echo "== [5/5] 刷新索引并复核 =="
apt-get update
apt-cache policy pve-manager | sed -n '1,4p'
EOS

三个脚本执行完,只要最后的 Candidate 跳到了 9.2.x(或你阅读本文时的最新版),修复即宣告成功。

方法二:Agent 自动配置

更省事的做法是把整个问题直接丢给 AI Agent(ZCode、Claude Code 等均可),你只需要提供 SSH 连接方式。下面这段提示词可以直接复制:

请帮我修复一台 Proxmox VE 服务器的"无法升级到最新版本"问题,大概率根因是
/etc/apt/trusted.gpg.d/proxmox-release-trixie.gpg 密钥环文件损坏(例如内容是
PGP 签名块而不是公钥),导致 apt 验签失败、索引长期不刷新。请按以下步骤操作:

1. 通过 SSH 登录该节点(连接方式见会话上下文/我会另行提供);
2. 记录现状:执行 pveversion 和 apt-cache policy pve-manager;
3. 用 file 命令检查上述密钥文件,若不是 "OpenPGP Public Key",
   先用 cp -a 备份原文件到 /root/ 下;
4. 执行 apt-get install --reinstall -y proxmox-archive-keyring,
   然后将 /usr/share/keyrings/proxmox-archive-keyring.gpg 以 0644 权限
   复制为上述密钥路径;若重装失败但本机已有该文件,直接复制即可;
5. 执行 apt-get update,确认输出中没有 Err:、Missing key、unsupported filetype;
6. 输出 apt-cache policy pve-manager 的 Candidate 作为修复证据。

约束:不要执行 dist-upgrade/full-upgrade,不要重启服务器,不要修改软件源地址。

Agent 跑完后,重点核对它给出的两点证据:apt-get updateErr: 警告、Candidate 已更新到最新版本。

七、举一反三:怎么避免再踩一次

五条防复发清单

图 13:防复发清单。核心思想只有一个——别相信"没报错"就是"没问题"。

这次事件里最值得记住的教训,其实不是 GPG 本身,而是"静默降级"这种故障形态:

  1. 升级前先跑"体检三连"apt-get update 盯一眼有没有 Err:/W:apt-cache policy <包名> 看候选版本;ls -l /var/lib/apt/lists/ 看索引时间戳。三步加起来不到 30 秒,能识破 90% 的"假最新"。
  2. 自动化脚本别只看退出码apt-get update 在仓库验签失败时依然"成功"退出。脚本里要加 apt-get update 2>&1 | grep -E '^(Err|W):' && exit 1 这类断言,把警告升级为失败。
  3. 密钥文件认准官方来源:重装 proxmox-archive-keyring 包是最稳的获取方式;从网页上手工下载密钥时,保存后务必用 file 确认是 OpenPGP Public Key 而不是其他什么东西——本次事故的直接起因就是存错了文件。
  4. 多机环境互为参照:同集群的机器出现版本差、行为差,第一反应就应该是 diff 两台机器的源配置和密钥目录,往往一眼见底。

八、Q&A

Q1:为什么 Web UI 的仓库页面看起来一切正常,问题却藏了四个多月?

A:一方面,apt-get update 遇到验签失败只输出 W: 警告,命令照常"成功",挂在 cron 里的自动更新永远发现不了;另一方面,PVE Web UI 的「节点 → 更新 → 仓库」页面其实自带仓库体检、能标出签名异常,但大多数人(包括当时的我)很少主动点开这一页。建议把它加进日常巡检清单。

Q2:在源上加 [trusted=yes] 岂不是一步到位?

A:那是把防伪公章检查整个拆掉,中间人塞什么包你都照单全收。生产环境绝对不要这么做。

Q3:换个镜像站能不能解决?

A:不能。密钥环坏在本地,换一百个镜像站,验签照样失败。这次两台机器用的是同一个镜像站,一台好一台坏就是明证。

Q4:9.1 → 9.2 需要按官方迁移文档一步步操作吗?

A:不需要。同一个大版本内的小版本更新,apt full-upgrade 即可;只有跨大版本(比如 8 → 9)才需要严格按照官方升级手册执行。

Q5:修复之后可以立刻升级吗?

A:技术上随时可以,但建议:先给重要虚拟机做备份/快照,确认集群状态正常,然后挑一个维护窗口执行 apt full-upgrade,重启后新内核才生效。我的节点 A 上跑着软路由和数据库,就是留着深夜窗口才动手的。

Q6:Debian 12 / PVE 8 也会有这个问题吗?

A:会的,只是报错长相不同。老一代验签器 gpgv 会报 NO_PUBKEY,原理一模一样:密钥环坏了 → 索引停更 → 版本被锁死。本文的修复思路(恢复官方密钥 + 刷新索引)同样适用,只是密钥文件名换成对应的 bookworm 版本。

参考资料

本文阅读量 --