依赖冲突的处理

装插件装到一半,屏幕上跳出一大段 Cannot satisfy dependencies,大概是 OpenWrt 玩家最烦躁的时刻之一。依赖冲突在精简固件、快照版固件和跨版本混装场景下尤其高发。本篇帮你建立一套诊断流程,让你看到报错时知道从哪里入手,而不是盲目敲命令。

依赖冲突的成因

opkg 在安装任何软件包之前,都会读取它的依赖声明,逐项检查系统里是否已经存在满足版本要求的包。只要有一项不满足,安装就会被拒绝。常见的触发原因有四类。

第一类是架构不匹配。.ipk 包是按 CPU 架构编译的,arm 和 mips 的包互不通用,x86_64 的包装到 arm 设备上必然报错。第二类是内核版本不匹配。所有以 kmod- 开头的内核模块都要求内核版本精确到编译哈希,快照版固件的内核每天都在变,今天刷的固件明天装 kmod 包就可能对不上。第三类是软件包版本冲突。系统里已经装了高版本的库,要装的软件声明依赖低版本,opkg 默认不允许降级。第四类是固件精简导致的连锁缺失,比如某些固件作者把 iptables 换成了别的实现,依赖 iptables 的包装上来就找不到目标。

除了这四类,还有一种隐蔽的情形值得单独留意:同一个软件在源里有多个版本共存。OpenWrt 的软件源按发行版组织,23.05.2 和 23.05.3 的仓库是分开的,如果你刷的是 23.05.3,但 distfeeds.conf 不知何时被人改指向了 23.05.2 的目录,opkg 就会从旧仓库取包,轻则装上旧版软件,重则依赖声明和系统现状对不上。所以排障流程之外,养成一个习惯:动不了手就 cat /etc/opkg/distfeeds.conf 看一眼源地址对应的版本号到底对不对。这一小动作能帮你排除掉相当一部分诡异问题。

诊断流程:四步定位

看到报错不要慌,按下面的顺序排查即可。

步骤 操作 目的
一 通读报错全文 分清缺的是库、内核还是别的包
二 opkg info 包名 看依赖清单和适用架构
三 对照系统信息 确认架构与内核版本
四 决策处理方式 换源、找 ipk 或重新规划

第一步读报错要读全:Cannot satisfy the following dependencies 后面列出的每一个依赖项都是线索,比如 kernel (= 6.x.x-1-哈希) 说明是内核版本问题,lib某某 缺失说明是库问题,两类问题的处理思路完全不同。把完整报错截图或复制保存,是排障的第一动作。报错文本不仅在安装时出现,升级和卸载依赖链时同样会触发,处理方式不变。

第二步,用 opkg 确认信息:

ubus call system board
opkg info 你要装的包名
opkg list-installed | grep -i 库名

ubus 输出包含设备代号、发行版本、内核版本和架构,opkg info 列出该包的 Depends 声明,第三条命令检查系统里是否已有对应库及其版本。三者的信息一对,问题基本就清楚了。如果你对架构和内核的关系还比较陌生,建议先读 Linux 系统基础 相关内容建立概念。

依赖报错诊断四步流程

各类报错的处理思路

库文件缺失是最简单的情形。报错里写着 cannot find dependency libmagic 之类,就按提示逐个补齐,opkg 会优先从源里找。如果源里找不到(常见于跨版本安装),可以去软件源网页手动下载对应架构的 .ipk 文件,再用本地路径安装:

# 按报错提示下载对应版本的 ipk
wget https://downloads.openwrt.org/releases/23.05.3/packages/x86_64/packages/keepalived_2.2.7-10_x86_64.ipk

# 手动安装依赖的库
opkg install libmagic
opkg install libnfnetlink0

# 再安装本地包文件
opkg install keepalived_2.2.7-10_x86_64.ipk

注意下载链接的路径和文件名必须与你的架构完全一致,换个设备就要重新找对应版本。

内核版本不匹配要复杂得多。快照版固件没有固定版本号,源里的 kmod 包和设备里的内核很可能差一两个构建版本。官方文档给出两条出路:要么刷回稳定版固件,让源和固件严格对应;要么用 --force-depends 强行安装碰碰运气。但内核模块是在内核空间执行的,版本对不上轻则模块加载失败,重则设备崩溃无法重启,务必三思。

