apt 看着什么来判断
一句话总结
apt 的所有判断都来自本地缓存的软件仓库索引。索引过期,apt 看到的就是过时的世界。
为什么需要这些知识
执行 apt-get install nginx 后提示“找不到软件包”,但仓库里明明存在。十有八九是因为没有运行 apt-get update。
apt 安装时不会直接询问仓库。它只查看下载到 /var/lib/apt/lists/ 的索引文件,据此计算依赖;计算完成后,才真正下载 .deb。这种分离让 apt 运行得更快,也造成了“仓库里有,但本地看不到”的问题。
它是如何运作的
一次安装分为四个阶段。
- 读取索引——读取
/etc/apt/sources.list和sources.list.d/*.list所列仓库中的Packages文件。 - 选择候选版本——同一软件包存在于多个仓库时,根据 **pin 优先级(priority)**选择。
apt-cache policy <패키지>会直接展示判断结果。 - 解析依赖——沿着 Depends 和 Recommends 生成安装集合。
- 下载并执行 unpack/configure——交给 dpkg 处理。
关键是学会读取 apt-cache policy 输出。
nginx:
Installed: (none)
Candidate: 1.24.0-2ubuntu7
Version table:
1.24.0-2ubuntu7 500
500 http://archive.ubuntu.com/ubuntu noble/main amd64 Packages
左侧数字是版本,右侧数字是优先级。默认值为 500,有两种方法可以修改相关行为。
- hold(
apt-mark hold)——“不要改动这个软件包”。这是 dpkg 层面的标记,upgrade 时会完全排除该包。撤销时使用apt-mark unhold。 - pin(
/etc/apt/preferences.d/*.pref)——“按这个程度优先选择某来源或版本”。优先级达到 1001 以上时,甚至允许降级。
二者用途不同。hold 表示“停留在当前版本”,pin 表示“在多个仓库中优先选择这一侧”。同时使用企业内部 mirror 和官方仓库的环境,必须设置 pin。
在实际工作中会是什么样
Kubernetes 节点上的 kubelet。 为了控制集群版本,apt-mark hold kubelet kubeadm kubectl 实际上已是标准流程。如果没有 hold,某天不经意运行的 apt-get upgrade 可能只把一个节点升级到新的 minor 版本,随后只有该节点无法启动 Pod。
企业内部 mirror 的背叛。 把内部 mirror 加入 sources.list 后,下载仍来自官方仓库。原因是优先级相同时,apt 会选择版本更高的一方。要强制使用 mirror,必须设置 pin。
先弄清 upgrade 会做什么,再执行
apt-get upgrade 和 apt-get dist-upgrade(现在称为 apt full-upgrade)名称相似,行为却不同,而这种差异会在生产环境中引发事故。
upgrade——把已经安装的软件包升级到新版本,但如果必须删除某些内容或安装新依赖,就会跳过该软件包。 因此较安全,却会出现“已经升级,但仍有几个包停留在原版本”的状态。full-upgrade——必要时会删除或安装软件包。要真正升级 kernel metapackage 这类依赖发生变化的包,需要使用它,但必须亲眼确认哪些内容将被删除。
因此,生产服务器应分两步执行。先使用 -s 进行模拟,查看会发生哪些变化;确认清单中没有陌生内容后,再真正执行。尤其必须每次阅读 The following packages will be REMOVED 这一行。 依赖混乱时,apt 提出的解决方案有时确实是“删除这个服务”。
还要说明 unattended upgrade。自动应用安全更新通常是正确选择,但如果没有规定如何处理需要重启的更新,就会出现两种结果之一:一直不重启,使更新后的库没有生效,漏洞依然存在;或随时重启,导致服务中断。可以使用工具列出哪些进程仍加载旧版库,再在计划窗口中重启。
最后是 apt 与 apt-get 的区别。人工操作时 apt 更方便,但脚本应使用 apt-get。apt 明确声明可能为了便于人类阅读而改变输出格式和行为;一旦格式变化,解析它的脚本就会悄然损坏。
下一次实操要做什么
你将针对 /opt/localrepo 离线仓库进行搜索、安装和删除,亲自设置 hold 与 pin,并观察 apt-cache policy 输出如何变化。