ARTICLE DETAIL

资讯详情

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

RPM与DEB打包全指南:从包结构到构建分发实战

RPM与DEB打包全指南:从包结构到构建分发实战 简介面向Linux系统管理员、运维工程师及软件打包入门者文档系统讲解RPM与DEB两大软件包管理体系的完整打包流程。内容依次覆盖两者适用的发行版差异、目录结构搭建、control/spec文件关键字段含义以及preinst、postinst、prerm、postrm等生命周期脚本的作用从DEB的DEBIAN控制目录和usr、opt等安装路径到RPM的BUILD、SPECS、SOURCES等固定目录均有具体说明还结合命令展示dpkg -b与rpmbuild的打包、安装、升级、卸载操作帮助读者理解两种格式的核心机制并快速制作自己的安装包避开依赖缺失、脚本权限等常见打包陷阱。资源包共1个doc文档大小约267KB以图文步骤和示例为主适合边看边练。目前已有416人学习浏览对需要为软件部署、分发和维护建立系统化认知的技术人员有直接参考价值。整体将DEB与RPM的目录结构、脚本时机、依赖配置等要点对照呈现便于在单一文档中快速检索与对比学习。1. RPM和DEB软件包打包教程在解决什么从“能跑”到“能装、能升级、能卸载”“RPM和DEB软件包打包教程”这类资料核心讲的是一件事把一段“本地能跑”的代码变成用户能稳定安装、升级、卸载干净的软件包。RPM 服务于 Fedora、CentOS、openEuler 这条 Red Hat 谱系DEB 服务于 Ubuntu、Debian 及大量衍生发行版两者把二进制、配置文件、安装卸载脚本和依赖关系收进单一文件交给系统的包管理器统一管理。适合谁读给服务器写工具链的研发、给内网做软件分发的运维、准备把软件正式交付外部用户的产品团队。读完你能判断自己的软件该走哪条打包路线并照着搭出第一条能跑通的流水线。2. 打包前先分清两套体系RPM 与 DEB 的包结构、生命周期与选型先说结论打包翻车的人里十有八九不是命令记错而是不理解“包”在系统里到底是个什么东西。RPM 和 DEB 表面都是“一个文件、一条命令装完”底层结构却完全是两套设计思路。先把这两套结构摸清楚后面 spec 和 control 里的每个字段你都能知道它去了哪里、在哪个环节生效。2.1 RPM 的包体结构header 元数据加 cpio 归档RPM 文件本质上是由两段拼起来的一段二进制 header一段 cpio 归档。header 里存的是机器可读的元数据包括包名、版本、Release、依赖关系、安装前后执行的脚本、文件清单的哈希等cpio 归档里才是真正的程序文件、配置文件和文档。任何 .rpm 文件都可以用rpm -qip读取元数据用rpm2cpio把归档内容原样解出来。这种结构决定了 RPM 的核心能力是“事务管理”。安装一个 RPM 包时rpm 会先做依赖检查再按顺序执行%pre脚本、解包、执行%post脚本整个过程写入 rpm 数据库卸载时再反向执行%preun、删文件、执行%postun。%files清单负责登记“哪些文件属于这个包”rpm 数据库里记录的是这些路径和校验值这就是它能做到“卸载干净”的根基。2.2 DEB 的包体结构ar 容器加三个成员DEB 走的是另一条路。一个 .deb 文件用古老的 ar 格式打包里面固定有三个成员debian-binary只是一个版本号文本control.tar.gz里装着 control 元数据、维护者脚本preinst、postinst、prerm、postrm和 conffilesdata.tar.*装实际文件。用dpkg-deb -e可以把控制信息和脚本单独解出来用dpkg-deb -x解开数据部分。你会发现 DEB 的脚本命名和 RPM 不同RPM 叫%pre、%post、%preun、%postunDEB 叫 preinst、postinst、prerm、postrm触发时机对应关系是安装前、安装后、卸载前、卸载后。另有一个 conffiles作用是标记“这是配置文件升级时如果用户改过就不要覆盖”它和 RPM 里的%config是同一个设计要解决的问题但实现方式完全独立。2.3 动手拆包rpm2cpio 与 dpkg-deb 的实际验证讲完结构建议你立刻找两个现成的包拆开看看。手里没有就先用构建产物拿个 nginx 或 MySQL 的安装包练手也行。这一套命令可以帮你把“黑匣子”变成透明的目录树# 拆 RPM rpm -qip ./hello.rpm # 查看元数据名称、版本、依赖、脚本 rpm2cpio ./hello.rpm | cpio -t # 只列出归档内的文件名 mkdir -p /tmp/rpm-extract cd /tmp/rpm-extract rpm2cpio ./hello.rpm | cpio -id # 完整解包到当前目录 # 拆 DEB dpkg-deb -I ./hello.deb # 查看 control 信息 dpkg-deb -c ./hello.deb # 列出 data 部分文件 mkdir -p /tmp/deb-extract cd /tmp/deb-extract dpkg-deb -x ./hello.deb . # 解出 data 内容到当前目录 dpkg-deb -e ./hello.deb # 解出 control 和维护者脚本到 DEBIAN 目录关键看两个点一是解出来的目录结构原生包的%files清单不是乱写的路径层级就是安装后的真实布局二是维护者脚本存的位置一个在 RPM header 里一个在 control.tar.gz 里但最终都服务于“安装、升级、卸载”这三个生命周期动作。2.4 选型逻辑用户跑什么发行版就交付什么包选型没有玄学只有一条主规则看你的用户群在什么发行版上跑。服务器内部以 CentOS、Rocky、openEuler 为主力就打 RPM桌面办公和教学场景多为 Ubuntu、Debian 衍生版就打 DEB对外商业发布则两种都给。观察一个现象就能理解——Chrome 官方提供的是 .deb 和 .rpm 两种安装包没有 tar.gzThonny 这类教学 IDE 官网也按发行版给安装包Ubuntu 用户拿到的是 .deb。为什么因为对最终用户来说双击或一条命令装完远比“下载解压然后自己找路径”可靠。对比项RPMDEB构建入口spec 文件 rpmbuilddebian/control debhelper查询文件归属rpm -ql 包名dpkg -L 包名安装本地包rpm -ivh ./x.rpmapt install ./x.deb卸载rpm -e 包名dpkg -r 包名配置标记%config、%config(noreplace)conffiles依赖检查安装时强制rpmdb 记录依赖 apt/dpkg 解析还有一个常见问法能不能用 alien 把 RPM 转成 DEB能用但我不会在正式交付里依赖它。alien 的转换对纯二进制文件问题不大一旦包里有维护者脚本、配置文件、多架构支持转换后的语义会变形依赖关系也经常对不上。应急可以长期还是要按原生体系重打。3. 从零打一个 RPM 包spec 文件、rpmbuild 与五个必调参数现在开始动手。以 CentOS 系的服务器为例目标是把一个最简单的 shell 脚本工具包装成 RPM。别看例子小RPM 的完整流程都会走一遍后面换成任何编译型项目只是增加%build段的复杂度。3.1 先搭 rpmbuild 目录骨架rpmbuild 对目录结构有约定默认是家目录下的rpmbuild里面五个目录各管一摊SOURCES 放源码归档和补丁SPECS 放 spec 文件BUILD 是解压和编译的临时工作目录BUILDROOT 是模拟安装根目录RPMS 和 SRPMS 放构建产物。新手最容易犯的错是把源码直接丢进 BUILD其实 SOURCES 才是 rpmbuild 真正去找文件的地方。# 创建目录骨架 mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} # 定义 topdir并关闭构建 ID 链接避免调试符号目录干扰 cat ~/.rpmmacros EOF %_topdir %(echo $HOME)/rpmbuild %_build_id_links none EOF # 验证宏是否生效 rpm --eval %{_topdir}最后一条rpm --eval是检查手段能输出%_topdir的实际路径就说明 .rpmmacros 被正确读取了。一般不需要改默认目录除非你的 CI 用户没有独立家目录。%_build_id_links none是省事选项某些发行版会为每个包生成 build-id 符号链接脚本包用不到关掉能少踩一个“文件清单莫名多出几个链接”的坑。3.2 写一个最小可用 spec五个必调参数逐个拆spec 文件是 RPM 打包的唯一入口rpmbuild 所有行为都由它驱动。下面这份是我平时写工具型小包的起步模板把注释读一遍再抄# ~/rpmbuild/SPECS/hello.spec Name: hello Version: 1.0 Release: 1%{?dist} Summary: A demo shell script packaged with rpmbuild License: GPLv3 URL: https://example.invalid/hello Source0: %{name}-%{version}.tar.gz BuildRequires: bash Requires: bash %description A minimal demonstration package that installs /opt/hello/hello.sh %prep %setup -q %build # 脚本包无需编译保留空段便于以后扩展 %install mkdir -p %{buildroot}/opt/hello install -m 0755 hello.sh %{buildroot}/opt/hello/hello.sh %files /opt/hello/hello.sh %post echo hello.sh installed, try /opt/hello/hello.sh源码归档里的 hello.sh 内容很简单#!/usr/bin/env bash echo Hello from RPM packagingSource0必须和 SOURCES 目录里实际存在的文件同名%setup -q会把它解压到 BUILD 目录并自动进入解压后的目录。%install段里的%{buildroot}是模拟安装根所有文件必须先装到这里再由%files清单决定哪些真正进入包。清单漏一个文件rpmbuild 会直接报错这个设计比手动 tar 可靠得多。五个必调参数按踩坑频率排参数作用不设的后果BuildRequires构建期依赖yumbuild 时自动装缺编译工具链%build段直接失败Requires运行期依赖安装时强制检查用户装上后跑不起来%attr设置文件的权限、属主、属组默认按 install 命令的权限走容易过宽%config / %config(noreplace)标记配置文件升级时配置文件被无差别覆盖用户配置丢失AutoReqProv自动依赖探测脚本包常被解析出 /bin/bash 等多余的依赖AutoReqProv的坑最隐蔽RPM 默认会扫描包内可执行文件自动生成对解释器、共享库的依赖。一个#!/usr/bin/env bash的脚本可能被自动加上对bash的依赖这本身没错但如果你的包依赖的是系统里没有的 Python 路径自动探测就会生成一个谁也无法满足的依赖。脚本类包我会显式关掉自动依赖生成改用Requires手写需要的运行时。3.3 执行构建并做安装、查询、卸载闭环构建、安装、验证三步走完才算真正“跑通”了 RPM 打包。下面的命令从源码归档放位开始到彻底卸载结束# 1. 源码归档放到位 cp hello-1.0.tar.gz ~/rpmbuild/SOURCES/ # 2. 构建二进制包和源码包 cd ~/rpmbuild/SPECS rpmbuild -ba hello.spec # 3. 查看产物 ls -l ~/rpmbuild/RPMS/x86_64/hello-1.0-1.x86_64.rpm # 4. 安装 sudo rpm -ivh ~/rpmbuild/RPMS/x86_64/hello-1.0-1.x86_64.rpm # 5. 查询文件归属并运行 rpm -ql hello /opt/hello/hello.sh # 6. 卸载 sudo rpm -e hello-ba表示同时构建二进制包和 SRPM 源码包日常只需要二进制包时用-bb。rpm -ql查询的是已安装包的%files清单和rpm -qpl查未安装包要区分开。卸载后可以再用rpm -qa | grep hello确认数据库里已经干净。如果构建失败优先看 BUILDROOT 目录里有没有残留文件再用-ba的完整日志定位rpm 的报错信息基本都会指到具体文件和阶段很少含糊。4. 从零打一个 DEB 包debhelper、control 文件与 dpkg-buildpackageDEB 的流程比 RPM 散一点因为 Debian 的工具链把“元数据”和“文件内容”拆得更明确。同样用 hello.sh 做例子在 Ubuntu 或 Debian 上一路走到底你会看到 control、rules、changelog 三者怎么配合。4.1 debian/controlDEB 的元数据入口DEB 打包的元数据集中在源码树顶层的debian/目录里。最重要的文件是 control它同时描述“源码包”和“二进制包”两段信息。一个最小可用的 control 长这样Source: hello Section: utils Priority: optional Maintainer: Your Name youexample.com Build-Depends: debhelper ( 10) Standards-Version: 4.5.0 Package: hello Architecture: all Depends: bash Description: A demo shell script packaged with deb A minimal demonstration package that installs /opt/hello/hello.sh注意Source:和Package:是两段前者描述源码包后者描述最终安装的二进制包。Architecture: all表示不依赖具体 CPU 架构纯脚本包就该这么写Depends对应 RPM 的Requires安装时由 apt 负责解析。Description的第一行是短描述后续行必须以一个空格开头做缩进这是 Deb 格式的硬性要求少一个空格dpkg-deb都会拒绝解析。4.2 用 dh_make 生成骨架再改手写整个 debian/ 目录容易漏文件常见做法是用dh_make生成模板再裁剪。命令如下cd ~/hello-1.0 dh_make -p hello_1.0 --create --single --yes参数里-p指定包名和版本--single表示单二进制包--yes自动采用默认模板。生成后 debian/ 下会多出一堆 .EX 模板文件我习惯只保留 control、rules、changelog、compat、source/format 五个其余删掉避免干扰。rules 文件最简洁的写法是#!/usr/bin/make -f %: dh $dh $是 debhelper 提供的默认序列驱动器一条规则把 clean、build、install、binary 全流程串起来。compat 文件写一个数字10 是兼容性最稳的选择新工具链环境可以用 11 或 12取决于 CI 镜像里的 debhelper 版本。changelog 至少要有初始条目否则 dpkg-buildpackage 会直接拒绝构建hello (1.0-1) unstable; urgencymedium * Initial release -- Your Name youexample.com Fri, 01 Jan 2024 00:00:00 0000日期格式是小坑必须符合 RFC 5322否则 lintian 和构建都会报警。4.3 构建二进制包并检查产物源码目录准备好后执行构建。这里有个新手上当率最高的点产物不在当前目录而在上一级目录。cd ~/hello-1.0 dpkg-buildpackage -us -uc -b # 产物位置上一级目录 ls -l ../hello_1.0_all.deb # 检查元数据和内容 dpkg-deb -I ../hello_1.0_all.deb dpkg-deb -c ../hello_1.0_all.deb-us -uc表示不签名源码包和 changes 文件本地验证时省去配置 GPG 的麻烦-b只构建二进制包不生成 .dsc 和 .tar.xz 源码包节奏更快。看到_all.deb就对了如果构建机是 x86_64 且 Architecture 写了 amd64产物名会是_amd64.deb。dpkg-deb -I的输出里重点核对 Depends 行它决定用户安装时 apt 会额外拉什么包。4.4 安装验证与本地依赖解析DEB 的安装命令值得单独说因为网上到处能看到cd ~/downloads然后执行那一串的命令。我自己的偏好是cd ~/downloads sudo apt install ./hello_1.0_all.deb路径前面的./不是装饰是告诉 apt“这是一个本地文件”的明确信号。apt 会先解析这个 deb 的 Depends 字段去已配置的源里拉缺失依赖然后再把包本身装上。如果你直接dpkg -idpkg 只会做简单的依赖告警不会自动装依赖依赖缺失时包虽然装上了但运行直接报错。验证文件归属用dpkg -L hello卸载用dpkg -r hello。这就是内网分发教程里常见的标准动作背后完整的逻辑先解析再安装最后留足查询和卸载的退路。5. 常见坑与排查没找到 rpm 命令、安装失败、卸载残留的定位思路打包的坑大多集中在环境误判、依赖错配、文件清单漏项。这一章按“现象 → 原因 → 解决”把出现率最高的五条捋一遍。5.1 现象Ubuntu 上执行 rpm 提示“没找到 rpm 命令”原因是 Ubuntu 和 Debian 默认不带 RPM 工具链rpm 本身是 Red Hat 系的包管理工具在 Debian 系里即使apt install rpm装上了也只能做查询和转换完整打包环境仍然不具备。解决方法是不要在 Debian 系里硬打 RPM换一个 RPM 系的容器环境docker run --rm -v ~/rpmbuild:/rpmbuild:Z -w /rpmbuild/SPECS \ rockylinux:9 bash -c yum install -y rpm-build rpmbuild -ba hello.spec容器挂载时加了:Z是因为 SELinux 环境会拦截容器读写宿主目录这在云主机上尤其常见。如果不用容器也可以准备一台 CentOS 虚拟机专门跑 rpmbuild总之别在 Ubuntu 上跟 rpm 硬刚。5.2 现象在 Windows cmd 里执行 deb 安装命令失败不要笑这个检索量还真不小。有人在 Windows 的 cmd 里敲sudo apt install ./xxx.deb当然报“不是内部或外部命令”。原因是 deb 的安装工具 dpkg、apt 只存在于 Linux 环境cmd 和 PowerShell 里没有。解决也简单用 WSL 进入 Linux 子系统执行或者把 deb 文件拷到一台 Linux 机器上cd ~/downloads后执行sudo apt install ./xxx.deb。这不算打包本身的坑但属于“执行环境没对上号”的典型排查顺序永远是先确认你在什么系统里再谈命令为什么不对。5.3 现象安装 deb 报 wrong architecture 或 dependency not satisfiable前者是架构不匹配后者是依赖找不到。先各自排查# DEB 系看当前系统架构 dpkg --print-architecture dpkg --print-foreign-architectures apt-cache policy 依赖包名 # RPM 系看包元数据里的架构 rpm -qp --qf %{ARCH}\n ./hello.rpm uname -mArchitecture 写了 amd64 的包装到 arm64 机器上必然报 wrong architecture这类问题往往是打包机架构和发布平台不一致造成的。依赖不满足的原因通常是 Depends 里写的包名在当前已启用的源里不存在或源里只有更高版本不符合版本要求。内网场景里从发行版文件池里直接拷一个 deb 硬装最容易撞上这条——文件本身没问题但它的依赖链在你这台机器上没有对应源头应该用 apt 源的方式引入整个目录而不是单文件强制安装。5.4 现象卸载后文件残留/usr/local 下到处是垃圾这是对打包机制理解不到位的最典型表现。RPM 只删%files清单里登记过的文件DEB 只删 data.tar 归档里的文件如果你在%install或 install 阶段直接cp到系统目录绕过了%{buildroot}和暂存区包管理器根本不知道那些文件的存在。解决方法是强制纪律RPM 侧所有文件先进%{buildroot}再由%files收口DEB 侧使用 dh_install 把文件放到debian/hello/usr/等对应位置由 dh 自动收集。另外注意%config(noreplace)在升级时保留用户修改并按 .rpmsave 后缀保存旧文件这是正常设计不是残留。5.5 现象rpmbuild 报“Installed (but unpackaged) file(s) found”这个报错直接告诉你%install阶段向 buildroot 里放入了文件但%files清单漏写了。rpmbuild 是对完整性有强迫症的工具它不允许“装了却没人登记”的文件存在。解决分两步# 1. 打开日志看最后的 unpackaged files 列表逐个补进 %files rpmbuild -ba hello.spec 21 | tail -30 # 2. 用 -bl 只校验文件清单快速迭代 rpmbuild -bl hello.spec经常连带的还有依赖误报脚本包里的%files补全后rpm 自动依赖收集可能扫出一个Requires: /bin/bash之类的噪音。纯脚本工具包我一般显式禁止自动依赖生成改写Requires这样打出来的包依赖面才干净。6. 进阶包签名、软件仓库与 CI 里的一次性打包6.1 给包签名别让 yum 或 apt 一直警告无签名RPM 用 GPG 密钥签名构建机生成密钥后对产物执行rpm --addsign hello.rpmDEB 侧用dpkg-sig -s builder hello.deb或debsign。签名后的包在客户端安装时会被信任链检查内网仓库尤其需要。私钥一旦泄露整个仓库的信任就归零所以密钥要单独保管不能跟着构建机一起漂移。6.2 自建仓库一条源解决内网分发散装安装包只能救急内网批量分发还是要建仓库。RPM 侧用createrepo生成 repodata然后让客户端指向这个目录DEB 侧用dpkg-scanpackages生成索引。最小命令# RPM 仓库 createrepo /srv/repo/rpm # DEB 仓库 dpkg-scanpackages /srv/repo/deb /dev/null | gzip /srv/repo/deb/Packages.gz生成后配合 nginx 暴露目录客户端分别写好 .repo 文件和 sources.list 条目。有了仓库你才能把“下载一个包硬装”升级成“yum install 一条命令带依赖装完”。6.3 在 CI 里两条命令打双格式有了容器化不再需要维护两台打包机。我在流水线里用的就是两个镜像各跑一条命令docker run --rm -v $PWD:/src -w /src rockylinux:9 bash -c rpmbuild -ba hello.spec docker run --rm -v $PWD:/src -w /src ubuntu:22.04 bash -c dpkg-buildpackage -us -uc -b每次构建都从干净镜像开始彻底杜绝“昨天在打包机上手工装过什么”导致的玄学问题。产物固定留到 CI 的 artifacts 目录打出来的包再进签名、进仓库形成一条完整链路。我自己最早做打包就是把 tar 解压再压回去结果第一个正式给团队的包就翻车卸载残留、依赖乱跳、签名缺失运维半夜抓狂。后来的习惯是新包永远先做最小闭环一个文件装上再卸掉确认干净才补元数据构建环境一旦被手工改过就不再信任立刻换成容器重跑。希望帮到你。本文还有配套的精品资源点击获取
返回列表