1. RPM(RedHat Packager Manager) Basics
1.1 rpm和srpm
rpm即软件包的一种管理方式,类似于安装包,使用它可以将相应的软件安装到系统上,包含的软件必须是经过编译的。
格式为:unzip-6.0-20.el7.centos.es.x86_64.rpm(n: unzip, v:6.0, r: 20.el7.centos.es,a:x86_64)
安装时会将软件信息写入到RPM数据库(/var/lib/rpm),便于查询。
rpm -ivh /tmp/unzip-6.0-41.el8.es.src.rpm // 安装并可查看安装信息及进度
rpm -qa // 查看安装的所有软件
rpm -qi unzip // 查看某个安装的软件详细信息
优点:编译打包完成,无需自己手动编译,安装方便
缺点1:安装的环境需要与打包时的环境需求一直或相当
srpm的存在可以使得包重新编译,srpm即为Source RPM,内容没有经过编译,提供原始代码,格式为: unzip-6.0-20.el7.src.rpm,因为我们安装需要环境一致等条件,我们不得不重新编译我们的rpm包
1.2 srpm解压后的文件内容
查看下文件解压后的内容:
# 使用此命令解压到指定文件夹
rpm -i --define '_topdir /root/build/unzip' \
/opt/unzip-6.0-20.el7.src.rpm
[root@localhost ~]# tree build/
build/
└── unzip
├── SOURCES
│ ├── unzip-6.0-alt-iconv-utf8.patch
│ ├── unzip-6.0-alt-iconv-utf8-print.patch
│ ├── unzip-6.0-attribs-overflow.patch
│ ├── unzip-6.0-bzip2-configure.patch
│ ├── unzip-6.0-caseinsensitive.patch
│ ├── unzip-6.0-close.patch
│ ├── unzip-6.0-configure.patch
│ ├── unzip-6.0-cve-2014-8139.patch
│ ├── unzip-6.0-cve-2014-8140.patch
│ ├── unzip-6.0-cve-2014-8141.patch
│ ├── unzip-6.0-cve-2014-9636.patch
│ ├── unzip-6.0-cve-2018-18384.patchldd
│ ├── unzip-6.0-exec-shield.patch
│ ├── unzip-6.0-fix-recmatch.patch
│ ├── unzip-6.0-format-secure.patch
│ ├── unzip-6.0-manpageandusage.patch
│ ├── unzip-6.0-manpage-fix.patch # 补丁
│ ├── unzip-6.0-symlink.patch
│ └── unzip60.tar.gz # 源代码
└── SPECS
└── unzip.spec # 配置文件
大概看下spec文件:
# 软件的基本信息
Summary: A utility for unpacking zip files
Name: unzip
Version: 6.0
Release: 20%{?dist}
License: BSD
Group: Applications/Archiving
Source: http://downloads.sourceforge.net/infozip/unzip60.tar.gz
# Not sent to upstream.
Patch1: unzip-6.0-bzip2-configure.patch
# Upstream plans to do this in zip (hopefully also in unzip).
Patch2: unzip-6.0-exec-shield.patchl
URL: http://www.info-zip.org/UnZip.html
# 制作成rpm包安装时,系统检查依赖的软件包
Requires: python-argparse, python-importlib
# 构建该软件包的依赖
BuildRequires: bzip2-devel
# build
%build
make -f unix/Makefile CF_NOOPT="-I. -DUNIX $RPM_OPT_FLAGS -DNOMEMCPY -DNO_LCHMOD" LFLAGS2="%{?__global_ldflags}" generic_gcc %{?_smp_mflags}
# install
%install
rm -rf $RPM_BUILD_ROOT
make -f unix/Makefile prefix=$RPM_BUILD_ROOT%{_prefix} MANDIR=$RPM_BUILD_ROOT/%{_mandir}/man1 INSTALL="cp -p" install
# 编译出来的binary rpm包名称及各个binary rpm包含的文件
%files
...
# changelog
%changelog
* Mon Feb 25 2019 Jakub Martisko <jamartis@redhat.com> - 6.0-20
- Fix CVE-2018-18384
Resolves: CVE-2018-18384
* Tue Jan 09 2018 Jakub Martisko <jamartis@redhat.com> - 6.0-19
- rename patch unzip-6.0-nostrip.patch to unzip-6.0-configure.patch
- make linking flags configurable from the specc file
- set linking flags to use relro hardening
Related: #1531116
1.3 dependencies
我们都知道安装某个软件时有很多依赖关系,我们以unzip为例:
[root@localhost ~]# rpm -qR unzip
/bin/sh
libbz2.so.1()(64bit)
libc.so.6()(64bit)
libc.so.6(GLIBC_2.14)(64bit)
libc.so.6(GLIBC_2.2.5)(64bit)
libc.so.6(GLIBC_2.3)(64bit)
libc.so.6(GLIBC_2.3.4)(64bit)
libc.so.6(GLIBC_2.4)(64bit)
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(PartialHardlinkSets) <= 4.0.4-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rpmlib(PayloadIsXz) <= 5.2-1
rtld(GNU_HASH)
1.4 rpm build实例
我们通过指令将srpm包编译成rpm包
rpmbuild -ba unzip.spec // 编译产生rpm和srpm文件
rpmbuild -bb unzip.spec // 编译仅产生rpm文件
rpmbuild -bs unzip.spec // 编译仅产生srpm文件
我们来执行ba操作
[root@localhost SPECS]# rpmbuild -ba --define '_topdir /root/build/unzip/' ./unzip.spec
错误:构建依赖失败:
bzip2-devel 被 unzip-6.0-20.el8.x86_64 需要
这里我们需要首先安装bzip2-devel
yum install bzip2-devel -y # 还要安装 make, gcc等
最后就会生成了一下文件:
[root@localhost unzip]# tree RPMS/ SRPMS/
RPMS/
└── x86_64
├── unzip-6.0-20.el8.x86_64.rpm
├── unzip-debuginfo-6.0-20.el8.x86_64.rpm
└── unzip-debugsource-6.0-20.el8.x86_64.rpm
SRPMS/
└── unzip-6.0-20.el8.src.rpm
缺点2:构建依赖需要先安装依赖的包
1.5 yum
这里我们指出来,构建rpm包需要安装依赖,安装软件包时也需要安装依赖,yum可以解决安装软件包时的依赖问题,那么构建rpm包需要安装依赖的依赖问题我们总不能人工去解决吧,这里我们就推出来mock
2. MOCK
主要用于实现chroot 环境下,编译RPM 的工作,编译过程中会自动在chroot环境去安装依赖的软件包
2.1 如何使用
需要使用srpm为前提:
mock -r ./centos-8-x86_64.cfg --rebuild ~/build/unzip/SRPMS/unzip-6.0-20.el8.src.rpm
或者
mock -r ./centos-8-x86_64.cfg --init
mock -r ./centos-8-x86_64.cfg --rebuild ~/build/unzip/SRPMS/unzip-6.0-20.el8.src.rpm
mock -r ./centos-8-x86_64.cfg --clean
看下cfg文件:
# meta包
onfig_opts['chroot_setup_cmd'] = 'install tar gcc-c++ redhat-rpm-config redhat-release which xz sed make bzip2 gzip gcc coreutils unzip shadow-utils diffutils cpio bash gawk rpm-build info patch util-linux findutils grep'
...
config_opts['dist'] = 'el8' # only useful for --resultdir variable subst
config_opts['releasever'] = '8'
config_opts['package_manager'] = 'dnf'
...
# repo
[baseos]
name=CentOS-$releasever - Base
mirrorlist=http://mirrorlist.centos.org/?release=8&arch=$basearch&repo=BaseOS&infra=$infra
failovermethod=priority
gpgkey=file:///usr/share/distribution-gpg-keys/centos/RPM-GPG-KEY-CentOS-Official
gpgcheck=1
skip_if_unavailable=False
[appstream] ...
[powertools] ...
[devel] ...
2.2 mock构建步骤
- 初始化chroot环境,创建文件夹(/var/lib/mock/centos-8-x86_64/root)
- 该环境中填充一些文件夹(/etc/, /bin/, /dev/null/)
- 拷贝srpm到chroot
- 安装meta包
- 安装要依赖的包
- 创建用户
- build (rpmbuild --rebuild)
2.3 mock caching
- mock提供了很多插件用来做cahce(ccache,root_cache , yum_cache)(/var/cache/mock)
- local repo // cfg文件修改源指向本地
3. KOJI
3.1 koji结构和功能
koji是rpm包的构建和管理系统,且可以制定多台builder构建,koji系统搭建参考:koji
koji的结构图:
-
koji-hub: 这个其实是整个koji架构的中心,负责着整个系统的维护和各个组件之间的数据交换。是一个xml-rpc的server,通过mod_wsgi模块挂载在apache上提供服务。接受client和web到来的请求,然后驱使其他组件完成构建工作,同时将数据存放到数据库中。
访问 /kojihub会定位到/koji-hub/kojixmlrpc.py文件来解析,只接收POST 请求
-
kojid:部署在要完成构建工作的相应机器上,可以部署到多个机器。主要负责从Koji-hub请求到任务进行处理,一般来讲就是构建任务
-
koji-web:主要提供给使用浏览器的一些接口,然后从上游(hub)获取数据传给浏览器,浏览器访问http://mirror.easystack.cn/koji/xxx,根据apache的kojiweb.conf配置,就会访问到web组件的代码文件wsgi_publisher.py
-
koji(client):命令行的客户端,输入指令与hub交互,执行一些指令(添加用户,提交build请求等等)
-
kojira: 主要负责和仓库建立,仓库更新等相关事宜。这里仓库不是指数据库,是指rpm repo。应该是只有一个实例在运行。
-
Postgresql DB: 数据库,只有hub和他建立连接,并存取一些关系的数据,比如说账号信息,构建信息,repo的信息等等
3.2 koji的一些概念
- package:包,单纯是指nvr中的n,比如说unzip-6.0-20.el7.centos.es.x86_64.rpm中package是指unzip
- build:我们所说的koji build是指构建这个动作,同时我们也会说产生nvr包也称为build,产生nvr的后果就是这个build包名字在koji系统中只能存在一个,不能重名
- tag:build-tag和destination-tag,tag可以继承。build-tag
- target:(build target),关联一个build-tag及destination-tag,当我们build的时候只需要制定target就可以以build-tag来打包,用destination-tag来管理包
3.3 koji build
3.3.1 build的流程
前置条件
-
创建tag
$ koji add-tag dist-foo $ koji add-tag --parent dist-foo --arches \ "x86_64" dist-foo-build
-
添加构建仓库
$ koji add-external-repo -t \ dist-foo-build base-external-repo \ https://mirrors.aliyun.com/centos/8/BaseOS/\$arch/os/ $ koji add-external-repo -t \ dist-foo-build appstream-external-repo \ https://mirrors.aliyun.com/centos/8/AppStream/\$arch/os/ $ koji add-external-repo -t \ dist-foo-build epel-external-repo \ https://mirrors.aliyun.com/epel/8/Everything/\$arch/
-
加入构建组
$ koji add-group dist-foo-build build $ koji add-group dist-foo-build srpm-build
-
添加meta包,我这里是从mock配置文件拷贝,最终这些包会从刚刚添加的仓库安装到chroot环境中
$ koji add-group-pkg dist-foo-build build \ tar gcc-c++ redhat-rpm-config redhat-release \ which xz sed make bzip2 gzip gcc coreutils unzip \ shadow-utils diffutils cpio bash gawk rpm-build \ info patch util-linux findutils grep $ koji add-group-pkg dist-foo-build srpm-build \ tar gcc-c++ redhat-rpm-config redhat-release \ which xz sed make bzip2 gzip gcc coreutils unzip \ shadow-utils diffutils cpio bash gawk rpm-build \ info patch util-linux findutils grep
-
添加build target
# koji add-target <name> <build tag> <dest tag> koji add-target dist-foo dist-foo-build dist-foo
-
添加相应的pkg到指定tag,表示可以在该tag下编译这个pkg
$ koji add-pkg --owner kojiadmin dist-foo unzip
build
-
build包,前边属于搭建仓库及构建等条件,每次编译包时只需要执行build指令即可
# 这里的dist-foo是build target, 使用build-tag的编译条件(包含仓库等),输出的包被打上destination-tag来管理 koji build dist-foo /opt/unzip-6.0-20.el7.src.rpm
3.3.2 build选项
build可以添加选项:
–skip-tag: 编译过程不加destination-tag,产生NVR,可做临时检查使用,之后决定发布时再tag build(一旦生成NVR,不可以重新编译)
–scratch: 只是编译包,不产生NVR,不会打tag
3.3.3 跟踪一下
以 “koji build dist-foo /opt/unzip-6.0-20.el7.src.rpm” 为开始,详细列出来koji系统的代码及相关结构讲述