一、准备制作环境

我系统是centos 6.6 64位,rpmdevtools 版本为7.5,rpm-build版本为4.8,本文以制作openresty-1.9.7.1的rpm包为例。

1.1 安装打包工具

制作rpm包主要用到rpmbuild工具,rpmdevtools套件包含了rpmbuild等软件,同时又提供了很多有用的脚本,安装:

 yum install rpmdevtools

spec配置检查工具:

yum install rpmlint

查看下rpmbuild的版本:

rpmbuild --version
RPM version 4.8.0

1.2 新建用户

rpm包在打包过程中,会将文件临时安装到系统中,为了保证构建程序不破坏系统,要使用普通用户打包:

useradd rpmuser
su - rpmuser

1.3 初始化打包环境

rpmdev-setuptree是rpmdevtools中带的初始化环境脚本,执行后会在用户主目录下创建rpmbuild目录和.rpmmacros文件,同时生成所需的子目录和默认的控制参数,执行:

rpmdev-setuptree

生成的目录结构:

rpmbuild/
    ├── BUILD   #打包过程中的工作目录
    ├── RPMS    #RPM包存放目录
    ├── SOURCES #源码和补丁目录
    ├── SPECS    #SPEC文件目录
    └── SRPMS    #src rpm包存放目录

默认的全局控制宏文件.rpmmacros内容:

cat ~/.rpmmacros 
%_topdir      %(echo $HOME)/rpmbuild
%_smp_mflags  -j3
%__arch_install_post   /usr/lib/rpm/check-rpaths   /usr/lib/rpm/check-buildroot

1.4 安装编译软件

制作rpm包要先编译在打二进制包,所以要装gcc、make等工具:

yum install gcc gcc-c++ make

准备环境就结束了

二、制作RPM包

以打包openresty为例

2.1 下载源码包

将源码包和补丁等放入rpmbuild/SOURCE目录:

cd rpmbuild/SOURCE
wget https://openresty.org/download/ngx_openresty-1.9.7.1.tar.gz

2.2 编写SPEC文件

SPEC文件控制整个rpm的打包过程,包含了打包的全部信息和控制参数,可以使用 rpmdev-newspec 命令来创建一个初始的SPEC文件,SPEC文件需放入rpmbuild/SPECS目录:

rpmdev-newspec  ngx_openresty.spec

这个脚本也自带了一些软件的模版,在/etc/rpmdevtools/spectemplate-*。spec文件主要定义了打包过程中的几个阶段的配置:

  1. 信息定义阶段 :定义软件包的名字、版本等信息和打包过程中用到的变量等
  2. 制作准备阶段 :解压源码包、补丁包并打补丁
  3. 编译阶段 :设置编译参数并编译源码
  4. 安装阶段 :定义安装过程所执行的命令,包括安装前、安装后、卸载前、卸载后执行的操作
  5. 清理阶段 :清理打包过程中用到的目录文件等
  6. 文件设置阶段 :要打包的文件和目录,并设置文件权限等

主要就是以上几个阶段,其中用到的宏比较多,看起来容易晕,遇到不明白的可以查最下面的参考文章。
附上ngx_openresty.spec文件:

## 1. 信息定义阶段

