apt 换源与 pinning:Ubuntu 24.04 上源越多,越要管住优先级

apt 换源与 pinning:Ubuntu 24.04 上源越多,越要管住优先级

APTUbuntu 24.04软件源pinning运维
📶 入门 🧩 APT · Ubuntu

一台新开的 Ubuntu 24.04,apt install 卡在 30KB/s,第一反应是搜个换源教程,sed 一行替换完事。 trouble 从下一个动作开始:装 Docker 加了官方源,装 Nginx 加了第三方源,几个月后某次 apt upgrade,系统自带的包被第三方仓库的新版本顶掉,依赖连锁崩了半屏。换源本身不出事,出事的是多个源混用之后,你没管住谁优先。

这篇讲两件事:Ubuntu 24.04 的源文件长什么样、怎么安全地换;以及当机器上源多起来之后,怎么用 pinning(优先级钉扎)定死每个源的话语权。自动安全补丁是另一条线,见 unattended-upgrades 配置详解。

源文件搬了家:deb822 格式上线

Ubuntu 24.04(代号 noble)把默认源从老式 /etc/apt/sources.list 挪到了 /etc/apt/sources.list.d/ubuntu.sources,用的是 deb822 格式。很多老教程还在教 sed 改 sources.list,改完发现毫无效果——文件根本不在那。

先看现在机器上真实生效的源:

cat /etc/apt/sources.list.d/ubuntu.sources
Types: deb
URIs: http://archive.ubuntu.com/ubuntu/
Suites: noble noble-updates noble-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

Types: deb
URIs: http://security.ubuntu.com/ubuntu/
Suites: noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

