ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux软件包管理全解析:从deb/rpm到apt/yum与依赖处理

Linux软件包管理全解析:从deb/rpm到apt/yum与依赖处理 今天这篇日志我想把Linux软件包管理这件事从头到尾捋一遍。起因很简单有朋友问我apt和yum到底有什么区别deb和rpm又是干什么的snap和AppImage到底该不该用。我一时竟然没法用一两句话讲清楚因为这套体系看似只是“装软件的工具”背后牵扯到压缩格式、依赖解析、仓库策略、系统安全边界甚至不同发行版走过的路都不一样。所以我花了一整天时间结合自己这些年踩过的坑把软件包管理体系完整整理成一份学习日志。你如果刚接触Linux这份内容能帮你建立完整的框架如果你已经用了一两年Linux里面关于依赖冲突和故障排查的部分应该也能给你一些新思路。1. 软件包管理在Linux里的位置1.1 没有包管理器Linux会变成什么样子先设想一个极端场景你下载了一个软件的源码压缩包解压之后发现它依赖三个库写代码的人说“你们自己去装依赖”。等你好不容易把三个库的源码也下下来了又发现它们各自又依赖别的库。如果你用的还是那种源码方式挨个编译光是解决依赖就能耗掉一个晚上。相信我这是早期Linux用户真实经历过的事情而且不止一次。软件包管理体系就是来终结这个问题的。它把“要装的文件有哪些”“需要依赖谁”“安装到哪个目录”“卸载时要清理什么”这些信息全部写进一个标准化的包里再由一个统一工具去处理安装、升级、卸载、查询、校验。这样用户安装软件就不再是“把一堆文件复制到某个位置”这么原始而是跟数据库打交道系统知道装了什么、装到哪了、有没有损坏、跟谁冲突。可以这么理解包管理器之于Linux就像应用商店之于手机。手机上的App有统一审核、统一打包格式、统一更新入口Linux里的软件包也有统一的格式、统一的仓库源、统一的安装命令。区别在于Linux这边的自主性高得多你可以自己加仓库、自己打本地包、自己锁定版本甚至可以绕开包管理器手动装东西。1.2 两大体系的基本框架deb与rpm目前主流的Linux发行版软件包管理基本分成两大家族。以Debian、Ubuntu为代表的一派底层是deb格式上层用apt或aptitude以Red Hat、CentOS、Fedora为代表的另一派底层是rpm格式上层用yum或dnf。注意这里有一个很关键的概念deb和rpm是底层包的“格式”apt和yum是上层的“包管理工具”。格式决定了包内部怎么写元信息和文件清单工具决定了你怎么调用仓库、处理依赖、执行安装。还有一条更细的路线Arch Linux用的是pacmanopenSUSE用zypper它们也都有自己的一套包格式或工具组合。但从学习价值来说先吃透deb和rpm两条主路线其他的都能望文生义。因为底层核心逻辑——元信息、文件清单、依赖声明、脚本钩子、事务处理——在任何一个体系里都是相通的。2. 底层包格式先把deb和rpm拆开看2.1 deb包内部长什么样如果你下载过某个软件提供的.deb文件可以试着在终端里这样看它的内容dpkg-deb -I firefox_xxx.deb dpkg-deb -c firefox_xxx.deb第一条命令显示包的元信息Package、Version、Architecture、Depends等第二条命令列出包里要释放的文件清单。一个典型的deb包内部其实是个ar归档里面包含debian-binary、control.tar.*和data.tar.*三个部分。control那个部分里装的就是依赖、描述、维护者、安装大小等信息data部分则是实际释放到系统里的文件。平时我们操作单个deb文件用得最多的命令# 安装 sudo dpkg -i xxx.deb # 查看已安装包的详细信息 dpkg -s 包名 # 查看某个文件是谁安装的 dpkg -S /usr/bin/firefox # 卸载但不删配置 dpkg -r 包名 # 彻底卸载 dpkg -P 包名我第一次用dpkg手动装一个.deb时系统提示缺少依赖我又不会用apt去自动补只能自己一个个找。后来才明白dpkg只是最底层的那把螺丝刀它只负责“把这个包里的东西按清单放到对应位置再跑一下配置脚本”不负责替你去网上拉别的包。所以日常环境中手动dpkg安装通常只用于补装个别离线包。如果缺依赖更好的做法是先试试apt install ./xxx.deb让apt把依赖一起解决掉。2.2 rpm包与rpm命令的对应操作rpm包对应的是Red Hat这一脉常用的本地包操作命令# 安装 sudo rpm -ivh xxx.rpm # 查看包信息 rpm -qi 包名 # 列出包安装的文件 rpm -ql 包名 # 查某个文件由哪个包提供 rpm -qf /usr/bin/命令 # 卸载 sudo rpm -e 包名rpm的底层能力跟dpkg非常像安装、查询、卸载、校验它都做得了但它也不会主动跨仓库解析依赖。假如你拿到一个rpm文件直接用rpm -ivh装经常会碰到“libxxx.so.1()(64bit) is needed by xxx”的错误这就是系统告诉你依赖缺失。这个报错格式当年劝退过不少新手。不过rpm比dpkg多了一个很出名的能力校验。rpm -V 包名能告诉你这个包释放出来的文件里哪些被改动过。安全检查的时候这个功能非常实用比如怀疑某个系统二进制被替换了验一下就知道。2.3 两种格式的直观对照对比项debrpm主要用于Debian、Ubuntu、Deepin等RHEL、CentOS、Rocky、Fedora、openEuler等底层命令dpkg、dpkg-debrpm、rpmbuild上层包管理apt、aptitudeyum、dnf、zypper元信息区control.tar包头部header依赖标记Depends、Recommends、SuggestsRequires、Recommends文件校验机制md5sums清单rpm -V 校验本地安装依赖体验apt install ./x.deb可自动补依赖dnf install ./x.rpm可自动补依赖从左边到右边我最大的感受是两者核心思想没有任何本质区别都是“元信息文件载荷脚本”三合一的打包方案。区别在于各自的元数据写法和生态规则一旦你跨体系使用记忆难免会串台。3. 上层管理工具apt与yum/dnf怎么帮你做决策3.1 apt仓库、索引、依赖一条龙apt是debian家族里最主流的上层工具。它做的事情可以分成三步读取源、拉取索引、解析事务。源就是仓库。Ubuntu/Debian的仓库地址写在 /etc/apt/sources.list 以及 /etc/apt/sources.list.d/ 下的文件里。一行源的典型格式deb http://archive.ubuntu.com/ubuntu/ focal main restricted universe multiverse这行看着简单拆开看是仓库里的包格式是deb访问地址是那个URL发行版代号是focal后面四个是组件分类。组件的意思就是把这个发行版的软件按自由程度和维护方分成几个仓库main是核心、universe是社区维护、restricted是受限支持、multiverse是可能有法律或专利问题的软件。这里有一个新手最常踩的坑修改了源之后不执行sudo apt update直接执行apt install结果系统说你包的版本不存在。因为apt只是读了你本地的索引缓存这个缓存还停在旧状态。update就是要重新拉取仓库里的Packages.gz索引文件让系统知道你有哪些新版本、什么包新出现了。相当于你新认识了一个资料库得先拿一份目录不然没法在里面找书。日常命令最常用的sudo apt update # 刷新索引 sudo apt upgrade # 升级所有可升级包 sudo apt install 包名 # 安装 sudo apt remove 包名 # 卸载 sudo apt autoremove # 清理孤立依赖 sudo apt purge 包名 # 卸载并删配置 apt-cache search 关键词 # 搜索 apt-cache policy 包名 # 查看候选版本和当前版本 apt depends 包名 # 查看依赖关系我特别建议新手养成一个习惯安装的时候加上--no-install-recommends。推荐依赖Recommends经常会把一大堆无关的东西带进来有些甚至是视频播放器、字体、办公组件。当年我在一台云服务器上执行apt安装某工具结果它给我装了GUI相关的一堆库浪费内存又难清理。加上这个参数装出来的环境干净得多。3.2 yum与dnf事务历史和组包管理Red Hat系的yum已经逐渐被dnf取代但底层源配置思路不变。仓库配置文件在 /etc/yum.repos.d/*.repo里面分几个小节每个小节对应一个仓库。典型结构[baseos] nameBaseOS baseurlhttps://mirrors.example.com/rocky/9/BaseOS/x86_64/os/ enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-Rocky-9这里的gpgcheck是签名校验开关老系统上有时因为仓库配置不严谨或者密钥过期会出现安装时校验失败的问题。这时候很多人会直接设gpgcheck0绕过去我强烈不建议这么干因为签名校验是防止软件包在传输或者源站被篡改的重要防线。正确做法是更新一下仓库的GPG密钥比如rpm --import对应密钥文件。dnf相对yum有一个巨大的优势事务历史。命令sudo dnf history sudo dnf history undo 事务编号这两条组合拳非常厉害。你昨天装了一个包连带更新了一堆依赖今天系统某个服务起不来了你不需要自己一个个猜改了什么。用history找出昨天的事务直接undo掉就能回滚整个变更。这本质上就是一个系统级的“撤销按钮”。Ubuntu那边的apt也有类似机制但默认的apt还不提供这么方便的history回滚一般要额外装snapshots或手动记录。3.3 为什么同一个Linux世界要分两套不少刚接触Linux的人都会问为什么不能全世界统一用一套包管理体系这里面有历史原因也有现实利益。因为包管理不只是技术它决定了发行版如何控制软件生态、如何发布补丁、如何保证系统一致性。Debian和Red Hat各自走了几十年私有工具链、打包规范、升级策略早就深度绑定在各自发行版的骨髓里。强行统一等于让两边推倒重来。与其纠结哪边更好不如两边都学反正底层逻辑是一样的多掌握一套只是多记几个命令和配置格式而已。4. 依赖解决机制与版本锁定的门道4.1 依赖是怎么被解析出来的先看deb包里的Depends字段它可能长这样Depends: libc6 ( 2.35), libssl3 ( 3.0.0), init-system-helpers ( 1.18~)这里每个依赖都可能有版本下限、上限条件。apt要做的就是从仓库里找到满足这些条件的包版本。这个过程听起来简单实际复杂在“包A依赖BB依赖CC又反过来对A有约束”这种连环套里。处理这种场景需要一定的算法和策略并不只是挑最新版本那么无脑。还有一个很容易忽略的概念虚拟包。Linux的共享库在打包时经常把库版本作为虚拟包名比如libssl3。实际提供这个库的文件可能叫libssl3.so它通过包的Provides字段对外宣称“我能提供libssl3”。这样其他包的依赖就可以写Depends: libssl3不需要关心具体是哪个发行版、哪个软件包名字。我自己在排查依赖问题时常用命令# 看这个包依赖谁 apt depends 包名 # 看这个包被谁依赖 apt-cache rdepends 包名 # 看虚拟包由谁提供 apt-cache show 包名用这些命令把依赖关系捋顺很多莫名其妙的“包无法安装”都能找到原因。4.2 锁定版本防止被自动升级带跑服务器或者生产环境里最怕的不是你不升级而是某些特定软件被意外升级之后行为变化。Kernel、数据库、编译器等都是敏感区。在apt体系里锁定版本用apt-marksudo apt-mark hold 包名 sudo apt-mark showhold sudo apt-mark unhold 包名hold住之后任何apt upgrade、apt dist-upgrade都不会动这个包。注意这只影响上层工具如果你手动dpkg -i装更高版本它拦不住。dnf体系里可以这样加启动参数sudo dnf install --exclude内核包 其他包也可以写进 /etc/dnf/dnf.conf 的 [main] 段excludekernel*如果用的还是yum可以考虑装yum-versionlock插件。我的体会是锁定版本这个动作本身不复杂麻烦的是你得记得自己锁过哪些包。所以每锁定一个包我都会在备注文件里记一行“包名 锁定原因 期望放开的日期”省得三个月后忘了为什么系统里这个包一直不升级。5. 源码编译安装老派但不可回避5.1 经典三步走尽管现在大多数软件都能通过仓库装但总有例外仓库里的版本太旧、软件只有源码包、你还需要定制编译参数。这时候就绕不开源码安装。经典流程# 1. 解压并进入 tar -xzf 软件.tar.gz cd 软件目录 # 2. 检测环境并生成Makefile ./configure --prefix/usr/local # 3. 编译 make -j$(nproc) # 4. 安装 sudo make install第一步的configure会检查编译器、检查依赖库、检查平台特性缺什么它会直接告诉你。第二步的-j$(nproc)是利用CPU全部核心并行编译能明显缩短编译时间。第三步的“install”默认可能会把文件散到 /usr/local/bin、/usr/local/lib 这几个目录。有一回我给一台新机器编译Nginx忘了加--prefix结果安装脚本把文件放到了默认的 /usr/local/nginx 下面。后来我要卸载找不到一个集中的install记录只能靠记忆自己删文件。这个经历之后我只要用源码安装一定是明确指定prefix和清晰的configure选项然后保存一份当时的configure命令方便日后识别。5.2 源码安装与包管理的冲突源码编译最大的隐患是它脱离包管理器视野。你编译了一个新版本库放到 /usr/local/lib然后某个仓库包需要的是系统库里那个旧版本两者就可能打架。老手都知道用源码安装时尽量做到下面几条尽量装到/usr/local下不要覆盖系统自带的/usr目录里的文件。优先提供动态库编译选项避免把一堆.so直接写进系统目录。记好安装路径卸载时手动清理或写一个卸载脚本。服务器上有多个用户协作时源码安装前先问清楚有没有人已经装过同款软件。我见过最难受的情况是有人用源码装了一个OpenSSL到/usr/local/lib结果很多程序启动时报错“找不到libssl.so.1.1”。检查半天发现不是库真的缺而是/usr/local/lib不在动态库搜索路径/etc/ld.so.conf.d/*.conf里。解决方法倒是简单把路径写进一个conf文件再执行sudo ldconfig即可但这种问题第一次遇到时很费时间。6. 跨发行版的新打包方式snap、flatpak与AppImage6.1 snap沙箱里的自动更新snap是CanonicalUbuntu背后的公司推的跨发行版打包方案。特点是安装包跟系统完全隔离自带依赖强制沙箱。命令是sudo snap install 软件名 snap list sudo snap refresh 软件名 sudo snap remove 软件名snap的优点明显打包方不用管你用哪个发行版依赖全部塞进包里开箱即用更新机制也是强制的默认每天检查更新。缺点也很明显安装体积大首次启动慢因为要解包并且建立沙箱环境。有人觉得snap“吃了资源还慢”这个感受在低配置机器上特别真实。我在树莓派上装过snap版软件启动能明显感觉到卡顿后来能用普通包就用普通包。6.2 flatpak重点在桌面应用分发flatpak也是跨发行版方案但它的主战场是图形界面应用。安装软件口令一般是flatpak install flathub org.mozilla.firefox flatpak run org.mozilla.firefox flatpak list flatpak updateflatpak的管理思路是用runtime运行时做共享基座同一套runtime可以支持多个应用不至于每个应用都重复带一份底层库。这在存储占用上比snap稍友好。它的问题是需要配置Flathub源而且沙箱对文件权限、网络访问等限制有时会让人困惑。比如装了一个文件管理器类的应用它默认连不上家里的SMB共享得去配置权限。6.3 AppImage最轻量的“免安装”方式AppImage是另一个方向不需要安装一个文件下载下来给上执行权限就能运行。chmod x 软件.AppImage ./软件.AppImage它特别适合那种“我就偶尔用一下”的软件不留后台服务不污染系统目录。缺点是没有后台更新机制版本升级需要重新下载新文件某些复杂应用因为库冲突在这个发行版能跑到另一个发行版可能就报错。6.4 我自己的选型意见方案适合场景需要注意系统软件包apt/dnf服务器基础组件、生产环境版本可能偏旧要做版本规划snap/flatpak桌面应用、需要跨发行版分发体积大、沙箱限制AppImage临时工具、便携软件无自动更新、兼容性偶有波动源码编译特殊定制、最新版本、嵌入式环境脱离包管理手动维护生产服务器上我的原则很简单能用系统包就用系统包更新可预测、回滚有记录、依赖安全有人管。桌面折腾另说但也不能折腾完就忘了自己用过什么来源。7. 常见故障与排查技巧实录7.1 dpkg中断或者被锁住这个情况几乎每个Debian系用户都会遇到上一次安装没执行完这次再运行任何apt命令就报错E: dpkg was interrupted, you must manually run sudo dpkg --configure -a to correct the problem.解决办法也写得明明白白sudo dpkg --configure -a它会重跑所有处于“半配置”状态的包把中断的安装流程补完。如果这个命令执行中又报某个具体包出错可以先sudo dpkg --remove --force-remove-reinstreq 包名把它清理掉再看。7.2 apt锁冲突多终端同时运行apt命令或者后台有程序在自动更新常常会出现Could not get lock /var/lib/dpkg/lock-frontend第一反应绝不是去删锁文件而是先查是什么在占用它ps aux | grep -E apt|dpkg确认是正在运行的更新程序等它结束就行。如果之前有过卡死的进程kill掉对应PID后再尝试。最后解决方案里我不推荐直接rm锁文件除非你能确认没有任何apt/dpkg进程在运行。删除锁文件的风险是可能损坏正在进行的dpkg事务。7.3 仓库源报错或过期最常见的一类报错是“Release file is not valid yet”或者“The repository no longer has a Release file”。前者通常是系统时间不对因为GPG校验和有效期验证依赖于正确时钟。后者一般是你源里指定的发行版版本已经停止维护仓库不再更新。排查顺序建议是# 看有没有时间偏差 date # 看具体哪一行源出错 sudo apt update # 检查源文件看看发行版代号是不是已经EOL cat /etc/os-release时间不对就同步时间源过期就换有效版本或改官方存档仓库。7.4 依赖破损与修复apt有时会提示“有N个软件包无法安装”或者“下列软件包有未满足的依赖关系”。最直接的修复命令是sudo apt --fix-broken install它会尝试通过补装依赖、降级、清理冲突等方式恢复一个完整的状态。但我要提醒一点如果报错信息里明确提到某个包被“held”锁定或者“broken”先解开锁定再试。具体可以先用apt-cache policy看有哪些候选版本把导致冲突的具体包列出来再决定是移除、降级还是换源。盲目执行autoremove有时候能清理环境但也可能把有用的依赖删掉执行前建议看一下它列出的删除清单。7.5 问题速查表现象常用处理经验备注执行apt报dpkg中断sudo dpkg --configure -a重跑未完成的配置提示无法获得锁找到占用进程或等待自动更新结束不优先删锁文件Release file expireddate检查时间并同步时间偏差常被忽略404仓库不存在检查发行版代号与源地址换有效版或存档源依赖未满足apt --fix-broken install先看held包再动手rpm安装提示依赖缺失dnf install ./x.rpm或自行补依赖避免用rpm -i硬装软件被意外升级使用hold/exclude/versionlock锁定记录要备注原因8. 从这次梳理到我的日常习惯这次整理软件包管理体系对我的实际影响是把一套散乱经验串成了可复用的流程。现在我在任何新机器上装软件第一反应都是先确认这是Debian系还是Red Hat系再决定用哪条命令看到新软件官网给的是source tarball会先想想仓库里有没有现成包没有的话就规划好prefix路径遇到依赖错误不再傻傻地反复删除重装而是先查看依赖报告里到底是版本冲突、虚拟包缺失还是仓库过期。另外有一个体会很想分享软件包管理不是Linux学习路上最炫酷的部分没有好看的界面也拍不出很酷的截图但它是整个系统的地基。地基不稳上面跑什么服务都会遇到玄学一般的故障。希望这篇日志能给同样在Linux学习路上的人一点参考。
返回列表