### 自定义宏,不是必须
#自定义一个宏名字为nginx_user值为nginx,%{nginx_user}引用 
%define nginx_user nginx    
### 软件名字,要与spec的文件名一致 openresty
Name:           ngx_openresty    
### 软件主版本号,参考下载的源码包      tengine-2.1.1.tar.gz 
Version:        1.9.7.1            
### 发行编号,每打包一次值递增,主版本号发布新版后需重置该值
Release:        4%{?dist}
### 一行简短的软件简介,结尾不要加标点
Summary:        OpenResty  is a full-fledged web platform 
### 安装后所属的组, 通过/usr/share/doc/rpm-4.8.0/GROUPS选择,部分发行版已经废除此标签
Group:          System Environment/Daemons  
### 软件许可
License:        GPLv2  
### 软件项目主页                       
URL:           https://openresty.org/ 
### 放置在SOUIRCES目录的软件源码包名,可以指定多个:source1、source2等
Source0:        %{name}-%{version}.tar.gz   
### 补丁名,也可以写多个 patch1、patch2等 
#patch0:            0.patch 
### 在 install 阶段的测试安装目录,方便写files               
#buildroot:      %_topdir/BUILDROOT         
### 编译过程所需的软件 
BuildRequires:  gcc,make                           
### 安装软件包时所需的依赖包列表,可以指定版本如 bash >= 1.1.1
Requires:       readline-devel,pcre-devel,openssl-devel
### 程序的详细多行描述,每行必须小于等于 80 个字符,空行表示开始新段
%description 
OpenResty is a full-fledged web platform by integrating the standard Nginx core,
LuaJIT, many carefully written Lua libraries, lots of high quality 
3rd-party Nginx modules, and most of their external dependencies. 
It is designed to help developers easily build scalable 
web applications, web services, and dynamic web gateways.
this rpm created by nixops.me 
### 2.准备阶段 

%prep    
### 静默模式解压并进入解压后的目录,也常用:%autosetup -n %{name}                                            
#%autosetup -n %{name}-%{version}.tar.gz
%setup -q        
### 需要打补丁,在这里写打补丁的命令                         
#%patch0 -p1                                       

### 3. 编译阶段

%build 
### 编译参数
./configure \
    --with-luajit \
    --without-http_redis2_module \
    --without-http_xss_module \
    --without-http_memc_module \
    --user=nginx \
    --group=nginx \

#使用多核处理器并行编译 
make %{?_smp_mflags}          
                                   
### 4.安装阶段 

%install 
### 删除之前的残留文件                                
rm -rf %{buildroot}   
### 指定安装目录为虚拟目录         
#gmake install DESTDIR=%{buildroot} 
gmake install DESTDIR=$RPM_BUILD_ROOT
#如果makefile不支持make install DESTDIR=$RPM_BUILD_ROOT,可以手写安装流程,即先建好目标目录,在复制文件
### rpm安装前制行的脚本 
%pre  
### $1==1 代表的是第一次安装,2代表是升级,0代表是卸载 
if [ $1 == 1 ];then     
     /usr/sbin/useradd -r %{nginx_user} 2> /dev/null 
fi 
### 安装后执行的脚本
%post        
###卸载前执行的脚本
%preun       
if [ $1 == 0 ];then 
     /usr/sbin/userdel -r %{nginx_user} 2> /dev/null 
fi 
### 卸载后执行的脚本
%postun 
                         
### 5.清理阶段

%clean 
### 删除buildroot目录
rm -rf %{buildroot} 
                             
###  6.文件设置阶段 

%files  
### 设定默认权限,如果下面没有指定权限,则继承默认
%defattr (-,root,root,0755)    
###要打包的文件和目录,在执行完rpmbuild -bi后,参考%{rootbuild}下生成的文件和目录
/usr/ 
                                   
###  变更日志 格式固定,生成请用命令: rpmdev-bumpspec --comment=COMMENT --userstring=NAME+EMAIL_STRING SPECFILES
%changelog
* Thu Dec 31 2015 will <will@nixops.me> - 1.9.7.1-4
- openresty init

编辑好ngx_openresty.spec文件后检查一下配置是否有错误:

rpmlint ngx_openresty.spec

如果有错误或者警告,使用 "-i" 选项可以查看更详细的信息

2.3 开始打包rpm

rpmbuild命令会根据spec文件来生成rpm包,主要用到以下几个参数:

rpmbuild  --help 
-bp 执行到%prep阶段结束
-bc 执行到%build阶段结束
-bi 执行到%install阶段结束 
-bl 检测%files文件是否有丢失
-ba 创建src.rpm和二进制包 
-bs 只创建src.rpm源码包 
-bb 只创建二进制rpm包 

常用的参数就这几个,其它的自己看man手册,开始打包:

rpmbuild -bp ngx_openresty.spec
rpmbuild -bc ngx_openresty.spec
rpmbuild -bi ngx_openresty.spec
rpmbuild -bl ngx_openresty.spec
rpmbuild -ba ngx_openresty.spec

