rpmbuild实战

在生产环境下,未确保软件版本的可靠及软件的稳定性,我们一般不使用源码安装软件。一般会根据os的不同,选择apt-get或yum安装软件。
本文主要讲解如何使用rpmbuild工具制作rpm包。并以Openstack为例,讲述了Openstack打包的过程。

RPM基础知识

本节主要分析是什么以及为什么的问题。
以及常用的rpm和yum命令。

RPM包管理是什么?

RPM全称是RedHat Package Manager,是红帽系Linux默认使用的软件管理方法。主要包括centos、fedora、rhel等Linux分支。
其格式为Linux标准(LSB),是数据库驱动的包管理解决方案。
配合软件源,在centos系操作系统下,使用yum命令(最新版本应该是逐渐向dnf过渡)可以很容易的解决软件安装时的版本依赖问题。

为什么使用RPM包管理?

RPM包管理的优点如下:

  • 易于分发,安装、卸载、升级
  • 自动依赖跟踪,结合yum自动解决依赖问题
  • 自带包验证,保证安装的正确性
  • 保存软件包安装的一致性
  • 易于保持对系统安装包的跟踪管理

基本RPM命令

我们可以直接使用rpm命令,对软件包进行一些操作。

  • 安装
    rpm –ivh <package_name>
    --nodeps忽略依赖问题,一般仅用于测试
  • 卸载
    rpm –e <package_name>
    有其它包依赖此RPM包时,卸载会报错

  • 升级
    升级时使用-ivh会提示文件冲突
    rpm –Uvh <package_name> 没有安装过,则安装
    rpm –Fvh <package_name> 没有安装过,则忽略

  • 查找
    rpm –q[option] <package name>
    -q 仅查询是否安装
    -qi 列出软件包的详细信息
    -ql 列出软件包的目录及文件
    -qf 列出文件对应的安装包
    -qa 列出所有已安装的软件包
    -qp 列出未安装的RPM包的信息

  • 验证
    对已安装的包进行文件校验:
    rpm –V <package_name>
    rpm -Vp <rpm_file>
    rpm -Va
    导入key:
    rpm --import <RPM_KEY>
    在包安装之前进行签名校验:
    rpm -K <rpm_>file

  • 重建数据库
    rpm –rebuilddb 解决系统RPM数据库的异常

基本YUM命令

yum其实是透明的调用rpm,相当于在rpm基础上封装的更易使用的“前端”。
它主要设计来解决软件包之间的依赖性,并可从多个资源库(我们称之为“源”,典型的由/etc/yum.repos.d/目录下的每个.repo文件定义)。

  • 源制作
    createrepo <dir>
    一般的centos系的iso镜像中,都带有安装时使用的rpm包。可以将这部分文件mount到本地,创建本地源,以减少软件安装时的上网下载。
  • 安装
    yum install <package_name> -y
    yum localinstall <rpm_file> 本地安装
    yum groupinstall <group_name> 组安装
  • 升级
    yum update [package_name]
  • 卸载
    yum remove/erase <package_name>
    谨慎使用: 此命令会卸载掉所有依赖此包的RPM包
  • 查找
    yum search <name>
    yum list|grep
  • 本地缓存相关
    yum makecache 只做本地缓存
    yum clean all 清除本地缓存

  • repo文件格式
    一个典型的repo文件格式如下:

    [repo-name]
    name=repo-name
    baseurl=http://yourserver.com/path/to/repo
    enabled=1
    gpgcheck=0

RPM Build相关知识

环境搭建

  1. 软件包安装
    yum install rpm-build -y
  2. 创建目录
    ~/rpmbuild: 默认使用主目录下的rpmbuild目录
    ~/rpmbuild/SPECS: 存放spec文件
    ~/rpmbuild/SOURCES : 存放源代码等source文件
    ~/rpmbuild/RPMS : 存放RPM包
    ~/rpmbuild/BUILD和~/rpmbuild/BUILDROOT: 存放build中间文件

rpmbuild命令

rpmbuild –ba 编译二进制及源RPM包
rpmbuild –bb 仅编译二进制RPM包
rpmbuild –bs 仅编译源RPM包
编译后的二进制RPM包在~/rpmbuild/RPMS/<arch>目录下,如~/rpmbuild/RPMS/noarch
源RPM包在~/rpmbuild/SRPMS/下。

Build准备

  • 源码包
    按SPEC文件中指定名称,放在SOURCES文件夹下
  • patch、init脚本
    按SPEC文件中指定名称,放在SOURCES文件夹下
  • SPEC文件
    放在SPECS文件夹下

SPEC文件语法

spec文件内容主要:

  • 指定编译、安装源码的过程
  • 编译和生成RPM包指令的集合
  • 指定RPM包的内容及信息

语法与shell类似,也可直接使用shell命令。

