一. 简介
之前介绍yum是基于rpm的包管理工具,yum最终安装的是rpm包,那rpm包是如何来的呢?
按照网上的教程制作了个rpm包,这里记录下。
二. 过程
1)安装制作工具
yum -y install rpm-build rpm-devel rpmdevtools
2)生成开发目录
rpmdev-setuptree
3)源代码
现在,我们来编码源代码,这里我们写个脚本,输出Hello World!
mkdir -p ~/rpmbuild/SOURCES/helloworld-1.0.0
cd ~/rpmbuild/SOURCES/helloworld-1.0.0
touch helloworld
chmod 755 helloworld
echo '#!/bin/sh' >> helloworld
echo 'echo Hello World!' >> helloworld
cd ~/rpmbuild/SOURCES
tar zcvf helloworld-1.0.0.tar.gz helloworld-1.0.0
这样我们就有了我们的源代码helloworld-1.0.0.tar.gz
4)编写spec文件
cd ~/rpmbuild/SPECS
# 生成spec模版文件
rpmdev-newspec helloworld.spec
将内容修改为
Name: helloworld
Version: 1.0.0
Release: 1%{?dist}
Summary: helloworld
Group: Development/Tools
License: GPL
#URL:
Source0: %{name}-%{version}.tar.gz
#BuildRequires:
#Requires:
%description
%prep
%setup -q
%build
%install
mkdir -p $RPM_BUILD_ROOT/usr/bin
cp $RPM_BUILD_DIR/%{name}-%{version}/helloworld $RPM_BUILD_ROOT/usr/bin/
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
%doc
/usr/bin/helloworld
%changelog
5)打包
rpmbuild -ba SPECS/helloworld.spec
查看下最后的目录结构
tree ~/rpmbuild/
/root/rpmbuild/
├── BUILD
│ └── helloworld-1.0.0
│ ├── debugfiles.list
│ ├── debuglinks.list
│ ├── debugsources.list
│ ├── elfbins.list
│ └── helloworld
├── BUILDROOT
├── RPMS
│ └── x86_64
│ ├── helloworld-1.0.0-1.el7.x86_64.rpm
│ └── helloworld-debuginfo-1.0.0-1.el7.x86_64.rpm
├── SOURCES
│ ├── helloworld-1.0.0
│ │ └── helloworld
│ └── helloworld-1.0.0.tar.gz
├── SPECS
│ └── helloworld.spec
└── SRPMS
└── helloworld-1.0.0-1.el7.src.rpm
BUILD是编译rpm包的临时目录
BUILDROOT是最后生成rpm包的临时安装目录
RPMS存放最终生成的rpm二进制包
SOURCES是源代码(.tar.gz)存放目录
SPECS用来存放spec文件
SRPMS存放最终生成的rpm源码包
rpmbuild/RPMS/x86_64/helloworld-1.0.0-1.el7.x86_64.rpm就是我们打出来的rpm包
6)安装
yum install ~/rpmbuild/RPMS/x86_64/helloworld-1.0.0-1.el7.x86_64.rpm
或者
rpm -ivh helloworld-1.0.0-1.el7.x86_64.rpm
命令行运行
helloworld
Hello World!
好啦,成功了!
三. spec
好了,回过头再看看打包的步骤,最重要的莫过于helloworld.spec
了,我们来详细解释下。
Name: helloworld
Version: 1.0.0
Release: 1%{?dist}
Summary: helloworld
Group: Development/Tools
License: GPL
名称、版本号、打包版本号、简介、Group、License
Source0: %{name}-%{version}.tar.gz
源代码路径,要确保在SOURCES目录下能找到该包
#BuildRequires:
编译需要的环境,如gcc>=4.7,这里不需要
#Requires:
该软件运行的依赖,如python rpm>=0:4.1.1,这里不需要
%description
详细描述
%prep
%setup -q
编译前的准备操作在这儿,%setup macro 会把 source code tarball 解开并自动进到 %{name}-%{version} 的目录中
%build
编译,对应源代码打包的./configure和make,例如(%configure –prefix=%{_prefix})
%install
mkdir -p $RPM_BUILD_ROOT/usr/bin
cp $RPM_BUILD_DIR/%{name}-%{version}/helloworld $RPM_BUILD_ROOT/usr/bin/
安装,对应源代码包打包的make install,一般我们是安装在$RPM_BUILD_ROOT下
%files
%defattr(-,root,root,-)
%doc
/usr/bin/helloworld
收集文件并创建二进制和源RPM文件
%clean
rm -rf $RPM_BUILD_ROOT
删除临时构建目录
四. 一些变量
%{_bindir} /usr/bin/
MYSQL打包示例
1、把原码包mysql-5.7.17.tar.gz上传到SOURCES目录下,并在SPECS目录下添加配置好的mysql.spec文件:
由于在Mysql5.6后都要依赖boost_1_59_0.tar.gz包,所以这里我直接解压到BUILD目录下,不然检测不到;
tar xf boost_1_59_0.tar.gz -C rpmbuild/BUILD/
2、配置mysql.spec文件,还需要添加一个my.cnf配置文件到SOURCES目录下;
Name: mysql
Version: 5.7.17
Release: 1%{?dist}
License: GPL
URL: http://downloads.mysql.com/archives/get/file/mysql-5.7.17.tar.gz
Group: applications/database
Source: mysql-5.7.17.tar.gz
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
BuildRequires: cmake
Packager: hz328@qq.com
Autoreq: no
#Source: %{name}-%{version}.tar.gz
prefix: /usr/local/mysql-%{version}
Summary: MySQL 5.7.17
%description
The MySQL(TM) software delivers a very fast, multi-threaded, multi-user,
and robust SQL (Structured Query Language) database server. MySQL Server
is intended for mission-critical, heavy-load production systems as well
as for embedding into mass-deployed software.
%define MYSQL_USER mysql
%define MYSQL_GROUP mysql
%prep
%setup -n mysql-%{version}
%build
#CFLAGS="-O3 -g -fno-exceptions -static-libgcc -fno-omit-frame-pointer -fno-strict-aliasing"
#CXX=g++
#CXXFLAGS="-O3 -g -fno-exceptions -fno-rtti -static-libgcc -fno-omit-frame-pointer -fno-strict-aliasing"
#export CFLAGS CXX CXXFLAGS
cmake \
-DCMAKE_INSTALL_PREFIX=%{prefix} \
-DMYSQL_DATADIR=/data \
-DWITH_BOOST=../boost_1_59_0 \
-DSYSCONFDIR=/etc \
-DWITH_MYISAM_STORAGE_ENGINE=1 \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_MEMORY_STORAGE_ENGINE=1 \
-DWITH_READLINE=1 \
-DMYSQL_UNIX_ADDR=/tmp/mysql.sock \
-DMYSQL_TCP_PORT=3306 \
-DENABLED_LOCAL_INFILE=1 \
-DWITH_PARTITION_STORAGE_ENGINE=1 \
-DEXTRA_CHARSETS=all \
-DDEFAULT_CHARSET=utf8 \
-DDEFAULT_COLLATION=utf8_general_ci
make -j `cat /proc/cpuinfo | grep processor| wc -l`
%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}
cp %{_sourcedir}/my.cnf $RPM_BUILD_ROOT%{prefix}/
%pre
mkdir -p /data
useradd -s /bin/nologin -M mysql >/dev/null 2>&1
%post
/bin/cp %{prefix}/support-files/mysql.server /etc/init.d/mysql
/bin/cp %{prefix}/my.cnf %{_sysconfdir}/my.cnf
chkconfig mysql on
%{prefix}/bin/mysqld --initialize-insecure --basedir=%{prefix} --datadir=/data/mysql --user=mysql
service mysql start
chown -R mysql:mysql /data/mysql
echo "export PATH=.:\$PATH:/usr/local/mysql-%{version}/bin;" >> ~/.bash_profile
source ~/.bash_profile
ln -s %{prefix}/lib %{prefix}/lib64
%preun
service mysql stop
chkconfig --del mysql
userdel -r mysql >/dev/null 2>&1
rm -rf /usr/local/mysql-%{version} >/dev/null 2>&1
rm -rf /data/mysql >/dev/null 2>&1
rm -rf /etc/init.d/mysql >/dev/null 2>&1
%files
%defattr(-, %{MYSQL_USER}, %{MYSQL_GROUP})
%attr(755, %{MYSQL_USER}, %{MYSQL_GROUP}) %{prefix}/*
%changelog
3、执行命令打包,需要一点时间看机器的配置;
rpmbuild -bb rpmbuild/SPECS/mysql-5.7.17-new.spec
4、完成后在rpmbuild/RPM目录下就会两个rpm包;
[root@rhce ~]# ll rpmbuild/RPMS/x86_64/
total 267280
-rw-r--r-- 1 root root 80603892 May 28 19:47 mysql-5.7.17-1.el6.x86_64.rpm
-rw-r--r-- 1 root root 193088996 May 28 19:48 mysql-debuginfo-5.7.17-1.el6.x86_64.rpm
5、直接安装,安装完成后会直接启动Mysql:
[root@rhce ~]# rpm -ivh rpmbuild/RPMS/x86_64/mysql-5.7.17-1.el6.x86_64.rpm
Preparing... ########################################### [100%]
1:mysql ########################################### [100%]
Starting MySQL...[ OK ]
不关闭服务直接卸载:
[root@rhce ~]# rpm -e --nodeps mysql-5.7.17-1.el6.x86_64
Shutting down MySQL..[ OK ]
注意这里配置了,在卸载服务时会把mysql目录删除,所以要先备份数据;
配置文件主体介绍
spec脚本主体
spec脚本的主体中也包括了很多关键字和描述,下面会一一列举。我会把一些特别需要留意的地方标注出来。
%prep 预处理脚本
%setup -n %{name}-%{version} 把源码包解压并放好
通常是从/usr/src/asianux/SOURCES里的包解压到/usr/src/asianux/BUILD/%{name}-%{version}中。
一般用%setup -c就可以了,但有两种情况:一就是同时编译多个源码包,二就是源码的tar包的名称与解压出来的目录不一致,此时,就需要使用-n参数指定一下了。
%patch 打补丁
通常补丁都会一起在源码tar.gz包中,或放到SOURCES目录下。一般参数为:
%patch -p1 使用前面定义的Patch补丁进行,-p1是忽略patch的第一层目录
%Patch2 -p1 -b xxx.patch 打上指定的补丁,-b是指生成备份文件
◎补充一下
引用
%setup 不加任何选项,仅将软件包打开。
%setup -n newdir 将软件包解压在newdir目录。
%setup -c 解压缩之前先产生目录。
%setup -b num 将第num个source文件解压缩。
%setup -T 不使用default的解压缩操作。
%setup -T -b 0 将第0个源代码文件解压缩。
%setup -c -n newdir 指定目录名称newdir,并在此目录产生rpm套件。
%patch 最简单的补丁方式,自动指定patch level。
%patch 0 使用第0个补丁文件,相当于%patch ?p 0。
%patch -s 不显示打补丁时的信息。
%patch -T 将所有打补丁时产生的输出文件删除。
%configure 这个不是关键字,而是rpm定义的标准宏命令。意思是执行源代码的configure配置
在/usr/src/asianux/BUILD/%{name}-%{version}目录中进行 ,使用标准写法,会引用/usr/lib/rpm/marcros中定义的参数。
另一种不标准的写法是,可参考源码中的参数自定义,例如:
引用
CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{_prefix}
%build 开始构建包
在/usr/src/asianux/BUILD/%{name}-%{version}目录中进行make的工作 ,常见写法:
引用
make %{?_smp_mflags} OPTIMIZE="%{optflags}"
都是一些优化参数,定义在/usr/lib/rpm/marcros中
%install 开始把软件安装到虚拟的根目录中
在/usr/src/asianux/BUILD/%{name}-%{version}目录中进行make install的操作。这个很重要,因为如果这里的路径不对的话,则下面%file中寻找文件的时候就会失败。 常见内容有:
%makeinstall 这不是关键字,而是rpm定义的标准宏命令。也可以使用非标准写法:
引用
make DESTDIR=$RPM_BUILD_ROOT install
或
引用
make prefix=$RPM_BUILD_ROOT install
需要说明的是,这里的%install主要就是为了后面的%file服务的。所以,还可以使用常规的系统命令:
引用
install -d $RPM_BUILD_ROOT/
cp -a * $RPM_BUILD_ROOT/
%clean 清理临时文件
通常内容为:
引用
[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf "$RPM_BUILD_ROOT"
rm -rf $RPM_BUILD_DIR/%{name}-%{version}
※注意区分$RPM_BUILD_ROOT和$RPM_BUILD_DIR:
$RPM_BUILD_ROOT是指开头定义的BuildRoot,而$RPM_BUILD_DIR通常就是指/usr/src/asianux/BUILD,其中,前面的才是%file需要的。
%pre rpm安装前执行的脚本
%post rpm安装后执行的脚本
%preun rpm卸载前执行的脚本
%postun rpm卸载后执行的脚本
%preun %postun 的区别是什么呢?
前者在升级的时候会执行,后者在升级rpm包的时候不会执行
%files 定义那些文件或目录会放入rpm中
这里会在虚拟根目录下进行,千万不要写绝对路径,而应用宏或变量表示相对路径。 如果描述为目录,表示目录中除%exclude外的所有文件。
%defattr (-,root,root) 指定包装文件的属性,分别是(mode,owner,group),-表示默认值,对文本文件是0644,可执行文件是0755
%exclude 列出不想打包到rpm中的文件
※小心,如果%exclude指定的文件不存在,也会出错的。
%changelog 变更日志
fpm快速制作rpm包
FPM功能简单说就是将一种类型的包转换成另一种类型。FPM是Ruby模块,其实打包时也是调用rpmbuild命令。
支持的源类型包:
- dir 将目录打包成所需要的类型,可以用于源码编译安装的软件包
- rpm 对rpm进行转换
- gem 对rubygem包进行转换
- python 将python模块打包成相应的类型
支持的目标类型包:
- rpm 转换成rpm包
- deb 转换成deb包
- solaris 转换成solaris包
- puppet 转换成puppet模块
安装FPM
- 安装依赖包
# yum -y install ruby rubygems ruby-devel gcc make
- 添加仓库
# gem sources -a http://mirrors.aliyun.com/rubygems/
- 移除原有的仓库
# gem sources --remove https://rubygems.org/
# gem sources --remove http://rubygems.org/
- 查看仓库是不是只有自己添加的那个仓库地址
# gem sources -l
http://mirrors.aliyun.com/rubygems/
- 安装fpm
# gem install fpm
如果遇到报错:
Building native extensions. This could take a while...
ERROR: Error installing fpm:
ffi requires Ruby version >= 1.9.
原因:安装fpm需要依赖包ruby的版本在1.9以上,那么升级ruby就可以了。
解决:
1. 使用rvm安装,首先需要安装rvm
先查看你是否安装过rvm, 执行命令
rvm -v
2.yum -y update nss
curl -L get.rvm.io | bash -s stable
执行成功后,根据尾部的提示执行:source /etc/profile.d/rvm.sh (路径以尾部提示为准)
3. 查看是否安装成功, 输入:rvm -v
4. 列表显示都可以安装ruby的哪些版本
rvm list known
5. 更新ruby至版本2.3.0
rvm install 2.3.0
- 如何查看fpm帮助
# fpm --help
常用参数:
- -s 指定源类型
- -t 指定目标类型
- -n 指定包的名字
- -v 指定包的版本号
- -C 指定打包的相对路径 change directory to here before searching for files
- -d 指定依赖于哪些包
- -f 第二次打包时目录下如果有同名安装包存在,则覆盖它
- -p 输出的安装包的目录,不想放在当前目录下就需要指定
- –post-install 软件包安装完成之后所要运行的脚本,同–after-install
- –pre-install 软件包安装完成之前所要运行的脚本,同–before-install
- –post-uninstall 软件包卸载之后所要运行的脚本,同–after-install
- –pre-uninstall 软件包卸载之前所要运行的脚本,同–before-install
打包示例
- 打包MySQL
事先安装好MySQL,MySQL安装过程这里不在详述。命令行终端输入以下命令,然后等待rpm包制作完成。
# fpm -s dir -t rpm -n mysql -v 5.6.27 --description 'author: jkzhao' -d 'libaio' -d 'libaio-devel' --pre-install /usr/local/mysql/mysql_pre_init.sh --post-install /usr/local/mysql/mysql_post_init.sh /usr/local/mysql /usr/local/mysql-5.6.27-linux-glibc2.5-x86_64 /data
【注意】:默认打好的包是在当前目录下。
命令说明:
- -s dir:指定源文件是目录的形式
- -t rpm:指定打包的格式
- -n:指定打包后名称
- -v:版本号
- –description:描述信息
- -d:指定需要依赖的包。安装MySQL前需要在系统上安装libaio、libaio-devel。当你安装fpm打包成的rpm包时,它会先去检测系统上是否安装了这两个包,如果没有安装会给出提示,并终止rpm的安装。
- –pre-install:安装rpm包前需要执行的脚本
- –post-install:安装rpm包后需要执行的脚本
mysql_pre_init.sh的内容如下:
#!/bin/bash
user=mysql
group=mysql
# create group if not exists.
egrep "^$group" /etc/group >& /dev/null
if [ $? -ne 0 ]
then
groupadd -r -g 300 $group
fi
# create user if not exists.
egrep "^$user" /etc/passwd >& /dev/null
if [ $? -ne 0 ]
then
useradd -g $group -r -s /sbin/nologin -u 300 $user
fi
mysql_post_init.sh的内容如下:
#!/bin/bash
# cp my.cnf force.
\cp /usr/local/mysql/my.cnf /etc/
# start/stop/restart script.
\cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
chkconfig --add mysqld
# MySQL Client PATH.
\cp /usr/local/mysql/mysql.sh /etc/profile.d/
cd /usr/local/mysql
chown -R root.mysql .
chown -R mysql.mysql /data
打包完成后正常安装,如:
# rpm -ivh mysql-5.6.27-1.x86_64.rpm
- 打包openresty
事先安装好openresty。命令行终端输入以下命令,然后等待rpm包制作完成。
# fpm -s dir -t rpm -n openresty -v 1.9.7.3 --description 'author: jkzhao' -d 'openssl-devel' -d 'readline-devel' -d 'pcre-devel' -d 'gcc' --post-install /usr/local/openresty/openresty_init.sh /usr/local/openresty
如果报如下错误:
Need executable 'rpmbuild' to convert dir to rpm {:level=>:error}
解决:
# yum install -y rpm-build
openresty_init.sh的内容如下:
#!/bin/bash
mv /usr/local/openresty/nginx.service /etc/systemd/system/
systemctl enable nginx.service
卸载安装后的rpm包
如果重新安装rpm,必须先卸载rpm包,然后删除相应的目录及文件,否则再次安装时会报错。
【注意】:卸载不要先直接删除目录和文件,否则你再次安装这个包时会说已安装,冲突了。
# rpm -e --nodeps mysql-5.6.27-1.x86_64.rpm
然后再去删除各个目录和配置文件。