刚入行做Linux运维那会儿,我第一件被安排的任务就是在CentOS上装一个内部软件,对方扔过来一个.rpm后缀的安装包,说“你把这个装上”。我当时连rpm和yum的区别都没搞明白,一路乱敲,最后依赖问题缠身,硬是把一台测试机搞得半残。后来啃了官方文档、看了无数报错、折腾了好几年生产环境,才算是把RPM这个东西用得有点心得了。
这篇文章不打算给你念man手册,而是想从一个实际干活的人角度,把RPM这套东西的底层逻辑、常用命令、实战场景和踩坑经验一次性说透。无论你是刚接触Linux的新手,还是已经在用yum/dnf但遇到问题就抓瞎的运维,这篇文章应该都能帮你在下次面对.rpm文件时更有底气。内容偏长,但每段都是实操里用得上的东西。
1. 先搞清楚RPM到底是什么,别把它和yum混为一谈
很多人习惯把“用yum装软件”和“用rpm装软件”当成一回事,其实两者压根不在一个层次。RPM全称Red Hat Package Manager,它做的事情非常底层:把编译好的二进制文件、配置文件、依赖声明、安装卸载脚本打包进一个.rpm文件里,然后在系统上完成安装、升级、卸载、查询、校验这一整套动作。你可以把它理解成一个“软件安装工具箱”,它只管把包里的东西放到正确位置,并记录到本地数据库里。
1.1 RPM包内部装的是什么
一个RPM包的内部结构,远比“把文件复制到目录”要复杂。你可以用rpm2cpio把包解开来看,比如:
rpm2cpio nginx-1.20.1-10.el9.x86_64.rpm | cpio -t这一句能看到包里的文件清单。通常包含这几类内容:
- 可执行文件:编译好的二进制,放在
/usr/bin、/usr/sbin等目录。 - 配置文件:比如
/etc/nginx/nginx.conf,这类文件通常标记为config,升级时如果你改过,rpm不会直接覆盖,而是生成.rpmnew文件。 - 动态链接库:如
.so文件,会被安装到/usr/lib64。 - 文档文件:帮助手册、README等,放在
/usr/share/doc。 - 安装脚本:比如安装前后需要创建系统用户、启动服务、更新缓存等操作,分别记录在
%pre、%post、%preun、%postun里。
了解这个结构有什么用?最大的用处是排查问题。比如你装了一个包,但找不到它的配置文件在哪,就可以先解开看它的文件清单;比如你升级后配置总是被覆盖,就知道rpm对配置文件是有特殊处理的,并不是无脑覆盖。
1.2 rpmdb:RPM的心脏
RPM把系统上所有安装过的包信息记录在一个本地数据库里,即/var/lib/rpm目录下的那些文件(老版本叫rpmdb)。你用rpm -qa能列出所有包,本质上就是在查这个数据库,而不是扫描磁盘。
这个设计带来一个巨大优势:RPM可以在卸载时清理干净所有文件,并且能快速校验文件完整性。代价是,如果你不小心删了或者损坏了/var/lib/rpm,整个RPM体系就乱了。我见过有人直接rm -rf /var/lib/rpm想清理空间,结果系统连rpm -qa都跑不了,最后只能从同版本系统里复制一份rpmdb回去救急。所以这个目录轻易别碰。
1.3 RPM与yum/dnf的分工
如果RPM是“安装工具箱”,那yum/dnf就是“管家”。yum/dnf本身不会直接安装软件,它们干的事是:读取远程仓库(repo)里的包索引,分析依赖关系,然后把需要的RPM包下载到本地缓存,再调用RPM完成安装。
所以在生产环境里,正确的做事顺序是:能用yum/dnf的地方绝不用rpm手动装,因为yum/dnf会自动解决依赖。只有两种情况你必须手动用rpm:一是你手里只有一个单独下载好的rpm文件,且你确定它依赖的库系统里已经齐全;二是你想做一些查询、校验、提取等诊断动作,这些操作yum/dnf做不了。
1.4 RPM和apt的区别,别被习惯坑了
用过Debian/Ubuntu的人可能会把RPM的使用习惯直接迁移过来,这里有个关键差异:Debian系的dpkg/apt,包名格式和依赖处理逻辑和RPM体系有些不同。RPM的包名规范通常是软件名-版本号-发布号.架构.rpm,比如vsftpd-3.0.5-oe2203sp1.x86_64.rpm,中间用短横线连接,架构标识放在扩展名前面。
特别要注意的是noarch这个架构标识,它表示“不依赖具体CPU架构”,通常是纯脚本或文档,比如Python写的工具。而x86_64、aarch64、i686这些则直接对应硬件架构。装包之前最好先看一眼架构,把x86_64的包装到aarch64机器上,rpm会直接拒绝。这一点和apt的体验类似,但报错信息更明确一些。
2. RPM命令日常使用手册:从安装到验证一条龙
2.1 安装与升级:你真的会用-ihv吗
安装RPM包最基础的命令是:
rpm -ivh package.rpm参数拆开看:-i表示install(安装)、-v表示verbose(显示详细信息)、-h表示输出hash进度条。如果你要升级一个已经安装的包,用-Uvh,也就是把-i换成-U:
rpm -Uvh package.rpm-U和-i的区别在于,升级会先卸载旧版本再装新版本,并且在卸载前执行旧包的%preun脚本,安装新包后执行%post脚本。还有一个容易被人忽略的参数是-F(freshen),它只对“系统里已经存在同名的旧包”时才会升级,如果系统里没装过就直接跳过。想批量同步一批包的时候,-Fvh非常好用。
有一个坑我必须提醒:不要用rpm -ivh去覆盖安装一个版本号相同的包,除非你明确知道自己在干什么,否则可能会造成文件冲突。如果旧包的部分文件被修改过,rpm会拒绝安装或者提示冲突,这时你通常需要先卸载旧的再装新的。
在生产环境升级软件,我个人习惯先做一次“演习”,也就是加上--test参数:
rpm -Uvh --test package.rpm这个参数不会真正改动系统,只会做依赖检查,提前发现缺库、冲突之类的问题,非常值得养成习惯。
2.2 查询:遇到问题先查再动手
rpm的查询功能是我日常用得最多的能力,关键就是-q配合各种选项。
- 查某个包是否安装、版本号是什么:
rpm -q nginx- 列出系统里所有已安装的包:
rpm -qa- 查某个文件属于哪个包(解决“这个文件哪来的”经典问题):
rpm -qf /etc/nginx/nginx.conf- 查一个未安装的rpm包里面包含哪些文件:
rpm -qlp mysql.rpm- 查一个已安装包安装了哪些文件:
rpm -ql nginx- 查一个包依赖哪些库和程序:
rpm -qpR mysql.rpm这些查询动作不会动系统,多做无妨。比如你拿到一个第三方rpm包,先-qlp看一眼文件清单和安装路径,再-qpR看依赖,心里有底了再动手装,能避免大部分装到一半中断的尴尬。
2.3 卸载:依赖关系会反过来咬你一口
卸载一个包的命令是:
rpm -e 包名注意,这里填的是包名,不是文件名。比如装的时候是rpm -ivh nginx-1.20.1-10.el9.x86_64.rpm,卸载时就要用rpm -e nginx。
卸载最大的痛点是依赖关系。如果有一个包B依赖包A,你直接卸载A,rpm会报错并列出哪些包还依赖它。这时你有两个选择:一是把依赖它的包也一起卸载,二是用--nodeps强制卸载。
关于--nodeps,我强烈建议你在生产环境里慎用。强制卸载一个被依赖的包,可能导致依赖它的软件变成“残缺状态”,表面上还在,跑起来全是问题。我曾经在测试环境为了图省事强制卸载了一个glibc相关的包(别笑,年轻时真干过),结果系统里几乎一半命令都废了,最后只能重装系统。如果一定要绕过依赖,请先评估波及范围并做好快照。
2.4 校验与验证:怎么知道文件被改动过
安全排查和故障定位时,rpm -V是我很喜欢的命令:
rpm -V nginx它的作用是校验系统里的文件属性和内容是否和rpmdb记录的一致。输出里有8个字符位,分别代表文件大小、权限、属主、校验和等维度的差异,后面跟文件名。比如:
S.5....T. /etc/nginx/nginx.confS表示文件大小变了,5表示MD5校验和变了,T表示修改时间变了。这个命令对排查入侵、误改配置、磁盘故障都非常有用。想校验系统里所有已经安装的包,可以:
rpm -Va但注意,这个命令会扫描大量文件,耗时较长,而且像/etc下很多配置文件本来就是被修改过的,会输出大量结果,需要自己判断哪些是异常。
2.5 导入密钥:不导密钥装包会报错
RPM包在构建时可以签名,系统安装时通常会导入发行方官方的GPG公钥,用来验证包的真实性和完整性。如果你手动装一个有签名的包但系统里没有对应公钥,就会看到NOKEY的警告。
处理方式不是简单加--nosignature跳过,而是应该先导入对应的公钥。CentOS/RHEL上常见的做法:
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-*如果是第三方仓库的包,要从官方文档拿到对应公钥并导入。验证签名是安全底线,尤其当你从非官方渠道下载了一个rpm包时,这一步能避免很多恶意或损坏的包混进系统。
3. 实战场景拆解:从装MySQL到做一个自己的rpm包
3.1 用RPM装MySQL时的依赖问题和组包问题
很多人在CentOS上用rpm安装MySQL时都遇到过依赖报错,因为MySQL的rpm包分了几个子包:mysql-community-server、mysql-community-client、mysql-community-libs、mysql-community-common。它们之间互相有依赖,如果只下载了server包然后rpm -ivh硬装,肯定会提示缺少某某依赖。
最省心的方式是把所有子包下到同一个目录,然后一条命令全部装上:
rpm -ivh mysql-community-*.rpmrpm会先把这一批包放进一个事务里,解析出它们之间的内部依赖,再统一安装。这个方法也适用于装docker、nginx等被拆成多个子包的软件。
但有个前提:如果MySQL还依赖系统里没有的第三方库(比如libaio、libnuma),你仍然得先通过yum装好这些基础依赖。所以我的实操建议是:先用rpm -qpR mysql-community-server-*.rpm把依赖列出来,逐个确认哪些系统里有、哪些没有,缺的用yum先补齐,再执行批量安装。
3.2 CentOS 7配置阿里云rpm源:装包基本靠仓库
如果你用的系统是CentOS 7或者类似的RHEL系老版本,默认的官方源可能已经停止维护或者慢得让人崩溃,最简单的加速方案就是换用国内镜像源。以阿里云为例,操作不复杂:
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo yum clean all yum makecache换源的核心思路是:yum/dnf本身只是一个壳,真正下载rpm包的地址都写在.repo文件里。把指向官方源的地址改成指向国内镜像,剩下的逻辑完全不变。阿里云、腾讯云、华为云都有自己的镜像站,选择逻辑都一样。
需要注意一点:修改.repo文件之后,一定要执行yum clean all清掉旧缓存,否则可能还会从旧地址拉取索引,起不到加速效果。还有,CentOS 7已经进入生命周期尾声,如果你还在生产环境用它,建议尽早规划迁移到Rocky Linux、AlmaLinux等接续发行版,这属于运维层面的长期考量,越早越好。
3.3 制作自己的RPM包:别让源码编译的苦再来一次
玩到后面,你可能会遇到这种需求:官方仓库没有某个软件,或者版本太老,你只能从源码编译。但源码编译出来的东西直接散在系统里,后续卸载、升级、版本管理都很麻烦。这时候就应该自己打一个rpm包。
打包工具是rpmbuild,核心是写一个.spec文件。以MySQL 8.0.36源码包为例,大致流程是:
- 安装打包工具和依赖:
yum install rpm-build gcc gcc-c++ make cmake - 创建工作目录:
rpmdev-setuptree,会在家目录下生成rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}这几个目录。 - 把源码包放到
SOURCES目录,在SPECS目录写spec文件。 - 运行
rpmbuild -ba 包名.spec,构建完成后在RPMS/x86_64下生成二进制rpm包。
一个最简spec文件长这样:
Name: hello Version: 1.0 Release: 1 Summary: A simple hello world package License: MIT %description A simple test package. %prep %setup -q %build make %install make install DESTDIR=%{buildroot} %files /usr/bin/hellospec文件里最难理解的是%install段里的DESTDIR=%{buildroot},这是rpm打包和普通make install最大的区别:你不能直接把文件装进系统,而是装到一个临时目录(buildroot)里,等打包完成后再由rpm安装到真实系统。不理解这个逻辑会导致打出来的包文件路径乱七八糟。
如果只是想快速把一个已编译好的二进制打成rpm包,可以不用让rpm去编译,直接在%install里用install命令把你的文件复制进buildroot即可。这种方式虽然“不正规”,但很多内部工具分发都在用。
3.4 跨发行版的RPM包:Fedora上装QQ这类国产软件
热搜词里有“Fedora安装QQ rpm”,这个场景很有代表性。像腾讯QQ、WPS这类国产软件,官方提供的基本是.deb和.rpm两种格式,但rpm包往往针对OpenSUSE或Fedora构建,不一定完全兼容所有RHEL系发行版。
在Fedora上安装这类第三方rpm包,常见问题和解决方案:
- 缺依赖库:报错信息会直接列出缺哪些
.so文件或包名,优先尝试用dnf install补齐。 - 动态链接库版本不匹配:比如软件依赖
libssl.so.1.1,但你的系统只有libssl.so.3,这种单纯靠rpm是解决不了的,需要评估软件是否有新版本,或者是否愿意自己编译适配。 - 与Wayland/X11的兼容问题:某些GUI软件在Fedora默认的Wayland环境下表现异常,可以尝试切换到Xorg会话运行。
我的经验是,装这类包时不要一上来就rpm -ivh,先rpm -qpR看依赖,再检查系统和软件要求的差距。如果依赖差距过大,不如直接放弃rpm,改用软件官方提供的其他安装方式,比如压缩包解压运行。硬装只会浪费时间。
3.5 openssh这类高危软件的rpm升级:胆子要大,心要细
网上很多人在找“openssh rpm包下载”,因为OpenSSH是公网服务器最容易被扫描攻击的服务,CentOS 7自带的OpenSSH版本已经很老(7.4),存在大量已知漏洞。于是很多人会去第三方站点下载新版本openssh的rpm包来升级。
但这里我必须给你泼一盆冷水:OpenSSH不是普通软件,升级它之前千万要想清楚后果,因为ssh服务一旦配置不当或安装失败,你可能会把自己锁在服务器外面。我的建议是:
- 先确保你还有别的方式能进系统,比如本地控制台、带外管理(IPMI/iLO)、云厂商的VNC控制台。
- 先把系统的
openssh-server、openssh-clients、openssh这几个rpm包备份出来,或者找到对应版本的旧包,以备回滚。 - 用
--test参数先做依赖检查,确认新包和系统里其他依赖openssh的包(比如openssh-server依赖openssh)不会冲突。 - 升级时不要重启sshd服务,先测试新二进制能正常执行,再手动重启并保持一个已建立的会话不要断开,万一坏了还有机会补救。
另外,第三方站点下载的openssh rpm包,来源安全性存疑。除非你确认这个站点可信,否则建议优先从发行商官方提供的安全源或经官方维护的第三方仓库获取。安全升级,最怕的就是为了堵一个漏洞,引入了另一个安全风险。
3.6 docker rpm包安装:企业内网离线安装利器
热搜词里还有“docker rpm包”,这也是运维日常高频场景。很多内网服务器是无法访问外网的,这时yum install docker根本跑不通,你有两个选择:一是配置内网镜像源,二是提前在外网机器上下载好所有rpm包,复制进内网安装。
如果走“离线rpm包”路线,建议用yumdownloader工具来拉取(没有就先yum install yum-utils),它可以自动把依赖的包一起下载,比手动一个个找包透明得多:
yumdownloader --resolve --destdir=/root/docker-rpms docker-ce docker-ce-cli containerd.io然后把/root/docker-rpms目录打包,传到内网机器上,执行:
rpm -ivh /root/docker-rpms/*.rpm这样一套操作下来,即使完全离线的环境也能把docker装好。注意下载时要选择和目标机器操作系统版本完全一致的仓库,否则依赖库的版本会对不上,这个坑我踩过不止一次。
4. 常见问题与排查技巧实录
4.1 “没找到rpm命令”是怎么回事
热搜词里第一个就是“没找到rpm命令”,这个问题看着低级,实际很容易碰到。最小化安装的CentOS容器镜像、某些精简版系统、或者刚从一个非RHEL系系统切过来的人,都可能遭遇bash: rpm: command not found。
排查思路是:先确认你这到底是不是RHEL系系统。如果是Debian/Ubuntu,那就压根不该用rpm;如果用cat /etc/os-release确认是CentOS/RHEL系,却没rpm命令,大概率是精简环境。
CentOS/RHEL最小化安装一般都会带rpm,但容器基础镜像不一定。解决办法是在Dockerfile里加一句:
RUN yum install -y rpm如果你连yum都没有,那就先装yum或dnf。再极端一点,如果所有包管理器都没有,可以考虑直接用curl从阿里云镜像站下载rpm包,再用cpio解开使用,但这种情况已经属于手工作业,只适合临时救急。
4.2 依赖地狱:缺库、缺包、缺依赖
这是rpm使用过程中最普遍的问题。常见的报错格式是:
error: Failed dependencies: libxxx.so.1()(64bit) is needed by package注意它报的是.so文件,而不是包名。遇到这种报错,你的目标不是找到这个.so文件然后复制进系统,而是找到“提供这个.so文件的包”。用yum whatprovides命令可以搜:
yum whatprovides '*/libxxx.so.1'它会列出提供该库的包,然后装那个包即可。这个思路比网上一顿搜索要高效得多。
如果你真要面对的依赖是一大串,建议先检查是否有多版本共存的问题。比如系统里已经有一个老版本的libssl,新版本的rpm包要求另一个版本,两个版本的库其实是可以在系统里共存的(文件名不同),只要rpm找到对应的包名和版本就行。这时候rpm -qa | grep ssl看一下现状,再决定是升级还是装兼容库。
4.3 文件冲突:和已有的文件“撞车”了
安装时还有一类常见报错:
file /usr/bin/foo from install of bar-1.0 conflicts with file from package baz-1.2意思是新包要装的文件,已经被另一个包占了。这种情况千万不要用--replacefiles强行覆盖,因为很可能两个包都是正常包,文件的归属权只应该属于其中一个。
正确做法是查明两个包里为什么会有同一个文件。常见原因是两个软件都自带了同一个第三方库的旧版本,或者系统里已经有老版本的软件,而你想装新版本但没加-U而是加-i。后者的处理方法是改成rpm -Uvh升级,让rpm先卸掉旧的再装新的。
如果真的是两个独立包都想占用同一个配置文件,那你需要判断哪一个才是系统里真正需要的,保留它,另一个包用--nodeps之前先评估是否必要。实在不行,可以手动先把冲突文件改名再安装,但我个人不推荐这种方式,因为它会把系统的状态弄得很“脏”,后续排查问题时非常难跟踪。
4.4 密钥、签名与校验:安全底线别放松
安装从第三方下载的rpm包时,常见三种报错需要区分清楚:
NOKEY:没有导入这个包对应的GPG公钥。这不代表包一定有问题,但你需要主动去确认包的来源。BAD SIGNATURE:签名校验失败。这个比较严重,说明包可能被篡改过,或者公钥不对,通常不建议继续装。DIGESTS SIGNATURES之类的校验错误:原因和上一条类似,谨慎处理。
对于生产环境,我强烈建议把“验证签名”当成习惯。即使是内部自己打的包,也最好配置好签名流程。系统安全往往败在日常的“图省事”上。
4.5 rpmdb损坏:让人头痛的一个场景
前面提到/var/lib/rpm是rpm的数据库,它的损坏通常是意外断电、磁盘满、强制kill进程导致的。症状是执行任何rpm命令都报错,比如:
rpmdb: Lock table is out of available locker entries error: cannot open Packages database in /var/lib/rpm处理思路,先说反面教材:不要直接删掉rpmdb目录重建,那样系统里上千个包的记录就全没了,后续卸载升级都会抓瞎。
正确思路是备份并重建:
mv /var/lib/rpm /var/lib/rpm.bak mkdir /var/lib/rpm但这样重建后rpmdb是空的,你需要用rpm -qa都没结果了,此时最好有系统安装时的原始rpmdb备份。最稳妥的方法还是从同版本机器上复制一份/var/lib/rpm目录——但前提是两边的包列表基本一致,否则查询到的包和实际安装的文件对不上,一样坑。
平时做备份时,别忘了把/var/lib/rpm纳入备份范围。这个目录看着不起眼,关键时刻能救命。
5. 一些关于RPM的“为什么”和“怎么办”
5.1 为什么安装一个包会执行脚本
很多新手看到rpm装包时输出一堆Running %post之类的内容,会觉得莫名其妙。这部分其实是包制作者在spec文件里定义的安装后动作,比如创建系统用户、设置权限、注册systemd服务等。
这就解释了为什么不要用解压tar包的方式替代rpm安装:tar包只能把文件放到位,但无法执行这些配置脚本。表面上“好像装好了”,实际服务起不来、用户没建好、权限不对,各种隐性问题。所以宁可用rpm老老实实装,也别自作聪明去“解密”。
5.2 包名、文件名、命令名,三者别搞混
装包时写文件名,卸载时写包名,查询时可能用命令名。比如文件叫nginx-1.20.1-10.el9.x86_64.rpm,包名叫nginx,命令名是nginx(在/usr/sbin/nginx)。rpm -q nginx查的是包;rpm -qf /usr/sbin/nginx查的是文件归属;nginx -v读取的是命令版本。三者看似一回事,实际在rpm语境里是不同维度的对象。搞混了,就可能在排查问题时走弯路。
比如你遇到nginx命令不见了,用rpm -qf /usr/sbin/nginx会提示文件不存在,这时候要查的是rpm -qa | grep nginx看看包本身是否正常。如果包在,命令却没了,那可能是文件被误删,用rpm -V nginx校验一下,然后在确认系统状态的情况下重装一次。
5.3 如何判断一个第三方rpm包是否可以信任
第三方提供的rpm包,来源复杂程度差异很大。我判断是否可用通常看四件事:
- 渠道是否官方:优先从软件官网、发行方官方仓库、知名镜像站获取。
- 签名是否有:有签名的包至少说明发布者在意完整性,导入了对应的公钥后可以验证。
- 文件清单是否合理:用
rpm -qlp看看文件都装到哪里去,有没有可疑路径(比如/tmp下的可执行文件)。 - 依赖是否合理:一个正常软件的依赖通常可预期,如果依赖了一堆名字可疑的包,就要警惕。
安全上没有侥幸可言。许多服务器被入侵,都是因为装了来路不明的软件包。多花两分钟做验证,比出事之后花几个小时排查要划算得多。
5.4 从源码打到rpm包这条路值得走
很多开发会觉得“打包”是运维的事,实际上掌握rpm打包对开发也有价值。项目交付给客户、部署到多台机器、应对离线环境,一个干净的rpm包能让交付体验大幅提升。
入门可以先用rpmdev-setuptree建目录,再从一个最简单的spec文件开始,里面只做文件复制,不涉及编译。跑通之后,再尝试加上%pre/%post脚本,处理服务注册、配置文件生成等逻辑。等你把一个真实的项目成功打包并放到一台全新机器上干净安装时,你会觉得之前那些源码编译的痛苦都值了。
6. 最后再分享一点我的个人习惯
RPM本身并不复杂,翻来覆去就那些命令。真正让人翻车的是在没搞清状态的情况下乱操作。我现在无论装什么包,都会先在一个临时环境里把命令跑一遍,尤其是rpm -Uvh --test这种只检查不生效的选项,性价比极高。
还有一点,处理rpm相关问题时,尽量让系统的包管理状态保持“干净”。能不手动改系统库文件就不改,能不用--nodeps就不用,能用仓库源解决就不到处找rpm文件。系统的包管理数据库一旦被折腾得乱七八糟,排查问题的时间成本会成倍上涨。
如果你读完这篇文章后,能对眼前那个.rpm文件多一份判断力——知道它里面是什么、依赖什么、装到哪、怎么卸载、怎么验证,那说明你已经从一个“敲命令的人”,变成了一个“知道为什么敲这条命令的人”。这个区别,在运维路上非常重要。