• CentOS 7上的程序管理:rpm、yum和源码编译安装


    简介

    在Linux的早期时代(也许吧?我猜的。也可能是Unix。),想要在系统上安装一款应用程序,是比较复杂的。需要专业的人员自行获取程序的源代码,并且编译安装,这是非常的复杂且需要一定的专业功底的,这种方式叫做源码编译安装(后面会描述)。

    再后来就有人/组织将这个过程简化了,他们将事先已经编译好的软件打包后,放到互联网上供用户下载。用户下载适用于自己的操作系统和硬件平台的软件包之后,只需要将其“解压”,那么该软件包所包含的各种文件(二进制程序文件、文档文件、库文件和配置文件等)就会基于包作者事先定义好的位置进入各自对应的目录下了。在CentOS上,这种打包工具就叫做rpm,即rpm安装

    但是这种工具又存在一个缺陷,那就是它无法自动解决RPM包之间的依赖性。

    假设安装A包需要依赖于B包,安装B包又要依赖于C包。

    A --> B --> C

    当你安装A包的时候提示你要安装B包,你安装B包的时候又提示你要安装C包,它无法自动去解决这个依赖关系。并且你还要自己去下载被依赖的B包和C包。

    因此诞生了一种更高级的包管理器,用于解决上述的RPM的问题,在CentOS上,这种包管理器就叫做yum,即yum安装。不过,在未来,可能是CentOS 8的时候,yum会被dnf工具所取代,在Fedora系统上,已经有dnf工具了。

    在不同的Linux发行版中,这种软件包的组织形式有点不同,见下表:

    发行版 初级包管理器 高级包管理器 包名称
    RHEL系列:Red Hat、CentOS、Fedora rpm yum、dnf *.rpm
    Debian、Ubuntu dpkg apt-get *.deb
    S.u.S.E rpm zypper *.rpm

    本文的讲解顺序是先讲rpm安装,再讲yum安装,最后说最复杂的源码编译安装。

    RPM

    在当前的运维环境下,一般我们安装某款软件,会直接使用yum命令,而不会单独去下载rpm包通过rpm命令来完成。

    更多的是使用rpm命令去执行一下对程序包的查询操作。

    程序包的获取

    推荐从软件的官方站点获取,或者可以在一些比较大型的受信任的镜像站点获取。

    1. 系统发行版的光盘。
    2. 项目的官方站点。
      • www.zabbix.com
      • www.mysq.com
      • 等等。
    3. 开源镜像站点及一些rpm包提供站点。
    4. 自己动手制作rpm包。

    程序包的名称

    程序包(rpm包)名称的大概格式如下。

    NAME-VERSION-RELEASE.OS.ARCH.rpm

    示例

    perl-5.16.3-293.el7.x86_64.rpm

    NAME:软件的名称,例如“perl”。

    VERSION:软件的版本,一般格式为“major.minor.release”,即主版本.次版本.发行版本,例如“5.16.3”。

    RELEASE:程序包的发行版,发行版可以理解为发行的次数,依次往上叠加,区别于VERSION中的release,这里的RELEASE指的是程序包的发行版本,而不是软件的。可能软件的版本没改变但是程序包再次制作发行了。例如“293”。

    OS:Linux系统的大版本,CentOS 6为el6,CentOS 7为el7,el表示Enterprise Linux。

    ARCH:硬件平台。例如i386,x86_64,amd64,ppc,noarch。noarch表示该程序包不受硬件平台的限制,可能该包均通过库调用之类来实现自身功能。

    主包与支包

    程序除了以自身名称命名的包以外,可能还存在一些其他的包用于提供与该程序相关的功能或者用于扩展该程序。

    以PHP来举例。

    PHP的主包

    php-5.4.16-46.el7.x86_64.rpm

    PHP的支包

    php-bcmath-5.4.16-46.el7.x86_64.rpm  # 提供了数学计算相关的PHP扩展库。
    php-cli-5.4.16-46.el7.x86_64.rpm  # 提供了PHP命令行程序/usr/bin/php。

    安装

    基本语法如下。

    rpm {-i|--install} [install-options] PACKAGE_FILE ...

    -v:输出详细信息。

    -vv:输出更详细的信息。

    -h,--hash:hash marks输出进度条,每个#表示2%的进度。

    --test:测试安装,检查并报告依赖关系及冲突消息等,类似于干跑(--dry-run)。

    --nodeps:忽略依赖关系,忽略依赖关系之后安装的程序包,可能可以正常运行(比如依赖的只是一个文档类的程序包),不建议这么做。

    --replacepkgs:重新安装,一般用于修改了程序包的某个文件无法复原的时候选择的选项,如果重新安装依然无法恢复修改的文件,可以事先删除该文件再重新安装。

    --noscripts:禁用rpm包的相关脚本(scriptlet)。该选项等同于“--nopre --nopost --nopreun --nopostun”。

    rpm包可以包含四种脚本:

    • preinstall:安装过程开始之前运行的脚本,%pre,--nopre
    • postinstall:安装过程完成之后运行的脚本,%post,--nopost
    • preuninstall:卸载过程开始之前运行的脚本,%preun,--nopreun
    • postuninstall:卸载过程完成之后运行的脚本,%postun,--nopostun

    可以思考一下,比如我们安装nginx或者php-fpm或者MySQL的时候,在系统中会产生与之对应的用于运行服务的用户,那么这个创建用户的操作,应该就是rpm包的脚本所实现的了。

    --nosignature:不检查包签名信息,即不检查包来源的合法性。

    --nodigest:不检查包摘要信息,即不检查包完整性信息。

    包来源合法性和包完整性是什么,在后面会描述,先知道该选项即可。

    最常用的选项如下。

    rpm -ivh PACKAGE_FILE ...
    [root@C7 ~]# rpm -ivh zsh-5.0.2-31.el7.x86_64.rpm 
    Preparing...                          ################################# [100%]
    Updating / installing...
       1:zsh-5.0.2-31.el7                 ################################# [100%]

    --test选项,也可能会用到。其他选项基本不会使用到。

    [root@C7 ~]# rpm -ivh --test zsh-5.0.2-31.el7.x86_64.rpm 
    Preparing...                          ################################# [100%]
        package zsh-5.0.2-31.el7.x86_64 is already installed

    因为此前已经安装过,所以我们通过测试运行,就可以看到会有已安装的提示。

    升级

    rpm {-U|--upgrade} [install-options] PACKAGE_FILE ...
    rpm {-F|--freshen} [install-options] PACKAGE_FILE ...

    -U:表示升级或者安装,即如果软件此前未安装,则可以直接安装。

    -F:明确表示只能升级,即如果软件此前未安装,则不会直接安装。

    一般也会结合-vh选项来使用。

    rpm -Uvh PACKAGE_FILE ...
    rpm -Fvh PACKAGE_FILE ...

    --oldpackage:默认情况下,升级操作只可以升级到新版本,使用该选项的话,可执行降级操作。

    --replacefiles:即便安装操作会覆盖其他程序包所安装的文件,仍然安装程序包。

    --replacepkgs:即便程序包的某些文件已经安装在系统上,仍然安装程序包。

    --force:等同于--replacepkgs、--replacefiles和--oldpackage。

    网上下载了zsh 5.1.1版本的,进行升级实验。

    [root@C7 ~]# rpm -Uvh --test zsh-5.1-1.gf.el7.x86_64.rpm 
    warning: zsh-5.1-1.gf.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID da8b7718: NOKEY
    Preparing...                          ################################# [100%]
    [root@C7 ~]# rpm -Uvh zsh-5.1-1.gf.el7.x86_64.rpm
    warning: zsh-5.1-1.gf.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID da8b7718: NOKEY
    Preparing...                          ################################# [100%]
    Updating / installing...
       1:zsh-5.1-1.gf.el7                 ################################# [ 50%]
    Cleaning up / removing...
       2:zsh-5.0.2-31.el7                 ################################# [100%]
    [root@C7 ~]# rpm -qa | grep -i "zsh"
    zsh-5.1-1.gf.el7.x86_64

    升级新的包之后,会卸载旧的包。rpm -qa命令用于查看当前系统上安装的程序包,下文会描述。

    仔细看的话,可以发现有一行警告。

    warning: zsh-5.1-1.gf.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID da8b7718: NOKEY

    这是因为rpm包的来源合法性和包完整性无法得到验证,我们没有提供包提供方的key导致的,后面会描述。

    降级操作。

    [root@C7 ~]# rpm -q zsh
    zsh-5.1-1.gf.el7.x86_64
    [root@C7 ~]# rpm -Uvh --oldpackage zsh-5.0.2-31.el7.x86_64.rpm 
    Preparing...                          ################################# [100%]
    Updating / installing...
       1:zsh-5.0.2-31.el7                 ################################# [ 50%]
    Cleaning up / removing...
       2:zsh-5.1-1.gf.el7                 ################################# [100%]
    [root@C7 ~]# rpm -q zsh
    zsh-5.0.2-31.el7.x86_64

    注意事项:

    • 不要对内核做升级操作;若想要使用新版本的内核,Linux支持多内核版本并存,因此直接安装新版本内核即可。
    • 如果某原程序包的配置文件在安装后曾被修改过,升级时,新版程序包提供的同一个配置文件不会覆盖老版本已修改过的配置文件,而是把新版本的配置文件重命名(FILENAME.rpmnew)后提供。

    卸载

    rpm {-e|--erase} [--allmatches] [--nodeps] [--noscripts] [--test] PACKAGE_NAME ...

    注意,在安装和升级的过程中,rpm命令的操作对象都是rpm包文件的名称(PACKAGE_FILE),例如“zsh-5.0.2-31.el7.x86_64.rpm”。

    而卸载的时候,只需要rpm包名(PACKAGE_NAME)即可,例如“zsh”。

    --allmatches:卸载所有匹配PACKAGE_NAME的所有版本的程序包。

    我这里无法测试该选项,例如zsh,无论我升级还是降级,rpm都只会保留一个版本的zsh,因此我不知道如何测试该选项。

    不过一般卸载所使用到的选项并不多,和安装一样,使用一些通用的选项即可。

    [root@C7 ~]# rpm -evh zsh
    Preparing...                          ################################# [100%]
    Cleaning up / removing...
       1:zsh-5.0.2-31.el7                 ################################# [100%]
    [root@C7 ~]# rpm -q zsh
    package zsh is not installed

    查询

    查询才是rpm使用中最经常用到的操作。因为安装、升级和卸载都可以使用yum命令来完成,而查询我们则基本上是使用rpm命令。

    rpm {-q|--query} [select-options] [query-options]

    [select-options]

    PACKAGE_NAME:查询指定的程序包是否已安装,若已安装则会显示其版本。

    [root@C7 ~]# rpm -q bash
    bash-4.2.46-30.el7.x86_64

    -a, --all:查询所有已安装的程序包,可以结合管道+grep命令过滤。

    [root@C7 ~]# rpm -qa | grep -i "bash"
    bash-4.2.46-30.el7.x86_64
    bash-completion-2.1-6.el7.noarch

    -f, --file FILE:查询出指定的FILE是哪个程序包的。

    [root@C7 ~]# rpm -qf /bin/bash
    bash-4.2.46-30.el7.x86_64

    -g, --group GROUP:查询某个程序包组包含哪些程序包。注意,这里的GROUP,并不是通过yum grouplist中所看到的程序包组名称,而是通过-i选项所查询到的包详细信息中的GROUP!

    例如我们常安装的包组为Development Tools,就不是这里所指的GROUP。即便已安装了该包组。

    [root@C7 ~]# rpm -qg "Development Tools"
    group Development Tools does not contain any packages
    [root@C7 ~]# rpm -qi bash | grep -i "^group"
    Group       : System Environment/Shells
    [root@C7 ~]# rpm -qg "System Environment/Shells"
    bash-4.2.46-30.el7.x86_64
    tcsh-6.18.01-15.el7.x86_64

    -p, --package PACKAGE_FILE:对未安装的程序包执行查询操作,查询的内容与query-options相关。

      在query-options中,许多的选项可以用于查看已安装的程序包的信息;

      但是如果我们拥有一个RPM包文件,但是不打算安装它,想查看此RPM包所包含的文件;

      就可以使用命令“rpm -qpl PACKAGE_FILE”,这就是p选项的用法;

    例如,假设我们本机没有安装zsh,那么我们就无法查询其相关信息了。

    [root@C7 ~]# rpm -qi zsh
    package zsh is not installed

    但如果我们有程序包文件的话,就可以借助-p选项来实现查询的功能,只不过这里的命令参数就是PACKAGE_FILE而不是PACKAGE_NAME了。

    [root@C7 ~]# rpm -qpi zsh-5.0.2-31.el7.x86_64.rpm

    --whatprovides CAPABILITY:查询所有包中具备某种能力(capability)的程序包。这里的能力指的是什么,就需要看底下的query-options中的--provides和-R选项了。

    首先我们基于下文的选项,可以知道bash程序包提供了以下能力。

    [root@C7 ~]# rpm -q --provides bash
    /bin/bash
    /bin/sh
    bash = 4.2.46-30.el7
    bash(x86-64) = 4.2.46-30.el7
    config(bash) = 4.2.46-30.el7

    以“/bin/bash”这个能力来举例,我们可以查看哪些程序包提供了该能力。

    [root@C7 ~]# rpm -q --whatprovides "/bin/bash"
    bash-4.2.46-30.el7.x86_64

    而如果能力是那种带有等于号与版本信息的,应该忽略掉等于号和版本信息,只保留能力名称,并且需要使用单引号或者双引号。

    [root@C7 ~]# rpm -q --whatprovides "bash(x86-64) = 4.2.46-30.el7"
    no package provides bash(x86-64) = 4.2.46-30.el7
    [root@C7 ~]# rpm -q --whatprovides "bash(x86-64)"
    bash-4.2.46-30.el7.x86_64
    [root@C7 ~]# rpm -q --whatprovides bash(x86-64)
    -bash: syntax error near unexpected token `('
    [root@C7 ~]# rpm -q --whatprovides 'bash(x86-64)'
    bash-4.2.46-30.el7.x86_64

    --whatrequires CAPABILITY:查询所有包中需要某种能力(capability)的程序包。

    [root@C7 ~]# rpm -q --whatrequires "/bin/bash"
    nss-softokn-freebl-3.34.0-2.el7.x86_64
    copy-jdk-configs-3.3-2.el7.noarch
    iproute-4.11.0-14.el7.x86_64
    xorg-x11-xinit-1.3.4-2.el7.x86_64
    ...

    我猜测,rpm包应该就是基于包的能力(capability)来决定包与包之间的依赖关系的。

    能力这块,目前知道即可,我们并不需要手工去查询来解决依赖关系,因为我们有yum工具。

    [query-options]

    --changelog:显示程序包的变更日志(changelog),这里的变更日志指的是RPM程序包的,而非软件源码包的。程序包的changelog和软件的changelog的区别可能在于,比如软件源代码可以不改变,但是程序包的打包方式改变了,例如部分文件的存放位置发生了修改。

    [root@C7 ~]# rpm -q --changelog bash | head
    * Mon Sep 25 2017 Siteshwar Vashisht <svashisht@redhat.com> - 4.2.46-30
    - Check for multibyte characters in commands
      Resolves: #1487615
    
    * Thu Aug 03 2017 Siteshwar Vashisht <svashisht@redhat.com> - 4.2.46-29
    - Fix a pipe fd leak in process substitution
      Resolves: #1473245
    
    * Tue Mar 07 2017 Kamil Dudka <kdudka@redhat.com - 4.2.46-28
    - CVE-2016-9401 - Fix crash when '-' is passed as second sign to popd

    -l, --list:查询程序包所包含的所有文件(二进制程序文件、文档文件、库文件和配置文件等)。

    [root@C7 ~]# rpm -ql bash
    /etc/skel/.bash_logout
    ...
    /usr/bin/alias
    /usr/bin/bash
    ...
    /usr/share/doc/bash-4.2.46
    ...
    /usr/share/man/man1/..1.gz
    ...

    -c, --configfiles:仅查询程序包所包含的配置文件。

    [root@C7 ~]# rpm -qc bash
    /etc/skel/.bash_logout
    /etc/skel/.bash_profile
    /etc/skel/.bashrc

    -d, --docfiles:仅查询程序包所包含的文档文件。

    [root@C7 ~]# rpm -qd bash
    /usr/share/doc/bash-4.2.46/COPYING
    ...
    /usr/share/man/man1/..1.gz
    ...

    -i, --info:查询程序包的详细信息(information)。这个是比较重要的信息!

    [root@C7 ~]# rpm -qi bash
    Name        : bash    # 程序包名称。
    Version     : 4.2.46    # 程序包版本。
    Release     : 30.el7    # 程序包发行版,包含了OS。
    Architecture: x86_64    # 程序包适用硬件平台。
    Install Date: Thu 27 Sep 2018 03:50:45 PM CST    # 安装日期。
    Group       : System Environment/Shells    # 这里的group,就是上面“rpm -qg GROUP”中的group。
    Size        : 3667709
    License     : GPLv3+
    Signature   : RSA/SHA256, Wed 25 Apr 2018 06:54:19 PM CST, Key ID 24c6a8a7f4a80eb5
    Source RPM  : bash-4.2.46-30.el7.src.rpm    # RPM源码包,和纯源码包又完全不同,后面会描述。
    Build Date  : Wed 11 Apr 2018 08:55:22 AM CST
    Build Host  : x86-01.bsys.centos.org
    Relocations : (not relocatable)
    Packager    : CentOS BuildSystem <http://bugs.centos.org>
    Vendor      : CentOS    # 程序包制作商。
    URL         : http://www.gnu.org/software/bash    # 软件相关的官方站点。
    Summary     : The GNU Bourne Again shell    # 软件的简要信息。
    Description :    # 软件的详细信息。
    The GNU Bourne Again shell (Bash) is a shell or command language
    interpreter that is compatible with the Bourne shell (sh). Bash
    incorporates useful features from the Korn shell (ksh) and the C shell
    (csh). Most sh scripts can be run by bash without modification.

    --provides:查询程序包所提供的能力(capability)。

    [root@C7 ~]# rpm -q --provides bash
    /bin/bash
    /bin/sh
    bash = 4.2.46-30.el7
    bash(x86-64) = 4.2.46-30.el7
    config(bash) = 4.2.46-30.el7

    -R, --requires:查询程序包所依赖的能力(capability)。

    [root@C7 ~]# rpm -qR bash
    /bin/sh
    config(bash) = 4.2.46-30.el7
    libc.so.6()(64bit)
    libc.so.6(GLIBC_2.11)(64bit)
    libc.so.6(GLIBC_2.14)(64bit)
    libc.so.6(GLIBC_2.15)(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)
    libc.so.6(GLIBC_2.8)(64bit)
    libdl.so.2()(64bit)
    libdl.so.2(GLIBC_2.2.5)(64bit)
    libtinfo.so.5()(64bit)
    rpmlib(BuiltinLuaScripts) <= 4.2.2-1
    rpmlib(CompressedFileNames) <= 3.0.4-1
    rpmlib(FileDigests) <= 4.6.0-1
    rpmlib(PayloadFilesHavePrefix) <= 4.0-1
    rtld(GNU_HASH)
    rpmlib(PayloadIsXz) <= 5.2-1

    --scripts:查询程序包在安装和卸载过程中使用的特殊脚本程序。我自己查了bash和postfix程序包,发现可使用shell script和lua语言编写。

    [root@C7 ~]# rpm -q --scripts bash
    postinstall scriptlet (using <lua>):
    ...
    postuninstall scriptlet (using <lua>):
    -- Run it only if we are uninstalling
    ...

    包来源合法性与包完整性

    当我们从网上下载了一个rpm包之后,如何确保这个rpm包就是该站点所提供的?如何确保rpm包没有被第三方所篡改呢?

    这就需要依赖于rpm包的来源合法性和包完整性验证了。

    来源合法性验证依赖于数字签名,包完整性验证依赖于消息摘要算法。

    rpm包实现包来源合法性和包完整性检测的原理如下:

    1. 包制作者使用单向加密算法(比如md5)对包进行加密得到校验码(或者说是消息摘要);
    2. 包制作者使用自己的私钥去加密校验码,并将加密后的校验码附加在包的尾部;
    3. 包使用者使用包制作者提供的公钥对包尾部的加密后的校验码进行解密,如果解密成功,则证明包的来源是合法的(确实是包制作者本人发出的,这里涉及到非对称加密的概念);
    4. 包使用者使用和包制作者相同的单向加密算法(比如md5)对包进行加密得到校验码,如果此校验码和刚才包使用者使用公钥解密后得到的校验码一致,那么就证明了包的完整性(包在传输的过程中未被篡改)。

    这里涉及到加密与解密的这些理论知识,将来应该会写一篇文章来详细阐述。

    如果我们想检测一个rpm,那么我们就需要一个公钥文件,这个文件,一般是包制作方会提供,在系统镜像文件中也会有。

    系统安装好之后,在“/etc/pki/rpm-gpg/”目录下会有公钥(密钥有公钥和私钥两种)文件。

    [root@C7 ~]# ls -l /etc/pki/rpm-gpg/
    total 16
    -rw-r--r--. 1 root root 1690 Apr 29  2018 RPM-GPG-KEY-CentOS-7
    -rw-r--r--. 1 root root 1004 Apr 29  2018 RPM-GPG-KEY-CentOS-Debug-7
    -rw-r--r--. 1 root root 1690 Apr 29  2018 RPM-GPG-KEY-CentOS-Testing-7
    -rw-r--r--  1 root root 1662 Oct  3  2017 RPM-GPG-KEY-EPEL-7

    当我们有了密钥后,就可以将其导入rpm,用作后续的校验操作。

    CentOS 6和CentOS 7操作有些许不同,因为6和7默认的rpm版本不一样。6的写法,在7上也可以使用。

    [root@C6 ~]# rpm -q rpm
    rpm-4.8.0-55.el6.x86_64
    [root@C7 ~]# rpm -q rpm
    rpm-4.11.3-35.el7.x86_64
    CentOS 6:rpm --import PUBKEY ...
    CentOS 7:rpmkeys --import PUBKEY ...

    有了key之后,就可以对程序包进行检测了。

    CentOS 6:rpm {-K|--checksig} [--nosignature] [--nodigest] PACKAGE_FILE ...
    CentOS 7:rpmkeys {-K|--checksig} PACKAGE_FILE ...

    zsh-5.0.2版本的,此前从阿里云的CentOS 7镜像下载,因为系统自带了key,因此可以检测通过。而zsh-5.1.1则没有key,因此无法检测。

    [root@C7 ~]# rpmkeys -K zsh-5.0.2-31.el7.x86_64.rpm 
    zsh-5.0.2-31.el7.x86_64.rpm: rsa sha1 (md5) pgp md5 OK
    [root@C7 ~]# rpmkeys -K zsh-5.1-1.gf.el7.x86_64.rpm 
    zsh-5.1-1.gf.el7.x86_64.rpm: RSA sha1 ((MD5) PGP) md5 NOT OK (MISSING KEYS: (MD5) PGP#da8b7718)

    除了主动检查包以外,每次在安装/升级等操作的时候,rpm也会自动去检查的,例如上文我们看到的那个警告信息。

    warning: zsh-5.1-1.gf.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID da8b7718: NOKEY

    相关的帮助。

    CentOS 6man rpm
    CentOS 7man rpmkeys

    校验

    rpm {-V|--verify} [select-options] [verify-options]

    校验操作会将已安装的文件的信息和rpm数据库的元数据中记录的信息做比较,这些信息包括了文件大小、摘要(digest)、权限、类型、属主和属组等。如果这些信息有改变,那么就会将其显示出来。

    如果在安装的时候没有安装该文件的话,则会忽略未安装的文件。例如,在安装的时候使用了--excludedocs选项,那么在校验的时候就会忽略文档类型的文件。

    [root@C7 ~]# rpm -V bash
    [root@C7 ~]#

    如果程序包中的文件均为改动过,那么就不会有任何的输出。

    接下来我们修改某个配置文件,加一行注释;修改了版权文件的时间;修改cd程序文件的属主和属组。

    [root@C7 ~]# vim /etc/skel/.bash_logout
    [root@C7 ~]# touch /usr/share/doc/bash-4.2.46/COPYING
    [root@C7 ~]# chown zwl:zwl /usr/bin/cd

    再次校验。

    [root@C7 ~]# rpm -V bash
    S.5....T.  c /etc/skel/.bash_logout
    .....UG..    /usr/bin/cd
    .......T.  d /usr/share/doc/bash-4.2.46/COPYING

    在输出的内容中,由三个部分构成。

    第一个部分由9个字段构成。小数点“.”表示检查没问题。如果出现问号“?”的话,表示无法检查,比如可能因为权限问题导致无法读取文件信息。

    • S:Size,文件大小改变。
    • M:Mode,权限或者文件类型改变。
    • 5:消息摘要改变,一般是md5算法;消息摘要不同,意味着文件内容发生了改变。
    • D:Device,设备的设备号不匹配。
    • L:readLink(2)路径不匹配。
    • U:User,属主改变。
    • G:Group,属组改变。
    • T:mTime,文件修改时间改变。
    • P:caPabilities,包能力改变。

    第二个部分是属性标记,可有可无的。

    • c:%config,配置文件。
    • d:%doc,文档文件。
    • g:%ghost file (i.e. the file contents are not included in the package payload).
    • l:%license,许可证文件。
    • r:%readme,类似文档文件。

    RPM数据库

    rpm的数据库位于目录/var/lib/rpm/中,rpm基于这些数据库的内容才可以完成它上述的功能。数据库文件的格式是伯克利DB。

    [root@C7 ~]# file /var/lib/rpm/*
    /var/lib/rpm/Basenames:    Berkeley DB (Btree, version 9, native byte-order)
    /var/lib/rpm/Conflictname: Berkeley DB (Btree, version 9, native byte-order)
    /var/lib/rpm/Dirnames:     Berkeley DB (Btree, version 9, native byte-order)
    /var/lib/rpm/Group:        Berkeley DB (Btree, version 9, native byte-order)
    /var/lib/rpm/Installtid:   Berkeley DB (Btree, version 9, native byte-order)
    /var/lib/rpm/Name:         Berkeley DB (Btree, version 9, native byte-order)
    /var/lib/rpm/Obsoletename: Berkeley DB (Btree, version 9, native byte-order)
    /var/lib/rpm/Packages:     Berkeley DB (Hash, version 9, native byte-order)
    /var/lib/rpm/Providename:  Berkeley DB (Btree, version 9, native byte-order)
    /var/lib/rpm/Requirename:  Berkeley DB (Btree, version 9, native byte-order)
    /var/lib/rpm/Sha1header:   Berkeley DB (Btree, version 9, native byte-order)
    /var/lib/rpm/Sigmd5:       Berkeley DB (Btree, version 9, native byte-order)
    /var/lib/rpm/Triggername:  Berkeley DB (Btree, version 9, native byte-order)
    rpm {--initdb|--rebuilddb} [-v] [--dbpath DIRECTORY] [--root DIRECTORY]

    --initdb:初始化数据库,即创建一个新的,如果原本已经有的话,就不创建。

    --rebuilddb:重建数据库,一般如果数据库损坏了,可使用。它会基于已安装的包头(package header)来重建数据库索引。

    --dbpath:指定数据库的位置,默认是/var/lib/rpm/。

    --root:指定rpm数据库所基于的根文件系统路径。默认应该是系统根“/”。注:该选项解释不确定。

    [root@C7 ~]# rpm --initdb -v --dbpath=/tmp/rpm_test
    [root@C7 ~]# ls /tmp/rpm_test/
    Basenames  Conflictname  __db.001  __db.002  __db.003  Dirnames  Group  Installtid  Name  Obsoletename  Packages  Providename  Requirename  Sha1header  Sigmd5  Triggername

    有尝试指定了--root选项的值为/usr或者/usr/local,均无法初始化出数据库文件。

    YUM

    正如文章开头所描述的,yum是一款基于rpm包的包管理器。相对于rpm,它自动帮我们解决了包下载以及包依赖性的问题。

    yum是一款C/S架构的工具。客户端即为本地的Linux端,需要安装软件的那一端;服务器端即为远程仓库(repository)端,也叫做yum源。

    仓库端除了存放众多的RPM包以外,还存放了相关的元数据(应该是记录包之间的依赖关系等)repodata目录。

    存放repodata目录的地址,即为yum源地址,例如该示例中的“https://mirrors.aliyun.com/zabbix/zabbix/3.0/rhel/7/x86_64/”。

    yum源的地址及仓库的地址。地址可使用的协议如下。一般最常见的还是http协议,file协议用于指定本地yum源。

    ftp://
    http://
    nfs://
    file:///

    YUM配置文件

    在使用yum之前,我们必须正确配置好yum的配置文件,系统安装好之后,默认就已经帮我们启用了一些配置文件。

    主配置文件:/etc/yum.conf,这里存放的是仓库相关的通用配置[main]。

    仓库配置文件:/etc/yum.repos.d/*.repo,这里才是每一个仓库所对应的配置文件,比如zabbix和MySQL可以存放在不同的配置文件中,zabbix.repo和mysql.repo。

    一般来说,主配置文件,我们是不用去改动的,更多的是管理仓库配置文件。

    主配置

    主配置文件中,一般只有一个[main]配置段,它管理的是全局配置。该配置段,也只能有一个。

    我们来看下系统默认提供的。

    [main]
    cachedir=/var/cache/yum/$basearch/$releasever
    keepcache=0
    debuglevel=2
    logfile=/var/log/yum.log
    exactarch=1
    obsoletes=1
    gpgcheck=1
    plugins=1
    installonly_limit=5
    bugtracker_url=http://bugs.centos.org/set_project.php?project_id=23&ref=http://bugs.centos.org/bug_report_page.php?category=yum
    distroverpkg=centos-release

    在一些表示“是”和“否”的配置中,会使用1(true)或者0(false)来表示。

    cachedir:yum的缓存和数据库存储目录。对于当前系统来说就是“/var/cache/yum/x86_64/7/”。

    keepcache:在安装成功后,是否保持头和包的缓存。值为1或者0,默认是1(表示保持)。

    debuglevel:debug消息输出级别。范围是0~10,默认是2。

    logfile:日志文件位置,需要写全路径(含目录名和文件名)。

    exactarch:

    obsoletes:只在更新/升级程序包时有效,应该是允许包含废弃/陈旧(obsolete)数据包,在发行版升级时(例如CentOS 6 --> CentOS 7),比较有用。默认是1。

    gpgcheck:是否执行GPG签名检测,默认是0。如果在[main]配置中设置了,那么它会应用于所有的仓库中。目前还没在man手册中看到明确的说法,不过个人认为,如果[main]和仓库的配置存在相同的配置项的话,那么在[main]中配置的会成为仓库的默认配置;如果两边均配置了,则应该是以仓库配置为准。

    plugins:是否启用插件,默认是0。插件用于扩展yum功能,我的系统默认有2个插件。

      fastestmirror:用于根据下载速度将yum源排序,然后选择下载最快的yum源来使用。

      langpacks:条件式语言支持包,应该是和语言(中英文这类)相关。

    installonlypkgs:程序包名称列表。在该列表中的程序包,只可以被安装,不可以被更新。一般都是内核相关的程序包,具体见man手册。

    installonly_limit:在installonlypkgs列表中,一次性同时安装的程序包数量,默认是3。这个配置项应该是和“installonlyn”插件比较有关系。

    bugtracker_url:应该是bug追踪反馈的一个地址。

    distroverpkg:这里配置的是一个rpm包的名字。一般是system-release(releasever)、redhat-release或者centos-release。对于我的系统来说,它是centos-release。这个包用来告诉yum我们所使用的Linux发行版是哪种,并且该包还提供了发行版的GPG密钥、repo文件等。

    仓库配置

    [repositoryid]
    name=Some name for this repository
    baseurl=url://path/to/repository/

    repositoryid:仓库的唯一标识符,一般是一个单词。

    name:仓库的描述信息。

    baseurl:仓库的地址,也叫yum源地址。这个地址下必须有“repodata”目录。URL可以有多个,但是官方不建议。URL也可以带上HTTP的基本验证信息。

    baseurl=http://user:passwd@example.com/

    enabled:是否启用该仓库。

    gpgcheck:同理[main]配置段。

    repo_gpgcheck:是否对仓库的repodata执行GPG 签名检查。

    gpgkey:指定GPG密钥,如果此前没有导入过的话,则yum会自动导入。

    enablegroups:是否允许yum安装程序包组,即yum groupinstall,默认为1。

    failovermethod:当yum源联系不上时,使用的故障转移办法。roundrobin表示随机,注意不是轮询;priority表示按顺序。

    username和password:yum源URL的基本验证信息,可写在URL中,或者[main]配置段,或者仓库配置段。

    cost:仓库的开销,开销越小越优先。默认是1000。

    变量

    配置文件中是可以包含变量的。

    $releasever:如果是CentOS 6的话,则该值为6;同理,CentOS 7的值为7。

    $arch:系统的硬件平台(architecture),常见的有i386、i486、i586、x86_64等等。

    $basearch:基础硬件平台,例如如果系统是i486、i586之类的,则会显示为i386。

    $uuid:根据当前机器生成的唯一标识符,值位于/var/lib/yum/uuid文件中。

    $YUM0-$YUM9:会被系统同名环境变量所取代,一般用于用户自定义。

    baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
    等同于
    baseurl=http://mirror.centos.org/centos/7/os/x86_64/

    YUM的命令

    基本语法。

    yum [options] [command] [package ...]

    yum的具体功能,通过command来实现。

    仓库相关

    repolist [all|enabled|disabled]

    用于列出仓库信息,默认只列出已启用的。

    [root@C7 ~]# yum repolist
    Loaded plugins: fastestmirror, langpacks    # 载入yum插件
    Loading mirror speeds from cached hostfile    # 插件输出信息,这部分输出后面会忽略
     * base: mirrors.aliyun.com
     * epel: mirrors.aliyun.com
     * extras: mirrors.huaweicloud.com
     * updates: mirrors.aliyun.com
    repo id                                                                     repo name                                                                                                status    # 状态,即程序包个数
    !base/7/x86_64    # repo id前的感叹号表示该仓库的元数据过期了。                   CentOS-7 - Base                                                                                          10,019
    !epel/x86_64                                                                Extra Packages for Enterprise Linux 7 - x86_64                                                           13,139
    !extras/7/x86_64                                                            CentOS-7 - Extras                                                                                           413
    !updates/7/x86_64                                                           CentOS-7 - Updates                                                                                        1,840

    可以跟上repo id或者repo name来显示对应的仓库。

    # yum repolist REPO_ID
    # yum repolist REPO_NAME

    可以通过-v选项或者repoinfo显示仓库详细信息。

    # yum repolist -v ...
    # yum repoinfo ...

    程序包列表

    list [...]

    默认情况下,列出所有的程序包,程序包名称支持globbing风格。

    yum list [all | glob_exp1] [glob_exp2] [...]

    可以列出程序包名称、具体的版本信息以及来自哪个仓库。@REPO_ID(中的@)表示这是一个已安装的包。

    dwz.x86_64                             0.11-3.el7                      @base    
    dyninst.x86_64                         9.3.1-1.el7                     @anaconda    # anaconda表示的应该是随系统安装的时候装的。

    列出未安装但是可安装的包。

    yum list available [glob_exp1] [...]

    列出所有可更新的包。

    yum list updates [glob_exp1] [...]

    更多的相关命令,查看“man yum”中的“LIST OPTIONS”。

    程序包安装

    install package1 [package2] [...]

    默认情况下,会安装仓库中最新版本的程序包。安装对象,也可以直接是一个本地的rpm文件,当需要依赖其他包时,yum会自动解决。

    程序包重装

    reinstall package1 [package2] [...]

    程序包更新/升级

    更新就是升级的意思。

    update [package1] [package2] [...]

    如果没指明package的话,那么默认情况下,yum会更新所有已安装的程序包。

    如果在更新过程中,被依赖的程序包也需要更新,那么yum会更新;如果新增了被依赖的程序包,那么yum也会新安装。

    upgrade [package1] [package2] [...]

    upgrade等同于“update --obsoletes”或者主配置文件的[main]中启用了“obsoletes”。英文原意是“在计算中包含废弃的包”,我猜测可能是保留这些废弃的包吧。

    可以在更新之前,查看有哪些程序包可以更新。

    # yum check-update

    除了升级以外,还可以降级。

    downgrade package1 [package2] [...]

    一般降级的时候,在包名中会指明版本号,如果没指明的话,则安装最新版的上一版。

    程序包卸载

    remove | erase package1 [package2] [...]

    在卸载的同时,会将依赖于该程序包的程序包一并卸载。yum程序包本身位于protected_packages配置中,所以不会被误卸载。

    程序包能力

    provides | whatprovides feature1 [feature2] [...]

    这里的feature,应该等同于上文所说的包的能力(capability)。

    根据能力,来查询哪些包支持这些。

    # yum provides "bash(x86-64)"

    缓存相关

    清理缓存。

    clean [ packages | metadata | expire-cache | rpmdb | plugins | all ]

    这里所清理的缓存,都是那些已启用的仓库,如果想清理所有(含未启用的),请使用“--enablerepo='*'”选项。

    我自己都尝试清理了一下,应该都是缓存,所以下次运行的时候,yum还会从源中去获取这些数据的,应该问题不大。

    但是不要自己通过rm去删除yum的缓存(“/var/cache/yum/”)。

    重新生成缓存。会从服务器端下载并生成元数据。

    makecache [fast]

    如果传递了fast参数,那么就等同于运行了“yum clean expire-cache”。

    程序包搜索

    search string1 [string2] [...]

    当你不知道程序包名称的时候,可以使用该选项。

    默认情况下,搜索包的name和summary字段(rpm -qi信息)。如果没找着的话,会尝试description和url字段。可通过传递all作为第一个参数来搜索全部字段。

    # yum search all database

    程序包依赖

    deplist package1 [package2] [...]

    会显示程序包所依赖的能力,以及可提供该能力的对应的程序包。了解即可,yum可自行解决依赖关系。

    程序包组相关

    groupinstall group1 [group2] [...]
    groupupdate group1 [group2] [...]
    grouplist [hidden] [groupwildcard] [...]
    groupremove group1 [group2] [...]
    groupinfo group1 [...]

    注意:上面的语法,是CentOS 6上的yum的,在CenTOS 7上会稍微有点不同。

    了解即可,基本上会使用到的也就只有安装而已。常见是安装开发工具。

    # yum group install "Development Tools"

    其他的,详见man手册。

    YUM的选项

    --nogpgcheck:禁止进行gpgcheck。

    -y, --assumeyes:自动回答yes安装。

    -q, --quiet:静默模式。

    --disablerepo=repoidglob:本次yum安装禁用某个或某些(使用glob)仓库。

    --enablerepo=repoidglob:本次yum安装启用某个或某些(使用glob)仓库。

    --noplugins:禁用所有插件。

    --disableplugin=plugin:禁用某些插件。

    连接YUM源

    光盘YUM源

    我自己是在虚拟机上实现。首先就是将光盘插入机器中,对于我来说就是指定光盘ISO镜像文件并且将其配置为“已连接”。

    在系统中挂载,留意挂载的文件系统类型。

    # mount -r -t iso9660 /dev/cdrom /media/cdrom/

    配置repo文件。

    # cat /etc/yum.repos.d/cdrom.repo 
    [cdrom]
    name=This is a yum repo from cdrom.
    baseurl=file:///media/cdrom/
    gpgcheck=1
    gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
    enabled=1

    查看仓库信息。

    [root@C7 ~]# yum repolist -v cdrom
    ...
    pkgsack time: 0.011
    Repo-id      : cdrom
    Repo-name    : This is a yum repo from cdrom.
    Repo-status  : enabled
    Repo-revision: 1525380860
    Repo-updated : Fri May  4 04:54:34 2018
    Repo-pkgs    : 3,971
    Repo-size    : 3.6 G
    Repo-baseurl : file:///media/cdrom/
    Repo-expire  : 21,600 second(s) (last: Tue May 14 11:16:02 2019)
      Filter     : read-only:present
    Repo-filename: /etc/yum.repos.d/cdrom.repo
    
    repolist: 3,971

    如果该仓库不使用了,那么先在仓库配置文件中关闭仓库启用状态,然后卸载(umount)文件系统,再然后使用eject命令弹出光盘驱动器。

    # eject /dev/sr0

    软件YUM源

    这里我以MariaDB为例,首先我是去找了阿里云的镜像站,跳转到了MariaDB的官网,在对方所提供的Repository Configuration Tool中选择了一些系统信息后,自动帮我生成了repo文件。

    [root@C7 ~]# cat /etc/yum.repos.d/MariaDB.repo 
    # MariaDB 10.3 CentOS repository list - created 2019-05-14 03:34 UTC
    # http://downloads.mariadb.org/mariadb/repositories/
    [mariadb]
    name = MariaDB
    baseurl = http://yum.mariadb.org/10.3/centos7-amd64
    gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
    gpgcheck=1

    其他的软件,也是类似的做法,要么去比较受信任的国内大型镜像站点,要么去官方站点。

    当我们想单独查看已增加的仓库中有哪些程序包时,可以使用该命令。

    # yum repository-packages mariadb list

    详情可参考repository-packages子命令。

    repository-packages <enabled-repoid> <install|remove|remove-or-reinstall|remove-or-distribution-synchronization> [package2] [...]

    源码编译安装

    参考资料:

    Linux下源代码的编译安装入门

    How to Install Software from Source Code… and Remove it Afterwards

    RPM安装,是使用别人事先制作好的rpm包。包中的二进制程序文件,是对方根据软件需求方的硬件、系统等信息事先编译好的,只能适用于那个系统、硬件的机器上。例如,64位CentOS 7系统上的rpm包,就无法应用于32位CentOS 6系统。

    YUM安装,由于是基于RPM实现的,所以也会有这个现象。

    源代码在撰写的过程中,可能将软件的所有功能都已经写好了。但是,编译的时候,一般是根据需求来编译,不需要的功能是不会编译入软件中的。

    如果通过rpm或者yum安装的软件,没有我们所需要的功能(但是软件的源代码中是有该功能的),此时就需要用户自行基于源码编译安装了。

    源码编译安装,是一个有时候让人觉得极其痛苦的过程,不同的Linux发行版,不同的软件,组合起来在安装的过程当中,会有不同的问题。加之本人对C语言源码编译的过程也不太了解,所以本文说的不一定正确。

    引用中有一篇简书的中文资料和一篇It's FOSS的英文资料可供参考,后者我没看过,若将来想深入研究,可看。

    源代码编译大致流程:
    源代码 --> 预处理 --> 编译(gcc) --> 汇编 --> 链接 --> 执行

    一般来说,Linux下的软件大多数为C语言所编写,所以使用的自动化构建工具一般是make。涉及的程序包为autoconf和automake,一般我们装上Development Tools包组就会有。

    # yum group install "Development Tools"

    接下来就是软件源代码的获取,一般是去官方网站,这里以memcached为例。获取到的是.tar.gz格式的文件,解压后,进入目录中。

    # tar -xzf memcached-1.5.14.tar.gz
    # cd memcached-1.5.14/

    进入目录后,我们首先要找到相关的帮助文件,一般是大写的INSTALL或者README,对本示例来说,是INSTALL和README.md。一般是文本文件,可直接查看。

    这类文件肯定都是英文的了,不好阅读。对于本示例来说,README.md文件还好,INSTALL文件就非常长了。一般来说,最简单的步骤为如下。

    # ./configure && make && make install

    configure是一个POSIX兼容的shell脚本,运行该脚本可用于检测软件所需要的各类环境,例如是否有各种各样的库支持等。

    可用于指定软件文件的安装位置,最常用的是--prefix选项。

    --prefix=PREFIX
    --prefix=/usr/local/memcached/

    还有更加细粒度的针对不同类型的文件的安装位置调整。

    --bindir=DIR
    --libexecdir=DIR
    --libdir=DIR
    --mandir=DIR
    --docdir=DIR
    ...

    可用于开启或者关闭一些特性。

    --disable-FEATURE
    --enable-FEATURE[=ARG]

    是否依赖某些程序包。一般是某些带有lib或者devel字符串的包。

    --with-PACKAGE[=ARG]
    --without-PACKAGE

    这些可详见:

    # ./configure --help

    接下来就让我们来实操一下,我们仅指定--prefix选项。源码编译基本最头疼的地方就在第一步,因为可能会报很多错误。

    # ./configure --prefix=/usr/local/memcached/

    错误一。

    checking for libevent directory... configure: error: libevent is required.  You can get it from http://www.monkey.org/~provos/libevent/
    
          If it's already installed, specify its path using --with-libevent=/dir/

    通过yum命令我发现本机已安装libevent程序包,但是没有安装libevent-devel,因此我们尝试安装后者。

    # yum list *libevent*
    # yum install libevent-devel

    再次configure后,成功了。

    注意:虽然报错提示我们去官网下载libevent来安装,但是能通过yum/rpm安装的就尽量通过该方式安装,尽量避免源码编译安装。而且根据本人经验,上文也说了,一般这种缺少库之类的,都是缺少一些devel类或者lib类的包。

    configure: creating ./config.status
    config.status: creating Makefile
    config.status: creating doc/Makefile
    config.status: creating config.h
    config.status: executing depfiles commands

    config.status:保留有我们此前的编译选项,直接运行该脚本应该或许可以再次生成相同的Makefile文件。

    config.h:用于包含系统依赖定义。

    Makefile:自动化构建所需要使用到的配置文件,运行完configure脚本后会根据模板文件Makefile.in生成。

    接下来我们就可以执行make了。

    # make

    make是根据Makefile文件调用gcc等编译程序完成编译链接等操作,一般是不会出错。注意,make本身不是编译工具。

    # make install

    make install一般就更不会出错了。

    成功的话,软件相关的所有文件就都在--prefix选项所指定的/usr/local/memcached目录中了。以下是简单的测试启动。

    [root@C7 memcached]# /usr/local/memcached/bin/memcached -d -u zwl
    [root@C7 memcached]# ps aux | grep -i "memcached"
    zwl        8965  0.0  0.3 413848  3124 ?        Ssl  17:02   0:00 /usr/local/memcached/bin/memcached -d -u zwl
    root       8978  0.0  0.0 112708   992 pts/0    S+   17:02   0:00 grep --color=auto -i memcached
    [root@C7 memcached]# netstat -ntulp | grep -i "memcached"
    tcp        0      0 0.0.0.0:11211           0.0.0.0:*               LISTEN      8965/memcached      
    tcp6       0      0 :::11211                :::*                    LISTEN      8965/memcached

    make还有很多其他的子命令,可深入了解。

    # make test|check
    # make installcheck
    # make clean
    # make distclean
    # make uninstall

    安装后还没完,源码安装后,由于安装位置是自定义的,所以系统在默认的PATH环境变量下、库路径下、man手册路径下,可能无法找到我们所安装的软件。因此涉及到以下操作。

    1、导出二进制程序目录至PATH环境变量中;

      编辑文件/etc/profile.d/NAME.sh

      export PATH=/PATH/TO/BIN:$PATH

    2、导出库文件路径

      编辑/etc/ld.so.conf.d/NAME.conf

      /usr/local/apache2/lib

      让系统重新生成缓存:

      ldconfig [-v]

    3、导出头文件

      基于字符链接方式实现:

      # ln -sv ...

    4、导出帮助手册

      编辑/etc/man.config文件

      添加一个MANPATH

    5、在源码目录的scripts目录下,有管理脚本,需要复制到相关的目录。

      scripts/memcached.service

    总结

    限于篇幅有限、本人懒惰以及专业不足,因此本博文可能写的不是很完善,特别是在源码编译安装这块。好在基本的软件安装都可通过yum来实现,该安装方式所安装的软件特性也基本符合大众需求。

    还有一种可实现类似于源码编译安装的叫做Source RPM安装,大家可参考一下。

    Working with Source RPMs

  • 相关阅读:
    关于React的脚手架
    yarn和npm
    谈谈NPM和Webpack的关系
    php开发环境和框架phalcon的搭建
    Centos6.5--svn搭建
    System.Diagnostics.Process.Start(ProcessStartInfo)
    PHP错误:call to undefined function imagecreatetruecolor
    PostgreSQL删除表中重复数据行
    URL存在http host头攻击漏洞-修复方案
    for循环的执行顺序
  • 原文地址:https://www.cnblogs.com/alongdidi/p/software_install_on_CentOS_7.html
Copyright © 2020-2023  润新知