分阶段测试,方便排错,如果哪个阶段有错误,可以使用--short-circuit跳过之前成功的阶段,节省时间:

rpmbuild -bi --short-circuit  ngx_openresty.spec

我这个spec文件为了省事,没有手写%install过程,导致rpmbuild -bi会报一个RPATH错误,不影响打包,但是要跳过:

QA_RPATHS=$[ 0x0002|0x0010 ] rpmbuild -ba ngx_openresty.spec

到这里就会在RPMS目录和SRPMS目录生产对应的rpm包,用root进行安装测试:

rpm -ivh RPMS/x86_64/ngx_openresty-1.9.7.1-4.el6.x86_64.rpm

默认安装到/usr/local/openresty,测试程序是否正常运行,卸载:

rpm -e ngx_openresty-1.9.7.1-4.el6.x86_64

三、RPM包签名

对rpm包进行签名可以防止软件包被篡改,像epel、remi等公共软件源都会使用签名,签名要用到gnupg软件包,查询是否安装:

rpm -qf `which gpg`

3.1 生成密钥对

此过程要用root执行,使用普通用户会出现错误

gpg --gen-key
Please select what kind of key you want: 默认
What keysize do you want? (2048) 默认
Key is valid for? (0) 默认
is this correct? (y/N) y 
Real name: willis 名字
Email address: willis@nixops.me 邮箱
Comment: GPG-RPM-KEY
hange (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
这步之后会要求输入密码,可以为空

生成密钥对时会使用/dev/random生成的真随机数(通过噪音产生),可能会因熵池不够而阻塞,需要安装rng-tools自动补充熵池:

yum install rng-tools

在 /etc/sysconfig/rngd中添加:

EXTRAOPTIONS="-r /dev/urandom"

启动:

service rngd start

查看熵池大小:

cat /proc/sys/kernel/random/entropy_avail

如果是几十,产生密钥对时就会卡住
将/root/.gnupg 移到 /home/rpmuser,并将权限改为rpmuser:

mv /root/.gnupg /home/rpmuser 
chown rpmuser.rpmuser /home/rpmuser/ -R

切换到rpmuser查看公钥:

 gpg --list-keys

3.2 rpm软件包签名

切换回rpmuser,在~/.rpmmacros中添加:

%_signature gpg
%_gpg_name willis

对rpm软件包签名:

rpm --addsign ngx_openresty-1.9.7.1-4.el6.x86_64.rpm
Enter pass phrase:    gpg密码,这里没有设置
Pass phrase is good.
ngx_openresty-1.9.7.1-4.el6.x86_64.rpm:

在rpmbuild打包时签名:

 rpmbuild --sign --bb ~/rpmbuild/SPECS/ngx_openresty.spec

3.3 验证签名

验证软件包签名要将公钥导入到rpm的数据库中,然后用rpm命令验证,导出公钥:

gpg --export -a willis >GPG-RPM-KEY-nixops

查看当前rpm数据库中已有的公钥:

rpm -q gpg-pubkey-*
gpg-pubkey-c105b9de-4e0fd3a3
gpg-pubkey-0608b895-4bd22942

使用root导入到rpm数据库中:

 rpm --import GPG-RPM-KEY-nixops

在查询一下:

rpm -q gpg-pubkey-*
gpg-pubkey-c105b9de-4e0fd3a3
gpg-pubkey-0608b895-4bd22942
gpg-pubkey-a2bc02d9-56849054

最后一个就是导入的公钥,验证签名:

rpm -K  rpmbuild/RPMS/x86_64/ngx_openresty-1.9.7.1-4.el6.x86_64.rpm 
rpmbuild/RPMS/x86_64/ngx_openresty-1.9.7.1-4.el6.x86_64.rpm: rsa sha1 (md5) pgp md5 OK

验证成功

参考文章:
https://fedoraproject.org/wiki/How_to_create_an_RPM_package/zh-cn
http://laoguang.blog.51cto.com/6013350/1103628
http://www.worldhello.net/2011/04/12/2442.html