版本冲突(不允许降级)的情形,先问自己是不是真的需要降级。大多数情况下,升级系统里已有的库、然后安装新软件,是比降级更安全的路径。把 opkg 视为只前进不后退的工具,能减少很多麻烦。真要降级时,务必确认这个旧版本库不被系统中其它已安装的软件所依赖,否则会引发更大范围的连锁故障。

force 参数的正确用法

opkg 提供了几个强制参数,用好了是排障利器,用不好是系统杀手,放在一起对比一下。

参数 作用 风险等级
--force-depends 忽略依赖检查直接装 高,内核包几乎必坏
--force-reinstall 强制重装已装过的包 低,用于修复损坏文件
--force-overwrite 覆盖其他包的文件 中,可能造成连锁异常
--force-removal-of-dependent-packages 卸载时连依赖一起删 高,可能误删基础库

一个实际场景:装某个应用时提示与已装包存在文件冲突,而你确认两个包可以共存,这时 --force-overwrite 能解决问题;但如果继续报错,说明设计上就不兼容,强安装只会留下一地烂摊子。判断原则是:只对用户态的纯应用层软件尝试强制参数,涉及内核、防火墙框架、核心网络组件的,一律回到正规路线,要么找匹配版本,要么重新规划。如果实在拿不准,最安全的兜底办法是先做一次完整备份(System → Backup 生成配置压缩包),失败后重刷固件再恢复配置,十几分钟就能满血复活。

实战案例:装一个插件的全过程

把前面的知识串成一个真实场景。某天你想给路由器装个 keepalived 做双机热备,.opkg install keepalived 直接报错,Cannot satisfy the following dependencies for keepalived,后面跟着 kernel (= 6.1.69-1-哈希值) 和 libmagic、libnfnetlink0 几行。

第一步读报错分层:kernel 那一行说明设备内核与源里的包构建时间对不上,两个库缺失是附带结果。第二步 ubus call system board 确认自己的设备是 x86_64,固件版本 23.05.2。第三步去软件源网页按这个版本和架构翻历史包,找到 23.05.2 目录下对应内核哈希的 keepalived 版本。第四步用 wget 把 ipk 拉到本地,先按报错提示补齐两个缺失的库(opkg install libmagic libnfnetlink0),再用本地文件路径安装那个 keepalived.ipk,依赖链终于闭合,安装成功。

整个过程的关键动作只有三个:报错逐条读、系统信息查清楚、按图索骥找匹配版本。反过来如果偷懒直接 --force-depends 强装,用户态的 keepalived 也许能跑,但万一缺的是 kmod 类内核模块,路由器重启起不来就只能重刷固件了。这就是为什么前面一直强调先诊断后动手。关于这种多步骤排障的思路,和 故障排查方法论 里讲的分步定位是同一套逻辑。

从根上避免依赖地狱

依赖冲突治标不如治本。几条经验总结下来:第一,尽量使用稳定版固件而不是快照版,稳定版的软件源和固件内核严格对应,能消除七成以上的依赖问题;第二,刷固件后先把要用的插件集中装完,别隔三差五装一个,拖得越久源里的包更新得越多,越容易对不上;第三,硬件允许的话优先选择自编译,把所有需要的插件直接打包进固件镜像,根本上就没有依赖解析这一步。

两种固件路线对比;稳定版,源包匹配可靠但功能固定;自编译版,一次成型但学习门槛高

自编译的话题展开内容很多,下一篇《自编译简介与固件定制》会专门讲。这里先确立一个意识:路由器上的每一次安装都应该是可预期、可回退的,操作前看一眼报错、留一手备份,比出事之后补救省心得多。备份怎么养成习惯,可以参考 系统运维与备份 里的方法。

延伸阅读

  • 《opkg 包管理与软件源》:软件源的切换与镜像配置是处理依赖问题的前置技能。
  • 《自编译简介与固件定制》:把插件打包进固件,一劳永逸绕开依赖地狱。
  • 《常见故障排查清单》:更多高频故障的速查手册。