
简介RPM与DEB是Linux生态中两大主流包管理机制分别面向CentOS/RHEL/Fedora与Debian/Ubuntu。这份教程面向系统管理员、运维工程师及软件打包初学者系统梳理DEB与RPM包的完整构建流程。内容包括DEB的DEBIAN目录、control字段与四个维护脚本以及RPM的目录布局、spec配置与%pre、%post等阶段脚本并附dpkg -b、rpmbuild -bb和安装、升级、卸载命令。单个DOC文档集中呈现两类打包方法整包仅267KB已有416人学习。按步骤实践即可掌握打包目录规范、控制文件或spec编写与部署操作快速用于软件分发。1. RPM和DEB软件包打包为什么你迟早要自己动手你刚下载好谷歌浏览器官网给的 deb 安装包双击想装结果界面弹出一个红色报错依赖关系不满足。或者你在公司内网维护着一批 CentOS 机器每次发个小工具都要 SSH 上去手动编译机器多了之后你想能不能像 yum install 一样一条命令搞定。这两个瞬间就是 RPM 和 DEB 软件包打包这门手艺出场的时机。RPM 和 DEB 是 Linux 生态里两套主流的软件包格式前者用于红帽系RHEL/CentOS/Fedora/openEuler后者用于 Debian 系Ubuntu/Debian/Kylin。本文要解决的是把一个可执行文件、脚本或源码工程变成能被系统包管理器正常安装、卸载、升级的 .rpm 或 .deb 文件并讲清楚中间会遇到哪些坑。适合两类人正要发布自有工具的开发者和需要做内网软件分发的运维。2. 打包前的决策与工具链先分清楚软件形态再装全家桶2.1 软件形态决定打包策略二进制、脚本、源码各走各的路很多人拿到题目就直接搜“怎么把程序变成 rpm”第一个动作往往会错没有先想清楚自己的软件属于什么形态。Linux 打包不是把文件塞进压缩包就完事包管理器的价值在于依赖解析、升级和卸载回滚而这套逻辑依赖你对安装内容、安装位置、运行依赖的完整描述。形态不同打包的工作量和坑的方向都不一样。纯二进制编译好的可执行文件加动态库加配置文件。打包要点是把文件放到正确目录/usr/bin、/usr/lib、/etc声明依赖的库处理 ldconfig 缓存。脚本类Python、Shell 脚本。不需要编译但是要把解释器、pip 依赖、数据文件都列清楚。thonny 官网直接提供 deb 下载就是这类形态的典型代表。源码类以 tar.gz 分发的 C/C 项目。需要 configure 和 makeRPM 的 %build 段和 DEB 的 rules 文件就是干这个活的。三种形态对应的打包入口差异很大列个表看得更清楚软件形态主要打包动作RPM 侧入口DEB 侧入口纯二进制文件清单与目录规划%install 安装到 %{buildroot}%files 声明清单dh_auto_install 加 override 指定安装路径脚本类解释器与数据文件依赖%files 保留可执行位声明 Requires依赖写进 control 的 Depends源码类configure、make、make install%prep 解压%build 编译%install 安装dh_auto_configure、dh_auto_build、dh_auto_install为什么一定要先做这个分类因为后续所有参数都围绕它展开。如果把源码类工程当作纯二进制来打包%files 里指不到编译产物打出来的包是空的——这是新手最常见的第一幕翻车现场。2.2 工具链安装与目录约定rpmbuild 和 dpkg 全家桶RPM 侧的核心工具是 rpmbuild另外 rpmdevtools 里带一个 rpmdev-setuptree 命令会一次性生成标准目录骨架。Debian 侧对应的是 dh_make、debhelper、devscripts 这一组。安装命令如下# Red Hat 系CentOS / Fedora / openEuler yum install -y rpmdevtools rpmdev-setuptree # Debian 系Ubuntu / Debian / Kylin sudo apt install -y dh-make debhelper devscripts dpkg-devrpmdev-setuptree 会在你的家目录生成 rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} 六个目录。其中 SOURCES 放原始 tar 包SPECS 放 spec 文件BUILD 是解压构建区RPMS 放最终产物。Debian 侧不需要手建目录dh_make 会在你的源码根目录生成 debian/ 目录——注意是生成在源码目录里不是一个独立的工作目录。这个区别很多人一开始不适应总想找一个“放 debian 目录”的标准位置结果把自己绕晕了。还有一组命令边界要分清yum/dnf 负责安装软件它们底层调用的还是 rpm 包管理apt 负责安装软件底层是 dpkg。打包时你用的是 rpmbuild 和 dpkg-buildpackage安装时你用 yum 和 apt。四个工具属于两个层面混着用会出很多幺蛾子。这些工作目录本身就像个黑匣子很多人把 tar.gz 手动解压到 BUILD 里再自己 make 一遍以为就等于打包好了——结果 rpmbuild 一执行就报 Source not found。正确姿势是源码包原封不动放进 SOURCES让 spec 文件的 %prep 段在构建时自己解压。3. RPM 打包实战从 spec 文件到 rpmbuild 出包3.1 spec 文件逐段拆解最小可用模板与必改参数spec 文件是 RPM 的核心它同时是构建配方、安装脚本和包元信息。下面用 GNU hello 这个 C 项目做最小模板照抄能跑通Name: hello Version: 2.10 Release: 1%{?dist} Summary: A friendly greeting program License: GPL-3.0-or-later URL: https://www.gnu.org/software/hello/ Source0: hello-%{version}.tar.gz BuildRequires: gcc, make Requires: glibc %description The GNU hello program produces a familiar, friendly greeting. %prep %setup -q %build %configure make %{_smp_mflags} %install make install DESTDIR%{buildroot} %files %{_bindir}/hello %{_mandir}/man1/hello.1.gz %changelog * Wed Jan 01 2025 Your Name youexample.com - 2.10-1 - Initial packageName、Version、Release 三个字段拼成完整的包版本标识。Release 里的 %{?dist} 在定义了 dist 宏的系统上会展开成 .el9 这样的后缀没有定义就自动消掉不影响构建。Source0 的路径必须对应 SOURCES 目录里实际存在的文件这里的 %{version} 是宏构建时会被替换成 2.10。BuildRequires 是构建期依赖Requires 是运行期依赖这两个最容易被混淆。漏了 BuildRequires换一台干净机器构建必然失败漏了 Requires安装时依赖检测不过。从 %prep 到 %install 四个段各有分工%prep 负责解压并进入源码目录%build 执行 configure 和 make%install 用 DESTDIR 把文件安装到 buildroot 而不是系统根目录%files 声明最终要进包的文件。GNU hello 自带标准 configure 和 make install所以这里能用 %configure 和 make install 原样跑。换成你自己的项目这三段要全部换成具体构建命令。%{_smp_mflags} 是 RPM 的并行编译开关展开后等价于 -jN老教程里手动写 -j4 的问题是换机器后核数不同步编译性能对不上。3.2 从零到 rpm 包的完整流程三次校验与产物路径目录骨架有了、spec 文件写好了接下来就是完整构建。把 tar 包放进 SOURCES 后执行cd ~/rpmbuild/SOURCES wget https://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz cp hello-2.10.tar.gz ~/rpmbuild/SOURCES/ cp hello.spec ~/rpmbuild/SPECS/ # 上节的 spec 存入此文件 cd ~/rpmbuild/SPECS rpmbuild -ba hello.spec ls -l ~/rpmbuild/RPMS/x86_64/rpmbuild -ba 的 b 是 builda 是 all表示同时构建二进制包和源码包.src.rpm。只构建二进制包把 -a 换成 -b只执行到解压阶段用 -bp执行到编译结束用 -bc。这几个渐进参数是调试时的后悔药%prep 段出错就用 -bp 快速定位不用每次跑完整构建省下的时间在慢机器上非常可观。构建完成后别急着安装先做三步校验rpm -qpl ~/rpmbuild/RPMS/x86_64/hello-2.10-1.el9.x86_64.rpm rpm -qpi ~/rpmbuild/RPMS/x86_64/hello-2.10-1.el9.x86_64.rpm sudo yum localinstall ~/rpmbuild/RPMS/x86_64/hello-2.10-1.el9.x86_64.rpmqpl 列出包内文件清单检查有没有文件散落qpi 看包的元信息确认 Name、Version、License 等字段是否正确。localinstall 用 yum/dnf 解析本地 rpm 的依赖并统一安装而不是直接 rpm -ivh——直接 -ivh 不会做仓库依赖解析走到一半卡在依赖问题上是常事。很多人在 CentOS 上 rpm 安装 mysql 时习惯一个包一个包地 -ivh缺一个依赖再找下一个最后陷入依赖地狱根子就在于绕过了 yum 这层依赖分析。常见做法是在构建机上装一遍再到一台干净的纯净机器上装一遍。构建机往往已经有编译依赖即使漏了 Requires 也测不出来只有干净机器能暴露真实的运行依赖。这一条要坚持否则发出去的包只在你自己机器上能装。4. DEB 打包实战debian/ 目录与 dpkg-buildpackage 全流程4.1 debian/ 目录四件套control、rules、changelog、copyrightDEB 的元信息不集中在单个文件里而是放在源码根目录的 debian/ 目录中。最小必须的文件有四个control 描述包信息和依赖rules 是构建脚本changelog 记录变更历史copyright 声明版权。control 是最容易被新手看漏的文件字段写错会直接影响依赖分析和安装判断。Source: hello Section: utils Priority: optional Maintainer: Your Name youexample.com Build-Depends: debhelper ( 11), autotools-dev Standards-Version: 4.5.0 Homepage: https://www.gnu.org/software/hello/ Package: hello Architecture: amd64 Depends: libc6 ( 2.28) Description: A friendly greeting program The GNU hello program produces a familiar, friendly greeting.注意 Source 和 Package 是两个区块。Source 描述源码包Package 描述二进制包。Build-Depends 是构建依赖对应 RPM 的 BuildRequiresDepends 是运行依赖对应 Requires。Depends 里的 “ 2.28” 是版本下限目标机器上 libc6 版本不够时 apt 会拒绝安装——这就是“同一份 deb 在 Ubuntu 不同版本之间不通用”的主要来源。另一点是 Description 的第一行是短描述换行后的缩进行是长描述这个格式错了 dpkg 会警告。rules 文件是一个 Makefile最简版本只要三行#!/usr/bin/make -f %: dh $dh 是一个驱动器会按顺序调用 dh_auto_configure、dh_auto_build、dh_auto_install、dh_strip、dh_compress、dh_fixperms 等一系列 dh_auto_* 命令。对遵循 configure/make、make install 规范的源码项目这三行 rules 已经够用。如果项目用 cmake需要加一行 override:override_dh_auto_configure: dh_auto_configure --buildsystemcmakechangelog 的格式是固定的包名(版本) 发行版; 紧急程度后面是变更条目。仓库工具和 dpkg-buildpackage 依赖它判断版本变化。现实中很多人一开始不写 changelog等要用 apt 锁版本或者日后追溯时才发现少这个文件补起来又对不上格式。4.2 用 dh_make 初始化再 dpkg-buildpackage 出包官方推荐的起点是 dh_make它会自动生成 debian 目录骨架。完整流程cd ~/build wget https://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz tar xzf hello-2.10.tar.gz cd hello-2.10 dh_make -p hello_2.10 --createorig # 按提示选择 single binary之后按需编辑 debian/ 下各文件 dpkg-buildpackage -us -uc -b ls -l ../hello_2.10-1_amd64.deb-p 参数指定“包名_版本”--createorig 自动把当前目录打成上游原始 tar 包命名是 hello_2.10.orig.tar.gz。dpkg-buildpackage -us -uc -b 的含义是不签名 source、不签名 changes只构建二进制 deb。个人内网分发用这组参数能省去配置 GPG 密钥的步骤公开发布时签名这一步迟早要补第 6 章会讲。产物生成在上层目录文件名是 hello_2.10-1_amd64.deb 这种固定结构包名_版本号-修订号_架构.deb。安装时要用 apt 而不是 dpkgcd ~/downloads sudo apt install ./hello_2.10-1_amd64.deb这里必须写 ./ 前缀否则 apt 会去仓库里找叫 hello_2.10-1_amd64.deb 的包。这个习惯里藏着 DEB 系一个核心心智deb 包有“来自本地文件”和“来自仓库”两条路径加 ./ 就是明确告诉 apt “我要安装本地文件”换来的是依赖自动解析。网上搜“deb 任何安装”的人多半是被依赖地狱折腾出来的其实一条带 ./ 的 apt install 就能解决。安装后验证dpkg-deb -c hello_2.10-1_amd64.deb dpkg -s hello apt-cache policy hellodpkg-deb -c 查看包内内容dpkg -s 查看已安装状态apt-cache policy 查看仓库里的版本与当前版本。这三个命令一进一出能确认一个 deb 包是否真的按预期装上了以及本机当前的版本状态。5. 打包避坑与常见问题排查4 个高频翻车现场还原5.1 没找到 rpm 命令在 Debian 系上硬装 rpm 包的翻车现场现象在 Ubuntu 上执行 rpm -ivh xxx.rpm终端直接给你 rpm: command not found。有人把命令换成 dnf又报“未找到命令”。搜“rpm 安装 mysql”的教程发现全是 CentOS 专属感觉被打了一拳。原因rpm 命令和 rpm 包格式都是红帽系专属工具链Debian 系用的是 dpkg 和 apt。这两条软件包管理分支各自独立包文件格式不通用。解决如果目标是装 rpm 包要么换到红帽系系统要么用 alien 把 rpm 转成 deb转换结果不一定完美要么直接找对应格式的包。对习惯 Windows cmd 装软件的人来说最需要先扭转的认知不是某条安装命令而是“每个发行版都有自己的包管理器命令不通用”。搞清楚自己站在哪个系再谈安装能省掉一大半折腾。5.2 双击 deb 报依赖错误谷歌浏览器安装包的经典卡壳现象下载 google-chrome-stable_current_amd64.deb双击用图形界面装立即报“依赖关系不满足需要 libgbm1、libnss3”之类的错误。界面没有下一步你不知道缺什么也不知道该装哪些。原因图形化的安装器只执行 dpkg 层面的文件解压不做仓库依赖解析。dpkg 把缺依赖视为硬错误直接拒绝安装。解决不要双击改在终端执行 sudo apt install ./google-chrome-stable_current_amd64.deb让 apt 从仓库自动补依赖。一条命令替代十分钟手动找依赖。如果系统里已经装了一半造成依赖状态混乱先 sudo apt --fix-broken install 修复状态再装新包。这和 RPM 系用 yum localinstall 的道理完全一致依赖解析交给仓库层工具而不是包层工具。5.3 make install 忘加 DESTDIR打包机被污染的血泪经验现象spec 或 rules 里写了 make install构建成功产物 rpm 装出来却没有可执行文件但构建机本身跑得好好的。打包机被慢慢“喂饱”越到后面问题越难以捉摸。原因%install 段执行 make install 时如果没有指定 DESTDIRRPM 侧是 %{buildroot}make 会按默认 prefix 装到构建机的 /usr/local 等真实目录。这等于把构建机当成了临时安装目标真正打进包的文件清单里一穷二白最终用户拿到的就是空包。DEB 侧 dh_auto_install 默认处理好了 DESTDIR但如果你在 override 里自己写了 make install同样会踩这个坑。解决RPM 的 %install 段写 make install DESTDIR%{buildroot}DEB 的 override_dh_auto_install 里不要手工覆盖 DESTDIR。装完前用 rpm -qpl 或 dpkg-deb -c 核对文件列表再交付空包在交付前就能挡住。这是我在一线见过最多的一类“明明打包成功了装出来却是空的”案例。5.4 同包不通用发行版差异和依赖版本下限现象在 Fedora 38 上用 rpmbuild 做的 hello-2.10-1.fc38.x86_64.rpm拿到 CentOS 7 上安装报 libc.so.6(GLIBC_2.34) 找不到或者 Ubuntu 24.04 上打出的 deb在 22.04 上报 libc6 版本不足。这是“为什么我打包的东西别人装不上”的高频原因之一。原因rpm 的包名带 dist 后缀.fc38不同发行版之间的元数据和依赖定义不同deb 的 Depends 里有最低 glibc 版本限制。更重要的是构建机上安装的开发库版本决定了这个包的依赖下限新系统上构建的包天然不适配旧系统。解决要么在目标版本相同的干净构建环境里重新打包要么把依赖下限写保守。RPM 系可以不用 %{?dist} 后缀明确写 Release: 1DEB 系把 Depends 里的库版本降低到目标系统实际水平同时通过 Build-Depends 保证构建环境兼容。发布多个系统版本的包是常态不是 bug提前规划构建矩阵能省很多事。6. 验证与进阶校验、签名与本地仓库先讲验证。我拿到一个打好包的 rpm 或 deb不会直接装而是先看三样东西文件清单、元信息、动态库依赖。RPM 用 rpm -qpl 看清单再用 ldd 查关键可执行文件的动态库依赖DEB 用 dpkg-deb -c 看内容配合 dpkg-shlibdeps 自动生成的依赖清单核对。这一步能把 90% 的空包和错误文件清单挡在交付之前。签名是“能不能给外部人用”的分水岭。个人发布软件、公司内网分发用户端最怕的其实是包被篡改。RPM 侧在 ~/.rpmmacros 里配置 %_gpg_namerpmbuild 之后执行 rpm --addsign xxx.rpm用户端用 rpm -K 验证签名DEB 侧用 debsigs 或 dpkg-sig --sign builder xxx.deb验证时 apt 会读取你的公钥库。没有签名所有包只能走信任内网通道有签名包就能公开放到任意 HTTP 服务器上。最后一个进阶能力是本地仓库。Debian 侧的做法是把 deb 文件放进一个目录执行 dpkg-scanpackages 生成 Packages 索引客户端写一条 deb [trustedyes] http://内网IP/debs ./ 的 source 列表。RPM 侧用 createrepo 生成 repodata 目录再把 .repo 文件分发到客户端的 /etc/yum.repos.d/ 下。做完之后客户端直接 yum install 或 apt install 就能装内网包不用再拷贝文件手工装。我在产线维护上千台 Linux本来每个工具都要逐台算拷贝加安装做完本地仓库后一条 dnf install 全部搞定。我个人的习惯是把“发布清单”当作打包流程的起点先写下要装的每个文件、要建的每个目录、每一条运行依赖再倒推到 spec 文件或 debian 目录。框架搭反了后面所有操作都是在用补丁打补丁浪费的时间远多于一开始多想十分钟。最后说句实在话打包这活第一次必然翻车。翻车后按 rpmbuild -bp、-bc 逐段定位deb 构建按 dpkg-buildpackage 的日志倒着读症状大多能找到对应的那一个字段。如果看到某个依赖死活装不上先去看版本下限而不是怀疑包坏了。希望帮到你。本文还有配套的精品资源点击获取