第 8 章 Linux 包管理

作者: Brinnatt 分类: ARM64 Linux 基础精修 发布时间: 2022-01-21 10:06

8.1、包基础知识

当今 Linux 的发行版本使用最广泛的有两大主流阵营,一个是 Red Hat 系列,一个是 Debian 系列。

Red Hat 系列包的名称以 rpm 结尾,分为二进制包和源码包。源码包以 ".src.rpm" 结尾,它是未编译过的包,可以自行进行编译或者用其制作自己的二进制 rpm 包,非 ".src.rpm" 结尾的包都是二进制包,都是已经编译完成的。

Debian 系列包的名称以 deb 结尾,一般官方提供的 deb 包都是二进制包,直接安装使用即可。

安装 rpm、deb 包的过程实际上就是将包中的文件复制到 Linux 上,有可能还会在复制文件的前后执行一些命令,如创建一个必要的用户,删除非必要文件等。

通过 Alien 工具,可以将 deb 包和 rpm 包进行相互转换。

8.2、rpm 包

一个 rpm 包的名称分为包全名和包名,包全名如 httpd-2.4.6-97.el7.centos.aarch64.rpm,包全名中各部分的意义如下:

httpd       包名
2.4.6       版本号,版本号格式[ 主版本号.[ 次版本号.[ 修正号 ] ] ]
97          软件发布次数
el7.centos  适合的操作系统平台以及适合的操作系统版本
aarch64     适合的硬件平台,硬件平台根据 cpu 来决定,有 aarch64、x86_64、MIPS、PowerPC、noarch 或者省略,noarch 或省略表示不区分硬件平台
rpm         软件包后缀扩展名

对于一个程序而言,在制作 rpm 包时,很多时候都将其按功能分割成多个子包,如客户端程序包、服务端程序包等。

以 mariadb 这个程序来说,它分有以下几个包:

mariadb-server.aarch64 : 提供 MariaDB 服务的主包
mariadb.aarch64 : 客户端主包
mariadb-bench.aarch64 : 对 MariaDB 进行压力测试的包
mariadb-devel.aarch64 : 开发 MariaDB 所需的头文件包
mariadb-embedded.aarch64 : MariaDB 作为一个可植入的库包
mariadb-embedded-devel.aarch64 : MariaDB 作为一个可植入的库开发所需的头文件包
mariadb-libs.aarch64 : MariaDB/MySQL 客户端所需的共享库
mariadb-test.aarch64 : The test suite distributed with MariaD
  • 如果是源码编译安装的包,会包含所有功能包,也就是说编译安装一个程序后,它的客户端工具、服务提供程序、库文件、头文件等等都已经安装了。

8.2.1、rpm 安装、升级、卸载

rpm 工具安装、升级和卸载的功能都很少使用。对于安装或升级来说,它需要人为解决包的依赖关系,这是一件令人抓狂的事,基本上都会使用 yum 工具进行安装和升级,而卸载行为在 Linux 上很少出现,大不了直接覆盖重装。

rpm -ivhUe --nodeps --test --force --prefix
选项说明:
-i:             表示安装,install的意思
-v:             显示安装信息,还可以"-vv"、"-vvv",v提供的越多显示信息越多
-h:             显示安装进度,以#显示安装的进度
-U:             升级或升级包
-F:             只升级已安装的包
-e:             卸载包,卸载也有依赖性,"--erase"
--nodeps:       忽略依赖性强制安装或卸载(no dependencies)
--test:         测试是否能够成功安装指定的rpm包
--prefix:       新路径 自行指定安装路径而不是使用默认路径,基本上都不支持该功能,功能极其简单的软件估计才支持重定位安装路径
--force:        强制动作
--replacepkgs:  替换安装,即重新覆盖安装。

注意:不要对内核进行升级;多版本的内核可以并存,因此可以执行安装操作。

rpm 包被安装后,会在 /var/lib/rpm 下会建立已装 rpm 数据库,以后有任何 rpm 的升级、查询、版本比较等包的操作都是从这个目录下获取信息并完成相应操作的。

[root@arm64v8 ~]# ls /var/lib/rpm
Basenames     __db.002  Group       Obsoletename  Requirename  Triggername
Conflictname  __db.003  Installtid  Packages      Sha1header
__db.001      Dirnames  Name        Providename   Sigmd5
[root@arm64v8 ~]#

rpm 安装完成后,相关的文件会复制到多个目录下(具体复制的路径是在制作 rpm 包时指定的)。一般来说,分布形式差不多如下表。

目录 描述
/etc 放置配置文件的目录
/bin、/sbin、/usr/bin 或 /usr/sbin 一些可执行文件
/lib、/lib64、/usr/lib(/usr/lib64) 一些库文件
/usr/include 一些头文件
/usr/share/doc 一些基本的软件使用手册与帮助文件
/usr/share/man 一些 man page 档案

8.2.2、rpm 查询

rpm 工具的安装功能很少使用,毕竟解决依赖关系不是件容易的事。但是 rpm 的查询功能则非常实用。

-q[p]:  -q 查询已安装的包,-qp 查询未安装的包。它们都可接下面的参数
            -a 查询所有已安装的包,也可以指定通配符名称进行查询
            -i 查询指定包的信息(版本、开发商、安装时间等)。从这里面可以查看到软件包属于哪个包组。
            -l 查询包的文件列表和目录(包在生产的时候就指定了文件路径,因此可查未装包)
            -R 查询包的依赖性(Required)
            -c 查询安装后包生成的配置文件
            -d 查询安装后包生成的帮助文档
            -f 查询系统文件属于哪个已安装的包(接的是文件而不是包)
            --scripts 查询包相关的脚本文档。脚本文档分四类:安装前运行、安装后运行、卸载前运行、卸载后运行

举几个例子:

  1. 查询文件 /etc/yum.conf 是通过哪个包安装的。

    [root@arm64v8 ~]# rpm -qf /etc/yum.conf
    yum-3.4.3-158.el7.centos.noarch
    [root@arm64v8 ~]# 
  2. 查询安装 httpd 时生成了哪些目录和文件。

    [root@arm64v8 packages]# rpm -qpl httpd-2.4.6-97.el7.centos.aarch64.rpm | wc -l
    warning: httpd-2.4.6-97.el7.centos.aarch64.rpm: Header V3 RSA/SHA256 Signature, key ID 305d49d6: NOKEY
    496
    [root@arm64v8 packages]#
  3. 查询某个未安装包的依赖性如 httpd-2.4.6-97.el7.centos.aarch64.rpm 的依赖性。

    [root@arm64v8 packages]# rpm -qpR httpd-2.4.6-97.el7.centos.aarch64.rpm
    warning: httpd-2.4.6-97.el7.centos.aarch64.rpm: Header V3 RSA/SHA256 Signature, key ID 305d49d6: NOKEY
    /etc/mime.types
    system-logos >= 7.92.1-1
    httpd-tools = 2.4.6-97.el7.centos
    /usr/sbin/useradd
    /usr/sbin/groupadd
    systemd-units
    ......
    libsystemd-daemon.so.0()(64bit)
    libsystemd-daemon.so.0(LIBSYSTEMD_DAEMON_31)(64bit)
    libz.so.1()(64bit)
    rtld(GNU_HASH)
    rpmlib(PayloadIsXz) <= 5.2-1
    [root@arm64v8 packages]# 
    • 实际上,查看包的依赖性时,使用 yum-utils 包中的 repoquery 工具更好,"repoquery -R pkg_name" 会更简洁。yum-utils 中包含了好几个非常实用的包管理工具,可以大致都了解下。

8.2.3、rpm 提取包文件

安装 rpm 包会安装 rpm 中所有文件,如果将某个文件删除了,除了重装 rpm,还可以通过从 rpm 包提取缺失文件的方式来修复。

在 win 上安装个万能压缩工具 "好压",可以直接打开 rpm 包,然后从中解压需要的文件出来。但是在 Linux 上,过程还是有点小复杂的,其中涉及了 cpio 归档工具。

rpm2cpio 是将 rpm 转换为 cpio 格式归档文件的命令,有了 cpio 文件,就可以使用 cpio 命令对其进行相关的操作。

cpio 命令是从归档文件中提取文件或向归档文件中写入文件的工具,一般都从标准输入或输出操作归档文件,所以都使用管道或重定向符号。

cpio 选项说明:

-i: 运行在copy-in模式,即从归档文件中将文件copy出来,即提取文件(提取)
-o: 运行在copy-out模式,将文件copy到归档文件中,即将文件拷贝到归档文件中(写入)
-d: 需要目录时自动建立目录
-v: 显示信息

提取 rpm 包文件的一般格式为:rpm2cpio package_full_name | cpio -idv dir_name

举个小例子:

  1. 确认一下 cat 命令来自于哪个软件包

    [root@arm64v8 ~]# which cat
    /usr/bin/cat
    [root@arm64v8 ~]# rpm -qf /usr/bin/cat
    coreutils-8.22-21.el7.aarch64
    [root@arm64v8 ~]#
  2. 如果 /usr/bin/cat 文件丢失,模拟从 coreutils-8.22-21.el7.aarch64 提取 /usr/bin/cat 填充丢失文件

    [root@arm64v8 ~]# rm -rf /usr/bin/cat
    [root@arm64v8 ~]# 
    [root@arm64v8 ~]# cat /etc/passwd
    -bash: cat: command not found
    [root@arm64v8 ~]# 
    [root@arm64v8 ~]# rpm2cpio coreutils-8.22-24.el7_9.2.aarch64.rpm | cpio -id
    34315 blocks
    [root@arm64v8 ~]# ls
    coreutils-8.22-24.el7_9.2.aarch64.rpm  etc  usr
    [root@arm64v8 ~]# 
    [root@arm64v8 ~]# ls usr/bin/cat 
    usr/bin/cat
    [root@arm64v8 ~]# cp usr/bin/cat /usr/bin/
    [root@arm64v8 ~]# 
    [root@arm64v8 ~]# cat /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin

8.2.4、yum 工具

yum 是 Yellowdog update Modified 的简称,yum 的宗旨是自动化的升级、安装/移除 rpm 安装包,收集 rmp 的相关信息,检查依赖性,并提示用户解决。

yum 工作原理分析

Server 端先对程序包进行分类后存储到不同 repository 中;再通过收集到大量的 rpm 的数据库文件中程序包之间的依赖关系数据,计算对应的依赖关系和所需文件,构造出关键性 xml 说明文件, 存放在本地的 repodata 目录下,下面对这些 xml 说明文件进行一下解释。

  1. repomd.xml:本质上是一个索引,它包含下面列出的其他 XML 元数据文件的位置、校验和和时间戳。
  2. repomd.xml.asc:只有当 repository 创建者使用 GPG 对 repomd.xml 文件进行签名时,才会生成此文件,如果用户安装了pygpgme 包,Yum 将下载并验证这个签名。
  3. primary.xml.gz:包含关于 repository 中每个包的详细信息。您将发现诸如名称、版本、许可、依赖关系信息、时间戳、大小等信息。
  4. filelists.xml.gz:包含关于 repository 中每个包中的每个文件和目录的信息。
  5. other.xml.gz:包含在 RPM SPEC 文件中为存储库中的每个包找到的变更日志条目。
  6. comps.xml:包含软件包组的列表,控制软件包组安装。