Spec文件分为不同的节

不同的节用于不同的阶段,分别用于完成不同功能。
主要分为以下几个阶段:

  • Preamble
  • Setup
  • Build
  • Install
  • Clean
  • Files
  • Changelog

Preamble

此节主要用于初始化,定义RPM包的特性:

  • Name/Version/Group/License
  • Release信息:BUILD版本号
  • Sources/Patches:编译时使用的源代码及patch
  • Requirements:使用Requires、BuildRequires指定依赖包及版本。
  • Summary/Description
  • 其它自定义属性

Setup

通过解压源码包等方式,生成需要的源码树
对源码进行patch等操作
build之前的所有其它操作,如删除不需要的文件
例子:

%prep #定义prep节开始
%setup –q #解压Source0
%patch0 –p1 #对源码包使用patch0

Build

编译二进制文件
可以使用%configure指定build时需要加入的参数
举例:

%build
%configure
%{__python} setup.py build #python编译
make %{?_smp_mflags} #C语言等使用make编译

Install

创建BUILDROOT目录
在BUILDROOT中创建需要的目录结构
将编译后的文件安装在BUILDROOT对应位置
清理不需要的文件
可以通过%pre/%post/%preun/%postun分别指定安装前、安装后、卸载前、卸载后要执行的动作。

举例:

%install  #执行python setup.py install进行安装,跳过编译阶段
%{__python} setup.py install -O1 --skip-build --root %{buildroot} #在%{_sharedstatedir}这个宏指定的目录下安装文件夹hello,并指定权限
install -d -m 755 %{buildroot}%{_sharedstatedir}/cinder
%pre
%post
%preun
%postun

Clean

清理资源,如移除BUILDROOT目录
举例:

%clean
rm –rf %{buildroot}

Files

指定所有RPM安装的文件
Install但未指定的文件,会报错
举例:

%files
%defattr(-, cinder, cinder, -) #指定权限
#已安装的文件,注意与install处不同,文件前无%{buildroot}
%dir %{_sharedstatedir}/cinder

Changelog

用于跟踪spec文件的修改记录
应该在每次修改后增加相关记录
举例:

%changelog
* Fri May 16 2014 Fansp <wangbin@awcloud.com> - 2014.1-15
Make a demo
* Mon Apr 21 2014 Eric Harney <eharney@redhat.com> - 2014.1-2
- Remove qpid settings from cinder-dist.conf

常用的宏

使用宏,而不是指定绝对路径,增加灵活性。因为不同系统中,使用不同系统路径,spec文件无需修改。
内置宏定义在 /usr/lib/rpm/macros文件中。同时支持自定义宏。

# Built-in macros
%_prefix /usr
%_exec_prefix %{_prefix}
%_bindir %{_exec_prefix}/bin
%_sbindir %{_exec_prefix}/sbin
%_libexecdir %{_exec_prefix}/libexec
%_datadir %{_prefix}/share
%_sysconfdir %{_prefix}/etc
%_sharedstatedir %{_prefix}/com
%_localstatedir %{_prefix}/var
%_libdir %{_exec_prefix}/lib
%_includedir %{_prefix}/include
%_oldincludedir /usr/include
%_infodir %{_prefix}/info
%_mandir %{_prefix}/man

RPM Build最佳实践建议

总结的一些经验教训:

  • 对于源码需要的修改,使用patch,而不是RPM Hacks
  • 避免脚本,减少pre/post的使用,逻辑尽量放在代码中
  • 使用Changelog或版本控制,便于查找及维护
  • 避免使用root用户制作RPM包
  • 在Fedora/EPEL中查找现成RPM包、SPEC文件

Openstack RPM制作

打包前准备

编写需要的SPEC文件及init脚本。或使用已有的SRPM包,如通过RDO:https://repos.fedorapeople.org/repos/openstack/

  1. 解压SRPM找到spec文件,放在SPECS目录下
  2. 找到其他source文件,放在SOURCES目录下
  3. Git clone代码,压缩为SPEC文件指定的名称,放在SOURCES目录下
  4. 按需修改各文件
    rpmbuild –ba <spec_file>
  5. 安装RPM包进行测试

以Nova为例

  • SPEC文件
    openstack-nova.spec
  • SOURCES
    nova-2014.1.3.tar.gz
    .init及.upstart文件,conf及logrotate等文件
  • 需要的patch
  • rpmbuild
    rpmbuild –ba openstack-nova.spec
  • 按照RDO文档依次安装RPM包搭建环境进行测试

各组件对应spec文件

  • nova - openstack-nova.spec
  • Neutron - openstack-neutron.spec
  • Keystone - openstack-keystone.spec
  • Glance - openstack-glance.spec
  • Cinder - openstack-cinder.spec
  • Swift - openstack-swift.spec
  • Horizon - python-django-horizon.spec

参考索引