换源这件事看着简单,真要在海外机器上跑通,前提是你先有一台网络能顺畅访问 Debian / Ubuntu 官方仓库的机器,节点和线路选得好,apt update 基本不卡;zgovps(全球云服务器) 在洛杉矶、荷兰诺德韦克、日本大阪都有节点,装完系统先把源调通,比事后慢慢折腾省事得多。
上周给一台新开的海外 VPS 装 Docker,第一步就卡住了。执行 apt-get update,屏幕上刷了七八行 "Get:1 ... 0 B/0 B",然后就停在那儿不动,等了整整六分钟才报错退出。我以为是网络问题, 换了个节点重装还是一样。后来才反应过来,这台机器的 sources.list 还是系统默认的官方源, 而默认源对国内和部分海外中转线路来说,慢到离谱,慢到 apt 自己会超时重试, 重试三次之后才抛出那句看起来毫不相关的 "Release file ... is not valid yet"。
我最后把 sources.list 改错了两处,前后折腾了将近两个小时。今天就把这两个坑完整写下来, 顺便把整条换源的判断流程捋一遍,方便后面再遇到类似情况能少走弯路。
一、动手前先搞清楚你的发行版用的是哪种源格式

这是最容易忽略的一步。Debian 12 及以后的版本,已经不把源写在 /etc/apt/sources.list 里了, 而是拆成了 /etc/apt/sources.list.d/ 目录下的 .sources 文件(deb822 格式)。 Ubuntu 24.04 也跟着换了。而老的 Debian 11、Ubuntu 22.04 仍然是传统的一行行 deb 文本格式。 两种格式语法完全不同,混着改只会得到一堆看不懂的解析报错。
判断方法很简单:先看文件长什么样。
传统格式大概是这样,一行一个源:
deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security bookworm-security main contrib
新的 deb822 格式是一个带字段名的段落,Types、URIs、Suites、Components 各占一行, 末尾用空行分段。如果你在 Ubuntu 24.04 上照着老教程去改 /etc/apt/sources.list, 会发现改完完全没效果因为真正生效的是 deb822 那个文件。
二、我改错的第一处:把 Suite 和 Component 混着写了
我第一次换源,是直接把官方源那一行复制下来,只替换了域名。改完执行 apt-get update, 报错说 "E: Malformed entry" 之类的语法错误。我当时盯着看了半天, 后来把新源写成下面这样才明白问题出在哪:
deb http://mirrors.example.com/debian bookworm main
问题在于我顺手把 main contrib non-free 里的 contrib 和 non-free 挪到了另一个位置,
还把 security 仓库的 suite 名写成了 bookworm-security-security,这种双写是自己加的。
Debian 的 security 仓库 suite 就是 bookworm-security,写成 bookworm-security-security 必然找不到。
这类错误在报错里往往只给一句笼统的 "Malformed entry",不告诉你是哪个字段错了,
只能一行行比对 deb822 或者 deb 的语法规范来定位。
另一个相关的坑是 bookworm-security 和 bookworm-updates 这两个仓库不能省。
只写 bookworm 主仓库的话,系统装不上安全更新,apt upgrade 会提示一堆包被保留,
看起来像 apt 坏了,其实是源不全。
三、我改错的第二处:换域名时把签名密钥也一起换了
第二次错误更隐蔽。我嫌 apt-key 那套老写法麻烦,直接把 trusted=yes 加到了源那一行,
想着省事。结果 apt-get update 报了 GPG 相关的错,提示仓库签名不对。
这里的原理是:每个发行版的软件仓库都有自己的一套 GPG 公钥,apt 会拿这个公钥去校验 Packages 索引的签名,校验通过才认为索引可信。换了源地址之后,如果新源的签名密钥 不在你机器的 keyring 里,apt 就没法验证,只能报 "NO_PUBKEY" 或者签名不匹配。

正规做法是把对应的公钥文件放到 /usr/share/keyrings/ 下,
然后在源那一行用 [signed-by=/usr/share/keyrings/xxx.gpg] 指明用哪把钥匙校验,
这样既不污染全局信任,也不依赖已经废弃的 apt-key 命令。
我一开始图省事加 trusted=yes,其实是在把校验这一层直接关掉短期能跑,长期很危险,
因为你从此不再知道这个源里的包是不是被改过。
四、正确的换源顺序,和验证方法
现在我的固定动作是这样的一套:先备份源文件,改完先刷新索引而不是直接装东西, 用两条命令确认没问题,再往下走。
第一步,备份:cp /etc/apt/sources.list /etc/apt/sources.list.bak,
有 deb822 文件的机器记得把 .sources 也一起备份。
第二步,先测速再决定用哪个源。直接用 curl 打一下索引文件的响应时间和实际速度, 比 apt update 报错快得多:
curl -o /dev/null -w 'speed:%{speed_download} time:%{time_total}\n' \
http://mirrors.example.com/debian/dists/bookworm/InRelease
第三步,改完刷新索引,只用 update,别急着 install:sudo apt-get update。
如果这一步干净利落地跑完,说明源本身没问题。
第四步,确认 apt 真的认识这个源、并且选中了它:apt-cache policy,
看输出里各仓库的优先级和 hit 数量。如果显示 "0 packages" 或者命中的是另一个老源,
说明配置根本没生效,多半是改错了文件位置或者格式不对。
第五步,看有没有被降级的包:apt-get upgrade --dry-run,
确认输出里没有 "The following packages will be DOWNGRADED",有的话先查清楚再继续。
这一步是我从一次误操作里学来的有一次源里的版本比本地还旧,
apt 直接把新装的包降级回去,业务代码当场挂了。
五、什么情况下根本不该换源

最后说个容易被忽略的前提:如果你的机器本身就在离官方仓库很近的机房, 网络本来就通畅,那换源反而是折腾。国内带宽出国的链路质量差异极大, 同一个源在不同时间、不同运营商、不同线路上表现能差好几倍。 我一般按上面那个 curl 命令实测一轮,谁快用谁,而不是照抄别人的推荐列表。
另外换源前想清楚一件事:源只是下载地址,优先级(priority)配置、 是否允许降级、backports 仓库要不要开,这些都得单独管。 换源不等于把系统更新到最新,只等于换了个地方下载而已。 真正决定系统状态的还是 apt 的策略配置,别把两件事混在一起。
回头看,这次折腾了两个小时,核心其实就一句话:换源改的是地址,apt 校验的是密钥, 两者都要写对,中间还要用 update 和 dry-run 各验证一步。 下次再碰到 apt update 卡住,先别急着怀疑网络,先去看看 sources.list 到底长什么样。


喜欢
顶
难过
囧
围观
无聊



