概述
本章,您将学习有关二进制软件包的管理。
在 GNU/Linux 操作系统中,可通过这两种方式安装您需要的软件:
- 从存储库下载软件包并安装到本地计算机中
- 源码包编译后安装到本地
Q:什么是软件包?什么是源文件?什么是源码包?
软件包:指开发者预先将一组源文件编译成可执行的机器语言并打包成特定格式的二进制文件。若无特殊说明,GNU/Linux 中的软件包特指二进制软件包。用听得懂的话来说,二进制软件包就类似别人预先组好但不能调整结构的装有一堆工具的工具箱,来完成特定的任务或工作。
源文件:人类可读的单个代码文件(如 .c、.py、.java),它可能只是整个项目的一个代码片段或模块,需编译或解释才能运行在计算机上。
源码包:将整个项目的源文件与相关文件(比如 Makefile、configure 等构建文件;比如 README、LICENSE 等文档文件)打包好的一个经过压缩的归档文件,如 .tar.gz 、.tar.xz 等。
软件包与源码包的对比如下:
类型 | 优点 | 缺点 |
---|---|---|
二进制软件包 | 安装速度快;安装、卸载、升级等很方便;借助包管理器可以自动解决依赖问题;稳定性与兼容性经过测试 | 不能看见源代码;安装之后功能已经固定,无法按需定制 |
源码包 | 自由查看、修改源代码;自由添加或扩展功能;性能释放比二进制软件包要好;学习别人的代码并提高代码能力 | 安装复杂且费时;需要解决复杂的依赖问题;对新手不友好 |
rpm
本地包管理器
rpm
命令:红帽上下游发行版中用于管理本地 RPM 软件包的命令行工具。请注意下 rpm 这个专有术语,有时它指二进制软件包的格式后缀(如 xxxx-xxx-xxx.rpm),有时指 rpm
这个命令行工具。
在正式使用 rpm
命令前,我们需要了解下二进制软件包的命名规范,如下所示:
[Package_Name]-[Version]-[Release].[OS].[Arch].rpm
[Package_Name]-[Version]-[Release].[OS].[Arch].src.rpm
有时在包全名中见到一些其他的名称,如:
- devel – 表示这个 RPM 包是软件的开发包
- noarch:说明该 RPM 软件包可以在任何架构上安装
包全名(Full Package Name):二进制软件包的完整名称,如 tree-1.7.0-15.el8.x86_64.rpm
。
包名(Package Name):软件包的名称,如 tree
。
有些命令的操作对象是 包全名,有些命令的操作对象是 包名,这点需要注意。
Q:什么时候需要包全名?什么时候需要包名?
操作系统中从来没有安装过该软件包时,相关命令需要使用包全名。比如使用 rpm
命令进行安装或升级时需要包全名。
操作系统已经安装过该软件包时,相关命令需要使用包名,因为相关软件包的信息存放在 /var/lib/rpm/ 数据库目录当中,所以不需要使用包全名。比如使用 rpm
命令进行查询或卸载时需要包名。
rpm
命令的使用语法如下:
Shell > rpm [options] <Package-Name> | <Full-Package-Name>
安装、升级、卸载软件包
相关选项:
-i
– 安装-v
– 显示安装过程中的细节-h
– 显示进度条-U
– 升级-e
– 卸载
安装一个或多个软件包 – rpm -ivh <Full-Package-Name> ...
升级一个或多个软件包 – rpm -Uvh <Full-Package-Name> ...
卸载一个或多个软件包 – rpm -e <Package-Name> ...
在安装软件的过程中,由于 rpm
是本地的包管理器,因此我们需要手动解决依赖性问题 —— 即在安装或升级过程中会出现诸如 "failed dependencies" 这样的提示。
这里我们需要了解下 RPM 包的依赖:
- 树形依赖 –
a --> b --> c
。安装软件包 a 时提示需要先安装软件包 b,安装软件包 b 提示需要先安装软件包 c,互相依赖。这种依赖比较好解决,直接在一条命令中执行rpm -ivh a.rpm b.rpm c.rpm
就好了 - 环形依赖 –
a --> b --> c --> a
。直接在一条命令中执行rpm -ivh a.rpm b.rpm c.rpm
就好了 - 模块依赖 – 去 这个网站 查找即可
Q:为什么 RPM 软件包会出现依赖问题呢?
因为软件应用程序几乎总是依赖于另一个软件或库,如果操作系统上没有找到所需的程序或共享库,那么在安装目标应用程序之前必须满足该先决条件。其实 Windows 中的 .exe 二进制软件包在安装之前也会进行依赖项的环境检查,若不符合或未找到相关的依赖项,则在安装过程中会提示安装失败,在安装一些大型的应用程序时会比较常出现。
查询软件包
相关选项:
-q
– 查询相关的程序是否安装过,例如rpm -q openssh
-a
– 与-q
搭配使用,查询所有已安装的 rpm 包,a 即 all 的意思-i
– 与-q
搭配使用,查询对应已安装的 rpm 包的详细信息,i 即 infomation 的意思-l
– 与-q
搭配使用,查询对应已安装的 rpm 包的释放的文件列表,l 即 list 的意思。-p
– 与-ql
搭配使用,查询未安装的 rpm 包的释放的文件列表,需要注意,命令后面需要使用包全名,例如rpm -qlp tree-1.7.0-15.el8.x86_64.rpm
-f
– 与-q
搭配使用,查询操作系统中某一系统文件属于哪个 rpm 包,f 即 file 的意思。比如rpm -qf /usr/bin/ssh
。这里的系统文件指的是通过二进制软件包释放出来的文件。-R
– 与-q
搭配使用,查询对应 rpm 包的依赖性,可以针对已经已经安装过的 rpm 包,也可以针对未安装的 rpm 包(需要搭配-p
选项)。比如rpm -qR openssh-clients
以及rpm -qRp tree-1.7.0-15.el8.x86_64.rpm
校验软件包的签名
当您从未知的网站或不受信任的位置下载 rpm 二进制软件包时,您不知道该软件包是否被做了篡改,因此,我们需要对软件包做签名的验证,以保证我们下载的软件包是完整的且未被篡改的。
从 RHEL 8.x 开始,您可以使用 dnf download
命令下载具体的软件包。例如您需要下载 wget
软件包,请键入:
# 若需要指定下载的目录位置,请添加 --destdir DESTDIR 或 --downloaddir DESRDIR 选项
Shell > dnf download wget
Shell > ls
wget-1.19.5-11.el8.x86_64.rpm
要对软件包的签名进行校验,需要的前提条件 —— 导入了所需的公钥(这通常不需要系统管理员来操作,但您需要知道这件事)。
例如针对上面的 wget-1.19.5-11.el8.x86_64.rpm 做签名校验,请键入:
# 若需要更加详细的消息,请添加 -v 或 -vv 选项
## 当输出信息中有 "digests signatures OK" 时,证明软件包没有问题,未被篡改。
Shell > rpm -K wget-1.19.5-11.el8.x86_64.rpm
wget-1.19.5-11.el8.x86_64.rpm: digests signatures OK
# 我们可以恶意地故意篡改软件包 —— 这可以通过在原始软件包中添加任何内容或从中删除某些内容来实现。请注意!任何以原始软件包不希望的方式变更软件包的行为都会导致其损坏或不完整,例如:
Shell > echo "change content" >> /root/wget-1.19.5-11.el8.x86_64.rpm
# 当输出信息中出现 "digests signatures NOT OK" 时,您不应该继续使用该软件包,因为它不再受到信任。
Shell > rpm -K wget-1.19.5-11.el8.x86_64.rpm
wget-1.19.5-11.el8.x86_64.rpm: digests signatures NOT OK
校验软件包的变更
上面文字提到的是软件包的签名校验,这里说的是软件包的变更校验。相关选项:
-V
– 校验软件包
当 rpm 软件包安装过后,rpm 数据库会记录相关文件的初始特征以及变更后的特征,以了解软件包释放出的文件是否被人恶意修改。
Shell > rpm -V chrony
S.5....T. c /etc/chrony.conf
第一部分(S.5....T
):表示验证文件内容中的 8 个信息,若相关信息没有被修改,则会用 .
进行表示。
- S – 文件大小是否改变
- M – 文件的类型或文件的权限(rwx)是否被改变
- 5 – 文件 MD5 校验和是否改变(可以看成文件内容是否改变)。MD5 是互联网的一种加密方式,主要用来进行文件的完整性验证。
- D – 设备是否改变
- L – 文件路径是否改变
- U – 文件的所有者是否改变
- G – 文件的所属组是否改变
- T – 文件的修改时间(mtime)是否改变
第二部分:代表变更的文件类型
- c – 配置文件(config file)
- d – 普通文件(documentation)
- g – "鬼"文件(ghost file),非常少见,一般该文件不会出现在 rpm 包里
- I – 授权文件(license file)
- r – 描述文件(readme file)
第三部分:代表被修改的文件路径
卸载软件包
安全且正确的做法是键入 -e
选项,支持对一个或多个软件包的卸载:
Shell > rpm -e <Package-Name> ...
需要注意的是,-e
选项默认情况下会移除软件包并检查依赖,例如 a.rpm 依赖 b.rpm ,当您使用 rpm -e a
时会卸载失败,提示 error: Failed dependencies
。虽然您可以强制移除某一个软件包(比如 rpm -e --nodeps a
),但我们不建议您这样做。
有时您的操作系统进行了升级,需要移除旧版本的内核,可以这样操作:
Shell > grubby --info ALL
index=0
kernel="/boot/vmlinuz-4.18.0-553.45.1.el8_10.x86_64"
args="ro crashkernel=auto resume=UUID=76e2324e-ccdc-4b75-bc71-64cd0edb2ebc $tuned_params"
root="UUID=57d8aec0-891b-4f58-9125-c2d68e81c572"
initrd="/boot/initramfs-4.18.0-553.45.1.el8_10.x86_64.img $tuned_initrd"
title="Rocky Linux (4.18.0-553.45.1.el8_10.x86_64) 8.10 (Green Obsidian)"
id="638c6d5d2b674f77be56174469099106-4.18.0-553.45.1.el8_10.x86_64"
index=1
kernel="/boot/vmlinuz-4.18.0-553.40.1.el8_10.x86_64"
args="ro crashkernel=auto resume=UUID=76e2324e-ccdc-4b75-bc71-64cd0edb2ebc $tuned_params"
root="UUID=57d8aec0-891b-4f58-9125-c2d68e81c572"
initrd="/boot/initramfs-4.18.0-553.40.1.el8_10.x86_64.img $tuned_initrd"
title="Rocky Linux (4.18.0-553.40.1.el8_10.x86_64) 8.10 (Green Obsidian)"
id="638c6d5d2b674f77be56174469099106-4.18.0-553.40.1.el8_10.x86_64"
index=2
kernel="/boot/vmlinuz-0-rescue-638c6d5d2b674f77be56174469099106"
args="ro crashkernel=auto resume=UUID=76e2324e-ccdc-4b75-bc71-64cd0edb2ebc"
root="UUID=57d8aec0-891b-4f58-9125-c2d68e81c572"
initrd="/boot/initramfs-0-rescue-638c6d5d2b674f77be56174469099106.img"
title="Rocky Linux (0-rescue-638c6d5d2b674f77be56174469099106) 8.10 (Green Obsidian)"
id="638c6d5d2b674f77be56174469099106-0-rescue"
Shell > uname -r
4.18.0-553.45.1.el8_10.x86_64
# 如您所见,我目前在使用这个内核索引号为 0 的这个内核 —— 4.18.0-553.45.1
## 我需要将 4.18.0-553.40.1 内核移除
Shell > rpm -e $(rpm -qa | grep ^kernel | grep -v 553\.45)
Shell > sed -i '9s/true/false/g' /etc/default/grub
Shell > grub2-mkconfig -o /boot/grub2/grub.cfg
dnf 包管理器
dnf
包管理器是 yum
包管理器的替代,前者采用 Pythton3.x + C 语言进行开发(libsolv 库以及被称为 SAT 的算法),后者采用 Python 2.x 语言进行开发,因此 dnf
在处理依赖时有着性能高、处理速度快以及内存占用低等优点。在一些较新的发行版中(如 Fedora 41),dnf
还支持并行下载软件包。
dnf
命令
dnf
命令的语法如下:
dnf [options] <command> [<args>...]
这里的 command 即 dnf
的功能项命令,这些功能项命令有些是 dnf
自带的,有些是第三方插件提供的,常用的有:
-
list
命令依据该命令后面选项的不同列出软件包,默认列出操作系统所有可被安装的软件包(
dnf list
等同于dnf list --all
)。列出当前操作系统已经安装的软件包 ——
dnf list --installed
列出可以升级的软件包 ——
dnf list --updates
-
search
命令依据给定的关键字从存储库中搜索软件包,例如
dnf search vim
-
install
命令从存储库中安装一个或多个软件包,例如
dnf install -y tree wget
,-y
的意思是将所有的交互提示都回答为yes
。除了可以从存储库中安装软件包外,您可从指定的 URL 安装软件包,比如
dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
-
info
命令查看一个或多个软件包的信息,比如
dnf info wget tree
-
deplist
命令列出软件包的依赖关系,已被弃用,请使用
dnf repoquery --deplist <Package-Name>
作为替代。 -
repolist
命令显示存储库方面的信息,默认显示已经被启用的存储库(
dnf repolist
等同于dnf repolist --enabled
)。查看所有的存储库 ——
dnf repolist --all
-
history
命令显示键入的 dnf 历史命令,默认情况下
dnf history
等同于dnf history list
,这里的 list 还可以是info
、redo
、replay
,rollback
、store
,undo
、userinstalled
的其中一个。 -
provides
命令查看给定的系统文件属于哪一个软件包,比如
dnf provides /usr/bin/systemctl
-
remove
命令移除当前操作系统中的一个或多个软件包,默认情况下会询问是否连带的依赖包一起卸载,若要应答为 yes,请键入
-y
,比如dnf remove -y vim tree
-
autoremove
命令自动移除那些曾经被当作依赖项但现在不再使用的软件包。比如
dnf autoremove -y
-
makecache
命令为新添加的或元数据过时的存储库生成缓存。
-
update
或upgrade
命令将操作系统中的一个或多个软件包进行升级,比如
dnf update -y
将升级操作系统中所有可升级的软件包 -
grouplist
、groupinstall
、groupremove
、groupinfo
命令这些命令主要的操作对象是软件包组,软件包组指的是为特定场景或环境而准备的一组软件包。
在 RockyLinux 8.x 中有这些软件包组:
Shell > dnf grouplist Available Environment Groups: Server with GUI Server Workstation KDE Plasma Workspaces Virtualization Host Custom Operating System Installed Environment Groups: Minimal Install Available Groups: Container Management .NET Core Development RPM Development Tools Development Tools Graphical Administration Tools Headless Management Legacy UNIX Compatibility Network Servers Scientific Support Security Tools Smart Card Support System Tools Fedora Packager Xfce
-
clean
命令清除缓存数据,通常我们会使用
dnf clean all
来清除所有的数据缓存,然后键入dnf makecache
生成新的缓存。 -
download
命令将存储库中的软件包下载到本地计算机而不安装它们。您可以使用
--destdir DESTDIR
或--downloaddir DESRDIR
选项来指定保存的路径,比如dnf download tree --downloaddir /tmp/
-
repoquery
命令与
rpm -q
命令类似,给定字符串并从存储库中搜索指定的包,然后列出相关的信息。比如查看依赖dnf repoquery --deplist <Package-Name>
,查看软件包(无论这个软件包是否已经安装到操作系统)释放出的文件路径dnf repoquery --list <Package-Name>
-
config-manager
命令以命令行方式管理存储库,包括对存储库的添加与删除,对某一个存储库的启用与关闭等。
例如需要添加一个新的存储库:
Shell > dnf config-manager --add-repo URL
永久关闭某一个已经被启用的存储库:
Shell > dnf repolist repo id repo name appstream Rocky Linux 8 - AppStream baseos Rocky Linux 8 - BaseOS docker-ce-stable Docker CE Stable - x86_64 epel Extra Packages for Enterprise Linux 8 - x86_64 extras Rocky Linux 8 - Extras powertools Rocky Linux 8 - PowerTools Shell > dnf config-manager --disable powertools
永久开启某一个已经被关闭的存储库:
Shell > dnf config-manager --enable powertools
存储库配置文件说明
所有存储库的配置文件都存放在 /etc/yum.repos.d/ 目录中并以 .repo 结尾,每个 .repo 文件可以包含一个或一组存储库,使用者可以根据情况选择性地启用或关闭:
Shell > ls -l /etc/yum.repos.d/
total 72
-rw-r--r-- 1 root root 1919 Sep 13 2024 docker-ce.repo
-rw-r--r-- 1 root root 1680 Aug 31 2024 epel-modular.repo
-rw-r--r-- 1 root root 1332 Aug 31 2024 epel.repo
-rw-r--r-- 1 root root 1779 Aug 31 2024 epel-testing-modular.repo
-rw-r--r-- 1 root root 1431 Aug 31 2024 epel-testing.repo
-rw-r--r--. 1 root root 710 Jun 7 2024 Rocky-AppStream.repo
-rw-r--r--. 1 root root 695 Jun 7 2024 Rocky-BaseOS.repo
-rw-r--r-- 1 root root 1773 Jun 7 2024 Rocky-Debuginfo.repo
-rw-r--r--. 1 root root 360 Jul 11 2024 Rocky-Devel.repo
-rw-r--r--. 1 root root 695 Jun 7 2024 Rocky-Extras.repo
-rw-r--r--. 1 root root 731 Jun 7 2024 Rocky-HighAvailability.repo
-rw-r--r--. 1 root root 680 Jun 7 2024 Rocky-Media.repo
-rw-r--r--. 1 root root 680 Jun 7 2024 Rocky-NFV.repo
-rw-r--r--. 1 root root 690 Jun 7 2024 Rocky-Plus.repo
-rw-r--r--. 1 root root 715 Mar 29 17:39 Rocky-PowerTools.repo
-rw-r--r--. 1 root root 746 Jun 7 2024 Rocky-ResilientStorage.repo
-rw-r--r--. 1 root root 681 Jun 7 2024 Rocky-RT.repo
-rw-r--r-- 1 root root 2335 Jun 7 2024 Rocky-Sources.repo
Shell > cat /etc/yum.repos.d/docker-ce.repo
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/stable
enabled=1
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
[docker-ce-stable-debuginfo]
name=Docker CE Stable - Debuginfo $basearch
baseurl=https://download.docker.com/linux/centos/$releasever/debug-$basearch/stable
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
...
Shell > cat /etc/yum.repos.d/Rocky-AppStream.repo
[appstream]
name=Rocky Linux $releasever - AppStream
mirrorlist=https://mirrors.rockylinux.org/mirrorlist?arch=$basearch&repo=AppStream-$releasever
#baseurl=http://dl.rockylinux.org/$contentdir/$releasever/AppStream/$basearch/os/
gpgcheck=1
enabled=1
countme=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
文件内容说明如下:
- 使用 "[ ]" 包含存储库的标识名 ID
- "name" 表示存储库的名称
- "baseurl" 表示存储库的路径,支持多种协议,如 https、http、ftp、file 、NFS等,URL 中的 "$" 表示相关的存储库变量
- "mirrorlist" 表示镜像存储库列表
- "#" 开头表示是注释行
- "gpgcheck" 表示是否对存储库中的软件包开启 gpg 签名检查,以防止软件包被人恶意篡改。非对称加密的内容我们介绍 Git 的时候说到了
- "enabled" 表示是否启用该存储库
- "gpgkey" 表示密钥(key)的位置,这里的密钥即公钥
其他未说明的内容参阅 man 5 yum.conf
搭建本地的存储库
文件存放的介质可以是 DVD 光盘,也可以是 .iso 光盘镜像文件。若选择的是 .iso 镜像文件,我们建议您最好选择包含所有存储库软件包的那个最大文件。
file
协议
演示环境下将当前活跃的存储库全部永久关闭:
Shell > dnf repolist
repo id repo name
appstream Rocky Linux 8 - AppStream
baseos Rocky Linux 8 - BaseOS
docker-ce-stable Docker CE Stable - x86_64
epel Extra Packages for Enterprise Linux 8 - x86_64
extras Rocky Linux 8 - Extras
powertools Rocky Linux 8 - PowerTools
Shell > dnf config-manager --disable appstream baseos docker-ce-stable epel extras powertools
# 假设您往当前的服务器上传了 .iso 文件
# 创建镜像的保存位置
Shell > mkdir /usr/local/src/DVD-iso
Shell > mv Rocky-8.10-x86_64-dvd1.iso /usr/local/src/DVD-iso/
# 非永久性地临时挂载
Shell > mount /usr/local/src/DVD-iso/Rocky-8.10-x86_64-dvd1.iso /mnt/
Shell > mount | grep -i iso
/usr/local/src/DVD-iso/Rocky-8.10-x86_64-dvd1.iso on /mnt type iso9660 (ro,relatime,nojoliet,check=s,map=n,blocksize=2048)
Shell > df -hT
Filesystem Type Size Used Avail Use% Mounted on
...
/dev/loop0 iso9660 14G 14G 0 100% /mnt
# 创建 repo 文件
Shell > cd /etc/yum.repos.d/ && touch my-local.repo
# 文件内容如下:
## "file://" 代表协议,"/mnt/AppStream/" 表示对应存储库的目录
## 请注意存储库 ID 必须是唯一的
Shell > cat /etc/yum.repos.d/my-local.repo
[local-appstream]
name = Rocky Linux $releasever Appstream
baseurl = file:///mnt/AppStream/
enabled=1
gpgcheck=0
# 生成缓存,完成
## 按照这种方法再依次添加 baseos、powertools 等存储库
Shell > dnf makecache
Shell > dnf repolist
repo id repo name
local-appstream Rocky Linux 8 Appstream
恢复到之前的存储库:
Shell > rm -rf /etc/yum.repos.d/my-local.repo
Shell > dnf config-manager --enable appstream baseos docker-ce-stable epel extras powertools
Shell > umount /mnt
ftp 协议
FTP 协议的配置方法也差不多,需要首先在局域网内创建一个可供所有人读取和下载的公共 FTP 服务器(注意 FTP 服务的权限以及相关目录的权限),然后配置相关的 baseurl 即可。见如下操作:
# 我使用的是 VSFTP
Shell > dnf -y install vsftpd
Shell > dnf config-manager --disable appstream baseos docker-ce-stable epel extras powertools
# 可供所有人读取和下载的 FTP 服务器,即 FTP 服务器允许匿名用户访问与下载,但不允许匿名用户创建目录和上传文件
## 查看 VSFTP 的默认配置
Shell > grep -v -E "^$|^#" /etc/vsftpd/vsftpd.conf
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
dirmessage_enable=YES
xferlog_enable=YES
connect_from_port_20=YES
xferlog_std_format=YES
listen=NO
listen_ipv6=YES
pam_service_name=vsftpd
userlist_enable=YES
# 关闭本地用户;开启匿名用户
Shell > grep -v -E "^$|^#" /etc/vsftpd/vsftpd.conf
anonymous_enable=YES
...
local_enable=NO
...
## 进行挂载
Shell > mount /usr/local/src/DVD-iso/Rocky-8.10-x86_64-dvd1.iso /mnt/
# 匿名用户的 FTP 根目录为 /var/ftp/ ,默认在根下会有一个 pub 目录
## 将我们 iso 镜像文件里面的文件全部递归复制到 pub 目录
## 文件挺大,复制需要花费挺长时间
Shell > cp -r /mnt/* /var/ftp/pub/
Shell > du -sh /var/ftp/pub/
14G /var/ftp/pub/
# 启用 VSFTP 服务
Shell > systemctl start vsftpd.service
Shell > cd /etc/yum.repos.d/ && touch ftp.repo
# 写入的文件如下:
Shell > cat /etc/yum.repos.d/ftp.repo
[ftp-local-appstream]
name = Rocky Linux $releasever Appstream
baseurl = ftp://192.168.100.20/pub/AppStream/
enabled=1
gpgcheck=0
# 配置成功
## 若还有其他机器需要指向相应的 ftp 存储库,完成相应配置即可
Shell > dnf clean all && dnf makecache
Shell > dnf repolist
repo id repo name
ftp-local-appstream Rocky Linux 8 Appstream
恢复到之前的存储库:
Shell > umount /mnt
Shell > rm -rf /etc/yum.repos.d/ftp.repo
Shell > systemctl stop vsftpd.service
Shell > rm -rf /var/ftp/pub/*
Shell > dnf config-manager --enable appstream baseos docker-ce-stable epel extras powertools
