目标是把 rpmbuild 的核心概念、标准流程、常见命令、服务类软件打包方式和排错方法串成一份可直接上手的教程。1. 什么是 RPM 和 rpmbuildRPMLinux 下常见的软件包格式与底层包管理机制。rpm 命令负责安装、卸载、查询、校验本地.rpm包。dnf/yum更上层的包管理工具负责仓库管理、依赖解析并最终调用 rpm。rpmbuild用来把源码、脚本、配置文件、systemd 服务文件等内容构建成 RPM 包。可以简单理解为dnf/yum 负责“拿包并解决依赖” rpm 负责“安装和管理包” rpmbuild 负责“制作包”2. 先建立正确认知RPM 包里到底装的是什么一个 RPM 包通常包含可执行文件如/usr/bin/*配置文件如/etc/*服务文件如/usr/lib/systemd/system/*.service文档、license、man 手册安装/升级/卸载脚本依赖关系、架构信息、校验信息等元数据最终哪些文件会进 RPM 包不是看你复制了什么而是看%files里声明了什么。在%install里放进%{buildroot}但没写到%files不会被打进包写到了%files但实际文件不存在打包会失败3. rpm 与 dnf/yum 的关系用户 ↓ dnf / yum ← 自动处理仓库和依赖 ↓ rpm ← 执行本地包安装、查询、卸载 ↓ 文件系统 RPM 数据库什么时候优先用哪个安装本地包做测试rpm -ivh或dnf install ./xxx.rpm正式部署且希望自动补依赖优先dnf install查看包内容、脚本、元数据用rpm -q*系列命令4. 安装打包工具在 RPM 系发行版上安装sudodnfinstall-yrpm-build rpmdevtools rpmlint适用于RHELCentOS StreamFedoraopenEulerRocky LinuxAlmaLinux部分麒麟/统信 RPM 环境说明rpmdevtools并不是所有 Debian/Ubuntu 环境都适用本文教程以 RPM 系系统为主。5. 初始化 rpmbuild 目录结构执行rpmdev-setuptree默认会在当前用户家目录下生成~/rpmbuild/ ├── BUILD/ # 解压源码、执行编译的工作目录 ├── BUILDROOT/ # 模拟安装根目录 ├── RPMS/ # 生成的二进制 RPM 包 ├── SOURCES/ # 源码包、补丁、配置源文件 ├── SPECS/ # .spec 文件 └── SRPMS/ # 生成的源码 RPM 包后续最常打交道的是SOURCES/SPECS/RPMS/BUILDROOT/6.%{buildroot}是什么%{buildroot}是RPM 构建时的虚拟根目录用来模拟真实系统的/。你在%install阶段不能直接把文件安装到真实系统路径而是必须先装进%{buildroot}最后再由 rpmbuild 打包。例如%install mkdir -p %{buildroot}/usr/bin install -m 0755 myapp %{buildroot}/usr/bin/这表示构建阶段先把myapp放到临时根目录里最终用户安装 RPM 后它才会出现在真实系统的/usr/bin/myapp为什么一定要这样做避免构建过程污染当前系统可以重复构建和检查包内容能清楚区分“构建机环境”和“安装目标环境”常见错误错误写法install -m 0755 myapp /usr/bin/正确写法install -m 0755 myapp %{buildroot}/usr/bin/7..spec文件是什么.spec是 RPM 打包的核心控制文件。它定义包名、版本、依赖、描述源码从哪里来怎么解压、怎么编译、怎么安装最终打哪些文件进包安装和卸载时需要执行哪些脚本可以把它理解成 RPM 的“打包配方”。8..spec文件基本结构一个最小化的.spec文件通常长这样Name: hello-kernel Version: 1.0 Release: 1%{?dist} Summary: A dummy package for learning rpmbuild License: GPL-3.0-or-later Source0: %{name}-%{version}.tar.gz BuildArch: noarch %description A dummy package for learning rpmbuild. %prep %setup -q %build : %install mkdir -p %{buildroot}/usr/share/hello printf Hello from kernel!\n %{buildroot}/usr/share/hello/message %files /usr/share/hello/message %changelog * Sun May 25 2026 Your Name youexample.com - 1.0-1 - Initial package9..spec中最重要的字段9.1 头部字段字段作用Name包名Version软件版本Release打包发布号Summary简短描述License许可证URL项目主页可选Source0源码压缩包BuildRequires构建依赖Requires运行依赖BuildArch架构如x86_64、noarch9.2 常见区段区段作用%description详细描述%prep解压源码、打补丁%build编译%install安装到%{buildroot}%files声明最终进包的文件%pre安装前脚本%post安装后脚本%preun卸载前脚本%postun卸载后脚本%changelog变更记录10. rpmbuild 的标准构建流程rpmbuild 一般遵循下面这条主线准备源码 → %prep → %build → %install → %files 校验 → 生成 RPM10.1%prep负责准备源码一般会解压Source0%prep %setup -q -n %{name}-%{version}其中-q安静模式-n指定解压后的顶层目录名10.2%build如果需要编译就在这里执行编译命令%build gcc -g -O2 -Wall -o myprogram src/myprogram.c如果是不需要编译的纯脚本/纯配置包也可以留空或写%build :10.3%install把产物安装到%{buildroot}%install mkdir -p %{buildroot}%{_bindir} install -m 0755 myprogram %{buildroot}%{_bindir}/10.4%files列出最终打包的路径%files %{_bindir}/myprogram11. 可以编译也可以不编译这是很多人最容易混淆的点。rpmbuild 只负责执行 spec 里的步骤并不强制要求你一定要编译。11.1 场景一源码编译型打包适合C/C 程序需要针对目标平台构建的软件从源码生成二进制示例%build make %{?_smp_mflags} %install make install DESTDIR%{buildroot}11.2 场景二预编译/脚本型打包适合shell/python/perl 脚本配置文件包已提前在 CI 中产出的二进制闭源程序分发示例%build : %install mkdir -p %{buildroot}%{_bindir} install -m 0755 myapp %{buildroot}%{_bindir}/12. 最小可运行示例打一个最简单的 RPM 包12.1 准备源码目录cd~mkdir-phello-kernel-1.0echoHello from kernel!hello-kernel-1.0/message12.2 制作源码包tar-czf~/rpmbuild/SOURCES/hello-kernel-1.0.tar.gz-C~ hello-kernel-1.0注意这里的压缩包名和顶层目录名要匹配压缩包hello-kernel-1.0.tar.gz顶层目录hello-kernel-1.0/12.3 编写 spec 文件保存到~/rpmbuild/SPECS/hello-kernel.specName: hello-kernel Version: 1.0 Release: 1%{?dist} Summary: A dummy package for learning rpmbuild License: GPL-3.0-or-later Source0: %{name}-%{version}.tar.gz BuildArch: noarch %description A dummy package for learning rpmbuild. %prep %setup -q %build : %install mkdir -p %{buildroot}/usr/share/hello install -m 0644 message %{buildroot}/usr/share/hello/message %files /usr/share/hello/message %changelog * Sun May 25 2026 Your Name youexample.com - 1.0-1 - Initial package12.4 开始构建rpmbuild-bb~/rpmbuild/SPECS/hello-kernel.spec12.5 成功后产物位置~/rpmbuild/RPMS/noarch/hello-kernel-1.0-1.noarch.rpm修正说明如果BuildArch: noarch最终包名通常是noarch.rpm不是x86_64.rpm。12.6 安装验证sudodnfinstall-y~/rpmbuild/RPMS/noarch/hello-kernel-1.0-1.noarch.rpmcat/usr/share/hello/message13. 常用 rpmbuild 命令13.1 构建二进制包rpmbuild-bbyour-package.spec13.2 构建源码包rpmbuild-bsyour-package.spec13.3 同时构建源码包和二进制包rpmbuild-bayour-package.spec13.4 查看已安装包信息rpm-qiyour-package-namerpm-qlyour-package-namerpm-q--scriptsyour-package-name13.5 查询某个文件属于哪个包rpm-qf/path/to/file13.6 查看 rpm 包内容但不安装rpm-qplyour-package.rpmrpm-qpiyour-package.rpm14. 常用宏宏含义常见值%{_topdir}rpmbuild 根目录~/rpmbuild%{_sourcedir}源文件目录~/rpmbuild/SOURCES%{_specdir}spec 目录~/rpmbuild/SPECS%{_builddir}编译目录~/rpmbuild/BUILD%{_buildrootdir}buildroot 根目录~/rpmbuild/BUILDROOT%{buildroot}当前包的临时安装根目录动态生成%{_bindir}可执行文件目录/usr/bin%{_sbindir}管理命令目录/usr/sbin%{_unitdir}systemd unit 目录通常是/usr/lib/systemd/system%{_sysconfdir}配置目录/etc%{_datadir}数据目录/usr/share建议优先写宏不要硬编码路径。例如install -m 0755 myapp %{buildroot}%{_bindir}/ install -m 0644 myapp.service %{buildroot}%{_unitdir}/15. RPM 安装过程到底发生了什么执行下面命令时sudorpm-ivhpkg.rpm或sudodnfinstall./pkg.rpm大致流程如下1. 读取 RPM 头部信息 2. 检查架构、依赖、签名 3. 执行 %pre 4. 解包并写入文件系统 5. 处理 %config 配置文件策略 6. 更新 RPM 数据库 7. 执行 %post如果是卸载%preun → 删除文件 → %postun16. 安装/升级/卸载脚本怎么写服务类软件经常会用到脚本区段。16.1 安装后脚本%post%post if [ $1 -eq 1 ]; then systemctl daemon-reload systemctl enable myapp.service /dev/null 21 || : systemctl start myapp.service /dev/null 21 || : fi16.2 卸载后脚本%postun%postun if [ $1 -eq 0 ]; then systemctl stop myapp.service /dev/null 21 || : systemctl disable myapp.service /dev/null 21 || : systemctl daemon-reload fi16.3$1的常见含义不同脚本阶段对$1的语义会有差异但在实际打包里最常见判断方式是$1 -eq 1通常表示安装后首次配置场景$1 -eq 0通常表示彻底卸载场景实际项目里如果脚本逻辑比较敏感建议结合发行版文档和测试结果确认。17. 配置文件与日志文件要区别处理17.1 配置文件如果文件允许用户改动建议使用%files %config(noreplace) /etc/myapp/myapp.conf这样升级时用户改过的配置尽量保留不会被新包直接覆盖17.2 日志文件日志文件一般不建议直接作为普通静态文件打进包更常见做法是由程序自己创建或通过tmpfiles.d、logrotate、首次启动逻辑创建如果只是教学演示也可以在%post中用touch创建%post install -d -m 0755 /var/log/myapp /dev/null 21 || : touch /var/log/myapp/myapp.log || : chmod 0644 /var/log/myapp/myapp.log || :说明生产环境更推荐让应用本身或日志系统接管日志文件而不是在%files里长期维护一个空日志文件。18. 实战示例把一个 systemd 服务打成 RPM下面用你文档里的sdet-monitor作为完整示例。目标安装一个监控程序det-monitor安装对应的 systemd 服务安装后自动启用并启动服务卸载时自动停止并禁用服务19. 项目目录建议先准备项目目录mkdir-p~/det-monitor/{src,systemd}cd~/det-monitor目录结构建议如下det-monitor/ ├── src/ │ └── det-monitor.c └── systemd/ └── det-monitor.service修正说明源码压缩包里通常不必再额外塞一个rpm/目录只要最终把 spec 复制到~/rpmbuild/SPECS/即可。20. 编写 systemd 服务文件文件systemd/det-monitor.service[Unit] DescriptionMonitor for det command execution Afternetwork.target [Service] Typesimple ExecStart/usr/bin/det-monitor Restartalways RestartSec5 Userroot Grouproot StandardOutputjournal StandardErrorjournal Nice19 IOSchedulingClassidle [Install] WantedBymulti-user.target说明Restartalways异常退出后自动重启RestartSec55 秒后重启Nice19降低 CPU 调度优先级IOSchedulingClassidle尽量减少 I/O 干扰如果程序不必须以 root 运行生产环境建议尽量使用专用低权限用户。21. 编写服务类软件的 spec 文件文件~/rpmbuild/SPECS/det-monitor.specName: det-monitor Version: 1.0 Release: 1%{?dist} Summary: Monitor for execution of det command License: GPL-3.0-or-later Source0: %{name}-%{version}.tar.gz BuildRequires: gcc, systemd Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %description This service monitors the execution of the det command. %prep %setup -q -n %{name}-%{version} %build gcc -g -O2 -Wall -o det-monitor src/det-monitor.c %install rm -rf %{buildroot} install -d %{buildroot}%{_bindir} install -d %{buildroot}%{_unitdir} install -m 0755 det-monitor %{buildroot}%{_bindir}/det-monitor install -m 0644 systemd/det-monitor.service %{buildroot}%{_unitdir}/det-monitor.service %files %{_bindir}/det-monitor %{_unitdir}/det-monitor.service %post if [ $1 -eq 1 ]; then systemctl daemon-reload /dev/null 21 || : systemctl enable det-monitor.service /dev/null 21 || : systemctl start det-monitor.service /dev/null 21 || : fi %preun if [ $1 -eq 0 ]; then systemctl stop det-monitor.service /dev/null 21 || : systemctl disable det-monitor.service /dev/null 21 || : fi %postun systemctl daemon-reload /dev/null 21 || : %changelog * Sun May 25 2026 Your Name youexample.com - 1.0-1 - Initial package这个版本相比原文修正了什么去掉了把日志文件直接塞进%files的做法对 systemd 脚本依赖加了更清晰的Requires(post/preun/postun)使用%{_bindir}、%{_unitdir}代替硬编码路径在%install前显式rm -rf %{buildroot}避免脏目录残留将停止/禁用服务放进%preun把daemon-reload放进%postun更符合常见实践22. 制作标准源码包源码包必须满足两个要求压缩包文件名与Source0对应解压后的顶层目录名与%setup期望一致在项目目录执行cd~/det-monitormkdir-pdet-monitor-1.0cp-asrc systemd det-monitor-1.0/tar-czf~/rpmbuild/SOURCES/det-monitor-1.0.tar.gz det-monitor-1.0rm-rfdet-monitor-1.0也可以先检查压缩包内容tar-tf~/rpmbuild/SOURCES/det-monitor-1.0.tar.gz|head你应该看到类似det-monitor-1.0/ det-monitor-1.0/src/ det-monitor-1.0/src/det-monitor.c det-monitor-1.0/systemd/ det-monitor-1.0/systemd/det-monitor.service23. 构建 RPM 包执行rpmbuild-ba~/rpmbuild/SPECS/det-monitor.spec成功后通常会生成~/rpmbuild/RPMS/x86_64/det-monitor-1.0-1.x86_64.rpm ~/rpmbuild/SRPMS/det-monitor-1.0-1.src.rpm注意这里是x86_64还是aarch64取决于你的构建机架构以及包本身是否声明为noarch。24. 安装与验证24.1 安装sudodnfinstall-y~/rpmbuild/RPMS/*/det-monitor-1.0-1.*.rpm也可以用sudorpm-ivh~/rpmbuild/RPMS/*/det-monitor-1.0-1.*.rpm24.2 检查服务状态systemctl status det-monitor.service systemctl is-enabled det-monitor.service预期状态为active (running)或至少已成功启动is-enabled输出enabled24.3 查看包内容rpm-qldet-monitorrpm-q--scriptsdet-monitor25. 卸载与验证卸载sudorpm-edet-monitor或sudodnf remove-ydet-monitor验证rpm-qdet-monitor systemctl status det-monitor.servicels-l/usr/bin/det-monitorls-l/usr/lib/systemd/system/det-monitor.service预期包查询不到服务单元文件已移除可执行文件已移除26. 常见错误与排查方法26.1File not found或%files报错原因通常是%install没把文件放进%{buildroot}%files路径写错目标文件名和实际文件名不一致检查思路find~/rpmbuild/BUILDROOT-typef|sort26.2%prep阶段解压失败常见原因Source0文件名不匹配压缩包顶层目录名与%setup -n ...不一致26.3 构建依赖缺失比如error: Failed build dependencies: gcc is needed by xxx处理sudodnfinstall-ygccmakesystemd-rpm-macros26.4 服务安装后没有启动检查systemctl status det-monitor.service journalctl-udet-monitor.service-xerpm-q--scriptsdet-monitor26.5 日志或配置文件被覆盖检查是否正确使用%config(noreplace)应用自身日志目录策略是否误把运行时文件直接打进%files27. 调试技巧27.1 检查 spec 质量rpmlint ~/rpmbuild/SPECS/det-monitor.spec rpmlint ~/rpmbuild/RPMS/*/det-monitor-*.rpm27.2 查看构建目录ls-l~/rpmbuild/BUILD/ls-l~/rpmbuild/BUILDROOT/27.3 查看最终 buildroot 内容tree ~/rpmbuild/BUILDROOT27.4 查看包内文件列表rpm-qpl~/rpmbuild/RPMS/*/det-monitor-*.rpm27.5 查看安装/卸载脚本rpm-qp--scripts~/rpmbuild/RPMS/*/det-monitor-*.rpm28. 最佳实践28.1 压缩包命名规范推荐name-version.tar.gz例如det-monitor-1.0.tar.gz28.2 顶层目录名保持一致例如压缩包内应是det-monitor-1.0/28.3 能用宏就用宏不要硬编码/usr/bin /usr/lib/systemd/system推荐%{_bindir} %{_unitdir}28.4 不要在%install中改真实系统所有写入都应进入%{buildroot}。28.5 区分构建依赖和运行依赖BuildRequires编译时依赖Requires安装运行时依赖28.6 尽量不用 root 做构建构建阶段通常应在普通用户下执行安装阶段再使用sudo。28.7 服务包优先考虑 systemd 生命周期至少要考虑安装后daemon-reload首次安装后enable/start卸载前stop/disable卸载后daemon-reload29. 一份从零到打包成功的最短操作清单如果你只想快速上手按下面执行即可。29.1 初始化环境sudodnfinstall-yrpm-build rpmdevtools rpmlint rpmdev-setuptree29.2 准备源码包mkdir-p~/demo-1.0echohello rpm~/demo-1.0/READMEtar-czf~/rpmbuild/SOURCES/demo-1.0.tar.gz-C~ demo-1.029.3 写 specName: demo Version: 1.0 Release: 1%{?dist} Summary: Simple demo rpm License: MIT Source0: %{name}-%{version}.tar.gz BuildArch: noarch %description Simple demo rpm. %prep %setup -q %build : %install mkdir -p %{buildroot}/usr/share/demo install -m 0644 README %{buildroot}/usr/share/demo/README %files /usr/share/demo/README保存到~/rpmbuild/SPECS/demo.spec29.4 构建rpmbuild-bb~/rpmbuild/SPECS/demo.spec29.5 安装验证sudodnfinstall-y~/rpmbuild/RPMS/noarch/demo-1.0-1.noarch.rpmrpm-qldemo30. 总结你可以把 RPM 打包记成三句话**源码、脚本、服务文件先准备好放进 **SOURCES/用.spec明确描述“怎么解压、怎么编译、怎么安装、打哪些文件”用rpmbuild构建再用rpm/dnf安装验证真正掌握 rpmbuild关键不是死记命令而是理解下面这条链路Source0 → %prep → %build → %install → %{buildroot} → %files → RPM 包 → rpm/dnf 安装