deb822 把每行前缀(deb http:// ...)拆成了字段:URIs 是镜像地址,Suites 是发行代号加更新通道,Components 是组件分区,Signed-By 指定验签密钥。结构化之后可读性强了,改起来也更精确——你只需替换 URIs 这两行。

换源实操:一条 sed 加验证

把官方源换成国内镜像(以阿里云为例,清华 TUNA、中科大同理,只需换域名):

sudo cp /etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources.bak
sudo sed -i 's|http://archive.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g; s|http://security.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g' /etc/apt/sources.list.d/ubuntu.sources

备份先行,一条命令同时替换常规源和 security 源——后者常被漏掉,漏掉的结果是安全补丁仍走原地址,换源换了个寂寞。刷新并验证:

sudo apt update
apt policy | head -20

apt policy 列出当前所有源及其优先级,输出末尾的 Get X 行数与执行时间能直观对比换源前后的速度。云端机器注意一点:部分云厂商的镜像通过内网域名走,内网流量免费,换成公网镜像反而多走计费流量,换之前看一眼 apt policy 里原本指向哪里。

还有一类情况:手里有几个不同镜像,不知道选哪个。先对候选域名各跑一次 curl -o /dev/null -w '%{speed_download}' <镜像地址>/dists/noble/Release 拿下载速度,取最快的一个写进 URIs。几分钟的测试比事后反复换源省时间。

优先级规则:apt 怎么决定用哪个源

源一多,apt 每次安装都要做选择题。规则只有两条:

  • 每个源有一个整数优先级,数字大的赢;
  • 优先级相同,版本号新的赢。

Ubuntu 默认优先级 500,backports 源例外,只有 100。所以 apt upgrade 永远不会自动把你的包升到 backports 版本——那是特意压低的。看实际数字:

apt policy nginx
nginx:
  Installed: 1.24.0-2ubuntu7
  Candidate: 1.24.0-2ubuntu7
  Version table:
     1.26.0-3ubuntu1 500
        500 https://mirrors.aliyun.com/ubuntu noble/main amd64 Packages

Installed 是已装版本,Candidate 是下次 apt install nginx 会装上的版本,后面每行是候选版本加它所在源的优先级。这一条输出就是所有争议的判决书——怀疑「装错源了」,先看它。

pinning:三种写法,每个源各有话语权

优先级可以在 /etc/apt/preferences.d/ 下用配置文件改写,文件名随意,按字母序生效。三种典型场景:

场景一:只从第三方源拿一个包,其余全挡掉。 加了某软件官方源后,它仓库里的同名包(比如依赖库)可能版本更新,apt upgrade 会拿它们顶掉系统包。钉死它:

# /etc/apt/preferences.d/99-limit-thirdparty
Package: *
Pin: origin deb.example-software.com
Pin-Priority: 1

Package: example-software
Pin: origin deb.example-software.com
Pin-Priority: 500

Pin: origin 后面写仓库域名。第一条把该源所有包压到优先级 1——这个数字的含义是「永不自动安装」,即使版本最新;第二条给目标软件放行回 500。效果:只有你点名的那一个包能从这装,它仓库里其余的一百多个包全部静默。

场景二:按需用 backports 的新版本,但不让它自动升级。 一次性安装用 -t 参数,不留配置文件:

sudo apt install -t noble-backports linux-image-generic

之后 apt upgrade 不会跟进 backports 的新版本。想让某个包长期跟踪 backports,才用 pin 文件把优先级提到 400 以上:

# /etc/apt/preferences.d/99-backports-nginx
Package: nginx
Pin: release a=noble-backports
Pin-Priority: 900

Pin: release 可以按仓库属性筛选:a= 归档名、o= 来源组织、n= 代号、c= 组件分区。可选值都能在 apt policy 的每行输出里直接抄。

场景三:紧急锁版本。 某个包的新版本有问题,想停在当前版本:

# /etc/apt/preferences.d/99-hold-nginx
Package: nginx
Pin: version 1.24.0-2ubuntu7
Pin-Priority: -1

优先级 -1 表示禁止安装该版本。临时锁版本更轻量的做法是 sudo apt-mark hold nginx,解除用 unhold,不落配置文件。

改完必做验证,两步:

sudo apt update
apt policy nginx

输出里 Candidate 的版本和来源变了,说明 pin 生效;没变,检查文件名是否带了多余后缀(deb822 时代 preferences.d 下 .save 之类文件会被忽略,但编辑器自动备份文件可能干扰判断)。

一个反直觉的坑:别碰 Default-Release

搜 pinning 教程常会遇到一种写法:APT::Default-Release "noble",意思是默认锁定当前发行版。看着很稳,实际会出事——它会压低 security 源的优先级,apt upgrade 不再自动安装安全补丁,你的机器静默裸奔。锁定需求一律用上面场景一的写法:把第三方源降到 1,让官方源和安全源保持默认 500,两者不冲突。

常见问题

Q:换了镜像源之后 apt update 报 Release 文件签名错误? 镜像同步中断或被劫持时会出现。先换一个镜像域名重试;仍报错则 apt update 输出里的 NO_PUBKEY 提示缺密钥,用 sudo apt-key adv --recv-keys <KEYID> 之外的新方式处理:确认 Signed-By 路径指向的密钥文件存在。官方镜像一般不会出这个问题,优先怀疑镜像本身。

Q:sources.list.d 里一堆 .list 和 .sources 文件,哪个生效? 都生效,格式都认,apt 按文件名顺序合并所有源。/etc/apt/sources.list 老文件同样有效。用 apt policy 不带参数的完整输出核对:每个源一行,一目了然。删源就是删对应文件,删完 apt update。

Q:pin 文件写了没生效? 三个高频原因:Pin: origin 写的是组织名(如 o=Ubuntu 那种字段)而不是域名——origin 后面必须抄 apt policy 输出里 origin 开头那行的域名;文件后缀带了 .conf 或 .bak;改完没跑 apt update。逐条排查,apt policy 看结果。

Q:第三方源卸载后,已经从它装上的包怎么办? 包还在,apt 不受影响,只是后续更新没有来源,会停在当前版本。用 apt policy <包名> 确认 Candidate 是否还指向已删除的源,指向则从官方源重装同版本回正轨。

Q:apt 和 apt-get 用哪个? 脚本里用 apt-get,接口稳定、输出格式不变。交互终端用 apt,有进度条和 apt list --upgradable 这类友好子命令。行为差异在依赖解析上可以忽略,别混着用同一个操作就行。

收尾

换源是三分钟的事,管住优先级才是一台机器长期不出事的关键。最小清单:换源时 URIs 两行一起换并保留备份;每加一个第三方源,同步写一个场景一的 pin 文件把它压到 1、只放行点名要的包;每改一次 preferences.d,跑 apt update 再用 apt policy 核对 Candidate。源越多,这套纪律越值钱。