Cilent 端通过 yum 命令安装软件时发现缺少某些依赖性程序包,Client 会根据本地的配置文件 /etc/yum.repos.d/*.repo 找到指定的 Server 端,从 Server 端 repodata 目录下获取说明文件 xxx.xml 后存储在本地 /var/cache/yum 中方便以后读取,通过 xxx.xml 文件查找到需要安装的依赖性程序包在 Server 端的存放位置, 再进入 Server 端 yum 库中的指定 repository 中获取所需程序包,下载完成后在本地实现安装。

yum

8.2.4.1、yum 配置文件

/etc/yum.conf 是 yum 的默认文件,里面配置的也是全局默认项。

[root@arm64v8 ~]# cat /etc/yum.conf 
[main]
cachedir=/var/cache/yum/$basearch/$releasever   # 缓存目录
keepcache=0                                     # 是否保留缓存,设置为1时,安装包时所下载的包将不会被删除
debuglevel=2                                    # 调试信息的级别
logfile=/var/log/yum.log                        # 日志文件位置
exactarch=1                                     # 设置为1将只会安装和系统架构完全匹配的包
obsoletes=1                                     # 是否允许更新旧的包
gpgcheck=1                                      # 是否要进行gpg check
plugins=1                                       # 是否允许使用yum插件
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                     # 指定基准包,yum会根据这个包判断发行版本

8.2.4.2、配置 yum 仓库

首先配置 yum 仓库,配置文件为 /etc/yum.conf 和 /etc/yum.repos.d/ 中的 ".repo" 文件,其中 /etc/yum.conf 配置的是仓库的默认项,一般配置 yum 源都是在 /etc/yum.repos.d/*.repo 中配置。注意,该目录中任意 repo 文件都会被读取。

默认 /etc/yum.repos.d/ 下会有以下几个仓库文件,除了 CentOS-Base.repo,其他的都可以删掉,基本没用。

[root@arm64v8 ~]# ls /etc/yum.repos.d/
CentOS-Base.repo       CentOS-fasttrack.repo  CentOS-Vault.repo
CentOS-Debuginfo.repo  CentOS-Media.repo
[root@arm64v8 ~]#

repo 文件的配置格式如下:

[base]      # 仓库ID,ID必须保证唯一性
name        # 仓库名称,可随意命名
mirrorlist  # 该地址下包含了仓库地址列表,包含一个或多个镜像站点,和baseurl使用一个就可以了
#baseurl    # 仓库地址。网络上的地址则写网络地址,本地地址则写本地地址,格式为“file://”后接路径,如file:///mnt/cdrom
gpgcheck    # 指定是否需要签名,1表示需要,0表示不需要
gpgkey      # 签名文件的路径
enable      # 该仓库是否生效,enable=1表示生效,enable=0表示不生效
cost        # 开销越高,优先级越低

[repo配置文件中可用的宏:]
$releasever:    程序的版本,对Yum而言指的是redhat-relrase版本。只替换为主版本号,如Redhat6.5 则替换为6
$arch:          系统架构
$basharch:      系统基本架构,如i686,i586等的基本架构为i386
$YUM0-9:        在系统定义的环境变量,可以在yum中使用

8.2.4.3、配置 epel 仓库

系统发行商在系统中放置的 rpm 包一般版本都较老,可能有些包有较大的延后性。而 epel 是由 fedora 社区维护的高质量高可靠性的安装源,有很多包是比系统包更新的,且多出很多系统没有的包。总之,用到 epel 的机会很多,所以就拿来当配置示例了。

有两种方式可以使用 epel 源:

  1. 安装 epel-release.noarch 包

    [root@arm64v8 ~]# ls /etc/yum.repos.d/
    CentOS-Base.repo
    [root@arm64v8 ~]# 
    [root@arm64v8 ~]# yum search epel
    epel-release.noarch : Extra Packages for Enterprise Linux repository configuration
    [root@arm64v8 ~]# 
    [root@arm64v8 ~]# yum install epel-release
    Installed:
     epel-release.noarch 0:7-11                                                                 
    Complete!
    [root@arm64v8 ~]# ls /etc/yum.repos.d/
    CentOS-Base.repo  epel.repo  epel-testing.repo
    [root@arm64v8 ~]# 
  2. 手动在 /etc/yum.repos.d/ 下面建一个 xxx.repo,复制国内 epel 源

    [root@arm64v8 ~]# cat /etc/yum.repos.d/epel.repo 
    [epel]
    name=Extra Packages for Enterprise Linux 7 - $basearch
    baseurl=https://repo.huaweicloud.com/epel/7/$basearch
    #metalink=https://mirrors.fedoraproject.org/#metalink?repo=epel-7&arch=$basearch
    failovermethod=priority
    enabled=1
    gpgcheck=1
    gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
    
    [epel-debuginfo]
    name=Extra Packages for Enterprise Linux 7 - $basearch - Debug
    baseurl=https://repo.huaweicloud.com/epel/7/$basearch/debug
    #metalink=https://mirrors.fedoraproject.org/#metalink?repo=epel-debug-7&arch=$basearch
    failovermethod=priority
    enabled=0
    gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
    gpgcheck=1
    
    [epel-source]
    name=Extra Packages for Enterprise Linux 7 - $basearch - Source
    baseurl=https://repo.huaweicloud.com/epel/7/SRPMS
    #metalink=https://mirrors.fedoraproject.org/#metalink?repo=epel-source-7&arch=$basearch
    failovermethod=priority
    enabled=0
    gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
    gpgcheck=1
    [root@arm64v8 ~]#

8.2.4.4、yum 命令

Usage: yum [options] COMMAND

List of Commands:

check                           检查 RPM 数据库问题
check-update                    检查是否有可用的软件包更新
clean                           删除缓存数据
deplist                         列出软件包的依赖关系
distribution-synchronization    已同步软件包到最新可用版本
downgrade                       降级软件包
erase                           从系统中移除一个或多个软件包
fs                              作用于主机的文件系统数据,主要用于为最小的主机删除docs/lanuages。  
fssnapshot                      创建文件系统快照,列出/删除当前快照。  
groups                          显示或使用、组信息
help                            显示用法提示
history                         显示或使用事务历史
info                            显示关于软件包或组的详细信息
install                         向系统中安装一个或多个软件包
list                            列出一个或一组软件包
load-transaction                从文件名中加载一个已存事务
makecache                       创建元数据缓存
provides                        查找提供指定内容的软件包
reinstall                       覆盖安装软件包
repo-pkgs                       将一个源当作一个软件包组,这样我们就可以一次性安装/移除全部软件包。
repolist                        显示已配置的源
search                          在软件包详细信息中搜索指定字符串
shell                           运行交互式的 yum shell
swap                            交换包的简单方法,而不是使用shell  
update                          更新系统中的一个或多个软件包
update-minimal                  跟升级差不多,但是升级到最新的匹配包解决影响系统的问题
updateinfo                      在 repository 上更新信息
upgrade                         更新软件包同时考虑软件包取代关系
version                         显示机器可用的源版本。

Options:
  -h, --help                                        显示此帮助消息并退出
  -t, --tolerant                                    忽略错误
  -C, --cacheonly                                   完全从系统缓存运行,不升级缓存
  -c [config file], --config=[config file]          配置文件路径
  -R [minutes], --randomwait=[minutes]              命令最长等待时间
  -d [debug level], --debuglevel=[debug level]      调试输出级别
  --showduplicates                                  在 list/search 命令下,显示源里重复的条目
  -e [error level], --errorlevel=[error level]      错误输出级别
  --rpmverbosity=[debug level name]                 RPM 调试输出级别
  -q, --quiet                                       静默执行
  -v, --verbose                                     详尽的操作过程
  -y, --assumeyes                                   回答全部问题为是
  --assumeno                                        回答全部问题为否
  --version                                         显示 Yum 版本然后退出
  --installroot=[path]                              设置安装根目录
  --enablerepo=[repo]                               启用一个或多个软件源(支持通配符)
  --disablerepo=[repo]                              禁用一个或多个软件源(支持通配符)
  -x [package], --exclude=[package]                 采用全名或通配符排除软件包
  --disableexcludes=[repo]                          禁止从主配置,从源或者从任何位置排除
  --disableincludes=[repo]                          禁用指定的 repo
  --obsoletes                                       更新时处理软件包取代关系
  --noplugins                                       禁用 Yum 插件
  --nogpgcheck                                      禁用 GPG 签名检查
  --disableplugin=[plugin]                          禁用指定名称的插件
  --enableplugin=[plugin]                           启用指定名称的插件
  --skip-broken                                     忽略存在依赖关系问题的软件包
  --color=COLOR                                     配置是否使用颜色
  --releasever=RELEASEVER                           在 yum 配置和 repo 文件里设置 $releasever 的值
  --downloadonly                                    仅下载而不更新
  --downloaddir=DLDIR                               指定一个其他文件夹用于保存软件包
  --setopt=SETOPTS                                  设置任意配置和源选项
  --bugfix                                          在更新中包含bug修复相关的包
  --security                                        在更新中包含安全相关的包  
  --advisory=ADVS, --advisories=ADVS                在更新中包含修复给定建议所需的包  
  --bzs=BZS                                         在更新中包含修复给定BZ所需的包  
  --cves=CVES                                       在更新中包含修复给定CVE所需的包  
  --sec-severity=SEVS, --secseverity=SEVS           在更新中包含匹配严重性的安全相关包  

常用的一些例子:

  1. 安装或卸载 tree 软件包

    [root@arm64v8 ~]# yum install tree -y
    [root@arm64v8 ~]# yum remove tree -y
  2. 有时候想用一个命令,比如 pstree,但是不知道安装哪个软件包才能提供这个命令,使用 yum provides

    [root@arm64v8 ~]# yum provides pstree
    Loaded plugins: fastestmirror
    Loading mirror speeds from cached hostfile
    base/aarch64/filelists_db                                                                                                                                             | 6.2 MB  00:00:00     
    extras/aarch64/filelists_db                                                                                                                                           | 289 kB  00:00:00     
    updates/aarch64/filelists_db                                                                                                                                          | 1.7 MB  00:00:00     
    psmisc-22.20-17.el7.aarch64 : Utilities for managing processes on your system
    Repo        : base
    Matched from:
    Filename    : /usr/bin/pstree
    
    [root@arm64v8 ~]# yum install psmisc -y
  3. 有时候只记得一个软件包名字大概,没法正确安装这个包,可以使用 yum search 模糊查找定位到想要安装的包

    [root@arm64v8 ~]# yum search mariadb
    Loaded plugins: fastestmirror
    Loading mirror speeds from cached hostfile
    =================================================================================== N/S matched: mariadb ====================================================================================
    mariadb-bench.aarch64 : MariaDB benchmark scripts and data
    mariadb-devel.aarch64 : Files for development of MariaDB/MySQL applications
    mariadb-embedded.aarch64 : MariaDB as an embeddable library
    mariadb-embedded-devel.aarch64 : Development files for MariaDB as an embeddable library
    mariadb-libs.aarch64 : The shared libraries required for MariaDB/MySQL clients
    mariadb-server.aarch64 : The MariaDB server and related files
    mariadb.aarch64 : A community developed branch of MySQL
    mariadb-test.aarch64 : The test suite distributed with MariaD
    
     Name and summary matches only, use "search all" for everything.
    [root@arm64v8 ~]# 
  4. 安装图形化界面时,不可能一个个软件去装,借用 yum groups 包组管理方式安装

    # 查看有哪些包组
    [root@arm64v8 ~]# yum groups list
    Loaded plugins: fastestmirror
    There is no installed groups file.
    Maybe run: yum groups mark convert (see man yum)
    Loading mirror speeds from cached hostfile
    Available Environment Groups:
      Minimal Install
      Compute Node
      Infrastructure Server
      File and Print Server
      Basic Web Server
      Virtualization Host
      Server with GUI
      GNOME Desktop
      KDE Plasma Workspaces
      Development and Creative Workstation
    Available Groups:
      Compatibility Libraries
      Console Internet Tools
      Development Tools
      Graphical Administration Tools
      Legacy UNIX Compatibility
      Scientific Support
      Security Tools
      Smart Card Support
      System Administration Tools
      System Management
    Done
    
    # 定位到"GNOME Desktop",这个组下面就是图形化界面所需的所有软件包,一起安装
    [root@arm64v8 ~]# yum groups install "GNOME Desktop" -y

8.3、deb 包

deb 是 Unix 系统(其实主要是 Linux)下的安装包,基于 tar 包,因此本身会记录文件的权限(读/写/可执行)以及所有者/用户组。由于 Unix 类系统对权限、所有者、组的严格要求,而 deb 格式安装包又经常会涉及到系统比较底层的操作,所以权限等的设置尤其重要。

deb 包本身有三部分组成:

  1. 数据包,包含实际安装的程序数据,文件名为 data.tar.XXX;data.tar.XXX 包含的是实际安装的程序数据,而在安装过程中,该包里的数据会被直接解压到根目录(即 / ),因此在打包之前需要根据文件所在位置设置好相应的文件/目录树。
  2. 安装信息及控制脚本包,包含 deb 的安装说明,标识,脚本等,文件名为 control.tar.gz;而 control.tar.gz 则包含了一个 deb 安装的时候所需要的控制信息。一般有 5 个文件:
    • control 用了记录软件标识,版本号,平台,依赖信息等数据;
    • preinst 在解包data.tar.gz 前运行的脚本;
    • postinst 在解包数据后运行的脚本;
    • prerm 卸载时,在删除文件之前运行的脚本;
    • postrm 在删除文件之后运行的脚本;
  3. deb 文件的一些二进制数据,包括文件头等信息,一般看不到,在某些软件中打开可以看到;

注意:deb 本身可以使用不同的压缩方式。tar 格式并不是一种压缩格式,而是直接把分散的文件和目录集合在一起,并记录其权限等数据信息。之前提到过的 data.tar.XXX,这里 XXX 就是经过压缩后的后缀名。deb 默认使用的压缩格式为 gzip 格式,所以最常见的就是 data.tar.gz。常有的压缩格式还有 bzip2 和 lzma,其中 lzma 压缩率最高,但压缩需要的 CPU 资源和时间都比较长。

deb 软件包名格式为:Package_Version-Build_Architecture.deb

vim_8.2.2434-3ubuntu1_arm64.deb
  • 软件包名称(Package Name):vim
  • 版本(Version Number):8.2.2434
  • 修订号(Build Number):3ubuntu1
  • 平台(Architecture):arm64

8.3.1、dpkg 工具简介

dpkg 是 Debian 的一个底层包管理工具,主要用于对已下载到本地和已安装的软件包进行管理。

dpkg 软件包相关的文件:

/etc/dpkg/dpkg.cfg dpkg:    包管理软件的配置文件[Configuration file with default options]
/var/log/dpkg.log dpkg:     包管理软件的日志文件[Default log file (see /etc/dpkg/dpkg.cfg(5) and option --log)]
/var/lib/dpkg/available:    存放系统所有安装过的软件包信息[List of available packages]
/var/lib/dpkg/status:       存放系统现在所有安装软件的状态信息
/var/lib/dpkg/info:         记安装软件包控制目录的控制信息文件

dpkg 数据库:

  • dpkg 使用文本文件作为数据库来维护系统中软件,包括文件清单、依赖关系、软件状态等详细的内容,通常在 /var/lib/dpkg 目录下。

  • 通常在 status 文件中存储软件状态和控制信息。

  • 在 info/ 目录下备份控制文件, 并在其下的 .list 文件中记录安装文件清单, 其下的 .mdasums 保存文件的 MD5 编码。

查询 dpkg 数据库(显示所有已安装的Deb包):

root@arm64v8:~# dpkg -l | more
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                                  Version                            Architecture Description
+++-=====================================-==================================-============-===============================================================================
ii  accountsservice                       0.6.45-1ubuntu1                    arm64        query and manipulate user account information
ii  acl                                   2.2.52-3build1                     arm64        Access control list utilities
ii  acpid                                 1:2.0.28-1ubuntu1                  arm64        Advanced Configuration and Power Interface event daemon
ii  adduser                               3.116ubuntu1                       all          add and remove users and groups
ii  apparmor                              2.12-4ubuntu5.1                    arm64        user-space parser utility for AppArmor
ii  apport                                2.20.9-0ubuntu7.5                  all          automatically generate crash reports for debugging
ii  apport-symptoms                       0.20                               all          symptom scripts for apport
ii  apt                                   1.6.8                              arm64        commandline package manager
ii  apt-utils                             1.6.8                              arm64        package management related utility programs
ii  aptitude                              0.8.10-6ubuntu1                    arm64        terminal-based package manager
ii  aptitude-common                       0.8.10-6ubuntu1                    all          architecture independent files for the aptitude package manager
......

如上代码,每条记录对应一个软件包,注意每条记录的第一、二、三个字符,这就是软件包的状态标识,后边依此是软件包名称、版本号和简单描述。

  1. 第一字符为期望值(Desired=Unknown/Install/Remove/Purge/Hold),它包括:
    • u Unknown 状态未知,这意味着软件包未安装,并且用户也未发出安装请求。
    • i Install 用户请求安装软件包。
    • r Remove 用户请求卸载软件包。
    • p Purge 用户请求清除软件包。
    • h Hold 用户请求保持软件包版本锁定。
  2. 第二列,是软件包的当前状态(Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend)
    • n Not 软件包未安装。
    • i Inst 软件包安装并完成配置。
    • c Conf-files 软件包以前安装过,现在删除了,但是它的配置文件还留在系统中。
    • u Unpacked 软件包被解包,但还未配置。
    • f halF-conf 试图配置软件包,但是失败了。
    • h Half-inst 软件包安装,但是没有成功。
    • h Half-inst 软件包安装,但是没有成功。
    • t Trig-pend 触发器未决。
  3. 第三列标识错误状态,第一种状态标识没有问题,为空。其它符号则标识相应问题(Err?=(none)/Reinst-required (Status,Err: uppercase=bad))。
    • h 软件包被强制保持,因为有其它软件包依赖需求,无法升级。
    • r Reinst-required 软件包被破坏,可能需要重新安装才能正常使用(包括删除)。
    • x 软包件被破坏,并且被强制保持。

常见的几种情况:

ii      表示系统正常安装了该软件

pn      表示安装了该软件,后来又清除了

un      表示从未安装过该软件

iu      表示安装了该软件,但是未配置

rc      表示该软件已被删除,但配置文件仍在

8.3.2、dpkg 安装

# 安装软件包,必须是deb包的完整名称。(软件的安装可被拆分为两个独立的过程“解包”和“配置”)
dpkg -i package-name.deb

# “解包”:解开软件包到系统目录但不配置,如果和-R一起使用,参数可以是一个目录
dpkg --unpack package-name.deb
# “配置”:配置软件包
dpkg --configure package-name.deb

# 列出 deb 包的内容
dpkg -c package-name.deb
# zsh 依赖 zsh-common,所以两个包要一起装
root@arm64v8:~# dpkg -i zsh-common_5.4.2-3ubuntu3.1_all.deb zsh_5.4.2-3ubuntu3.1_arm64.deb 
Selecting previously unselected package zsh-common.
(Reading database ... 67232 files and directories currently installed.)
Preparing to unpack zsh-common_5.4.2-3ubuntu3.1_all.deb ...
Unpacking zsh-common (5.4.2-3ubuntu3.1) ...
Preparing to unpack zsh_5.4.2-3ubuntu3.1_arm64.deb ...
Unpacking zsh (5.4.2-3ubuntu3.1) over (5.4.2-3ubuntu3.1) ...
Setting up zsh-common (5.4.2-3ubuntu3.1) ...
Setting up zsh (5.4.2-3ubuntu3.1) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
root@arm64v8:~#

8.3.3、dpkg 移除软件包

# 移除软件包,但保留其配置文件
dpkg -r/--remove package-name 

# 清除软件包的所有文件(removes everything,including conffiles)
dpkg -P/--purge package-name
root@arm64v8:~# dpkg -P zsh zsh-common
(Reading database ... 68526 files and directories currently installed.)
Removing zsh (5.4.2-3ubuntu3.1) ...
Purging configuration files for zsh (5.4.2-3ubuntu3.1) ...
Removing zsh-common (5.4.2-3ubuntu3.1) ...
Purging configuration files for zsh-common (5.4.2-3ubuntu3.1) ...
rmdir: failed to remove '/usr/local/share/zsh/site-functions': No such file or directory
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
root@arm64v8:~# 

8.3.4、dpkg 查询

# 查看系统中软件包名符合pattern模式的软件包
dpkg -l/--list package-name-pattern

# 查看package-name对应的软件包安装的文件及目录
dpkg -L/--listfiles package-name

# 显示包的具体信息
dpkg -p/--print-avail package-name

# 查看package-name(已安装)对应的软件包信息
dpkg -s/--status package-name

# 从已经安装的软件包中查找包含filename的软件包名称 (Tip:也可使用子命令dpkg-query来进行查询操作)
dpkg -S/--search filename-search-pattern

举几个小例子:

  1. 列出系统上安装的与 dpkg 相关的软件包

    root@arm64v8:~# dpkg -l *dpkg*
    Desired=Unknown/Install/Remove/Purge/Hold
    | Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
    |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
    ||/ Name              Version       Architecture  Description
    +++-=================-=============-=============-=======================================
    ii  dpkg              1.19.0.5ubunt arm64         Debian package management system
    un  dpkg-dev                          (no description available)
    un  libdpkg-perl                      (no description available)
    root@arm64v8:~#
  2. 查看 dpkg 软件包安装到系统中的文件

    root@arm64v8:~# dpkg -L dpkg | more
    /.
    /etc
    /etc/alternatives
    /etc/alternatives/README
    /etc/cron.daily
    /etc/cron.daily/dpkg
    /etc/dpkg
    /etc/dpkg/dpkg.cfg
    /etc/dpkg/dpkg.cfg.d
    /etc/logrotate.d
    ......

说明:这里只是列出了 dpkg 命令几个最常用的用法,dpkg 其实还有别的选项和子命令,这里不再多说,因为大多数情况用不上,而是使用功能更加强大的前端工具 apt。

8.3.5、apt 工具

APT 全称为 "Advanced Package Tool" 即高级软件包工具,这是现今最成熟的软件包管理系统。

虽然我们在使用 dpkg 时,已经解决掉了软件安装过程中的大量问题,但是当依赖关系不满足时,仍然需要手动解决,而 apt 可以自动检测软件依赖问题,下载和安装所有文件;甚至只需要一条命令,就可以更新整个系统上所有的软件包。

APT 最初被设计运行于 Debian 系统上,只支持 .deb 格式的软件包文件。如今,APT 已经被移植到使用 PRM 软件包机制的发行版上。可以从 apt-rpm.org 获得 APT 的 RPM 版本。

apt 工作原理分析

apt 的 repository 定义在 /etc/apt/sources.list 这个文件当中,包源的格式如下:

deb uri distribution [component1] [component2] [...]

比如说:

deb https://deb.debian.org/debian stable main contrib non-free
  • 这里的 deb 指向的是二进制包的源,如果是 deb-src 则指向源码包的源。一个文件可以同时定义 deb 和 deb-src,但是要分开指定。

  • 这里的 uri https://deb.debian.org/debian 指的是归档根目录,通常情况下 Debian 归档目录在服务器上的 debian/ 目录下,当然也可以定义在任何地方。

  • 这里的 stable 指的是 $ARCHIVE_ROOT/dists 下的一个子目录,也就是 Debian 归档根目录下发行版本下的一个稳定版本,而 stable 下面也组织了很多子目录和文件 main、contrib、non-free、InRelease、Release等;而所谓的版本 distribution 是关联 SuiteCodename,这在 Release 文件中清楚地定义了。再往深走,比如 main 下面就是不同的 CPU 架构,然后再往下就是二进制包索引文件 Packages.gz 等。

提示:分析到这里,不难发现,Debian 作为 Linux 操作系统的发行版,有自己的一套版本管理系统,涉及到发布的版本、自由和非自由二进制包管理集、不同的 CPU 架构管理集等;实际上,几乎所有的 Debian 系操作系统都采用类似的版本管理策略,即便组织方式略有不同,但是核心思想是一样的。

为了从 repository 下载软件包,apt 将会从 $ARCHIVE_ROOT/dists/$DISTRIBUTION 目录下载一个 InRelease 或者 Release 文件

  • InRelease 文件是自带签名密钥行的,而 Release 文件专门有一个附带的 Release.gpg 密钥文件。
  • Release 文件列出了 distribution 的 index 文件和它们的 hash 值(列出的 index 文件是相对于 Release 文件位置来说的)

为了下载 main 组件的索引 Packages.gz,apt 将会在 main 目录下面为文件的 hash 值扫描 Release 文件。

比如说:

https://deb.debian.org/debian/dists/testing/main/binary-arm64/Packages.gz 
就应该存在于
https://deb.debian.org/debian/dists/testing/Release
作为
main/binary-arm64/Packages.gz
  • Packages.gz 包含每个包的名称、版本、大小、长短描述和依赖关系,以及一些我们不感兴趣的附加信息。所有这些信息都被列出(并被 Debian 包管理器使用),比如 dselect 或 aptitude。

二进制包索引在组件 component 目录的子目录 binary-$arch,同理源码包在源码子目录;无论是二进制包或是源码包,具体位置都跟归档根目录有关。

为了避免文件重复,二进制包和源码包通常保存在归档根目录 archive root 的 pool 子目录中。Packages 和 Sources 索引可以列出相对于 archive root 的任何路径,然而,我们建议把包放在 archive root 的子目录下,不是 dists 目录下,也不应该直接放在 archive root 目录下。

Contents 和 Translation 索引不是特定于架构的就直接放在 dists/$DISTRIBUTION/$COMPONENT 目录下,不需要放在架构的子目录。

一个标准的 Debian Repository:

(your repository root) 
| 
+-dists
  | 
  |-stable
  | |-main
  | | |-binary-alpha 
  | | |-binary-arm
  | | |-binary-...
  | | +-source 
  | |-contrib
  | | |-binary-alpha 
  | | |-binary-arm
  | | |-binary-...
  | | +-source 
  | +-non-free
  |   |-binary-alpha
  |   |-binary-arm
  |   |-binary-...
  |   +-source
  |
  |-testing 
  | |-main
  | | |-binary-alpha 
  | | |-binary-arm
  | | |-binary-...
  | | +-source 
  | |-contrib
  | | |-binary-alpha 
  | | |-binary-arm
  | | |-binary-...
  | | +-source 
  | +-non-free
  |   |-binary-alpha
  |   |-binary-arm
  |   |-binary-...
  |   +-source
  |
  +-unstable 
    |-main
    | |-binary-alpha 
    | |-binary-arm
    | |-binary-...
    | +-source 
    |-contrib
    | |-binary-alpha 
    | |-binary-arm
    | |-binary-...
    | +-source 
    +-non-free
      |-binary-alpha
      |-binary-arm
      |-binary-...
      +-source
  • 自由的包在 main 里面,非自由的包在 non-free 里面,自由的依赖于非自由的在 contrib 里面;Debian目前支持 11 个架构;为了简洁,我省略了大部分。
  • 每个 binary-* 目录包含一个 Packages.gz 和一个可选的 Release 文件;每个 source 目录包含一个 Sources.gz 和一个可选的 Release 文件。请注意,包不必与索引文件在同一个目录中,因为索引文件包含了各个包的路径;实际上,它们可以在存储库中的任何其他地方。这使得创建 pool 成为可能。
  • 你可以自由地创建任意多的发行版和组件,并按你的意愿调用它们;我在示例中使用的只是 Debian 中使用的。例如,你可以创建发行版 current 和 beta(而不是 stable、testing 和 instability),以及组件 foo、bar、baz 和 qux(而不是 main、contrib 和 non-free)。
  • 虽然您可以随意调用组件,但使用标准 Debian 发行版通常是个好主意,因为这是 Debian 用户所期望的。

提示:通过以上原理分析,相信 deb 包的组织方式基本上搞清楚了,还有很多细节这里没有办法一一说明,如果想搞得更加清楚,可以参考官方文档 https://wiki.debian.org/PackageManagement https://wiki.debian.org/DebianRepository/Format。另外如果对于 Debian 发行版感兴趣,可以参考中文官方文档 https://www.debian.org/doc/manuals/debian-reference/index.zh-cn.html

8.3.5.1、apt 更新元数据

当使用 APT 工具时,如 aptitude, apt-get, synaptic, apt-file, auto-apt,我们需要更新包含 Debian 档案库信息元数据的本地拷贝。这些本地拷贝的文件名称,和在 "/etc/apt/sources.list" 文件里面的 distribution, area, architecture 相应名称一致。

  • "/var/lib/apt/lists/deb.debian.org_debian_dists_<distribution>_Release"
  • "/var/lib/apt/lists/deb.debian.org_debian_dists_<distribution>_Release.gpg"
  • "/var/lib/apt/lists/deb.debian.org_debian_dists_<distribution>_<area>_binary-<architecture>_Packages"
  • "/var/lib/apt/lists/deb.debian.org_debian_dists_<distribution>_<area>_source_Sources"
  • "/var/cache/apt/apt-file/deb.debian.org_debian_dists_<distribution>_Contents-<architecture>.gz" (apt-file)

前 4 种类型的文件是所有相关的 APT 命令共享的,并且可以通过 “apt-get update” 或 “aptitude update” 在命令行中进行更新。

如果在 “/etc/apt/sources.list” 中有相应的 “deb” 行,则 “软件包” 元数据会进行更新。如果在 “/etc/apt/sources.list” 中有相应的 “deb-src” 行,则 “源代码” 元数据会进行更新。

"Packages" 和 "Sources" 的元数据文件包含有“Filename:”字段,指向二进制和源代码包文件的位置。目前,这些软件包都统一放在"pool/"目录树下,这样可以改善跨版本发布的传输。

软件包”元数据的本地副本可以使用 aptitude 来进行交互式的搜索。专门的搜索命令 grep-dctrl(1) 可以搜索“软件包”和“源代码”元数据的本地副本。

"Contents-<architecture>"元数据的本地拷贝,能够被"apt-file update"更新,它的位置和其它 4 个不同。参见 apt-file(1). (auto-apt 的 "Contents-<architecture>.gz"文件的本地拷贝默认也使用不同的位置。)

说明:学习是有一个过程的,我花了大量的时间研究官方文档,有时不得不研究英文文档,有时会有官方中文文档,有时英文文档和中文文档内容有穿插重合,所以我会进行一些必要的整合并提炼出来。推荐一个不错的中文文档 https://www.debian.org/doc/manuals/debian-reference/ch02.zh-cn.html

root@arm64v8:~# aptitude update
Get: 1 https://repo.huaweicloud.com/ubuntu-ports bionic InRelease [242 kB]
Get: 2 https://repo.huaweicloud.com/ubuntu-ports bionic-security InRelease [88.7 kB]
Get: 3 https://repo.huaweicloud.com/ubuntu-ports bionic-updates InRelease [88.7 kB]
Get: 4 https://repo.huaweicloud.com/ubuntu-ports bionic-backports InRelease [74.6 kB]
Get: 5 https://repo.huaweicloud.com/ubuntu-ports bionic/restricted Sources [5,324 B]
Get: 6 https://repo.huaweicloud.com/ubuntu-ports bionic/universe Sources [9,051 kB]
Get: 7 https://repo.huaweicloud.com/ubuntu-ports bionic/main Sources [829 kB]
Get: 8 https://repo.huaweicloud.com/ubuntu-ports bionic/multiverse Sources [181 kB]
Get: 9 https://repo.huaweicloud.com/ubuntu-ports bionic/main arm64 Packages [975 kB]
Get: 10 https://repo.huaweicloud.com/ubuntu-ports bionic/main Translation-en [516 kB]
Get: 11 https://repo.huaweicloud.com/ubuntu-ports bionic/restricted arm64 Packages [664 B]
Get: 12 https://repo.huaweicloud.com/ubuntu-ports bionic/restricted Translation-en [3,584 B]
Get: 13 https://repo.huaweicloud.com/ubuntu-ports bionic/universe arm64 Packages [8,316 kB]
Get: 14 https://repo.huaweicloud.com/ubuntu-ports bionic/universe Translation-en [4,941 kB]
Get: 15 https://repo.huaweicloud.com/ubuntu-ports bionic/multiverse arm64 Packages [126 kB]
Get: 16 https://repo.huaweicloud.com/ubuntu-ports bionic/multiverse Translation-en [108 kB]
Get: 17 https://repo.huaweicloud.com/ubuntu-ports bionic-security/restricted Sources [19.2 kB]
Get: 18 https://repo.huaweicloud.com/ubuntu-ports bionic-security/multiverse Sources [5,240 B]
Get: 19 https://repo.huaweicloud.com/ubuntu-ports bionic-security/universe Sources [279 kB]
Get: 20 https://repo.huaweicloud.com/ubuntu-ports bionic-security/main Sources [255 kB]
Get: 21 https://repo.huaweicloud.com/ubuntu-ports bionic-security/main arm64 Packages [997 kB]
Get: 22 https://repo.huaweicloud.com/ubuntu-ports bionic-security/main Translation-en [329 kB]
Get: 23 https://repo.huaweicloud.com/ubuntu-ports bionic-security/restricted arm64 Packages [2,852 B]
Get: 24 https://repo.huaweicloud.com/ubuntu-ports bionic-security/restricted Translation-en [48.9 kB]
Get: 25 https://repo.huaweicloud.com/ubuntu-ports bionic-security/universe arm64 Packages [994 kB]
Get: 26 https://repo.huaweicloud.com/ubuntu-ports bionic-security/universe Translation-en [256 kB]
Get: 27 https://repo.huaweicloud.com/ubuntu-ports bionic-security/multiverse arm64 Packages [2,732 B]
Get: 28 https://repo.huaweicloud.com/ubuntu-ports bionic-security/multiverse Translation-en [4,412 B]
Get: 29 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/restricted Sources [22.1 kB]
Get: 30 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/main Sources [514 kB]
Get: 31 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/multiverse Sources [14.5 kB]
Get: 32 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/universe Sources [451 kB]
Get: 33 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/main arm64 Packages [1,298 kB]
Get: 34 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/main Translation-en [422 kB]
Get: 35 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/restricted arm64 Packages [3,404 B]
Get: 36 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/restricted Translation-en [52.8 kB]
Get: 37 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/universe arm64 Packages [1,536 kB]
Get: 38 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/universe Translation-en [371 kB]
Get: 39 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/multiverse arm64 Packages [4,920 B]
Get: 40 https://repo.huaweicloud.com/ubuntu-ports bionic-updates/multiverse Translation-en [6,792 B]
Get: 41 https://repo.huaweicloud.com/ubuntu-ports bionic-backports/main Sources [5,440 B]
Get: 42 https://repo.huaweicloud.com/ubuntu-ports bionic-backports/universe Sources [5,360 B]
Get: 43 https://repo.huaweicloud.com/ubuntu-ports bionic-backports/main arm64 Packages [9,992 B]
Get: 44 https://repo.huaweicloud.com/ubuntu-ports bionic-backports/main Translation-en [4,764 B]
Get: 45 https://repo.huaweicloud.com/ubuntu-ports bionic-backports/universe arm64 Packages [9,932 B]
Get: 46 https://repo.huaweicloud.com/ubuntu-ports bionic-backports/universe Translation-en [4,588 B]
Fetched 33.5 MB in 11s (3,176 kB/s)
Current status: 257 (+257) upgradable, 62330 (+62330) new.
root@arm64v8:~#
  • 从 update 的输出信息可以感受到 repository 元数据的下载过程。
  • 除了远程获取元数据,aptitude 命令还会将它在本地产生的安装状态信息保存在 “/var/lib/aptitude/pkgstates” 中,这些信息只能被 aptitude 使用。
  • aptitude 的这个缓存文件清理策略,能够在"Options" → "Preferences"下设置,也可以通过它的菜单,"Actions"下的"Clean package cache" 或 "Clean obsolete files" 来执行强制清理。
  • 另外需要说明的是,如果是使用 apt 工具,所有通过 APT 机制远程获取的软件包都被保存在 “/var/cache/apt/archives” 中,直到它们被清除。除了远程获取元数据,lenny 之后的 APT 工具还会将它在本地产生的安装状态信息保存在 “/var/lib/apt/extended_states” 中,APT 会使用它们来追踪自动安装的所有软件包。

8.3.5.2、apt 实用命令

在 Debian 系统中有许多基于 APT 的软件包管理工具可以在 Debian 系统上进行基于仓库的软件包管理操作。在这里,我们将介绍 3 种基本的软件包管理工具:apt,apt-get / apt-cacheaptitude

对于涉及软件包安装或更新软件包元数据的软件包管理操作,你必须有 root 权限。

apt vs apt-get 或者 apt-cache vs aptitude

尽管 aptitude 是作者主要使用的一个非常好的可互动工具,但你应该知道下列警示:

  1. 不建议在新版本发布后在 stable Debian 系统上使用 aptitude 命令来进行跨版本的系统升级。
    • 建议使用"apt full-upgrade"或“apt-get dist-upgrade” 来进行这个操作。
  2. aptitude命令有时候会为了testingunstable Debian 系统升级清除大量软件包。
    • 这个情况吓坏了许多的系统管理员。请不要惊慌。
    • 这似乎大多数是由元软件包的依赖或推荐的软件包版本偏差造成的,例如 gnome-core
    • 要解决这个问题,可以在 aptitude 命令菜单中选择 “取消待执行的动作” ,退出 aptitude,并使用 “apt full-upgrade”。

apt-getapt-cache 是最 基础 的基于 APT 的软件包管理工具。

  1. apt-getapt-cache 只提供命令行用户界面。
  2. apt-get 是进行跨版本的 主系统升级 等操作的最合适工具。
  3. apt-get 提供了一个 强大 的软件包依赖解析器。
  4. apt-get 对硬件资源的要求不高。它消耗更少的内存并且运行速度更快。
  5. apt-cache 提供了一个 标准的 正则表达式来搜索软件包名称和描述。
  6. apt-getapt-cache 可以使用 /etc/apt/preferences 来管理软件包的多个版本,但这非常繁琐。

apt 命令是一个用于软件包管理的高级命令行界面。它基本上是 apt-getapt-cache 和类似命令的一个封装,被设计为针对终端用户交互的界面,它默认启用了某些适合交互式使用的选项。

  1. apt 工具在用户使用 apt install 安装软件包时提供了一个友好的进度条。
  2. 在成功安装下载的软件包后,apt 将默认 删除 缓存的 .deb 软件包。

提示:建议用户使用新的 apt(8) 命令用于 交互式 的使用场景,而在 shell 脚本中使用 apt-get(8) 和 apt-cache(8) 命令。

aptitude 命令是最 通用的 基于 APT 的软件包管理工具。

  1. aptitude 提供了一个全屏的交互式文本用户界面。
  2. aptitude 同样也提供了一个命令用户界面。
  3. aptitude 是用于 日常软件包管理(例如检查已安装的软件包和搜索可用的软件包)的最合适工具。
  4. aptitude 对硬件资源的要求更高。它消耗更多的内存并且运行速度更慢。
  5. aptitude 提供一个 增强的 正则表达式来搜索所有的软件包元数据。
  6. aptitude 可以管理软件包的多个版本,并且不使用 /etc/apt/preferences,这会十分直观。

命令行中的基础软件包管理操作

apt 语法 aptitude 语法 apt-get / apt-cache 语法 说明
apt update aptitude update apt-get update 更新软件包档案库元数据
apt install foo aptitude install foo apt-get install foo 安装 foo 软件包的候选版本以及它的依赖
apt upgrade aptitude safe-upgrade apt-get upgrade 安装已安装的软件包的候选版本并且不移除任
何其它的软件包
apt full-upgrade aptitude full-upgrade apt-get dist-upgrade 安装已安装的软件包的候选版本,并且需要的
话会移除其它的软件包
apt remove foo aptitude remove foo apt-get remove foo 移除 foo 软件包,但留下配置文件
apt autoremove N/A apt-get autoremove 移除不再需要的自动安装的软件包
apt purge foo aptitude purge foo apt-get purge foo 清除 foo 软件包的配置文件
apt clean aptitude clean apt-get clean 完全清除本地仓库的软件包检索文件
apt autoclean aptitude autoclean apt-get autoclean 清除本地仓库中过时软件包的软件包检索文件
apt show foo aptitude show foo apt-cache show foo 显示 foo 软件包的详细信息
apt search <正则表达式> aptitude search <regex> apt-cache search <regex> 搜索匹配 <regex> 的软件包
N/A aptitude why <regex> N/A 解释匹配 <regex> 的软件包必须被安装的原因
N/A aptitude why-not <regex> N/A 解释匹配 <regex> 的软件包不必安装的原因
N/A aptitude search '~i!~M' apt-mark showmanual 列出手动安装的软件包
  • 虽然 aptitude 命令提供了丰富的功能,例如增强的软件包解析器,但它的复杂程度导致了(或可能导致)一些退步,例如 Bug #411123Bug #514930Bug #570377。如有疑问,请使用 apt,apt-getapt-cache 命令来替代 aptitude 命令。
  • 因为在 lenny 版本之后的 Debian 系统中, aptapt-getaptitude 会共享自动安装的软件包的状态(参见第 2.5.5 节 “APT 的软件包状态”),因此你可以混合使用这些工具而不会出现严重的麻烦(参见 Bug #594490)。

提示:更多详细的内容请参考 https://www.debian.org/doc/manuals/debian-reference/ch02.zh-cn.html#_basic_package_management_operations

举几个小例子:

  1. 查看 apache2 包的详细信息

    root@arm64v8:~# apt-cache show apache2
    Package: apache2
    Architecture: arm64
    Version: 2.4.29-1ubuntu4.16
    Priority: optional
    Section: web
    Origin: Ubuntu
    Maintainer: Ubuntu Developers 
    Original-Maintainer: Debian Apache Maintainers 
    Bugs: https://bugs.launchpad.net/ubuntu/+filebug
    Installed-Size: 523
    ......
    Package: apache2
    Architecture: arm64
    Version: 2.4.29-1ubuntu4
    Priority: optional
    Section: web
    Origin: Ubuntu
    Maintainer: Ubuntu Developers 
    Original-Maintainer: Debian Apache Maintainers 
    Bugs: https://bugs.launchpad.net/ubuntu/+filebug
    Installed-Size: 520
    ......
    root@arm64v8:~#
  2. 使用 install -d 下载 apache2 安装时的所有依赖包

    root@arm64v8:~# tree /tmp/
    /tmp/
    
    0 directories, 0 files
    root@arm64v8:~# 
    root@arm64v8:~# apt install -d -y -o dir::cache=/tmp apache2 
    Reading package lists... Done
    Building dependency tree       
    Reading state information... Done
    The following additional packages will be installed:
     apache2-bin apache2-data apache2-utils libapr1 libaprutil1 libaprutil1-dbd-sqlite3
     libaprutil1-ldap liblua5.2-0 libssl1.1 ssl-cert
    Suggested packages:
     www-browser apache2-doc apache2-suexec-pristine | apache2-suexec-custom openssl-blacklist
    The following NEW packages will be installed:
     apache2 apache2-bin apache2-data apache2-utils libapr1 libaprutil1
     libaprutil1-dbd-sqlite3 libaprutil1-ldap liblua5.2-0 ssl-cert
    The following packages will be upgraded:
     libssl1.1
    1 upgraded, 10 newly installed, 0 to remove and 256 not upgraded.
    Need to get 2,579 kB of archives.
    After this operation, 7,012 kB of additional disk space will be used.
    ......
    Fetched 2,579 kB in 1s (3,135 kB/s)   
    Download complete and in download only mode
    root@arm64v8:~# 
    root@arm64v8:~# tree /tmp/
    /tmp/
    ├── archives
    │   ├── apache2_2.4.29-1ubuntu4.16_arm64.deb
    │   ├── apache2-bin_2.4.29-1ubuntu4.16_arm64.deb
    │   ├── apache2-data_2.4.29-1ubuntu4.16_all.deb
    │   ├── apache2-utils_2.4.29-1ubuntu4.16_arm64.deb
    │   ├── libapr1_1.6.3-2_arm64.deb
    │   ├── libaprutil1_1.6.1-2_arm64.deb
    │   ├── libaprutil1-dbd-sqlite3_1.6.1-2_arm64.deb
    │   ├── libaprutil1-ldap_1.6.1-2_arm64.deb
    │   ├── liblua5.2-0_5.2.4-1.1build1_arm64.deb
    │   ├── libssl1.1_1.1.1-1ubuntu2.1~18.04.9_arm64.deb
    │   ├── lock
    │   ├── partial
    │   └── ssl-cert_1.0.39_all.deb
    ├── pkgcache.bin
    └── srcpkgcache.bin
    
    2 directories, 14 files
    root@arm64v8:~# 

8.4、yum 和 apt 对比

下面是在 Debian/Ubuntu 和 Red Hat/Fedora 系统上用于包管理的等价命令表:

任务 Redhat/Fedora Debian/Ubuntu
Refresh list of available packages Yum refreshes each time it's used apt-get update
Install a package from a repository yum install package_name apt-get install package_name
Install a package file yum install package.rpm
rpm -i package.rpm
dpkg --install package.deb
Remove a package yum remove package_name
rpm -e package_name
apt-get remove package_name
Check for package upgrades yum check-update apt-get -s upgrade
apt-get -s dist-upgrade
Upgrade packages yum update
rpm -Uvh [args]
apt-get upgrade
Upgrade the entire system yum upgrade apt-get dist-upgrade
Get information about an available package yum search package_name apt-cache search package_name
Show available packages yum list available apt-cache dumpavail
List all installed packages yum list installed
rpm -qa
dpkg --list
Get information about a package yum info package_name apt-cache show package_name
Get information about an installed package rpm -qi package_name dpkg --status package_name
List files in an installed package rpm -ql package_name dpkg --listfiles package_name
List documentation files in an installed package rpm -qd package_name -
List configuration files in an installed package rpm -qc package_name dpkg-query --show -f '${Conffiles}\n' package_name
Show the packages a given package depends on rpm -qR package_name apt-cache depends
Show other packages that depend on a given
package (reverse dependency)
rpm -q -whatrequires [args] apt-cache rdepends
Get information about a package file rpm -qpi package.rpm dpkg --info package.deb
List files in a package file rpm -qpl package.rpm dpkg --contents package.deb
List documentation files in a package file rpm -qpd package.rpm -
List configuration files in a package file rpm -qpc package.rpm -
Extract files in a package rpm2cpio package.rpm | cpio -vid dpkg-deb --extract package.deb dir-to-extract-to
Find package that installed a file rpm -qf filename dpkg --search filename
Find package that provides a particular file yum provides filename apt-file search filename
Show stats about the package cache - apt-cache stats
Verify all installed packages rpm -Va debsums
Remove packages from the local cache directory yum clean packages apt-get clean
Remove only obsolete packages from the local
cache directory
- apt-get autoclean
Remove header files from the local cache
directory
yum clean headers apt-file purge
Package file extension *.rpm *.deb
Repository location configuration /etc/yum.conf /etc/apt/sources.list

8.5、yum 和 apt 本地源制作

前面我们说过,Redhat 系和 Debian 系操作系统是当今两天主流,所以很多商业公司为了节约成本,会选择这两个开源分支,特别是一些初创公司。

为了节约成本,小公司不会选择购买昂贵的企业网络,而是组建维持公司正常运作的必要内网。这种情况下,大多数开发工作会受到限制,因为没法通过外网获取开发依赖的软件包。搭建本地源就显得很关键,根据经验,当一个公司只有一到两个运维的时候,撑握该技术不可避免。

8.5.1、CentOS 安装源制作

作为 Redhat 系最受欢迎的操作系统当然是 CentOS,因为 Redhat 是一家商业公司,直接使用 Redhat 操作系统难免要支付一部分服务费才能用得很顺畅。而 CentOS 作为 Redhat 旗下全开源的操作系统自然成为一众公司的不二之选。

下面就以 CentOS 7.6 版本为例,搭建本地源。

8.5.1.1、环境准备

  1. 操作系统准备

    [root@yidam ~]# cat /etc/redhat-release 
    CentOS Linux release 7.6.1810 (AltArch) 
    [root@yidam ~]#
    • 操作系统的安装是比较基础的能力,这里不打算介绍,可以参考 CentOS 官方文档 https://docs.centos.org/en-US/centos/install-guide/Simple_Installation/
  2. 配置好操作系统的 YUM 源

    [root@yidam ~]# cat /etc/yum.repos.d/CentOS-Base.repo | grep -v "^$" | grep -v "^#"
    [base]
    name=CentOS-$releasever - Base
    baseurl=https://mirrors.aliyun.com/centos-altarch/$releasever/os/$basearch/
    gpgcheck=1
    gpgkey=http://archive.kernel.org/centos-vault/altarch/7.6.1810/os/aarch64/RPM-GPG-KEY-CentOS-7-aarch64
    [updates]
    name=CentOS-$releasever - Updates
    baseurl=https://mirrors.aliyun.com/centos-altarch/$releasever/updates/$basearch/
    gpgcheck=1
    gpgkey=http://archive.kernel.org/centos-vault/altarch/7.6.1810/os/aarch64/RPM-GPG-KEY-CentOS-7-aarch64
    [extras]
    name=CentOS-$releasever - Extras
    baseurl=https://mirrors.aliyun.com/centos-altarch/$releasever/extras/$basearch/
    gpgcheck=1
    gpgkey=http://archive.kernel.org/centos-vault/altarch/7.6.1810/os/aarch64/RPM-GPG-KEY-CentOS-7-aarch64
    enabled=1
    [centosplus]
    name=CentOS-$releasever - Plus
    baseurl=https://mirrors.aliyun.com/centos-altarch/$releasever/centosplus/$basearch/
    gpgcheck=1
    enabled=0
    gpgkey=http://archive.kernel.org/centos-vault/altarch/7.6.1810/os/aarch64/RPM-GPG-KEY-CentOS-7-aarch64
    [root@yidam ~]#
    • 这里复制的是阿里云的源,我们需要通过阿里云的镜像源把所有的 centos7.6 相关软件下载到本地。
    • 注意这里的 gpgkey 借用了 Centos 官方的 gpgkey,直接用阿里的源 gpgkey 是错误的。
  3. 补充 epel 源

    [root@yidam ~]# yum install epel-release.noarch  -y
    • 前面我们有介绍 epel 源,如果有不明白的地方可以回顾。
  4. 确认当前可以同步的 repository。

    [root@yidam ~]# yum repolist
    repo id
    base/7/aarch64
    epel/aarch64
    extras/7/aarch64
    updates/7/aarch64
    repolist: 22,035
    [root@yidam ~]#

8.5.1.2、安装相关软件

制作 rpm 源,我们需要借用一个命令工具 createrepo,此工具使用起来既简单又强大。

[root@yidam ~]# yum install yum-utils -y
  • 该软件包可以提供 createrepo 命令,利用此命令就可以为 rpm 包创建元数据索引

8.5.1.3、制作源

  1. 准备好下载 rpm 包的目录,一般情况下官方发布的 centos7 版本源大约 12GB,epel 源大约 15GB,另外还有一些不停更新的包,总共大约 30GB。我当前生产环境中的本地 centos7.6 源达到了 33GB,此生产环境已经不间断运行了 2 年,有了这些数据,您应该可以规划自已的存储空间了。

    [root@yidam ~]# fdisk -l | grep sda
    Disk /dev/sda: 1099.5 GB, 1099511627776 bytes, 2147483648 sectors
    [root@yidam ~]#
    [root@yidam ~]# mkdir /mirrors/
    [root@yidam ~]# mount /dev/sda /mirrors/
    [root@yidam ~]# df -h |grep mirror
    /dev/sda                1008G   28G  930G   3% /mirrors
    [root@yidam ~]# mkdir /mirrors/c76
    • 我的生产环境规划的是 1 TB 存储,使用的是 lvm,保证了存储的可扩展性,底层存储使用的是 arm64 版本的商业 ceph 存储,性能还不错。
  2. 开始同步

    [root@yidam ~]# reposync -p /mirrors/c76/
    • reposync 会把 centos 当前 yum.repos.d 下的所有源都同步下来,-p 指定下载存放的目录。可以指定 repoid 下载,reposync -r base -p /mirror/c76,同步 base 目录到本地。
  3. 创建包索引

    [root@yidam ~]# createrepo -po /mirrors/c76/base/ /mirrors/c76/base/
    [root@yidam ~]# createrepo -po /mirrors/c76/updates/ /mirrors/c76/updates/
    [root@yidam ~]# createrepo -po /mirrors/c76/extras/ /mirrors/c76/extras/
    [root@yidam ~]# createrepo -po /mirrors/c76/epel/ /mirrors/c76/epel/
    • createrepo 会把指定目录中所有的 rpm 包建立一个 repodata,repodata 的作用本章前面有详细介绍。
    • -p 是美化 xml 元数据文件的格式,-o 是指把 repodata 输出到哪个目录。
  4. 创建包组索引

    [root@yidam ~]# wget http://archive.kernel.org/centos-vault/altarch/7.6.1810/os/aarch64/repodata/aced7d22b338fdf7c0a71ffcf32614e058f4422c42476d1f4b9e9364d567702f-c7-x86_64-comps.xml
    [root@yidam ~]# mv aced7d22b338fdf7c0a71ffcf32614e058f4422c42476d1f4b9e9364d567702f-c7-x86_64-comps.xml /mirrors/c76/base/base-aarch64-comps.xml
    [root@yidam ~]# createrepo -g /mirrors/c76/base/base-aarch64-comps.xml /mirrors/c76/base/
    • aarch64 centos7.5 包组元数据文件官方命名为 xxx-c7-aarch64-comps.xml,aarch64 centos7.6 官方命名为 xxx-c7-x86_64-comps.xml,由此可见包组元数据文件是 x86 aarch64 或者其它架构都是通用的。
  5. 更新 rpm 包

    [root@yidam ~]# reposync -np /mirrors/c76/
    • CentOS 的每一个版本在生命周期内都是在不停发展的,所以 rpm 包也在不停地更新当中。
    • reposync 用法同第 2 步,-n 则是 newest-only 也就是只同步最新的包。
  6. 更新源数据

    [root@yidam ~]# createrepo --update /mirrors/c76/base/
    [root@yidam ~]# createrepo --update /mirrors/c76/extras/
    [root@yidam ~]# createrepo --update /mirrors/c76/updates/
    [root@yidam ~]# createrepo --update /mirrors/c76/epel/
    
    [root@yidam ~]# createrepo --update -g /mirrors/c76/base/base-aarch64-comps.xml /mirrors/c76/base
    • rpm 包更新完了,repodata 元数据当然也得跟着更新。

8.5.1.4、自动化

由于 CentOS 的每一个版本在生命周期内都是在不停地发展,为了能让生产环境中的 CentOS 软件包可以正常的更新迭代,我们可以配置任务计划,进行周期性地更新。

  1. 创建同步脚本

    [root@yidam ~]# cat /mirrors/script/c76_yum_update.sh 
    #!/bin/bash
    echo "Updating Aliyun Yum Sources"
    Datetime=date +%F_%T
    exec > /var/log/updateAliyunRepo_$Datetime.log
       reposync -np /mirrors/c76/
    if [ $? -eq 0 ];then
       createrepo --update /mirrors/c76/base
       createrepo --update /mirrors/c76/extras
       createrepo --update /mirrors/c76/updates
       createrepo --update /mirrors/c76/epel
       createrepo --update -g base-aarch64-comps.xml /mirrors/c76/base
       echo "Success: $Datetime updateAliyunRepo successful!!!"
    else
       echo "Error: $Datetime updateAliyunRepo failed!!!"
    fi
    [root@yidam ~]# chmod u+x /mirrors/script/c76_yum_update.sh 
    • 脚本执行的时候,如果没有 /bin/bash 解析,则脚本本身必须要有执行权限。
  2. 制作任务计划

    [root@yidam ~]# crontab -l
    # Update AliyunRepo Sources
    # 每月第一个周六的23点更新阿里云yum源
    00 23 * * 6 [ date +%d -eq cal|awk 'NR==3{print $NF}' ] && /bin/bash /mirrors/script/c76_yum_update.sh
    [root@yidam ~]#

8.5.1.5、发布源

发布源的方式有很多,常用的是 http 协议或者 ftp 协议,我们这里选择 http 协议,搭建一个 nginx 服务器将源发布出去。

  1. 配置 nginx 发布自制 yum 源

    [root@yidam ~]# groupadd nginx
    [root@yidam ~]# useradd -r -g nginx -s /bin/false -M nginx
    [root@yidam ~]# yum install nginx -y
    [root@yidam ~]# cat /etc/nginx/nginx.conf | grep -v "^#" | grep -v "^$"
    user nginx;
    worker_processes auto;
    error_log /var/log/nginx/error.log;
    pid /run/nginx.pid;
    include /usr/share/nginx/modules/*.conf;
    events {
       worker_connections 1024;
    }
    http {
       log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                         '$status $body_bytes_sent "$http_referer" '
                         '"$http_user_agent" "$http_x_forwarded_for"';
       access_log  /var/log/nginx/access.log  main;
       sendfile            on;
       tcp_nopush          on;
       tcp_nodelay         on;
       keepalive_timeout   65;
       types_hash_max_size 2048;
       include             /etc/nginx/mime.types;
       default_type        application/octet-stream;
       # Load modular configuration files from the /etc/nginx/conf.d directory.
       # See http://nginx.org/en/docs/ngx_core_module.html#include
       # for more information.
       include /etc/nginx/conf.d/*.conf;
       server {
           listen       80 default_server;
           listen       [::]:80 default_server;
           server_name  _;
           root         /mirrors/c76/;
           # Load configuration files for the default server block.
           include /etc/nginx/default.d/*.conf;
           location / {
           autoindex on;
           autoindex_exact_size off;
           autoindex_localtime on;
           charset utf-8,gbk;
           index index.html;
           }
           error_page 404 /404.html;
               location = /40x.html {
           }
           error_page 500 502 503 504 /50x.html;
               location = /50x.html {
           }
       }
    }
    [root@yidam ~]#
    [root@yidam ~]# systemctl enable nginx
    [root@yidam ~]# systemctl start nginx
  2. nginx 服务器的地址是 192.168.100.10,客户端打开浏览器输入 http://192.168.100.10,效果就跟利用网络打开 https://archive.kernel.org/centos-vault/altarch/7.5.1804/os/aarch64/ 是一样的。客户端直接配置 yum 源即可。

  3. [可选] 公司内部可以搭建一个 DNS 服务器,设计一个主机域名比如 mirrors.brinnatt.com,A 记录指向 192.168.100.10,如此,客户端配置 yum 源时就可以使用 http://mirrors.brinnatt.com

8.5.2、Ubuntu 安装源制作

作为 Debian 系最受欢迎的操作系统当属 Ubuntu,桌面非常美观,使用简单且功能强大,有庞大的受众群体。使用过程中如果遇到任何困难,只要一 google,基本上都迎刃而解。

相比较而言,Ubuntu 背后没有像 Redhat 那样强大的商业公司,但凭借着自身的优势加之完全开源,在开源社区发挥着举足轻重的作用。

8.5.2.1、环境准备

  1. 操作系统准备

    ly@ubuntu:~$ sudo lsb_release -a
    No LSB modules are available.
    Distributor ID:  Ubuntu
    Description: Ubuntu 18.04.2 LTS
    Release: 18.04
    Codename:    bionic
    ly@ubuntu:~$
    • ubuntu 操作系统的安装属于比较基础的能力,这里不作介绍,可以参见官方文档 https://ubuntu.com/tutorials/install-ubuntu-desktop#1-overview
  2. 配置好操作系统的 APT 源

    ly@ubuntu:~$ sudo cat /etc/apt/sources.list
    deb http://mirrors.aliyun.com/ubuntu-ports bionic main restricted
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-updates main restricted
    deb http://mirrors.aliyun.com/ubuntu-ports bionic universe
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-updates universe
    deb http://mirrors.aliyun.com/ubuntu-ports bionic multiverse
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-updates multiverse
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-backports main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-security main restricted
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-security universe
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-security multiverse
    ly@ubuntu:~$
    • 这里配置的是阿里云的镜像源,用来安装同步镜像所需的工具。
    • 如果想要了解 apt 源背后的原理,可以参考本章中对 apt 详解的部分。

8.5.2.2、安装相关软件

  1. 安装 apt-mirror 工具

    ly@ubuntu:~$ sudo apt install apt-mirror -y
    • apt-mirror 工具会按照 /etc/apt/mirror.list 配置文件把指定 repository 中的所有软件包都下载到本地
  2. 配置 /etc/apt/mirror.list

    ly@ubuntu:~$ sudo cat /etc/apt/mirror.list 
    ############# config ##################
    set base_path    /mirrors/1804/apt-mirror        # 配置数据存放基目录
    set mirror_path  $base_path/mirror               # 配置镜像存储位置  
    set skel_path    $base_path/skel             # 配置临时下载索引位置
    set var_path     $base_path/var                  # 配置日志,URLs和MD5校验信息存储位置
    set cleanscript $var_path/clean.sh               # 配置删除过期源脚本位置(默认不删除,方便安装旧版本软件)      
    # set defaultarch     # 设置默认架构, 可填: amd64 或 i386,默认是和本机一个架构
    set postmirror_script $var_path/postmirror.sh    # 设定下载后运行的脚本位置
    set run_postmirror 0                         # 设置是否执行下载后的脚本操作,默认是1(但是默认没有postmirror.sh脚本)
    set nthreads     20                              # 设置下载线程数
    set _tilde 0                                 # 是否替换URL中的波浪线,替换成%7E(HTML代码),否则会跳过不进行下载                                    
    ############# end config ##############
    
    # 配置 Ubuntu bionic 源,apt-mirror 会把该源下的所有软件包同步到本地
    deb http://mirrors.aliyun.com/ubuntu-ports bionic main restricted
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-updates main restricted
    deb http://mirrors.aliyun.com/ubuntu-ports bionic universe
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-updates universe
    deb http://mirrors.aliyun.com/ubuntu-ports bionic multiverse
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-updates multiverse
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-backports main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-security main restricted
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-security universe
    deb http://mirrors.aliyun.com/ubuntu-ports bionic-security multiverse
    
    clean http://mirrors.aliyun.com/ubuntu-ports
    ly@ubuntu:~$

8.5.2.3、制作源

  1. 准备好存放源的目录

    ly@ubuntu:~$ sudo mkdir /mirrors -pv
    ly@ubuntu:~$ sudo fdisk -l |grep sda
    Disk /dev/sda: 1 TiB, 1099511627776 bytes, 2147483648 sectors
    ly@ubuntu:~$ sudo mount /dev/sda /mirrors
    ly@ubuntu:~$ df -h | grep sda
    /dev/sda       1007G  219G  737G  23% /mirrors
    ly@ubuntu:~$ sudo mkdir /mirrors/1804
    • 通过调研,官方发布的 ubuntu 18.04 版本的软件包总共有 100GB 左右。这是我的生产环境,配置了 1TB 的大小。这个环境已经不间断生产了 2 年,目前的软件包共有 102GB。有了这些数据,您应该可以规划自已的存储空间了。
    • 注意:apt-mirror 在同步软件包的时候需要访问数据目录,一定要注意数据目录的权限,有的人会选择创建 apt-mirror 用户,专门用来通步软件包,此时数据目录的属主属组应该改成 apt-mirror。我这里用的是 root 用户来同步软件包。
  2. 开始同步

    ly@ubuntu:~$ sudo apt-mirror
  3. 验收同步的源

    ly@ubuntu:~$ sudo tree /mirrors/1804/ -d -L 6
    /mirrors/1804/
    └── apt-mirror
       ├── mirror
       │   └── mirrors.aliyun.com
       │       └── ubuntu-ports
       │           ├── dists
       │           │   ├── bionic
       │           │   ├── bionic-backports
       │           │   ├── bionic-security
       │           │   └── bionic-updates
       │           └── pool
       │               ├── main
       │               ├── multiverse
       │               ├── restricted
       │               └── universe
       ├── skel
       │   └── mirrors.aliyun.com
       │       └── ubuntu-ports
       │           └── dists
       │               ├── bionic
       │               ├── bionic-backports
       │               ├── bionic-security
       │               └── bionic-updates
       └── var
    
    23 directories
    ly@ubuntu:~$

8.5.2.4、自动化

由于 Ubuntu 的每一个版本在生命周期内都是在不停地发展,为了能让生产环境中的 Ubuntu 软件包可以正常的更新迭代,我们可以配置任务计划,进行周期性地更新。

  1. 创建同步脚本

    ly@ubuntu:~$ sudo cat /usr/local/bin/apt_update.sh 
    #!/bin/bash
    echo "Updating Aliyun APT Sources"
    Datetime=date +%F_%T
    exec > /var/spool/apt-mirror/var/updateAliyunAPT_$Datetime.log
       /usr/bin/apt-mirror
       if [ $? -eq 0 ];then
           echo "Success: $Datetime updateAliyunAPT successful!!!"
       else
           echo "Error: $Datetime updateAliyunAPT failed!!!"
       fi
    ly@ubuntu:~$
  2. 制作任务计划

    ly@ubuntu:~$ sudo crontab -l | tail -3
    # Update AliyunAPT Sources
    # 每月第一个周六的23点更新阿里云apt源
    00 23 * * 6 [ date +%d -eq cal|awk 'NR==3{print $NF}' ] && /bin/bash /usr/local/bin/apt_update.sh
    ly@ubuntu:~$

8.5.2.5、发布源

发布源的方式有很多,常用的是 http 协议或者 ftp 协议,我们这里选择 http 协议,搭建一个 nginx 服务器将源发布出去。

  1. 配置 nginx 发布自制 apt 源

    ly@ubuntu:~$ sudo apt install nginx -y
    ly@ubuntu:~$ sudo cat /etc/nginx/nginx.conf 
    user www-data;
    worker_processes auto;
    pid /run/nginx.pid;
    include /etc/nginx/modules-enabled/*.conf;
    
    events {
    worker_connections 768;
    # multi_accept on;
    }
    
    http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
       server {
           listen          80;
           server_name     1804;
           root  /mirrors/1804/apt-mirror/mirror/mirrors.aliyun.com/;
           location / {
               autoindex on;
               autoindex_exact_size off;
               autoindex_localtime on;
               charset utf-8,gbk;
               index index.html;
           }
       }
    }
    ly@ubuntu:~$ systemctl enable nginx
    ly@ubuntu:~$ systemctl restart nginx
  2. nginx 服务器的地址是 192.168.100.11,客户端打开浏览器输入 http://192.168.100.11,效果就跟利用网络打开 http://ports.ubuntu.com/ubuntu-ports/ 是一样的。客户端直接配置 apt 源即可。

  3. [可选] 公司内部可以搭建一个 DNS 服务器,设计一个主机域名比如 mirrors.kuber.com,A 记录指向 192.168.100.11,如此,客户端配置 apt 源时就可以使用 http://mirrors.kuber.com

8.6、源码编译

这里说的源码编译是指使用 GNU gcc 编译器编译的源码或者项目,也就是通常使用 C 或 C++ 开发的程序。使用其它语言开发的程序不一定在这里实用,所以读者一定要搞清楚的是,决定源码如何编译成二进制程序的关键是程序设计者使用了什么样的管理方式。

因此,编译源码之前要阅读解压目录中的 INSTALL/README 文件,这是程序设计者对源码做的相关说明,其中包括如何安装。大多数情况下,这些开发者都有约定俗成的套路,减少程序的使用难度。下面介绍一下源码编译约定俗成的套路。

8.6.1、通用步骤

  1. 准备好编译环境,比如 CentOS 首先就应该安装 "Development Tools" 和 "Development Libraries" 。

  2. 下载源码包,一般是 xxx.tar.gz、xxx.tar.bz2 或者 xxx.tar.xz 等压缩格式的源码包。

  3. 解压源码包,源码中一般有一个 configure,执行 ./configure -h 获取帮助,根据自己的需求,定制配置选项,比如启用哪些功能、禁用哪些功能、安装路径定义在哪等。执行 ./configure 过程中会检查系统环境是否符合满足安装要求,并将定义好的安装配置和系统环境信息写入Makefile文件中。

    • configure 一般都会接受以下几个编译选项

      --prefix=          :指定安装的路径
      --sysconfdir=      :指定配置文件目录
      --enable-feature   :启用某个特性
      --disable-fecture  :禁用特性
      --with-function    :启用某功能
      --without-function :禁用某功能
    • 一般情况下源文件自带的是 makefile.in 文件,执行 configure 后,makefile.in 转换成 makefile。

    • configure 和 makefile.in 虽然由源文件自带,但是并非是由作者自己写的,由两个命令生成。

      • automake 命令生成 makefile.in,绝大多数情况源码中已经生成 makefile.in。
      • autoconf 命令生成 configure,所以如果源码没有 configure,也不要㤺,使用 autoconf 生成即可。
  4. 执行 make 命令进行编译。make 命令会根据 Makefile 文件进行编译。编译工作主要是调用编译器(如gcc g++)将源码编译为可执行文件,通常需要一些函数库才能产生一个完整的可执行文件。

  5. make install 将上一步所编译的数据复制到指定的目录下。

上面的每一个步骤都不能出错,否则无法达到预想的效果。所有的步骤都正常进行完成,需要做一些后续操作,虽非必须,但是都是个性化定制,方便以后的操作。个性化定制大致包括以下几项:

  1. 将安装路径下的命令路径加入到环境变量。

    root@arm64v8:~# echo "export PATH=/usr/local/apache/bin:$PATH" > /etc/profile.d/apache.sh
    root@arm64v8:~# chmod +x /etc/profile.d/apache.sh
    root@arm64v8:~# source /etc/profile.d/apache.sh
  2. 按需求定制服务启动脚本,并考虑是否加入开机启动项。

  3. 输出头文件和库文件。

    # 输出头文件
    root@arm64v8:~# ln -s /usr/local/apache/include /usr/include/apache
    
    # 输出库文件
    root@arm64v8:~# echo "/usr/local/apache/lib" > /etc/ld.so.conf.d/apache.conf
    root@arm64v8:~# ldconfig
    • 头文件库文件很多时候只是为其他程序提供的,所以可能不输出它们的路径也不会影响该程序的运行。
  4. 导出 man 路径

    echo  "MANPATH /usr/local/apache/man" >> /etc/man.conf

卸载时,只需删除安装目录即可。如果安装的路径比较分散,不方便直接删除,那么也可回到源码目录(如果删除了需要重新下载并解压),执行 make uninstall。

8.7、打补丁

在 ARM64 架构下无论是操作系统,还是应用程序,打补丁的可能性还是比较大的。因为在服务器市场 ARM64 架构还不够成熟,很多厂商使用的操作系统改了一部分内核源代码,这就会造成一些不稳定因素,所以很有可能会涉及到修改内核或者应用程序的源代码,使用打补丁的方式比较简单。

8.7.1、命令简介

制作补丁和应用补丁会涉及到两个命令,一个 diff 命令,一个 patch 命令。

8.7.1.1、diff 命令

diff 可以比较两个对象,并可同时记录下二者的区别。制作补丁时的一般用法和常见选项为:

diff [选项] 源文件(夹) 目的文件(夹)

-r: 递归。设置后diff会将两个不同版本源代码目录中的所有对应文件全部都进行一次比较,包括子目录文件。
-N: 选项确保补丁文件将正确地处理已经创建或删除文件的情况。
-u: 输出每个修改前后的3行,也可以用-u5等指定输出更多上下文。
-E, -b, -w, -B, --strip-trailing-cr: 忽略各种空白,可参见文档,按需选用。
  • 由于历史原因,diff 的输出格式经历过几个变种,正常格式(normal diff)、上下文格式(context diff)、合并格式(unified diff);

    • 正常格式(normal diff):diff file1 file2 这种方式可以得到二者的正常格式对比结果。
    root@arm64v8:~# echo -e "1\n2\n3\n4\n5\n6\n7" > file1
    root@arm64v8:~# echo -e "1\n2\n3\na\n5\n6\n7" > file2
    root@arm64v8:~# diff file1 file2 
    4c4
    < 4
    ---
    > a
    root@arm64v8:~#
    • 4c4 分成三个部分:前面的 "4",表示 file1 的第 4 行有变化;中间的 "c" 表示变动的模式是内容改变(change),其他模式还有 "增加"(a,代表addition)和 "删除"(d,代表deletion);后面的 "4",表示变动后变成 file2 的第 4 行。

    • < 4 前面的小于号,表示要从 file1 当中去除该行(也就是第 4 行),后面的 "4" 表示该行的内容。

    • --- 用来分割 file1 和 file2

    • > a 前面的大于号表示 file2 增加了该行,后面的 "a" 表示该行的内容。

    • 上下文格式(context diff):diff -c file1 file2 这种方式可以得到二者的上下文格式对比结果。

    root@arm64v8:~# echo -e "1\n2\n3\n4\n5\n6\n7" > file1
    root@arm64v8:~# echo -e "1\n2\n3\na\n5\n6\n7" > file2
    root@arm64v8:~# diff -c file1 file2
    *** file1   2021-07-12 02:41:09.442080211 -0400
    --- file2   2021-07-12 02:41:13.142045812 -0400
    ***************
    *** 1,7 ****
      1
      2
      3
    ! 4
      5
      6
      7
    --- 1,7 ----
      1
      2
      3
    ! a
      5
      6
      7
    root@arm64v8:~#
    • diff 结果输出部分前两行显示两个文件的基本情况:文件名和时间信息。"***" 表示变动前的文件,"---" 表示变动后的文件。

    • diff 结果输出部分第三行是15个星号,将文件的基本情况与变动内容分割开。

    • *** 1,7 **** 以下 7 行是 file1 显示变动前的文件,这时不仅显示发生变化的第 4 行,还显示第 4 行的前面三行和后面三行,因此一共显示 7 行。所以,前面的 "*** 1,7 ****" 就表示,从第 1 行开始连续 7 行。另外,文件内容的每一行最前面,还有一个标记位。如果为空,表示该行无变化;如果是感叹号(!),表示该行有改动;如果是减号(-),表示该行被删除;如果是加号(+),表示该行为新增。

    • --- 1,7 ---- 以下 7 行是 file2 显示变动前的文件,除了变动行(第 4 行)以外,也是上下文各显示三行,总共显示 7 行。

    • 合并格式(unified diff):diff -u file1 file2 这种方式可以得到二者的合并格式对比结果。

    root@arm64v8:~# echo -e "1\n2\n3\n4\n5\n6\n7" > file1
    root@arm64v8:~# echo -e "1\n2\n3\na\n5\n6\n7" > file2
    root@arm64v8:~# diff -u file1 file2
    --- file1   2021-07-12 02:53:45.395052106 -0400
    +++ file2   2021-07-12 02:53:51.222997923 -0400
    @@ -1,7 +1,7 @@
     1
     2
     3
    -4
    +a
     5
     6
     7
    root@arm64v8:~#
    • diff 结果输出部分 --- file1 表示变动前的文件,+++ file2 表示变动后的文件。
    • @@ -1,7 +1,7 @@ 表示变动的位置用两个@作为起首和结束。前面的 "-1,7" 分成三个部分:减号表示第一个文件(即file1),"1" 表示第 1 行,"7" 表示连续 7 行。合在一起,就表示下面是第一个文件从第 1 行开始的连续 7 行。同样的,"+1,7" 表示变动后,成为第二个文件从第 1 行开始的连续 7 行。
    • 接着 @@ -1,7 +1,7 @@ 下面的 8 行是变动的具体内容。除了有变动的那些行以外,也是上下文各显示 3 行。它将两个文件的上下文,合并显示在一起,所以叫做 "合并格式"。每一行最前面的标志位,空表示无变动,减号表示第一个文件删除的行,加号表示第二个文件新增的行。
  • 我们这里介绍的 diff 以当今大家通用最多的 "合并格式" 为主,将来使用 diff 甚至是 git diff,只要没有加以特殊说明,我们都认为是 "合并格式" 的 diff。

8.7.1.2、patch 命令

patch 的作用则是将 diff 记录的结果(即补丁)应用到相应文件(夹)上。最常见的用法为:

patch -pNUM <patchfile>

-p Num: 忽略几层文件夹,随后详解。
-E:     选项说明如果发现了空文件,那么就删除它
-R:     取消打过的补丁。

为了解释 -p 参数,需要看看如下 patch 文件片段:

--- old/modules/pcitable       Mon Sep 27 11:03:56 1999
+++ new/modules/pcitable       Tue Dec 19 20:05:41 2000
  • 如果使用参数 -p0,那就表示从当前目录找一个叫做 old 的文件夹,再在它下面寻找 modules/pcitable 文件来执行 patch 操作。
  • 而如果使用参数 -p1,那就表示忽略第一层目录(即不管old),从当前目录寻找 modules 的文件夹,再在它下面找 pcitable

8.7.1.3、应用

利用以上命令,处理单个文件补丁的方法:

# 产生补丁
diff -uN from-file to-file > to-file.patch

# 打补丁
patch -p0 < to-file.patch

# 取消补丁
patch -RE -p0 < to-file.patch

对整个文件夹打补丁的情况:

# 产生补丁
diff -uNr from-docu to-docu > to-docu.patch

# 打补丁
cd to-docu
patch -p1 < to-docu.patch

# 取消补丁
patch -R -p1 < to-docu.patch

另外,使用版本控制工具时,可以直接用 svn diffgit diff 生成补丁文件。

值得一提的是,由于应用补丁时的目标代码和生成补丁时的代码未必相同,打补丁操作可能失败。补丁失败的文件会以 .rej 结尾,下面命令可以找出所有 rej 文件:

find . -name '*.rej'

举一个小例子:

root@arm64v8:~# echo -e "111111\n111111\n111111" > file1
root@arm64v8:~# echo -e "222222\n111111\n222222\n111111" > file2
root@arm64v8:~# diff -uN file1 file2 > file.patch
root@arm64v8:~# 
root@arm64v8:~# cat file.patch 
--- file1   2021-07-12 03:43:07.219515984 -0400
+++ file2   2021-07-12 03:43:30.475299775 -0400
@@ -1,3 +1,4 @@
+222222
 111111
-111111
+222222
 111111
root@arm64v8:~#

8.7.2、patch 文件构成

补丁文件里到底存储了哪些信息呢?看看下面例子:

--- file1   2021-07-12 03:43:07.219515984 -0400
+++ file2   2021-07-12 03:43:30.475299775 -0400
@@ -1,3 +1,4 @@
+222222
 111111
-111111
+222222
 111111
  • 补丁头
    • 补丁头是分别由 ---/+++ 开头的两行,用来表示要打补丁的文件。--- 开头表示旧文件,+++ 开头表示新文件。
  • 一个补丁文件中的多个补丁
    • 一个补丁文件中可能包含以 ---/+++ 开头的很多节,每一节用来打一个补丁。所以在一个补丁文件中可以包含好多个补丁。
    • 块是补丁中要修改的地方。它通常由一部分不用修改的东西开始和结束。他们只是用来表示要修改的位置。他们通常以 @@ 开始,结束于另一个块的开始或者一个新的补丁头。'-' 后的数字代表该块在旧文件的行号,修改前该块总行数;'+' 后的数字代表该块在新文件的行号,修改后该块总行数。
  • 块的缩进
    • 块会缩进一列,而这一列是用来表示这一行是要增加还是要删除的。
  • 块的第一列
    • + 号表示这一行是要加上的。- 号表示这一行是要删除的。没有加号也没有减号表示这里只是引用的而不需要修改。

8.7.3、实例分析

8.7.3.1、单文件补丁

设当前目录有文件 file1:

root@arm64v8:~# echo -e "111111\n111111\n111111" > file1
root@arm64v8:~# cat file1 
111111
111111
111111
root@arm64v8:~#

创建文件 file2:

root@arm64v8:~# echo -e "222222\n111111\n222222\n111111" > file2
root@arm64v8:~# cat file2 
222222
111111
222222
111111
root@arm64v8:~#

使用 diff 创建补丁file.patch

root@arm64v8:~# diff -uN file1 file2 > file.patch
root@arm64v8:~# ls
file1  file2  file.patch
root@arm64v8:~#

因为是单个文件,故不需要 -r 选项。此命令得到如下补丁:

root@arm64v8:~# cat file.patch 
--- file1   2021-07-12 03:54:40.973066148 -0400
+++ file2   2021-07-12 03:55:37.724538529 -0400
@@ -1,3 +1,4 @@
+222222
 111111
-111111
+222222
 111111
root@arm64v8:~#

要应用补丁,只需:

root@arm64v8:~# patch -p0 < file.patch 
patching file file1
root@arm64v8:~# 
root@arm64v8:~# ls
file1  file2  file.patch
root@arm64v8:~# 
root@arm64v8:~# cat file1
222222
111111
222222
111111
root@arm64v8:~# 
root@arm64v8:~# cat file2 
222222
111111
222222
111111
root@arm64v8:~#
  • 应用补丁后,file1 和 file2 变成一样的了。

如果要取消补丁做出的更改,恢复旧版本:

root@arm64v8:~# patch -RE -p0 < file.patch 
patching file file1
root@arm64v8:~# 
root@arm64v8:~# cat file1
111111
111111
111111
root@arm64v8:~# 
root@arm64v8:~# cat file2 
222222
111111
222222
111111
root@arm64v8:~#

8.7.3.2、文件夹补丁

准备如下环境:

root@arm64v8:~# echo -e "--------\nproject1/name1\n--------" > project1/project1_name1
root@arm64v8:~# echo -e "--------\nproject2/name2\n--------" > project2/project2_name2
root@arm64v8:~# 
root@arm64v8:~# tree
.
├── project1
│   ├── file1
│   └── project1_name1
└── project2
    ├── file2
    └── project2_name2

2 directories, 4 files
root@arm64v8:~# 
root@arm64v8:~# 
root@arm64v8:~# cat project1/file1 
111111
111111
111111
root@arm64v8:~# cat project1/project1_name1 
--------
project1/name1
--------
root@arm64v8:~# cat project2/file2 
222222
111111
222222
111111
root@arm64v8:~# cat project2/project2_name2
--------
project2/name2
--------
root@arm64v8:~# 

使用 diff -uNr 创建补丁

root@arm64v8:~# diff -uNr project1 project2 > project.patch
root@arm64v8:~# cat project.patch 
diff -uNr project1/file1 project2/file1
--- project1/file1  2021-07-12 04:00:57.905561801 -0400
+++ project2/file1  1969-12-31 19:00:00.000000000 -0500
@@ -1,3 +0,0 @@
-111111
-111111
-111111
diff -uNr project1/file2 project2/file2
--- project1/file2  1969-12-31 19:00:00.000000000 -0500
+++ project2/file2  2021-07-12 03:55:37.724538529 -0400
@@ -0,0 +1,4 @@
+222222
+111111
+222222
+111111
diff -uNr project1/project1_name1 project2/project1_name1
--- project1/project1_name1 2021-07-12 04:10:38.048168208 -0400
+++ project2/project1_name1 1969-12-31 19:00:00.000000000 -0500
@@ -1,3 +0,0 @@
---------
-project1/name1
---------
diff -uNr project1/project2_name2 project2/project2_name2
--- project1/project2_name2 1969-12-31 19:00:00.000000000 -0500
+++ project2/project2_name2 2021-07-12 04:11:03.451932029 -0400
@@ -0,0 +1,3 @@
+--------
+project2/name2
+--------
root@arm64v8:~#

如果要应用此补丁:

root@arm64v8:~# ls
project1  project2  project.patch
root@arm64v8:~# cd project1/
root@arm64v8:~/project1# 
root@arm64v8:~/project1# patch -p1 < ../project.patch 
patching file file1
patching file file2
patching file project1_name1
patching file project2_name2
root@arm64v8:~/project1#

此时可以看到打补丁后的结果:

root@arm64v8:~# tree 
.
├── project1
│   ├── file2
│   └── project2_name2
├── project2
│   ├── file2
│   └── project2_name2
└── project.patch

2 directories, 5 files
root@arm64v8:~#

类似的,如果要回滚补丁操作:

root@arm64v8:~# cd project1/
root@arm64v8:~/project1# 
root@arm64v8:~/project1# patch -R -p1 < ../project.patch 
patching file file1
patching file file2
patching file project1_name1
patching file project2_name2
root@arm64v8:~/project1# 
root@arm64v8:~/project1# cd ..
root@arm64v8:~# 
root@arm64v8:~# tree 
.
├── project1
│   ├── file1
│   └── project1_name1
├── project2
│   ├── file2
│   └── project2_name2
└── project.patch

2 directories, 5 files
root@arm64v8:~#
标签云