commit 3c39ce6d18dd80c868024c3820886f583f04bcf5 Author: AaronXu <718827633@qq.com> Date: Thu Nov 13 14:27:03 2025 +0800 init diff --git a/01.Linux基础/01.初识Linux.md b/01.Linux基础/01.初识Linux.md new file mode 100644 index 0000000..c1402a5 --- /dev/null +++ b/01.Linux基础/01.初识Linux.md @@ -0,0 +1,80 @@ +# 1. 操作系统简介 + +世上本没有路,走的人多了,便变成了路。——鲁迅 + +这句话真的是鲁迅说的,出自鲁迅的《故乡》,我们可以理解为一个东西用的人多了,自然而然就会把这个东西的使用方式打磨完善了,人类自动获得了智慧开始就掌握了工具的使用方法。众所周知做不同的事情就需要有不同的方法,走不同的路,也就有了不同的工具。 + +一台机器往往有不同的零件组成,一台电脑也是由很多不同的硬件组成,这么多的硬件设备,就需要有一个系统去代替我们管理好,这个就是操作系统。 + +操作系统需要完成的任务很简单,就是去学习每个硬件的不同使用方法,专业的术语叫“安装驱动程序”,当操作系统学会了每个硬件的使用方法之后,就会以某种人类可以操作的方式来被人类使用,有的操作系统是图形化界面的,我们可以鼠标点击来操作,有的是命令行的,我们需要输入命令来操作,不管是哪种方式,最终都是让计算机硬件来为我们而工作。 + +![img-操作系统简介](01.初识Linux/操作系统简介.png) + +# 2. Linux操作系统 + +不同的人对计算机有不同的操作习惯,不同的使用场景对操作系统有不同的挑战,所以当前就出现了多种多样的操作系统 + +- Windows + - 收费闭源操作系统,主要用于日常办公、游戏、娱乐多一些 +- Linux + - 免费的,开源的,主要用于服务器领域,性能稳定,安全 +- Unix + - 用于封闭的硬件环境,企业的应用依赖于定制化开发 + +# 3. Linux图谱 + +Linux发展到今天出现了大量不同的分支。不同的分支都有不同的发展方向,可以扫描下面二维码,查看Linux发展分支图,或者点开[此链接](https://user-images.githubusercontent.com/19638095/97118180-e25f3680-1708-11eb-8bef-1ebe025e1374.png)。 + +![img-Linux发展分支图](01.初识Linux/Linux发展分支图.png) + +# 4. Linux名词 + +- Linux系统内核指的是一个由Linus Torvalds负责维护,提供硬件抽象层、硬盘及文件系统控制及多任务功能的系统核心程序。 +- Linux发行套件系统是我们常说的Linux操作系统,也即是由Linux内核与各种常用软件的集合产品 + +# 5. RedHat认证 + +redhat是目前全球最大的Linux商业公司,redhat为Linux技术工程师提供了一个考试认证,通过这个考试认证的工程师,可以获得redhat公司颁发的证书。 + +在行业内,redhat的技术证书还是占有很高的含金量的,证书的认证难度依次如下。 + +![img-RedHat认证](01.初识Linux/RedHat认证.png) + +## 5.1 RHCSA + +- 管理文件、目录、文档以及命令行环境; +- 使用分区、LVM逻辑卷管理本地存储; +- 安装、更新、维护、配置系统与核心服务; +- 熟练创建、修改、删除用户与用户组,并使用LDAP进行集中目录身份认证; +- 熟练配置防火墙以及SELinux来保障系统安全。 + +## 5.2 RHCE + +- 熟练配置防火墙规则链与SElinux安全上下文; +- 配置iSCSI(互联网小型计算机系统接口)服务; +- 编写Shell脚本来批量创建用户、自动完成系统的维护任务; +- 配置HTTP/HTTPS网络服务; +- 配置FTP服务; +- 配置NFS服务; +- 配置SMB服务; +- 配置SMTP服务; +- 配置SSH服务; +- 配置NTP服务。 +- 自动化运维工具ansible + +## 5.3 RHCA + +欲取得红帽RHCA认证,您必须通过以下任意5门认证考试。 + +| 考试代码 | 认证名称 | +| :------- | :--------------------------------- | +| EX210 | 红帽OpenStack 认证系统管理员考试 | +| EX220 | 红帽混合云管理专业技能证书考试 | +| EX236 | 红帽混合云存储专业技能证书考试 | +| EX248 | 红帽认证 JBoss管理员考试 | +| EX280 | 红帽平台即服务专业技能证书考试 | +| EX318 | 红帽认证虚拟化管理员考试 | +| EX401 | 红帽部署和系统管理专业技能证书考试 | +| EX413 | 红帽服务器固化专业技能证书考试 | +| EX436 | 红帽集群和存储管理专业技能证书考试 | +| EX442 | 红帽性能调优专业技能证书考试 | \ No newline at end of file diff --git a/01.Linux基础/01.初识Linux/Linux发展分支图.png b/01.Linux基础/01.初识Linux/Linux发展分支图.png new file mode 100644 index 0000000..7684eb6 Binary files /dev/null and b/01.Linux基础/01.初识Linux/Linux发展分支图.png differ diff --git a/01.Linux基础/01.初识Linux/RedHat认证.png b/01.Linux基础/01.初识Linux/RedHat认证.png new file mode 100644 index 0000000..eb274af Binary files /dev/null and b/01.Linux基础/01.初识Linux/RedHat认证.png differ diff --git a/01.Linux基础/01.初识Linux/操作系统简介.png b/01.Linux基础/01.初识Linux/操作系统简介.png new file mode 100644 index 0000000..70cc516 Binary files /dev/null and b/01.Linux基础/01.初识Linux/操作系统简介.png differ diff --git a/01.Linux基础/02.Linux系统安装.md b/01.Linux基础/02.Linux系统安装.md new file mode 100644 index 0000000..231252d --- /dev/null +++ b/01.Linux基础/02.Linux系统安装.md @@ -0,0 +1,189 @@ +RockyLinux系统在企业中一般是安装在服务器上的,但是我们学习的时候买台服务器并不方便,所以我们可以将其安装在虚拟机中(实际上很多企业也这么做) + +# 1. VMware安装 + +- 下面是安装过程,从双击安装包开始 + +![image-20251111144109577](02.Linux系统安装/image-20251111144109577.png) + +![image-20251111144129467](02.Linux系统安装/image-20251111144129467.png) + +![image-20251](02.Linux系统安装/image-20251111144142613.png) + +![image-20251111144200630](02.Linux系统安装/image-20251111144200630.png) + +![image-20251111144251466](02.Linux系统安装/image-20251111144251466.png) + +![image-20251111144307960](02.Linux系统安装/image-20251111144307960.png) + +- 此处的序列号是:`JU090-6039P-08409-8J0QH-2YR7F` + +![image-20251111144320341](02.Linux系统安装/image-20251111144320341.png) + +![image-20251111144346889](02.Linux系统安装/image-20251111144346889.png) + +# 2. 创建Linux虚拟机 + +- 创建步骤如下图 + +![image-20251111145856613](02.Linux系统安装/image-20251111145856613.png) + +![image-20251111145907058](02.Linux系统安装/image-20251111145907058.png) + +![image-20251111145922740](02.Linux系统安装/image-20251111145922740.png) + +![image-20251111145936220](02.Linux系统安装/image-20251111145936220.png) + +![image-20251111150028970](02.Linux系统安装/image-20251111150028970.png) + +![image-20251111150048071](02.Linux系统安装/image-20251111150048071.png) + +![image-20251111150110299](02.Linux系统安装/image-20251111150110299.png) + +![image-20251111150220296](02.Linux系统安装/image-20251111150220296.png) + +# 3. 安装Linux系统 + +- 点击开启此虚拟机 + +![image-20251111150314626](02.Linux系统安装/image-20251111150314626.png) + +- 鼠标点击进入这个黑框中,通过方向键选择安装选项,回车确认,我们选择第一个:Install Rocky Linux 9.4 + +![image-20251111150342548](02.Linux系统安装/image-20251111150342548.png) + +- 依次按照图片所示,完成以下设置 + +![image-20251111150604317](02.Linux系统安装/image-20251111150604317.png) + +- 设置安装目标位置 + +![image-20251111150634635](02.Linux系统安装/image-20251111150634635.png) + +![image-20251111150647727](02.Linux系统安装/image-20251111150647727.png) + +- 设置root密码 + +![image-20251111150702989](02.Linux系统安装/image-20251111150702989.png) + +- 下图因为密码过于简单,需要按两次完成,才可以设置成功! + +![image-20251111150744375](02.Linux系统安装/image-20251111150744375.png) + +- 点击开始安装 + +![image-20251111150838072](02.Linux系统安装/image-20251111150838072.png) + +- 点击重启系统 + +![image-20251111153004089](02.Linux系统安装/image-20251111153004089.png) + +# 4. 系统使用 + +重启之后就进入 RockyLinux 系统的界面,没有图形化,只有黑框框。然后我们先输入用户名`root`回车以后,在输入我们设置的密码`1`。但是输入密码的时候,是没有任何显示的,这是命令行为了保护密码的安全故意为之,我们我们输入了密码以后这直接回车即可进入系统。 + +![img-RL系统使用1](02.Linux系统安装/RL系统使用1.png) + +如果输入密码回车之后,显示`[root@localhost ~]#`那么说明我们成功进入系统 + +最好检查一下,网络有没有问题,我们可以使用命令`ping qq.com -c 4`来测试与腾讯服务器的连通性,以此来判断网络是否正常连接。 + +![img-RL系统使用2](02.Linux系统安装/RL系统使用2.png) + +如果输出的结果如图所示,说明我们系统的网络连接正常。 + +# 5. 快照 + +快照可以理解为保存的一个当前状态的还原点,可以完整保存当前的环境及状态。当我们以后再实验中,不小心把环境弄坏了以后,我们可以通过拍摄的快照,时光回溯到现在这个状态,避免我们重新安装系统的麻烦。 + +## 5.1 创建快照 + +在创建快照的时候,一定要关机了以后再拍摄快照,不要在开机的状态下创建快照。 + +关机命令: + +```shell +poweroff +``` + +创建快照: + +在上方点击虚拟机,找到快照,拍摄快照,然后起个名字,最后点击拍摄快照。我们的快照就创建好了 + +![img-虚拟机快照1](02.Linux系统安装/虚拟机快照1.png) + +![img-虚拟机快照2](02.Linux系统安装/虚拟机快照2.png) + +## 5.2 还原快照 + +- 如果我们想要回复到某一个快照的时候,我们还是在上方虚拟机,快照,然后选择恢复到快照即可。 + +![img-虚拟机快照3](02.Linux系统安装/虚拟机快照3.png) + +# 6. 远程管理 + +大多数情况下 Linux 服务器都是安装在数据中心机房的机柜里面的,大家可以把数据中心理解为大仓库,那么机柜就是一个个货架,而管理人员都是坐在办公室当中,通过电脑远程连接服务器进行管理,所以我们在后续学习的时候,为了方便,全部使用远程连接。 + +## 6.1 安装xshell+xftp + +远程管理工具下载地址:https://www.xshell.com/zh/free-for-home-school/ + +下图两个软件都需要下载安装。 + +![image-20251111153425344](02.Linux系统安装/image-20251111153425344.png) + +- 安装步骤如下图 + +![image-20251111153521001](02.Linux系统安装/image-20251111153521001.png) + +![image-20251111153533240](02.Linux系统安装/image-20251111153533240.png) + +![image-20251111153545071](02.Linux系统安装/image-20251111153545071.png) + +![image-20251111153603651](02.Linux系统安装/image-20251111153603651.png) + +![image-20251111153633632](02.Linux系统安装/image-20251111153633632.png) + +- 填写昵称和邮箱,然后Xshell装好后,一定一定要把xshell关掉,再安装xftp软件!!!! +- xftp的安装步骤类似 + +## 6.2 查看Linux的IP + +- 首先查看虚拟机的IP地址,也就是 Linux 服务器的 IP 地址,登录进虚拟机之后输入`ip address`可以查看 + +![image-20251111153828418](02.Linux系统安装/image-20251111153828418.png) + +这边可以看到本次安装的 RockyLinux 操作系统获取到的IP地址是 `192.168.173.148` 每个人装的虚拟机获取的 IP 地址可能是不一样的,需要仔细看清楚。 + +## 6.3 xshell远程连接 + +xshell工具只是用来远程连接和管理我们的 RockyLinux 虚拟机,所以在使用的时候,我们必须先启动 RockyLinux 虚拟机。然后再次使用 xshell 工具连接上来。并且我们在 xshell 上面执行的命令,也都会被传递到我们的 RockyLinux 上面执行。 + +- 先在xshell上添加主机 + +![image-20251111154152950](02.Linux系统安装/image-20251111154152950.png) + +- 按照下图填写 + +![image-20251111154248793](02.Linux系统安装/image-20251111154248793.png) + +- 将密码填上去 + +![image-20251111154323546](02.Linux系统安装/image-20251111154323546.png) + +- 点击确定,然后在打开文件夹处可以看到我们刚刚添加的 + +![image-20251111154402833](02.Linux系统安装/image-20251111154402833.png) + +![image-20251111154420541](02.Linux系统安装/image-20251111154420541.png) + +- 双击这个会话,就可以连接,第一次连接的时候,点击接受并保存 + +![image-20251111154447472](02.Linux系统安装/image-20251111154447472.png) + +- 之后在这里敲的命令都会被发送到Linux系统去执行 + +![image-20251111154518692](02.Linux系统安装/image-20251111154518692.png) + + + diff --git a/01.Linux基础/02.Linux系统安装/RL系统使用1.png b/01.Linux基础/02.Linux系统安装/RL系统使用1.png new file mode 100644 index 0000000..cc17d0f Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/RL系统使用1.png differ diff --git a/01.Linux基础/02.Linux系统安装/RL系统使用2.png b/01.Linux基础/02.Linux系统安装/RL系统使用2.png new file mode 100644 index 0000000..8a3df8b Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/RL系统使用2.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111144046235.png b/01.Linux基础/02.Linux系统安装/image-20251111144046235.png new file mode 100644 index 0000000..7aa5e93 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111144046235.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111144109577.png b/01.Linux基础/02.Linux系统安装/image-20251111144109577.png new file mode 100644 index 0000000..757fb48 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111144109577.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111144129467.png b/01.Linux基础/02.Linux系统安装/image-20251111144129467.png new file mode 100644 index 0000000..82fdfda Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111144129467.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111144142613.png b/01.Linux基础/02.Linux系统安装/image-20251111144142613.png new file mode 100644 index 0000000..9669548 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111144142613.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111144200630.png b/01.Linux基础/02.Linux系统安装/image-20251111144200630.png new file mode 100644 index 0000000..e2e44d1 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111144200630.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111144251466.png b/01.Linux基础/02.Linux系统安装/image-20251111144251466.png new file mode 100644 index 0000000..96c92a3 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111144251466.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111144307960.png b/01.Linux基础/02.Linux系统安装/image-20251111144307960.png new file mode 100644 index 0000000..b5a58fa Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111144307960.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111144320341.png b/01.Linux基础/02.Linux系统安装/image-20251111144320341.png new file mode 100644 index 0000000..64b113a Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111144320341.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111144346889.png b/01.Linux基础/02.Linux系统安装/image-20251111144346889.png new file mode 100644 index 0000000..41199c8 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111144346889.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111145856613.png b/01.Linux基础/02.Linux系统安装/image-20251111145856613.png new file mode 100644 index 0000000..2c051c4 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111145856613.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111145907058.png b/01.Linux基础/02.Linux系统安装/image-20251111145907058.png new file mode 100644 index 0000000..0119ec5 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111145907058.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111145922740.png b/01.Linux基础/02.Linux系统安装/image-20251111145922740.png new file mode 100644 index 0000000..e960737 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111145922740.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111145936220.png b/01.Linux基础/02.Linux系统安装/image-20251111145936220.png new file mode 100644 index 0000000..41d1e8d Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111145936220.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150028970.png b/01.Linux基础/02.Linux系统安装/image-20251111150028970.png new file mode 100644 index 0000000..657d788 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150028970.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150048071.png b/01.Linux基础/02.Linux系统安装/image-20251111150048071.png new file mode 100644 index 0000000..a99ae5d Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150048071.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150110299.png b/01.Linux基础/02.Linux系统安装/image-20251111150110299.png new file mode 100644 index 0000000..2dab84c Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150110299.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150220296.png b/01.Linux基础/02.Linux系统安装/image-20251111150220296.png new file mode 100644 index 0000000..f6d993c Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150220296.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150314626.png b/01.Linux基础/02.Linux系统安装/image-20251111150314626.png new file mode 100644 index 0000000..e4914ba Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150314626.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150342548.png b/01.Linux基础/02.Linux系统安装/image-20251111150342548.png new file mode 100644 index 0000000..55d4f3a Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150342548.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150604317.png b/01.Linux基础/02.Linux系统安装/image-20251111150604317.png new file mode 100644 index 0000000..83198ee Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150604317.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150634635.png b/01.Linux基础/02.Linux系统安装/image-20251111150634635.png new file mode 100644 index 0000000..549716e Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150634635.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150647727.png b/01.Linux基础/02.Linux系统安装/image-20251111150647727.png new file mode 100644 index 0000000..92e78ba Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150647727.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150702989.png b/01.Linux基础/02.Linux系统安装/image-20251111150702989.png new file mode 100644 index 0000000..0d04453 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150702989.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150744375.png b/01.Linux基础/02.Linux系统安装/image-20251111150744375.png new file mode 100644 index 0000000..f6c7b10 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150744375.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111150838072.png b/01.Linux基础/02.Linux系统安装/image-20251111150838072.png new file mode 100644 index 0000000..95576c4 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111150838072.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111153004089.png b/01.Linux基础/02.Linux系统安装/image-20251111153004089.png new file mode 100644 index 0000000..4c7123c Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111153004089.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111153425344.png b/01.Linux基础/02.Linux系统安装/image-20251111153425344.png new file mode 100644 index 0000000..e51533e Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111153425344.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111153521001.png b/01.Linux基础/02.Linux系统安装/image-20251111153521001.png new file mode 100644 index 0000000..3a4c90d Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111153521001.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111153533240.png b/01.Linux基础/02.Linux系统安装/image-20251111153533240.png new file mode 100644 index 0000000..1c0acab Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111153533240.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111153545071.png b/01.Linux基础/02.Linux系统安装/image-20251111153545071.png new file mode 100644 index 0000000..d81d6c9 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111153545071.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111153603651.png b/01.Linux基础/02.Linux系统安装/image-20251111153603651.png new file mode 100644 index 0000000..73d7a7b Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111153603651.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111153633632.png b/01.Linux基础/02.Linux系统安装/image-20251111153633632.png new file mode 100644 index 0000000..265cf25 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111153633632.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111153828418.png b/01.Linux基础/02.Linux系统安装/image-20251111153828418.png new file mode 100644 index 0000000..73d1c65 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111153828418.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111154152950.png b/01.Linux基础/02.Linux系统安装/image-20251111154152950.png new file mode 100644 index 0000000..ae2f4ad Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111154152950.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111154248793.png b/01.Linux基础/02.Linux系统安装/image-20251111154248793.png new file mode 100644 index 0000000..7bbd3ef Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111154248793.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111154323546.png b/01.Linux基础/02.Linux系统安装/image-20251111154323546.png new file mode 100644 index 0000000..d8977b7 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111154323546.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111154402833.png b/01.Linux基础/02.Linux系统安装/image-20251111154402833.png new file mode 100644 index 0000000..343e17c Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111154402833.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111154420541.png b/01.Linux基础/02.Linux系统安装/image-20251111154420541.png new file mode 100644 index 0000000..388466a Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111154420541.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111154447472.png b/01.Linux基础/02.Linux系统安装/image-20251111154447472.png new file mode 100644 index 0000000..57b2ddb Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111154447472.png differ diff --git a/01.Linux基础/02.Linux系统安装/image-20251111154518692.png b/01.Linux基础/02.Linux系统安装/image-20251111154518692.png new file mode 100644 index 0000000..aa7942d Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/image-20251111154518692.png differ diff --git a/01.Linux基础/02.Linux系统安装/虚拟机快照1.png b/01.Linux基础/02.Linux系统安装/虚拟机快照1.png new file mode 100644 index 0000000..96c7892 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/虚拟机快照1.png differ diff --git a/01.Linux基础/02.Linux系统安装/虚拟机快照2.png b/01.Linux基础/02.Linux系统安装/虚拟机快照2.png new file mode 100644 index 0000000..e497c1d Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/虚拟机快照2.png differ diff --git a/01.Linux基础/02.Linux系统安装/虚拟机快照3.png b/01.Linux基础/02.Linux系统安装/虚拟机快照3.png new file mode 100644 index 0000000..22e62a4 Binary files /dev/null and b/01.Linux基础/02.Linux系统安装/虚拟机快照3.png differ diff --git a/01.Linux基础/03.Linux命令行.md b/01.Linux基础/03.Linux命令行.md new file mode 100644 index 0000000..8d21104 --- /dev/null +++ b/01.Linux基础/03.Linux命令行.md @@ -0,0 +1,428 @@ +# 1. 初识 Shell + +虽然我们已经安装好了系统,但是光会安装不会操作是不够的。我们还要像玩手机一样熟悉并记忆操作方法。 + +Shell 是系统的**用户界面**,提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令并把它送入内核去执行。实际上 Shell 是一个**命令解释器**,它解释用户输入的命令并且把用户的意图传达给内核。(可以理解为用户与内核之间的翻译官角色) + +![img-命令解释器](03.Linux命令行/命令解释器.png) + +我们可以使用 Shell 实现对Linux系统的大部分管理,例如: + +1. 文件管理 +2. 用户管理 +3. 权限管理 +4. 磁盘管理 +5. 软件管理 +6. 网络管理 + ... + +使用 Shell 的两种方式 + +- 交互式命令行 + - 默认等待用户输入命令,输入一行回车后执行一行命令 + - 适合少量的工作 +- Shell 脚本 + - 将需要执行的命令和逻辑判断语句都写入一个文件后执行该文件 + - 适合完成复杂,重复性工作 + +# 2. Bash shell 提示符 + +登录 Linux 系统之后,默认进入交互式的命令行界面,在光标前边会出现提示符 + +```shell +[root@localhost ~]#  +[用户名@主机名 目录名]权限标识 + +``` + +**解释说明:** + +- 用户名:当前登录的用户 +- 主机名:当前这台主机的名字,默认叫`localhost` +- 目录名:当前光标所在的目录;当前用户家目录表示成`~` +- 权限标识:超级管理员权限就表示为`#`;普通用户标识为`$` + +**修改提示符:** + +```shell +# 这个提示符格式被`$PS1`控制,我们可以查看这个变量 +# \u表示是用户名 \h表示的是主机名 \W表示的当前所在目录 \$是权限标识 +[root@localhost ~]# echo $PS1 +[\u@\h \W]\$ + +# 可以通过export命令修改PS1变量,让提示符可以根据你的习惯变化 +[root@localhost ~]# export PS1="{\u@\h}\W \$" +{root@localhost}~ $ + +# 修改回原来的样子 +{root@localhost}~ $ $export PS1="[\u@\h \W]\$ " + +``` + +# 3. 常用命令 + +**Linux 常见命令比较多,这边只列出初学者最常用的部分命令,大家可以根据命令有意去进行练习。** + +注意 Linux 会准确的识别出命令的大小写,所以大家需要注意大小写的问题。命令选项和参数之间是用空格进行分隔,请大家在输入的时候注意不要缺失空格。 + +学习 Linux 最重要的就是以下三个方面 + +1. 命令的积累 +2. 原理的掌握 +3. 大量的实战 + +下面就是开始第一步,积累基础的命令 + +## 3.1 ls + +用于显示指定工作目录下之内容(列出目前工作目录所含之文件及子目录) + +```shell +Usage: ls [OPTION]... [FILE]... +List information about the FILEs (the current directory by default). +Sort entries alphabetically if none of -cftuvSUX nor --sort is specified. +``` + +**常用选项** + +| 选项 | 含义 | +| :--- | :----------------------------------------------------------- | +| `-a` | 显示所有文件及目录 (`.`开头的隐藏文件也会列出) | +| `-l` | 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出 | +| `-r` | 将文件以相反次序显示(原定依英文字母次序) | +| `-t` | 将文件依建立时间之先后次序列出 | +| `-A` | 同 `-a`,但不列出 `.(目前目录)` 及 `.. (父目录)` | +| `-F` | 在列出的文件名称后加一符号;例如可执行档则加 `*`, 目录则加 `/`,链接加`@` | +| `-R` | 若目录下有文件,则以下之文件亦皆依序列出 | +| `-h` | 将显示出来的文件大小以合适的单位显示出来 | + + +**案例演示** + +```shell +# 查看当前目录下的文件 +[root@localhost ~]# ls + +# 查看根目录下的文件,查看/usr目录下的文件 +[root@localhost ~]# ls / +[root@localhost ~]# ls /usr + +# 查看当前目录下所有文件,包括隐藏文件 +[root@localhost ~]# ls -a + +# 查看当前目录下文件详情,包括隐藏文件 +[root@localhost ~]# ls -lha + +# 查看 /usr/sbin/ 目录下的文件,并且显示出目录,文件,程序的区别 +# 可以看到普通文件只有文件名,可执行文件后面带*,文件夹后面带/ +[root@localhost ~]# ls -F /usr/sbin/ + +# 查看 /etc/ssh/ 目录下的文件,如果有文件夹,那么将文件夹中的文件也显示出来 +[root@localhost ~]# ls -FR /etc/ssh/ + +# 显示详细的信息 +[root@localhost ~]# ls -FRl +``` + +## 3.2 cd + +用于切换当前工作目录 + +```shell +cd: cd [-L|[-P [-e]] [-@]] [dir] + Change the shell working directory. +``` + +**案例演示** + +```shell +# 跳转到 /usr/bin 目录下 +[root@localhost ~]# cd /usr/bin + +# 跳到自己的 home 目录 +[root@localhost bin]# cd ~ + +# 跳到目前目录的上一层 +[root@localhost ~]# cd .. +``` + +## 3.3 pwd + +显示工作目录 + +```shell +pwd: pwd [-LP] + Print the name of the current working directory. +``` + +## 3.4 clear + +用于清除屏幕 + +```shell +Usage: clear [options] +``` + +## 3.5 echo + +用于字符串的输出 + +```shell +echo [option]... [string]... +``` + +**常用选项** + +| 选项 | 含义 | +| :--- | :----------------------------------------- | +| `-n` | 不输出行尾的换行符 | +| `-e` | 允许对下面列出的加反斜线转义的字符进行解释 | +| `\\` | 反斜线 | +| `\a` | 报警符(BEL) | +| `\b` | 退格符 | +| `\c` | 禁止尾随的换行符 | +| `\f` | 换页符 | +| `\n` | 换行符 | +| `\r` | 回车符 | +| `\t` | 水平制表符 | +| `\v` | 纵向制表符 | +| `-E` | 禁止对在 STRINGs 中的那些序列进行解释 | + +**案例演示** + +```shell +# 显示出 hello world +[root@localhost ~]# echo "hello world" +# 用两行显示出 hello world +[root@localhost ~]# echo -e "hello\nworld" +# 输出 hello world 的时候让系统发出警报音 +[root@localhost ~]# echo -e "hello\aworld" + +``` + + +# 4. 系统命令 + +## 4.1 poweroff + +用于关闭计算器并切断电源 + +```shell +poweroff [OPTIONS...] +Power off the system. +``` + +**常用选项** + +| 选项 | 含义 | +| :--- | :----------------------------------------------------------- | +| `-n` | 这个选项用于在关机时不执行文件系统的同步操作,即不调用 `sync()` 系统调用。通常,系统在关机时会自动同步所有挂载的文件系统,以确保所有挂起的磁盘写入操作都完成,从而避免数据丢失。使用 `-n` 参数可以跳过这个同步过程。 | +| `-w` | 仅记录关机信息到 `/var/log/wtmp` 文件中,但并不实际执行关机操作。 | +| `-d` | 不把记录写到 /var/log/wtmp 文件里 | +| `-f` | 强制关机。此参数会立即停止所有进程并关闭系统,而不是正常关机流程。 | + +## 4.2 reboot + +用来重新启动计算机 + +```shell +reboot [OPTIONS...] [ARG] +Reboot the system +``` + +**常用选项** + +| 选项 | 含义 | +| :--- | :----------------------------------------------------------- | +| `-n` | 这个选项用于在关机时不执行文件系统的同步操作,即不调用 `sync()` 系统调用。通常,系统在关机时会自动同步所有挂载的文件系统,以确保所有挂起的磁盘写入操作都完成,从而避免数据丢失。使用 `-n` 参数可以跳过这个同步过程。 | +| `-w` | 仅记录重启信息到 `/var/log/wtmp` 文件中,但并不实际执行重启操作。 | +| `-d` | 不把记录写到 /var/log/wtmp 档案里(-n 这个参数包含了 -d) | +| `-f` | 强迫重开机,不呼叫 shutdown 这个指令 | + + +## 4.3 whoami + +用于显示自身用户名称 + +```shell +[root@localhost ~]# whoami +root + +``` + +# 5. 快捷键 + +| 快捷键 | 作用 | +| :----- | :----------------------- | +| `^C` | 终止前台运行的程序 | +| `^D` | 退出 等价exit | +| `^L` | 清屏 | +| `^A` | 光标移动到命令行的最前端 | +| `^E` | 光标移动到命令行的后端 | +| `^U` | 删除光标前所有字符 | +| `^K` | 删除光标后所有字符 | +| `^R` | 搜索历史命令,利用关键词 | + + +# 6. 帮助命令 + +## 6.1 history + +```shell +history [n]  n为数字,列出最近的n条命令 +``` + +**常用选项** + +| 选项 | 含义 | +| :--- | :------------------------------------------------------- | +| `-c` | 将目前shell中的所有history命令消除 | +| `-a` | 将目前新增的命令写入histfiles, 默认写入`~/.bash_history` | +| `-r` | 将histfiles内容读入到目前shell的history记忆中 | +| `-w` | 将目前history记忆的内容写入到histfiles | + + +**案例演示** + +```shell +# 将history的内容写入一个新的文件中 +[root@localhost ~]# history -w histfiles.txt +# 清理所有的history记录,注意并不清空 ~/.bash_history 文件 +[root@localhos t ~]# history -c +[root@localhost ~]# history + 1 history + 2 cat .bash_history + 3 ping -c 3 baidu.com + 4 history +# 这里是执行第三条命令的意思 +[root@localhost ~]# !3 +# 从最近的命令查到以`command`开头的命令执行 +[root@localhost ~]# !ping +# 执行上一条命令 +[root@localhost ~]# !! + +``` + +## 6.2 help + +显示命令的帮助信息 + +```shell +help [-dms] [内置命令] +``` + +**常用选项** + +| 选项 | 含义 | +| :--- | :-------------------------------------------- | +| `-d` | 输出每个主题的简短描述 | +| `-m` | 以伪 man 手册的格式显示使用方法 | +| `-s` | 为每一个匹配 PATTERN 模式的主题仅显示一个用法 | + +**案例演示** + +```shell +# 查看echo的帮助信息 +[root@localhost ~]# help echo +``` + +## 6.3 man + +显示在线帮助手册页 + +```shell +man 需要帮助的命令或者文件 +``` + +**快捷键** + +| 按键 | 用途 | +| :---------- | :--------------------------------- | +| `空格键` | 向下翻一页 | +| `PaGe down` | 向下翻一页 | +| `PaGe up` | 向上翻一页 | +| `home` | 直接前往首页 | +| `end` | 直接前往尾页 | +| `/` | 从上至下搜索某个关键词,如“/linux” | +| `?` | 从下至上搜索某个关键词,如“?linux” | +| `n` | 定位到下一个搜索到的关键词 | +| `N` | 定位到上一个搜索到的关键词 | +| `q` | 退出帮助文档 | + + +**手册的结构** + +| 结构名称 | 代表意义 | +| :---------- | :----------------------- | +| NAME | 命令的名称 | +| SYNOPSIS | 参数的大致使用方法 | +| DESCRIPTION | 介绍说明 | +| EXAMPLES | 演示(附带简单说明) | +| OVERVIEW | 概述 | +| DEFAULTS | 默认的功能 | +| OPTIONS | 具体的可用选项(带介绍) | +| ENVIRONMENT | 环境变量 | +| FILES | 用到的文件 | +| SEE ALSO | 相关的资料 | +| HISTORY | 维护历史与联系方式 | + +## 6.4 alias + +用于设置指令的别名 + +**案例演示** + +```shell +# 查看系统当前的别名 +[root@localhost ~]# alias +alias cp='cp -i' +alias egrep='egrep --color=auto' +alias fgrep='fgrep --color=auto' +alias grep='grep --color=auto' +alias l.='ls -d .* --color=auto' +alias ll='ls -l --color=auto' +alias ls='ls --color=auto' +alias mv='mv -i' +alias rm='rm -i' +alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde' +[root@localhost ~]# ll +总用量 4 +-rw-------. 1 root root 1241 8月 22 2018 anaconda-ks.cfg +drwxr-xr-x. 2 root root 19 8月 21 12:15 home +# 查看命令类型 +# ls 是 `ls --color=auto' 的别名 +# ls 是 /usr/bin/ls +[root@xwz ~]# type -a ls +# 修改别名,比如使用wl来查看IP地址相关信息 +[root@localhost ~]# alias wl='ip address' +[root@localhost ~]# wl +# 为了让别名永久生效,可以将修改别名的命令写入 bashrc 文件,这个文件中的命令会在每次登陆命令行的时候执行 +[root@localhost ~]# echo "alias wl='ip address'" >> /etc/bashrc + +``` + +# 7. 环境变量 + +环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。 + +简单的理解就是告诉操作系统在程序运行的时候,有一些默认的设置是什么。 + +比如上面我们修改了 `LANG` 变量,就是一个环境变量,会影响到显示的语言是中文还是英文。 + +比如在讲解 `pwd` 命令的时候,我们修改了 `$PWD` 变量,就影响了当前所处的文件夹。 + +在我们使用 shell 命令行输入命令的时候,其实每个命令都是有一个可执行文件去完成我们下达的任务,这个可执行文件在操作系统中是分布在不同的文件夹中的,我们总不能每次执行的时候都要告诉操作系统这个文件在哪里,那么就算是查看一个文件,我们都需要输入如下的命令: + +```shell +# 在Linux中,ls的可执行程序在/usr/bin目录下 +[root@localhost ~]# /usr/bin/ls -lh +``` + +这样就太麻烦了,所以就指定了一个环境变量 `$PATH`,这个变量中有很多的目录地址,当我们执行命令的时候,操作系统就会到这些目录中查找,是否存在你所输入的命令。如果有那么就会去执行。 + +```shell +[root@localhost ~]# echo $PATH +/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin +``` + +如果你想让自己安装的某个软件可以在操作系统的任意位置直接输入文件名执行,那么你也可以把自定义的目录加入到这个 `$PATH` 中 \ No newline at end of file diff --git a/01.Linux基础/03.Linux命令行/命令解释器.png b/01.Linux基础/03.Linux命令行/命令解释器.png new file mode 100644 index 0000000..66b920e Binary files /dev/null and b/01.Linux基础/03.Linux命令行/命令解释器.png differ diff --git a/01.Linux基础/04.文件管理.md b/01.Linux基础/04.文件管理.md new file mode 100644 index 0000000..90aae53 --- /dev/null +++ b/01.Linux基础/04.文件管理.md @@ -0,0 +1,526 @@ +我们知道Linux的目录结构为树状结构,最顶级的目录为根目录 /。 + +其他目录通过挂载可以将它们添加到树中,通过解除挂载可以移除它们。 + +在开始本教程前我们需要先知道什么是绝对路径与相对路径。 + +* 绝对路径 + * 路径的写法,由根目录`/`写起,例如:`/usr/share/doc`这个目录 +* 相对路径 + * 路径的写法,不是由`/`写起,例如由`/usr/share/doc`要到`/usr/share/man`底下时,可以写成:`cd ../man`这就是相对路径的写法。 + +# 1. 文件管理命令 + +几个常见的处理目录的命令 + +* ls(英文全拼:list files): 列出目录及文件名 +* cd(英文全拼:change directory):切换目录 +* pwd(英文全拼:print work directory):显示目前的目录 +* mkdir(英文全拼:make directory):创建一个新的目录 +* rmdir(英文全拼:remove directory):删除一个空的目录 +* cp(英文全拼:copy file): 复制文件或目录 +* rm(英文全拼:remove): 删除文件或目录 +* mv(英文全拼:move file): 移动文件与目录,或修改文件与目录的名称 +* touch: 用于创建一个新的文件 + +## 1.1 touch + +创建新文件:用于修改文件或者目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。 + +```shell +touch 文件名 +``` + +**案例演示** + +```shell +# 创建新空白文件 +[root@localhost ~]# touch newfile +``` + +## 1.2 mkdir + +创建新目录 + +```shell +mkdir [-mpv] 目录名称 +``` + +**常用选项** + +| 选项 | 含义 | +| :--- | :--------------------------------------------------- | +| `-m` | 配置文件的权限,直接配置,不需要看默认权限 (umask) | +| `-p` | 帮助你直接将所需要的目录(包含上一级目录)递归创建起来 | +| `-v` | 显示目录创建的过程 | + + +**案例演示** + +```shell +# 创建一个新的文件夹叫dir +[root@localhost ~]# mkdir dir +# 无法直接创建多层目录 +[root@localhost ~]# mkdir a/b/c +mkdir: 无法创建目录"a/b/c": 没有那个文件或目录 +# 加上-p选项之后可以自动创建父级目录 +[root@localhost ~]# mkdir -p a/b/c +# 显示详细的创建过程 +[root@localhost ~]# mkdir -pv dir1/dir2 +mkdir: 已创建目录 "dir1" +mkdir: 已创建目录 "dir1/dir2" + +``` + +## 1.3 cp + +拷贝文件和目录 + +```shell +cp [选项]... 源文件... 目录 +``` + +**常用选项** + +| 选项 | 含义 | +| :--- | :----------------------------------------------------------- | +| `-a` | 相当于`-pdr `的意思,至于` pdr` 请参考下列说明 | +| `-d` | 如果源文件是符号链接,则复制链接本身,而不是链接指向的文件 | +| `-f` | 为强制(force)的意思,若目标文件已经存在且无法开启,则移除后再尝试一次 | +| `-i` | 若目标档(destination)已经存在时,在覆盖时会先询问动作的进行 | +| `-l` | 复制符号链接指向的文件,而不是链接本身 | +| `-p` | 连同文件的属性一起复制过去,保持文件的原始属性(如时间戳、权限等) | +| `-r` | 递归持续复制,用於目录的复制行为 | +| `-u` | 仅在源文件比目标文件新或目标文件不存在时进行复制 | +| `-v` | 显示复制的详细过程 | + + +**案例演示*** + +```shell +[root@localhost ~]# mkdir /home/dir{1,2} +[root@localhost ~]# touch install.log +# 复制文件到目录下 +[root@localhost ~]# cp -v install.log /home/dir1 +# 复制文件到目录下,并且重命名为abc.txt +[root@localhost ~]# cp -v install.log /home/dir1/abc.txt +# 复制目录 +[root@localhost ~]# cp -rv /etc /home/dir1 +# 将多个文件复制到同一个目录 +[root@localhost ~]# cp -rv /etc/passwd /etc/hostname /home/dir2 +# 将多个文件复制到当前目录 +[root@localhost ~]# cp -rv /etc/passwd /etc/hostname . +# 备份文件 +[root@localhost ~]# cp -rv install.log{,-old} + +``` + +## 1.4 mv + +移动文件与目录,或修改名称 + +```shell +mv [选项]... 源文件... 目录 +``` + +**常用选项** + +| 选项 | 含义 | +| :--- | :--------------------------------------------------------- | +| `-f` | force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖 | +| `-i` | 若目标文件 (destination) 已经存在时,就会询问是否覆盖 | +| `-u` | 若目标文件已经存在,且 source 比较新,才会升级 (update) | +| `-v` | 显示复制的详细过程 | + + +**常用实例** + +```shell +# 将file1移动到/home/dir2 +[root@localhost ~]# touch file1 +[root@localhost ~]# mv file1 /home/dir2 +# 将file2移动到/home/dir2,并且改名file20 +[root@localhost ~]# touch file2 +[root@localhost ~]# mv file2 /home/dir2/file20 +# 将file改名为file.txt +[root@localhost ~]# mv file file.txt + +``` + +## 1.5 rm + +删除文件或目录 + +```shell +rm [选项]... 文件... +``` + +**常用选项** + +| 选项 | 含义 | +| :--- | :----------------------------------------------------- | +| `-f` | 就是 force 的意思,忽略不存在的文件,不会出现警告信息 | +| `-i` | 互动模式,在删除前会询问使用者是否动作 | +| `-r` | 递归删除啊!最常用在目录的删除了!这是非常危险的选项! | + +**案例演示** + +```shell +[root@localhost ~]# cd /home +[root@localhost home]# rm -rf dir1 +``` + +## 1.6 禁忌命令 + +如下命令会从根目录开始删除Linux系统中所有的文件,包括系统文件。严令禁止在任何服务器上输入如下命令 + +```sh +# 不要操作!!! +[root@localhost ~]# rm -rf /* +``` + +# 2. 文本文件查看 + +在Linux中一切皆文件,说的就是Linux利用文本文件来保存系统所有的设置。 + +我们在Linux中想实现一个功能,不可避免的需要查看文本文件,修改文本文件。 + +## 2.1 cat + +用于打开文本文件并显示出来 + +```shell +cat [选项]... [文件]... +``` + +**常用选项** + +| 选项 | 含义 | +| :--- | :------------------------------------------------- | +| `-n` | 由 1 开始对所有输出的行数编号 | +| `-b` | 和 -n 相似,只不过对于空白行不编号 | +| `-s` | 当遇到有连续两行以上的空白行,就代换为一行的空白行 | +| `-A` | 显示控制字符 | + +**案例演示*** + +```shell +# 查看anaconda-ks.cfg文件 +[root@localhost ~]# cat anaconda-ks.cfg +# 查看anaconda-ks.cfg文件,并且显示行号 +``` + +## 2.2 less + +可以随意浏览文件,支持翻页和搜索,支持向上翻页和向下翻页 + +**案例演示** + +```shell +[root@localhost ~]# less anaconda-ks.cfg +``` + +## 2.3 head + +查看文件的开头部分的内容 + +```shell +head [选项]... [文件]... +``` + +**常用选项** + +| 选项 | 含义 | +| :----- | :--------------------- | +| `-q` | 隐藏文件名,默认是隐藏 | +| `-v` | 显示文件名 | +| `-c N` | 显示的字节数 | +| `-n N` | 显示的行数 | + +**案例演示** + +```shell +# 查看文件的前6行 +[root@localhost ~]# head -n 6 anaconda-ks.cfg +``` + +## 2.4 tail + +会把文本文件里的最尾部的内容显示在屏幕上 + +```shell +tail [选项]... [文件]... +``` + +**常用选项** + +| 选项 | 含义 | +| :----- | :----------------------------------- | +| `-f` | 循环读取 | +| `-q` | 隐藏文件名,默认隐藏 | +| `-v` | 显示文件名 | +| `-c N` | 显示的字节数 | +| `-n N` | 显示文件的尾部 n 行内容 | +| `-s` | 与-f合用,表示在每次反复的间隔休眠S秒 | + + +**案例演示** + +```shell +# 查看文件anaconda-ks.cfg尾部的3行 +[root@localhost ~]# tail -n 3 anaconda-ks.cfg +# 查看日志的实时更新情况 +[root@localhost ~]# tail -f /var/log/messages  +# ctrl+c退出 +# 查看文件anaconda-ks.cfg从第10行到结尾 +[root@localhost ~]# tail -n +10 anaconda-ks.cfg + +``` + +## 2.5 grep + +针对文件内容进行过滤,本工具属于文本三剑客,后续会详细讲解,目前只要求初学者掌握最基本的实例即可 + +**案例演示** + +```shell +# 在/etc/passwd的文件中找出有root的行 +[root@xwz ~]# grep 'root' /etc/passwd +# 在/etc/passwd中找出root开头的行 +[root@xwz ~]# grep '^root' /etc/passwd +# 在/etc/passwd中找出bash结尾的行 +[root@xwz ~]# grep 'bash$' /etc/passwd + +``` + +# 3. 文本文件编辑 + +在 Linux 中只掌握文本查看是远远不够的,我们还需要掌握编辑文本文件。 + +Linux 上也有图形化的文本编辑器,类似于 Windows 的记事本,但是很多时候我们只能用命令行来管理 Linux 操作系统,所以必须要掌握命令行的文本编辑器软件。 + +目前常见的命令行文本编辑器 + +* **nano**:在 Debain 系列的系统上会比较常见,但是其他的 Linux 发行版也都可以安装 +* **vi**:所有的 Unix Like 系统都会内建 vi 文本编辑器,其他的文本编辑器则不一定会存在。 +* **vim**:具有程序编辑的能力,可以主动的以字体颜色辨别语法的正确性,方便程序设计。 + + +## 3.1 什么是 Vim + +Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。 + +Vim 是一个纯命令行文本编辑器,很多文本编辑的功能都是通过键盘快捷键的方式完成,所以我们需要记住常用的键位。 + +![img](04.文件管理/vim键盘图.png) + +## 3.2 使用方式 + +基本上 vi/vim 共分为三种模式,分别是命令模式(Command mode),输入模式(Insert mode)和末行模式(Last line mode)。 这三种模式的作用分别是: + +### 3.2.1 命令模式 + +用户刚刚启动 vi/vim,便进入了命令模式。 + +此状态下敲击键盘动作会被Vim识别为命令,而非输入字符。比如我们此时按下`i`,并不会输入一个字符,`i`被当作了一个命令。 + +以下是常用的几个命令: + +| 命令 | 含义 | +| :--- | :----------------------------------- | +| `i` | 切换到输入模式,以输入字符 | +| `x` | 删除当前光标所在处的字符 | +| `:` | 切换到末行模式,以在最底一行输入命令 | + +### 3.2.2 输入模式 + +在命令模式下按下 `i` 就进入了输入模式。 + +在输入模式中,可以使用以下按键: + +| 命令 | 含义 | +| :---------------------- | :--------------------------------------------- | +| `字符按键以及Shift组合` | 输入字符 | +| `ENTER` | 回车键,换行 | +| `BACK SPACE` | 退格键,删除光标前一个字符 | +| `DEL` | 删除键,删除光标后一个字符 | +| `方向键` | 在文本中移动光标 | +| `HOME/END` | 移动光标到行首/行尾 | +| `Page Up/Page Down` | 上/下翻页 | +| `Insert` | 切换光标为输入/替换模式,光标将变成竖线/下划线 | +| `ESC` | 退出输入模式,切换到命令模式 | + +### 3.2.3 末行模式 + +在命令模式下按下 `:`(英文冒号)就进入了末行模式。 + +末行模式可以输入单个或多个字符的命令,可用的命令非常多。 + +在末行模式中,基本的命令有: + +| 命令 | 含义 | +| :--- | :------- | +| `q` | 退出程序 | +| `w` | 保存文件 | + +按 ESC 键可随时退出末行模式。 + +简单的说,我们可以将这三个模式的关系用下图来表示: + +![img](04.文件管理/vim模式关系.png) + +## 3.3 使用实例 + +有些 linux 发行套件上并没有安装 vim,我们可以安装一下,下面提供了 Rockylinux 的安装命令 + +```shell +# yum是在线安装软件的命令,后面会有详细的介绍,这边直接使用 +[root@localhost ~]# yum -y install vim + +``` + +vi和vim大部分的操作完全一模一样,所以会使用vim自然也会使用vi + +**案例演示** + +直接输入`vim 文件名`就能够进入 vim 的命令模式了。请注意,记得 vim 后面一定要加文件名,不管该文件存在与否! + +```shell +[root@localhost ~]# vim file.txt +``` + +输入这条命令之后,会看到如下画面 + +![img](04.文件管理/vim命令模式.png) + +按下`i`进入输入模式(也称为编辑模式),开始编辑文字 + +在命令模式之中,只要按下`i`,`o`,`a`等字符就可以进入输入模式了! + +在编辑模式当中,你可以发现在左下角状态栏中会出现`--INSERT--`的字样,那就是可以输入任意字符的提示。 + +这个时候,键盘上除了 Esc 这个按键之外,其他的按键都可以视作为一般的输入按钮了,所以你可以进行任何的编辑。 + +![img](04.文件管理/vim编辑模式.png) + +好了,假设我已经按照上面的样式给他编辑完毕了,那么应该要如何退出呢?是的!没错!就是给他按下 Esc 这个按钮即可!马上你就会发现画面左下角`--INSERT--`的不见了! + +![img](04.文件管理/vim内容编辑.png) + +输入**:wq**后回车即可保存离开,注意其中的冒号必须是英文输入法下的冒号! + +![img](04.文件管理/vim末行模式.png) + +现在我们就成功创建了一个文件,查看文件的内容吧 + +```shell +[root@localhost ~]# ls -lh file.txt +[root@localhost ~]# cat file.txt +``` + +## 3.4 按键说明 + +下面将会列举出 vim 非常多的常用按键,初学者只需要浏览一遍,记住大概 vim 有哪些功能,等后面大量使用 vim 的时候,再来翻阅笔记,并且在多次使用中把这些功能记住。 + +### 3.4.1 命令模式 + +下面的操作都是在命令模式下进行的 + +**移动光标的方法** + +| 方法 | 含义 | +| :------------------- | :----------------------------------------------------------- | +| `h 或 向左箭头键(←)` | 光标向左移动一个字符 | +| `j 或 向下箭头键(↓)` | 光标向下移动一个字符 | +| `k 或 向上箭头键(↑)` | 光标向上移动一个字符 | +| `l 或 向右箭头键(→)` | 光标向右移动一个字符 | +| `[Ctrl] + [f]` | 屏幕『向下』移动一页,相当于 [Page Down]按键 (常用) | +| `[Ctrl] + [b]` | 屏幕『向上』移动一页,相当于 [Page Up] 按键 (常用) | +| `[Ctrl] + [d]` | 屏幕『向下』移动半页 | +| `[Ctrl] + [u]` | 屏幕『向上』移动半页 | +| `+` | 光标移动到非空格符的下一行 | +| `-` | 光标移动到非空格符的上一行 | +| `n + [space]` | 那个 n 表示『数字』,例如 20 。按下数字后再按空格键,光标会向右移动这一行的 n 个字符。例如 20 则光标会向后面移动 20 个字符距离。 | +| `0 或功能键[Home]` | 移动到这一行的最前面字符处 | +| `$ 或功能键[End]` | 移动到这一行的最后面字符处 | +| `H ` | 光标移动到这个屏幕的最上方那一行的第一个字符 | +| `M` | 光标移动到这个屏幕的中央那一行的第一个字符 | +| `L` | 光标移动到这个屏幕的最下方那一行的第一个字符 | +| `G` | 移动到这个档案的最后一行(常用) | +| `nG` | n 为数字。移动到这个文档的第 n 行。例如 20G 则会移动到这个档案的第 20 行 | +| `gg` | 移动到这个档案的第一行,相当于 1G | +| `n + [Enter]` | n 为数字。光标向下移动 n行 | + +**文本的搜索与替换** + +| 方法 | 含义 | +| :--------------------------------------------- | :----------------------------------------------------------- | +| `/word ` | 向光标之下寻找一个名称为 word 的字符串。 | +| `?word` | 向光标之上寻找一个字符串名称为 word 的字符串。 | +| `n` | 这个 n 是英文按键。代表重复前一个搜寻的动作。 | +| `N` | 这个 N 是英文按键。与 n 刚好相反,为『反向』进行前一个搜寻动作。 | +| `:n1,n2s/word1/word2/g` | n1 与 n2 为数字。在第 n1 与 n2 行之间寻找 word1 这个字符串,并将该字符串取代为 word2 | +| `:1,$s/word1/word2/g` 或 `:%s/word1/word2/g` | 从第一行到最后一行寻找 word1 字符串,并将该字符串取代为 word2 ! | +| `:1,$s/word1/word2/gc` 或 `:%s/word1/word2/gc` | 从第一行到最后一行寻找 word1 字符串,并将该字符串取代为 word2 !且在取代前显示提示字符给用户确认 (confirm) 是否需要取代! | + +**删除/剪切、复制与粘贴** + +| 方法 | 含义 | +| :--------- | :----------------------------------------------------------- | +| `x, X` | 在一行字当中,x 为向后删除一个字符 (相当于 [del] 按键), X 为向前删除一个字符(相当于 [backspace] 亦即是退格键) | +| `nx` | n 为数字,连续向后删除 n 个字符。举例来说,我要连续删除 10 个字符, 『10x』。 | +| `dd` | 删除/剪切光标所在的那一整行(常用) | +| `ndd` | n 为数字。删除/剪切光标所在的向下 n 行,例如 20dd 则是删除 20 行 | +| `d1G` | 删除光标所在到第一行的所有数据 | +| `dG` | 删除光标所在到最后一行的所有数据 | +| `d$` | 删除游标所在处,到该行的最后一个字符 | +| `d0` | 那个是数字的 0 ,删除光标所在处,到该行的最前面一个字符 | +| `yy` | 复制游标所在的那一行 | +| `nyy` | n 为数字。复制光标所在的向下 n 行,例如 20yy 则是复制 20 行 | +| `y1G` | 复制游标所在行到第一行的所有数据 | +| `yG` | 复制游标所在行到最后一行的所有数据 | +| `y0` | 复制光标所在的那个字符到该行行首的所有数据 | +| `y$` | 复制光标所在的那个字符到该行行尾的所有数据 | +| `p, P` | p 为将已复制的数据在光标下一行贴上,P 则为贴在光标上一行! 举例来说,我目前光标在第 20 行,且已经复制了 10 行数据。则按下 p 后, 那 10 行数据会贴在原本的 20 行之后,亦即由 21 行开始贴。但如果是按下 P 呢? 那么原本的第 20 行会被推到变成 30 行。 | +| `J` | 将光标所在行与下一行的数据结合成同一行 | +| `c` | 重复删除多个数据,例如向下删除 10 行,[ 10c ] | +| `u` | 复原前一个动作。 | +| `[Ctrl]+r` | 重做上一个动作。 | +| `.` | 重复前一个动作。 如果你想要重复删除、重复贴上等等动作,按下小数点『.』就好了! | + +**进入输入或取代的编辑模式** + +| 方法 | 含义 | +| :------ | :----------------------------------------------------------- | +| `i, I` | 进入输入模式(Insert mode): i 为『从目前光标所在处输入』, I 为『在目前所在行的第一个非空格符处开始输入』。 | +| `a, A` | 进入输入模式(Insert mode): a 为『从目前光标所在的下一个字符处开始输入』, A 为『从光标所在行的最后一个字符处开始输入』。 | +| `o, O` | 进入输入模式(Insert mode): 这是英文字母 o 的大小写。o 为在目前光标所在的下一行处输入新的一行; O 为在目前光标所在的上一行处输入新的一行! | +| `r, R` | 进入取代模式(Replace mode): r 只会取代光标所在的那一个字符一次;R会一直取代光标所在的文字,直到按下 ESC 为止 | +| `[Esc]` | 退出编辑模式,回到一般模式中 | + +### 3.4.2 末行模式 + +**末行模式下的储存、离开等指令** + +| 方法 | 含义 | +| :-------------------- | :----------------------------------------------------------- | +| `:w` | 将编辑的数据写入硬盘中 | +| `:w!` | 若文件属性为『只读』时,强制写入该文件。不过,到底能不能写入, 还是跟你对该文件的权限有关 | +| `:q` | 离开 vim | +| `:q!` | 若曾修改过文件,又不想储存,使用 ! 为强制离开不储存。 | +| `:wq` | 保存后离开,若为 `:wq!` 则为强制保存退出 | +| `ZZ` | 这是大写的 Z 喔!如果修改过,保存当前文件,然后退出!效果等同于(保存并退出) | +| `:x` | 效果等同于(保存并退出) | +| `:X` | 大写的X,用于加密文件 | +| `ZQ` | 不保存,强制退出。效果等同于 `:q!` | +| `:w [filename]` | 将编辑的数据储存成另一个文件(类似文件另存为) | +| `:r [filename]` | 在编辑的数据中,读入另一个文件的数据。亦即将 『filename』 这个文件内容加到光标所在行后面 | +| `:n1,n2 w [filename]` | 将 n1 到 n2 的内容储存成 filename 这个文件。 | +| `:!command` | 暂时离开 vi 到bash命令行下执行 command 的显示结果!例如 『:! ls /home』即可在 vi 当中察看 /home 底下以 ls 输出的文件信息 | + +**vim环境变量修改** + +| 方法 | 含义 | +| :---------- | :------------------------------------------------- | +| `:set nu` | 显示行号,设定之后,会在每一行的前缀显示该行的行号 | +| `:set nonu` | 与 set nu 相反,为取消行号 | \ No newline at end of file diff --git a/01.Linux基础/04.文件管理/vim内容编辑.png b/01.Linux基础/04.文件管理/vim内容编辑.png new file mode 100644 index 0000000..4699c52 Binary files /dev/null and b/01.Linux基础/04.文件管理/vim内容编辑.png differ diff --git a/01.Linux基础/04.文件管理/vim命令模式.png b/01.Linux基础/04.文件管理/vim命令模式.png new file mode 100644 index 0000000..8ee07fc Binary files /dev/null and b/01.Linux基础/04.文件管理/vim命令模式.png differ diff --git a/01.Linux基础/04.文件管理/vim末行模式.png b/01.Linux基础/04.文件管理/vim末行模式.png new file mode 100644 index 0000000..d2b3d30 Binary files /dev/null and b/01.Linux基础/04.文件管理/vim末行模式.png differ diff --git a/01.Linux基础/04.文件管理/vim模式关系.png b/01.Linux基础/04.文件管理/vim模式关系.png new file mode 100644 index 0000000..f3cbdc6 Binary files /dev/null and b/01.Linux基础/04.文件管理/vim模式关系.png differ diff --git a/01.Linux基础/04.文件管理/vim编辑模式.png b/01.Linux基础/04.文件管理/vim编辑模式.png new file mode 100644 index 0000000..738b4fc Binary files /dev/null and b/01.Linux基础/04.文件管理/vim编辑模式.png differ diff --git a/01.Linux基础/04.文件管理/vim键盘图.png b/01.Linux基础/04.文件管理/vim键盘图.png new file mode 100644 index 0000000..b0d0c82 Binary files /dev/null and b/01.Linux基础/04.文件管理/vim键盘图.png differ diff --git a/01.Linux基础/05.文件属性与查找.md b/01.Linux基础/05.文件属性与查找.md new file mode 100644 index 0000000..d0fc589 --- /dev/null +++ b/01.Linux基础/05.文件属性与查找.md @@ -0,0 +1,273 @@ +# 1. 文件时间 + +任何一个操作系统都有时间的概念,时间的概念主要用于对文件和系统中发生的时间进行记录,在 Linux 中,可以使用 stat 查看 Linux 系统中文件的时间。 + +## 1.1 stat + +用于显示文件时间和 inode 内容,inode相关的知识会在后面的磁盘管理章节详细讲解,这边主要来看文件的时间 + +```shell +stat [选项]... 文件... +``` + +**案例演示** + +stat查看文件时间,这边为了我们方便看得懂,建议改为英文系统环境 + +```shell +[root@localhost ~]# export LANG="en_US.UTF-8" +# 改回中文是LANG="zh_CN.UTF-8" +[root@localhost ~]# stat anaconda-ks.cfg  +  File: ‘anaconda-ks.cfg’ +  Size: 1241      Blocks: 8          IO Block: 4096   regular file +Device: fd00h/64768d Inode: 33574979    Links: 1 +Access: (0600/-rw-------)  Uid: (    0/    root)   Gid: (    0/    root) +Context: system_u:object_r:admin_home_t:s0 +Access: 2021-04-04 17:54:09.700844151 +0800 +Modify: 2021-04-04 16:53:30.524854041 +0800 +Change: 2021-04-04 16:53:30.524854041 +0800 + Birth: - +``` + +- **Access:访问时间,也叫 atime** + - 当文件被访问的时候,这个时间就会发生改变 + - Linux文件运行的时候查看文件又频繁数量又大,如果每次 atime 发生变化的时候都记入硬盘,或造成很大的压力。必须满足其中一个条件: + - 自上次 atime 修改后,已达到 86400 秒 + - 发生写操作时 +- **Modify:修改时间,也叫 mtime** + - 当文件内容发生变化的时候,这个时间就会发生改变 +- **Change:改变时间,也叫 ctime** + - 当文件状态被改变的时候,这个时间就会发生修改 + +# 2. 文件类型 + +Linux 系统和 Windows 系统有很大的区别,Windows 系统查看文件的后缀名就可以知道这个是什么类型的文件,比如:`test.jpg` 这个是一个图片,如果你在 Windows 上双击打开,就会使用支持查看图片的软件打开。 + +Linux 系统就根本不看文件的后缀名,你认为这个是什么文件,你就使用什么工具打开这个文件,如果打开错误,就会报错,看下面的案例 + +```shell +[root@localhost ~]# cat file +cat: file: Is a directory +``` + +当你以为 file 是个文件,使用 cat 命令查看的时候,cat 会提示你这个是个文件夹,你才反应过来,应该使用 cd 命令打开。 +所以在 Linux 中,我们需要使用和 windows 不一样的方法来判断这个文件的类型。 + +## 2.1 方式一:ls + +通过 `ls -l` 查看第一个字母: + +| 标识符 | 文件类型 | +| `-` | 普通文件(文本文档,二进制文件,压缩文件,电影,图片等等)| +| `d` | 目录文件 | +| `b` | 块设备文件(块设备)存储设备硬盘,U盘 `/dev/sda` 和 `/dev/sda1` | +| `c` | 字符设备文件(字符设备)打印机,终端 `/dev/tty1` 和 `/dev/zero` | +| `s` | 套接字文件 | +| `p` | 管道文件 | +| `l` | 链接文件 | + +**案例演示** + +```shell +[root@localhost ~]# type ll +[root@localhost ~]# ll -d /etc/hosts /bin/ls /home /dev/nvme0n1 /dev/tty1 /etc/grub2.cfg /dev/log +-rwxr-xr-x. 1 root root 140872 4月 21 2024 /bin/ls +lrwxrwxrwx. 1 root root 28 11月 8 21:42 /dev/log -> /run/systemd/journal/dev-log +brw-rw----. 1 root disk 259, 0 11月 8 21:42 /dev/nvme0n1 +crw--w----. 1 root tty 4, 1 11月 8 21:42 /dev/tty1 +lrwxrwxrwx. 1 root root 22 9月 5 03:00 /etc/grub2.cfg -> ../boot/grub2/grub.cfg +-rw-r--r--. 1 root root 158 6月 23 2020 /etc/hosts +drwxr-xr-x. 10 root root 124 11月 8 09:57 /home +``` + +对于初学者而言,我们现在只要知道可以通过这样的方式查看文件的类型,并且能够知道 `-` 和 `d` 的意思即可。后面在学习的过程中,会慢慢的将所有文件类型都掌握的。 + +## 2.2 方法二:file + +file 是专门用来查看文件的类型的命令,有时候也可以使用 + +**案例演示** + +```shell +[root@localhost ~]# file /etc/hosts +/etc/hosts: ASCII text +[root@localhost ~]# file /bin/ls +/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=ceaf496f3aec08afced234f4f36330d3d13a657b, stripped +[root@localhost ~]# file /dev/nvme0n1 +/dev/nvme0n1: block special (259/0) +[root@localhost ~]# file /dev/tty1 +/dev/tty1: character special +[root@localhost ~]# file /etc/grub2.cfg  +/etc/grub2.cfg: symbolic link to `../boot/grub2/grub.cfg' +[root@localhost ~]# file /home +/home: directory +[root@localhost ~]# file /run/dmeventd-client +/run/dmeventd-client: fifo (named pipe) +``` + +## 2.3 方法三:stat + +这个命令上面已经介绍过了,在输出结果中也是可以看到文件的类型 + +# 3. 文件查找 + +在 windows 中可以在文件管理器中很方便的输入文件名查找文件,然而 Linux 的文件查找功能更加的方便,并且功能更加的强大。 + +find 命令实时查找工具,通过遍历指定路径下的文件系统完成文件查找 + +工作特点: + +- 查找速度略慢 +- 精确查找 +- 实时查找 +- 可以满足多种条件匹配 + +```shell +find [选项] [路径] [查找条件 + 处理动作] + 查找路径:指定具体目录路径,默认是当前文件夹 + 查找条件:指定的查找标准(文件名/大小/类型/权限等),默认是找出所有文件 + 处理动作:对符合条件的文件做什么操作,默认输出屏幕 +``` + +## 3.1 查找条件 + +根据文件名查找 + +```shell +[root@localhost ~]# find /etc -name "ens160.nmconnection" +[root@localhost ~]# find /etc -iname "ens160.nmconnection" # 忽略大小写 +[root@localhost ~]# find /etc -iname "ens*" +``` + +按文件大小 + +```shell +[root@localhost ~]# find /etc -size +5M # 大于5M +[root@localhost ~]# find /etc -size 5M # 等于5M +[root@localhost ~]# find /etc -size -5M # 小于5M +[root@localhost ~]# find /etc -size +5M -ls # 找到的处理动作-ls +``` + +指定查找的目录深度 + +```shell +[root@localhost ~]# find / -maxdepth 3 -a -name "ens160.nmconnection" # 最大查找深度 +# -a是同时满足,-o是或 +[root@localhost ~]# find / -mindepth 3 -a -name "ens160.nmconnection" # 最小查找深度 +``` + +按时间找 + +```shell +[root@localhost ~]# find /etc -mtime +5 # 修改时间超过5天 +[root@localhost ~]# find /etc -mtime 5 # 修改时间等于5天 +[root@localhost ~]# find /etc -mtime -5 # 修改时间5天以内 +``` + +按照文件属主、属组找,文件的属主和属组,会在下一篇详细讲解。 + +```shell +[root@localhost ~]# find /home -user xwz # 属主是xwz的文件 +[root@localhost ~]# find /home -group xwz +[root@localhost ~]# find /home -user xwz -group xwz +[root@localhost ~]# find /home -user xwz -a -group root +[root@localhost ~]# find /home -user xwz -o -group root +[root@localhost ~]# find /home -nouser # 没有属主的文件 +[root@localhost ~]# find /home -nogroup # 没有属组的文件 +``` + +按文件类型 + +```shell +[root@localhost ~]# find /dev -type d +``` + +按文件权限,文件权限会在下一篇详细讲解 + +```shell +[root@localhost ~]# find / -perm 644 -ls +[root@localhost ~]# find / -perm -644 -ls # 权限大于等于/包含644的 +``` + +按正则表达式 + +```shell +[root@localhost ~]# find /etc -regex '.*ens[0-9][0-9][0-9].*' +# .* 任意多个字符 +# [0-9] 任意一个数字 +``` + +* 条件组合 + * **-a**:多个条件and并列 + * **-o**:多个条件or并列 + * **-not**:条件取反 + +## 3.2 处理动作 + +| 动作 | 含义 | +| :----------------------- | :------------------------------------------- | +| `‐print` | 默认的处理动作,显示至屏幕 | +| `-ls` | 对查找到的文件执行 `ls ‐l` 命令 | +| `-delete` | 删除查找到的文件 | +| `-fls /path/to/filename` | 查找到的所有文件的长格式信息保存至指定文件中 | +| `{}` | 用于引用查找到的文件名称自身 | +| `-exec` | 允许对找到的每个文件执行一个命令 | + +下面的相关案例大家学习完后续用户权限管理之后,就可以完全看的懂了 + +## 3.3 相关案例 + +- 查找到root目录下面以.log结尾的文件,并且复制到/home/dir1中 + +```shell +[root@localhost ~]# find /root -name "*.log" -exec mv {} /home/dir1 \; +``` + +* 查找/var目录下属主为root,且属组为mail的所有文件或目录 + +```shell +[root@localhost ~]# find /var -user root -group mail +``` + +* 查找/usr目录下不属于root,bin或ftp用户的所有文件或目录 + +```shell +[root@localhost ~]# find /usr -not -user root -a -not -user bin -a -not -user ftp +[root@localhost ~]# find /usr -not \( -user root -o -user bin -o -user ftp \) +``` + +* 查找/etc目录下最近一周内容曾被修改过的文件或目录 + +```shell +[root@localhost ~]# find /etc -mtime -7 +``` + +* 查找当前系统上没有属主或属组,且最近一周内曾被访问过的文件或目录 + +```shell +[root@localhost ~]# find / \( -nouser -o -nogroup \) -a -atime -7 +``` + +* 查找/etc目录下大于1M且类型为普通文件的所有文件或目录 + +```shell +[root@localhost ~]# find /etc -size +1M -type f +``` + +* 查找/etc目录下所有用户都没有写权限的文件 + +```shell +[root@localhost ~]# find /etc -not -perm /222 +``` + +* 查找/etc目录下至少一类用户没有执行权限的文件 + +```shell +[root@localhost ~]# find /etc -not -perm -111 +``` + +* 查找/etc/init.d目录下,所有用户都有执行权限,且其它用户写权限的文件 + +```shell +[root@localhost ~]# find /etc/init.d -perm -113 +``` \ No newline at end of file diff --git a/01.Linux基础/06.用户权限管理.md b/01.Linux基础/06.用户权限管理.md new file mode 100644 index 0000000..c43e17a --- /dev/null +++ b/01.Linux基础/06.用户权限管理.md @@ -0,0 +1,648 @@ +Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统。 + +为了更加方便的管理多个用户,就出现了用户组的概念,关于用户和用户组: + +* 系统上的每个进程(运行的程序)都是作为特定用户运行 +* 每个文件是由一个特定的用户拥有 +* 访问文件和目录受到用户的限制 +* 与正在运行的进程相关联的用户确定该进程可访问的文件和目录 + +实现用户账号的管理,要完成的工作主要有如下几个方面: + +* 用户账号的添加、删除与修改 +* 用户口令的管理 +* 用户组的管理 + +# 1. 用户和用户组查看 + +## 1.1 id + +用于显示用户的ID,以及所属群组的 ID + +id会显示用户以及所属群组的实际与有效 ID。若两个 ID 相同,则仅显示实际 ID。若仅指定用户名称,则显示目前用户的 ID。 + +```shell +id [OPTION]... [USER] +``` + +| 选项 | 含义 | +| :--- | :---------------------------------------------------------- | +| `-g` | 显示用户所属群组的 ID | +| `-G` | 显示用户所属附加群组的 ID | +| `-n` | 显示用户,所属群组或附加群组的名称 | +| `-r` | 显示用户真实 ID,用户真实的 uid | +| `-u` | 显示用户有效 ID,可以都某些高权限用户,通过有效 id 限制权限 | + +### 1.1.1 uid 约定 + +Linux 操作系统会依据用户的 uid 数值来判定这个用户的角色,分别如下 + +- **0**:超级管理员,也就是root,在linux系统中拥有所有权力 +- **1~999**:系统用户,系统用户往往是用来约束系统中的服务的 +- **1000+**:普通用户,可以用来登陆和使用Linux操作系统 + +关于root用户 + +- uid是0 +- 拥有操作系统所有权力 +- 该用户有权力覆盖文件系统上的普通权限 +- 安装或删除软件并管理系统文件和目录 +- 大多数设备只能由root控制 + +### 1.1.2 案例演示 + +查看当前登陆的用户信息 + +```shell +[root@localhost ~]# id +uid=0(root) gid=0(root) 组=0(root) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 +``` + +查看文件的拥有者 + +```shell +[root@localhost ~]# ll anaconda-ks.cfg  +-rw-------. 1 root root 1241 4月   4 16:53 anaconda-ks.cfg +``` + +![img](06.用户权限管理/文件属性介绍.png) + +查看运行进程的用户名,ps命令会在后面进程管理部分讲解 + +```shell +[root@localhost ~]# ps aux +USER PID %CPU %MEM    VSZ   RSS TTY STAT START   TIME COMMAND +root 2  0.0  0.0      0     0 ? S    09:06   0:00 [kthreadd] +root 3  0.0  0.0      0     0 ? S    09:06   0:01 [ksoftirqd/0] +root 4  0.1  0.0      0     0 ? R    09:06   0:09 [kworker/0:0] +root 5  0.0  0.0      0     0 ? S<   09:06   0:00 [kworker/0:0H] +``` + +## 1.2 相关的文件 + +之前说过 Linux 一切皆文件,所以用户和用户组相关的信息也都是保存在文本文件中的,下面列举出相关的文件。 + +### 1.2.1 passwd 文件 + +用于保存用户的信息,一般第一行是 root 用户,下面都是其他用户 + +```shell +[root@localhost ~]# head -n 1 /etc/passwd +root:x:0:0:root:/root:/bin/bash +# 这个格式为用户名:密码:uid:gid:描述:家目录:登陆后执行的命令 +``` + +### 1.2.2 shadow 文件 + +格式中密码占位置太长了,所以使用x来替代,Linux系统会到shadow中查找x部分的的密码内容 + +```shell +[root@localhost ~]# head -n 1 /etc/shadow +root:$6$frokclXSnQa8EbKs$pWElbjPlmxjYh30tr8qLsTQVOhuPg7GmW9Sanm2yXAK8TNMgje1gyc/vwPgqvmSMf6VaoEvveM0gFvtETmXy/.::0:99999:7::: +# 这个格式为用户名:加密密码:最后一次修改时间:最小修改时间间隔:密码有效期:密码需要变更前的警告天数:密码过期后的宽限时间:账号失效时间:保留字段 +``` + +格式不需要大家记住,只需要知道关于这个用户的密码和有效期都在这个文件中即可。 + +密码在`passwd`文件中会使用加密算法加密,所以别想知道我的密码是什么,加密算法默认是`$6`,这个类型6的加密算法是sha-512。我们也可以在man手册中看到对shadow文件的详细解释。 + +```shell +[root@localhost ~]# man 5 shadow +# man手册一个有9个章节,其中第5个章节是对文件格式的说明 +# 对man手册感兴趣的同学,也可以自己在网上查找学习man手册的更多内容 +``` + +### 1.2.3 group文件 + +用户和组的对应关系,会保存在group文件中 + +```shell +[root@localhost ~]# head -n 1 /etc/group +root:x:0: +# 这个格式是组名:口令:组标识号:组内用户列表 +``` + +# 2. 用户组管理 + +## 2.1 添加用户组 + +groupadd 命令用于创建一个新的工作组,新工作组的信息将被添加到系统文件中 + +```shell +groupadd [选项] 组 +``` + +### 2.1.1 常用选项 + +| 选项 | 含义 | +| :--- | :----------------------------------------------------------- | +| `-g` | 指定新建工作组的 id | +| `-r` | 创建系统工作组,系统工作组的组ID小于 500 | +| `-K` | 覆盖配置文件`/etc/login.defs` | +| `-o` | 允许添加组 ID 号不唯一的工作组 | +| `-f` | 如果指定的组已经存在,此选项将失明了仅以成功状态退出。当与 -g 一起使用,并且指定的GID_MIN已经存在时,选择另一个唯一的GID(即-g关闭) | + +### 2.1.2 案例演示 + +按照下图创建组,并且指定gid,并且检查是否成功 + +![img](06.用户权限管理/用户创建实战示例1.png) + +```shell +[root@localhost ~]# groupadd hr -g 1000 +[root@localhost ~]# groupadd sale -g 2000 +[root@localhost ~]# groupadd it -g 3000 +[root@localhost ~]# groupadd fd -g 4000 +[root@localhost ~]# tail -n 4 /etc/group +hr:x:1000: +sale:x:2000: +it:x:3000: +fd:x:4000: +``` + +## 2.2 修改用户组 + +groupmod命令用于更改群组识别码或名称 + +```shell +groupmod [选项] 组 +``` + +### 2.2.1 常用选项 + +- **-g**:将组 ID 改为 GID +- **-n**:改名为 NEW_GROUP +- **-o**:允许使用重复的 GID + +### 2.2.2 案例演示 + +* 修改fd组的名字为finance + +```shell +[root@localhost ~]# groupmod -n finance fd +[root@localhost ~]# tail -n 1 /etc/group +finance:x:4000: +``` + +## 2.3 删除用户组 + +groupdel命令用于删除群组 + +需要从系统上删除群组时,可用groupdel(group delete)指令来完成这项工作。倘若该群组中仍包括某些用户,则必须先删除这些用户后,方能删除群组。 + +```shell +groupdel [组名] +``` + +### 2.3.1 案例演示 + +* 删除一个用户组 + +```shell +[root@localhost ~]# groupadd test +[root@localhost ~]# groupdel test +``` + +## 2.4 用户组成员管理 + +gpasswd 是 Linux 下工作组文件 /etc/group 和 /etc/gshadow 管理工具,用于将一个用户添加到组或者从组中删除 + +```shell +gpasswd [选项] 组 +``` + +### 2.4.1 常用选项 + +- **-a**:添加用户到组; +- **-d**:从组删除用户; +- **-A**:指定管理员,可以执行添加或者删除组员; +- **-M**:替换组中的全部用户列表,不包含在内的用户将会从组中删除; +- **-R**:限制用户登入组,只有组中的成员才可以用newgrp加入该组。 + +### 2.4.2 案例演示 + +创建用户 itadmin,并且将其加入 it 组 + +```shell +[root@localhost ~]# useradd itadmin +[root@localhost ~]# gpasswd -a itadmin it +正在将用户“itadmin”加入到“it”组中 +[root@localhost ~]# cat /etc/group |grep it: +it:x:3000:itadmin +# 在组文件中,可以看到这个组的成员 +[root@localhost ~]# id itadmin +uid=6667(itadmin) gid=6667(itadmin) 组=6667(itadmin),3000(it) +# 在用户的信息中,可以看到这个用户的所属组 +``` + +# 3. 用户管理 + +## 3.1 添加用户 + +useradd 可以用来添加新的用户账号 + +```shell +useradd [选项] 用户名 +``` + +### 3.1.1 常用选项 + +* **-c comment**:指定一段注释性描述。 +* **-d 目录**:指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。 +* **-m**:创建用户的主目录 +* **-g 用户组**:指定用户所属的用户组,默认会创建一个和用户名同名的用户组。 +* **-G 用户组**:用户组 指定用户所属的附加组,一个用户可以属于多个附加组。 +* **-s Shell文件**:指定用户的登录Shell。 +* **-u 用户号**:指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。 + +### 3.1.2 案例演示 + +* 添加一般用户 + +```shell +[root@localhost ~]# useradd user01 +``` + +* 为添加的用户指定相应的用户组 + +```shell +[root@localhost ~]# useradd -g root user02 +``` + +* 为新添加的用户指定home目录 + +```shell +[root@localhost ~]# useradd -d /home/test user03 +``` + +* 建立一个不给登录的用户 + +```shell +[root@localhost ~]# useradd -s /sbin/nologin user04 +``` + +- 查看用户描述信息 + +```bash +[root@localhost ~]# useradd -c "this is python_developmer user" python-developmer +[root@localhost ~]# cat /etc/passwd |grep python-developmer +python-developmer:x:1005:1005:this is python_developmer user:/home/python-developmer:/bin/bash +``` + +## 3.2 切换用户 + +su命令用户Linux系统中的用户切换 + +```BASH +su [选项] 用户名 +``` + +### 3.2.1 常用选项 + +- `-`:以目标用户的环境变量启动新会话。这将模拟用户完全登录到新用户账户,包括其家目录、环境变量等。 +- `-c 命令`:执行指定的命令,并在执行完毕后返回原始用户。 +- `-s shell`:使用指定的 shell 替代目标用户的默认 shell。 + +## 3.3 修改用户:usermod + +usermod命令用于修改用户帐号 + +usermod可用来修改用户帐号的各项设定 + +```shell +usermod [选项] 登录 +``` + +### 3.3.1 常用选项 + +- **-c<备注>**:修改用户帐号的备注文字。 +- **-a**:追加,默认的修改是覆盖 +- **-d登入目录>**:修改用户登入时的目录。 +- **-e<有效期限>**:修改帐号的有效期限。 +- **-f<缓冲天数>**:修改在密码过期后多少天即关闭该帐号。 +- **-g<群组>**:修改用户所属的群组。 +- **-G<群组>**:修改用户所属的附加群组。 +- **-l<帐号名称>**:修改用户帐号名称。 +- **-L**:锁定用户密码,使密码无效。 +- **-s**:修改用户登入后所使用的shell。 +- **-u**:修改用户ID。 +- **-U**:解除密码锁定。 + +### 3.3.2 案例演示 + +* 更改登录的目录 + +```shell +[root@localhost ~]# usermod -d /home user01 +[root@localhost ~]# su - user01 +[user01@localhost ~]$ pwd +/home +``` + +* 改变用户的uid + +```shell +[root@localhost ~]# usermod -u 6666 user02 +``` + +## 3.4 删除用户:userdel + +userdel命令用于删除用户帐号 + +userdel可删除用户帐号与相关的文件。若不加参数,则仅删除用户帐号,而不删除相关文件 + +```shell +userdel [-r][用户帐号] +``` + +### 3.4.1 常用选项 + +- **-r**:删除用户登入目录以及目录中所有文件 + +### 3.4.2 案例演示 + +删除用户账号 + +```shell + [root@localhost ~]# userdel user04 +``` + +删除用户账户和家目录 + +```shell +[root@localhost ~]# userdel -r user03 +``` + +# 4. passwd 文件中的 shell + +查看`/etc/passwd`文件会发现在每行的最后是登录成功之后执行的命令,有两种是使用最为频繁的: + +- /bin/bash:这个是 Linux 的命令行工具,我们正常登陆之后默认就是进入命令行 +- /sbin/nologin:如果写成 nologin,那么用户将无法登录,有些用户是作为进程权限管理而存在的,不需要登录。如果提供登录的功能反而不安全,所以写成 nologin + +```shell +[root@localhost ~]# cat /etc/passwd +root:x:0:0:root:/root:/bin/bash +bin:x:1:1:bin:/bin:/sbin/nologin +daemon:x:2:2:daemon:/sbin:/sbin/nologin +adm:x:3:4:adm:/var/adm:/sbin/nologin +lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin +``` + +我们可以新建一个用户,然后尝试自定义登录成功之后执行的命令,用来加深印象。 + +```shell +[root@localhost ~]# useradd test01 +[root@localhost ~]# tail -n 1 /etc/passwd +test01:x:1000:1000::/home/test01:/bin/vi +``` + +切换到test01用户,会发现自动进入vi的界面,说明最后的这个段内容就是用户登录之后会运行的程序 + +![img](06.用户权限管理/用户创建实战示例2.png) + +# 5. 用户密码管理 + +root用户可以直接设置普通用户密码,普通用户必须要提供原密码,才可以修改自己密码。 + +## 5.1 passwd + +passwd命令用来更改使用者的密码 + +```shell +passwd [选项...] <帐号名称> +``` + +### 5.1.1 常用选项 + +- **-k**:保持身份验证令牌不过期 +- **-d**:删除已命名帐号的密码(只有根用户才能进行此操作) +- **-l**:锁定指名帐户的密码(仅限 root 用户) +- **-u**:解锁指名账户的密码(仅限 root 用户) +- **-x**:密码的最长有效时限(只有根用户才能进行此操作) +- **-n**:密码的最短有效时限(只有根用户才能进行此操作) +- **-w**:在密码过期前多少天开始提醒用户(只有根用户才能进行此操作) +- **-i**:当密码过期后经过多少天该帐号会被禁用(只有根用户才能进行此操作) +- **-S**:报告已命名帐号的密码状态(只有根用户才能进行此操作) +- **--stdin**:从标准输入读取令牌(只有根用户才能进行此操作) + +### 5.1.2 案例演示 + +修改test01用户密码 + +```shell +[root@localhost ~]# passwd test01 +更改用户 test01 的密码 。 +新的 密码: +重新输入新的 密码: +passwd:所有的身份验证令牌已经成功更新。 +``` + +使用管道符设置用户密码 + +```shell +[root@localhost ~]# echo 123456 | passwd --stdin test01 +更改用户 test01 的密码 。 +passwd:所有的身份验证令牌已经成功更新。 +``` + +## 5.2 login.defs文件 + +`/etc/login.defs`文件是用来创建用户时进行一定的限制,但是优先级低于`/etc/passwd`和`/etc/shadow`,如果有冲突的地方,系统会以`/etc/passwd`和`/etc/shadow`为准 + +下面是这个文件的内容,egrep命令我们后续会讲到,这边可以理解为不看文件的注释和空行。 + +```shell +[root@localhost ~]# egrep -v '^[ ]*$|^#' /etc/login.defs +MAIL_DIR /var/spool/mail # 系统消息(邮件)文件夹 +PASS_MAX_DAYS 99999 # 密码有效最大天数  +PASS_MIN_DAYS 0 # 密码有效最小天数 +PASS_MIN_LEN 5 # 密码长度 +PASS_WARN_AGE 7 # 密码失效警告倒计时 +UID_MIN                  1000 # 用户UID最小1000 +UID_MAX                 60000 # 用户UID最大60000 +SYS_UID_MIN               201 # 系统用户UID最小201 +SYS_UID_MAX               999 # 系统用户UID最大999 +GID_MIN                  1000 # 用户组GID最小1000 +GID_MAX                 60000 # 用户组GID最大60000 +SYS_GID_MIN               201 +SYS_GID_MAX               999 +CREATE_HOME yes # 创建家目录 +UMASK           077 # 创建文件/目录的权限掩码 +USERGROUPS_ENAB yes # 创建用户时同时生成组是  如果此处是no 创建的用户 会是gid=100(users)groups=100(users)  +ENCRYPT_METHOD SHA512 # 加密  方法  sha 512 这个方法生成的密码在/etc/shadow里面的第二列会以$6$开头 +``` + +## 5.3 chage + +chage是用于更改用户密码过期信息 + +```shell +chage [选项] 登录 +``` + +### 5.3.1 常用选项 + +- **-d**:设置密码的最后更改日期 +- **-E 过期日期**:设置账号的过期日期 +- **-I INACITVE**:设置密码过期后若未更改,多少天后用户账号被禁用。 +- **-l**:显示用户账号的密码过期信息。 +- **-m 最小天数**:设置两次改变密码之间相距的最小天数 +- **-M 最大天数**:设置将两次改变密码之间相距的最大天数 +- **-W 警告天数**:设置密码过期前的警告天数 + +### 5.3.2 案例演示 + +强制用户在下次登录的时候换密码 + +```shell +[root@localhost ~]# chage -d 0 test01 +[root@localhost ~]# logout +You must change your password now and login again! +更改用户 test01 的密码 。 +为 test01 更改 STRESS 密码。 +(当前)UNIX 密码: + +[root@localhost ~]# chage -l root +最近一次密码修改时间 :从不 +密码过期时间 :从不 +密码失效时间 :从不 +帐户过期时间 :从不 +两次改变密码之间相距的最小天数 :0 +两次改变密码之间相距的最大天数 :99999 +在密码过期之前警告的天数 :7 + +# 设置用户 user01 的密码过期前7天提醒用户修改密码 +[root@localhost ~]# chage -W 7 user01 + +# 设置用户 user01 的密码最短使用天数为7天,即密码修改后7天内不能再次修改 +[root@localhost ~]# chage -m 7 user01 +[root@localhost ~]# chage -l user01 +最近一次密码修改时间 :11月 08, 2024 +密码过期时间 :从不 +密码失效时间 :从不 +帐户过期时间 :从不 +两次改变密码之间相距的最小天数 :7 +两次改变密码之间相距的最大天数 :99999 +在密码过期之前警告的天数 :7 +``` + +**小知识:** 当你新建用户的时候,用户的home目录下会有一些默认的隐藏文件,这些隐藏文件是在创建用户的时候从`/etc/skel/`中复制过去的。 + +# 6. sudoers + +Linux是多用户多任务的操作系统, 共享该系统的用户往往不只一个。出于安全性考虑, 有必要通过useradd创建一些非root用户, 只让它们拥有不完全的权限; 如有必要,再来提升权限执行。 + +sudo就是来解决这个需求的: 这些非root用户不需要知道root的密码,就可以提权到root,执行一些root才能执行的命令。 + +```shell +sudo [选项] [用户名] [命令] +``` + +## 6.1 sudo 命令执行过程 + +1. 当用户执行sudo时,系统会主动寻找`/etc/sudoers`文件,判断该用户是否有执行sudo的权限 +2. 确认用户具有可执行sudo的权限后,让用户输入用户自己的密码确认 +3. 若密码输入成功,则开始执行sudo后续的命令 + +## 6.2 赋予用户 sudo 操作的权限 + +通过useradd添加的用户,并不具备sudo权限。在ubuntu/centos/RockyLinux等系统下, 需要将用户加入admin组或者wheel组或者sudo组。以root用户身份执行如下命令, 将用户加入wheel/admin/sudo组。 + +```shell +usermod -a -G wheel <用户名> +``` + +如果提示wheel组不存在, 则还需要先创建该组 + +```shell +groupadd wheel +``` + +## 6.3 配置文件 + +sudo 的权限控制可以在 `/etc/sudoers` 文件中查看到。一般来说,通过 `cat /etc/sudoers` 指令来查看该文件, 会看到如下几行代码。 + +```shell +[root@localhost ~]# egrep -v '^[ ]*$|^#' /etc/sudoers +=====省略===== +root ALL=(ALL) ALL +%wheel ALL=(ALL) ALL +``` + +对/etc/sudoers文件进行编辑的代码公式可以概括为 + +```shell +授权用户/组 主机=[(切换到哪些用户或组)] [是否需要输入密码验证] 命令1,命令2,... +字段1 字段2 =[(字段3)] [字段4] 字段5 +``` + +凡是[ ]中的内容, 都能省略; 命令和命令之间用`,`号分隔,字段3、字段4,是可以省略的。 + +* "字段1"不以%号开头的表示"将要授权的用户",以%号开头的表示"将要授权的组"。 +* "字段2"表示允许登录的主机, ALL表示所有,;如果该字段不为ALL,表示授权用户只能在某些机器上登录本服务器来执行sudo命令 +* "字段3"如果省略, 相当于(root:root),表示可以通过sudo提权到root,如果为(ALL)或者(ALL:ALL), 表示能够提权到(任意用户:任意用户组)。 +* "字段4"的可能取值是NOPASSWD:。请注意NOPASSWD后面带有冒号:。表示执行sudo时可以不需要输入密码。 + * 比如:`lucy ALL=(ALL) NOPASSWD: /bin/useradd`表示: 普通用户lucy可以在任何主机上, 通过sudo执行/bin/useradd命令, 并且不需要输入密码 + * 比如:`peter ALL=(ALL) NOPASSWD: ALL`,表示: 普通用户peter可以在任何主机上, 通过sudo执行任何命令, 并且不需要输入密码。 +* "字段5"是使用逗号分开一系列命令,这些命令就是授权给用户的操作; ALL表示允许所有操作。命令都是使用绝对路径, 这是为了避免目录下有同名命令被执行,从而造成安全隐患。 + * 如果你将授权写成如下安全性欠妥的格式:`lucy ALL=(ALL) chown,chmod,useradd`那么用户就有可能创建一个他自己的程序, 也命名为userad, 然后放在它的本地路径中, 如此一来他就能够使用root来执行这个"名为useradd的程序"。这是相当危险的! + +## 6.4 编辑配置文件 + +在实践中,去编辑`/etc/sudoers`文件,系统提示我没权限,这是因为`/etc/sudoers`的内容如此敏感,以至于该文件是只读的。所以,编辑该文件前,请确认清楚你知道自己正在做什么。 + +强烈建议通过`visudo`命令来修改该文件,通过`visudo`修改,如果配置出错,会有提示。 + +官方文档推荐的做法,不是直接修改`/etc/sudoers`文件,而是将修改写在`/etc/sudoers.d/`目录下的文件中。如果使用这种方式修改sudoers,需要在`/etc/sudoers`文件的最后行,加上`#includedir /etc/sudoers.d`一行(默认已有)。需要注意,这个`#includedir /etc/sudoers.d`中的`#`并不是注释,请勿修改。 + +## 6.5 sudo 选项 + +* **-u**:以指定用户或 ID 运行命令(或编辑文件) +* **-l**:显示出自己(执行 sudo 的使用者)的权限 +* **-b**:将要执行的指令放在后台执行 +* **-i**: 以目标用户身份运行一个登录 shell;可同时指定一条命令。相当于切换到root,不过只需要用户自己的密码即可。 + +## 6.6 案例演示 + +* 以管理员身份查看shadow文件 + +```shell +[root@localhost ~]# useradd user +[root@localhost ~]# echo 123456 | passwd --stdin user +[root@localhost ~]# usermod -a -G wheel user +[root@localhost ~]# su - user +[user@localhost ~]$ cat /etc/shadow +cat: /etc/shadow: 权限不够 +[user@localhost ~]$ sudo -u root cat /etc/shadow +[sudo] user 的密码: +[user@localhost ~]$ sudo cat /etc/shadow +# sudo -u root用的比较多,可以被精简为sudo +``` + +* 查看下列示例 + +```shell +papi ALL=(root) NOPASSWD: /bin/chown,/usr/sbin/useradd +``` + +* 表示: 用户papi能在所有可能出现的主机上, 提权到root下执行`/bin/chown`, 不必输入密码; 但运行`/usr/sbin/useradd`命令时需要密码 +* 在具有sudo操作的用户下, 执行`sudo -l`可以查看到该用户被允许和被禁止运行的命令 + +* 查看下列示例 + +```shell +papi ALL=/usr/sbin/,/sbin/,!/usr/sbin/fdisk +``` + +* 命令前面加上!号表示取消该命令 +* 用户papi在所有可能出现的主机上, 能够运行目录/usr/sbin和/sbin下所有的程序, 但fdisk除外。 + +* 默认情况下输入一次sudo可以保持15分钟不再要求输入密码,如果想要延长这个时间,可以修改配置文件 + +```shell +[root@localhost ~]# visudo +Defaults env_reset,pwfeedback,timestamp_timeout=60 +# 这个是改成60分钟才会需要再次输入密码,并且输入密码的时候会显示*号 +``` + diff --git a/01.Linux基础/06.用户权限管理/文件属性介绍.png b/01.Linux基础/06.用户权限管理/文件属性介绍.png new file mode 100644 index 0000000..405108e Binary files /dev/null and b/01.Linux基础/06.用户权限管理/文件属性介绍.png differ diff --git a/01.Linux基础/06.用户权限管理/用户创建实战示例1.png b/01.Linux基础/06.用户权限管理/用户创建实战示例1.png new file mode 100644 index 0000000..0fc55ed Binary files /dev/null and b/01.Linux基础/06.用户权限管理/用户创建实战示例1.png differ diff --git a/01.Linux基础/06.用户权限管理/用户创建实战示例2.png b/01.Linux基础/06.用户权限管理/用户创建实战示例2.png new file mode 100644 index 0000000..a478e2a Binary files /dev/null and b/01.Linux基础/06.用户权限管理/用户创建实战示例2.png differ diff --git a/01.Linux基础/07.文件权限管理.md b/01.Linux基础/07.文件权限管理.md new file mode 100644 index 0000000..f311009 --- /dev/null +++ b/01.Linux基础/07.文件权限管理.md @@ -0,0 +1,543 @@ +# 1. 文件权限 + +文件权限设置:可以赋予某个用户或组,能够以何种方式,访问某个文件 + +Linux 系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限。 + +为了保护系统的安全性,Linux 系统对不同的用户访问同一文件(包括目录文件)的权限做了不同的规定。 + +在 Linux 中我们通常使用以下两个命令来修改文件或目录的所属用户与权限: + +* chown (change ownerp) : 修改所属用户与组。 +* chmod (change mode) : 修改用户的权限。 + +在 Linux 中我们可以使用 ll 或者 ls –l 命令来显示一个文件的属性以及文件所属的用户和组 + +![文件权限介绍](07.文件权限管理/文件权限介绍.png) + +每个文件的属性由左边第一部分的 10 个字符来确定(如下图)。 + +![文件ugo权限解释](07.文件权限管理/文件ugo权限解释.png) + +从左至右用 0-9 这些数字来表示。 + +* 第 0 位确定文件类型,第 1-3 位确定属主(该文件的所有者)拥有该文件的权限。 +* 第4-6位确定属组(所有者的同组用户)拥有该文件的权限,第7-9位确定其他用户拥有该文件的权限。 +* 其中,第 1、4、7 位表示读权限,如果用`r`字符表示,则有读权限,如果用`-`字符表示,则没有读权限; +* 第 2、5、8 位表示写权限,如果用`w`字符表示,则有写权限,如果用`-`字符表示没有写权限;第 3、6、9 位表示可执行权限,如果用`x`字符表示,则有执行权限,如果用`-`字符表示,则没有执行权限。 + +# 2. 修改文件属主chown + +chown用于设置文件所有者和文件关联组的命令 + +chown 需要超级用户`root`的权限才能执行此命令 + +```shell +chown [选项]... [所有者][:[组]] 文件... +``` + +## 2.1 选项 + +* **-R**: 处理指定目录以及其子目录下的所有文件 +* **-v**: 显示详细的处理信息 + +## 2.2 实例 + +* 设置所有者为root + +```shell +[root@localhost ~]# chown root anaconda-ks.cfg +``` + +* 将文件的拥有者设置为user01,允许使用的组设置为it + +```shell +[root@localhost ~]# chown user01:it file.txt +``` + +* 将目录下的所有文件拥有者设置为user01,允许使用的组设置为it + +```shell +[root@localhost ~]# chown -R user01:it dir/* +``` + +# 3. 修改文件权限chmod + +chmod是控制用户对文件的权限的命令 + +```shell +chmod [选项]... 模式[,模式]... 文件... +``` + +## 3.1 模式 + +mode : 权限设定字串,格式如下 : + +```shell +[ugoa...][[+-=][rwx]...][,...] +``` + +* `u`表示该文件的拥有者,`g`表示与该文件的拥有者属于同一个群体(group)者,`o`表示其他以外的人,`a`表示这三者皆是。 +* `+`表示增加权限、`-`表示取消权限、`=`表示唯一设定权限。 +* `r`表示可读取,`w`表示可写入,`x`表示可执行 +* `r``w``x`权限对文件和目录的意义 + +| 权限 | 对文件的影响 | 对目录的影响 | +| :-------- | :------------------- | :--------------------------------------------------------- | +| r(读取) | 可以读取文件的内容 | 可以列出目录的内容(文件名),可以使用ls命令 | +| w(写入) | 可以更改文件的内容 | 可以创建或删除目录中的任一文件,可以使用touch、rm命令 | +| x(可执行) | 可以作为命令执行文件 | 可以访问目录的内容(取决于目录中文件的权限),可以使用cd命令 | + +## 3.2 八进制语法 + +chmod命令可以使用八进制数来指定权限。文件或目录的权限位是由9个权限位来控制,每三位为一组,它们分别是文件所有者(User)的读、写、执行,用户组(Group)的读、写、执行以及其它用户(Other)的读、写、执行。历史上,文件权限被放在一个比特掩码中,掩码中指定的比特位设为1,用来说明一个类具有相应的优先级。 + +| # | 权限 | rwx | 二进制 | +| :--- | :------------- | :--- | :----- | +| 7 | 读 + 写 + 执行 | rwx | 111 | +| 6 | 读 + 写 | rw- | 110 | +| 5 | 读 + 执行 | r-x | 101 | +| 4 | 只读 | r-- | 100 | +| 3 | 写 + 执行 | -wx | 011 | +| 2 | 只写 | -w- | 010 | +| 1 | 只执行 | --x | 001 | +| 0 | 无 | --- | 000 | + +例如, 765 将这样解释: + +* 所有者的权限用数字表达:属主的那三个权限位的数字加起来的总和。如 rwx ,也就是 4+2+1 ,应该是 7。 +* 用户组的权限用数字表达:属组的那个权限位数字的相加的总和。如 rw- ,也就是 4+2+0 ,应该是 6。 +* 其它用户的权限数字表达:其它用户权限位的数字相加的总和。如 r-x ,也就是 4+0+1 ,应该是 5。 + +## 3.3 选项 + +* **-f**: 若该文件权限无法被更改也不要显示错误讯息 +* **-v**: 显示权限变更的详细资料 +* **-R**: 对目前目录下的所有文件与子目录进行相同的权限变更(即以递归的方式逐个变更) + +## 3.4 实例 + +* 限制用户user1对`file`文件的写入 + +```shell +[root@localhost ~]# chmod a-w /home/user1/file  +[root@localhost ~]# ls -lh /home/user1/file  +-r--r--r--. 1 user1 user1 0 4月  13 10:56 /home/user1/file +[root@localhost ~]# su - user1 +上一次登录:二 4月 13 10:47:41 CST 2021pts/1 上 +[user1@localhost ~]$ echo "hello world" >> file  +-bash: file: 权限不够 +``` + +* 限制所有用户删除dir目录下的文件 + +```shell +[root@localhost ~]# chmod a-w /home/user1/dir/ +[root@localhost ~]# ls -lhd /home/user1/dir +dr-xr-xr-x. 2 user1 user1 18 4月  13 10:58 /home/user1/dir +[root@localhost ~]# su - user1 +上一次登录:二 4月 13 10:57:11 CST 2021pts/1 上 +[user1@localhost ~]$ cd dir/ +[user1@localhost dir]$ rm -rf test  +rm: 无法删除"test": 权限不够 +``` + +# 4. 文件访问控制列表 + +文件访问控制列表(Access Control Lists,ACL)是Linux开的一套新的文件系统权限管理方法。 + +传统的Linux文件系统的权限控制是通过user、group、other与r(读)、w(写)、x(执行)的不同组合来实现的。随着应用的发展,这些权限组合已不能适应现时复杂的文件系统权限控制要求。例如,我们可能需把一个文件的读权限和写权限分别赋予两个不同的用户或一个用户和一个组这样的组合。传统的权限管理设置起来就力不从心了。 + +文件访问控制列表可以针对文件单独设置某个用户或者用户组队文件的管理权限。 + +## 4.1 getfacl命名 + +获取文件访问控制列表的详细内容 + +```shell +getfacl [-aceEsRLPtpndvh] file ... +``` + +### 4.1.1 选项 + +* **-a**:仅显示文件访问控制列表 +* **-d**:仅显示默认的访问控制列表 +* **-c**:不显示注释表头 +* **-e**:显示所有的有效权限 +* **-E**:显示无效权限 +* **-R**:递归显示子目录 +* **-t**:使用制表符分隔的输出格式 + +### 4.1.2 实例 + +* 查看acl权限列表 + +```shell +[root@localhost ~]# getfacl anaconda-ks.cfg  +# file: anaconda-ks.cfg +# owner: root +# group: root +user::rwx +group::r-x +other::r-x +``` + +* 查看acl权限列表,不显示注释表头 + +```shell +[root@localhost ~]# getfacl -c anaconda-ks.cfg  +user::rwx +group::r-x +other::r-x +``` + +## 4.2 setfacl命令 + +用来设置更精确的文件权限 + +```shell +setfacl [-bkndRLP] { -m|-M|-x|-X ... } file ... +``` + +### 4.2.1 选项 + +* **-m**:更改文件的访问控制列表 +* **-M**:从文件读取访问控制列表条目更改 +* **-x**:根据文件中访问控制列表移除条目 +* **-X**:从文件读取访问控制列表条目并删除 +* **-b**:删除所有扩展访问控制列表条目 +* **-k**:移除默认访问控制列表 +* **-d**:应用到默认访问控制列表的操作 +* **-R**:递归操作子目录 + +### 4.2.2 实例 + +* 给指定用户添加acl权限 +* 首先用户user1是没有`/workdir`的权限的 + +```shell +[root@localhost ~]# groupadd worker +[root@localhost ~]# mkdir /workdir +[root@localhost ~]# chown root:worker /workdir +[root@localhost ~]# chmod 770 /workdir # 不允许其他用户对目录的权限 +[root@localhost ~]# ll -d /workdir/ +drwxrwx---. 2 root worker 6 4月  14 09:14 /workdir/ +[root@localhost ~]# su - user1 +[user1@localhost ~]$ cd /workdir/ +-bash: cd: /workdir/: 权限不够 +``` + +* 单独给予user1的可读和可进入权限 + +```shell +[root@localhost ~]# setfacl -m u:user1:rx /workdir/ +[root@localhost ~]# getfacl -c /workdir +user::rwx +user:user1:r-x # 成功添加user1对workdir的权限 +group::rwx +mask::rwx +other::--- +[root@localhost ~]# su - user1 +[user1@localhost ~]$ cd /workdir/ +[user1@localhost workdir]$ ll -d +drwxrwx---+ 2 root worker 6 4月  14 09:14 . # 权限位后面多了一个"+",表示存在ACL权限 +[user1@localhost workdir]$ touch file +touch: 无法创建"file": 权限不够 +``` + +* 移除user1的访问控制列表权限 + +```shell +[root@localhost ~]# setfacl -x u:user1 /workdir/ +[root@localhost ~]# getfacl -c /workdir/ +user::rwx +group::rwx +mask::rwx +other::--- +``` + +* 创建worker2组,然后给这个组访问acl的权限,将user1加入worker2组验证是否成功 + +```shell +[root@localhost ~]# groupadd worker2 +[root@localhost ~]# setfacl -m g:worker2:rwx /workdir +[root@localhost ~]# usermod -aG worker2 user1 +[root@localhost ~]# su - user1 +[user1@localhost ~]$ cd /workdir/ +[user1@localhost workdir]$ touch file +``` + +* 对workdir设置的acl权限并不会被之后在workdir下创建的子文件和子目录继承,可以设置默认ACL权限,来让目录下面的新建文件和文件夹都继承父目录的权限 + +```shell +[root@localhost ~]# setfacl -b /workdir +[root@localhost ~]# setfacl -m d:u:user1:rx /workdir +# 在前面加上一个d,就可以设置默认facl权限 +[root@localhost ~]# getfacl -c /workdir +user::rwx +group::rwx +other::--- +default:user::rwx # 前面多出来了default +default:user:user1:r-x +default:group::rwx +default:mask::rwx +default:other::--- +[root@localhost ~]# touch /workdir/newfile +[root@localhost ~]# getfacl -c /workdir/newfile  +user::rw- +user:user1:r-x #effective:r-- # 新建的文件会自动继承 +group::rwx #effective:rw- +mask::rw- +other::--- +``` + +# 5. mask有效权限 + +mask 权限,指的是用户或群组能拥有的最大 ACL 权限,也就是说,给用户或群组设定的 ACL 权限不能超过 mask 规定的权限范围,超出部分做无效处理。 + +注意上面的案例,在newfile的facl中,有一个mask权限是`rw-`,所以我们即使给了user1`r-x`权限,在实际生效的时候,`x`也不会有的,注意后面的提示`#effective:r--`表示`x`权限并没有赋予。 + +## 5.1 实例 + +* 修改上面案例中的newfile的mask权限 + +```shell +[root@localhost ~]# setfacl -m m::rwx /workdir/newfile  +[root@localhost ~]# getfacl -c /workdir/newfile  +user::rw- +user:user1:r-x +group::rwx +mask::rwx +other::--- +``` + +# 6. 特殊权限 + +文件除了上述的`r`,`w`,`x`权限以外,还有三个特殊权限:suid,sgid,sticky + +## 6.1 suid + +suid 属性只能运用在可执行文件上,含义是开放文件所有者的权限给其他用户,即当用户执行该执行文件时,会拥有该执行文件所有者的权限。如果给一个非二进制文件文件附加suid权限,则会显示大写S,属于无效。 + +普通用户能够执行`passwd`命令修改自己的密码,修改密码其实就是修改`/etc/shadow`这个文件,查看`/etc/passwd`这个文件的权限,发现除了`root`其他人没有写权限,但是普通用户能够成功执行`passwd`,其原因就在于`passwd`这个命令的权限是`-rwsr-xr-x`,其中`s`的作用就是让执行命令的人具有和该命令拥有者相同的权限。 + +```shell +[root@localhost ~]# ll /usr/bin/passwd +-rwsr-xr-x. 1 root root 32656 May 15 2022 /usr/bin/passwd +``` + +### 6.1.1 实例 + +* 切换成普通用户输入修改密码命令 + +```shell +[root@localhost ~]# su - user1 +[user1@localhost ~]$ passwd +更改用户 user1 的密码 。 +为 user1 更改 STRESS 密码。 +(当前)UNIX 密码: +``` + +* 保持这个会话别端口,新建一个ssh会话,查看系统进程,可以看到当前是调用root用户执行的passwd命令 + +```shell +[root@localhost ~]# ps -aux |grep passwd +root 2031 0.0 0.3 14904 6656 pts/2 S+ 22:30 0:00 passwd +root 2034 0.0 0.1 3876 2048 pts/1 S+ 22:31 0:00 grep --color=auto passwd +``` + +* 如果想某个文件添加suid权限,可以输入下面两个命令 + +```shell +chmod u+s file +chmod 4765 file +``` + +要设置特殊权限,可以使用`chmod`命令的4位数字表示法,其中第一位用于特殊权限,后三位分别代表所有者、组和其他用户的权限。特殊权限的设置如下: + +\- `4`: 设置setuid。 +\- `2`: 设置setgid。 +\- `1`: 设置sticky bit。 + +以下是一些设置特殊权限的例子: + +\- 设置setuid: `chmod u+s file` +\- 设置setgid: `chmod g+s file` +\- 设置sticky bit: `chmod o+t /dir` + +如果你想要将特殊权限与其他权限组合使用,可以这样: + +\- 设置文件的setuid和可读可执行权限:`chmod 4555 file` +\- 设置目录的setgid和可读可执行可进入权限:`chmod 2775 /dir` + +请注意,特殊权限的设置需要谨慎,因为它们可能会影响系统的安全性和文件的访问控制。通常,只有系统管理员或有经验的用户才会设置这些权限。 + +## 6.2 sgid + +sgid 属性可运用于二进制文件或者目录,运用在文件的含义是开放文件所属组的权限给其他用户,即当用户执行该执行文件时,会拥有该执行文件所属组用户的权限。如果给一个非二进制文件文件附加sgid权限,则会显示大写S,属于无效。 + +运用在目录上的含义是,在该目录下所有用户创建的文件或者目录的所属组都和其一样。即如果`/home/user1`目录具有sgid权限,且所属组是`user1`,则任何用户在`/home/user1`下创建的子目录或者文件的所属组都是`user1`。 + +### 6.2.1 实例 + +* 设置sgid,让用户在workdir下创建的文件都属于worker组 + +```shell +[root@localhost ~]# mkdir /workdir +[root@localhost ~]# groupadd worker +[root@localhost ~]# chown .worker /workdir/ +[root@localhost ~]# ll -d /workdir/ +drwxr-xr-x. 2 root worker 6 Nov 15 22:42 /workdir/ +[root@localhost ~]# chmod g+s /workdir/ +[root@localhost ~]# cd /workdir/ +[root@localhost workdir]# touch file +[root@localhost workdir]# ll +total 0 +-rw-r--r--. 1 root worker 0 Nov 15 22:44 file +``` + +## 6.3 sticky + +sticky 权限只能运用于目录上,含义是该目录下所有的文件和子目录只能由所属者删除,即使其的权限是777或者其他。一个公共目录,每个人都可以创建文件,删除自己的文件,但不能删除别人的文件(仅对目录有效)。 + +### 6.3.1 实例 + +* 设置sticky,让普通用户只能创建文件,不能删除文件 + +```shell +[root@localhost ~]# mkdir /workdir +[root@localhost ~]# chmod 1777 /workdir/ +[root@localhost ~]# ll -d /workdir/ +drwxrwxrwt. 2 root root 6 Nov 15 22:47 /workdir/ +[root@localhost ~]# su - user1 +[user1@localhost ~]$ cd /workdir/ +[user1@localhost workdir]$ touch user1file +[user1@localhost workdir]$ exit +logout +[root@localhost ~]# useradd user2 +[root@localhost ~]# su - user2 +[user2@localhost ~]$ cd /workdir/ +[user2@localhost workdir]$ rm -rf user1file # 不能删除别人的创建的文件 +rm: cannot remove 'user1file': Operation not permitted +[user2@localhost workdir]$ touch user2file +[user2@localhost workdir]$ rm -rf user2file # 只能删除自己创建的文件 +[user2@localhost workdir]$ ll +total 0 +-rw-r--r--. 1 user1 user1 0 Nov 15 22:48 user1file +``` + +# 7. chattr文件属性 + +chattr命令用于改变文件属性。 + +这项指令可改变存放在文件或目录属性,这些属性共有以下8种模式: + +* **a**:让文件或目录仅供追加用途 +* **b**:不更新文件或目录的最后存取时间 +* **c**:将文件或目录压缩后存放 +* **i**:不得任意更动文件或目录 +* **s**:保密性删除文件或目录 +* **S**:即时更新文件或目录 +* **u**:预防意外删除 + +```shell +chattr [-RV][+/-/=<属性>][文件或目录...] +``` + +## 7.1 选项 + +* **-R**:递归处理,将指定目录下的所有文件及子目录一并处理 +* **-v <版本编号>**:设置文件或目录版本 +* **-V**:显示指令执行过程 +* **+ <属性>**:开启文件或目录的该项属性 +* **- <属性>**:关闭文件或目录的该项属性 +* **= <属性>**:指定文件或目录的该项属性 + +## 7.2 实例 + +* 用chattr命令防止系统中某个关键文件被修改 + +```shell +[root@localhost ~]# chattr +i /etc/resolv.conf +[root@localhost ~]# lsattr /etc/resolv.conf  +----i----------- /etc/resolv.conf +[root@localhost ~]# echo test >> /etc/resolv.conf +-bash: /etc/resolv.conf: 权限不够 +``` + +* 让某个文件只能往里面追加数据,但不能删除,适用于各种日志文件 + +```shell +[root@localhost ~]# chattr +a /var/log/messages  +[root@localhost ~]# lsattr /var/log/messages  +-----a---------- /var/log/messages +[root@localhost ~]# echo > /var/log/messages  # 不允许清空日志 +-bash: /var/log/messages: 不允许的操作 +``` + +# 8. umask + +umask命令指定在建立文件时预设的权限掩码,进程、新建文件、目录的默认权限会收到umask的影响,umask表示要减掉得到权限。 + +umask可用来设定[权限掩码]。[权限掩码]是由3个八进制的数字所组成,将现有的存取权限减掉权限掩码后,即可产生建立文件时预设的权限。 + +```shell +umask [选项][权限掩码] +``` + +## 8.1 选项 + +* **-S**:以文字的方式来表示权限掩码 + +## 8.2 实例 + +* 查看当前用户的umask权限 + +```shell +[root@localhost ~]# umask +0022 +``` + +* 查看最终有的权限 + +```shell +[root@localhost ~]# umask -S +u=rwx,g=rx,o=rx +``` + +* 修改umask的数值(临时) + +```shell +[root@localhost ~]# umask 0000 +[root@localhost ~]# mkdir dir +[root@localhost ~]# ll +总用量 4 +drwxrwxrwx. 2 root root    6 4月  14 11:25 dir +-rw-rw-rw-. 1 root root    0 4月  14 11:25 file +``` + +* 修改umask的数值(永久) + +```shell +[root@localhost ~]# vim /etc/profile +-------------- + 59 if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then + 60     umask 002 + 61 else + 62     umask 022 + 63 fi +--------------- +[root@localhost ~]# source /etc/profile        # 立即在当前shell中生效 +``` + +* 通过umask决定新建用户`HOME`目录的权限 + +```shell +[root@localhost ~]# vim /etc/login.defs +----------------- + 61 # The permission mask is initialized to this value. If not specified, + 62 # the permission mask will be initialized to 022. + 63 UMASK 077 +------------------ +``` + diff --git a/01.Linux基础/07.文件权限管理/文件ugo权限解释.png b/01.Linux基础/07.文件权限管理/文件ugo权限解释.png new file mode 100644 index 0000000..4b1c755 Binary files /dev/null and b/01.Linux基础/07.文件权限管理/文件ugo权限解释.png differ diff --git a/01.Linux基础/07.文件权限管理/文件权限介绍.png b/01.Linux基础/07.文件权限管理/文件权限介绍.png new file mode 100644 index 0000000..f8f5b8a Binary files /dev/null and b/01.Linux基础/07.文件权限管理/文件权限介绍.png differ diff --git a/01.Linux基础/08.进程管理.md b/01.Linux基础/08.进程管理.md new file mode 100644 index 0000000..ff7b8d2 --- /dev/null +++ b/01.Linux基础/08.进程管理.md @@ -0,0 +1,680 @@ +# 1. 进程 + +进程是一个在系统中运行的程序 + +进程是已启动的可执行程序的运行实例,进程有以下组成部分 + +* 已分配内存的地址空间 +* 安全属性,包括所有权凭据和特权 +* 进程代码的一个或多个执行线程 +* 进程状态 + +程序:二进制文件,是静态的 + +`/bin/date`,`/usr/sbin/httpd`,`/usr/sbin/sshd`,`/usr/local/nginx/sbin/ngix` + +进程:是程序运行的过程,动态的,有生命周期及运行状态 + +# 2. 进程类型 + +守护进程:在系统引导过程中启动的进程,跟终端无关的进程 + +前台进程:跟终端相关,通过终端启动的进程 + +# 3. 进程的生命周期 + +![img](08.进程管理/WPYNtDaMQ3fkYeQs.png!thumbnail) + +父进程复制自己的地址空间(fork)创建一个新的(子)进程结构。每个新进程分配一个唯一的进程ID(PID),满足跟踪安全性之需。PID和父进程ID(PPID)是子进程环境的元素,任何进程都可以创建子进程,所有进程都是第一个系统进程的后代。 + +# 4. systemd简介 + +首先 systmed 是一个用户空间的程序,属于应用程序,不属于 Linux 内核范畴。Systemd 是 Linux 系统中最新的初始化系统(init),它主要的设计目标是克服 sysvinit(这个是centos6中的初始化系统)固有的缺点,提高系统的启动速度。 + +Linux内核加载启动后,用户空间的第一个进程就是初始化进程,这个程序的物理文件约定位于`/sbin/init`,当然也可以通过传递内核参数来让内核启动指定的程序。这个进程的特点是进程号为1,代表第一个运行的用户空间进程。不同发行版采用了不同的启动程序,主要有以下几种主流选择: + +* 以 Ubuntu 为代表的 Linux 发行版采用 upstart +* 红帽系列发行版本中 + - 红帽6以前的版本使用System V init + - 红帽7及以后,使用systemd + +RockyLinux上所有的进程都是systemd的后代,systemd的功能繁多,不仅用来管理服务,还可以管理挂载点,定义定时任务等。这些工作都是由编辑相应的配置单元文件完成的。 + +## 4.1 systemd unit 类型 + +systemd需要管理的功能比较多,所以支持的配置单元类型也比较繁多,我们在日常使用Linux的过程中对系统服务的管理最多,所以我们主要了解一下`service`类型即可,其他类型作为一个了解,下面列举出所有类型详细的解释。 + +| **单元类型** | **文件格式** | **描述** | +| :------------- | :----------- | :----------------------------------------------------------- | +| Service unit | .service | 服务类 | +| Target unit | .target | 一个 unit 服务组,用于模拟实现运行级别 | +| Automount unit | .automount | 文件系统自动挂载点 | +| Device unit | .device | 内核识别的设备文件 | +| Mount unit | .mount | 文件系统挂载点 | +| Path unit | .path | 文件或目录 | +| Scope unit | .scope | 外部创建的进程 | +| Slice unit | .slice | A group of hierarchically organized units that manage system processes. | +| Snapshot unit | .snapshot | 系统快照 | +| Socket unit | .socket | 套接字 | +| Swap unit | .swap | 标识 swap 设备 | +| Timer unit | .timer | systemd 的计时器 | + +## 4.2 unit 文件保存位置 + +我们目前了解到文件所在的位置即可,关于文件内部的格式与如何修改这些文件,我们会在后续的服务搭建过程中细讲。 + +| **目录** | **描述** | +| :----------------------- | :-------------------------------- | +| /usr/lib/systemd/system/ | RPM 包安装时分发的 unit 文件 | +| /run/systemd/system/ | systemd 运行时创建的文件 | +| /etc/systemd/system/ | systemctl enable 创建的 unit 文件 | + +# 5. 管理系统服务 + +systemd 的主要命令行工具是`systemctl` + +```shell +systemctl [选项...] {命令} ... +``` + +## 5.1 systemctl常用命令 + +这边列举出来的命令会在后面的学习过程中经常用到,所以大家只要对本部分中系统管理的命令合集有一个影响即可。 + +| **命令** | **描述** | +| :----------------------------------------------------------- | :------------------------------------- | +| systemctl start name.service | 启动服务 | +| systemctl stop name.service | 停止服务 | +| systemctl restart name.service | 重启服务(没启动的服务会启动) | +| systemctl try-restart name.service | 只重启正在运行的服务 | +| systemctl reload name.service | 重载配置文件 | +| systemctl status name.service systemctl is-active name.service | 检查服务状态检查服务是否启动 | +| systemctl list-units --type service --all | 显示所有的服务状态 | +| systemctl enable name.service | 启用开机自启服务 | +| systemctl disable name.service | 停用自启服务 | +| systemctl status name.service systemctl is-enabled name.service | 检查服务状态查看服务是否自启 | +| systemctl list-unit-files --type service | 查看所有服务 | +| systemctl list-dependencies --after | 列出在指定服务之前启动的服务(依赖) | +| systemctl list-dependencies --before | 列出在指定服务之后启动的服务(被依赖) | + +# 6. ps + +ps命令用于显示当前进程的状态,类似于 windows 的任务管理器 + +```shell +ps [选项] +``` + +## 6.1 选项 + +* **-a**:列出所有的进程 +* **-e**:列出所有的进程,等同于`-A` +* **-f**:显示不包含资源使用率的相关信息 +* **‐H**:以进程层级格式显示进程相关信息 +* **-w**:显示加宽可以显示较多的信息 +* **-u**:显示较详细的信息 +* **-x**:显示其他使用者的进程 + +显示信息的格式说明 + +| **列名** | **说明** | +| :------- | :----------------- | +| USER | 进程拥有者 | +| PID | 进程ID | +| %CPU | 占用的 CPU 使用率 | +| %MEM | 占用的内存使用率 | +| VSZ | 占用的虚拟内存大小 | +| RSS | 占用的常驻内存大小 | +| TTY | 执行的终端编号 | +| STAT | 该进程的状态* | +| START | 进程开始时间 | +| TIME | CPU使用时间 | +| COMMAND | 所执行的命令 | + +*STAT表示的进程状态有如下几种: + +* **D**: 无法中断的休眠状态 ,将一直等待事件的发生或等待某种系统资源 +* **R**: 正在执行中 +* **S**: 可中断状态 +* **T**: 暂停执行 +* **Z**: 不存在但暂时无法消除,也叫僵尸进程 + * 每个进程在运行结束后都会处于僵死状态,等待父进程调用进而释放系统资源,处于该状态的进程已经运行结束,但是它的父进程还没有释放其系统资源 + * 孤儿进程: +* **W**: 没有足够的内存可分配 +* **<**: 高优先序的进程 +* **N**: 低优先序的进程 +* **+**:前台进程 +* **l**:多线程进程 +* **s**:主进程(先导进程) + +## 6.2 实例 + +**常用组合:ps aux** + +```bash +u:以用户为中心组织进程状态信息显示 +a:与终端相关的进程; +x:与终端无关的进程; +[root@localhost ~]# ps aux | head -n 5 +USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND +root 1 0.0 0.3 128144 6656 ? Ss 5月10 0:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 22 +root 2 0.0 0.0 0 0 ? S 5月10 0:00 [kthreadd] +root 3 0.0 0.0 0 0 ? S 5月10 0:00 [ksoftirqd/0] +root 5 0.0 0.0 0 0 ? S< 5月10 0:00 [kworker/0:0H] +``` + +**常用组合:ps -aux** + +```sh +-a: 列出所有的进程 +-u: 显示较详细的信息 +-x: 显示其他使用者的进程 +[root@localhost ~]# ps -aux +USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND +root 1 0.1 0.9 172972 16128 ? Ss 20:38 0:01 /usr/lib/sys +root 2 0.0 0.0 0 0 ? S 20:38 0:00 [kthreadd] +root 3 0.0 0.0 0 0 ? I< 20:38 0:00 [rcu_gp] +root 4 0.0 0.0 0 0 ? I< 20:38 0:00 [rcu_par_gp] +root 5 0.0 0.0 0 0 ? I< 20:38 0:00 [slub_flushw +root 6 0.0 0.0 0 0 ? I< 20:38 0:00 [netns] +``` + +**ps aux和ps -aux两个命令的区别:** + +`ps aux` 是一种 **BSD 风格** 的 `ps` 命令,它不需要破折号(`-`)作为选项的前缀。 + +- **`a`**:显示所有用户的进程,包括其他用户的进程(不仅限于当前用户)。 +- **`u`**:以用户为中心的格式显示进程信息,提供更多详细信息,如用户名、CPU 和内存使用率等。 +- **`x`**:显示没有控制终端(TTY)的后台进程。 + +`ps -aux` 与 `ps aux` 看起来很相似,但它们有不同的含义。 + +- 这里的 `-` 表示使用的是 **UNIX 标准** 或 **POSIX 标准** 的 `ps` 选项。 +- 作为 POSIX 风格,`-a`、`-u`、`-x` 的含义会有所不同,甚至可能与 BSD 风格冲突。 + +**常用组合:ps -ef** + +```bash +-e:显示所有进程 +-f:显示完整格式程序信息 +[root@localhost ~]# ps -ef |head -n 10 +UID PID PPID C STIME TTY TIME CMD +root 1 0 0 5月10 ? 00:00:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 22 +root 2 0 0 5月10 ? 00:00:00 [kthreadd] +root 3 2 0 5月10 ? 00:00:00 [ksoftirqd/0] +root 5 2 0 5月10 ? 00:00:00 [kworker/0:0H] +root 6 2 0 5月10 ? 00:00:00 [kworker/u256:0] +root 7 2 0 5月10 ? 00:00:00 [migration/0] +root 8 2 0 5月10 ? 00:00:00 [rcu_bh] +root 9 2 0 5月10 ? 00:00:01 [rcu_sched] +root 10 2 0 5月10 ? 00:00:00 [lru-add-drain] + +``` + +**常用组合:ps -efH** + +```shell +`查看进程以层级格式(类似于pstree命令)` +[root@localhost ~]# ps -efH +root 1 0 0 14:26 ? 00:00:00 /usr/lib/systemd/systemd --switched-root --system --deserialize 22 +root 479 1 0 14:26 ? 00:00:00 /usr/lib/systemd/systemd-journald +root 500 1 0 14:26 ? 00:00:00 /usr/sbin/lvmetad -f +root 507 1 0 14:26 ? 00:00:00 /usr/lib/systemd/systemd-udevd +root 615 1 0 14:27 ? 00:00:00 /sbin/auditd +polkitd 638 1 0 14:27 ? 00:00:00 /usr/lib/polkit-1/polkitd --no-debug +dbus 639 1 0 14:27 ? 00:00:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation +root 646 1 0 14:27 ? 00:00:00 /usr/sbin/NetworkManager --no-daemon +root 649 1 0 14:27 ? 00:00:00 /usr/sbin/irqbalance --foreground +root 653 1 0 14:27 ? 00:00:00 /usr/lib/systemd/systemd-logind +root 655 1 0 14:27 ? 00:00:00 /usr/sbin/crond -n +chrony 661 1 0 14:27 ? 00:00:00 /usr/sbin/chronyd +root 662 1 0 14:27 ? 00:00:00 login -- root +``` + +**按照CPU使用率排序查看所有进程** + +```shell +[root@localhost ~]# ps -aux --sort %cpu # 递增 +[root@localhost ~]# ps -aux --sort -%cpu # 递减 +``` + +**按照实际内存使用排序查看所有进程** + +```shell +[root@localhost ~]# ps -aux --sort rss # 递增 +[root@localhost ~]# ps -aux --sort -rss # 递减 +``` + +**按照父子进程显示ssh服务的层级关系** + +```shell +[root@localhost ~]# ps -auxf |grep sshd +# ps -efH +root       6814  0.0  0.2 112756  4320 ?        Ss   09:06   0:00 /usr/sbin/sshd -D +root       7097  0.0  0.2 158760  5576 ?        Ss   09:06   0:00  \_ sshd: root@pts/0 +root       7337  0.1  0.2 158760  5588 ?        Ss   10:21   0:00  \_ sshd: root@pts/1 +root       7364  0.0  0.0 112724   988 pts/1    S+   10:24   0:00          \_ grep --color=auto sshd +``` + +**自定义显示格式** + +```shell +[root@localhost ~]# ps -axo user,pid,ppid,%mem,%cpu,command --sort -%cpu +``` + +**查看指定进程的PID,多种查看的方式** + +```shell +[root@localhost ~]# cat /run/sshd.pid  +[root@localhost ~]# ps -aux |grep sshd +[root@localhost ~]# pgrep -l sshd +[root@localhost ~]# pidof sshd +``` + +**查看进程树** + +```shell +[root@localhost ~]# yum install -y psmisc +[root@localhost ~]# pstree +systemd─┬─NetworkManager───2*[{NetworkManager}] + ├─agetty + ├─atd + ├─auditd─┬─sedispatch + │ └─2*[{auditd}] + ├─bluetoothd + ├─chronyd + ├─crond + ├─dbus-broker-lau───dbus-broker + ├─firewalld───{firewalld} + ├─irqbalance───{irqbalance} + ├─lsmd + ├─mcelog + ├─polkitd───7*[{polkitd}] + ├─rsyslogd───2*[{rsyslogd}] + ├─sshd───sshd───sshd───bash───bash───pstree + ├─systemd───(sd-pam) + ├─systemd-journal + ├─systemd-logind + ├─systemd-udevd + └─tuned───3*[{tuned}] +``` + +# 7. top + +top命令用于实时显示 process 的动态 + +## 7.1 示例 + +```shell +[root@localhost ~]# top +``` + +第1行:系统时间、运行时间、登录终端数、系统负载(三个数值分别为1分钟、5分钟、15分钟内的平 均值,数值越小意味着负载越低)。 + +第2行:进程总数、运行中的进程数、睡眠中的进程数、停止的进程数、僵死的进程数。 + +第3行:用户占用资源百分比、系统内核占用资源百分比、改变过优先级的进程资源百分比、空闲的资源百分比等。 + +第4行:物理内存总量、内存使用量、内存空闲量、作为内核缓存的内存量。(buffer和cache之间的区 别,cache是提高cpu和内存之间的数据交换速度,buffer是io设备和存储设备之间的缓冲区) + +第5行:虚拟内存总量、虚拟内存使用量、虚拟内存空闲量、已被提前加载的内存量。 + +## 7.2 选项 + +* **-d**: 改变显示的更新速度,或是在交互式指令列( interactive command)按 s +* **-c**: 切换显示模式,共有两种模式,一是只显示程序的名称,另一种是显示完整的路径与名称 +* **-S**: 累积模式,会将己完成或消失的子行程 ( dead child process ) 的 CPU time 累积起来 +* **-s**: 安全模式,将交互式指令取消, 避免潜在的危机 +* **-i**: 不显示任何闲置 (idle) 或无用 (zombie) 的行程 +* **-n**: 更新的次数,完成后将会退出 top +* **-b**: 显示模式,搭配 "n" 参数一起使用,可以用来将 top 的结果输出到文件内 +* **-z:**彩色 + +## 7.3 交互模式快捷键 + +| 快捷键 | 功能 | +| :----- | :------------------------------------------------------- | +| 空格 | 立即刷新 | +| P | 根据CPU使用多少排序 | +| T | 根据时间、累计排序 | +| q | 退出top命令 | +| m | 切换显示内存信息 | +| t | 切换显示进程和CPU状态信息 | +| c | 切换显示命令名称和完整命令行 | +| M | 根据内存的大小排序 | +| W | 将当前设置写入 ~/.toprc  文件中,这是top配置文件推荐方法 | +| N | 以PID的大小排序 | +| z | 彩色 | + +## 7.4 实例 + +* 显示进程信息,每个1秒钟刷新一次 + +```shell +[root@localhost ~]# top -d 1 +``` + +* 显示完整命令 + +```shell +[root@localhost ~]# top -c +``` + +* 更新两次后终止更新显示 + +```shell +[root@localhost ~]# top -n 2 +``` + +* 显示指定的进程信息 + +```shell +[root@localhost ~]# top -p 7097 +``` + +- 查看指定用户的进程 + +```sh +[root@localhost ~]# top -d 1 -u user01 +``` + +- 将2次top信息写入到文件 + +```sh +[root@localhost ~]# top -d 1 -b -n 2 > top.txt +``` + +# 8. kill + +kill 命令用于删除执行中的程序或工作 + +kill 可将指定的信号送至程序。预设的信号为 SIGTERM(15),可将指定程序终止 + +```shell +kill [-s <信息名称或编号>][程序] 或 kill [-l <信息编号>][程序] +``` + +## 8.1 选项 + +* **-l <信号编号>**: 若不加<信号编号>选项,则 -l 参数会列出全部的信号名称 +* **-s <信号名称或编号>**:指定要送出的信息 +* **[程序]**:[程序]可以是程序的PID或是PGID + +使用 kill -l 命令列出所有可用信号,最常用的信号如下 + +| **编号** | **信号名** | **作用** | +| :------- | :--------- | :------------------------------------------- | +| 1 | SIGHUP | 重新加载配置 | +| 2 | SIGINT | 通过键盘 `ctrl+c` 打印捕获信息,程序继续运行 | +| 3 | SIGQUIT | 通过键盘 `ctrl+\` 打印捕获信息,程序优雅退出 | +| 9 | SIGKILL | 强制终止 | +| 15 | SIGTERM | 终止(正常结束) | +| 18 | SIGCONT | 继续 | +| 19 | SIGSTOP | 停止 | +| 20 | SIGTSTP | 暂停`ctrl z` | + +## 8.2 实例 + +* 杀死所有的ping命令,在linux命令中,使用反引号`包含的命令会优先执行,并将执行的结果作为参数提供给命令。 + +```shell +[root@localhost ~]# kill `pgrep ping` +``` + +* 强制杀死进程 + +```shell +[root@localhost ~]# kill -9 `pgrep ping` +``` + +* 温柔的杀死进程 + +```bash +[root@localhost ~]# kill -15 `pgrep ping` +``` + +# 9. pkill + +pkill 用于杀死一个进程,与 kill 不同的是它会杀死指定名字的所有进程 + +```shell +pkill [选项] name +``` + +## 9.1 选项 + +* **name**: 进程名 +* **-u**:指定用户名 +* **-t**:指定终端 + +## 9.2 实例 + +* 结束所有的sshd进程 + +```shell +[root@localhost ~]# pkill sshd +``` + +* 结束用户user1的所有进程 + +```shell +[root@localhost ~]# pkill -u user1 +``` + +* 终止pts/2上所有进程 + +```shell +[root@localhost ~]# pkill -t pts/2 +``` + +* 终止pts/2上所有进程,并结束pts/2 + +```shell +[root@localhost ~]# pkill -9 -t pts/1 +``` + +* 查看远程登录用户,并踢出用户 + +```shell +[root@localhost ~]# w + 15:02:26 up  5:21,  2 users,  load average: 0.05, 0.03, 0.05 +USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT +root     tty1                      14:41   20:34   0.02s  0.02s -bash +root     pts/0    192.168.175.1    14:42    2.00s  0.05s  0.01s w +user1    pts/1   192.168.175.1     14:40   20:34   0.02s  0.02s -bash +[root@localhost ~]# pkill -u user1 +``` + +# 10. 进程优先级 nice + +## 10.1 Linux进程调度及多任务 + +每个CPU(或CPU核心)在一个时间点上只能处理一个进程,通过时间片技术,Linux实际能够运行的进程 (和线程数)可以超出实际可用的CPU及核心数量。Linux内核进程调度程序将多个进程在CPU核心上快速切换,从而给用户多个进程在同时运行的假象。(并发) + + ### 相对优先级 + +由于不是每个进程都与其他进程同样重要,可告知进程调度程序为不同的进程使用不同的调度策略。常规系统上运行的大多数进程所使用的的调度策略为SCHED_OTHER(也称为SCHED_NORMAL),但还有其他 一些调度策略用于不同的目的。SCHED_OTHER调度策略运行的进程的相对优先级称为进程的nice值, 可以有40种不同级别的nice值。 + +在Linux中,决定一个进程的优先级的因素有两个,一个是PR值,一个是NICE值,PR值是由系统内核动态调控。而NICE值由我们用户自己决定。不过,PR值跟NICE值之间也是有一定的关联的。 + +**NICE值的范围:**-20到19,数值越小,优先级越高 + +**PR值的范围:**-99到39,其中0-39是与**NICE值**有着一定的对应关系 + +进程默认启动时nice为0,优先级为20 + +![img](08.进程管理/进程优先级.png) + +nice值越高表示优先级越低,例如19,该进程容易将CPU使用量让给其他进程 + +nice值越低表示优先级越高,例如-20,该进程更倾向于不让出CPU + +### 10.1.1 查看进程的nice级别 + +#### 10.1.1.1 使用ps查看 + +```bash +[root@localhost ~]# ps -axo pid,command,nice --sort=nice +[root@localhost ~]# ps -axo pid,command,nice,cls --sort=-nice +``` + +TS表示该进程使用的调度策略为SCHED_OTHER + +#### 10.1.1.2 使用top查看 + +NI:实际nice级别 + +PR:将nice级别显示为映射到更大优先级队列,-20映射到0,+19映射到39 + +#### 10.1.1.3 启动具有不同nice级别的进程 + +启动进程的时候为进程指定nice值 + +启动进程时,通常会继承父进程的nice级别,默认为0 + +```sh +[root@localhost ~]# nice -n -20 vim +[root@localhost ~]# ps -axo command,pid,nice |grep vim +``` + +## 10.2 PRI + +在top命令中可以看到有PR这个数值,PR 和 nice 值,都会影响进程执行的优先级。PR 由 OS 内核动态调整,用户不能调整(PR 值越低,进程执行的优先级越高)。 + +nice值用户可以自己调整,在用户调整了nice值后系统会通过如下公式来调整新的PR值,从而确定这个进程在系统中的优先级 + +```shell +PR(新) = PR(旧) + nice +``` + +PR值是OS动态调整的,但是PR的最终值还是需要由OS分析决定的 + +#### 10.2.1 更改现有进程的nice级别 + +调整进程的优先级(Nice Level) (-20高) - - - 0 - - - (19低) + +- 使用shell更改nice级别 + +```bash +[root@localhost ~]# vim +[root@localhost ~]# ps -axo command,pid,nice |grep vim +vim 1855 0 +grep --color=auto vim 1888 0 +[root@localhost ~]# renice -20 1855 +1855 (process ID) old priority 0, new priority -20 +[root@localhost ~]# ps -axo command,pid,nice |grep vim +vim 1855 -20 +grep --color=auto vim 1895 0 +``` + +# 11. jobs + +jobs 命令可以用来查看当前终端放入后台的任务 + +```shell +jobs [-lnprs] [任务声明 ...] +``` + +## 11.1 将任务放入到后台 + +Linux 命令放入后台的方法有两种: + +* 在命令后面加入`空格 &`。使用这种方法放入后台的命令,在后台处于执行状态 +* 命令执行过裎中按 Ctrl+Z 快捷键,命令在后台处于暂停状态 + +### 11.1.1 实例 + +* 将任务放入后台,然后查看任务 + * "+"号代表最近一个放入后台的工作,也是工作恢复时默认恢复的工作 + * "-"号代表倒数第二个放入后台的工作 + +```shell +[root@localhost ~]# top & +[root@localhost ~]# vi & +[root@localhost ~]# ping baidu.com > /dev/null & +# 让ping运行,但是不显示结果 +[root@localhost ~]# jobs +[1]   已停止               top +[2]-  已停止               vi +[3]+  运行中               ping baidu.com > /dev/null & +``` + +## 11.2 将任务恢复到前台 + +fg 命令用于把后台工作恢复到前台执行 + +```shell +fg %工作号 +``` + +注意,在使用此命令时,% 可以省略,但若将% 工作号全部省略,则此命令会将带有 + 号的工作恢复到前台。另外,使用此命令的过程中, % 可有可无。 + +### 11.2.1 实例 + +* 将top恢复到前台 + * 命令虽然是对的,但是top这种需要交互的任务是无法后台的,所以也恢复不了 + +```shell +[root@localhost ~]# top & +[1] 20584 +[root@localhost ~]# jobs +[1]+  已停止               top +[root@localhost ~]# fg 1 +``` + +## 11.3 后台任务恢复到后台运行 + +前面讲过,使用`Ctrl+z`快捷键的方式,可以将前台工作放入后台,但是会处于暂停状态,可以使用bg命令 + +```shell +bg %工作号 +``` + +这里的 % 同上,可以省略 + +### 11.3.1 实例 + +* 将ping命令暂停到后台,然后恢复后台运行 + +```shell +[root@localhost ~]# ping baidu.com > /dev/null +^Z # 这边按下了Ctrl+z +[1]+  已停止               ping baidu.com > /dev/null +[root@localhost ~]# jobs +[1]+  已停止               ping baidu.com > /dev/null +[root@localhost ~]# bg %1 +[1]+ ping baidu.com > /dev/null & +[root@localhost ~]# jobs +[1]+  运行中               ping baidu.com > /dev/null & +``` + +# 12. nohup + +虽然可以将程序放在后台运行,但是一旦关闭远程连接那么程序就会中断,如果我们想要将程序一直保持在后台运行,那么我们可以有如下三个选择: + +* 把需要在后台执行的命令加入`/etc/rc.local`文件,让系统在启动时执行这个后台程序。这种方法的问题是,服务器是不能随便重启的,如果有临时后台任务,就不能执行了 +* 使用系统定时任务,让系统在指定的时间执行某个后台命令。这样放入后台的命令与终端无关,是不依赖登录终端的 +* 使用 nohup 命令 + +```shell +nohup 命令 & +``` + +注意,这里的`&`表示此命令会在终端后台工作;反之,如果没有`&`,则表示此命令会在终端前台工作。 + +## 12.1 实例 + +* 让一个任务后台运行 + +```shell +[root@localhost ~]# nohup ping baidu.com & +``` + +我们退出远程连接,然后重新登录回来,会看到`ping baidu.com`依旧在运行,查看`~/nohup.out`文件可以看到程序执行输出的内容 + +```shell +[root@localhost ~]# ps aux |grep ping +root       7157  0.0  0.0 149968  1988 ?        S    14:12   0:00 ping baidu.com +[root@localhost ~]# tail -f nohup.out +``` + diff --git a/01.Linux基础/08.进程管理/WPYNtDaMQ3fkYeQs.png!thumbnail b/01.Linux基础/08.进程管理/WPYNtDaMQ3fkYeQs.png!thumbnail new file mode 100644 index 0000000..b5a0857 Binary files /dev/null and b/01.Linux基础/08.进程管理/WPYNtDaMQ3fkYeQs.png!thumbnail differ diff --git a/01.Linux基础/08.进程管理/进程优先级.png b/01.Linux基础/08.进程管理/进程优先级.png new file mode 100644 index 0000000..b41ba86 Binary files /dev/null and b/01.Linux基础/08.进程管理/进程优先级.png differ diff --git a/01.Linux基础/09.存储管理.md b/01.Linux基础/09.存储管理.md new file mode 100644 index 0000000..20afba8 --- /dev/null +++ b/01.Linux基础/09.存储管理.md @@ -0,0 +1,948 @@ +我们在计算机中保存的文件大都是保存在电脑硬盘中,部分同学还会将自己的文件存储在网盘中,网盘中的文件只能说是用来做备份,并不能像本地硬盘上的数据那样随时使用,我们在学习存储管理的时候主要学习的就是如何对自己计算机上的硬盘进行管理,目前我们主流的硬盘主要有两种形态,一种是及机械硬盘(HDD),一种是的固态硬盘(SSD) + +# 1. 硬盘工作原理 + +## 1.1 机械硬盘HDD + +机械硬盘是靠机械马达以固定的速度转动磁盘,然后再有机械臂的摆动,让磁头可以读取盘片上的磁性信息,这种工作原理的硬盘我们成为机械硬盘。 + +![img](09.存储管理/机械硬盘hdd.png) + +由于机械硬盘是依靠机械的运动来读写数据的,大家都知道机械在长时间运转的过程中是会产生一定的损耗,所以机械硬盘虽然理论上寿命很长,但随着时间的变长,机械磨损变大,导致磁头定位的误差变大,从而影响到硬盘的速度,这个就是为什么你的老电脑开机缓慢,运行卡顿的原因了。 + +## 1.2 固态硬盘SSD + +固态硬盘依靠主控芯片来将数据的二进制信息写入浮栅晶体管中,以这样的原理实现数据的读取和写入。 + +![img](09.存储管理/固态硬盘ssd.png) + +固态硬盘在读取和写入数据的时候完全是依靠数字电路实现的,所以并没有机械硬盘的机械磨损,而且也没有在磁盘上定位读写的延迟,所以固态硬盘的数据读写效率是远远高于机械硬盘的,但是也有一定的缺点,由于浮栅晶体管的设计原理,每次数据的读写,都会让浮栅晶体管所需要的电压更高,一定寿命以后,固态硬盘就会失效。并且固态硬盘如果发生了损坏,数据恢复的难度是大于机械硬盘的,所以为了可靠性,企业目前还是在大量使用机械硬盘的。 + +固态硬盘设计1 + +固态硬盘设计2 + +固态硬盘设计3 + +# 2. 硬盘设备命名 + +一台服务器往往会配备有多块硬盘,Linux中为了区分不同的硬盘,会使用不同的命名来将硬盘设备标识,Linux系统对物理磁盘设备会使用`sd[a-z]`的方式来命名。物理硬盘上的详细分区会使用数字的方式来进行标识,比如`sda`设备上的多个分区会标识为`sda[1,2,3....]` + +# 3. 分区表 + +为了让系统能够识别和管理物理设备上的不同分区,需要在物理设备前加上一些数据用于记录分区的情况,这些数据就是分区表数据,目前分区表有两种主流的格式: + +* MBR + * 管理的硬盘大小不能超过2T + * 有主分区和扩展分区两种,不管是哪种分区,总数不能超过4个分区 + * 扩展分区内部可以逻辑上划分多个区域,但是在分区表中都算在一个分区内 + * 如果作为系统盘,必须要设置一个激活分区,用于存放系统的引导文件 + +![MBR分区表](09.存储管理/MBR分区表.png) + +* GPT + * 管理硬盘大小不能超过18EB,目前人类还没制造出单块这么大的硬盘 + * 理论上支持无限分区,在win10系统上,最多可以分128个分区 + * 如果作为系统盘,**必须要创建一个EFI分区,用于存放系统的引导文件**建议ESP分区512M,对齐8扇区4096字节 + +![gpt分区表](09.存储管理/gpt分区表.png) + +![分区演示](09.存储管理/分区演示.png) + +电脑启动的时候: + +MBR分区表会找到激活分区,从里面加载系统启动; + +GPT分区表会查找EFI分区,没有是开不了机的,所以GPT分区表情况下需要建立ESP,MSR分区。 + +MSR是微软保留的引导分区,分区一般不会坏因此一般不需要。 + +ESP建立512MB,对齐到扇区整数倍是指一次读写几个扇区(例如新建个txt只写一个字符,依旧占用4K,除非越写越大超过4K,占4K的整数倍),并不是越大越好也不是越小越好,根据实际需求,一般是4KB。 + +# 4. 管理分区 + +- 列出块设备 + +```bash +[root@localhost ~]# lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT +sda 8:0 0 50G 0 disk +├─sda1 8:1 0 1G 0 part /boot +└─sda2 8:2 0 49G 0 part + ├─centos-root 253:0 0 47G 0 lvm / + └─centos-swap 253:1 0 2G 0 lvm [SWAP] +sdb 8:16 0 20G 0 disk +sdc 8:32 0 20G 0 disk +sr0 11:0 1 1024M 0 rom +``` + +- 创建分区命令 + +```bash +fdisk 管理MBR分区 +gdisk 管理GPT分区 +parted 高级分区操作 +``` + +- 重新设置内存中的内核分区表版本 + +```bash +partprobe +``` + +## 4.1 parted 命令 + +- parted的操作都是实时生效的,小心使用 +- 格式: + +```bash +parted [选项]... [设备 [命令 [参数]...]...] +``` + +- 操作命令: + +```bash +cp [FROM-DEVICE] FROM-MINOR TO-MINOR #将文件系统复制到另一个分区 +help [COMMAND] #打印通用求助信息,或关于 COMMAND 的信息 +mklabel 标签类型 #创建新的磁盘标签 (分区表) +mkfs MINOR 文件系统类型 #在 MINOR 创建类型为“文件系统类型”的文件系统 +mkpart 分区类型 [文件系统类型] 起始点 终止点 #创建一个分区 +mkpartfs 分区类型 文件系统类型 起始点 终止点 #创建一个带有文件系统的分区 +move MINOR 起始点 终止点 #移动编号为 MINOR 的分区 +name MINOR 名称 #将编号为 MINOR 的分区命名为“名称” +print [MINOR] #打印分区表,或者分区 +quit #退出程序 +rescue 起始点 终止点 #挽救临近“起始点”、“终止点”的遗失的分区 +resize MINOR 起始点 终止点 #改变位于编号为 MINOR 的分区中文件系统的大小 +rm MINOR #删除编号为 MINOR 的分区 +select 设备 #选择要编辑的设备 +set MINOR 标志 状态 #改变编号为 MINOR 的分区的标志 +``` + +- 查看分区情况 + +```bash +[root@localhost ~]# parted /dev/nvme0n1 print +Model: VMware Virtual NVMe Disk (nvme) +Disk /dev/nvme0n1: 21.5GB +Sector size (logical/physical): 512B/512B +Partition Table: msdos +Disk Flags: + +Number Start End Size Type File system Flags + 1 1049kB 1075MB 1074MB primary xfs boot + 2 1075MB 21.5GB 20.4GB primary lvm + +[root@localhost ~]# parted /dev/nvme0n2 print +Error: /dev/nvme0n2: unrecognised disk label +Model: VMware Virtual NVMe Disk (nvme) +Disk /dev/nvme0n2: 5369MB +Sector size (logical/physical): 512B/512B +Partition Table: unknown +Disk Flags: +``` + +- 设置磁盘的分区表 + +```bash +# [root@localhost ~]# parted /dev/nvme0n2 mklabel msdos +[root@localhost ~]# parted /dev/nvme0n2 mklabel gpt +Information: You may need to update /etc/fstab. + +[root@localhost ~]# parted /dev/nvme0n2 print +Model: VMware Virtual NVMe Disk (nvme) +Disk /dev/nvme0n2: 5369MB +Sector size (logical/physical): 512B/512B +Partition Table: gpt +Disk Flags: + +Number Start End Size File system Name Flags +# 由于没有分区,所有没有具体的分区信息 +``` + +- 对磁盘进行分区 + +```bash +[root@localhost ~]# parted /dev/nvme0n2 mkpart primary 1 1G +# 创建1个G大小的分区 +Information: You may need to update /etc/fstab. +[root@localhost ~]# parted /dev/nvme0n2 print +Model: VMware Virtual NVMe Disk (nvme) +Disk /dev/nvme0n2: 5369MB +Sector size (logical/physical): 512B/512B +Partition Table: gpt +Disk Flags: + +Number Start End Size File system Name Flags + 1 1049kB 1000MB 999MB primary + +``` + +- 删除分区 + +```bash +[root@localhost ~]# parted /dev/nvme0n2 rm 1 +Information: You may need to update /etc/fstab. + +[root@localhost ~]# parted /dev/nvme0n2 print +Model: VMware Virtual NVMe Disk (nvme) +Disk /dev/nvme0n2: 5369MB +Sector size (logical/physical): 512B/512B +Partition Table: gpt +Disk Flags: + +Number Start End Size File system Name Flags +``` + +- 修改磁盘为mbr分区,注意会丢失所有数据 + +```bash +[root@localhost ~]# parted /dev/nvme0n2 mklabel msdos +警告: The existing disk label on /dev/nvme0n2 will be destroyed and all data on this disk will be lost. Do +you want to continue? +是/Yes/否/No? yes +信息: You may need to update /etc/fstab. +``` + +## 4.2 fdisk工具 + +管理磁盘中MBR分区 + +`fdisk [磁盘名称]` + +fdisk命令中的参数以及作用 + +| 参数 | 作用 | +| ---- | ---------------------- | +| m | 查看全部可用的参数 | +| n | 添加新的分区 | +| d | 删除某个分区信息 | +| l | 列出所有可用的分区类型 | +| t | 改变某个分区的类型 | +| p | 查看分区信息 | +| w | 保存并退出 | +| q | 不保存直接退出 | + +![分区操作.gif](09.存储管理/分区操作.gif) + +```bash +[root@localhost ~]# fdisk /dev/nvme0n2 + +Welcome to fdisk (util-linux 2.37.4). +Changes will remain in memory only, until you decide to write them. +Be careful before using the write command. + +Device does not contain a recognized partition table. +Created a new DOS disklabel with disk identifier 0x447f44d1. + +Command (m for help): n +Partition type + p primary (0 primary, 0 extended, 4 free) + e extended (container for logical partitions) +Select (default p): p +Partition number (1-4, default 1): +First sector (2048-10485759, default 2048): +Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-10485759, default 10485759): +1G + +Created a new partition 1 of type 'Linux' and of size 1 GiB. + +Command (m for help): p +Disk /dev/nvme0n2: 5 GiB, 5368709120 bytes, 10485760 sectors +Disk model: VMware Virtual NVMe Disk +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes +Disklabel type: dos +Disk identifier: 0x447f44d1 + +Device Boot Start End Sectors Size Id Type +/dev/nvme0n2p1 2048 2099199 2097152 1G 83 Linux + +Command (m for help): w +The partition table has been altered. +Calling ioctl() to re-read partition table. +Syncing disks. + +[root@localhost ~]# lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS +sr0 11:0 1 1.7G 0 rom +nvme0n1 259:0 0 20G 0 disk +├─nvme0n1p1 259:1 0 1G 0 part /boot +└─nvme0n1p2 259:2 0 19G 0 part + ├─rl-root 253:0 0 17G 0 lvm / + └─rl-swap 253:1 0 2G 0 lvm [SWAP] +nvme0n2 259:3 0 5G 0 disk +└─nvme0n2p1 259:8 0 1G 0 part +nvme0n3 259:4 0 5G 0 disk +nvme0n4 259:5 0 5G 0 disk +nvme0n5 259:6 0 5G 0 disk +nvme0n6 259:7 0 5G 0 disk +``` + +## 4.3 gdisk工具 + +管理磁盘中的GPT分区 + +使用方式和选项与fdisk中几乎一致 + +# 5. 存储结构与磁盘划分 + +Linux系统中常见的目录名称以及相应内容 + +| 目录名称 | 应放置文件的内容 | +| ----------- | --------------------------------------------------------- | +| /boot | 开机所需文件—内核、开机菜单以及所需配置文件等 | +| /dev | 以文件形式存放任何设备与接口 | +| /etc | 配置文件 | +| /home | 用户家目录 | +| /bin | 存放单用户模式下还可以操作的命令 | +| /lib | 开机时用到的函数库,以及/bin与/sbin下面的命令要调用的函数 | +| /sbin | 开机过程中需要的命令 | +| /media | 用于挂载设备文件的目录 | +| /opt | 放置第三方的软件 | +| /root | 系统管理员的家目录 | +| /srv | 一些网络服务的数据文件目录 | +| /tmp | 任何人均可使用的“共享”临时目录 | +| /proc | 虚拟文件系统,例如系统内核、进程、外部设备及网络状态等 | +| /usr/local | 用户自行安装的软件 | +| /usr/sbin | Linux系统开机时不会使用到的软件/命令/脚本 | +| /usr/share | 帮助与说明文件,也可放置共享文件 | +| /var | 主要存放经常变化的文件,如日志 | +| /lost+found | 当文件系统发生错误时,将一些丢失的文件片段存放在这里 | + +表示路径的方式: + +- 绝对路径指的是从根目录(/)开始写起的文件或目录名称 +- 相对路径则指的是相对于当前路径的写法 + +# 6. 物理设备的命名规则 + +常见的硬件设备及其文件名称 + +| 硬件设备 | 文件名称 | +| ------------- | ------------------ | +| IDE设备 | /dev/hd[a-d] | +| SCSI/SATA/U盘 | /dev/sd[a-p] | +| 软驱 | /dev/fd[0-1] | +| 打印机 | /dev/lp[0-15] | +| 光驱 | /dev/cdrom | +| 鼠标 | /dev/mouse | +| 磁带机 | /dev/st0或/dev/ht0 | + +一般的硬盘设备都会是以“/dev/sd”开头,分区编号按照如下规则: + +- 主分区或扩展分区的编号从1开始,到4结束; +- 逻辑分区从编号5开始。 + +设备文件名称: + +![设备命名规则](09.存储管理/设备命名规则.png) + +# 7. 文件系统与数据资料 + +当我们对一块硬盘分区好了以后,接下来我们还要做一件事情,就是我们要对这个分区做一个规定。也可以理解为,当我们要在这个分区上面存放数据的时候,应该按照什么样的规律存放,或者是读取的时候,应该按照什么样的规律读取。 + +这样对磁盘或者分区的规定,我们就称之为文件系统。 + +文件系统的作用是合理规划硬盘,以保证用户正常的使用需求。Linux系统支持数十种的文件系统。但是从大的角度上来看,其实主要分为日志式文件系统和索引式文件系统两种。 + +日志文件系统1 + +日志文件系统2 + +## 7.1 Linux常见的文件系统 + +| 文件系统 | 解释 | +| -------- | ------------------------------------------------------------ | +| Ext3 | 是一款日志文件系统,能够在系统异常宕机时避免文件系统资料丢失,并能自动修复数据的不一致与错误。然而,当硬盘容量较大时,所需的修复时间也会很长,而且也不能百分之百地保证资料不会丢失。它会把整个磁盘的每个写入动作的细节都预先记录下来,以便在发生异常宕机后能回溯追踪到被中断的部分,然后尝试进行修复。 | +| Ext4 | Ext3的改进版本,作为RHEL 6系统中的默认文件管理系统,它支持的存储容量高达1EB(1EB=1,073,741,824GB),且能够有无限多的子目录。另外,Ext4文件系统能够批量分配block块,从而极大地提高了读写效率。 | +| XFS | 是一种高性能的日志文件系统,而且是RHEL 7中默认的文件管理系统,它的优势在发生意外宕机后尤其明显,即可以快速地恢复可能被破坏的文件,而且强大的日志功能只用花费极低的计算和存储性能。并且它最大可支持的存储容量为18EB,这几乎满足了所有需求。 | + +格式化后会发生如下事情 + +- 日志式文件系统先干掉inode表,索引式文件系统先干掉第一个索引,表示空间可用。 + +- Linux创建一个硬盘地图'superblock',记录此filesystem 的整体信息,包括inode/block的总量、使用量、剩余量, 以及文件系统的格式与相关信息等; +- inode:记录文件的属性,一个文件占用一个inode,同时记录此文件的数据所在的block 号码,默认一个inode表格128字节,一个block记录消耗4B,记录满了后会新建inode用于扩展。 + - 该文件的访问权限(read、write、execute); + - 该文件的所有者与所属组(owner、group); + - 该文件的大小(size); + - 该文件的创建或内容修改时间(ctime); + - 该文件的最后一次访问时间(atime); + - 该文件的修改时间(mtime); + - 文件的特殊权限(SUID、SGID、SBIT); + - 该文件的真实数据地址(point)。 +- block:用于存储数据 + +计算机系统在发展过程中产生了众多的文件系统,为了使用户在读取或写入文件时不用关心底层的硬盘结构,Linux内核中的软件层为用户程序提供了一个VFS(Virtual File System,虚拟文件系统)接口,这样用户实际上在操作文件时就是统一对这个虚拟文件系统进行操作了。 + +![虚拟文件接口](09.存储管理/虚拟文件接口.png) + +## 7.2 mkfs工具 + +Linux mkfs(英文全拼:make file system)命令用于在特定的分区上建立 linux 文件系统。 + +**格式:** + +```bash +mkfs [-V] [-t fstype] [fs-options] filesys [blocks] +``` + +选项: + +- device : 预备检查的硬盘分区,例如:/dev/nvme0n1p1 +- -V : 详细显示模式 +- -c : 在制做档案系统前,检查该partition 是否有坏轨 +- -l bad_blocks_file : 将有坏轨的block资料加到 bad_blocks_file 里面 +- block : 给定 block 的大小 + +**实例:使用parted分区,然后使用mkfs创建ext4文件系统** + +```bash +[root@localhost ~]# parted /dev/nvme0n3 mklabel gpt +[root@localhost ~]# parted /dev/nvme0n3 mkpart primary 1 1G +Information: You may need to update /etc/fstab. +[root@localhost ~]# parted /dev/nvme0n3 print +Model: VMware Virtual NVMe Disk (nvme) +Disk /dev/nvme0n3: 5369MB +Sector size (logical/physical): 512B/512B +Partition Table: gpt +Disk Flags: + +Number Start End Size File system Name Flags + 1 1049kB 1000MB 999MB primary + +[root@localhost ~]# mkfs.ext4 /dev/nvme0n3p1 +mke2fs 1.46.5 (30-Dec-2021) +Creating filesystem with 243968 4k blocks and 61056 inodes +Filesystem UUID: db15a3c8-ae5d-4e69-aa93-cc0ee4107547 +Superblock backups stored on blocks: + 32768, 98304, 163840, 229376 + +Allocating group tables: done +Writing inode tables: done +Creating journal (4096 blocks): done +Writing superblocks and filesystem accounting information: done + +[root@localhost ~]# parted /dev/nvme0n3 print +Model: VMware Virtual NVMe Disk (nvme) +Disk /dev/nvme0n3: 5369MB +Sector size (logical/physical): 512B/512B +Partition Table: gpt +Disk Flags: + +Number Start End Size File system Name Flags + 1 1049kB 1000MB 999MB ext4 primary +``` + +## 7.3 mount挂载 + +挂载文件系统`mount 文件系统 挂载目录` + +mount命令中的参数以及作用 + +| 参数 | 作用 | +| ---- | ------------------------------------ | +| -a | 挂载所有在/etc/fstab中定义的文件系统 | +| -t | 指定文件系统的类型 | + +临时挂载文件系统 + +系统在重启后挂载就会失效 + +```bash +[root@node-1 ~]# mkdir backup +[root@node-1 ~]# mount /dev/sda2 /root/backup +``` + +如果想让硬件设备和目录永久地进行自动关联,就必须把挂载信息按照指定的填写格式“设备文件 挂载目录 格式类型 权限选项 是否备份 是否自检”,写入到/etc/fstab文件中。 + +用于挂载信息的指定填写格式中,各字段所表示的意义 + +| 字段 | 意义 | +| -------- | ------------------------------------------------------------ | +| 设备文件 | 一般为设备的路径+设备名称,也可以写唯一识别码(UUID,Universally Unique Identifier) | +| 挂载目录 | 指定要挂载到的目录,需在挂载前创建好 | +| 格式类型 | 指定文件系统的格式,比如Ext3、Ext4、XFS、SWAP、iso9660(此为光盘设备)等 | +| 权限选项 | 若设置为defaults,则默认权限为:rw, suid, dev, exec, auto, nouser, async | +| 是否备份 | 若为1则开机后使用dump进行磁盘备份,为0则不备份 | +| 是否自检 | 若为1则开机后自动进行磁盘自检,为0则不自检 | + +- 实例,挂载分区`/dev/sdb1`到`/mnt/volume1`下,并且设置为永久自动挂载 + +```bash +[root@localhost ~]# mkdir -p /mnt/volume1 +[root@localhost ~]# mount /dev/sdb1 /mnt/volume1 +[root@localhost ~]# df -h +文件系统 容量 已用 可用 已用% 挂载点 +/dev/mapper/centos-root 47G 995M 46G 3% / +devtmpfs 979M 0 979M 0% /dev +tmpfs 991M 0 991M 0% /dev/shm +tmpfs 991M 8.5M 982M 1% /run +tmpfs 991M 0 991M 0% /sys/fs/cgroup +/dev/sda1 1014M 133M 882M 14% /boot +tmpfs 199M 0 199M 0% /run/user/0 +/dev/sdb1 9.1G 37M 8.6G 1% /mnt/volume1 +# 先卸载sdb1 +[root@localhost ~]# umount /dev/sdb1 +[root@localhost ~]# vim /etc/fstab +# 最后一行加上 +/dev/sdb1 /mnt/volume1 ext4 defaults 0 0 + +[root@localhost ~]# mount -a +# 测试是否正确配置,fstab上的设备挂一遍试试看,万一错了重启系统会失败。 +[root@localhost ~]# df -h +文件系统 容量 已用 可用 已用% 挂载点 +/dev/mapper/centos-root 47G 995M 46G 3% / +devtmpfs 979M 0 979M 0% /dev +tmpfs 991M 0 991M 0% /dev/shm +tmpfs 991M 8.5M 982M 1% /run +tmpfs 991M 0 991M 0% /sys/fs/cgroup +/dev/sda1 1014M 133M 882M 14% /boot +tmpfs 199M 0 199M 0% /run/user/0 +/dev/sdb1 9.1G 37M 8.6G 1% /mnt/volume1 +``` + +## 7.4 umount + +撤销已经挂载的设备文件`umount [挂载点/设备文件]` + +```bash +[root@node-1 ~]# umount /dev/sda2 +``` + +挂载实验: + +```shell +#进行了格式化后 +#[root@localhost ~]# mkfs.ext4 /dev/sdb1 +#未挂载sdb1前,此时file0创建在sda1上 +[root@localhost ~]# mkdir /mnt/disk1 +[root@localhost ~]# touch /mnt/disk1/file0 +[root@localhost ~]# ll /mnt/disk1/ +-rw-r--r-- 1 root root 0 6月 9 19:44 file0 +#挂载sdb1,file0消失 +[root@localhost ~]# mount /dev/sdb1 /mnt/disk1/ +[root@localhost ~]# ll /mnt/disk1/ +drwx------ 2 root root 16384 6月 9 19:42 lost+found +#此时file0创建在sda1 +[root@localhost ~]# touch /mnt/disk1/file1 +[root@localhost ~]# ll /mnt/disk1/ +-rw-r--r-- 1 root root 0 6月 9 19:48 file1 +drwx------ 2 root root 16384 6月 9 19:42 lost+found +#卸载sdb1,file1消失,file0出现 +[root@localhost ~]# umount /dev/sdb1 +[root@localhost ~]# ll /mnt/disk1/ +-rw-r--r-- 1 root root 0 6月 9 19:44 file0 +``` + +## 7.5 df + +用于显示文件系统的磁盘空间使用情况 + +`df [选项]` + +常用选项 + +- `-h`:以人类可读的格式显示磁盘空间,使用K、M、G等单位。 +- `-T`:显示文件系统类型。 + +```bash +[root@localhost ~]# df -h +文件系统 容量 已用 可用 已用% 挂载点 +/dev/mapper/centos-root 47G 995M 46G 3% / +devtmpfs 979M 0 979M 0% /dev +tmpfs 991M 0 991M 0% /dev/shm +tmpfs 991M 8.5M 982M 1% /run +tmpfs 991M 0 991M 0% /sys/fs/cgroup +/dev/sda1 1014M 133M 882M 14% /boot +tmpfs 199M 0 199M 0% /run/user/0 +/dev/sdb1 9.1G 37M 8.6G 1% /mnt/volume1 +``` + +## 7.6 du + +查看某个目录下文件数据的占用量 + +`du [选项] [文件]` + +```bash +[root@localhost ~]# du -sh /etc +23M /etc +``` + +# 8. 添加交换分区 + +SWAP(交换)分区是一种通过在硬盘中预先划分一定的空间,然后将把内存中暂时不常用的数据临时存放到硬盘中,以便腾出物理内存空间让更活跃的程序服务来使用的技术 + +在生产环境中,交换分区的大小一般为真实物理内存的1.5~2倍 + +```bash +[root@localhost ~]# mkswap /dev/nvme0n2p1 +Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes) +no label, UUID=9d5158b3-9a30-4a0a-8167-7ee1bc9ce4f8 +[root@localhost ~]# free -h + total used free shared buff/cache available +Mem: 1.7Gi 448Mi 1.2Gi 6.0Mi 201Mi 1.3Gi +Swap: 2.0Gi 0B 2.0Gi +[root@localhost ~]# swapon /dev/nvme0n2p1 # 挂载swap交换分区 +[root@localhost ~]# vim /etc/fstab # 写入fstab文件中永久挂载 +# +# /etc/fstab +# Created by anaconda on Sat Nov 9 02:51:16 2024 +# +# Accessible filesystems, by reference, are maintained under '/dev/disk/'. +# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info. +# +# After editing this file, run 'systemctl daemon-reload' to update systemd +# units generated from this file. +# +/dev/mapper/rl-root / xfs defaults 0 0 +UUID=12fcea99-d1db-4f0a-ad86-f03129024fdb /boot xfs defaults 0 0 +/dev/mapper/rl-swap none swap defaults 0 0 +/dev/nvme0n2p1 swap swap defaults 0 0 + +[root@localhost ~]# free -h + total used free shared buff/cache available +Mem: 1.7Gi 451Mi 1.2Gi 6.0Mi 205Mi 1.3Gi +Swap: 3.0Gi 0B 3.0Gi +``` + +# 9. 磁盘容量配额 + +简单理解就是可以通过磁盘配额限制某个用户能够使用多大的空间 + +## 9.1 quota + +### 9.1.1 实例切入 + +描述: + +- 创建5个用户user1,user2,user3,user4,user5,密码和用户名相同,初始组为usergrp组。 +- 5个用户都可以取得300M的磁盘使用空间,文件数量不限。超过250M,给于提示。 + +### 9.1.2 准备磁盘 + +- 创建分区 + +```bash +[root@localhost ~]# fdisk /dev/nvme0n2 + +Welcome to fdisk (util-linux 2.37.4). +Changes will remain in memory only, until you decide to write them. +Be careful before using the write command. + +Device does not contain a recognized partition table. +Created a new DOS disklabel with disk identifier 0x27819a3d. + +Command (m for help): n +Partition type + p primary (0 primary, 0 extended, 4 free) + e extended (container for logical partitions) +Select (default p): + +Using default response p. +Partition number (1-4, default 1): +First sector (2048-10485759, default 2048): +Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-10485759, default 10485759): +2G + +Created a new partition 1 of type 'Linux' and of size 2 GiB. + +Command (m for help): p +Disk /dev/nvme0n2: 5 GiB, 5368709120 bytes, 10485760 sectors +Disk model: VMware Virtual NVMe Disk +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes +Disklabel type: dos +Disk identifier: 0x27819a3d + +Device Boot Start End Sectors Size Id Type +/dev/nvme0n2p1 2048 4196351 4194304 2G 83 Linux + +Command (m for help): w +The partition table has been altered. +Calling ioctl() to re-read partition table. +Syncing disks. +``` + +- lsblk 查看当前分区情况 + +```bash +[root@localhost ~]# lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS +sr0 11:0 1 1.7G 0 rom +nvme0n1 259:0 0 20G 0 disk +├─nvme0n1p1 259:1 0 1G 0 part /boot +└─nvme0n1p2 259:2 0 19G 0 part + ├─rl-root 253:0 0 17G 0 lvm / + └─rl-swap 253:1 0 2G 0 lvm [SWAP] +nvme0n2 259:3 0 5G 0 disk +└─nvme0n2p1 259:8 0 2G 0 part +nvme0n3 259:4 0 5G 0 disk +nvme0n4 259:5 0 5G 0 disk +nvme0n5 259:6 0 5G 0 disk +nvme0n6 259:7 0 5G 0 disk +``` + +- 格式化分区的文件系统 + +```bash +[root@localhost ~]# mkfs.ext4 /dev/nvme0n2p1 +mke2fs 1.46.5 (30-Dec-2021) +Creating filesystem with 524288 4k blocks and 131072 inodes +Filesystem UUID: da4f8f5c-6867-4cf8-8486-333a16adbcdb +Superblock backups stored on blocks: + 32768, 98304, 163840, 229376, 294912 + +Allocating group tables: done +Writing inode tables: done +Creating journal (16384 blocks): done +Writing superblocks and filesystem accounting information: done +``` + +- 创建目录并挂载 + +```bash +[root@localhost ~]# mkdir /mnt/mountpoint +[root@localhost ~]# mount /dev/nvme0n2p1 /mnt/mountpoint +[root@localhost ~]# df -Th +Filesystem Type Size Used Avail Use% Mounted on +devtmpfs devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs tmpfs 872M 0 872M 0% /dev/shm +tmpfs tmpfs 349M 6.3M 343M 2% /run +/dev/mapper/rl-root xfs 17G 1.7G 16G 10% / +/dev/nvme0n1p1 xfs 960M 261M 700M 28% /boot +tmpfs tmpfs 175M 0 175M 0% /run/user/0 +/dev/nvme0n2p1 ext4 2.0G 24K 1.8G 1% /mnt/mountpoint +``` + +### 9.1.3 准备用户 + +```bash +[root@atopos ~]# setenforce 0 +# 临时关闭SELinux +[root@atopos ~]# getenforce +Permissive +[root@atopos ~]# groupadd usergrp +[root@atopos ~]# for i in {1..5}; do useradd -g usergrp -b /mnt/mountpoint user$i; done +``` + +### 9.1.4 确保文件系统支持 + +- 检查挂载点是否支持quota配置 + +```bash +[root@localhost ~]# mount | grep mountpoint +/dev/nvme0n2p1 on /mnt/mountpoint type ext4 (rw,relatime,seclabel) +``` + +- 重新挂载,让文件系统支持quota配置 + +```bash +[root@localhost ~]# mount -o remount,usrquota,grpquota /mnt/mountpoint/ +[root@localhost ~]# mount | grep mountpoint +/dev/nvme0n2p1 on /mnt/mountpoint type ext4 (rw,relatime,seclabel,quota,usrquota,grpquota) +``` + +### 9.1.5 安装 quota + +- 软限制:当达到软限制时会提示用户,但仍允许用户在限定的额度内继续使用。 +- 硬限制:当达到硬限制时会提示用户,且强制终止用户的操作。 + +quotacheck主要参数 + +- -a:扫描所有在/etc/mtab内含有quota参数的文件系统 +- -u:针对用户扫描文件与目录的使用情况,会新建一个aquota.user文件 +- -g:针对用户组扫描文件与目录的使用情况,会新增一个aquota.group文件 +- -v:显示扫描过程的信息 + +```bash +[root@atopos ~]# yum install -y quota +``` + +### 9.1.6 开启 quota + +```bash +[root@localhost ~]# quotacheck -avug +quotacheck: Your kernel probably supports ext4 quota feature but you are using external quota files. Please switch your filesystem to use ext4 quota feature as external quota files on ext4 are deprecated. +quotacheck: Scanning /dev/nvme0n2p1 [/mnt/mountpoint] done +quotacheck: Cannot stat old user quota file /mnt/mountpoint/aquota.user: No such file or directory. Usage will not be subtracted. +quotacheck: Cannot stat old group quota file /mnt/mountpoint/aquota.group: No such file or directory. Usage will not be subtracted. +quotacheck: Cannot stat old user quota file /mnt/mountpoint/aquota.user: No such file or directory. Usage will not be subtracted. +quotacheck: Cannot stat old group quota file /mnt/mountpoint/aquota.group: No such file or directory. Usage will not be subtracted. +quotacheck: Checked 8 directories and 15 files +quotacheck: Old file not found. +quotacheck: Old file not found. +[root@localhost ~]# quotaon -avug +quotaon: Your kernel probably supports ext4 quota feature but you are using external quota files. Please switch your filesystem to use ext4 quota feature as external quota files on ext4 are deprecated. +/dev/nvme0n2p1 [/mnt/mountpoint]: group quotas turned on +/dev/nvme0n2p1 [/mnt/mountpoint]: user quotas turned on +``` + +### 9.1.7 编辑配额配置 + +```bash +[root@localhost ~]# edquota -u user1 +``` + +![磁盘容量配额1](09.存储管理/磁盘容量配额1.png) + +- 可以将针对user1的限制复制给user2 + +```sh +[root@localhost ~]# edquota -p user1 -u user2 +``` + +- 查看限制情况 + +```bash +[root@localhost ~]# repquota -as +*** Report for user quotas on device /dev/nvme0n2p1 +Block grace time: 7days; Inode grace time: 7days + Space limits File limits +User used soft hard grace used soft hard grace +---------------------------------------------------------------------- +root -- 20K 0K 0K 2 0 0 +user1 -- 16K 245M 293M 4 0 0 +user2 -- 16K 245M 293M 4 0 0 +user3 -- 16K 0K 0K 4 0 0 +user4 -- 16K 0K 0K 4 0 0 +user5 -- 16K 0K 0K 4 0 0 +``` + +### 9.1.8 测试 + +```bash +# user1用户测试 +[root@localhost ~]# su - user1 +[user1@localhost ~]$ dd if=/dev/zero of=bigfile bs=10M count=50 +nvme0n2p1: warning, user block quota exceeded. +nvme0n2p1: write failed, user block limit reached. +dd: error writing 'bigfile': Disk quota exceeded +30+0 records in +29+0 records out +307179520 bytes (307 MB, 293 MiB) copied, 1.1834 s, 260 MB/s +[user1@localhost ~]$ du -sh +293M . + +# user2用户测试 +[root@localhost ~]# su - user2 +[user2@localhost ~]$ dd if=/dev/zero of=bigfile bs=10M count=50 +nvme0n2p1: warning, user block quota exceeded. +nvme0n2p1: write failed, user block limit reached. +dd: error writing 'bigfile': Disk quota exceeded +30+0 records in +29+0 records out +307183616 bytes (307 MB, 293 MiB) copied, 1.43269 s, 214 MB/s +[user2@localhost ~]$ du -sh +293M . +``` + +### 9.1.9 quota 命令 + +Linux quota命令用于显示磁盘已使用的空间与限制。 + +执行quota指令,可查询磁盘空间的限制,并得知已使用多少空间 + +选项: + +- -g 列出群组的磁盘空间限制。 +- -q 简明列表,只列出超过限制的部分。 +- -u 列出用户的磁盘空间限制。 +- -v 显示该用户或群组,在所有挂入系统的存储设备的空间限制。 +- -V 显示版本信息。 + +## 9.2 xfs_quota + +专门针对XFS文件系统来管理quota磁盘容量配额服务 + +`xfs_quota [参数] 配额 文件系统` + +- \-c参数用于以参数的形式设置要执行的命令 + +- \-x参数是专家模式 + +硬盘使用量的软限制和硬限制分别为3MB和6MB;创建文件数量的软限制和硬限制分别为3个和6个。 + +```bash +[root@localhost ~]# xfs_quota -x -c 'limit bsoft=3m bhard=6m isoft=3 ihard=6 test' /boot +[root@localhost ~]# xfs_quota -x -c report /boot +User quota on /boot (/dev/sda1) + Blocks +User ID Used Soft Hard Warn/Grace +---------- -------------------------------------------------- +root 115516 0 0 00 [--------] +test 0 3072 6144 00 [--------] +``` + +```bash +[root@localhost ~]# su - test +[test@localhost ~]$ dd if=/dev/zero of=/boot/tom bs=5M count=1 +1+0 records in +1+0 records out +5242880 bytes (5.2 MB) copied, 0.00350676 s, 1.5 GB/s +[test@localhost ~]$ dd if=/dev/zero of=/boot/tom bs=8M count=1 +dd: error writing ‘/boot/tom’: Disk quota exceeded +1+0 records in +0+0 records out +4194304 bytes (4.2 MB) copied, 0.00760235 s, 552 MB/s +``` + +# 10. 软硬方式链接 + +在Linux系统中存在硬链接和软连接两种文件。 + +- 硬链接(hard link): + - 可以将它理解为一个“指向原始文件inode的指针”,系统不为它分配独立的inode和文件。所以,硬链接文件与原始文件其实是同一个文件,只是名字不同。我们每添加一个硬链接,该文件的inode连接数就会增加1;而且只有当该文件的inode连接数为0时,才算彻底将它删除。换言之,由于硬链接实际上是指向原文件inode的指针,因此即便原始文件被删除,依然可以通过硬链接文件来访问。需要注意的是,由于技术的局限性,我们不能跨分区对目录文件进行链接。 +- 软链接(也称为符号链接[symbolic link]): + - 仅仅包含所链接文件的路径名,因此能链接目录文件,也可以跨越文件系统进行链接。但是,当原始文件被删除后,链接文件也将失效,从这一点上来说与Windows系统中的“快捷方式”具有一样的性质。 + +## 10.1 ln + +用于创建链接文件 + +`ln [选项] 目标` + +ln命令中可用的参数以及作用 + +| 参数 | 作用 | +| ---- | -------------------------------------------------- | +| -s | 创建“符号链接”(如果不带-s参数,则默认创建硬链接) | +| -f | 强制创建文件或目录的链接 | +| -i | 覆盖前先询问 | +| -v | 显示创建链接的过程 | + +### 10.1.1 软链接演示 + +```bash +[root@localhost ~]# echo "hello linux" > testfile +[root@localhost ~]# ln -s testfile linkfile # 创建软连接 +[root@localhost ~]# ll +total 8 +-rw-------. 1 root root 905 Nov 9 10:57 anaconda-ks.cfg +lrwxrwxrwx. 1 root root 8 Nov 15 20:41 linkfile -> testfile +-rw-r--r--. 1 root root 12 Nov 15 20:40 testfile +[root@localhost ~]# cat linkfile +hello linux +[root@localhost ~]# ls -l linkfile +lrwxrwxrwx. 1 root root 8 Nov 15 20:41 linkfile -> testfile +[root@localhost ~]# rm -f testfile +[root@localhost ~]# cat linkfile +cat: linkfile: No such file or directory +``` + +### 10.1.2 硬链接演示 + +```bash +[root@localhost ~]# echo "hello linux" > testfile +[root@localhost ~]# ln testfile linkfile +[root@localhost ~]# cat linkfile +hello linux +[root@localhost ~]# ls -l linkfile +-rw-r--r--. 2 root root 12 Nov 15 20:42 linkfile +[root@localhost ~]# rm -f testfile +[root@localhost ~]# cat linkfile +hello linux +``` + diff --git a/01.Linux基础/09.存储管理/MBR分区表.png b/01.Linux基础/09.存储管理/MBR分区表.png new file mode 100644 index 0000000..bed4ea9 Binary files /dev/null and b/01.Linux基础/09.存储管理/MBR分区表.png differ diff --git a/01.Linux基础/09.存储管理/gpt分区表.png b/01.Linux基础/09.存储管理/gpt分区表.png new file mode 100644 index 0000000..ff64873 Binary files /dev/null and b/01.Linux基础/09.存储管理/gpt分区表.png differ diff --git a/01.Linux基础/09.存储管理/分区操作.gif b/01.Linux基础/09.存储管理/分区操作.gif new file mode 100644 index 0000000..6ac3b18 Binary files /dev/null and b/01.Linux基础/09.存储管理/分区操作.gif differ diff --git a/01.Linux基础/09.存储管理/分区演示.png b/01.Linux基础/09.存储管理/分区演示.png new file mode 100644 index 0000000..3ed22ea Binary files /dev/null and b/01.Linux基础/09.存储管理/分区演示.png differ diff --git a/01.Linux基础/09.存储管理/固态硬盘ssd.png b/01.Linux基础/09.存储管理/固态硬盘ssd.png new file mode 100644 index 0000000..2446da2 Binary files /dev/null and b/01.Linux基础/09.存储管理/固态硬盘ssd.png differ diff --git a/01.Linux基础/09.存储管理/固态硬盘设计1.png b/01.Linux基础/09.存储管理/固态硬盘设计1.png new file mode 100644 index 0000000..b00820a Binary files /dev/null and b/01.Linux基础/09.存储管理/固态硬盘设计1.png differ diff --git a/01.Linux基础/09.存储管理/固态硬盘设计2.png b/01.Linux基础/09.存储管理/固态硬盘设计2.png new file mode 100644 index 0000000..6a6c3e4 Binary files /dev/null and b/01.Linux基础/09.存储管理/固态硬盘设计2.png differ diff --git a/01.Linux基础/09.存储管理/固态硬盘设计3.png b/01.Linux基础/09.存储管理/固态硬盘设计3.png new file mode 100644 index 0000000..7bb3e8e Binary files /dev/null and b/01.Linux基础/09.存储管理/固态硬盘设计3.png differ diff --git a/01.Linux基础/09.存储管理/日志文件系统1.png b/01.Linux基础/09.存储管理/日志文件系统1.png new file mode 100644 index 0000000..016be3b Binary files /dev/null and b/01.Linux基础/09.存储管理/日志文件系统1.png differ diff --git a/01.Linux基础/09.存储管理/日志文件系统2.png b/01.Linux基础/09.存储管理/日志文件系统2.png new file mode 100644 index 0000000..835e8df Binary files /dev/null and b/01.Linux基础/09.存储管理/日志文件系统2.png differ diff --git a/01.Linux基础/09.存储管理/机械硬盘hdd.png b/01.Linux基础/09.存储管理/机械硬盘hdd.png new file mode 100644 index 0000000..f704bcf Binary files /dev/null and b/01.Linux基础/09.存储管理/机械硬盘hdd.png differ diff --git a/01.Linux基础/09.存储管理/磁盘容量配额1.png b/01.Linux基础/09.存储管理/磁盘容量配额1.png new file mode 100644 index 0000000..28d07ab Binary files /dev/null and b/01.Linux基础/09.存储管理/磁盘容量配额1.png differ diff --git a/01.Linux基础/09.存储管理/虚拟文件接口.png b/01.Linux基础/09.存储管理/虚拟文件接口.png new file mode 100644 index 0000000..f856e3a Binary files /dev/null and b/01.Linux基础/09.存储管理/虚拟文件接口.png differ diff --git a/01.Linux基础/09.存储管理/设备命名规则.png b/01.Linux基础/09.存储管理/设备命名规则.png new file mode 100644 index 0000000..e57e23d Binary files /dev/null and b/01.Linux基础/09.存储管理/设备命名规则.png differ diff --git a/01.Linux基础/10.磁盘阵列RAID.md b/01.Linux基础/10.磁盘阵列RAID.md new file mode 100644 index 0000000..353c345 --- /dev/null +++ b/01.Linux基础/10.磁盘阵列RAID.md @@ -0,0 +1,883 @@ +# 1. 磁盘阵列 + +磁盘阵列(RAID,**R**edundant **A**rray of **I**ndependent **D**isks)是一种将多个物理硬盘组合在一起形成一个逻辑硬盘组的技术。通过 RAID,可以实现数据冗余、性能提升或两者兼具。RAID 在存储系统中广泛使用,尤其是在服务器和大数据存储环境中,用于提升数据可靠性和性能。 + +## 1.1 **RAID 0(条带化,Striping)** + +- **特点**: 将数据分块并在多个磁盘上并行存储,提升读写性能。 +- **优点**: 极大提高了读写速度,因为数据在多个磁盘上并行操作。 +- **缺点**: 没有冗余,任何一个磁盘的损坏都会导致整个 RAID 失效,数据丢失。 +- **适用场景**: 需要高性能、数据安全性需求不高的场景,比如视频编辑、临时数据存储等。 + +![RAID0](10.磁盘阵列RAID/RAID0.png) + +## 1.2 **RAID 1(镜像,Mirroring)** + +- **特点**: 将数据完全复制到另一块磁盘上,提供 1:1 的数据冗余。 +- **优点**: 数据安全性高,任何一个磁盘损坏时,数据仍可以从镜像磁盘中恢复。 +- **缺点**: 存储效率较低,实际可用存储空间为所有磁盘容量的一半。 +- **适用场景**: 需要高数据可靠性和快速恢复的场合,比如数据库、操作系统磁盘等。 + +![RAID1](10.磁盘阵列RAID/RAID1.png) + +## 1.3 **RAID 5(带奇偶校验的条带化,Striping with Parity)** + +- **特点**: 将数据和奇偶校验数据分布在所有磁盘上,提供冗余的同时提升性能。 +- **优点**: 提供数据冗余,同时存储效率较高(磁盘数量越多,效率越好),能够在一个磁盘损坏的情况下恢复数据。 +- **缺点**: 写入性能稍差,尤其是在发生设备故障时,重建过程较慢。 +- **适用场景**: 需要兼顾性能和数据冗余的场景,如文件服务器和应用服务器。 + +- 写性能*1,读性能\*(N-1) + +RAID5的核心思想是使用奇偶校验信息来提供数据的冗余备份。当其中一个硬盘驱动器发生故障时,剩余的硬盘驱动器可以通过计算奇偶校验信息来恢复丢失的数据。这种方式既提供了数据冗余和容错能力,又降低了整体存储成本。 + +![RAID5](10.磁盘阵列RAID/RAID5.png) + +## 1.4 **RAID 10(RAID 1+0,镜像与条带化的组合)** + +- **特点**: 将 RAID 1(镜像)和 RAID 0(条带化)结合使用,既提供数据冗余又提升了性能。 +- **优点**: 既有 RAID 0 的高性能,又有 RAID 1 的高安全性。能够快速恢复数据。 +- **缺点**: 存储效率较低,要求至少 4 块磁盘,实际可用空间为所有磁盘容量的一半。 +- **适用场景**: 需要高性能和高冗余的关键应用场景,如数据库、虚拟化环境等。 + +- 读写性能*N(N为RAID0的硬盘数,此例为2),不能同时坏数据块1a与2a + +RAID10 + +## 1.5 **硬件磁盘阵列(Hardware RAID)** + +### 1.5.1 **特点** + +- **专用硬件控制器**:硬件 RAID 使用专门的 RAID 控制器卡(RAID Controller),通常是插入主板的 PCIe 扩展卡或者集成在服务器主板上的芯片。该控制器管理磁盘阵列的所有操作并执行 RAID 级别的计算。 +- **独立于操作系统**:硬件 RAID 完全独立于操作系统,RAID 控制器负责所有 RAID 相关操作,操作系统只看到一个逻辑磁盘。 +- **硬件加速**:硬件 RAID 卡通常带有专用处理器(ASIC 或 FPGA)和缓存,用来加速 RAID 操作,提高性能 + +### 1.5.2 阵列卡 + +阵列卡 + +## 1.6 **软件磁盘阵列(Software RAID)** + +### 1.6.1 **特点** + +- **由操作系统实现**:软件 RAID 通过操作系统内核或专门的 RAID 软件来管理和实现 RAID 功能。Linux 的 `mdadm`、Windows 的动态磁盘管理和 macOS 的磁盘工具等都支持软件 RAID。 +- **没有专用硬件**:软件 RAID 不需要专用的 RAID 控制器,所有 RAID 计算和管理工作都由主机的 CPU 执行。 +- **灵活性高**:软件 RAID 通常可以在不同的硬件平台之间迁移,因为 RAID 配置数据存储在磁盘上,而不是特定的控制器中。 + +## 1.7 **硬件 RAID 和软件 RAID 的对比** + +| **对比项** | **硬件 RAID** | **软件 RAID** | +| ------------ | ---------------------------------------- | ---------------------------------- | +| **实现方式** | 专用 RAID 控制器 | 操作系统或软件实现 | +| **性能** | 高,特别是在 RAID 5/6 等级别中 | 较低,占用主机 CPU 资源 | +| **磁盘管理** | 独立于操作系统,系统只看到一个逻辑磁盘 | 依赖于操作系统的磁盘管理 | +| **成本** | 高,需购买 RAID 控制器 | 低,无需额外硬件 | +| **功能支持** | 支持热备盘、硬盘监控、电池缓存等高级功能 | 功能较少,依赖操作系统提供的功能 | +| **灵活性** | 依赖特定硬件,不易迁移 | 容易迁移,跨硬件平台使用 | +| **故障恢复** | 控制器损坏可能需要相同型号的控制器 | 可以在不同硬件上恢复 | +| **适用场景** | 企业级、大型存储系统,数据中心 | 个人用户、小型服务器,开发测试环境 | + +# 2. 部署磁盘阵列 + +为了后续实验,我们先添加5块全新的磁盘。关机状态下,在虚拟机设置中新增磁盘即可。最好添加完成以后,拍个快照保存一下。方便我们后续多次实验恢复。 + +部署磁盘阵列 + +## 2.1 mdadm磁盘整列 + +对于Linux上通过软件的形式构建磁盘整列,我们常用的方式是通过mdadm这个工具来完成。当然对于部分Linux发行版本,mdadm可能没有内置安装好,所以我们需要手动安装一下。 + +## 2.2 安装mdadm工具 + +yum是Linux中用于安装软件的工具,可以理解为应用商店 + +```sh +[root@localhost ~]# yum -y install mdadm +``` + +## 2.3 mdadm命令 + +常用参数以及作用 + +| 参数 | 作用 | +| ---- | ---------------- | +| -a | 检测设备名称 | +| -n | 指定设备数量 | +| -l | 指定RAID级别 | +| -C | 创建 | +| -v | 显示过程 | +| -f | 模拟设备损坏 | +| -r | 移除设备 | +| -Q | 查看摘要信息 | +| -D | 查看详细信息 | +| -S | 停止RAID磁盘阵列 | +| -x | 备份盘数量 | + +## 2.4 RAID 0阵列实验 + +### 2.4.1 部署 + +1. 创建RAID 0阵列 + +```sh +[root@localhost ~]# mdadm -Cv /dev/md0 -a yes -n 2 -l 0 /dev/nvme0n2 /dev/nvme0n3 +mdadm: chunk size defaults to 512K +mdadm: Defaulting to version 1.2 metadata +mdadm: array /dev/md0 started. +[root@localhost ~]# lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS +sr0 11:0 1 1.7G 0 rom +nvme0n1 259:0 0 20G 0 disk +├─nvme0n1p1 259:1 0 1G 0 part /boot +└─nvme0n1p2 259:2 0 19G 0 part + ├─rl-root 253:0 0 17G 0 lvm / + └─rl-swap 253:1 0 2G 0 lvm [SWAP] +nvme0n2 259:3 0 5G 0 disk +└─md0 9:0 0 10G 0 raid0 +nvme0n3 259:4 0 5G 0 disk +└─md0 9:0 0 10G 0 raid0 +nvme0n4 259:5 0 5G 0 disk +nvme0n5 259:6 0 5G 0 disk +nvme0n6 259:7 0 5G 0 disk +``` + +通过lsblk命令检查RAID 0阵列创建情况 + +2. 格式化文件系统 + +```sh +[root@localhost ~]# mkfs.ext4 /dev/md0 +mke2fs 1.46.5 (30-Dec-2021) +/dev/md0 contains a ext4 file system + last mounted on Wed Nov 13 15:48:49 2024 +Proceed anyway? (y,N) y +Creating filesystem with 2618880 4k blocks and 655360 inodes +Filesystem UUID: 8d1dcad6-2788-40db-8dbd-55b5b7105dbc +Superblock backups stored on blocks: + 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632 + +Allocating group tables: done +Writing inode tables: done +Creating journal (16384 blocks): done +Writing superblocks and filesystem accounting information: done +``` + +3. 创建挂载点,并对设备进行挂载 + +```sh +[root@localhost ~]# mkdir /mnt/RAID0 +[root@localhost ~]# mount /dev/md0 /mnt/RAID0 +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 6.3M 343M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/md0 9.8G 24K 9.3G 1% /mnt/RAID0 + +# mount为临时挂载,也可以将挂载信息写入/etc/fstab文件中,进行永久挂载 +[root@localhost ~]# echo "/dev/md0 /mnt/RAID0 ext4 defaults 0 0" >> /etc/fstab +[root@localhost ~]# mount -a +mount: (hint) your fstab has been modified, but systemd still uses + the old version; use 'systemctl daemon-reload' to reload. +[root@localhost ~]# systemctl daemon-reload +[root@localhost ~]# mount -a +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 6.3M 343M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/md0 9.8G 24K 9.3G 1% /mnt/RAID0 + +# 确保fstab文件中的内容格式正确 +``` + +4. 查看/dev/md0磁盘阵列的详细信息 + +```sh +[root@localhost ~]# mdadm -D /dev/md0 +/dev/md0: + Version : 1.2 + Creation Time : Thu Nov 14 16:10:58 2024 + Raid Level : raid0 + Array Size : 10475520 (9.99 GiB 10.73 GB) + Raid Devices : 2 + Total Devices : 2 + Persistence : Superblock is persistent + + Update Time : Thu Nov 14 16:10:58 2024 + State : clean + Active Devices : 2 + Working Devices : 2 + Failed Devices : 0 + Spare Devices : 0 + + Layout : original + Chunk Size : 512K + +Consistency Policy : none + + Name : localhost.localdomain:0 (local to host localhost.localdomain) + UUID : 25a30480:323ceeda:d3d95a78:0f5321f7 + Events : 0 + + Number Major Minor RaidDevice State + 0 259 3 0 active sync /dev/nvme0n2 + 1 259 4 1 active sync /dev/nvme0n3 +``` + +**部分字段解释:** + +**Number**: 这个字段表示每个磁盘在 RAID 阵列中的编号(从 0 开始)。这是 RAID 阵列中每个设备的索引或设备号。 + +**Major**: 这是设备文件的主设备号(major number),它用于标识设备的类型。不同的主设备号通常对应不同的驱动程序或设备类别。 + +**Minor**: 这是设备文件的次设备号(minor number),用于标识同一类型设备中的不同实例。主设备号和次设备号共同唯一标识系统中的一个设备。 + +**RaidDevice**: 这是设备在 RAID 阵列中的逻辑设备编号。通常从 0 开始,表示该设备在 RAID 阵列中的顺序。 + +**State**: 表示 RAID 阵列中该设备的当前状态。常见状态如下: + +- **active sync**: 表示该设备处于活动状态,并且与 RAID 阵列中的其他设备同步。 +- **faulty**: 表示设备出现故障,不能正常工作。 +- **spare**: 表示该设备是热备盘,当其他设备故障时可自动替换。 + +**set-A / set-B**: 这可能是与 RAID 10 或其他多组 RAID 配置相关的信息,表示该磁盘属于某个 RAID 子组或镜像组。例如,**set-A** 和 **set-B** 可能表示 RAID 10 配置中的两个镜像组。 + +**/dev/nvme0nX**: 这是设备的名称,表示设备在系统中的路径。在这里,设备是 NVMe 协议的磁盘(例如,`/dev/nvme0n2`)。不同的数字后缀(例如 `n2`, `n3` 等)表示不同的 NVMe 设备。 + +### 2.4.2 测试 + +通过fio磁盘测试工具来测试我们的RAID 0阵列的读写情况 + +```sh +[root@localhost ~]# yum install -y fio +``` + +#### 2.4.2.1 单块硬盘测试 + +```sh +# 先挂载单块硬盘 +[root@localhost ~]# mkdir /mnt/test +[root@localhost ~]# mkfs.ext4 /dev/nvme0n4 +mke2fs 1.46.5 (30-Dec-2021) +Creating filesystem with 1310720 4k blocks and 327680 inodes +Filesystem UUID: 0694e52f-3816-4223-b51b-6600bf32bb21 +Superblock backups stored on blocks: + 32768, 98304, 163840, 229376, 294912, 819200, 884736 + +Allocating group tables: done +Writing inode tables: done +Creating journal (16384 blocks): done +Writing superblocks and filesystem accounting information: done + +[root@localhost ~]# mount /dev/nvme0n4 /mnt/test +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 6.3M 343M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/md0 9.8G 24K 9.3G 1% /mnt/RAID0 +/dev/nvme0n4 4.9G 24K 4.6G 1% /mnt/test + +# 开始测试 +[root@localhost test]# fio --name=write_test --filename=/mnt/test/testfile --size=1G --bs=1M --rw=write --direct=1 --numjobs=1 +write_test: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, i oengine=psync, iodepth=1 +fio-3.35 +Starting 1 process +write_test: Laying out IO file (1 file / 1024MiB) + +write_test: (groupid=0, jobs=1): err= 0: pid=29670: Thu Nov 14 16:51:32 2024 + write: IOPS=1404, BW=1405MiB/s (1473MB/s)(1024MiB/729msec); 0 zone resets + clat (usec): min=570, max=2700, avg=694.62, stdev=136.39 + lat (usec): min=588, max=2709, avg=710.02, stdev=136.41 + clat percentiles (usec): + | 1.00th=[ 586], 5.00th=[ 594], 10.00th=[ 603], 20.00th=[ 619], + | 30.00th=[ 635], 40.00th=[ 652], 50.00th=[ 668], 60.00th=[ 693], + | 70.00th=[ 709], 80.00th=[ 742], 90.00th=[ 783], 95.00th=[ 848], + | 99.00th=[ 1057], 99.50th=[ 1450], 99.90th=[ 2409], 99.95th=[ 2704], + | 99.99th=[ 2704] + bw ( MiB/s): min= 1400, max= 1400, per=99.67%, avg=1400.00, stdev= 0.00, samples=1 + iops : min= 1400, max= 1400, avg=1400.00, stdev= 0.00, samples=1 + lat (usec) : 750=83.11%, 1000=15.53% + lat (msec) : 2=1.07%, 4=0.29% + cpu : usr=0.14%, sys=8.52%, ctx=1024, majf=0, minf=8 + IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% + submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + issued rwts: total=0,1024,0,0 short=0,0,0,0 dropped=0,0,0,0 + latency : target=0, window=0, percentile=100.00%, depth=1 + +Run status group 0 (all jobs): + WRITE: bw=1405MiB/s (1473MB/s), 1405MiB/s-1405MiB/s (1473MB/s-1473MB/s), io=1024MiB (1074MB), run=729-729msec + +Disk stats (read/write): + nvme0n4: ios=0/1793, merge=0/6, ticks=0/1173, in_queue=1173, util=86.50% +``` + +#### 2.4.2.2 RAID 0测试 + +```sh +[root@localhost RAID0]# fio --name=write_test --filename=/mnt/RAID0/testfile --size=1G --bs=1M --rw=write --d irect=1 --numjobs=1 +write_test: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync , iodepth=1 +fio-3.35 +Starting 1 process +write_test: Laying out IO file (1 file / 1024MiB) + +write_test: (groupid=0, jobs=1): err= 0: pid=29232: Thu Nov 14 16:40:26 2024 + write: IOPS=1314, BW=1315MiB/s (1378MB/s)(1024MiB/779msec); 0 zone resets + clat (usec): min=513, max=3382, avg=737.41, stdev=259.09 + lat (usec): min=527, max=3469, avg=759.04, stdev=265.36 + clat percentiles (usec): + | 1.00th=[ 537], 5.00th=[ 570], 10.00th=[ 578], 20.00th=[ 611], + | 30.00th=[ 627], 40.00th=[ 660], 50.00th=[ 676], 60.00th=[ 709], + | 70.00th=[ 758], 80.00th=[ 824], 90.00th=[ 898], 95.00th=[ 963], + | 99.00th=[ 2147], 99.50th=[ 2540], 99.90th=[ 3294], 99.95th=[ 3392], + | 99.99th=[ 3392] + bw ( MiB/s): min= 1275, max= 1275, per=97.03%, avg=1275.45, stdev= 0.00, samples=1 + iops : min= 1275, max= 1275, avg=1275.00, stdev= 0.00, samples=1 + lat (usec) : 750=69.43%, 1000=26.86% + lat (msec) : 2=2.34%, 4=1.37% + cpu : usr=0.39%, sys=9.77%, ctx=1024, majf=0, minf=10 + IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% + submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + issued rwts: total=0,1024,0,0 short=0,0,0,0 dropped=0,0,0,0 + latency : target=0, window=0, percentile=100.00%, depth=1 + +Run status group 0 (all jobs): + WRITE: bw=1315MiB/s (1378MB/s), 1315MiB/s-1315MiB/s (1378MB/s-1378MB/s), io=1024MiB (1074MB), run=779-779ms ec + +Disk stats (read/write): + md0: ios=0/1660, merge=0/0, ticks=0/1229, in_queue=1229, util=86.06%, aggrios=0/1024, aggrmerge=0/0, aggr ticks=0/703, aggrin_queue=703, aggrutil=86.57% + nvme0n2: ios=0/1024, merge=0/0, ticks=0/702, in_queue=702, util=86.57% + nvme0n3: ios=0/1024, merge=0/0, ticks=0/705, in_queue=705, util=86.57% +``` + +由此可以看出,似乎单块磁盘和RAID 0的写入速度是差不多一样的。按道理来说,RAID 0是将数据分散在两块磁盘上,写入速度理应翻倍。但是由于这是虚拟机,使用我们自己电脑的物理磁盘,我们的物理硬盘只有一块,所以这里看不到RAID 0的双倍读写速度的效果。 + +### 2.4.3 删除磁盘阵列 + +```sh +# 先取消挂载,如果写入到fstab文件中,要记得删除 +[root@localhost ~]# umount /dev/md0 +# 停止磁盘整列 +[root@localhost ~]# mdadm -S /dev/md0 +mdadm: stopped /dev/md0 +# 删除阵列中的数据块信息 +[root@localhost ~]# mdadm --zero-superblock /dev/nvme0n2 /dev/nvme0n3 +# 检查磁盘情况 +[root@localhost ~]# lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS +sr0 11:0 1 1.7G 0 rom +nvme0n1 259:0 0 20G 0 disk +├─nvme0n1p1 259:1 0 1G 0 part /boot +└─nvme0n1p2 259:2 0 19G 0 part + ├─rl-root 253:0 0 17G 0 lvm / + └─rl-swap 253:1 0 2G 0 lvm [SWAP] +nvme0n2 259:3 0 5G 0 disk +nvme0n3 259:4 0 5G 0 disk +nvme0n4 259:5 0 5G 0 disk /mnt/test +nvme0n5 259:6 0 5G 0 disk +nvme0n6 259:7 0 5G 0 disk +``` + +## 2.5 RAID 1阵列实验 + +### 2.5.1 部署 + +按照上面实验中的方式,创建RAID1阵列以及完成挂载 + +```bash +[root@localhost ~]# mdadm -Cv /dev/md0 -a yes -n 2 -l 1 /dev/nvme0n2 /dev/nvme0n3 +mdadm: Note: this array has metadata at the start and + may not be suitable as a boot device. If you plan to + store '/boot' on this device please ensure that + your boot-loader understands md/v1.x metadata, or use + --metadata=0.90 +mdadm: size set to 5237760K +Continue creating array? y +mdadm: Defaulting to version 1.2 metadata +mdadm: array /dev/md0 started. +[root@localhost ~]# lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS +sr0 11:0 1 1.7G 0 rom +nvme0n1 259:0 0 20G 0 disk +├─nvme0n1p1 259:1 0 1G 0 part /boot +└─nvme0n1p2 259:2 0 19G 0 part + ├─rl-root 253:0 0 17G 0 lvm / + └─rl-swap 253:1 0 2G 0 lvm [SWAP] +nvme0n2 259:3 0 5G 0 disk +└─md0 9:0 0 5G 0 raid1 +nvme0n3 259:4 0 5G 0 disk +└─md0 9:0 0 5G 0 raid1 +nvme0n4 259:5 0 5G 0 disk /mnt/test +nvme0n5 259:6 0 5G 0 disk +nvme0n6 259:7 0 5G 0 disk +[root@localhost ~]# mkfs.ext4 /dev/md0 +mke2fs 1.46.5 (30-Dec-2021) +/dev/md0 contains a ext4 file system + last mounted on /mnt/RAID0 on Thu Nov 14 16:39:48 2024 +Proceed anyway? (y,N) y +Creating filesystem with 1309440 4k blocks and 327680 inodes +Filesystem UUID: fa3601df-6ddb-4471-afcc-fbf501a8e891 +Superblock backups stored on blocks: + 32768, 98304, 163840, 229376, 294912, 819200, 884736 + +Allocating group tables: done +Writing inode tables: done +Creating journal (16384 blocks): done +Writing superblocks and filesystem accounting information: done + +[root@localhost ~]# mkdir /mnt/RAID1 +[root@localhost ~]# mount /dev/md0 /mnt/RAID1 +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 6.3M 343M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/nvme0n4 4.9G 1.1G 3.6G 22% /mnt/test +/dev/md0 4.9G 24K 4.6G 1% /mnt/RAID1 +``` + +**注意最后的磁盘大小,我们将使用两块大小为5G的磁盘创建RAID1阵列。但是发现最后的磁盘大小还是5G,这也验证了RAID 1的特性,两块磁盘存放一样的数据,将数据镜像化,所以可用空间也会打对折** + +### 2.5.2 磁盘写入测试 + +```sh +[root@localhost ~]# fio --name=write_test --filename=/mnt/RAID1/testfile --size=1G --bs=1M --rw=write --direct=1 --numjobs=1 +write_test: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1 +fio-3.35 +Starting 1 process +write_test: Laying out IO file (1 file / 1024MiB) +Jobs: 1 (f=1) +write_test: (groupid=0, jobs=1): err= 0: pid=29656: Thu Nov 14 16:48:10 2024 + write: IOPS=726, BW=727MiB/s (762MB/s)(1024MiB/1409msec); 0 zone resets + clat (usec): min=1057, max=6693, avg=1349.52, stdev=401.77 + lat (usec): min=1074, max=6748, avg=1372.68, stdev=404.55 + clat percentiles (usec): + | 1.00th=[ 1090], 5.00th=[ 1156], 10.00th=[ 1172], 20.00th=[ 1205], + | 30.00th=[ 1221], 40.00th=[ 1237], 50.00th=[ 1270], 60.00th=[ 1303], + | 70.00th=[ 1336], 80.00th=[ 1385], 90.00th=[ 1500], 95.00th=[ 1713], + | 99.00th=[ 3097], 99.50th=[ 3884], 99.90th=[ 5866], 99.95th=[ 6718], + | 99.99th=[ 6718] + bw ( KiB/s): min=716800, max=763904, per=99.48%, avg=740352.00, stdev=33307.56, samples=2 + iops : min= 700, max= 746, avg=723.00, stdev=32.53, samples=2 + lat (msec) : 2=96.58%, 4=2.93%, 10=0.49% + cpu : usr=0.36%, sys=7.53%, ctx=1031, majf=0, minf=9 + IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% + submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + issued rwts: total=0,1024,0,0 short=0,0,0,0 dropped=0,0,0,0 + latency : target=0, window=0, percentile=100.00%, depth=1 + +Run status group 0 (all jobs): + WRITE: bw=727MiB/s (762MB/s), 727MiB/s-727MiB/s (762MB/s-762MB/s), io=1024MiB (1074MB), run=1409-1409msec + +Disk stats (read/write): + md0: ios=0/2009, merge=0/0, ticks=0/2531, in_queue=2531, util=92.94%, aggrios=0/2050, aggrmerge=0/0, aggrticks=0/2499, aggrin_queue=2500, aggrutil=91.91% + nvme0n2: ios=0/2050, merge=0/0, ticks=0/2476, in_queue=2477, util=91.91% + nvme0n3: ios=0/2050, merge=0/0, ticks=0/2522, in_queue=2523, util=91.91% +``` + +从测试结果中可以看见,RAID 1阵列的磁盘写入速度会比单块磁盘相对慢一点,因为RAID 1阵列是将一份数据写入两遍。 + +## 2.6 RAID 10阵列实验 + +建议恢复快照到磁盘刚添加磁盘的状态。最少需要4块额外的硬盘 + +### 2.6.1 部署 + +1. 创建RAID 10阵列 + +```sh +[root@localhost ~]# mdadm -Cv /dev/md0 -a yes -n 4 -l 10 /dev/nvme0n2 /dev/nvme0n3 /dev/nvme0n4 /dev/nvme0n5 +mdadm: layout defaults to n2 +mdadm: layout defaults to n2 +mdadm: chunk size defaults to 512K +mdadm: size set to 5237760K +mdadm: Defaulting to version 1.2 metadata +mdadm: array /dev/md0 started. +[root@localhost ~]# mkfs.ext4 /dev/md0 +mke2fs 1.46.5 (30-Dec-2021) +Creating filesystem with 2618880 4k blocks and 655360 inodes +Filesystem UUID: bd12aabd-52c7-41c3-81b8-bfb89a8ee580 +Superblock backups stored on blocks: + 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632 + +Allocating group tables: done +Writing inode tables: done +Creating journal (16384 blocks): done +Writing superblocks and filesystem accounting information: done + +[root@localhost ~]# mkdir /mnt/RAID10 +[root@localhost ~]# mount /dev/md0 /mnt/RAID10 +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 6.3M 343M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/md0 9.8G 24K 9.3G 1% /mnt/RAID10 +# 这里可以看到,虽然我们使用了4块大小为5G的硬盘,但是最后的实际大小只有10G。 +``` + +2. 检查整列情况 + +```sh +[root@localhost ~]# mdadm -D /dev/md0 +/dev/md0: + Version : 1.2 + Creation Time : Thu Nov 14 17:07:34 2024 + Raid Level : raid10 + Array Size : 10475520 (9.99 GiB 10.73 GB) + Used Dev Size : 5237760 (5.00 GiB 5.36 GB) + Raid Devices : 4 + Total Devices : 4 + Persistence : Superblock is persistent + + Update Time : Thu Nov 14 17:21:26 2024 + State : clean + Active Devices : 4 + Working Devices : 4 + Failed Devices : 0 + Spare Devices : 0 + + Layout : near=2 + Chunk Size : 512K + +Consistency Policy : resync + + Name : localhost.localdomain:0 (local to host localhost.localdomain) + UUID : 7011a5e6:862c3556:6bbba92b:8a61ce4e + Events : 17 + + Number Major Minor RaidDevice State + 0 259 3 0 active sync set-A /dev/nvme0n2 + 1 259 4 1 active sync set-B /dev/nvme0n3 + 2 259 5 2 active sync set-A /dev/nvme0n4 + 3 259 6 3 active sync set-B /dev/nvme0n5 +``` + +### 2.6.2 磁盘写入测试 + +```sh +[root@localhost RAID10]# fio --name=write_test --filename=/mnt/RAID10/testfile --size=1G --bs=1M --rw=write --direct=1 --numjobs=1 +write_test: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=1 +fio-3.35 +Starting 1 process +write_test: Laying out IO file (1 file / 1024MiB) +Jobs: 1 (f=1) +write_test: (groupid=0, jobs=1): err= 0: pid=27150: Thu Nov 14 17:29:34 2024 + write: IOPS=715, BW=715MiB/s (750MB/s)(1024MiB/1432msec); 0 zone resets + clat (usec): min=1122, max=5646, avg=1373.87, stdev=264.26 + lat (usec): min=1140, max=5741, avg=1395.28, stdev=266.74 + clat percentiles (usec): + | 1.00th=[ 1156], 5.00th=[ 1205], 10.00th=[ 1221], 20.00th=[ 1254], + | 30.00th=[ 1287], 40.00th=[ 1303], 50.00th=[ 1336], 60.00th=[ 1369], + | 70.00th=[ 1401], 80.00th=[ 1450], 90.00th=[ 1516], 95.00th=[ 1614], + | 99.00th=[ 2040], 99.50th=[ 2606], 99.90th=[ 5669], 99.95th=[ 5669], + | 99.99th=[ 5669] + bw ( KiB/s): min=733184, max=737852, per=100.00%, avg=735518.00, stdev=3300.77, samples=2 + iops : min= 716, max= 720, avg=718.00, stdev= 2.83, samples=2 + lat (msec) : 2=98.73%, 4=1.07%, 10=0.20% + cpu : usr=0.49%, sys=6.57%, ctx=1026, majf=0, minf=10 + IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% + submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + issued rwts: total=0,1024,0,0 short=0,0,0,0 dropped=0,0,0,0 + latency : target=0, window=0, percentile=100.00%, depth=1 + +Run status group 0 (all jobs): + WRITE: bw=715MiB/s (750MB/s), 715MiB/s-715MiB/s (750MB/s-750MB/s), io=1024MiB (1074MB), run=1432-1432msec + +Disk stats (read/write): + md0: ios=0/1984, merge=0/0, ticks=0/2554, in_queue=2554, util=93.47%, aggrios=0/1024, aggrmerge=0/0, aggrticks=0/1284, aggrin_queue=1284, aggrutil=92.57% + nvme0n4: ios=0/1024, merge=0/0, ticks=0/1272, in_queue=1272, util=92.31% + nvme0n2: ios=0/1024, merge=0/0, ticks=0/1277, in_queue=1278, util=92.31% + nvme0n5: ios=0/1024, merge=0/0, ticks=0/1312, in_queue=1312, util=92.57% + nvme0n3: ios=0/1024, merge=0/0, ticks=0/1277, in_queue=1277, util=92.31% + +``` + +### 2.6.3 损坏磁盘阵列及修复 + +在确认有一块物理硬盘设备出现损坏而不能继续正常使用后,应该使用mdadm命令将其移除,然后查看RAID磁盘阵列的状态,可以发现状态已经改变。 + +```bash +[root@localhost RAID10]# mdadm /dev/md0 -f /dev/nvme0n2 +mdadm: set /dev/nvme0n2 faulty in /dev/md0 +[root@localhost RAID10]# mdadm -D /dev/md0 +/dev/md0: + Version : 1.2 + Creation Time : Thu Nov 14 17:07:34 2024 + Raid Level : raid10 + Array Size : 10475520 (9.99 GiB 10.73 GB) + Used Dev Size : 5237760 (5.00 GiB 5.36 GB) + Raid Devices : 4 + Total Devices : 4 + Persistence : Superblock is persistent + + Update Time : Fri Nov 15 10:05:55 2024 + State : clean, degraded + Active Devices : 3 + Working Devices : 3 + Failed Devices : 1 + Spare Devices : 0 + + Layout : near=2 + Chunk Size : 512K + +Consistency Policy : resync + + Name : localhost.localdomain:0 (local to host localhost.localdomain) + UUID : 7011a5e6:862c3556:6bbba92b:8a61ce4e + Events : 19 + + Number Major Minor RaidDevice State + - 0 0 0 removed + 1 259 4 1 active sync set-B /dev/nvme0n3 + 2 259 5 2 active sync set-A /dev/nvme0n4 + 3 259 6 3 active sync set-B /dev/nvme0n5 + + 0 259 3 - faulty /dev/nvme0n2 +``` + +在RAID 10级别的磁盘阵列中,当RAID 1磁盘阵列中存在一个故障盘时并不影响RAID 10磁盘阵列的使用。当购买了新的硬盘设备后再使用mdadm命令来予以替换即可,在此期间我们可以在/RAID目录中正常地创建或删除文件。由于我们是在虚拟机中模拟硬盘,所以先重启系统,然后再把新的硬盘添加到RAID磁盘阵列中。 + +```bash +[root@localhost ~]# umount /mnt/RAID10 +#有时候失败,告诉设备忙,-f参数都没用 +#可以使用fuser -mk /mnt/RAID10命令杀死所有占用该文件的进程 + +# 先热移除/dev/nvme0n2 +[root@localhost ~]# mdadm /dev/md0 -r /dev/nvme0n2 +mdadm: hot removed /dev/nvme0n2 from /dev/md0 +[root@localhost ~]# mdadm -D /dev/md0 +/dev/md0: + Version : 1.2 + Creation Time : Thu Nov 14 17:07:34 2024 + Raid Level : raid10 + Array Size : 10475520 (9.99 GiB 10.73 GB) + Used Dev Size : 5237760 (5.00 GiB 5.36 GB) + Raid Devices : 4 + Total Devices : 3 + Persistence : Superblock is persistent + + Update Time : Fri Nov 15 10:09:16 2024 + State : clean, degraded + Active Devices : 3 + Working Devices : 3 + Failed Devices : 0 + Spare Devices : 0 + + Layout : near=2 + Chunk Size : 512K + +Consistency Policy : resync + + Name : localhost.localdomain:0 (local to host localhost.localdomain) + UUID : 7011a5e6:862c3556:6bbba92b:8a61ce4e + Events : 22 + + Number Major Minor RaidDevice State + - 0 0 0 removed + 1 259 4 1 active sync set-B /dev/nvme0n3 + 2 259 5 2 active sync set-A /dev/nvme0n4 + 3 259 6 3 active sync set-B /dev/nvme0n5 +# 再重新添加/dev/nvme0n2 +[root@localhost ~]# mdadm /dev/md0 -a /dev/nvme0n2 +mdadm: added /dev/nvme0n2 +[root@localhost ~]# mdadm -D /dev/md0 +/dev/md0: + Version : 1.2 + Creation Time : Thu Nov 14 17:07:34 2024 + Raid Level : raid10 + Array Size : 10475520 (9.99 GiB 10.73 GB) + Used Dev Size : 5237760 (5.00 GiB 5.36 GB) + Raid Devices : 4 + Total Devices : 4 + Persistence : Superblock is persistent + + Update Time : Fri Nov 15 10:10:25 2024 + State : clean, degraded, recovering + Active Devices : 3 + Working Devices : 4 + Failed Devices : 0 + Spare Devices : 1 + + Layout : near=2 + Chunk Size : 512K + +Consistency Policy : resync + + Rebuild Status : 31% complete + + Name : localhost.localdomain:0 (local to host localhost.localdomain) + UUID : 7011a5e6:862c3556:6bbba92b:8a61ce4e + Events : 28 + + Number Major Minor RaidDevice State + 4 259 3 0 spare rebuilding /dev/nvme0n2 + 1 259 4 1 active sync set-B /dev/nvme0n3 + 2 259 5 2 active sync set-A /dev/nvme0n4 + 3 259 6 3 active sync set-B /dev/nvme0n5 +# 会发现,刚添加上的磁盘处于rebuilding的状态,等待一会儿,就会恢复正常了。 + +# 重新再次挂载即可正常使用 +[root@localhost ~]# mount /dev/md0 /mnt/RAID10/ +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 6.3M 343M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/md0 9.8G 1.1G 8.3G 11% /mnt/RAID10 + +# 有的时候,可能会存在/dev/nvme0n2无法添加的情况,对于这种情况,只能将整个整列全部删除干净之后重新创建 +``` + +## 2.7 RAID 5阵列实验+备份盘 + +为了避免多个实验之间相互发生冲突,我们需要保证每个实验的相对独立性,为此需要大家自行将虚拟机还原到初始状态。 + +部署RAID 5磁盘阵列时,至少需要用到3块硬盘,还需要再加一块备份硬盘,所以总计需要在虚拟机中模拟4块硬盘设备 + + +现在创建一个RAID 5磁盘阵列+备份盘。在下面的命令中,参数-n 3代表创建这个RAID 5磁盘阵列所需的硬盘数,参数-l 5代表RAID的级别,而参数-x 1则代表有一块备份盘。当查看/dev/md0(即RAID 5磁盘阵列的名称)磁盘阵列的时候就能看到有一块备份盘在等待中了。 + +```bash +[root@localhost ~]# mdadm -Cv /dev/md0 -n 3 -l 5 -x 1 /dev/nvme0n2 /dev/nvme0n3 /dev/nvme0n4 /dev/nvme0n5 +mdadm: layout defaults to left-symmetric +mdadm: layout defaults to left-symmetric +mdadm: chunk size defaults to 512K +mdadm: size set to 5237760K +mdadm: Defaulting to version 1.2 metadata +mdadm: array /dev/md0 started. +[root@localhost ~]# mdadm -D /dev/md0 +/dev/md0: + Version : 1.2 + Creation Time : Fri Nov 15 10:33:07 2024 + Raid Level : raid5 + Array Size : 10475520 (9.99 GiB 10.73 GB) + Used Dev Size : 5237760 (5.00 GiB 5.36 GB) + Raid Devices : 3 + Total Devices : 4 + Persistence : Superblock is persistent + + Update Time : Fri Nov 15 10:33:33 2024 + State : clean + Active Devices : 3 + Working Devices : 4 + Failed Devices : 0 + Spare Devices : 1 + + Layout : left-symmetric + Chunk Size : 512K + +Consistency Policy : resync + + Name : localhost.localdomain:0 (local to host localhost.localdomain) + UUID : 8f9130a4:06668353:958e7628:985d1167 + Events : 18 + + Number Major Minor RaidDevice State + 0 259 3 0 active sync /dev/nvme0n2 + 1 259 4 1 active sync /dev/nvme0n3 + 4 259 5 2 active sync /dev/nvme0n4 + + 3 259 6 - spare /dev/nvme0n5 +``` + +将部署好的RAID 5磁盘阵列格式化为ext4文件格式,然后挂载到目录上,之后就可以使用了。 + +```bash +[root@localhost ~]# mkfs.ext4 /dev/md0 +mke2fs 1.46.5 (30-Dec-2021) +Creating filesystem with 2618880 4k blocks and 655360 inodes +Filesystem UUID: 33c5ee4e-82de-4c0f-98f1-88cda85292ea +Superblock backups stored on blocks: + 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632 + +Allocating group tables: done +Writing inode tables: done +Creating journal (16384 blocks): done +Writing superblocks and filesystem accounting information: done + +[root@localhost ~]# mkdir /mnt/RAID5 +[root@localhost ~]# echo "/dev/md0 /mnt/RAID5 ext4 defaults 0 0" >> /etc/fstab +[root@localhost ~]# mount -a +mount: (hint) your fstab has been modified, but systemd still uses + the old version; use 'systemctl daemon-reload' to reload. +[root@localhost ~]# systemctl daemon-reload +[root@localhost ~]# mount -a +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 6.3M 343M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/md0 9.8G 24K 9.3G 1% /mnt/RAID5 + +``` + +把硬盘设备/dev/nvme0n2移出磁盘阵列,然后迅速查看/dev/md0磁盘阵列的状态 + +```bash +[root@localhost ~]# mdadm /dev/md0 -f /dev/nvme0n2 +mdadm: set /dev/nvme0n2 faulty in /dev/md0 +# /dev/nvme0n5由原来的spare顶上来变成spare-building +[root@localhost ~]# mdadm -D /dev/md0 +/dev/md0: + Version : 1.2 + Creation Time : Fri Nov 15 10:33:07 2024 + Raid Level : raid5 + Array Size : 10475520 (9.99 GiB 10.73 GB) + Used Dev Size : 5237760 (5.00 GiB 5.36 GB) + Raid Devices : 3 + Total Devices : 4 + Persistence : Superblock is persistent + + Update Time : Fri Nov 15 10:41:21 2024 + State : clean, degraded, recovering + Active Devices : 2 + Working Devices : 3 + Failed Devices : 1 + Spare Devices : 1 + + Layout : left-symmetric + Chunk Size : 512K + +Consistency Policy : resync + + Rebuild Status : 11% complete + + Name : localhost.localdomain:0 (local to host localhost.localdomain) + UUID : 8f9130a4:06668353:958e7628:985d1167 + Events : 21 + + Number Major Minor RaidDevice State + 3 259 6 0 spare rebuilding /dev/nvme0n5 + 1 259 4 1 active sync /dev/nvme0n3 + 4 259 5 2 active sync /dev/nvme0n4 + + 0 259 3 - faulty /dev/nvme0n2 +# 然后过一会儿,/dev/nvme0n5的状态就会变成active sync +``` \ No newline at end of file diff --git a/01.Linux基础/10.磁盘阵列RAID/RAID0.png b/01.Linux基础/10.磁盘阵列RAID/RAID0.png new file mode 100644 index 0000000..4de929f Binary files /dev/null and b/01.Linux基础/10.磁盘阵列RAID/RAID0.png differ diff --git a/01.Linux基础/10.磁盘阵列RAID/RAID1.png b/01.Linux基础/10.磁盘阵列RAID/RAID1.png new file mode 100644 index 0000000..9cabdce Binary files /dev/null and b/01.Linux基础/10.磁盘阵列RAID/RAID1.png differ diff --git a/01.Linux基础/10.磁盘阵列RAID/RAID10.png b/01.Linux基础/10.磁盘阵列RAID/RAID10.png new file mode 100644 index 0000000..83ae8d1 Binary files /dev/null and b/01.Linux基础/10.磁盘阵列RAID/RAID10.png differ diff --git a/01.Linux基础/10.磁盘阵列RAID/RAID5.png b/01.Linux基础/10.磁盘阵列RAID/RAID5.png new file mode 100644 index 0000000..9cc9f3e Binary files /dev/null and b/01.Linux基础/10.磁盘阵列RAID/RAID5.png differ diff --git a/01.Linux基础/10.磁盘阵列RAID/部署磁盘阵列.png b/01.Linux基础/10.磁盘阵列RAID/部署磁盘阵列.png new file mode 100644 index 0000000..f5812e1 Binary files /dev/null and b/01.Linux基础/10.磁盘阵列RAID/部署磁盘阵列.png differ diff --git a/01.Linux基础/10.磁盘阵列RAID/阵列卡.png b/01.Linux基础/10.磁盘阵列RAID/阵列卡.png new file mode 100644 index 0000000..5cd4d7b Binary files /dev/null and b/01.Linux基础/10.磁盘阵列RAID/阵列卡.png differ diff --git a/01.Linux基础/11.LVM逻辑卷管理.md b/01.Linux基础/11.LVM逻辑卷管理.md new file mode 100644 index 0000000..e7bdf6e --- /dev/null +++ b/01.Linux基础/11.LVM逻辑卷管理.md @@ -0,0 +1,617 @@ +# 1. LVM(逻辑卷管理器) + +逻辑卷管理器是Linux系统用于对硬盘分区进行管理的一种机制,理论性较强,其创建初衷是为了解决硬盘设备在创建分区后不易修改分区大小的缺陷。尽管对传统的硬盘分区进行强制扩容或缩容从理论上来讲是可行的,但是却可能造成数据的丢失。而LVM技术是在硬盘分区和文件系统之间添加了一个逻辑层,它提供了一个抽象的卷组,可以把多块硬盘进行卷组合并。这样一来,用户不必关心物理硬盘设备的底层架构和布局,就可以实现对硬盘分区的动态调整。 + +![LVM逻辑图](11.LVM逻辑卷管理/LVM逻辑图.png) + +物理卷处于LVM中的最底层,可以将其理解为物理硬盘、硬盘分区或者RAID磁盘阵列,这都可以。卷组建立在物理卷之上,一个卷组可以包含多个物理卷,而且在卷组创建之后也可以继续向其中添加新的物理卷。逻辑卷是用卷组中空闲的资源建立的,并且逻辑卷在建立后可以动态地扩展或缩小空间。这就是LVM的核心理念。 + +PE最小存储单元,VG通过PV组合好PE,成一个整的;然后LV从VG分出来可供挂载使用 + +## 1.1 部署逻辑卷 + +常用的LVM部署命令 + +| 功能/命令 | 物理卷管理 | 卷组管理 | 逻辑卷管理 | +| --------- | ---------- | --------- | ---------- | +| 扫描 | pvscan | vgscan | lvscan | +| 建立 | pvcreate | vgcreate | lvcreate | +| 显示 | pvdisplay | vgdisplay | lvdisplay | +| 删除 | pvremove | vgremove | lvremove | +| 扩展 | | vgextend | lvextend | +| 缩小 | | vgreduce | lvreduce | + +为了避免多个实验之间相互发生冲突,请大家自行将虚拟机还原到初始状态,并在虚拟机中添加两块新硬盘设备,然后开机 + +第1步:让新添加的两块硬盘设备支持LVM技术 + +```bash +[root@localhost ~]# pvcreate /dev/nvme0n2 /dev/nvme0n3 + Physical volume "/dev/nvme0n2" successfully created. + Physical volume "/dev/nvme0n3" successfully created. +#有时候创建不成功,有残留数据需要重新做分区表 +#[root@localhost ~]#parted /dev/sd[b-c] mklabel msdos +``` + +第2步:把两块硬盘设备加入到storage卷组中,然后查看卷组的状态 + +```bash +[root@localhost ~]# vgcreate storage /dev/nvme0n2 /dev/nvme0n3 + Volume group "storage" successfully created +[root@localhost ~]# vgdisplay storage + --- Volume group --- + VG Name storage + System ID + Format lvm2 + Metadata Areas 2 + Metadata Sequence No 1 + VG Access read/write + VG Status resizable + MAX LV 0 + Cur LV 0 + Open LV 0 + Max PV 0 + Cur PV 2 + Act PV 2 + VG Size 9.99 GiB + PE Size 4.00 MiB + Total PE 2558 + Alloc PE / Size 0 / 0 + Free PE / Size 2558 / 9.99 GiB + VG UUID fhR9kn-q5mT-CxO1-Skst-c5mN-X1nN-TbQpU6 +``` + +第3步:切割出一个约为150MB的逻辑卷设备。 + +这里需要注意切割单位的问题。在对逻辑卷进行切割时有两种计量单位。 + +第一种是以容量为单位,所使用的参数为-L。例如,使用-L 150M生成一个大小为150MB的逻辑卷。 + +另外一种是以基本单元的个数为单位,所使用的参数为-l。每个基本单元的大小默认为4MB。例如,使用-l 37可以生成一个大小为37×4MB=148MB的逻辑卷。 + +```bash +# [root@localhost ~]# lvcreate -n vo -l 37 -I 8M storage +[root@localhost ~]# lvcreate -n vo -l 37 storage + Logical volume "vo" created. +# 每个PE占4M,37*4=148M,远少于/dev/nvme0n2的5G,不会占用到第二个PV(/dev/nvme0n3),所以lsblk只看到占用了/dev/nvme0n2 +[root@localhost ~]# lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS +sr0 11:0 1 1.7G 0 rom +nvme0n1 259:0 0 20G 0 disk +├─nvme0n1p1 259:1 0 1G 0 part /boot +└─nvme0n1p2 259:2 0 19G 0 part + ├─rl-root 253:0 0 17G 0 lvm / + └─rl-swap 253:1 0 2G 0 lvm [SWAP] +nvme0n2 259:3 0 5G 0 disk +└─storage-vo 253:2 0 148M 0 lvm +nvme0n3 259:4 0 5G 0 disk +nvme0n4 259:5 0 5G 0 disk +nvme0n5 259:6 0 5G 0 disk +nvme0n6 259:7 0 5G 0 disk + +[root@localhost ~]# lvdisplay + --- Logical volume --- + LV Path /dev/storage/vo + LV Name vo + VG Name storage + LV UUID X83QDm-cgYS-Vlsj-FHqq-pJWh-bWnY-NhoozO + LV Write Access read/write + LV Creation host, time localhost.localdomain, 2024-11-16 17:35:36 +0800 + LV Status available + # open 0 + LV Size 148.00 MiB + Current LE 37 + Segments 1 + Allocation inherit + Read ahead sectors auto + - currently set to 8192 + Block device 253:2 + + --- Logical volume --- + LV Path /dev/rl/swap + LV Name swap + VG Name rl + LV UUID mStLV7-xYcw-4GHG-vdEC-igob-LNmI-xWVQRF + LV Write Access read/write + LV Creation host, time localhost.localdomain, 2024-11-09 10:51:12 +0800 + LV Status available + # open 2 + LV Size 2.00 GiB + Current LE 512 + Segments 1 + Allocation inherit + Read ahead sectors auto + - currently set to 8192 + Block device 253:1 + + --- Logical volume --- + LV Path /dev/rl/root + LV Name root + VG Name rl + LV UUID yQ6wgx-mgEh-ACiF-Au17-X7Qs-PzQx-0eiqg3 + LV Write Access read/write + LV Creation host, time localhost.localdomain, 2024-11-09 10:51:12 +0800 + LV Status available + # open 1 + LV Size <17.00 GiB + Current LE 4351 + Segments 1 + Allocation inherit + Read ahead sectors auto + - currently set to 8192 + Block device 253:0 +``` + +第4步:把生成好的逻辑卷进行格式化,然后挂载使用。 + +```bash +[root@localhost ~]# mkfs.ext4 /dev/storage/vo +mke2fs 1.46.5 (30-Dec-2021) +Creating filesystem with 151552 1k blocks and 37848 inodes +Filesystem UUID: 11f83627-21f1-42fb-a19b-6ccfdc62453d +Superblock backups stored on blocks: + 8193, 24577, 40961, 57345, 73729 + +Allocating group tables: done +Writing inode tables: done +Creating journal (4096 blocks): done +Writing superblocks and filesystem accounting information: done + +[root@localhost ~]# mkdir /mnt/vo +[root@localhost ~]# mount /dev/storage/vo /mnt/vo +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 5.2M 344M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/mapper/storage-vo 134M 14K 123M 1% /mnt/vo +[root@localhost ~]# lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS +sr0 11:0 1 1.7G 0 rom +nvme0n1 259:0 0 20G 0 disk +├─nvme0n1p1 259:1 0 1G 0 part /boot +└─nvme0n1p2 259:2 0 19G 0 part + ├─rl-root 253:0 0 17G 0 lvm / + └─rl-swap 253:1 0 2G 0 lvm [SWAP] +nvme0n2 259:3 0 5G 0 disk +└─storage-vo 253:2 0 148M 0 lvm /mnt/vo +nvme0n3 259:4 0 5G 0 disk +nvme0n4 259:5 0 5G 0 disk +nvme0n5 259:6 0 5G 0 disk +nvme0n6 259:7 0 5G 0 disk + +[root@localhost ~]# echo "/dev/storage/vo /mnt/vo ext4 defaults 0 0" >> /etc/fstab +``` + +## 1.2 扩容逻辑卷 + +第1步:把上一个实验中的逻辑卷vo扩展至290MB + +```bash +[root@localhost ~]# umount /mnt/vo +[root@localhost ~]# lvextend -L 290M /dev/storage/vo + Rounding size to boundary between physical extents: 292.00 MiB. + Size of logical volume storage/vo changed from 148.00 MiB (37 extents) to 292.00 MiB (73 extents). + Logical volume storage/vo successfully resized. +``` + +第2步:检查硬盘完整性,并重置硬盘容量,否则挂载了还是148M + +```bash +[root@localhost ~]# e2fsck -f /dev/storage/vo +e2fsck 1.46.5 (30-Dec-2021) +Pass 1: Checking inodes, blocks, and sizes +Pass 2: Checking directory structure +Pass 3: Checking directory connectivity +Pass 4: Checking reference counts +Pass 5: Checking group summary information +/dev/storage/vo: 11/37848 files (0.0% non-contiguous), 15165/151552 blocks +[root@localhost ~]# resize2fs /dev/storage/vo +resize2fs 1.46.5 (30-Dec-2021) +Resizing the filesystem on /dev/storage/vo to 299008 (1k) blocks. +The filesystem on /dev/storage/vo is now 299008 (1k) blocks long. +``` + +第3步:重新挂载硬盘设备并查看挂载状态 + +```bash +[root@localhost ~]# systemctl daemon-reload +[root@localhost ~]# mount -a +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 5.2M 344M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/mapper/storage-vo 268M 14K 250M 1% /mnt/vo +``` + +第4步,如果扩容到6G,则会占用到nvme0n3的pv + +```shell +[root@localhost ~]# umount /mnt/vo +[root@localhost ~]# lvextend -L 6G /dev/storage/vo + Size of logical volume storage/vo changed from 292.00 MiB (73 extents) to 6.00 GiB (1536 extents). + Logical volume storage/vo successfully resized. +[root@localhost ~]# e2fsck -f /dev/storage/vo +e2fsck 1.46.5 (30-Dec-2021) +Pass 1: Checking inodes, blocks, and sizes +Pass 2: Checking directory structure +Pass 3: Checking directory connectivity +Pass 4: Checking reference counts +Pass 5: Checking group summary information +/dev/storage/vo: 11/73704 files (0.0% non-contiguous), 24683/299008 blocks +[root@localhost ~]# resize2fs /dev/storage/vo +resize2fs 1.46.5 (30-Dec-2021) +Resizing the filesystem on /dev/storage/vo to 6291456 (1k) blocks. +The filesystem on /dev/storage/vo is now 6291456 (1k) blocks long. + +[root@localhost ~]# mount -a +[root@localhost ~]# lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS +sr0 11:0 1 1.7G 0 rom +nvme0n1 259:0 0 20G 0 disk +├─nvme0n1p1 259:1 0 1G 0 part /boot +└─nvme0n1p2 259:2 0 19G 0 part + ├─rl-root 253:0 0 17G 0 lvm / + └─rl-swap 253:1 0 2G 0 lvm [SWAP] +nvme0n2 259:3 0 5G 0 disk +└─storage-vo 253:2 0 6G 0 lvm /mnt/vo +nvme0n3 259:4 0 5G 0 disk +└─storage-vo 253:2 0 6G 0 lvm /mnt/vo +nvme0n4 259:5 0 5G 0 disk +nvme0n5 259:6 0 5G 0 disk +nvme0n6 259:7 0 5G 0 disk +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 5.2M 344M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/mapper/storage-vo 5.7G 14K 5.4G 1% /mnt/vo +``` + +## 1.3 缩小逻辑卷 + +第1步:检查文件系统的完整性 + +```bash +[root@localhost ~]# umount /mnt/vo +[root@localhost ~]# e2fsck -f /dev/storage/vo +e2fsck 1.46.5 (30-Dec-2021) +Pass 1: Checking inodes, blocks, and sizes +Pass 2: Checking directory structure +Pass 3: Checking directory connectivity +Pass 4: Checking reference counts +Pass 5: Checking group summary information +/dev/storage/vo: 11/1529856 files (0.0% non-contiguous), 391996/6291456 blocks +``` + +第2步:把逻辑卷vo的容量减小到120MB + +```bash +[root@localhost ~]# resize2fs /dev/storage/vo 120M +resize2fs 1.46.5 (30-Dec-2021) +Resizing the filesystem on /dev/storage/vo to 122880 (1k) blocks. +The filesystem on /dev/storage/vo is now 122880 (1k) blocks long. + +[root@localhost ~]# lvreduce -L 120M /dev/storage/vo + File system ext4 found on storage/vo. + File system size (120.00 MiB) is equal to the requested size (120.00 MiB). + File system reduce is not needed, skipping. + Size of logical volume storage/vo changed from 6.00 GiB (1536 extents) to 120.00 MiB (30 extents). + Logical volume storage/vo successfully resized. +``` + +第3步:重新挂载文件系统并查看系统状态 + +```bash +[root@localhost ~]# mount -a +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 5.2M 344M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/mapper/storage-vo 108M 14K 99M 1% /mnt/vo +``` + +## 1.4 逻辑卷快照 + +LVM还具备有“快照卷”功能,该功能类似于虚拟机软件的还原时间点功能。例如,可以对某一个逻辑卷设备做一次快照,如果日后发现数据被改错了,就可以利用之前做好的快照卷进行覆盖还原。LVM的快照卷功能有两个特点: + +- 快照卷的容量必须等同于逻辑卷的容量; +- 快照卷仅一次有效,一旦执行还原操作后则会被立即自动删除。 + +```bash +[root@localhost ~]# vgdisplay + --- Volume group --- + VG Name storage + System ID + Format lvm2 + Metadata Areas 2 + Metadata Sequence No 5 + VG Access read/write + VG Status resizable + MAX LV 0 + Cur LV 1 + Open LV 1 + Max PV 0 + Cur PV 2 + Act PV 2 + VG Size 9.99 GiB + PE Size 4.00 MiB + Total PE 2558 + Alloc PE / Size 30 / 120.00 MiB + Free PE / Size 2528 / <9.88 GiB # 容量剩余9.88G + VG UUID fhR9kn-q5mT-CxO1-Skst-c5mN-X1nN-TbQpU6 + + --- Volume group --- + VG Name rl + System ID + Format lvm2 + Metadata Areas 1 + Metadata Sequence No 3 + VG Access read/write + VG Status resizable + MAX LV 0 + Cur LV 2 + Open LV 2 + Max PV 0 + Cur PV 1 + Act PV 1 + VG Size <19.00 GiB + PE Size 4.00 MiB + Total PE 4863 + Alloc PE / Size 4863 / <19.00 GiB + Free PE / Size 0 / 0 + VG UUID GbAoZ2-IvHg-YKLQ-366L-f2lq-qo71-T79Hm6 +``` + +接下来用重定向往逻辑卷设备所挂载的目录中写入一个文件 + +```bash +[root@localhost ~]# echo "hello world" > /mnt/vo/readme.txt +[root@localhost ~]# ls -l /mnt/vo +total 14 +drwx------. 2 root root 12288 Apr 18 13:38 lost+found +-rw-r--r--. 1 root root 12 Apr 18 13:48 readme.txt +``` + +第1步:使用-s参数生成一个快照卷,使用-L参数指定切割的大小。 + +另外,还需要在命令后面写上是针对哪个逻辑卷执行的快照操作。**强烈建议快照大小和原来的LVM一样大** + +```bash +[root@localhost ~]# lvcreate -L 120M -s -n SNAP /dev/storage/vo +#强烈建议快照大小和原来的LVM一样大,虽然指定大小是120M,并不是创建一个120M大小的快照,除非修改、新增的文件超出120M才会出事情,删除不影响。 + Logical volume "SNAP" created. +[root@localhost ~]# lvdisplay + --- Logical volume --- + LV Path /dev/storage/vo + LV Name vo + VG Name storage + LV UUID X83QDm-cgYS-Vlsj-FHqq-pJWh-bWnY-NhoozO + LV Write Access read/write + LV Creation host, time localhost.localdomain, 2024-11-16 17:35:36 +0800 + LV snapshot status source of + SNAP [active] + LV Status available + # open 1 + LV Size 120.00 MiB + Current LE 30 + Segments 1 + Allocation inherit + Read ahead sectors auto + - currently set to 8192 + Block device 253:2 + + --- Logical volume --- + LV Path /dev/storage/SNAP + LV Name SNAP + VG Name storage + LV UUID om37W1-JVXY-yctX-4Rm7-Bh9L-ERVo-JlqTuV + LV Write Access read/write + LV Creation host, time localhost.localdomain, 2024-11-16 17:43:02 +0800 + LV snapshot status active destination for vo + LV Status available + # open 0 + LV Size 120.00 MiB + Current LE 30 + COW-table size 120.00 MiB + COW-table LE 30 + Allocated to snapshot 0.01% + Snapshot chunk size 4.00 KiB + Segments 1 + Allocation inherit + Read ahead sectors auto + - currently set to 8192 + Block device 253:5 + + --- Logical volume --- + LV Path /dev/rl/swap + LV Name swap + VG Name rl + LV UUID mStLV7-xYcw-4GHG-vdEC-igob-LNmI-xWVQRF + LV Write Access read/write + LV Creation host, time localhost.localdomain, 2024-11-09 10:51:12 +0800 + LV Status available + # open 2 + LV Size 2.00 GiB + Current LE 512 + Segments 1 + Allocation inherit + Read ahead sectors auto + - currently set to 8192 + Block device 253:1 + + --- Logical volume --- + LV Path /dev/rl/root + LV Name root + VG Name rl + LV UUID yQ6wgx-mgEh-ACiF-Au17-X7Qs-PzQx-0eiqg3 + LV Write Access read/write + LV Creation host, time localhost.localdomain, 2024-11-09 10:51:12 +0800 + LV Status available + # open 1 + LV Size <17.00 GiB + Current LE 4351 + Segments 1 + Allocation inherit + Read ahead sectors auto + - currently set to 8192 + Block device 253:0 +``` + +第2步:在逻辑卷所挂载的目录中创建一个50MB的垃圾文件,然后再查看快照卷的状态。可以发现存储空间占的用量上升了 + +```bash +[root@localhost ~]# dd if=/dev/zero of=/mnt/vo/files count=1 bs=50M +1+0 records in +1+0 records out +104857600 bytes (105 MB) copied, 3.29409 s, 31.8 MB/s +[root@localhost ~]# lvdisplay + --- Logical volume --- + LV Path /dev/storage/vo + LV Name vo + VG Name storage + LV UUID X83QDm-cgYS-Vlsj-FHqq-pJWh-bWnY-NhoozO + LV Write Access read/write + LV Creation host, time localhost.localdomain, 2024-11-16 17:35:36 +0800 + LV snapshot status source of + SNAP [active] + LV Status available + # open 1 + LV Size 120.00 MiB + Current LE 30 + Segments 1 + Allocation inherit + Read ahead sectors auto + - currently set to 8192 + Block device 253:2 + + --- Logical volume --- + LV Path /dev/storage/SNAP + LV Name SNAP + VG Name storage + LV UUID om37W1-JVXY-yctX-4Rm7-Bh9L-ERVo-JlqTuV + LV Write Access read/write + LV Creation host, time localhost.localdomain, 2024-11-16 17:43:02 +0800 + LV snapshot status active destination for vo + LV Status available + # open 0 + LV Size 120.00 MiB + Current LE 30 + COW-table size 120.00 MiB + COW-table LE 30 + Allocated to snapshot 41.90% # 从0.01变成了41.90 + Snapshot chunk size 4.00 KiB + Segments 1 + Allocation inherit + Read ahead sectors auto + - currently set to 8192 + Block device 253:5 + + --- Logical volume --- + LV Path /dev/rl/swap + LV Name swap + VG Name rl + LV UUID mStLV7-xYcw-4GHG-vdEC-igob-LNmI-xWVQRF + LV Write Access read/write + LV Creation host, time localhost.localdomain, 2024-11-09 10:51:12 +0800 + LV Status available + # open 2 + LV Size 2.00 GiB + Current LE 512 + Segments 1 + Allocation inherit + Read ahead sectors auto + - currently set to 8192 + Block device 253:1 + + --- Logical volume --- + LV Path /dev/rl/root + LV Name root + VG Name rl + LV UUID yQ6wgx-mgEh-ACiF-Au17-X7Qs-PzQx-0eiqg3 + LV Write Access read/write + LV Creation host, time localhost.localdomain, 2024-11-09 10:51:12 +0800 + LV Status available + # open 1 + LV Size <17.00 GiB + Current LE 4351 + Segments 1 + Allocation inherit + Read ahead sectors auto + - currently set to 8192 + Block device 253:0 +``` + +第3步:为了校验SNAP快照卷的效果,需要对逻辑卷进行快照还原操作。在此之前记得先卸载掉逻辑卷设备与目录的挂载。 + +```bash +[root@localhost ~]# umount /mnt/vo +[root@localhost ~]# lvconvert --merge /dev/storage/SNAP + Merging of volume storage/SNAP started. + storage/vo: Merged: 31.39% + storage/vo: Merged: 100.00% +``` + +第4步:快照卷会被自动删除掉,并且刚刚在逻辑卷设备被执行快照操作后再创建出来的50MB的垃圾文件也被清除了 + +```bash +[root@localhost ~]# mount -a +[root@localhost ~]# ls /mnt/vo/ +lost+found readme.txt +``` + +## 1.5 删除逻辑卷 + +第1步:取消逻辑卷与目录的挂载关联,删除配置文件中永久生效的设备参数。 + +```bash +[root@localhost ~]# umount /mnt/vo/ +[root@localhost ~]# vi /etc/fstab +# +# /etc/fstab +# Created by anaconda on Mon Apr 15 17:31:00 2019 +# +# Accessible filesystems, by reference, are maintained under '/dev/disk' +# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info +# +/dev/mapper/centos-root / xfs defaults 0 0 +UUID=63e91158-e754-41c3-b35d-7b9698e71355 /boot xfs defaults 0 0 +/dev/mapper/centos-swap swap swap defaults 0 0 +``` + +第2步:删除逻辑卷设备,需要输入y来确认操作 + +```bash +[root@localhost ~]# lvremove /dev/storage/vo +Do you really want to remove active logical volume storage/vo? [y/n]: y + Logical volume "vo" successfully removed +``` + +第3步:删除卷组,此处只写卷组名称即可,不需要设备的绝对路径。 + +```bash +[root@localhost ~]# vgremove storage + Volume group "storage" successfully removed +``` + +第4步:删除物理卷设备 + +```bash +[root@localhost ~]# pvremove /dev/nvme0n2 /dev/nvme0n3 + Labels on physical volume "/dev/nvme0n2" successfully wiped. + Labels on physical volume "/dev/nvme0n3" successfully wiped. +``` \ No newline at end of file diff --git a/01.Linux基础/11.LVM逻辑卷管理/LVM逻辑图.png b/01.Linux基础/11.LVM逻辑卷管理/LVM逻辑图.png new file mode 100644 index 0000000..e560700 Binary files /dev/null and b/01.Linux基础/11.LVM逻辑卷管理/LVM逻辑图.png differ diff --git a/01.Linux基础/12.软件包管理.md b/01.Linux基础/12.软件包管理.md new file mode 100644 index 0000000..e029dd7 --- /dev/null +++ b/01.Linux基础/12.软件包管理.md @@ -0,0 +1,647 @@ +# 1. 软件包的类型 + +- 源码包 + - 需要编译 +- 二进制包 + - 已编译 + +常见二进制包类型 + +- RedHat/Centos + - RPM + - 工具 + - rpm + - yum(在线安装,自动解决依赖关系) + - dnf(yum的升级版本,在RockyLinux中,yum命令是dnf的软连接) +- Ubuntu/Debain + - DPKG + - 工具 + - dpkg + - apt(在线安装,自动解决依赖关系) + +注意!不管是源码包,还是二进制包,安装时都可能会有依赖关系 + +# 2. yum/dnf + +yum/dnf是红帽系列的发行版本上用来下载和安装软件包的一个工具 + +我们可以使用`yum install xxx`命令来安装一个软件 + +但是大家有没有考虑过一个问题?yum工具是去哪里找到这些软件包的呢? + +这就不得不提到`yum源`,所谓`源`的意思就是指这些软件包的来源,也可以成称之为`软件仓库` + +## 2.1 dnf与yum的对比 + +**YUM (Yellowdog Updater, Modified)**: + +- 是 RHEL 8 及之前版本使用的包管理工具。 +- 提供了一个简单的命令行接口,用来安装、更新、删除和管理 RPM 软件包。 +- 基于 Python 2 开发,在性能和依赖性解析方面存在一定的限制。 + +**DNF (Dandified YUM)**: + +- 是 YUM 的下一代替代工具,首次在 Fedora 18 引入,并从 RHEL 8 开始逐步取代 YUM,到了 RHEL 9 完全淘汰 YUM。 +- 采用了更先进的依赖解析器和更高效的代码。 +- 基于 Python 3 开发,支持现代特性和技术,性能更高。 + +**RockyLinux中yum命令的本质:** + +```sh +[root@localhost bin]# ll /usr/bin/ | grep dnf +lrwxrwxrwx. 1 root root 5 Apr 14 2024 dnf -> dnf-3 +-rwxr-xr-x. 1 root root 2094 Apr 14 2024 dnf-3 +lrwxrwxrwx. 1 root root 5 Apr 14 2024 yum -> dnf-3 +``` + +由此可以看出,yum命令和dnf命令都链接自dnf-3。实际上dnf-3才是包管理器的真正可执行程序,代表基于python3开发。这样链接是为了我们使用起来更方便。 + +## 2.2 官方源 + +一些有名的大厂或者高校都有提供他们整合好的`软件仓库` + +- 阿里云镜像:[https://mirrors.aliyun.com/rockylinux/](https://mirrors.aliyun.com/rockylinux/) +- 腾讯云镜像:[https://mirrors.cloud.tencent.com/rocky/](https://mirrors.cloud.tencent.com/rocky/) +- 中科大镜像:[https://mirrors.ustc.edu.cn/rocky/](https://mirrors.ustc.edu.cn/rocky/) +- 上海交大镜像:[https://mirrors.sjtug.sjtu.edu.cn/rocky/](https://mirrors.sjtug.sjtu.edu.cn/rocky/) +- 西安交大镜像:[https://mirrors.xjtu.edu.cn/rocky/](https://mirrors.xjtu.edu.cn/rocky/) +- 浙江大学镜像:[https://mirrors.zju.edu.cn/rocky/](https://mirrors.zju.edu.cn/rocky/) +- 南京大学镜像:[https://mirrors.nju.edu.cn/rocky/](https://mirrors.nju.edu.cn/rocky/) +- 山东大学镜像:[https://mirrors.sdu.edu.cn/rocky/](https://mirrors.sdu.edu.cn/rocky/) +- 兰州大学镜像:[https://mirror.lzu.edu.cn/rocky/](https://mirror.lzu.edu.cn/rocky/) +- 大连东软镜像:[https://mirrors.neusoft.edu.cn/rocky/](https://mirrors.neusoft.edu.cn/rocky/) + +```bash +[root@localhost ~]# yum repolist # 查看现有的软件仓库 +repo id repo name +appstream Rocky Linux 9 - AppStream +baseos Rocky Linux 9 - BaseOS +extras Rocky Linux 9 - Extras +``` + +软件源相关文件存放的位置`/etc/yum.repos.d/` + +```bash +[root@localhost ~]# ls /etc/yum.repos.d/ +rocky-addons.repo rocky-devel.repo rocky-extras.repo rocky.repo + +# 这里面的一个个.repo文件就是一个个软件仓库 +``` + +可以尝试添加中科大软件仓库,下面的是RockyLinux的添加命令 + +```bash +# 备份原来的源 +[root@localhost ~]# mkdir /repobackup +[root@localhost ~]# cp /etc/yum.repos.d/* /repobackup/ +# 使用sed命令,替换原来repo文件中的内容 +[root@localhost ~]# sed -e 's|^mirrorlist=|#mirrorlist=|g' \ + -e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.ustc.edu.cn/rocky|g' \ + -i.bak \ + /etc/yum.repos.d/rocky-extras.repo \ + /etc/yum.repos.d/rocky.repo + +# 更新缓存 +[root@localhost ~]# yum makecache +Rocky Linux 9 - BaseOS 4.1 MB/s | 2.3 MB 00:00 +Rocky Linux 9 - AppStream 9.2 MB/s | 8.3 MB 00:00 +Rocky Linux 9 - Extras 34 kB/s | 15 kB 00:00 +Metadata cache created. +[root@localhost ~]# yum repolist +repo id repo name +appstream Rocky Linux 9 - AppStream +baseos Rocky Linux 9 - BaseOS +extras Rocky Linux 9 - Extras +``` + +- EPEL源(Extra Packages for Enterprise Linux)为“红帽系”的操作系统提供额外的软件包 + - `yum -y install epel-release` + +## 2.3 yum常用命令 + +### 2.3.1 **查看当前可用仓库** + +```bash +[root@localhost ~]# yum clean all # 清空缓存及其他文件 +[root@localhost ~]# yum makecache # 重建缓存 +[root@localhost ~]# yum repolist # 查询可用的仓库 +``` + +### 2.3.2 **查找软件包** + +注意:有些命令跟软件包的名字是不同的 + +```bash +1. 通过命令的名字查找是属于哪个软件包 +[root@localhost ~]# yum provides pstree +Last metadata expiration check: 0:00:20 ago on Fri Nov 22 14:50:43 2024. +psmisc-23.4-3.el9.x86_64 : Utilities for managing processes on your system +Repo : @System +Matched from: +Filename : /usr/bin/pstree + +psmisc-23.4-3.el9.x86_64 : Utilities for managing processes on your system +Repo : baseos +Matched from: +Filename : /usr/bin/pstree + +2. 通过配置文件查找属于哪个软件包提供的 +[root@localhost ~]# yum provides /etc/ssh/sshd_config +Last metadata expiration check: 0:00:56 ago on Fri Nov 22 14:50:43 2024. +openssh-server-8.7p1-38.el9.x86_64 : An open source SSH server daemon +Repo : @System +Matched from: +Filename : /etc/ssh/sshd_config + +openssh-server-8.7p1-43.el9.x86_64 : An open source SSH server daemon +Repo : baseos +Matched from: +Filename : /etc/ssh/sshd_config + +3. 通过search参数查找软件包 +[root@localhost ~]# yum search psmisc +Last metadata expiration check: 0:02:07 ago on Fri Nov 22 14:50:43 2024. +======================================= Name Exactly Matched: psmisc ======================================== +psmisc.x86_64 : Utilities for managing processes on your system + +4. 查看系统中的安装包 +[root@localhost ~]# yum list installed # 查看已安装的所有软件包 +[root@localhost ~]# yum list # 查看所有可以安装的软件包 +[root@localhost ~]# yum list httpd +``` + +### 2.3.3 **安装软件包** + +```bash +# yum install xxx + +- 安装httpd软件包 +[root@localhost ~]# yum install -y httpd +-y:一律允许 +[root@localhost ~]# yum install -y httpd vim nginx # 多个软件包之间用空格隔开 +[root@localhost ~]# yum reinstall httpd # 重新安装 +[root@localhost ~]# yum update httpd # 更新软件包的版本-laster +[root@localhost ~]# yum update # 更新所有软件包到软件仓库的最新版本 + +# 从本地安装 +[root@localhost ~]# yum install -y httpd-2.4.57-11.el9_4.x86_64.rpm +Last metadata expiration check: 0:06:47 ago on Fri Nov 22 14:50:43 2024. +Error: + Problem: conflicting requests + - nothing provides httpd-core = 2.4.57-11.el9_4 needed by httpd-2.4.57-11.el9_4.x86_64 from @commandline +(try to add '--skip-broken' to skip uninstallable packages or '--nobest' to use not only best candidate packages) + +# 从URL安装 +[root@localhost ~]# yum install -y https://rpmfind.net/linux/almalinux/9.4/AppStream/x86_64/os/Packages/httpd-2.4.57-11.el9_4.x86_64.rpm +Last metadata expiration check: 0:07:55 ago on Fri Nov 22 14:50:43 2024. +httpd-2.4.57-11.el9_4.x86_64.rpm 27 kB/s | 45 kB 00:01 +Error: + Problem: conflicting requests + - nothing provides httpd-core = 2.4.57-11.el9_4 needed by httpd-2.4.57-11.el9_4.x86_64 from @commandline +(try to add '--skip-broken' to skip uninstallable packages or '--nobest' to use not only best candidate packages) +``` + +### 2.3.4 管理软件包组 + +软件包组就是把一些相关的软件包给整合起来,可以打包安装和卸载 + +```sh +[root@localhost ~]# export LANG="en-US.utf8" 切换英文环境 +[root@localhost ~]# yum grouplist +Failed to set locale, defaulting to C.UTF-8 +Last metadata expiration check: 0:08:59 ago on Fri Nov 22 14:50:43 2024. +Available Environment Groups: + Server with GUI + Server + Workstation + KDE Plasma Workspaces + Custom Operating System + Virtualization Host +Installed Environment Groups: + Minimal Install +Available Groups: + Fedora Packager + VideoLAN Client + Xfce + Legacy UNIX Compatibility + Console Internet Tools + Container Management + Development Tools + .NET Development + Graphical Administration Tools + Headless Management + Network Servers + RPM Development Tools + Scientific Support + Security Tools + Smart Card Support + System Tools + +[root@localhost ~]# yum groupinfo "Server with GUI" +[root@localhost ~]# yum groupinstall "Server with GUI" +[root@localhost ~]# yum -y groupremove "Server with GUI" +``` + +#### 2.3.4.1 切换图形化模式 + +- 临时切换到图形化模式 + +```python +[root@localhost ~]# systemctl isolate graphical.target +``` + +- 永久切换(开机进入图形化) + +```sh +[root@localhost ~]# systemctl set-default graphical.target + +# 验证启动模式 +[root@localhost ~]# systemctl get-default +multi-user.target +``` + +- 临时切换到命令行模式 + +```sh +[root@localhost ~]# systemctl isolate multi-user.target +``` + +- 永久切换 + +```sh +[root@localhost ~]# systemctl set-default multi-user.target +``` + +#### 2.3.4.2 **软件包组解释** + +##### 2.3.4.2.1 **Environment Groups(环境组)** + +- **Server with GUI** + GNOME 桌面环境及相关应用 +- **Server** + 无图形界面的纯服务器环境,包含基本的服务器组件(SSH、存储管理、网络管理工具等) +- **Workstation** + 桌面工作站环境,适合普通用户和开发人员,包含办公软件、多媒体支持和开发工具 +- **KDE Plasma Workspaces** + KDE Plasma 桌面环境及其相关工具 +- **Custom Operating System** + 自定义操作系统环境,用户可以手动选择需要的软件包 +- **Virtualization Host** + 专用于虚拟化的环境,包含 KVM、`libvirt` 和虚拟化管理工具 +- **Minimal Install** + 最小化安装,仅包含基础系统和命令行工具 + +------ + +##### 2.3.4.2.2 **Available Groups(可用的软件包组)** + +- **Fedora Packager** + 用于构建 Fedora 或 RHEL/CentOS 软件包的工具(如 `rpmdevtools` 和 `mock`) +- **VideoLAN Client** + VLC 媒体播放器及其依赖项 +- **Xfce** + 轻量级桌面环境,适合资源受限的设备或低性能机器 +- **Legacy UNIX Compatibility** + 提供传统 UNIX 系统兼容的工具和库(如 `csh` 和 `compat-libstdc++`) +- **Console Internet Tools** + 基于命令行的互联网工具(如 `wget`、`curl` 和文本浏览器 `lynx`) +- **Container Management** + 用于管理容器的工具(如 Podman 或 Docker) +- **Development Tools** + 常用开发工具(如 `gcc` 编译器、`gdb` 调试器和 `git` 版本控制工具) +- **.NET Development** + 用于开发和运行 .NET 应用程序的工具和运行时 +- **Graphical Administration Tools** + 图形化的系统管理工具(如磁盘管理和网络配置工具) +- **Headless Management** + 用于远程管理无图形界面服务器的工具 +- **Network Servers** + 常见网络服务(如 `httpd`、`vsftpd` 和 `bind`) +- **RPM Development Tools** + 用于构建和维护 RPM 软件包的工具(如 `rpmbuild` 和 `createrepo`) +- **Scientific Support** + 科学计算工具和库(如 `numpy` 和 `scipy`) +- **Security Tools** + 安全扫描、入侵检测和分析工具(如 `nmap`、`wireshark` 和 `clamav`) +- **Smart Card Support** + 与智能卡相关的工具和库,支持基于智能卡的身份认证 +- **System Tools** + 常用的系统管理工具(如磁盘管理和日志分析工具) + +### 2.3.5 **卸载** + +```bash +[root@localhost ~]# yum -y remove httpd # 卸载httpd软件包 +[root@localhost ~]# yum -y groupremove "Server with GUI" # 卸载软件包组 +``` + +### 2.3.6 **历史记录** + +用来查看 yum 软件包管理器的历史记录的 + +```bash +[root@localhost ~]# yum history +[root@localhost ~]# yum history info 4 # 查看具体某个yum记录的信息 +``` + +## 2.4 yum自建源 + +除了使用上述提到的官方源之外,我们也可以自己搭建一个自建源用来为系统提供软件仓库 + +### 2.4.1 实验一:本地yum镜像仓库 + +可以通过安装系统时下载的iso中的软件包来提供本地软件仓库 + +RockyLinux ISO下载地址:https://dl.rockylinux.org/vault/rocky/9.4/isos/x86_64/Rocky-9.4-x86_64-dvd.iso + +iso文件中具有一些基础的软件包 + +```bash +- 挂载本地ISO光盘: +[root@localhost ~]# ls +Rocky-9.4-x86_64-dvd.iso anaconda-ks.cfg +[root@localhost ~]# mkdir /media/cdrom +[root@localhost ~]# mount Rocky-9.4-x86_64-dvd.iso /media/cdrom/ +mount: /media/cdrom: WARNING: source write-protected, mounted read-only. +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 5.2M 344M 2% /run +/dev/mapper/rl-root 17G 12G 5.1G 71% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +/dev/loop0 11G 11G 0 100% /media/cdrom + +- 清空原有的软件仓库 +[root@localhost ~]# rm -f /etc/yum.repos.d/* + +- 配置本地yum +[root@localhost ~]# vim /etc/yum.repos.d/local.repo +[BaseOS] +name=RockyLinux 9.4 BaseOS +baseurl=file:///media/cdrom/BaseOS +enabled=1 +gpgcheck=0 + +[AppStream] +name=RockyLinux 9.4 AppStream +baseurl=file:///media/cdrom/AppStream +enabled=1 +gpgcheck=0 + +=======================字段解释========================= +-name 软件仓库名字 +-baseurl 软件包的存放的地址 +-enabled 是否启用这个仓库 +-gpgcheck 是否校验软件包 + +# 安装软件验证 +[root@localhost cdrom]# yum install -y httpd +``` + +### 2.4.2 实验二:提供内网服务器软件包更新 + +一台server1服务器可以访问公网,一台server2服务器无法访问公网 + +我们可以在这台能够访问公网的服务器上下载好软件包,通过http或者ftp提供给内网中的server2机器安装软件包 + +**server1:** + +第一步:下载软件包,可以使用`--downloadonly`选项仅下载不安装 + +第二步:将软件包放到合适的位置上,并且创建软件索引数据 + +```bash +[root@localhost ~]# yum clean all # 清理缓存 +[root@localhost ~]# yum update -y --downloadonly --downloaddir=/packages # 仅下载更新包 +[root@localhost ~]# ls /packages/ # 查看下载的包 + +[root@localhost ~]# mkdir /var/ftp/update +[root@localhost ~]# find /packages -iname "*.rpm" -exec cp -rf {} /var/ftp/update/ \; +[root@localhost ~]# yum -y install createrepo # createrepo用来创建软件包的索引 +[root@localhost ~]# createrepo /var/ftp/update/ # 创建软件包的索引 +``` + +第三步:部署ftp文件服务器,将软件包通过ftp协议共享给sever2 + +```bash +# 部署ftp服务并且启动 +[root@localhost ~]# yum -y install vsftpd +[root@localhost ~]# vim /etc/vsftpd/vsftpd.conf # 更改ftp的配置文件,允许匿名登录 +anonymous_enable=yes + +[root@localhost ~]# systemctl start vsftpd + +# 关闭防火墙和selinux +[root@localhost ~]# systemctl stop firewalld +[root@localhost ~]# setenforce 0 +``` + +**server2:** + +在server2上我们编写repo文件,软件的来源是有server1通过ftp服务提供 + +```bash +[root@localhost ~]# rm -rf /etc/yum.repos.d/*.repo #干掉所有的仓库 +[root@localhost ~]# vi /etc/yum.repos.d/update.repo +[update] +name=RockyLinux update +baseurl=ftp://192.168.88.104/update +gpgcheck=0 +enabled=1 + +# 关闭防火墙和selinux +[root@localhost ~]# systemctl stop firewalld +[root@localhost ~]# setenforce 0 + +[root@localhost ~]# yum clean all +[root@localhost ~]# yum update -y # 更新测试 +``` + +# 3. RPM + +在RHEL系列的系统上所使用的软件包为rpm包,上面使用的yum为在线安装软件包。除了在线的安装方法,我们还可以将rpm包下载到本地,然后使用rpm工具来安装这个软件包 + +- rpm工具使用分为安装、查询、验证、更新、删除等操作 +- rpm包的格式说明: + +```bash +[root@atopos ~]# rpm -ivh httpd-2.4.6-93.el7.centos.x86_64.rpm +- httpd:包名 +- 2:主版本号 +- 4:次版本号 +- 6:修订次数,指的是第几次修改bug +- 93:release(第几次发布,指的是简单的修改参数) +- e17:操作系统版本 +- x86_64:64位操作系统 +``` + +- 命令格式 + +```bash +[root@atopos ~]# rpm [选项] 软件包 +``` + +## 3.1 选项 + +**安装:** + +- -i:install的意思,安装软件包 +- -v:显示附加信息,提供更多详细信息 +- -V:校验,对已安装的软件进行校验 + +**查询:** + +- -q:查询,一般跟下面的参数配合使用 +- -a:查询所有已安装的软件包 +- -f:系统文件名(查询系统文件属于哪个安装包) +- -i:显示已安装的rpm软件包信息 +- -l:查询软件包文件的安装位置 +- -p:查询未安装软件包的相关信息 +- -R:查询软件包的依赖性 + +**卸载:** + +- -e:erase +- --nodeps:忽略依赖 + +**升级:** + +- -U:一般配合vh使用 + + + +## 3.2 实例:安装 + +```bash +[root@localhost ~]# rpm -ivh https://rpmfind.net/linux/almalinux/9.4/AppStream/x86_64/os/Packages/httpd-2.4.57-11.el9_4.x86_64.rpm + +# 或者 +[root@localhost ~]# wget https://rpmfind.net/linux/almalinux/9.4/AppStream/x86_64/os/Packages/httpd-2.4.57-11.el9_4.x86_64.rpm +[root@localhost ~]# rpm -ivh httpd-2.4.57-11.el9_4.x86_64.rpm +错误:依赖检测失败: + /etc/mime.types 被 httpd-2.4.6-95.el7.centos.x86_64 需要 + httpd-tools = 2.4.6-95.el7.centos 被 httpd-2.4.6-95.el7.centos.x86_64 需要 + libapr-1.so.0()(64bit) 被 httpd-2.4.6-95.el7.centos.x86_64 需要 + libaprutil-1.so.0()(64bit) 被 httpd-2.4.6-95.el7.centos.x86_64 需要 + +# 需要自己手动解决依赖关系 + +# 额外安装选项 +# --nosignature 不检验软件包的签名 +# --force 强制安装软件包 +# --nodeps 忽略依赖关系 +``` + +## 3.3 实例:查询 + +```bash +[root@atopos ~]# rpm -q vim-enhanced +vim-enhanced-7.4.629-8.el7_9.x86_64 +[root@atopos ~]# rpm -qa |grep vim-enhanced +vim-enhanced-7.4.629-8.el7_9.x86_64 +[root@atopos ~]# rpm -ql vim-enhanced +/etc/profile.d/vim.csh +/etc/profile.d/vim.sh +/usr/bin/rvim +/usr/bin/vim +/usr/bin/vimdiff +/usr/bin/vimtutor +[root@atopos ~]# rpm -qc openssh-server +/etc/pam.d/sshd +/etc/ssh/sshd_config +/etc/sysconfig/sshd +[root@atopos ~]# +``` + +## 3.4 实例:卸载 + +```bash +[root@atopos ~]# rpm -e vim-enhanced +``` + +# 4. 源码包管理 + +源码包获得途径: + +- 官方网站 + - apache: www.apache.org + - nginx: www.nginx.org + +- github + - www.github.com + +编译源码包所需要的环境 + +这取决于源码所使用的编程语言和框架等,但是Linux基础软件包一般都是由C语言编写的,所以我们使用较多的编译环境为`gcc`、`make`等等 + +`yum install -y ncurses* gcc gcc-c++ make` + +## 4.1 编译 + +源码包编译三部曲 + +```bash +- 第一步: +./configure +#可以指定安装路径,启用或者禁用功能等,最终生成makefile + +- 第二步 +make +#按Makefile文件编译。可以加 -j 2 参数使用两个cpu核心进行编译 + +- 第三步 +make install +#按Makefile定义的文件路径安装 + +make clean +#清除上一次make命令所产生的object文件,要重新执行 +configure时,需要执行make clean。 +``` + + + +## 4.2 案例:编译炫酷代码雨 + +### 4.2.1 补充:压缩与解压缩 + +**打包一个文件或者目录**: + +```bash +tar -cvf archive.tar /path/to/directory + +-c 表示创建一个新的归档文件。 +-v 表示在压缩过程中显示详细信息(verbose 详细模式)。 +-f 指定归档文件的名称。 + +# 如果要创建gzip压缩包 +tar -zcvf archive.tar.gz /path/to/directory +``` + +**解压缩:** + +```bash +tar -xvf archive.tar + +-x 表示解压缩。 +-v 表示显示详细信息。 +-f 指定要解压缩的归档文件。 + +# 如果归档文件是使用 gzip 压缩的,比如 archive.tar.gz,则需要添加 -z 选项: + +tar -zxvf archive.tar.gz +``` + +### 4.2.2 开始编译 + +```bash +[root@localhost ~]# yum -y install ncurses* gcc gcc-c++ +[root@localhost ~]# tar -zxvf cmatrix-1.2a.tar.gz +[root@localhost ~]# cd cmatrix-1.2a +[root@localhost cmatrix-1.2a]# ./configure +[root@localhost cmatrix-1.2a]# make +[root@localhost cmatrix-1.2a]# make install +[root@localhost cmatrix-1.2a]# cmatrix +``` + diff --git a/01.Linux基础/13.网络管理.md b/01.Linux基础/13.网络管理.md new file mode 100644 index 0000000..b2747d8 --- /dev/null +++ b/01.Linux基础/13.网络管理.md @@ -0,0 +1,409 @@ +# 1. 数据通信基础 + +- 数据从产生到传递到目的地的过程中会经历好几个过程,每个过程都负责加工自己这部分的内容,类似于工厂流水线 +- 目前我们只需要有个最基本的概念: + - IP地址是用来标识网络中位置的,比如你在江苏省xxx市xxx路xxx号 + - MAC地址是每个网络设备的唯一ID,比如你的身份证号码 + - 如果想要发送数据,必须(暂且认为必须)同时拥有IP和MAC地址 + - Linux的网络管理基础部分就是需要大家掌握IP地址的配置 + +![img-数据通信基础](13.网络管理/数据通信基础.png) + +## 1.1 IP地址 + +- 在IP网络中,通信节点需要有一个唯一的IP地址 +- IP地址用于IP报文的寻址以及标识一个节点 +- IPv4地址一共32bits,使用点分十进制的形式表示 + +img-IP地址 + +- IPv4地址由网络位和主机位组成 + - 网络位一致表示在同一个广播域中,可以直接通信 + - 主机位用于在同一个局域网中标识唯一节点 + +### 1.1.1 早期IP地址的划分 + +img-IP地址划分 + +- 早期参与互联网的设备不多,所以仅仅使用ABC类地址分配给用户即可 +- 随着网络用户的增多,ABC类分配地址过于浪费,于是出现子网掩码方式划分网络位和主机位 + +### 1.1.2 IP网络通信类型 + +- 单播(Unicast) +- 广播(Broadcast) +- 组播(Multicast) + +### 1.1.3 子网掩码(Netmask) + +- 网络掩码与IP地址搭配使用,用于描述一个IP地址中的网络部分及主机部分 +- 网络掩码32bits,与32bits的IP地址一一对应,掩码中为1的位对应IP地址中的网络位,掩码中为0的位对应IP地址中的主机位 + +img-子网掩码 + +- 减少一个局域网中的设备数量可以有效降低广播报文消耗资源 + +- 可变长子网掩码可以将一个局域网中的主机地址分配的更加小 + +img-可变长子网掩码 + +### 1.1.4 广播地址与网络号 + +- 在局域网中经常会有广播的需要(比如,mac地址查询,地址冲突检测等等),所以将主机位全为1的地址做为本局域网的广播地址(注意!广播并不能跨越不同的局域网) +- 在网络中需要表示整个局域网,就像邮政编码表示一个大的区域一样,所以将主机位全为0的地址作为本局域网的网络号,用来代指整个网段 +- 综上所述,计算产生的子网及每个子网的主机数量公式如下: + +img-子网和主机IP数 + +### 1.1.5 私有IP地址 + +- 如果要取得互联网合法地址用于通信,必须要找 iana.org 组织分配 +- 很多企业内部都有大量的网络设备,大多数时候这些设备只需要内部通信即可 +- 企业的网络管理员可以从如下网段中自行分配地址 + +img-私有IP地址 + +- 私有IP地址空间中的地址不需要申请,随意使用,但是不能在互联网上与合法地址通信(因为对方没法回复你这个地址,因为世界上私有IP地址段无数个重复的,怎么知道回到谁那里呢) +- 而我们明明用的私有IP地址,也可以上网,因为我们需要先把自己的上网请求提交给网络中的网关(就是你家的出口路由器),再由网关代替我们去获取内容转交给我们的电脑手机,而网关往往能从运营商那里得到一个合法的公有IP地址 + +# 2. DHCP + +DHCP(动态主机配置协议),主要用于给设备自动的分配IP地址以及网络信息,以便网络设备能够连接到网络并进行通信 + +DHCP给设备提供的信息如下: + +- IP地址 +- 子网掩码 +- 网关地址 +- DNS服务器地址 +- 租约时间 + + + +## 2.1 DHCP工作过程 + +1. 客户端启动时发送 DHCP 发现(DHCPDISCOVER)广播消息,寻找 DHCP 服务器。 +2. DHCP 服务器收到广播消息后,会提供一个未被使用的 IP 地址以及其他网络配置信息(如子网掩码、默认网关、DNS 服务器等),并发送 DHCP 提供(DHCPOFFER)消息给客户端。 +3. 客户端收到一个或多个 DHCP 提供消息后,会选择其中一个 DHCP 服务器提供的 IP 地址,并发送 DHCP 请求(DHCPREQUEST)消息确认。 +4. DHCP 服务器收到客户端的 DHCP 请求消息后,会将该 IP 地址分配给客户端,并发送 DHCP 确认(DHCPACK)消息给客户端。 +5. 客户端收到 DHCP 确认消息后,就可以使用分配到的 IP 地址和其他网络配置参数连接到网络了。 + +`dhclient` + +我们可以通过抓包软件,捕捉到主机通过DHCP方式获取ip的过程,一共有4个数据包: + +![img-DHCP数据包](13.网络管理/DHCP数据包.png) + +其中每个数据包的作用如下: + +1. `Discover`消息是用于客户端向整个内网发送广播,期待DHCP服务器进行回应 + 1. 这个数据包中的重要内容就是:消息类型,客户端ID,主机名,请求获得的信息 +2. `Offer`消息是DHCP服务器对客户的回应 + 1. 这个消息中会回复对方所需要的所有信息 +3. `Request`这个是客户端确认DHCP服务器的消息 + 1. 这个消息和第一个消息差不多,但是消息类别变为`Request`,并且会携带请求的IP地址 +4. `ACK`DHCP服务器给客户端的最终确认 + 1. 这个消息和第二个消息差不多,但是消息类型变为`ACK` + +## 2.2 DHCP续租 + +DHCP分配的信息是有有效期的,再租约时间快到的时候,如果我们想要继续使用这个地址信息,就需要向DHCP服务器续租 + +这也是大家虚拟机上IP地址经常发生变化的原因,这是因为虚拟机上默认就是以DHCP的方式获得IP地址的 + +我们可以查看Linux上的网络配置信息,该信息位于`/etc/NetworkManager/system-connections/ens160.nmconnection` + +```bash +[root@localhost ~]# cat /etc/NetworkManager/system-connections/ens160.nmconnection +[connection] +id=ens160 +uuid=dfea55d8-6ddc-3229-8152-cb9e261de181 +type=ethernet +autoconnect-priority=-999 +interface-name=ens160 +timestamp=1731149277 + +[ethernet] + +[ipv4] +method=auto + +[ipv6] +addr-gen-mode=eui64 +method=auto + +[proxy] + + +# 可以看到配置中的method字段是以dhcp来获得IP地址的。 +``` + +同样从VMware的虚拟网络设置中,也可以看到租约时间相关的内容 + +img-虚拟网络编辑器 + + + +# 3. DNS(域名解析) + +DNS(Domain Name System) 是一套从域名到IP的映射的协议 + +在网络世界中,如果我们想要给某个机器,或者是某个网站去发送一个数据,都需要通过IP地址来找到对方,比如我们想要使用百度来搜索东西,就需要知道百度的IP地址,换句话说只要知道了百度的IP地址,我们就可以访问到百度 + +但是IP地址不易记忆,不可能记住每一个网站的IP地址是什么,于是早期的搞IT的那帮人研发出一个叫做主机名的东西。 + +最开始人们把IP和主机名的的对应关系记录在本地,这个文件目前在windows系统的`C:\Windows\System32\drivers\etc\hosts`中。但是后面发现并不好用,而且需要经常手动更新这个文件。类似于黄历 + + + +随着主机名越来越多,hosts文件不易维护,所以后来改用**域名解析系统DNS**: + +- 一个组织的系统管理机构,维护系统内的每个主机的`IP和主机名`的对应关系 +- 如果新计算机接入网络,将这个信息注册到`数据库`中 +- 用户输入域名的时候,会自动查询`DNS`服务器,由`DNS服务器`检索数据库, 得到对应的IP地址。 + +## 3.1 域名 + +主域名是用来识别主机名称和主机所属的组织机构的一种分层结构的名称。 +例如:http://www.baidu.com(域名使用.连接) + +- com: 一级域名,表示这是一个企业域名。同级的还有 "net"(网络提供商),"org"(非盈利组织) 等。 +- baidu: 二级域名, 公司名。 +- www: 只是一种习惯用法,并不是每个域名都支持。 +- http:// : 要使用什么协议来连接这个主机名。 + +## 3.2 常见的域名解析服务器 + +- 114dns + +​ 114.114.114.114 + +​ 114.114.115.115 + +​ 这是国内用户量数一数二的 dns 服务器,该 dns 一直标榜高速、稳定、无劫持、防钓鱼。 + +- Google DNS + +​ 8.8.8.8 + +​ 8.8.4.4 + +​ ...... + +可以理解为由这些服务器帮我们记录的域名和IP的对应关系,我们访问域名的时候去找这些dns服务器询问该域名对应的IP地址,当然,除了上述提到的这些dns服务器,三大运营商也提供了dns解析服务 + +## 3.3 域名解析的过程 + +1. 浏览器发起域名解析,首先查询浏览器缓存,如果没有,就查询hosts文件,如果没有就提出域名解析请求 +2. 客户机提出域名解析请求,并将该请求发送给本地的域名服务器。 +3. 当本地的域名服务器收到请求后,就先查询本地的缓存,如果有该纪录项,则本地的域名服务器就直接把查询的结果返回。 +4. 如果本地的缓存中没有该纪录,则本地域名服务器就直接把请求发给根域名服务器,然后根域名服务器再返回给本地域名服务器一个所查询域(根的子域)的主域名服务器的地址。 +5. 本地服务器再向上一步返回的域名服务器发送请求,然后接受请求的服务器查询自己的缓存,如果没有该纪录,则返回相关的下级的域名服务器的地址。 +6. 重复第四步,直到找到正确的纪录。 +7. 本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时还将结果返回给客户机 + + + +# 4. 配置网络服务 + +## 4.1 修改网卡配置文件 + +```bash +# 修改网卡配置文件 +[root@localhost ~]# cat /etc/NetworkManager/system-connections/ens160.nmconnection +[connection] +id=ens160 +uuid=dfea55d8-6ddc-3229-8152-cb9e261de181 +type=ethernet +autoconnect-priority=-999 +interface-name=ens160 +timestamp=1732264040 + +[ethernet] + +[ipv4] +address1=192.168.88.110/24,192.168.88.2 +dns=114.114.114.114;114.114.115.115; +method=manual + +[ipv6] +addr-gen-mode=eui64 +method=auto + +[proxy] +``` + +当修改完Linux系统中的服务配置文件后,并不会对服务程序立即产生效果。要想让服务程序获取到最新的配置文件,需要手动重启相应的服务,之后就可以看到网络畅通了 + +```bash +[root@localhost ~]# systemctl restart NetworkManager +[root@localhost ~]# ping -c 4 www.baidu.com +PING www.a.shifen.com (180.101.50.242) 56(84) bytes of data. +64 bytes from 180.101.50.242 (180.101.50.242): icmp_seq=1 ttl=128 time=11.3 ms +... +``` + +## 4.2 网卡配置文件参数 + +| **节** | **参数** | **描述** | +| -------------- | ---------------------- | ------------------------------------------------------------ | +| `[connection]` | `id` | 连接的名称,这里是 `ens160`。 | +| | `uuid` | 连接的唯一标识符(UUID),用于唯一识别此连接。 | +| | `type` | 连接的类型,`ethernet` 表示这是一个以太网连接。 | +| | `autoconnect-priority` | 自动连接优先级,数值越小优先级越低。这里设置为 `-999`,表示极低的优先级。 | +| | `interface-name` | 连接对应的网络接口名称,这里是 `ens160`。 | +| | `timestamp` | 连接的时间戳,表示最后修改的时间。 | +| `[ethernet]` | - | 此节用于配置以太网特定的设置,当前没有额外参数。 | +| `[ipv4]` | `address1` | 静态 IPv4 地址及其子网掩码,格式为 `IP地址/子网掩码` 和网关,例:`192.168.88.110/24,192.168.88.2`。 | +| | `dns` | DNS 服务器地址,多个地址用分号分隔,这里是 `114.114.114.114;114.114.115.115;`。 | +| | `method` | IPv4 地址配置方法,这里设置为 `manual`,表示使用手动配置。 | +| `[ipv6]` | `addr-gen-mode` | 地址生成模式,`eui64` 表示使用 EUI-64 地址生成方式。 | +| | `method` | IPv6 地址配置方法,这里设置为 `auto`,表示自动获取 IPv6 地址。 | +| `[proxy]` | - | 此节用于配置代理设置,当前没有额外参数。 | + +## 4.3 nmcli 工具 + +nmcli命令是redhat7或者centos7之后的命令,该命令可以完成网卡上所有的配置工作,并且可以写入 配置文件,永久生效 + +- 查看接口状态 + +```bash +[root@localhost ~]# nmcli device status +DEVICE TYPE STATE CONNECTION +ens160 ethernet connected ens160 +lo loopback connected (externally) lo +``` + +- 查看链接信息 + +```bash +[root@localhost ~]# nmcli connection show +NAME UUID TYPE DEVICE +ens160 dfea55d8-6ddc-3229-8152-cb9e261de181 ethernet ens160 +lo 529b2eed-2755-4cce-af3c-999beb49882d loopback lo +``` + +- 配置IP等网络信息 + +```bash +[root@localhost ~]# nmcli con mod "ens160" ipv4.addresses 192.168.88.140/24 ipv4.gateway 192.168.88.2 ipv4.dns "8.8.8.8,8.8.4.4" ipv4.method manual +``` + +- 启动/停止接口 + +```bash +[root@localhost ~]# nmcli connection down ens160 +[root@localhost ~]# nmcli connection up ens160 +``` + +- 创建链接 + +```bash +[root@localhost ~]# nmcli connection add type ethernet ifname ens160 con-name dhcp_ens160 +# 激活链接 +[root@localhost ~]# nmcli connection up dhcp_ens160 +``` + +- 删除链接 + +```bash +[root@localhost ~]# nmcli connection delete dhcp_ens160 +成功删除连接 'dhcp_ens160'(37adadf4-419d-47f0-a0f6-af849160a4f7)。 +``` + + + +# 5. ifconfig + +- ifconfig 命令用于显示或设置网络设备。 + +**安装:**`yum install -y net-tools` + +## 5.1 语法 + +```bash +ifconfig [network_interface] + [down|up] + [add
] + [del
] + [hw ] + [io_addr ] + [irq ] + [media ] + [mem_start ] + [metric ] + [mtu ] + [netmask ] + [tunnel
] + [-broadcast
] + [-pointopoint
] + [] +``` + +## 5.2 选项说明 + +- `down`/`up`: 禁用/启用网络接口 +- `add
`/`del
`: 添加或删除 IP 地址 +- `hw `: 设置硬件地址 (MAC 地址) +- `io_addr `: 设置 I/O 地址 +- `irq `: 设置中断请求 +- `media `: 设置网络媒体类型 +- `mem_start `: 设置内存起始地址 +- `metric `: 设置路由度量值 +- `mtu `: 设置 MTU 值 +- `netmask `: 设置子网掩码 +- `tunnel
`: 设置隧道地址 +- `-broadcast
`: 设置广播地址 +- `-pointopoint
`: 设置点对点地址 +- ``: 设置 IP 地址 + +## 5.3 实例 + +- 显示网络设备信息 + +```bash +[root@localhost cmatrix-1.2a]# ifconfig +ens33: flags=4163 mtu 1500 + inet 192.168.88.136 netmask 255.255.255.0 broadcast 192.168.88.255 + inet6 fe80::a49c:12c9:1ebd:8fb2 prefixlen 64 scopeid 0x20 + ether 00:0c:29:cb:d5:1a txqueuelen 1000 (Ethernet) + RX packets 122441 bytes 178564616 (170.2 MiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 13762 bytes 1315614 (1.2 MiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +lo: flags=73 mtu 65536 + inet 127.0.0.1 netmask 255.0.0.0 + inet6 ::1 prefixlen 128 scopeid 0x10 + loop txqueuelen 1000 (Local Loopback) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 +``` + +- 启动关闭指定网卡 + +```bash +[root@localhost ~]# ifconfig ens33 down +[root@localhost ~]# ifconfig ens33 up +``` + +- 配置IP地址 + +```bash +[root@localhost ~]# ifconfig eth0 192.168.1.56 +//给eth0网卡配置IP地址 +[root@localhost ~]# ifconfig eth0 192.168.1.56 netmask 255.255.255.0 +// 给eth0网卡配置IP地址,并加上子掩码 +[root@localhost ~]# ifconfig eth0 192.168.1.56 netmask 255.255.255.0 broadcast 192.168.1.255 +// 给eth0网卡配置IP地址,加上子掩码,加上个广播地址 +``` + +- 设置最大传输单元 + +```bash +[root@localhost ~]# ifconfig eth0 mtu 1500 +//设置能通过的最大数据包大小为 1500 bytes +``` + diff --git a/01.Linux基础/13.网络管理/DHCP数据包.png b/01.Linux基础/13.网络管理/DHCP数据包.png new file mode 100644 index 0000000..5e98d87 Binary files /dev/null and b/01.Linux基础/13.网络管理/DHCP数据包.png differ diff --git a/01.Linux基础/13.网络管理/IP地址.png b/01.Linux基础/13.网络管理/IP地址.png new file mode 100644 index 0000000..7fc7b27 Binary files /dev/null and b/01.Linux基础/13.网络管理/IP地址.png differ diff --git a/01.Linux基础/13.网络管理/IP地址划分.png b/01.Linux基础/13.网络管理/IP地址划分.png new file mode 100644 index 0000000..64dc746 Binary files /dev/null and b/01.Linux基础/13.网络管理/IP地址划分.png differ diff --git a/01.Linux基础/13.网络管理/可变长子网掩码.png b/01.Linux基础/13.网络管理/可变长子网掩码.png new file mode 100644 index 0000000..627f128 Binary files /dev/null and b/01.Linux基础/13.网络管理/可变长子网掩码.png differ diff --git a/01.Linux基础/13.网络管理/子网和主机IP数.png b/01.Linux基础/13.网络管理/子网和主机IP数.png new file mode 100644 index 0000000..2b3a674 Binary files /dev/null and b/01.Linux基础/13.网络管理/子网和主机IP数.png differ diff --git a/01.Linux基础/13.网络管理/子网掩码.png b/01.Linux基础/13.网络管理/子网掩码.png new file mode 100644 index 0000000..305ec9d Binary files /dev/null and b/01.Linux基础/13.网络管理/子网掩码.png differ diff --git a/01.Linux基础/13.网络管理/数据通信基础.png b/01.Linux基础/13.网络管理/数据通信基础.png new file mode 100644 index 0000000..32d8c1f Binary files /dev/null and b/01.Linux基础/13.网络管理/数据通信基础.png differ diff --git a/01.Linux基础/13.网络管理/私有IP地址.png b/01.Linux基础/13.网络管理/私有IP地址.png new file mode 100644 index 0000000..50e6708 Binary files /dev/null and b/01.Linux基础/13.网络管理/私有IP地址.png differ diff --git a/01.Linux基础/13.网络管理/虚拟网络编辑器.png b/01.Linux基础/13.网络管理/虚拟网络编辑器.png new file mode 100644 index 0000000..cdfff2b Binary files /dev/null and b/01.Linux基础/13.网络管理/虚拟网络编辑器.png differ diff --git a/01.Linux基础/14.防火墙与selinux.md b/01.Linux基础/14.防火墙与selinux.md new file mode 100644 index 0000000..c6ac297 --- /dev/null +++ b/01.Linux基础/14.防火墙与selinux.md @@ -0,0 +1,1351 @@ +# 1. 防火墙相关概念 + +主要作用:保护内部的网络、主机、服务的安全等 + +我们常听到的防火墙有很多不同的类型,从大的角度来看,主要是分为硬件防火墙和软件防火墙。 + +**硬件防火墙:** + +img-硬件防火墙 + +**软件防火墙:** + +软件防火墙就是通过软件来对数据包,流量请求进行过滤等,属于从软件层面保护系统的安全。如我们熟知的360,金山毒霸,火绒安全等等。 + +当然防火墙除了软件及硬件的分类,也可以对数据封包的取得方式来分类,可分为**代理服务器(Proxy)**及**封包过滤机制(IP Filter)**。 + +- 代理服务 + + - 是一种网络服务,通常就架设在路由上面,可完整的掌控局域网的对外连接。 + +- IP Filter + + - 这种方式可以直接分析最底层的封包表头数据来进行过滤,所以包括 MAC地址, IP, TCP, UDP, ICMP 等封包的信息都可以进行过滤分析的功能,用途非常广泛。 + + 相较于企业内网,外部的公网环境更加恶劣,罪恶丛生。在公网与企业内网之间充当保护屏障的防火墙虽然有软件或硬件之分,但主要功能都是依据策略对穿越防火墙自身的流量进行过滤。防火墙策略可以基于流量的源目地址、端口号、协议、应用等信息来定制,然后防火墙使用预先定制的策略规则监控出入的流量,若流量与某一条策略规则相匹配,则执行相应的处理,反之则丢弃。这样一来,就可以保证仅有合法的流量在企业内网和外部公网之间流动了 + +# 2. RHEL/Centos系统 + +对于RHEL、Centos等红帽系Linux操作系统,我们常用到的防火墙软件主要有**iptables和firewalld** + +- iptables: + - iptables 是 Linux 默认的防火墙管理工具,它是基于命令行的。 + - iptables 使用规则表(tables)和规则链(chains)的方式来控制网络数据包的流向和处理。 + - iptables 提供了强大的灵活性和细粒度的控制,可以根据数据包的各种属性进行复杂的过滤和转发操作。 + - iptables 的配置相对比较复杂,需要对防火墙的工作原理有一定的了解。 +- firewalld: + - firewalld 是 RHEL/CentOS 7 及以后版本中引入的动态防火墙管理工具。 + - firewalld 采用区域(zones)和服务(services)的概念来管理防火墙规则,相比 iptables 更加简单和易用。 + - firewalld 支持动态更新防火墙规则,无需重启服务即可生效,这对于需要经常调整防火墙的场景很有帮助。 + - firewalld 提供了图形化的管理界面,方便管理员进行配置和管理。 + + + +**不过,对于Linux而言,其实Iptables和firewalld服务不是真正的防火墙,只是用来定义防火墙规则功能的"防火墙管理工具",将定义好的规则交由内核中的netfilter即网络过滤器来读取,从而真正实现防火墙功能。** + + + +# 3. iptables + +iptables过滤数据包的方式: + +- 允许/拒绝让 Internet 的封包进入 Linux 主机的某些 port + +- 允许/拒绝让某些来源 IP 的封包进入 + +- 允许/拒绝让带有某些特殊标志( flag )的封包进入 + +- 分析硬件地址(MAC)来提供服务 + +## 3.1 五链 + +iptables命令中设置数据过滤或处理数据包的策略叫做规则,将多个规则合成一个链,叫规则链。 +规则链则依据处理数据包的位置不同分类: + +- PREROUTING + - 在进行路由判断之前所要进行的规则(DNAT/REDIRECT) +- INPUT + - 处理入站的数据包 +- OUTPUT + - 处理出站的数据包 +- FORWARD + - 处理转发的数据包 +- POSTROUTING + - 在进行路由判断之后所要进行的规则(SNAT/MASQUERADE) + + + +## 3.2 四表 + +iptables中的规则表是用于容纳规则链,规则表默认是允许状态的,那么规则链就是设置被禁止的规则,而反之如果规则表是禁止状态的,那么规则链就是设置被允许的规则。 + +- raw表 + - 确定是否对该数据包进行状态跟踪 +- mangle表 + - 为数据包设置标记(较少使用) +- nat表 + - 修改数据包中的源、目标IP地址或端口 +- filter表 + - 确定是否放行该数据包(过滤) + +![img-四表五链](14.防火墙与selinux/四表五链.png) + +规则表的先后顺序:0000 + +规则链的先后顺序: + +- 入站顺序 + - PREROUTING→INPUT +- 出站顺序 + - OUTPUT→POSTROUTING +- 转发顺序 + - PREROUTING→FORWARD→POSTROUTING + +img-四表五链顺序 + +## 3.3 基本选项和用法 + +### 3.3.1 选项 + +| 参数 | 作用 | +| ------------ | ----------------------------------------------------------- | +| -P | 设置默认策略:iptables -P INPUT (DROP\|ACCEPT)类似于黑白名单 | +| -F | 清空规则链 | +| -L | 查看规则链 | +| -A | 在规则链的末尾加入新规则 | +| -I num | 在规则链的头部加入新规则 | +| -D num | 删除某一条规则 | +| -s | 匹配来源地址IP/MASK,加叹号"!"表示除这个IP外。 | +| -d | 匹配目标地址 | +| -i 网卡名称 | 匹配从这块网卡流入的数据 | +| -o 网卡名称 | 匹配从这块网卡流出的数据 | +| -p | 匹配协议,如tcp,udp,icmp | +| --dports num | 匹配目标端口号 | +| --sports num | 匹配来源端口号 | + + + +为了防止firewalld与iptables的冲突,建议在学习iptables的时候,先关闭firewalld + + + +### 3.3.2 简单用法(初体验) + +案例:现在有一个web服务提供网站访问,默认情况下是允许被我们的主机访问的,我们可以尝试通过设置iptables的策略,来禁止别人访问该网站 + +- 为了防止firewalld对iptables的影响,我们学习iptables的时候应该关闭firewalld + +```bash +[root@localhost ~]# systemctl stop firewalld +``` + +- 网站部署: + +```bash +[root@localhost ~]# yum install -y epel-release +# 安装扩展源,以便安装下面的web服务器nginx +[root@localhost ~]# yum install -y nginx +# 安装web服务器 +[root@localhost ~]# systemctl start nginx +# 启动web服务器 +``` + +- 访问测试: + +```shell +curl -I x.x.x.x +``` + +- 接下来通过iptables进行访问控制 + +```bash +[root@localhost ~]# iptables -A INPUT -p tcp --dport 80 -j DROP +# 对INPUT链上增加一个策略,丢弃掉来自80端口tcp协议的请求报文 +``` + +- 再次访问发现,这个网站已经打不开了,提示回应超时! + +```shell +curl -I x.x.x.x +``` + +- 在Linux上查看刚刚添加的过滤规则 + +```bash +[root@localhost ~]# iptables -vnL INPUT --line-numbers +Chain INPUT (policy ACCEPT 62 packets, 4776 bytes) +num pkts bytes target prot opt in out source destination +1 75 3900 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 +``` + +## 3.4 iptables规则说明 + +- 规则:根据指定的匹配条件来尝试匹配每个经流“关卡”的报文,一旦匹配成功,则由规则后面指定的处理动作进行处理 +- 匹配条件 + - 基本匹配条件:sip、dip + - 扩展匹配条件:sport、dport + - 扩展条件也是条件的一部分,只不过使用的时候需要用`-m`参数声明对应的模块 +- 处理动作 + - accept:接受 + - drop:丢弃 + - reject:拒绝 + - snat:源地址转换,解决内网用户同一个公网地址上上网的问题 + - masquerade:是snat的一种特殊形式,使用动态的、临时会变的ip上 + - dnat:目标地址转换 + - redirect:在本机作端口映射 + - log:记录日志,/var/log/messages文件记录日志信息,然后将数据包传递给下一条规则 + + + +## 3.5 iptables高级用法 + +![img-高级用法](14.防火墙与selinux/高级用法.png) + +- `-t table`指定表 + - raw + - mangle + - nat + - filter 默认 +- `-A`:添加规则,后面更上你要添加到哪个链上 +- `-j`:设置处理策略 + - accept:接受 + - drop:丢弃 + - reject:拒绝 +- `-s`:指定数据包的来源地址 +- `-p`:指定数据包协议类型 + - tcp + - udp + - icmp + - ...... + +### 3.5.1 增加规则 + +**案例一:**屏蔽来自其他主机上的ping包 + +```bash +# 正常通过cmd去ping我们的虚拟机的IP地址 +C:\Users\Atopos>ping 192.168.88.136 + +正在 Ping 192.168.88.136 具有 32 字节的数据: +来自 192.168.88.136 的回复: 字节=32 时间<1ms TTL=64 +来自 192.168.88.136 的回复: 字节=32 时间<1ms TTL=64 +来自 192.168.88.136 的回复: 字节=32 时间<1ms TTL=64 +来自 192.168.88.136 的回复: 字节=32 时间<1ms TTL=64 + +192.168.88.136 的 Ping 统计信息: + 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失), +往返行程的估计时间(以毫秒为单位): + 最短 = 0ms,最长 = 0ms,平均 = 0ms + + +# 在虚拟机上屏蔽来自我们物理机的ping包,注意物理机的IP的地址为:192.168.xxx.1 +# ping命令是基于icmp协议的,所以这里对类型为icmp协议的数据包进行丢弃 +[root@localhost ~]# iptables -A INPUT -s 192.168.88.1 -p icmp -j DROP + +# 丢弃以后ping测试: +C:\Users\Atopos>ping 192.168.88.136 + +正在 Ping 192.168.88.136 具有 32 字节的数据: +请求超时。 + +# 注意,DROP和REJECT有一定的区别,DROP为丢弃数据包,不给予对方回应,所以对方就会一直等待。REJECT是拒绝对方,会将拒绝的数据包发给对方,对方就会知道我们拒绝的访问。 +# 如果改成REJECT现象是: +C:\Users\Atopos>ping 192.168.88.136 + +正在 Ping 192.168.88.136 具有 32 字节的数据: +来自 192.168.88.136 的回复: 无法连到端口。 +来自 192.168.88.136 的回复: 无法连到端口。 +来自 192.168.88.136 的回复: 无法连到端口。 +来自 192.168.88.136 的回复: 无法连到端口。 + +192.168.88.136 的 Ping 统计信息: + 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失), + + +`其他用法` +[root@localhost ~]# iptables -A INPUT -s 192.168.88.100 -j ACCEPT +# 目标来自 192.168.88.100 这个 IP 的封包都予以接受 +[root@localhost ~]# iptables -A INPUT -s 192.168.88.0/24 -j ACCEPT +[root@localhost ~]# iptables -A INPUT -s 192.168.88.10 -j DROP +# 192.168.88.0/24 可接受,但 192.168.88.10 丢弃 +``` + +**案例二:**屏蔽Linux系统中的远程链接服务(sshd),使得我们无法通过MobaXterm连接到虚拟机上 + +```bash +[root@localhost ~]# iptables -A INPUT -p tcp --dport 22 -j REJECT +# ssh服务所用的协议类型为tcp协议,端口号为22 +# 所以我们将来自22端口上的tcp协议数据包给拒绝 +``` + +当敲完这个命令以后,你的远程连接就会中断了,可以在VMware中的虚拟机终端上删除这个规则,就恢复了 + +删除命令:`iptables -D INPUT -p tcp --dport 22 -j REJECT` + + + +### 3.5.2 查看规则 + +```bash +[root@localhost ~]# iptables [-t tables] [-L] [-nv] +参数: +-t 后面接table,例如nat或filter,如果省略,默认显示filter +-L 列出目前的table的规则 +-n 不进行IP与主机名的反查,显示信息的速度会快很多 +-v 列出更多的信息,包括封包数,相关网络接口等 +--line-numbers 显示规则的序号 +``` + +- 查看具体每个表的规则 + + - policy:当前链的默认策略,当所有规则都没有匹配成功时执行的策略 + - packets:当前链默认策略匹配到的包的数量 + - bytes:当前链默认策略匹配到的包的大小 + - pkts:对应规则匹配到的包数量 + - bytes:对应规则匹配到的包大小 + - target:对应规则执行的动作 + - prot:对应的协议,是否针对某些协议应用此规则 + - opt:规则对应的选项 + - in:数据包由哪个接口流入 + - out:数据包由哪个接口流出 + - source:源ip地址 + - distination:目的ip地址 + +```bash +[root@localhost ~]# iptables -vnL INPUT -t filter --line-numbers +[root@localhost ~]# iptables -nL -t nat +[root@localhost ~]# iptables -nL -t mangle +[root@localhost ~]# iptables -nL -t raw +``` + + + +### 3.5.3 删除规则 + +通过上面学习的查看规则的方法,先查找到你想要删除的规则 + +```bash +# 举例 +# 查看nat表上所有链的规则 +[root@localhost ~]# iptables -t nat -nL --line-numbers +# 查看filter(默认表)上所有链的规则 +[root@localhost ~]# iptables -nL --line-numbers +``` + +- 通过num序号进行删除 + +```bash +删除input链上num为1的规则 +[root@localhost ~]# iptables -D INPUT 1 +``` + +- 通过规则匹配删除 + +```bash +[root@localhost ~]# iptables -D INPUT -p tcp --dport 80 -j DROP +``` + +- 清空所有的规则 + +```bash +[root@localhost ~]# iptables -F +``` + + + +### 3.5.4 修改规则 + +方案一:通过`iptables -D`删除原有的规则后添加新的规则 + +方案二:通过`iptables -R`可以对具体某一个num的规则进行修改 + +```bash +# 修改端口的为8080 +[root@localhost ~]# iptables -R INPUT 1 -p tcp --dport 8080 -j ACCEPT + +# 针对具体的某一个表和链进行修改 +# 将8080修改回80 +[root@localhost ~]# iptables -t filter -R INPUT 1 -p tcp --dport 80 -j ACCEPT +``` + + + +## 3.6 自定义链 + +iptables中除了系统自带的五个链之外,还可以自定义链,来实现将规则进行分组,重复调用的目的 + +具体方法见如下案例: + +案例一:我们自定义一个WEB_CHAIN链,专门管理跟web网站相关的规则策略 + +```bash +# 1. 先查看我们现有的系统链 +[root@localhost ~]# iptables -L +Chain INPUT (policy ACCEPT) +target prot opt source destination +Chain FORWARD (policy ACCEPT) +target prot opt source destination +Chain OUTPUT (policy ACCEPT) +target prot opt source destination + +# 2. 添加WEB_CHAIN自定义链,并且查看 +[root@localhost ~]# iptables -t filter -N web_chain +[root@localhost ~]# iptables -L +.......... +Chain web_chain (0 references) +target prot opt source destination + +# 3. 可以通过-E参数修改自定义链 +[root@localhost ~]# iptables -t filter -E web_chain WEB_CHAIN +[root@localhost ~]# iptables -L +......... +Chain WEB_CHAIN (0 references) +target prot opt source destination +# 可以看到名字已经发生变化了 + +# 4. 向我们创建的自定义链中添加规则,开放80端口上的服务 +[root@localhost ~]# iptables -t filter -A WEB_CHAIN -p tcp -m multiport --dports 80,443 -j ACCEPT + +# 5. 将自定义链关联到系统链上才能使用 +# 因为数据包只会经过上面讲过的五个系统链,不会经过我们的自定义链,所以需要把自定义链关联到某个系统链上 + +# 我们允许来自IP:192.168.88.1的访问,随后拒绝其他所有的访问,这样,只有192.168.88.1的主机可以访问这个网站 +[root@localhost ~]# iptables -t filter -A INPUT -s 192.168.88.1 -j WEB_CHAIN +[root@localhost ~]# iptables -t filter -A INPUT -p tcp -m multiport --dports 80,443 -j DROP +[root@localhost ~]# iptables -L +Chain INPUT (policy ACCEPT) +target prot opt source destination +WEB_CHAIN all -- 192.168.88.1 anywhere +Chain FORWARD (policy ACCEPT) +target prot opt source destination +Chain OUTPUT (policy ACCEPT) +target prot opt source destination +Chain WEB_CHAIN (1 references) +target prot opt source destination +ACCEPT tcp -- anywhere anywhere tcp dpt:http + +``` + +访问测试: + +```shell +# windows本身可以访问我们的网站,但是其他主机无法访问 +curl -I x.x.x.x +``` + +删除自定义链 + +```shell +# 先清空自定义链上的规则 +[root@localhost ~]# iptables -t filter -F WEB_CHAIN +# 然后通过-X选项删除自定义链 +[root@localhost ~]# iptables -t filter -X WEB_CHAIN +``` + +## 3.7 其他用法(模块) + +- tcp/udp + - --dport:指定目的端口 + - --sport:指定源端口 + +- iprange:匹配报文的源/目的地址所在范围 + - --src-range + - --dst-range + +```bash +[root@localhost ~]# iptables -A INPUT -d 172.16.1.100 -p tcp --dport 80 -m iprange --src-range 172.16.1.5-172.16.1.10 -j DROP +``` + +- string:指定匹配报文中的字符串 + - --algo:指定匹配算法,可以是bm/kmp + - bm:Boyer-Moore + + - kmp:Knuth-Pratt-Morris + - --string:指定需要匹配的字符串 + - --from offset:开始偏移 + - --to offset:结束偏移 + +```bash +[root@localhost ~]# iptables -A OUTPUT -p tcp --sport 80 -m string --algo bm --from 62 --string "google" -j REJECT +``` + +**案例:** + +```shell +[root@localhost ~]# yum -y install httpd +[root@localhost ~]# systemctl start httpd +[root@localhost ~]# systemctl enable httpd +[root@localhost ~]# echo "

hello world

" > /var/www/html/index.html +[root@localhost ~]# iptables -A OUTPUT -p tcp --sport 80 -m string --algo bm --string "world" -j REJECT +#用浏览器刷新页面发现打不开 +[root@localhost ~]# iptables -nL +Chain INPUT (policy ACCEPT) +target prot opt source destination +Chain FORWARD (policy ACCEPT) +target prot opt source destination +Chain OUTPUT (policy ACCEPT) +target prot opt source destination +REJECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp spt:80 STRING match "world" ALGO name bm TO 65535 reject-with icmp-port-unreachable + +[root@localhost ~]# echo "

hello XHZ

" > /var/www/html/index.html +# 此时浏览器打开成功 +``` + + + +- time:指定匹配报文的时间 + - --timestart + - --timestop + - --weekdays + - --monthdays + - --datestart + - --datestop + +```bash +[root@localhost ~]# iptables -A INPUT -s 172.16.0.0/16 -d 172.16.100.10 -p tcp --dport 80 -m time --timestart 14:30 --timestop 18:30 --weekdays Sat,Sun -j DROP + +-m time --timestart 14:30 --timestop 18:30 --weekdays Sat,Sun:在指定的时间范围内生效,即从周六到周日的14:30到18:30之间。这表示该规则只在这个时间段内生效 + +- 该规则的作用是在指定时间范围内,阻止来自172.16.0.0/16网段源IP地址、目标IP地址为172.16.100.10、目标端口为80的TCP数据包进入系统 +``` + + + +- connlimit:限制每个ip连接到server端的数量,不需要指定ip默认就是针对每个ip地址,可防止Dos(Denial of Service,拒绝服务)攻击 + - --connlimit-above:最大连接数 + +```bash +[root@localhost ~]# iptables -A INPUT -d 172.16.100.10 -p tcp --dport 22 -m connlimit --connlimit-above 2 -j REJECT + +-m connlimit --connlimit-above 2:使用连接限制模块,限制连接数超过2的数据包。这表示如果同一源IP地址的连接数超过2个,将匹配到该规则 + +- 该规则的作用是限制目标IP地址为172.16.100.10、目标端口为22的数据包,如果同一源IP地址的连接数超过2个,则拒绝回复该数据包。这个规则可以用于防止对SSH服务的暴力破解攻击,限制来自同一IP地址的并发连接数 +``` + +**案例:** + +```shell +[root@localhost ~]# iptables -A INPUT -s 192.168.88.0/24 -p tcp --dport 22 -m connlimit --connlimit-above 2 -j REJECT + +#此时登第三个终端,会登不上 +[root@localhost ~]# iptables -nL --line-numbers +Chain INPUT (policy ACCEPT) +num target prot opt source destination +1 DROP tcp -- 172.16.0.0/16 172.16.100.10 tcp dpt:80 TIME from 14:30:00 to 18:30:00 on Sat,Sun UTC +2 REJECT tcp -- 192.168.88.0/24 0.0.0.0/0 tcp dpt:22 #conn src/32 > 2 reject-with icmp-port-unreachable + +[root@Centos7-VM-1 ~]# iptables -D INPUT 2 +``` + + + +- limit:对报文到达的速率继续限制,限制包数量 + - 10/second + - 10/minute + - 10/hour + - 10/day + - --limit-burst:空闲时可放行的包数量,默认为5,前多少个包不限制 + + +```bash +iptables -A INPUT -p icmp -m limit --limit-burst 10 --limit 20/minute -j ACCEPT +iptables -A INPUT -p icmp -j REJECT + +--limit-burst 10:设置允许的初始突发量为10个数据包。 +--limit 20/minute:设置平均速率为20个数据包/分钟,即每分钟允许通过的ICMP数据包数量为20个。 + +- 该规则的作用是限制ICMP数据包的速率,允许初始突发量为10个数据包,以后每分钟最多允许通过20个ICMP数据包。匹配到的ICMP数据包将被接受,其他超出限制的数据包将被丢弃。这个规则可以用于控制对系统的ICMP流量,以避免过多的ICMP请求对系统造成负载过大的影响 +``` + + + +- 指定TCP匹配扩展 + +```bash +使用 --tcp-flags 选项可以根据tcp包的标志位进行过滤。 +[root@localhost ~]# iptables -A INPUT -p tcp --tcp-flags SYN,FIN,ACK SYN +[root@localhost ~]# iptables -A FROWARD -p tcp --tcp-flags ALL SYN,ACK +上实例中第一个表示SYN、ACK、FIN的标志都检查,但是只有SYN匹配。第二个表示ALL(SYN,ACK,FIN,RST,URG,PSH)的标志都检查,但是只有设置了SYN和ACK的匹配。 +[root@localhost ~]# iptables -A FORWARD -p tcp --syn +选项--syn相当于"--tcp-flags SYN,RST,ACK SYN"的简写。 +``` + + + +- state模块:用于针对tcp连接进行限制,较耗资源 + +```bash +[root@localhost ~]# iptables -A INPUT -m 模块名 --state 状态 +参数: +-m iptables的模块 + state: 状态检查 + mac:网卡硬件地址 +--state 连接追踪中的状态: + NEW: 新建立一个会话 + ESTABLISHED:已建立的连接 + RELATED: 有关联关系的连接 + INVALID: 无法识别的连接 +[root@localhost ~]# iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT +-m state --state NEW:使用状态模块,匹配数据包的连接状态为NEW,即新建的连接。 +- 该规则的作用是允许进入系统的TCP数据包,目标端口为22(SSH服务),且连接状态为NEW(新建的连接)。这个规则用于允许建立新的SSH连接,而拒绝已经建立的或结束的连接 + +[root@localhost ~]# iptables -A INPUT -m mac --mac-source 00:0C:29:56:A6:A2 -j ACCEPT +-m mac --mac-source 00:0C:29:56:A6:A2:使用MAC模块,匹配源MAC地址为00:0C:29:56:A6:A2的数据包 +- 该规则的作用是允许源MAC地址为00:0C:29:56:A6:A2的数据包进入系统。这个规则可以用于根据MAC地址限制网络访问,只允许特定的MAC地址通过防火墙进入系统 +``` + +## 3.8 规则的保存与恢复 + +- 用规则文件保存各规则,开机后导入该规则文件中的规则 + +```bash +# 保存到文件中 +[root@localhost ~]# iptables-save > /etc/sysconfig/iptables-config +# 从文件中导入规则 +[root@localhost ~]# iptables-restore < /etc/sysconfig/iptables-config +``` + + +- 安装`iptables-services`,在centos7和centos8上,通过该服务来帮助我们自动管理iptables规则。 + +```bash +[root@localhost ~]# yum -y install iptables-services +[root@localhost ~]# systemctl start iptables.service +[root@localhost ~]# systemctl enable iptables.service +``` + + + +## 3.9 NAT(地址转换协议) + +​ 网络地址转换 NAT(Network Address Translation),被广泛应用于各种类型 Internet 接入方式和各种类型的网络中。原因很简单,NAT 不仅完美地解决了 IP 地址不足的问题,而且还能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机。默认情况下,内部 IP 地址是无法被路由到外网的,内部主机 10.1.1.1 要与外部 Internet通信,IP 包到达 NAT 路由器时,IP 包头的源地址 10.1.1.1 被替换成一个合法的外网 IP,并在 NAT 转发表中保存这条记录。当外部主机发送一个应答到内网时,NAT 路由器收到后,查看当前 NAT 转换表,用 10.1.1.1 替换掉这个外网地址。NAT 将网络划分为内部网络和外部网络两部分,局域网主机利用 NAT 访问网络时,是将局域网内部的本地地址转换为全局地址(互联网合法的 IP 地址)后转发数据包; + +### 3.9.1 案例一:内网数据代理及服务映射 + +背景:现在有两个服务器server1和server2,server1是可以上网的,server2是不可以上网的,我们需要在server1上配置NAT来让server2通过server1去访问外网,同样我们也可以通过server1来访问位于server2服务器上的内部网站 + +img-内网代理和服务映射 + +**环境准备:** +两台虚拟机,可以克隆一个出来,然后server1添加一张仅主机的网卡。为了模拟server2不能上网的情况,将server2上原有的网卡改成仅主机模式。 + +由于server2的网卡修改成仅主机以后无法通过yum部署httpd,所以我们先在server2上部署一个Web网站 + +```bash +[root@localhost ~]# yum install -y httpd +[root@localhost ~]# systemctl enable --now httpd +# 确保关闭了firewalld,防止firewalld造成干扰 +[root@localhost ~]# systemctl disable --now firewalld + +# 检查80端口是否启动 +[root@localhost ~]# ss -nlt +State Recv-Q Send-Q Local Address:Port Peer Address:Port Process +LISTEN 0 128 0.0.0.0:22 0.0.0.0:* +LISTEN 0 128 [::]:22 [::]:* +LISTEN 0 511 *:80 *:* +``` + +**server1:** + +![img-网络适配器1](14.防火墙与selinux/网络适配器1.png) + +**server2:** + +![img-网络适配器2](14.防火墙与selinux/网络适配器2.png) + + +第一步:配置仅主机模式网卡的IP地址,其中: + +**server1:** + +​ IP:192.168.12.1/24 + +**server2:** + +​ IP:192.168.12.2/24 + +​ 网关:192.168.12.1 # 网关指向server1的地址 + +​ dns:114.114.114.114 + +第二步:在server1的Linux内核中开启数据转发功能 + +```bash +- 开启数据转发 +[root@localhost ~]# echo "net.ipv4.ip_forward=1" >> /usr/lib/sysctl.d/50-default.conf +[root@localhost ~]# sysctl -w net.ipv4.ip_forward=1 +[root@localhost ~]# sysctl -p + +或者: +[root@localhost ~]# echo 1 > /proc/sys/net/ipv4/ip_forward + +禁止数据转发: +[root@localhost ~]# echo 0 > /proc/sys/net/ipv4/ip_forward +``` + +第三步:访问测试 + +- server2访问外网测试 + + `ping www.baidu.com` + +- Windows访问server2上的网站测试 + +### 3.9.2 配置SNAT让server2能够网上冲浪 + +server1上操作: + +```bash +[root@localhost ~]# iptables -t nat -A POSTROUTING -s 192.168.12.0/24 -j SNAT --to-source 192.168.88.136 + +- POSTROUTING:数据包出口链 +- SNAT:将源地址进行转发 + +也可以使用如下写法(PAT): +[root@localhost ~]# iptables -t nat -A POSTROUTING -s 192.168.12.0/24 -j MASQUERADE +``` + +server2上测试网络连通性: + +`ping www.baidu.com` + +并且通过对Vmnet 8网卡抓包查看,虽然我们是用server去ping百度。但是却能抓到server1的ens160网卡发往百度的ping包 + +img-server2抓包 + +### 3.9.3 配置DNAT使得可以访问server2中的内部网站 + +```bash +[root@localhost ~]# iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.12.2:80 + +# 在PREROUTING链上将来自8080端口上的访问转发到内网中的192.168.88.12.2上的80端口 + +`访问测试:` +浏览器中输入:192.168.88.136:8080 + +- 扩展案例: +- 配置DNAT来映射server2上的ssh服务 +[root@localhost ~]# iptables -t nat -A PREROUTING -p tcp --dport 3322 -j DNAT --to-destination 192.168.12.2:22 + +`连接测试:` +ssh root@192.168.88.136 -p 3322 +``` + + + +## 3.10 案例二:转发本地端口 + +```bash +[root@localhost ~]# iptables -t nat -A PREROUTING -p tcp --dport 6666 -j REDIRECT --to-port 22 +# 此时连接6666端口,iptables会帮我们转发到22端口 +``` + + + +# 4. Firewalld + +Firewalld是CentOS 7.0新推出的管理netfilter的用户空间软件工具 + +并且支持划分区域zone,每个zone可以设置独立的防火墙规则 + +- 流量的规则匹配顺序 + - 先根据数据包中源地址,将其纳为某个zone + - 纳为网络接口所属zone + - 纳入默认zone,默认为public zone,管理员可以改为其它zone + +- firewalld中常用的区域名称及策略规则 + +| 区域 | 默认策略规则 | +| -------- | ------------------------------------------------------------ | +| public | 默认区域,对应普通网络环境。允许选定的连接,拒绝其他连接 | +| drop | 所有传入的网络包都会被直接丢弃,不会发送任何响应 | +| block | 所有传入的网络包会被拒绝,并发送拒绝信息 | +| external | 用于路由/NAT流量的外部网络环境。与public类似,更适用于网关、路由器等处理外部网络流量的设备 | +| dmz | 隔离区域,只允许选定的连接,适用于部署公开服务的网络区域,如 Web 服务器,可以最大限度地降低内部网络的风险 | +| work | 适用于工作环境,开放更多服务,如远程桌面、文件共享等。比 public 区域更加信任 | +| home | 适用于家庭环境,开放更多服务,比如默认情况下会开放一些如:3074端口(Xbox)、媒体、游戏数据等待 | +| trusted | 信任区域,允许所有连接 | + +## 4.1 firewall-cmd 管理工具 + +| 参数 | 作用 | +| ----------------------------- | ---------------------------------------------------- | +| --get-default-zone | 查询默认的区域名称 | +| --set-default-zone=<区域名称> | 设置默认的区域,使其永久生效 | +| --get-zones | 显示可用的区域 | +| --get-services | 显示预先定义的服务 | +| --get-active-zones | 显示当前正在使用的区域与网卡名称 | +| --add-source= | 将源自此IP或子网的流量导向指定的区域 | +| --remove-source= | 不再将源自此IP或子网的流量导向某个指定区域 | +| --add-interface=<网卡名称> | 将源自该网卡的所有流量都导向某个指定区域 | +| --change-interface=<网卡名称> | 将某个网卡与区域进行关联 | +| --list-all | 显示当前区域的网卡配置参数、资源、端口以及服务等信息 | +| --list-all-zones | 显示所有区域的网卡配置参数、资源、端口以及服务等信息 | +| --add-service=<服务名> | 设置默认区域允许该服务的流量 | +| --add-port=<端口号/协议> | 设置默认区域允许该端口的流量 | +| --remove-service=<服务名> | 设置默认区域不再允许该服务的流量 | +| --remove-port=<端口号/协议> | 设置默认区域不再允许该端口的流量 | +| --reload | 让“永久生效”的配置规则立即生效,并覆盖当前的配置规则 | +| --panic-on | 开启应急状况模式 | +| --panic-off | 关闭应急状况模式 | +| --permanent | 设定的当前规则保存到本地,下次重启生效 | + + + +## 4.2 简单用法(初体验) + +为了防止之前配置过的iptables规则导致与firewalld策略冲突,建议恢复快照后再开始练习 + +通过web服务部署一个网站,然后访问测试 + +```bash +[root@localhost ~]# yum -y install httpd +[root@localhost ~]# systemctl start httpd +``` + +在浏览器中输入`192.168.88.136`访问测试发现,我们无法访问此网站,这是因为默认情况下,firewalld是出于开启的状态,并且默认区域为public。在public区域中,除了部分系统服务,其他服务的流量一律是禁止的。 + +```bash +# 查看firewalld是否启用,可以看到当前是出于actice的状态 +[root@localhost ~]# systemctl status firewalld +● firewalld.service - firewalld - dynamic firewall daemon + Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled) + Active: active (running) since 四 2024-05-23 15:39:56 CST; 28min ago + Docs: man:firewalld(1) + Main PID: 6264 (firewalld) + CGroup: /system.slice/firewalld.service + └─6264 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid + +# 查看当前所在的区域 +[root@localhost ~]# firewall-cmd --get-default-zone +public +# 查看当前网卡所在的区域 +[root@localhost ~]# firewall-cmd --get-zone-of-interface=ens160 +public + +# 查询public区域是否允许请求SSH或者HTTP协议的流量 +[root@localhost ~]# firewall-cmd --zone=public --query-service=ssh +yes +[root@localhost ~]# firewall-cmd --zone=public --query-service=http +no + +`可以看出,在public区域中,默认是禁止来自http协议的请求数据包的` + +``` + +修改firewalld策略,使得我们可以访问到刚刚部署的网站 + +```bash +- 方法一:针对服务协议类型进行放行 +# 临时放行 +[root@localhost ~]# firewall-cmd --zone=public --add-service=http +# 可以加上--permanent实现永久放行 +[root@localhost ~]# firewall-cmd --permanent --zone=public --add-service=http + +- 方法二:针对服务具体访问请求的端口号进行放行 +[root@localhost ~]# firewall-cmd --zone=public --add-port=80/tcp +success +# 同样也可以加上--permanent实现永久放行 +``` + +以上方法二选一,然后访问测试 + +```shell +curl -I x.x.x.x +``` + +取消放行 + +```bash +[root@localhost ~]# firewall-cmd --zone=public --remove-service=http --permanent +success +``` + + + +## 4.3 其他用法 + +- 把firewalld服务的当前默认区域设置为public + +```bash +[root@localhost ~]# firewall-cmd --set-default-zone=public +Warning: ZONE_ALREADY_SET: public +success +[root@localhost ~]# firewall-cmd --get-default-zone +public +``` + +- 查看某个区域的详细配置 + +```bash +[root@localhost ~]# firewall-cmd --zone=public --list-all +public (active) + target: default + icmp-block-inversion: no + interfaces: ens33 + sources: + services: ssh dhcpv6-client + ports: 80/tcp + protocols: + masquerade: no + forward-ports: + source-ports: + icmp-blocks: + rich rules: +``` + +- 将firewalld 切换到紧急模式(panic mode)。这种模式下,firewalld 会采取一种非常严格的防护策略: + 1. 所有传入的网络连接都会被拒绝,除了 ssh 和 ICMP 回送(ping)请求。 + 2. 所有区域的防火墙规则都会被重置为默认的严格策略。 + 3. 所有区域的默认策略都会被设置为 DROP + +```bash +[root@localhost ~]# firewall-cmd --panic-on +# 取消紧急模式 +[root@localhost ~]# firewall-cmd --panic-off +``` + +- 临时生效/永久生效 + +```bash +# 临时生效 +[root@localhost ~]# firewall-cmd --zone=public --add-service=http +success +# 永久生效,加了--permanent所进行的更改,会被写入到配置文件中,下次开机任然生效 +[root@localhost ~]# firewall-cmd --permanent --zone=public --add-service=http +success +# 使得最近对防火墙规则变更或者所作的修改生效 +[root@localhost ~]# firewall-cmd --reload +success +[root@localhost ~]# firewall-cmd --zone=public --query-service=http +yes + +-配置文件路径 +/etc/firewalld/zones/public.xml +/etc/firewalld/direct.xml +/etc/firewalld/firewalld.conf +``` + +- 把firewalld服务中请求HTTPS协议的流量设置为永久拒绝,并立即生效 + +```bash +[root@localhost ~]# firewall-cmd --zone=public --remove-service=https --permanent +success +[root@localhost ~]# firewall-cmd --reload +success +``` + +- 把在firewalld服务中访问8080和8081端口的流量策略设置为允许,当前生效 + +```bash +# 可以指定要开放端口的范围 +[root@localhost ~]# firewall-cmd --zone=public --add-port=8080-8081/tcp +success +[root@localhost ~]# firewall-cmd --zone=public --list-ports +8080-8081/tcp +``` + +## 4.4 端口转发 + +通过firewalld,将用户访问服务器上6666端口的流量转发到22端口上,实现本地端口映射。我们就可以使用6666端口来远程连接到服务器 + +```bash +[root@localhost ~]# firewall-cmd --permanent --add-forward-port=port=6666:proto=tcp:toport=22 +success +[root@localhost ~]# firewall-cmd --reload +``` + +连接测试: + +```bash +C:\Users\Atopos>ssh root@192.168.88.136 -p 6666 +root@192.168.88.136's password: +Last login: Thu May 23 21:20:38 2024 from 192.168.88.1 +[root@localhost ~]# +可以看到我们通过6666端口成功连接到我们的虚拟机 +``` + +## 4.5 firewalld富规则 + +富规则是 firewalld 提供的一种灵活且强大的防火墙规则配置方式。与简单的端口和服务规则不同,富规则支持更复杂的匹配条件和操作。 + +使用富规则,可以实现复杂的防火墙策略,例如: + +- 允许特定 IP 地址访问某个端口 +- 拒绝特定 IP 地址访问某个服务 +- 限制某个网段的连接频率 +- 转发某个端口到另一个端口 + +**富规则的配置方法:** + +`firewall-cmd --permanent --add-rich-rule=''` + +需要注意的是,富规则的语法比较复杂,使用时务必仔细检查,以免引入安全隐患。同时,在修改完成后记得执行 `firewall-cmd --reload` 命令,让更改生效 + +其中rule部分的语法如下: + +```bash +rule [family=""] [source address="
"][source port=""] [destination address="
"][destination port=""] [protocol value=""] [icmp-block-inversion][forward-port port="" protocol="" to-port=""][masquerade][log [prefix=""] [level=""] [limit value=""] [accept][reject][drop] +``` + +其中各个选项的含义如下: + +- `family`: 指定地址族,可以是 `ipv4` 或 `ipv6` +- `source`/`destination`: 指定源/目标地址 +- `port`: 指定端口号 +- `protocol`: 指定协议,如 `tcp`、`udp` 等 +- `icmp-block-inversion`: 反转 ICMP 阻止规则 +- `forward-port`: 端口转发规则 +- `masquerade`: 启用地址伪装 +- `log`: 日志记录规则,可指定前缀、日志级别、限速 +- `accept`/`reject`/`drop`: 动作,分别表示允许、拒绝、丢弃 + + + +由于firewalld中的富规则比较复杂,我们从以下几个案例中来简单学习富规则的一些用法即可 + +**案例一:**允许某个IP地址访问系统中的Web网站服务 + +```bash +[root@localhost ~]# firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.88.1" port port="80" protocol="tcp" accept' +[root@localhost ~]# firewall-cmd --reload +``` + +这条规则允许来自 192.168.88.1 的 IPv4 流量访问 80 端口的 Web 服务 + +**案例二:**限制某个区域内的 SSH 连接次数 + +```bash +[root@localhost ~]# firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.88.0/24" port port="22" protocol="tcp" limit value="3/m" accept' +[root@localhost ~]# firewall-cmd --reload +``` + +这条规则允许 192.168.88.0/24 网段内的 IPv4 流量访问 22 端口的 SSH 服务,但是限制每分钟最多 3 次连接 + +**案例三**:使用富规则配置将6666端口上的访问转发到22端口上 + +```shell +# 这条规则将 6666 端口的 TCP 流量转发到 22 端口 +[root@localhost ~]# firewall-cmd --permanent --add-rich-rule='rule family="ipv4" forward-port port="6666" protocol="tcp" to-port="22"' +[root@localhost ~]# firewall-cmd --reload +``` + +**注意**:富规则的语法比较复杂,但是可以让您更精细地控制防火墙规则。通过组合不同的条件,您可以实现更复杂的防火墙策略。使用富规则时,请务必仔细检查语法和规则是否符合您的需求,以免引入安全隐患 + +# 5. 服务访问控制 TCP Wrappers + +TCP Wrappers是一种用于网络服务访问控制的工具,它使用配置文件中的规则来决定是否允许或拒绝对特定网络服务的访问。控制列表由两个主要文件组成:/etc/hosts.allow和/etc/hosts.deny。这些文件包含服务和客户端的规则,用于控制服务的访问权限 + +**TCP Wrapperes在最新的RockyLinux 9系统中默认已经不适用。通过上述讲到的iptables和firewalld来进行服务访问控制即可。** + +TCP Wrappers服务的控制列表文件中常用的参数 + +| 客户端类型 | 示例 | 满足示例的客户端列表 | +| -------------- | -------------------------- | --------------------------------- | +| 单一主机 | 192.168.10.10 | IP地址为192.168.10.10的主机 | +| 指定网段 | 192.168.10. | IP段为192.168.10.0/24的主机 | +| 指定网段 | 192.168.10.0/255.255.255.0 | IP段为192.168.10.0/24的主机 | +| 指定主机名称 | www.eagleslab.com | 主机名称为www.eagleslab.com的主机 | +| 指定所有客户端 | ALL,* | 所有主机全部包括在内 | + +在配置TCP Wrappers服务时需要遵循两个原则: + +- 编写拒绝策略规则时,填写的是服务名称,而非协议名称; +- 建议先编写拒绝策略规则,再编写允许策略规则,以便直观地看到相应的效果。 + +**/etc/hosts.deny:**该文件包含拒绝访问网络服务的规则。如果没有在hosts.allow文件中找到允许访问的规则,TCP Wrappers将检查hosts.deny文件以确定是否拒绝访问。以下规则拒绝所有客户端访问SSH服务: + +```bash +[root@localhost ~]# vim /etc/hosts.deny +# +# hosts.deny This file contains access rules which are used to +# deny connections to network services that either use +# the tcp_wrappers library or that have been +# started through a tcp_wrappers-enabled xinetd. +# +# The rules in this file can also be set up in +# /etc/hosts.allow with a 'deny' option instead. +# +# See 'man 5 hosts_options' and 'man 5 hosts_access' +# for information on rule syntax. +# See 'man tcpd' for information on tcp_wrappers +# +# +sshd:* +``` + +```bash +[c:\~]$ ssh root@192.168.88.136 + + +Connecting to 192.168.88.136:22... +Connection established. +To escape to local shell, press 'Ctrl+Alt+]'. +Connection closing...Socket close. + +Connection closed by foreign host. + +Disconnected from remote host(192.168.88.136:22) at 16:35:31. +``` + +**/etc/hosts.allow:**该文件包含允许访问网络服务的规则。每个规则占据一行,有两个主要部分:服务和允许访问的客户端。例如,以下规则允许192.168.88.0/24网段访问SSH服务: + +```bash +[root@localhost ~]# vim /etc/hosts.allow +# +# hosts.allow This file contains access rules which are used to +# allow or deny connections to network services that +# either use the tcp_wrappers library or that have been +# started through a tcp_wrappers-enabled xinetd. +# +# See 'man 5 hosts_options' and 'man 5 hosts_access' +# for information on rule syntax. +# See 'man tcpd' for information on tcp_wrappers +# +sshd:192.168.136. +``` + +# 6. SELinux安全子系统 + +SELinux 是一个强大的访问控制机制,它建立在 Linux 内核之上,为系统提供了更细粒度的安全策略控制。与传统的基于用户/组的访问控制不同,SELinux 采用基于角色(role)和类型(type)的强制访问控制(Mandatory Access Control, MAC)。简单来讲,SELinux 主要作用就是最大限度地减小系统中服务进程可访问的资源(最小权限原则) + +SELinux 的主要特点包括: + +1. 基于策略的安全访问控制:SELinux 通过预先定义的安全策略来控制系统进程和资源的访问权限,而不是依赖于用户/组的身份 +2. 最小特权原则:SELinux 遵循"最小特权"的安全原则,即只授予程序运行所需的最小权限,大大降低了系统被攻击者利用的风险 +3. 灵活的策略配置:SELinux 提供了丰富的策略配置选项,可以根据系统的具体需求进行定制和调整 +4. 审计能力:SELinux 内置了强大的审计日志记录功能,可以帮助管理员快速发现和分析系统安全事件 + +SELinux 的主要工作模式包括: + +- Enforcing 模式:完全执行 SELinux 策略,阻止任何未经授权的访问行为 +- Permissive 模式:只记录违反 SELinux 策略的行为,但不会阻止它们 +- Disabled 模式:完全关闭 SELinux 功能 + +## 6.1 调整SELinux的模式 + +**临时调整:** + +```bash +# 查看当前SELinux的模式 +[root@localhost ~]# getenforce +Enforcing + +# 临时调整为Permissive +[root@localhost ~]# setenforce 0 +[root@localhost ~]# getenforce +Permissive +[0]为Permissive模式,只记录行为,不阻止 +[1]为Enforcing模式 +``` + +临时调整只在当前状态有效,如果系统重启,将会恢复为默认的Enforcing模式 + +**永久调整:** + +通过编辑配置文件`/etc/selinux/config`中的SELINUX字段来更改SELinux的模式 + +```bash +[root@localhost ~]# vim /etc/selinux/config +# This file controls the state of SELinux on the system. +# SELINUX= can take one of these three values: +# enforcing - SELinux security policy is enforced. +# permissive - SELinux prints warnings instead of enforcing. +# disabled - No SELinux policy is loaded. +SELINUX=enforcing +# SELINUXTYPE= can take one of three values: +# targeted - Targeted processes are protected, +# minimum - Modification of targeted policy. Only selected processes are protected. +# mls - Multi Level Security protection. +SELINUXTYPE=targeted +``` + +## 6.2 自主访问控制(DAC) + +在没有使用 SELinux 的操作系统中,决定一个资源是否能被访问的因素是:某个资源是否拥有对应用户的权限(读、写、执行) + +只要访问这个资源的进程符合以上的条件就可以被访问 + +而最致命问题是,root 用户不受任何管制,系统上任何资源都可以无限制地访问 + +这种权限管理机制的主体是用户,也称为自主访问控制(DAC) + +## 6.3 强制访问控制(MAC) + +在使用了 SELinux 的操作系统中,决定一个资源是否能被访问的因素除了上述因素之外,还需要判断每一类进程是否拥有对某一类资源的访问权限 + +这样一来,即使进程是以 root 身份运行的,也需要判断这个进程的类型以及允许访问的资源类型才能决定是否允许访问某个资源。进程的活动空间也可以被压缩到最小 + +即使是以 root 身份运行的服务进程,一般也只能访问到它所需要的资源。即使程序出了漏洞,影响范围也只有在其允许访问的资源范围内。安全性大大增加 + +这种权限管理机制的主体是进程,也称为强制访问控制(MAC) + +## 6.4 案例说明: + +假设您有一个 Web 服务器,运行着 Apache 服务。默认情况下,Apache 需要访问一些系统资源,比如网页文件、日志文件等。如果没有 SELinux 的保护,任何程序都可以访问这些资源,这可能会带来安全隐患。 + +但是如果开启了 SELinux,情况就不一样了。SELinux 会为 Apache 进程设置专门的安全上下文(context),限制它只能访问特定的资源。比如: + +- Apache 进程的安全上下文是 `httpd_t` +- Apache 网页文件的安全上下文是 `httpd_sys_content_t` +- Apache 日志文件的安全上下文是 `httpd_log_t` + +这样一来,即使 Apache 进程被黑客利用,它也只能访问预先授权的资源,无法对系统造成更大的破坏 + +我们比较一下自己创建的acpache目录和原本的目录的安全上下文 + +```bash +# 安装Apache服务,使其产生/var/www/html/ +[root@localhost ~]# yum install -y httpd +[root@localhost ~]# systemctl start httpd +[root@localhost ~]# mkdir /html # 新建一个目录做为对比 + +# 查看Apache服务创建的工作目录和自己手动创建的目录的区别 +[root@localhost ~]# ls -Zd /var/www/html/ +system_u:object_r:httpd_sys_content_t:s0 /var/www/html/ +[root@localhost ~]# ls -Zd /html/ +unconfined_u:object_r:default_t:s0 /html/ + +- 会发现由Apache创建的工作目录 /var/www/html 具有一个httpd_sys_content_t的标签,这样,SELinux就可以通过在内核中对于这个带有这个标签的文件进行限制了 + +``` + +**字段解释:** + +普通目录:unconfined_u:object_r:user_home_dir_t:s0 + +- 用户身份(user): `unconfined_u` +- 角色(role): `object_r` +- 类型(type): `user_home_dir_t` +- 敏感度级别(sensitivity level): `s0` + +Apache工作目录:system_u:object_r:httpd_sys_content_t:s0 + +- 用户身份(user): `system_u` +- 角色(role): `object_r` +- 类型(type): `httpd_sys_content_t` +- 敏感度级别(sensitivity level): `s0` + +## 6.5 semanage + +用于管理SELinux策略的一个工具 + +如果系统中没有这个命令的话,可以通过以下命令安装一下该工具 + +```bash +[root@localhost ~]# yum install -y policycoreutils-python-utils-3.6-2.1.el9.noarch +``` + +**用法:** + +`semanage [选项] [文件]` + +**基本选项:** + +- -l:参数用于查询; +- -a:参数用于添加; +- -m:参数用于修改; +- -d:参数用于删除。 + +### 6.5.1 **案例:** + +更改httpd网站的默认工作目录,检查能否访问,如果不能访问,则为其添加`httpd_sys_content_t`策略再次访问测试: + +```bash +1. 部署httpd + +2. 更改httpd的配置文件,将工作目录变成/html +[root@localhost ~]# vim /etc/httpd/conf/httpd.conf +...... +DocumentRoot "/html" + +# +# Relax access to content within /var/www. +# + + AllowOverride None + # Allow open access: + Require all granted + + +# Further relax access to the default document root: + + +3. 在工作目录中添加一个index.html文件做为网站首页 +[root@localhost ~]# echo "

hello world

" > /html/test.html + +4. 浏览完访问192.168.88.10/test.html测试,发现访问被拒绝,提示403Forbidden +- 这是因为新工作目录/html 没有携带httpd_sys_content_t标签,所以httpd进程受到SELinux限制,无法访问该资源 + + +4. 可以向网站新的工作目录中新添加一条SELinux安全上下文,让这个目录以及里面的所有文件能够被httpd服务程序所访问到 +[root@localhost ~]# semanage fcontext -a -t httpd_sys_content_t /html +[root@localhost ~]# semanage fcontext -a -t httpd_sys_content_t /html/* + +#使用restorecon命令来检查SELinux上下文并且递归更新 +[root@localhost ~]# restorecon -Rv /html +restorecon reset /data/html context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0 +restorecon reset /data/html/index.html context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0 + + +5. 再次访问测试 + +Hello world +``` + + + +### 6.5.2 查看跟Apache(httpd)服务相关的所有标签 + +使用getsebool命令查询并过滤出所有与HTTP协议相关的安全策略。其中,off为禁止状态,on为允许状态。 + +```bash +[root@localhost ~]# getsebool -a | grep http +httpd_anon_write --> off +httpd_builtin_scripting --> on +httpd_can_check_spam --> off +httpd_can_connect_ftp --> off +httpd_can_connect_ldap --> off +httpd_can_connect_mythtv --> off +httpd_can_connect_zabbix --> off +httpd_can_network_connect --> off +httpd_can_network_connect_cobbler --> off +httpd_can_network_connect_db --> off +httpd_can_network_memcache --> off +httpd_can_network_relay --> off +httpd_can_sendmail --> off +httpd_dbus_avahi --> off +httpd_dbus_sssd --> off +httpd_dontaudit_search_dirs --> off +httpd_enable_cgi --> on +httpd_enable_ftp_server --> off +httpd_enable_homedirs --> off +httpd_execmem --> off +httpd_graceful_shutdown --> on +httpd_manage_ipa --> off +httpd_mod_auth_ntlm_winbind --> off +httpd_mod_auth_pam --> off +httpd_read_user_content --> off +httpd_run_ipa --> off +httpd_run_preupgrade --> off +httpd_run_stickshift --> off +httpd_serve_cobbler_files --> off +httpd_setrlimit --> off +httpd_ssi_exec --> off +httpd_sys_script_anon_write --> off +httpd_tmp_exec --> off +httpd_tty_comm --> off +httpd_unified --> on +httpd_use_cifs --> off +httpd_use_fusefs --> off +httpd_use_gpg --> off +httpd_use_nfs --> off +httpd_use_openstack --> off +httpd_use_sasl --> off +httpd_verify_dns --> off +named_tcp_bind_http_port --> off +prosody_bind_http_port --> off +[root@localhost ~]# setsebool -P httpd_enable_homedirs=on +``` + +面对如此多的`SELinux`域安全策略规则,实在没有必要逐个理解它们,我们只要能通过名字大致猜测出相关的策略用途就足够了。比如,想要开启`httpd`服务的个人用户主页功能,那么用到的`SELinux`域安全策略是`httpd_enable_homedir` \ No newline at end of file diff --git a/01.Linux基础/14.防火墙与selinux/server2抓包.png b/01.Linux基础/14.防火墙与selinux/server2抓包.png new file mode 100644 index 0000000..c949e5a Binary files /dev/null and b/01.Linux基础/14.防火墙与selinux/server2抓包.png differ diff --git a/01.Linux基础/14.防火墙与selinux/内网代理和服务映射.png b/01.Linux基础/14.防火墙与selinux/内网代理和服务映射.png new file mode 100644 index 0000000..5f328da Binary files /dev/null and b/01.Linux基础/14.防火墙与selinux/内网代理和服务映射.png differ diff --git a/01.Linux基础/14.防火墙与selinux/四表五链.png b/01.Linux基础/14.防火墙与selinux/四表五链.png new file mode 100644 index 0000000..7508c1e Binary files /dev/null and b/01.Linux基础/14.防火墙与selinux/四表五链.png differ diff --git a/01.Linux基础/14.防火墙与selinux/四表五链顺序.png b/01.Linux基础/14.防火墙与selinux/四表五链顺序.png new file mode 100644 index 0000000..f04b601 Binary files /dev/null and b/01.Linux基础/14.防火墙与selinux/四表五链顺序.png differ diff --git a/01.Linux基础/14.防火墙与selinux/硬件防火墙.png b/01.Linux基础/14.防火墙与selinux/硬件防火墙.png new file mode 100644 index 0000000..980bd09 Binary files /dev/null and b/01.Linux基础/14.防火墙与selinux/硬件防火墙.png differ diff --git a/01.Linux基础/14.防火墙与selinux/网络适配器1.png b/01.Linux基础/14.防火墙与selinux/网络适配器1.png new file mode 100644 index 0000000..9ed49f0 Binary files /dev/null and b/01.Linux基础/14.防火墙与selinux/网络适配器1.png differ diff --git a/01.Linux基础/14.防火墙与selinux/网络适配器2.png b/01.Linux基础/14.防火墙与selinux/网络适配器2.png new file mode 100644 index 0000000..25cec6d Binary files /dev/null and b/01.Linux基础/14.防火墙与selinux/网络适配器2.png differ diff --git a/01.Linux基础/14.防火墙与selinux/高级用法.png b/01.Linux基础/14.防火墙与selinux/高级用法.png new file mode 100644 index 0000000..db4059a Binary files /dev/null and b/01.Linux基础/14.防火墙与selinux/高级用法.png differ diff --git a/01.Linux基础/15.计划任务与日志.md b/01.Linux基础/15.计划任务与日志.md new file mode 100644 index 0000000..0b12039 --- /dev/null +++ b/01.Linux基础/15.计划任务与日志.md @@ -0,0 +1,528 @@ +# 1. 计划任务 + +- 计划任务主要是做一些周期性的任务,可以让某个任务按照我们的预期的时间或计划去执行。 + +## 1.1 单次调度执行at + +安装at软件包: + +`yum install -y at` + +启动at服务: + +```bash +[root@localhost ~]# systemctl start atd +[root@localhost ~]# systemctl enable atd # 开机自启动 +``` + +### 1.1.1 语法结构 + +`at [options] time` + +#### 1.1.1.1 选项: + +- -f:指定包含具体指令的任务文件 +- -q:指定新任务的队列名称 +- -l:显示待执行任务的列表 +- -d:删除指定的待执行任务 +- -c:根据序号查看具体任务的内容 + +或者也可以使用一下命令查看和删除 + +`atq`:查看待执行的任务 + +`atrm`:通过序号删除任务 + +#### 1.1.1.2 timespec + +```bash +at now +5min # 从现在开始5分钟后 +at teatime tomorrow # 明天的下午16:00 +at noon +4 days # 4天后的中午 +at 11:20 AM # 早上11:20 + ..... + ..... + +# 对于at任务的时间写法有很多,不需要记忆,用到的时候查询一下就行 +``` + + + +**示例一:**5分钟后创建一个文件 + +```bash +[root@localhost ~]# at now +5min +at> touch /root/file2 +at> # ctrl+D 结束任务编辑 +job 1 at Fri May 31 21:03:00 2024 +[root@localhost ~]# at -l # 查看任务 +1 Fri May 31 21:03:00 2024 a root +``` + +**示例二:**通过文件指定任务 + +```bash +[root@localhost ~]# vim at.jobs +useradd testuser +[root@localhost ~]# at -l +[root@localhost ~]# at now +5min -f at.jobs +job 2 at Fri May 31 21:03:00 2024 +[root@localhost ~]# at -l +2 Fri May 31 21:03:00 2024 a root +``` + + + +## 1.2 循环调度执行cron + +计划任务(Cron)是一种强大的工具,可以自动执行预定的任务。它非常适合定期运行脚本、备份数据、清理临时文件等一系列重复性任务。 + +crontab:是cron服务提供的管理工具 + +**检查cron服务有没有启动:** + +```bash +[root@localhost ~]# systemctl status crond.service +● crond.service - Command Scheduler + Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled) + Active: active (running) since 五 2024-05-31 21:30:35 CST; 3min 48s ago + Main PID: 6225 (crond) + CGroup: /system.slice/crond.service + └─6225 /usr/sbin/crond -n + +5月 31 21:30:35 localhost.localdomain systemd[1]: Started Command Scheduler. +5月 31 21:30:36 localhost.localdomain crond[6225]: (CRON) INFO (RANDOM_DELAY... +5月 31 21:30:36 localhost.localdomain crond[6225]: (CRON) INFO (running with... +Hint: Some lines were ellipsized, use -l to show in full. + + + +# 启动 +systemctl start crond.service +``` + +cron计划任务主要分为用户的任务和系统任务,用户任务是又具体某个用户编写的任务,而系统任务则在系统启动时始终生效 + +### 1.2.1 cron的基本语法 + +cron命令的基本语法如下: + +```text +crontab [-l | -r | -e | -n | -m] +``` + +#### 1.2.1.1 选项: + +- `-l`:列出当前用户的定时任务。 +- `-r`:删除当前用户的定时任务。 +- `-e`:编辑当前用户的定时任务。 +- `-n`:检查定时任务是否可用。 +- `-m`:发送类似于电子邮件的消息,用于通知定时任务执行的结果。 + +```bash +[root@localhost ~]# crontab -l # 列出当前用户所有计划任务 +[root@localhost ~]# crontab -r # 删除当前用户计划任务 +[root@localhost ~]# crontab -e # 编辑当前用户计划任务 +管理员可以使用 -u username,去管理其他用户的计划任务 +[root@localhost ~]# vi /etc/cron.deny # 这个文件中加入的用户名无法使用cron +``` + +### 1.2.2 cron任务的配置 + +每个用户可以通过`crontab`命令来编辑自己的任务计划。要编辑cron任务,可以使用`crontab -e`命令。 + +### 1.2.3 编辑cron任务 + +要编辑cron任务计划,可以使用以下命令打开cron编辑器: + +```bash +crontab -e +``` + +### 1.2.4 配置cron任务的格式 + +cron任务的格式如下: + +```bash +分 时 日 月 星期 命令 +* 表示任何数字都符合 +0 2 * * * date >> date.txt # 每天的2点 +0 2 14 * * date >> date.txt # 每月14号2点 +0 2 14 2 * date >> date.txt # 每年2月14号2点 +0 2 * * 5 date >> date.txt # 每个星期5的2点 +0 2 * 6 5 date >> date.txt # 每年6月份的星期5的2点 +0 2 2 * 5 date >> date.txt # 每月2号或者星期5的2点 星期和日同时存在,那么就是或的关系 +0 2 2 6 5 date >> date.txt # 每年6月2号或者星期5的2点 + +*/5 * * * * date >> date.txt # 每隔5分钟执行一次 +0 2 1,4,6 * * date >> date.txt # 每月1号,4号,6号的2点 +0 2 5-9 * * date >> date.txt # 每月5-9号的2点 + +* * * * * date >> date.txt # 每分钟 +0 * * * * date >> date.txt # 每整点 +* * 2 * * date >> date.txt # 每月2号的每分钟 +``` + +其中,第一个字段表示分钟(0-59),第二个字段表示小时(0-23),第三个字段表示天(1-31),第四个字段表示月份(1-12),第五个字段表示星期(0-7)。第六个字段是要执行的命令或脚本。 + +例如,要每天上午10点重启系统,可以配置如下: + +```bash +0 10 * * * reboot +``` + +在线crontab测试网站: + +### 1.2.5 查看任务存放位置 + +`/var/spool/cron/root` + +```bash +[root@localhost ~]# cat /var/spool/cron/root +* * * * * date >> file.txt +``` + +### 1.2.6 示例 + +这里列举几个常见的cron任务的示例: + +1. 每天凌晨3点备份文件: + + ```text + 0 3 * * * /path/to/backup.sh + ``` + +2. 每周一上午8点清理临时文件: + + ```text + 0 8 * * 1 /path/to/cleanup.sh + ``` + +3. 每隔10分钟检查服务器状态: + + ```text + */10 * * * * /path/to/check_status.sh + ``` + + + +## 1.3 cron系统任务 + +- 临时文件的清理`/tmp` `/var/tmp` +- 系统信息的采集 `sar` +- 日志的轮转(切割) `lgrotate` +- 通常不是由用户定义 +- 文件的位置 + +```bash +[root@localhost ~]# vim /etc/crontab # 默认没有定义任何计划任务 +[root@localhost ~]# ls /etc/cron.d # 定义的计划任务每个小时会执行 +0hourly sysstat +[root@localhost ~]# cat /etc/cron.d/0hourly +# Run the hourly jobs +SHELL=/bin/bash +PATH=/sbin:/bin:/usr/sbin:/usr/bin +MAILTO=root +01 * * * * root run-parts /etc/cron.hourly # 每小时01分以root身份执行/etc/cron.hourly/目录下的所有脚本 +``` + +- `crond`仅仅会执行每小时定义的脚本 `/etc/cron.hourly` + +```bash +[root@localhost ~]# ls /etc/cron.hourly/ +[root@localhost ~]# cat /etc/cron.hourly/0anacron +/usr/sbin/anacron -s # anacron是用来检查是否有错过的计划任务需要被执行 +[root@localhost ~]# vi /etc/anacrontab +1 5 cron.daily nice run-parts /etc/cron.daily +#每天开机 5 分钟后就检查 /etc/cron.daily 目录内的文件是否被执行,如果今天没有被执行,那就执行 +7 25 cron.weekly nice run-parts /etc/cron.weekly +#每隔 7 天开机后 25 分钟检查 /etc/cron.weekly 目录内的文件是否被执行,如果一周内没有被执行,就会执行 +©monthly 45 cron.monthly nice run-parts /etc/cron.monthly +#每隔一个月开机后 45 分钟检查 /etc/cron.monthly 目录内的文件是否被执行,如果一个月内没有被执行,那就执行 +``` + + + +# 2. 日志管理 + +Linux系统和许多程序会产生各种错误信息、告警信息和其他的提示信息,这些各种信息都应该记录到日志文件中。Linux系统日志对管理员来说,是了解系统运行的主要途径,因此需要对Linux日志系统有详细的了解。 + +## 2.1 常见的日志文件 + +| 日志文件 | 解释 | +| :-----------------: | :-------------------------------------------------: | +| /var/log/messages | 系统主日志文件 | +| /var/log/secure | 记录认证、安全的日志 | +| /var/log/maillog | 跟邮件postfix相关 | +| /var/log/cron | crond、at进程产生的日志 | +| /var/log/dmesg | 记录系统启动时加载的硬件相关信息日志 | +| /var/log/yum.log | yum的日志 | +| /var/log/mysqld.log | MySQL日志 | +| var/log/xferlog | 和访问FTP服务器相关 | +| /var/log/boot.log | 系统启动过程日志记录存放 | +| /var/log/wtmp | 当前登录的用户(可以直接在命令行输入w查看) | +| /var/log/lastlog | 所有用户的登录情况(可以直接在命令行输入lastlog查看) | + +## 2.2 查看日志 + +有多种方法可以查看日志,可以通过cat、tail等命令来查看 + +但是往往日志文件中内容都是非常多的,所以对于初学者通过cat查看不是很直观。这个时候我们可以配合一些其他过滤工具来过滤日志里面的内容。如:grep,awk等等。 + +### 2.2.1 示例一:查看message日志中关于ens160网卡的信息 + +```bash +[root@localhost ~]# cat /var/log/messages | grep ens160 +# 通过grep来过滤出跟ens160相关的内容 +[root@localhost ~]# cat /var/log/messages | grep ens160 +Nov 9 11:13:20 localhost kernel: vmxnet3 0000:03:00.0 ens160: renamed from eth0 +Nov 9 11:13:24 localhost NetworkManager[785]: [1731122004.5476] manager: (ens160): new Ethernet device (/org/freedesktop/NetworkManager/Devices/2) +Nov 9 11:13:24 localhost NetworkManager[785]: [1731122004.5482] device (ens160): state change: unmanaged -> unavailable (reason 'managed', sys-iface-state: 'external') +Nov 9 11:13:24 localhost kernel: vmxnet3 0000:03:00.0 ens160: intr type 3, mode 0, 5 vectors allocated +Nov 9 11:13:24 localhost kernel: vmxnet3 0000:03:00.0 ens160: NIC Link is Up 10000 Mbps +Nov 9 11:13:24 localhost NetworkManager[785]: [1731122004.5604] device (ens160): carrier: link connected +``` + +### 2.2.2 示例二:统计远程登录信息 + +```bash +[root@localhost ~]# cat /var/log/secure | grep Accepted +Nov 9 14:14:19 localhost sshd[4953]: Accepted password for root from 192.168.88.1 port 57324 ssh2 +Nov 28 11:44:38 localhost sshd[1523]: Accepted password for root from 192.168.88.1 port 1665 ssh2 +Nov 28 11:44:38 localhost sshd[1525]: Accepted password for root from 192.168.88.1 port 1666 ssh2 +Nov 28 13:39:50 localhost sshd[1514]: Accepted password for root from 192.168.88.1 port 3682 ssh2 +``` + +### 2.2.3 示例三:查看用户与用户组相关的信息 + +```bash +[root@localhost ~]# cat /var/log/secure | grep useradd +May 30 11:09:15 localhost useradd[7762]: new group: name=user1, GID=1001 +May 30 11:09:15 localhost useradd[7762]: new user: name=user1, UID=1001, GID=1001, home=/home/user1, shell=/bin/bash +May 30 11:09:18 localhost useradd[7767]: new group: name=user2, GID=1002 +May 30 11:09:18 localhost useradd[7767]: new user: name=user2, UID=1002, GID=1002, home=/home/user2, shell=/bin/bash +[root@localhost ~]# cat /var/log/secure | grep groupadd +May 30 11:09:29 localhost groupadd[7772]: group added to /etc/group: name=IT, GID=1003 +May 30 11:09:29 localhost groupadd[7772]: group added to /etc/gshadow: name=IT +May 30 11:09:29 localhost groupadd[7772]: new group: name=IT, GID=1003 +``` + +### 2.2.4 示例四:查看计划任务cron相关 + +```bash +[root@localhost ~]# cat /var/log/cron +May 30 10:15:01 localhost run-parts(/etc/cron.daily)[7516]: finished logrotate +May 30 10:15:01 localhost run-parts(/etc/cron.daily)[7504]: starting man-db.cron +May 30 10:15:01 localhost run-parts(/etc/cron.daily)[7715]: finished man-db.cron +May 30 10:15:01 localhost anacron[7492]: Job `cron.daily' terminated +May 30 10:35:01 localhost anacron[7492]: Job `cron.weekly' started +May 30 10:35:01 localhost anacron[7492]: Job `cron.weekly' terminated +May 30 10:55:01 localhost anacron[7492]: Job `cron.monthly' started +May 30 10:55:01 localhost anacron[7492]: Job `cron.monthly' terminated +May 30 10:55:01 localhost anacron[7492]: Normal exit (3 jobs run) +May 30 11:01:01 localhost CROND[7741]: (root) CMD (run-parts /etc/cron.hourly) +May 30 11:01:01 localhost run-parts(/etc/cron.hourly)[7741]: starting 0anacron +May 30 11:01:01 localhost run-parts(/etc/cron.hourly)[7750]: finished 0anacron +``` + +## 2.3 日志系统-rsyslogd + +### 2.3.1 处理日志的进程 + +`rsyslogd`:绝大部分日志记录,和系统操作有关,安全,认证`sshd,su`,计划任务`at,cron` + +`httpd/nginx/mysql`等等应用可以以自己的方式记录日志 + +```bash +[root@localhost ~]# ps aux |grep rsyslogd +root 6808 0.0 0.2 216408 5016 ? Ssl 09:32 0:00 /usr/sbin/rsyslogd -n +root 7724 0.0 0.0 112724 988 pts/0 S+ 10:39 0:00 grep --color=auto rsyslogd +``` + +日志可以存放在本地 + +日志可以存放在远程服务器 + +### 2.3.2 配置文件 + +```bash +[root@localhost ~]# rpm -qc rsyslog +/etc/logrotate.d/syslog # 日志轮转(切割)相关 +/etc/rsyslog.conf # rsyslogd的主配置文件 +/etc/sysconfig/rsyslog # rsyslogd相关文件 +``` + +```bash +[root@localhost ~]# vim /etc/rsyslog.conf +# 告诉rsyslogd进程 哪个设备(facility),关于哪个级别的信息,以及如何处理 +authpriv.* /var/log/secure +mail.* /var/log/maillog +cron.* /var/log/cron +*.emerg :omusrmsg:* +authpriv.* * # 所有终端 +authpriv.* @192.168.1.123 # UDP +authpriv.* @@192.168.1.123 # TCP +``` + +### 2.3.3 日志类型(facility) + +| 序号 | Facility | 解释 | +| :---: | :-----------: | :--------------------------------------------------------: | +| 0 | kern (kernel) | Linux内核产生的信息,大部分是硬件检测和内核功能的启用 | +| 1 | user | 用户层级产生的信息 | +| 2 | mail | 所有邮件收发的相关信息 | +| 3 | daemon | 系统服务产生的信息 | +| 4 | auth | 与认证、授权相关的信息,如`login`、`ssh`、`su`等产生的信息 | +| 5 | syslog | `syslogd`服务产生的信息 | +| 6 | lpr | 打印相关的信息 | +| 7 | news | 新闻群组相关的信息 | +| 8 | uucp | Unix to Unix Copy Protocol 早期Unix系统间的数据交换协议 | +| 9 | cron | 周期性计划任务程序,如`cron`、`at`等产生的信息 | +| 10 | authpriv | 与auth类似,但记录的多为帐号相关的信息,如pam模块的调用等 | +| 16~23 | local0~local7 | 保留给本地用户使用的日志类型,通常与终端交互相关。 | + +**日志级别** + +| 级别(日志重要级别) | 解释 | +| :----------------: | :------------------------------------------: | +| LOG_EMERG | 紧急,致命,服务无法继续运行,如配置文件丢失 | +| LOG_ALERT | 报警,需要立即处理,如磁盘空间使用95% | +| LOG_CRIT | 致命行为 | +| LOG_ERR(error) | 错误行为 | +| LOG_WARNING | 警告信息 | +| LOG_NOTICE | 普通 | +| LOG_INFO | 标准信息 | +| LOG_DEBUG | 调试信息,排错才开,一般不建议使用 | + + + +### 2.3.4 案例:远程管理日志 + +**环境准备:**两台服务器,其中server1模仿生产服务器,server2做为日志服务器。 + +**实验要求:**server1和server2可以通信(建议两边的虚拟网卡都调整成NAT模式) + +**实验目的:**通过配置rsyslog,使得server1上关于ssh连接的日志发送到server2日志服务器上保存,在server2上可以查看到server1上的ssh日志 + +**注意关闭server1和server2上的firewalld!!** + +`systemctl stop firewalld` + + + +**server1上配置:** + +1. 编辑`/etc/rsyslog.conf`文件 + +```bash +# 在最下面添加如下字段 +[root@localhost ~]# vim /etc/rsyslog.conf +:msg,contains,"sshd" @192.168.88.20:514 + +#=====字典解释===== +- msg 要发送的消息 +- contains,"sshd" 过滤器,过滤所有跟sshd有关的日志 +- @192.168.88.20:514 要发送到日志服务器的地址,其中@表示UDP,@@表示TCP +``` + +2. 重启rsyslog服务 + +```bash +[root@localhost ~]# systemctl restart rsyslog +``` + +**server2上配置:** + +修改配置文件 + +```bash +[root@localhost ~]# vim /etc/rsyslog.conf +# Provides UDP syslog reception +# 在配置文件中下入下面几行 +# 加载imudp模块,启用对UDP网络接口的支持 +$ModLoad imudp +$UDPServerRun 514 + +$ModLoad imtcp +$InputTCPServerRun 514 + +:msg,contains,"sshd" /var/log/remote_ssh.log # 任意位置添加规则, + +# 重启rsyslog服务 +[root@localhost ~]# systemctl restart rsyslog +``` + +**测试:** + +通过MobaXterm远程连接server1 + +通过`tail -f /var/log/remote_ssh.log`来实时检测是否又日志记录发过来 + + + +## 2.4 日志文件归档 + +如果我们不管理系统产生的上述各种日志文件,那么久而久之日志就会变成“屎山”。日志文件及内容越堆越多,不仅难以查阅,还会因为单一文件过大而影响新的内容写入的效率 + +`logrotate`就是一个不错的日志处理程序,准确的说是对日志进行“归档”之类的工作 + +## 2.5 logrotate(日志轮转) + +- 如果没有日志轮转,日志文件会越来越大 +- 将丢弃系统中最旧的日志文件,以节省空间 +- 事实上`logrotate`是挂在`cron`配置目录`cron.daily`下面的,所以会被`cron`每天执行一次: + +```bash +[root@localhost ~]# ll /etc/cron.daily/ +-rwx------. 1 root root 219 10月 31 2018 logrotate +-rwxr-xr-x. 1 root root 618 10月 30 2018 man-db.cron + +# 查看logrotate内容 +[root@localhost ~]# cat /etc/cron.daily/logrotate +#!/bin/sh +/usr/sbin/logrotate -s /var/lib/logrotate/logrotate.status /etc/logrotate.conf + +# 日志轮转状态/var/lib/logrotate/logrotate.status +# 日志轮转规则按照/etc/logrotate.conf中来 +``` + +- logrotate主配置文件 + +```bash +[root@localhost ~]# vim /etc/logrotate.conf +weekly # 多久会执行一次“轮转”,这里设置的是每周一次 +rotate 4 # 轮转后会保留几个历史日志文件,这里是4,也就是说轮转后会删除编号为5的历史日志 +create # 轮转后创建新的空白日志 +dateext # 使用日期而非数字编号作为历史日志的标识进行轮转 +include /etc/logrotate.d # 加载/etc/logrotate.d目录下的配置文件 +/var/log/wtmp { # 对/var/log/wtmp日志的特殊设置 + monthly # 每月进行一次轮转 + create 0664 root utmp # 创建的新的空白日志权限为0664,用户为root,用户组为utmp + minsize 1M # 原始日志文件超过1M大小才进行轮转 + rotate 1 # 仅保留1个历史日志文件 +} +/var/log/btmp { # 对/var/log/btmp日志的特殊设置 + missingok # 日志轮转期间任何错误都会被忽略 + monthly # 每月进行一次轮转 + create 0600 root utmp # 创建的新日志文件权限为0600,用户为root,用户组为utmp + rotate 1 # 仅保留1个历史日志文件 +} +``` + +- 子配置文件 + +```bash +[root@localhost ~]# cat /etc/logrotate.d/syslog +/var/log/cron +/var/log/maillog +/var/log/messages +/var/log/secure +/var/log/spooler +{ + missingok + sharedscripts + postrotate + /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true + endscript +} +``` \ No newline at end of file diff --git a/01.Linux基础/16.管道与重定向.md b/01.Linux基础/16.管道与重定向.md new file mode 100644 index 0000000..78ea519 --- /dev/null +++ b/01.Linux基础/16.管道与重定向.md @@ -0,0 +1,271 @@ +# 1. I/O重定向 + +I/O对应系统中的输入(input)和输出(output) + +其中输入可以是键盘输入,鼠标输入等等。同样输出也可以输出到终端中或者文件中 + +对于Linux系统,我们常见的输入输出的类型有: + +- 标准输入 0 +- 标准正确输出 1 +- 标准错误输出 2 + +![img-IO重定向](16.管道与重定向/IO重定向.png) + +## 1.1 File Descriptors(FD,文件描述符 ) + +**案例切入**:通过vim来追踪fd的信息 + +我们打开两个终端,然后在其中一个终端中使用vim打开一个文件,然后在另一个终端中查看与FD相关的文件 + +```bash +终端一:vim file.txt + +终端二: +[root@192 fd]# ps aux | grep vim +root 16350 0.0 0.2 149432 5064 pts/1 S+ 23:01 0:00 vim file1.txt +root 16360 0.0 0.0 112724 984 pts/0 S+ 23:03 0:00 grep --color=auto vim +[root@192 fd]# cd /proc/16350/fd +[root@192 fd]# ll +总用量 0 +lrwx------. 1 root root 64 6月 1 23:01 0 -> /dev/pts/1 +lrwx------. 1 root root 64 6月 1 23:01 1 -> /dev/pts/1 +lrwx------. 1 root root 64 6月 1 23:01 2 -> /dev/pts/1 +lrwx------. 1 root root 64 6月 1 23:01 3 -> /root/.file1.txt.swp + +# 这里有0 1 2 3 +其中0表示标准输入 +1表示标准正确输出 +2表示标准错误输出 +其他的都为输出到文件中 + +# 我们可以尝试通过0来向我们刚刚vim打开的文档里面输入内容 +[root@192 fd]# echo hello > 0 + +这里也是体现Linux一切皆文件的特性,连输入输出都可以由文件来进行控制 +``` + +| Number | Channel name | Description | Default connection | Usage | +| ------ | ------------ | ----------- | ------------------ | ------------ | +| 0 | stdin | 标准输入 | 键盘 | 只读 | +| 1 | stdout | 标准输出 | 命令行 | 只写 | +| 2 | stderr | 标准错误 | 命令行 | 只写 | +| 3+ | filename | 其他文件 | none | 读 and/or 写 | + +**所以说,到底什么是文件描述符呢?** + +文件描述符是操作系统内部用于管理和跟踪打开的文件和I/O资源。比如当我们打开一个文件的时候,操作系统会分配一个文件描述符给这个文件,我们可以通过这个文件描述符来对文件进行读写等操作。 + +每个进程默认打开三个文件描述符: + +- `0`:标准输入(stdin) +- `1`:标准输出(stdout) +- `2`:标准错误输出(stderr) + + + +# 2. 输出重定向(覆盖,追加) + +`>`:覆盖重定向 + +`>>`:追加重定向 + +`1>`:覆盖重定向正确的输出数据流 + +`1>>`:追加重定向正确的输出数据流 + +`2>`:覆盖重定向错误输出数据流 + +`2>>`:追加重定向错误输出数据流 + + + +**示例一:输出重定向(覆盖)** + +```bash +[root@localhost ~]# date 1> date.txt +[root@localhost ~]# cat date.txt +``` + +![img-输出重定向1](16.管道与重定向/输出重定向1.png) + +**示例二:输出重定向(追加)** + +```bash +[root@localhost ~]# date >> date.txt +[root@localhost ~]# date >> date.txt +[root@localhost ~]# date >> date.txt +[root@localhost ~]# cat date.txt +``` + +![img-输出重定向2](16.管道与重定向/输出重定向2.png) + +**示例三:错误输出重定向** + +```bash +[root@localhost ~]# touch file +[root@localhost ~]# cd file +bash: cd: file: 不是目录 +[root@localhost ~]# cd file 2> log.txt +[root@localhost ~]# cat log.txt +bash: cd: file: 不是目录 +``` + +![img-错误输出重定向](../../../../课程/课件git版/SRE/Linux基础/管道与重定向/错误输出重定向.png) + +**示例四:正确和错误都输入到相同位置** + +```bash +[root@localhost ~]# ls /home/ /test +ls: 无法访问/test: 没有那个文件或目录 +/home/: +user01 + +#将正确和错误的输出都输出的文件中 +[root@localhost ~]# ls /home/ /test &> log.txt +[root@localhost ~]# cat log.txt +ls: 无法访问/test: 没有那个文件或目录 +/home/: +user01 + +``` + +![image-20191212140103719]() + +**示例五:正确和错误都输出到相同位置** + +```bash +[root@localhost ~]# ls /home/ /test > log.txt +ls: 无法访问/test: 没有那个文件或目录 +[root@localhost ~]# ls /home/ /test > log.txt 2>&1 +[root@localhost ~]# cat log.txt +ls: 无法访问/test: 没有那个文件或目录 +/home/: +user01 + +``` + +![img-正确错误输出重定向](16.管道与重定向/正确错误输出重定向.png) + +**示例六:重定向到空设备`/dev/null`** + +```bash +# /dev/null是一个空设备,可以理解为一个无限大的垃圾堆,可以往里面丢任意多的垃圾进去 + +[root@localhost ~]# ls /home/ /test > log.txt 2>/dev/null +[root@localhost ~]# cat log.txt +/home/: +user01 + +[root@localhost ~]# ls /home/ /test &>/dev/null +``` + + + +# 3. 输入重定向 + +标准输入:`< 等价 0<` + +**案例1:** + +```bash +[root@localhost ~]# cat < /etc/passwd +root:x:0:0:root:/root:/bin/bash +bin:x:1:1:bin:/bin:/sbin/nologin +daemon:x:2:2:daemon:/sbin:/sbin/nologin +adm:x:3:4:adm:/var/adm:/sbin/nologin +lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin +sync:x:5:0:sync:/sbin:/bin/sync +shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown +halt:x:7:0:halt:/sbin:/sbin/halt +mail:x:8:12:mail:/var/spool/mail:/sbin/nologin +operator:x:11:0:operator:/root:/sbin/nologin +games:x:12:100:games:/usr/games:/sbin/nologin +ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin +nobody:x:99:99:Nobody:/:/sbin/nologin +systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin +dbus:x:81:81:System message bus:/:/sbin/nologin +polkitd:x:999:998:User for polkitd:/:/sbin/nologin +sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin +postfix:x:89:89::/var/spool/postfix:/sbin/nologin +chrony:x:998:996::/var/lib/chrony:/sbin/nologin +user01:x:1000:1000::/home/user01:/bin/bash + +``` + +**案例2:** + +```bash +[root@localhost ~]# grep 'root' < /etc/passwd +root:x:0:0:root:/root:/bin/bash +operator:x:11:0:operator:/root:/sbin/nologin +``` + +**案例3:at** + +```bash +[root@localhost ~]# at now +5 min +at> ping -c4 baidu.com +at> +job 1 at Thu Dec 12 15:50:00 2019 +[root@localhost ~]# vim at.txt +ping -c4 baidu.com +[root@localhost ~]# at now +1 min < at.txt +job 2 at Thu Dec 12 15:47:00 2019 +``` + +**案例4:**利用重定向建立多行的文件 + +```bash +[root@localhost ~]# cat > file2.txt < hello +> python +> linux +> EOF +[root@localhost ~]# cat file2.txt +hello +python +linux +``` + + + +# 4. 进程管道 + +用法:`command1 | command2 | command3 | ....` + +![img-进程管道](16.管道与重定向/进程管道.png) + +```bash +[root@localhost ~]# ll /dev/ |less +[root@localhost ~]# ps aux |grep 'sshd' +[root@localhost ~]# rpm -qa |grep 'httpd' +[root@localhost ~]# yum list |grep 'httpd' +``` + +**案例1:** 统计出最占CPU的5个进程 + +```bash +[root@localhost ~]# ps aux --sort=-%cpu |head -6 +``` + +**案例2:** 过滤secure文件中跟ssh相关的内容 + +```bash +[root@localhost ~]# cat /var/log/secure | grep ssh +``` + + + +# 5. tee管道 + +![img-tee管道](16.管道与重定向/tee管道.png) + +```bash +[root@localhost ~]# du -h . | sort -hr | tee file_sizes.txt +44K . +0 ./dir1 +0 ./dir + +使用 du 命令列出当前目录下的文件大小,然后通过管道传递给 sort 命令进行降序排序。最后使用 tee 命令将排序结果同时输出到终端和 file_sizes.txt 文件中。这样既可以在终端查看结果,也可以保存一份供后续分析使用 +``` \ No newline at end of file diff --git a/01.Linux基础/16.管道与重定向/IO重定向.png b/01.Linux基础/16.管道与重定向/IO重定向.png new file mode 100644 index 0000000..3e8ec8a Binary files /dev/null and b/01.Linux基础/16.管道与重定向/IO重定向.png differ diff --git a/01.Linux基础/16.管道与重定向/tee管道.png b/01.Linux基础/16.管道与重定向/tee管道.png new file mode 100644 index 0000000..9417fe1 Binary files /dev/null and b/01.Linux基础/16.管道与重定向/tee管道.png differ diff --git a/01.Linux基础/16.管道与重定向/正确错误输出重定向.png b/01.Linux基础/16.管道与重定向/正确错误输出重定向.png new file mode 100644 index 0000000..2bbcaa6 Binary files /dev/null and b/01.Linux基础/16.管道与重定向/正确错误输出重定向.png differ diff --git a/01.Linux基础/16.管道与重定向/输出重定向1.png b/01.Linux基础/16.管道与重定向/输出重定向1.png new file mode 100644 index 0000000..a7be910 Binary files /dev/null and b/01.Linux基础/16.管道与重定向/输出重定向1.png differ diff --git a/01.Linux基础/16.管道与重定向/输出重定向2.png b/01.Linux基础/16.管道与重定向/输出重定向2.png new file mode 100644 index 0000000..8ae23d3 Binary files /dev/null and b/01.Linux基础/16.管道与重定向/输出重定向2.png differ diff --git a/01.Linux基础/16.管道与重定向/进程管道.png b/01.Linux基础/16.管道与重定向/进程管道.png new file mode 100644 index 0000000..f7ee3f9 Binary files /dev/null and b/01.Linux基础/16.管道与重定向/进程管道.png differ diff --git a/02.企业服务/01.SSH远程管理.md b/02.企业服务/01.SSH远程管理.md new file mode 100644 index 0000000..58b09cc --- /dev/null +++ b/02.企业服务/01.SSH远程管理.md @@ -0,0 +1,194 @@ +# 1. SSH协议/OpenSSH服务 + +SSH(Secure Shell)协议是一种网络协议,用于加密方式远程登录到服务器。它提供了一种安全的方法来传输数据,防止数据在传输过程中被窃听或篡改。SSH 协议不仅用于远程登录,还可用于远程执行命令、文件传输(SFTP)、端口转发等。 + +OpenSSH 是 SSH 协议的一个开源实现工具,由 OpenBSD 项目开发和维护。它是大多数 Unix和类 Unix 操作系统中默认的 SSH 实现,包括 Linux、macOS 和 FreeBSD 等等 + +OpenSSH 提供了服务端程序(openssh-server)和客户端工具(openssh-client) + +* Mac 和 Linux 中默认已安装 ssh 客户端,可直接在终端中使用 ssh 命令 +* Windows 需手动安装 ssh 客户端,较常用的 Windows SSH 客户端有 XShell和 MobaXterm + +**SSH 能够提供两种安全验证的方法:** + +* 基于**口令**的验证—用账户和密码来验证登录 +* 基于**密钥**的验证—需要在本地生成密钥对,然后把密钥对中的公钥上传至服务器,并与服务器中的公钥进行比较;该方式相较来说更安全 + +# 2. SSH 客户端使用 + +OpenSSH 服务提供我们 SSH 工具,该工具采用 SSH 协议来连接到远程主机上 + +## 2.1 SSH 常用操作 + +通过 SSH 协议登录远程主机 + +```shell +# 以 root 用户登录远程主机(192.168.88.20) +[root@localhost ~]# ssh root@192.168.88.20 +root@192.168.88.20's password: +Last login: Tue Dec 24 15:42:36 2024 from 192.168.88.1 +``` + +指定连接远程主机的端口号 + +```shell +# -P 参数指定远程主机的端口号 +[root@localhost ~]# ssh root@192.168.88.20 -P22 +# +root@192.168.88.20's password: +Last login: Tue Dec 24 15:43:18 2024 from 192.168.88.10 +``` + +不登陆到远程主机中,仅仅执行某个命令并返回结果 + +```shell +[root@localhost ~]# ssh root@192.168.88.20 cat /etc/hosts +root@192.168.88.20's password: +127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 +::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 +``` + +## 2.2 SCP 远程文件传输 + +除了连接到远程主机之外,我们也可以用附带的小工具 SCP 来进行远程文件下载和本地文件上传至远程主机 + + + +```shell +# 将本地文件上传至远程主机 +本地 % scp root@172.16.175.129:/etc/passwd ./ +root@172.16.175.129's password: +passwd 100% 945 1.2MB/s 00:00 +# 将远程主机上的文件下载至本地 +本地 % scp cmatrix-1.2a.tar.gz root@172.16.175.129:/tmp/ +root@172.16.175.129's password: +cmatrix-1.2a.tar.gz 100% 73KB 20.2MB/s 00:00 +``` + +# 3. 服务端配置文件 + +sshd 服务的配置信息保存在 `/etc/ssh/sshd_config` 文件中。运维人员一般会把保存着最主要配置信息的文件称为主配置文件,而配置文件中有许多以井号开头的注释行,要想让这些配置参数生效,需要在修改参数后再去掉前面的 `#` + +**常见配置项** + +| 配置项 | 说明 | +| :-------------------------------- | :---------------------------------- | +| Port 22 | 默认的sshd服务端口 | +| ListenAddress 0.0.0.0 | 设定sshd服务器监听的IP地址 | +| Protocol 2 | SSH协议的版本号 | +| HostKey /etc/ssh/ssh_host_key | SSH协议版本为1时,DES私钥存放的位置 | +| HostKey /etc/ssh/ssh_host_rsa_key | SSH协议版本为2时,RSA私钥存放的位置 | +| HostKey /etc/ssh/ssh_host_dsa_key | SSH协议版本为2时,DSA私钥存放的位置 | +| PermitRootLogin yes | 设定是否允许root管理员直接登录 | +| StrictModes yes | 当远程用户的私钥改变时直接拒绝连接 | +| MaxAuthTries 6 | 最大密码尝试次数 | +| MaxSessions 10 | 最大终端数 | +| PasswordAuthentication yes | 是否允许密码验证 | +| PubkeyAuthentication yes | 是否允许使用公钥进行身份验证 | + +# 4. 安全密钥验证 + +上面讲到,ssh 远程连接,除了使用密码的方式登录,还可以使用密钥对进行登录。相比于密码等于而言,密钥登录会更加的安全 + +如果使用公钥和私钥进行加解密,那么我们称之为是一种非对称加密的方式进行加密,那同样的还有对称加密,同一个密钥加解密 + +非对称加密是一种加密方式,它涉及到两个密钥:一个公钥和一个私钥。公钥可以公开给任何人,而私钥则必须保密,只有密钥的拥有者才知道。这种加密方式的特点是使用公钥加密的数据只能通过对应的私钥来解密,反之亦然,使用私钥加密的数据只能通过对应的公钥来解密。 + +## 4.1 非对称加密 + +1. **密钥生成**:首先生成一对密钥,一个公钥和一个私钥。这两个密钥是数学上相关的,但即使知道其中一个,也很难计算出另一个。 +2. **加密**:发送方使用接收方的公钥来加密信息。这个过程是可逆的,但只有拥有正确私钥的人才能解密。 +3. **解密**:接收方使用自己的私钥来解密信息。这个过程确保了只有拥有私钥的接收方才能阅读信息。 + +我们可以想象一下,你有一个非常特别的邮箱,这个邮箱有一个特点:它有两个锁。一个锁是公开的,任何人都可以往里投信,但只有你知道如何打开它(私钥)。另一个锁是私有的,只有你知道它在哪里,而且只有你拥有打开它的钥匙(公钥)。 + +- **公钥(锁)**:你把这个特别的锁(公钥)放在一个公共的地方,比如你的家门口。任何人都可以给你写信,他们只需要用这个锁把你的信锁起来,然后投进你的邮箱。因为只有你知道如何打开这个锁,所以你的信件在运输过程中是安全的。 +- **私钥(钥匙)**:你把打开这个锁的钥匙(私钥)藏在家里一个安全的地方。当信件到达时,你可以用你的私钥打开锁,取出信件阅读。 +- **安全性**:即使有人试图复制这个锁(公钥),他们也无法制造出能打开它的钥匙(私钥),因为这两个是数学上相关的,但计算其中一个从另一个是几乎不可能的。 + +## 4.2 SSH密钥对口令验证 + +在客户端主机中生成**密钥对** + +```shell +[root@localhost ~]# ssh-keygen +# 为简化操作请一路向北 + +# 查看/root/.ssh目录中,是否存在id_rsa(私钥)和id_rsa.pub(公钥) +[root@localhost ~]# ls -al /root/.ssh/ +total 16 +drwx------. 2 root root 80 Dec 25 09:24 . +dr-xr-x---. 3 root root 159 Dec 24 15:58 .. +-rw-------. 1 root root 2610 Dec 25 09:24 id_rsa +-rw-r--r--. 1 root root 580 Dec 25 09:24 id_rsa.pub +-rw-------. 1 root root 837 Dec 24 15:43 known_hosts +-rw-r--r--. 1 root root 95 Dec 24 15:43 known_hosts.old +``` + +把客户端主机中生成的公钥文件传送至远程主机 + +```shell +# 使用 ssh-copy-id 这个工具,可以将公钥内容发送给目标主机 +[root@localhost ~]# ssh-copy-id 192.168.88.20 +# 为简化操作请一路向北,输入一次密码即可 + +# 检查服务端,是否成功接收到公钥:authorized_keys 文件中是否有相关记录 +[root@localhost ~]# cat .ssh/authorized_keys + +``` + +对远程主机进行设置,使其只允许密钥验证,拒绝传统的口令验证方式 + +```shell +# 编辑 /etc/ssh/sshd_config 文件,更改的配置项如下 +PasswordAuthentication no +PubkeyAuthentication yes +# 重启 sshd 服务 +[root@localhost ~]# systemctl restart sshd +``` + +客户端免密登录到远程主机 + +```shell +[root@localhost ~]# ssh root@192.168.88.20 +``` + +# 5. mobaxterm 生成密钥登录 + +通过mobaxterm我们也可以生成密钥对,从而通过密钥对进行登录 + +在工具选项中找到ssh密钥生成器 + +image-SSH密钥生成器01 + +选择生成的密钥对类型以及点击Generator生成 + +image-SSH密钥生成器02 + +生成密钥对并且保存公钥和私钥 + +生成的时候,要鼠标不断移动,该工具会根据鼠标移动的坐标,来生成随机的密钥 + +image-SSH远程管理协议03 + + + +服务端 `.ssh` 目录下新建文件`authorized_keys`,将上面生成的密钥粘进去 + +image-authorized_keys文件 + +如果连接失败,原因是由于我们通过 mobaxterm 生成的密钥文件权限不满足要求,并且在 windows 上修改权限的话非常麻烦。所以我们可以考虑通过在 cmd 命令行中使用 ssh-keygen 工具来生成密钥文件。 + +image-ssh-keygen命令 + +可以看到生成的公钥和私钥 + +image-公私钥文件 + +打开公钥文件,复制其中的内容,在 Linux 中的 /root.ssh/authorized_keys 文件中粘贴 + +然后继续通过 cmd 连接测试: + +image-公私钥文件 + +也可以尝试使用 Mobaxterm 工具,选择私钥进行连接。但是 Mobaxterm 连接的时候也可能会遇到报错的问题。这个是受不同工具的影响。如果通过 cmd 能够连接成功的话。就说明我们的密钥和配置是正常的。 \ No newline at end of file diff --git a/02.企业服务/01.SSH远程管理/SSH密钥生成器01.png b/02.企业服务/01.SSH远程管理/SSH密钥生成器01.png new file mode 100644 index 0000000..ca8c919 Binary files /dev/null and b/02.企业服务/01.SSH远程管理/SSH密钥生成器01.png differ diff --git a/02.企业服务/01.SSH远程管理/SSH密钥生成器02.png b/02.企业服务/01.SSH远程管理/SSH密钥生成器02.png new file mode 100644 index 0000000..b27e61d Binary files /dev/null and b/02.企业服务/01.SSH远程管理/SSH密钥生成器02.png differ diff --git a/02.企业服务/01.SSH远程管理/SSH密钥生成器03.png b/02.企业服务/01.SSH远程管理/SSH密钥生成器03.png new file mode 100644 index 0000000..5547228 Binary files /dev/null and b/02.企业服务/01.SSH远程管理/SSH密钥生成器03.png differ diff --git a/02.企业服务/01.SSH远程管理/authorized_keys文件.png b/02.企业服务/01.SSH远程管理/authorized_keys文件.png new file mode 100644 index 0000000..4f9964d Binary files /dev/null and b/02.企业服务/01.SSH远程管理/authorized_keys文件.png differ diff --git a/02.企业服务/01.SSH远程管理/ssh-keygen命令.png b/02.企业服务/01.SSH远程管理/ssh-keygen命令.png new file mode 100644 index 0000000..4d5eaba Binary files /dev/null and b/02.企业服务/01.SSH远程管理/ssh-keygen命令.png differ diff --git a/02.企业服务/01.SSH远程管理/公私钥文件.png b/02.企业服务/01.SSH远程管理/公私钥文件.png new file mode 100644 index 0000000..92e08c1 Binary files /dev/null and b/02.企业服务/01.SSH远程管理/公私钥文件.png differ diff --git a/02.企业服务/02.Samba文件共享.md b/02.企业服务/02.Samba文件共享.md new file mode 100644 index 0000000..efbd9e3 --- /dev/null +++ b/02.企业服务/02.Samba文件共享.md @@ -0,0 +1,468 @@ +# 1. Samba文件共享 + +## 1.1 什么是Samba + +Samba 是一个开源软件套件,允许 Linux/Unix 系统与 Windows 系统之间实现文件共享和打印服务。Samba 使用 SMB(Server Message Block)/CIFS(Common Internet File System)协议,这些协议是 Windows 系统共享资源的基础。 + +## 1.2 发展历程 + +**1992 年**:Samba 项目由 Andrew Tridgell 发起,最初作为一个简单工具来查看 DOS 文件共享。 + +**1994 年**:Samba 正式命名,支持 SMB 协议。 + +**1996 年**:开始支持 Windows NT 域。 + +**2003 年**:引入 LDAP 集成,支持 Active Directory。 + +**2012 年**:Samba 4 发布,完全实现了 Active Directory 的功能。 + +**现在**:Samba 成为企业级跨平台文件共享的核心工具之一。 + +## 1.3 Samba用途 + +**文件共享**:允许用户在不同操作系统之间共享文件。 + +**打印服务**:提供跨平台的打印服务和在线编辑。 + +**域控制器**:Samba 可以用作 Windows 网络的域控制器。 + +**认证与授权**:支持用户认证、访问控制和权限管理。 + +**跨平台互操作性**:让 Linux/Unix 系统与 Windows 系统无缝协作。 + +Windows计算机网络管理模式: + +* 工作组WORKGROUP:计算机对等关系,帐号信息各自管理 +* 域DOMAIN:C/S结构,帐号信息集中管理,DC,AD + +## 1.4 Samba相关软件包介绍 + +在 Rocky Linux 中,Samba 的核心组件包含以下软件包: + +- **samba**:Samba 的主包,包括核心服务和工具。 +- **samba-client**:提供客户端工具,用于访问远程的 SMB/CIFS 共享。 +- **samba-common**:共享的配置文件和库。 +- **samba-libs**:Samba 运行所需的库。 +- **samba-common-tools**:包含测试和管理工具,例如 `smbstatus`。 +- **smbclient**:命令行工具,用于访问 SMB/CIFS 共享。 +- **cifs-utils**:提供挂载 SMB 文件系统的工具(如 `mount.cifs`)。 + +## 1.5 相关服务进程 + +**smbd**:提供文件共享和打印服务,TCP:139、445。 + +**nmbd**:负责 NetBIOS 名称解析和浏览功能,UDP:137、138。 + +**winbindd**:用于与 Windows 域集成,支持用户和组的认证。 + +**samba-ad-dc**:Samba 4 中的域控制器服务。 + +## 1.6 Samba主配置文件 + +主配置文件:/etc/samba/smb.conf 帮助参看:man smb.conf + +语法检查: testparm [-v] [/etc/samba/smb.conf] + +```bash +[global] + workgroup = WORKGROUP # 工作组名称 + server string = Samba Server # 服务器描述 + security = user # 认证模式 + log file = /var/log/samba/log.%m # 日志文件路径 + max log size = 50 # 最大日志文件大小(KB) + dns proxy = no # 禁用 DNS 代理 + +[shared] + path = /srv/samba/shared # 共享路径 + browseable = yes # 是否可浏览 + writable = yes # 是否可写 + valid users = @smbgroup # 允许访问的用户/组 +``` + +### 1.6.1 全局设置([global]) + +- `workgroup`:指定工作组名称,默认是 `WORKGROUP`。 +- security: + - `user`:用户级认证(常用)。 + - `share`:共享级认证(不推荐)。 + - `domain`:域级认证。 + - `ads`:Active Directory 服务。 +- `log file`:日志文件路径。 +- `max log size`:限制日志文件大小。 + +### 1.6.2 共享设置([共享名]) + +- `path`:共享目录的路径。 +- `browseable`:决定共享是否可被浏览。 +- `writable`:是否允许写入。 +- `valid users`:指定允许访问的用户或组。 + +# 2. 快速开始 + +## 2.1 安装Samba服务 + +```bash +[root@localhost ~]# yum -y install samba +[root@localhost ~]# systemctl enable --now smb +[root@localhost ~]# systemctl enable --now nmb +[root@localhost ~]# ss -nlt +State Recv-Q Send-Q Local Address:Port Peer Address:Port Process +LISTEN 0 50 0.0.0.0:445 0.0.0.0:* +LISTEN 0 50 0.0.0.0:139 0.0.0.0:* +LISTEN 0 128 0.0.0.0:22 0.0.0.0:* +LISTEN 0 50 [::]:445 [::]:* +LISTEN 0 50 [::]:139 [::]:* +LISTEN 0 128 [::]:22 [::]:* +``` + +在`ss`命令的输出中,`Recv-Q`和`Send-Q`是与TCP连接相关的两个队列的大小。 + +- `Recv-Q`表示接收队列的大小。它指示了尚未被应用程序(进程)接收的来自网络的数据的数量。当接收队列的大小超过一定限制时,可能会发生数据丢失。 +- `Send-Q`表示发送队列的大小。它指示了应用程序(进程)等待发送到网络的数据的数量。当发送队列的大小超过一定限制时,可能会导致发送缓冲区已满,从而阻塞应用程序发送更多的数据。 + +## 2.2 配置Samba用户 + +* 包:samba-common-tools +* 工具:smbpasswd pdbedit +* 用户数据库:/var/lib/samba/private/passdb.tdb + +说明:samba用户须是Linux用户,建议使用/sbin/nologin + +一、创建系统用户 + +```bash +[root@localhost ~]# useradd -s /sbin/nologin smbuser +[root@localhost ~]# echo 123 | passwd --stdin smbuser +Changing password for user smbuser. +passwd: all authentication tokens updated successfully. +``` + +二、创建Samba用户 + +```bash +[root@localhost ~]# smbpasswd -a smbuser +New SMB password: +Retype new SMB password: +Added user smbuser. +[root@localhost ~]# smbpasswd -e smbuser # 启用用户 +Enabled user smbuser. +``` + +三、其他操作(视具体情况而使用) + +* 如果已经存在,想修改密码 + +```shell +[root@localhost ~]# smbpasswd smb1 +``` + +* 想要删除用户和密码 + +```shell +[root@localhost ~]# smbpasswd -x smb1 +[root@localhost ~]# pdbedit -x -u smb1 +``` + +* 查看samba用户列表 + +```shell +[root@localhost ~]# pdbedit -L -v +``` + +* 查看samba服务器状态 + +```shell +[root@localhost ~]# yum install -y samba +[root@localhost ~]# smbstatus +``` + +## 2.3 基于特定用户或组的共享 + +### 2.3.1 服务端操作 + +一、创建共享目录 + +共享目录为:`/data/samba` + +```bash +[root@localhost ~]# mkdir -p /data/samba +[root@localhost ~]# chown -R smbuser:smbuser /data/samba +[root@localhost ~]# chmod -R 2770 /data/samba +``` + +二、添加配置文件 + +```bash +[root@localhost ~]# vim /etc/samba/smb.conf +...... +[shared] + path = /data/samba + browseable = yes + writable = yes + valid users = @smbuser + create mask = 0660 + directory mask = 2770 +``` + +三、关闭防火墙与SELinux + +```bash +[root@localhost ~]# systemctl stop firewalld +[root@localhost ~]# setenforce 0 +``` + +四、重启smb服务 + +```bash +[root@localhost ~]# systemctl restart smb +[root@localhost ~]# systemctl restart nmb +``` + +### 2.3.2 客户端操作 + +#### 2.3.2.1 Windows连接 + +一、在运行窗口中输入:`\\192.168.88.10\`进行连接 + +image-20250111104202425 + +二、用户验证:smbuser/123 + +三、文件创建写入测试 + +image-20250111105213658 + +四、Samba服务端中查看 + +```bash +[root@localhost ~]# cat /data/samba/file.txt +The file is Created by windows... +``` + +#### 2.3.2.2 Linux连接 + +一、客户端工具下载 + +```bash +[root@localhost ~]# yum -y install samba-client +``` + +二、创建上传测试文件 + +```bash +[root@localhost ~]# echo "In server2..." > server2.txt +``` + +三、使用smbclient连接服务器测试 + +```bash +[root@localhost ~]# smbclient -L 192.168.88.10 -U smbuser +Password for [SAMBA\smbuser]: + + Sharename Type Comment + --------- ---- ------- + print$ Disk Printer Drivers + shared Disk + IPC$ IPC IPC Service (Samba 4.20.2) + smbuser Disk Home Directories +SMB1 disabled -- no workgroup available +[root@localhost ~]# smbclient //192.168.88.10/shared -U smbuser +Password for [SAMBA\smbuser]: +Try "help" to get a list of possible commands. +smb: \> ls + . D 0 Sat Jan 11 11:06:08 2025 + .. D 0 Sat Jan 11 11:06:08 2025 + file.txt A 33 Sat Jan 11 10:51:54 2025 + + 17756160 blocks of size 1024. 16032064 blocks available +smb: \> get file.txt # 下载文件 +getting file \file.txt of size 33 as file.txt (16.1 KiloBytes/sec) (average 16.1 KiloBytes/sec) +smb: \> put /server2.txt # 上传文件 +putting file /server2.txt as \server2.txt (4.6 kb/s) (average 4.6 kb/s) +``` + +### 2.3.3 挂载CIFS文件系统 + +手动挂载: + +```bash +[root@localhost ~]# yum install -y cifs-utils +[root@localhost ~]# mkdir /mnt/smb +[root@localhost ~]# mount -t cifs //192.168.88.10/shared /mnt/smb -o username=smbuser,password=123 +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 5.2M 344M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +tmpfs 175M 0 175M 0% /run/user/0 +//192.168.88.10/shared 17G 1.7G 16G 10% /mnt/smb +[root@localhost ~]# cd /mnt/smb/ +[root@localhost smb]# ls +file.txt server2.txt +``` + +开机自动挂载: + +```bash +[root@localhost ~]# vim /etc/fstab +//192.168.88.10/shared /mnt/smb cifs defaults,username=smbuser,password=123 0 0 +[root@localhost ~]# systemctl daemon-reload +[root@localhost ~]# mount -a +[root@localhost ~]# df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs 872M 0 872M 0% /dev/shm +tmpfs 349M 5.2M 344M 2% /run +/dev/mapper/rl-root 17G 1.7G 16G 10% / +/dev/nvme0n1p1 960M 261M 700M 28% /boot +//192.168.88.10/shared 17G 1.7G 16G 10% /mnt/smb +tmpfs 175M 0 175M 0% /run/user/0 +``` + +# 3. 实战:不同账户访问不同目录 + +## 3.1 服务端 + +一、创建并启用用户 + +创建三个samba用户,分别为smb1、smb2、smb3。密码均为:123 + +```bash +[root@localhost ~]# useradd -s /sbin/nologin -r smb1 # -r 不创建家目录 +[root@localhost ~]# useradd -s /sbin/nologin -r smb2 +[root@localhost ~]# useradd -s /sbin/nologin -r smb3 +[root@localhost ~]# +[root@localhost ~]# smbpasswd -a smb1 +New SMB password: +Retype new SMB password: +Added user smb1. +[root@localhost ~]# smbpasswd -a smb2 +New SMB password: +Retype new SMB password: +Added user smb2. +[root@localhost ~]# smbpasswd -a smb3 +New SMB password: +Retype new SMB password: +Added user smb3. +[root@localhost ~]# smbpasswd -e smb1 +Enabled user smb1. +[root@localhost ~]# smbpasswd -e smb2 +Enabled user smb2. +[root@localhost ~]# smbpasswd -e smb3 +Enabled user smb3. + +# 查看smb用户 +[root@localhost ~]# pdbedit -L +smbuser:1000: +smb2:986: +smb1:987: +smb3:985: +``` + +二、修改Samba配置文件如下 + +```bash +[root@localhost ~]# vim /etc/samba/smb.conf +# 在global中添加该字段 +[global] +config file = /etc/samba/conf.d/%U # 变量%U表示匹配用户名 + +# 新建共享配置 +[share] + path = /data/samba/share + browseable = yes + writable = yes + Guest ok = yes + create mask = 0660 + directory mask = 2770 +``` + +三、配置共享目录和文件 + +```bash +[root@localhost ~]# mkdir -p /data/samba/share +[root@localhost ~]# mkdir -p /data/samba/smb1 +[root@localhost ~]# mkdir -p /data/samba/smb2 +[root@localhost ~]# touch /data/samba/share/share.txt # 共享目录及文件 +[root@localhost ~]# touch /data/samba/smb1/smb1.txt # smb1目录及文件 +[root@localhost ~]# touch /data/samba/smb2/smb2.txt # smb2目录及文件 +[root@localhost ~]# tree /data/samba/ +/data/samba/ +├── file.txt +├── server2.txt +├── share +│   └── share.txt +├── smb1 +│   └── smb1.txt +└── smb2 + └── smb2.txt + +# 将/data/samba目录权限放开 +[root@localhost ~]# chmod 777 -R /data/samba +``` + +四、针对smb1用户和smb2用户单独编辑配置文件 + +```bash +[root@localhost ~]# vim /etc/samba/conf.d/smb1 +[share] + path = /data/samba/smb1 + writable = yes + create mask = 0660 + browseable = yes +[root@localhost ~]# vim /etc/samba/conf.d/smb2 +[share] + path = /data/samba/smb2 + writable = yes + create mask = 0660 + browseable = yes +``` + +五、重启相关服务 + +```bash +[root@localhost ~]# systemctl restart smb +[root@localhost ~]# systemctl restart nmb +``` + +## 3.2 客户端 + +客户端访问测试 + +```bash +[root@localhost ~]# smbclient //192.168.88.10/share -U smb1 +Password for [SAMBA\smb1]: +Try "help" to get a list of possible commands. +smb: \> ls + . D 0 Sat Jan 11 15:00:58 2025 + .. D 0 Sat Jan 11 15:00:58 2025 + smb1.txt N 0 Sat Jan 11 15:00:58 2025 + + 17756160 blocks of size 1024. 16030864 blocks available +smb: \> exit +[root@localhost ~]# smbclient //192.168.88.10/share -U smb2 +Password for [SAMBA\smb2]: +Try "help" to get a list of possible commands. +smb: \> ls + . D 0 Sat Jan 11 15:00:03 2025 + .. D 0 Sat Jan 11 15:00:03 2025 + smb2.txt N 0 Sat Jan 11 15:00:03 2025 + + 17756160 blocks of size 1024. 16030864 blocks available +smb: \> exit +[root@localhost ~]# smbclient //192.168.88.10/share -U smb3 +Password for [SAMBA\smb3]: +Try "help" to get a list of possible commands. +smb: \> ls + . D 0 Sat Jan 11 14:59:44 2025 + .. D 0 Sat Jan 11 14:59:44 2025 + share.txt N 0 Sat Jan 11 14:59:44 2025 + + 17756160 blocks of size 1024. 16030884 blocks available +``` + +## 3.3 **结论** + +由此可以看出,我们通过针对不同用户编写子配置文件的方式来覆盖主配置文件中相同的共享。可以实现对于没有子配置的用户,访问主配置文件中的定义的目录。对于具备子配置的用户,访问子配置所定义的目录。实现控制不用用户登录访问不同目录。 \ No newline at end of file diff --git a/02.企业服务/02.Samba文件共享/image-20250111104202425.png b/02.企业服务/02.Samba文件共享/image-20250111104202425.png new file mode 100644 index 0000000..2b0ce25 Binary files /dev/null and b/02.企业服务/02.Samba文件共享/image-20250111104202425.png differ diff --git a/02.企业服务/02.Samba文件共享/image-20250111105213658.png b/02.企业服务/02.Samba文件共享/image-20250111105213658.png new file mode 100644 index 0000000..c33755e Binary files /dev/null and b/02.企业服务/02.Samba文件共享/image-20250111105213658.png differ diff --git a/02.企业服务/03.HTTP协议.md b/02.企业服务/03.HTTP协议.md new file mode 100644 index 0000000..b64202a --- /dev/null +++ b/02.企业服务/03.HTTP协议.md @@ -0,0 +1,180 @@ +# 1. HTTP 协议介绍 + +HTTP(HyperText Transfer Protocol,超文本传输协议)是用于在客户端(通常是浏览器)和服务器之间传输超文本(如HTML)的应用层协议。它是无状态的、面向请求-响应的协议,基于TCP/IP传输。 + +HTTP有不同版本号,不同版本号区别如下 + +- HTTP0.9:仅支持 GET 方法,仅能访问 HTML 格式的资源 +- HTTP1.0:增加 POST 和 HEAD 方法,MIME 支持多种数据格式,开始支持 Cache,支持 tcp 短连接 +- HTTP1.1:支持持久连接(长连接),一个 TCP 连接允许多个请求,新增 PUT、PATCH、DELETE 等 +- HTTP2.0:性能大幅提升,新的二进制格式,多路复用,header 压缩,服务端推送 + +# 2. HTTP 工作原理 + +HTTP的工作过程可以总结为以下几个步骤: + +1. **建立连接:** 客户端(如浏览器)与服务器之间通过TCP三次握手建立连接。 +2. **发送请求:** 客户端向服务器(Apache、Nginx、IIS服务器)发送HTTP请求报文,请求资源或操作。 +3. **服务器处理请求:** HTTP服务器接收到请求后,处理请求并生成响应。 +4. **返回响应:** 服务器将响应报文返回给客户端。 +5. **断开连接:** 通常在响应完成后关闭TCP连接(HTTP/1.0默认短连接,HTTP/1.1支持长连接)。 + +# 3. URL 与 URI + +## 3.1 URI(Uniform Resource Identifier) + +URI 是统一资源标识符,用于标识互联网上的资源。URI 分为两种 + +- **URL(Uniform Resource Locator):** 统一资源定位符,用于描述资源的地址。 + +- **URN(Uniform Resource Name):** 统一资源名称,用于标识资源的名称,不依赖物理位置 + +## 3.2 URL(Uniform Resource Locator + +HTTP 使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。URL 是一种特殊类型的URI,包含了用于查找某个资源的足够的信息 + +URL 全称是 UniformResourceLocator, 中文叫统一资源定位符,是互联网上用来标识某一处资源的地址。以下面这个URL为例,介绍下普通URL的各部分组成: + +```shell +http://iproute.cn:80/news/search?keyword=123&enc=utf8#name=321 +``` + +从上面的URL可以看出,一个完整的URL包括以下几部分: + +1. **协议部分:** 该URL的协议部分为 `http:`,这代表网页使用的是HTTP 协议。在 Internet 中可以使用多种协议,如 HTTP,FTP 等等本例中使用的是 HTTP 协议。在 `http:` 后面的 `//` 为分隔符 +2. **域名(主机)部分:** 该 URL 的域名部分为 `iproute.cn`。一个URL 中,也可以使用 IP 地址作为域名使用 +3. **端口部分:** 跟在域名后面的是端口,域名和端口之间使用 `:` 作为分隔符。端口不是一个 URL 必须的部分,如果省略端口部分,将采用默认端口 +4. **虚拟目录部分:** 从域名后的第一个 `/` 开始到最后一个 `/` 为止,是虚拟目录部分。虚拟目录也不是一个 URL 必须的部分。本例中的虚拟目录是 `/news/` +5. **文件名部分:** 从域名后的最后一个 `/` 开始到 `?` 为止,是文件名部分,如果没有 `?` ,则是从域名后的最后一个 `/` 开始到 `#` 为止,是文件部分,如果没有 `?` 和 `#`,那么从域名后的最后一个 `/` 开始到结束,都是文件名部分。本例中的文件名是 `search` 。文件名部分也不是一个 URL 必须的部分,如果省略该部分,则使用默认的文件名 +6. **参数部分:** 从 `?` 开始到 `#` 为止之间的部分为参数部分,又称搜索部分、查询部分。本例中的参数部分为 `keyword=123&enc=utf8`。参数可以允许有多个参数,参数与参数之间用 `&` 作为分隔符 +7. **锚部分:** 从 `#` 开始到最后,都是锚部分。本例中的锚部分是`name`。锚部分也不是一个 URL 必须的部分 + +# 4. HTTP 注意事项 + +1. HTTP 是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。 +2. HTTP 是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过 HTTP 发送。客户端以及服务器指定使用适合的 MIME-type 内容类型。 +3. HTTP 是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。 + +# 5. HTTP 消息结构 + +- HTTP 是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议。 +- HTTP 客户端是一个应用程序(Web 浏览器或其他任何客户端),通过连接到服务器达到向服务器发送一个或多个 HTTP 的请求的目的。 +- HTTP 服务端是一个应用程序(通常是一个 Web 服务,如 Apache Web服务器或IIS服务器等),通过接收客户端的请求并向客户端发送 HTTP 响应数据。 +- HTTP 使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。 +- HTTP 响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。 + +![img-CS架构](03.HTTP协议/CS架构.png) + +## 5.1 HTTP 请求报文 + +客户端发送一个 HTTP 请求到服务器的请求消息包括以下格式: + +![img](03.HTTP协议/请求报文格式.png) + +![img](03.HTTP协议/请求报文示例.png) + +## 5.2 POST 和 GET 请求方法区别 + +- 提交的过程 + - GET 提交,请求的数据会附在URL之后(就是把数据放置在 HTTP 协议头中),以?分割URL和传输数据,多个参数用&连接 + - POST 提交:把提交的数据放置在是HTTP包的包体中 +- 传输数据的大小 + - 首先声明:HTTP协议没有对传输的数据大小进行限制,HTTP 协议规范也没有对URL长度进行限制 + - GET 提交,特定浏览器和服务器对URL长度有限制 + - POST 提交,由于不是通过URL传值,理论上数据不受限 +- 安全性 + - POST 的安全性要比 GET 的安全性高 + - 登录页面有可能被浏览器缓存,而缓存的是 URL + - 其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码 + - 使用 GET 提交数据还可能会造成 Cross-site request forgery 攻击 + + +## 5.3 常见请求报文头部属性 + +- Accpet + - 告诉服务端,客户端接收什么类型的响应 +- Referer + - 表示这是请求是从哪个URL进来的,比如想在网上购物,但是不知道选择哪家电商平台,你就去问百度,说哪家电商的东西便宜啊,然后一堆东西弹出在你面前,第一给就是某宝,当你从这里进入某宝的时候,这个请求报文的Referer 就是 `http://www.baidu.com` +- Cache-Control + - 对缓存进行控制,如一个请求希望响应的内容在客户端缓存一年,或不被缓可以通过这个报文头设置 +- Accept-Encoding + - 这个属性是用来告诉服务器能接受什么编码格式,包括字符编码,压缩格式 +- Host + - 指定要请求的资源所在的主机和端口 +- User-Agent + - 告诉服务器,客户端使用的操作系统、浏览器版本和名称 + + +## 5.4 HTTP 响应报文 + +服务的相应信息格式如下: + +![img-响应报文格式](03.HTTP协议/响应报文格式.png) + +img-响应报文示例 + +## 5.5 常见响应报文头部属性 + +- Cache-Control + - 响应输出到客户端后,服务端通过该属性告诉客户端该怎么控制响应内容的缓存 +- ETag + - 表示客户端请求资源的版本,如果该资源发生了变化,那么这个属性也会跟着变 +- Location + - 在重定向中或者创建新资源时使用 +- Set-Cookie + - 服务端可以设置客户端的 cookie + +# 6. HTTP 状态码 + +- 状态代码有三位数字组成,第一个数字定义了响应的类别,共分五种类别: + - 1xx:指示信息--表示请求已接收,继续处理 + - 2xx:成功--表示请求已被成功接收、理解、接受 + - 3xx:重定向--要完成请求必须进行更进一步的操作 + - 4xx:客户端错误--请求有语法错误或请求无法实现 + - 5xx:服务器端错误--服务器未能实现合法的请求 +- 常见状态码 + +```xml +200 OK //客户端请求成功 +400 Bad Request //客户端请求有语法错误,不能被服务器所理解 +401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 +403 Forbidden //服务器收到请求,但是拒绝提供服务 +404 Not Found //请求资源不存在,eg:输入了错误的URL +500 Internal Server Error //服务器发生不可预期的错误 +503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常 +``` + + +# 7. HTTP 请求方法 + +根据 HTTP 标准,HTTP 请求可以使用多种请求方法。 + +HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法。 + +HTTP1.1 新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。 + +- GET:请求指定的页面信息,并返回实体主体。 +- HEAD:类似于 get 请求,只不过返回的响应中没有具体的内容,用于获取报头 +- POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 +- PUT:从客户端向服务器传送的数据取代指定的文档的内容。 +- DELETE:请求服务器删除指定的页面。 +- CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。 +- OPTIONS:允许客户端查看服务器的性能。 +- TRACE:回显服务器收到的请求,主要用于测试或诊断。 + + +# 8. 长连接与短连接 + +HTTP1.1 规定了默认保持长连接(HTTP persistent connection ,也有翻译为持久连接),数据传输完成了保持TCP连接不断开(不发RST包、不四次握手),等待在同域名下继续用这个通道传输数据;相反的就是短连接。 + +在实际使用中,HTTP 头部有了 Keep-Alive 这个值并不代表一定会使用长连接,客户端和服务器端都可以无视这个值,也就是不按标准来。毕竟 TCP 是一个双向连接的协议,双方都可以决定是不是主动断开。 + +客户端的长连接不可能无限期的拿着,会有一个超时时间,服务器有时候会告诉客户端超时时间。下图中的 Keep-Alive: timeout=20,表示这个TCP通道可以保持20秒 + +![img-Keep-Alive报文](03.HTTP协议/Keep-Alive报文.png) + +# 9. 课后作业 + +- [扩展]了解更多请求/响应报文头属性 +- [扩展]了解更多响应状态码 + diff --git a/02.企业服务/03.HTTP协议/CS架构.png b/02.企业服务/03.HTTP协议/CS架构.png new file mode 100644 index 0000000..96be4c0 Binary files /dev/null and b/02.企业服务/03.HTTP协议/CS架构.png differ diff --git a/02.企业服务/03.HTTP协议/Keep-Alive报文.png b/02.企业服务/03.HTTP协议/Keep-Alive报文.png new file mode 100644 index 0000000..8872372 Binary files /dev/null and b/02.企业服务/03.HTTP协议/Keep-Alive报文.png differ diff --git a/02.企业服务/03.HTTP协议/响应报文格式.png b/02.企业服务/03.HTTP协议/响应报文格式.png new file mode 100644 index 0000000..077803e Binary files /dev/null and b/02.企业服务/03.HTTP协议/响应报文格式.png differ diff --git a/02.企业服务/03.HTTP协议/响应报文示例.png b/02.企业服务/03.HTTP协议/响应报文示例.png new file mode 100644 index 0000000..1c67422 Binary files /dev/null and b/02.企业服务/03.HTTP协议/响应报文示例.png differ diff --git a/02.企业服务/03.HTTP协议/请求报文格式.png b/02.企业服务/03.HTTP协议/请求报文格式.png new file mode 100644 index 0000000..204aa70 Binary files /dev/null and b/02.企业服务/03.HTTP协议/请求报文格式.png differ diff --git a/02.企业服务/03.HTTP协议/请求报文示例.png b/02.企业服务/03.HTTP协议/请求报文示例.png new file mode 100644 index 0000000..c867a24 Binary files /dev/null and b/02.企业服务/03.HTTP协议/请求报文示例.png differ diff --git a/02.企业服务/04.Apache.md b/02.企业服务/04.Apache.md new file mode 100644 index 0000000..4d8a17d --- /dev/null +++ b/02.企业服务/04.Apache.md @@ -0,0 +1,1592 @@ +# 1. 常用的网站服务软件 + +- 曾经Apache是第一,虽然现在不是,但是在行业中依旧非常的常见 +- [https://w3techs.com/technologies/overview/web_server](https://w3techs.com/technologies/overview/web_server) + +![img-web_server_top](04.Apache/web_server_top.png) + +**Apache是什么** + +Apache HTTP Server简称为 Apache,是 Apache 软件基金会的一个高性能、功能强大、见状可靠、又灵活的开放源代码的web服务软件,它可以运行在广泛的计算机平台上如 Linux、Windows。因其平台型和很好的安全性而被广泛使用,是互联网最流行的 web 服务软件之一。 + +**特点** + +- 功能强大 +- 高度模块化 +- 采用MPM多路处理模块 +- 配置简单 +- 速度快 +- 应用广泛 +- 性能稳定可靠 +- 可做代理服务器或负载均衡来使用 +- 双向认证 +- 支持第三方模块 + +**应用场景** + +- 使用Apache运行静态HTML网页、图片 +- 使用Apache结合PHP、Linux、MySQL可以组成LAMP经典架构 +- 使用Apache作代理、负载均衡等 + +**MPM工作模式** + +- prefork:多进程I/O模型,一个主进程,管理多个子进程,一个子进程处理一个请求。 +- worker:复用的多进程I/O模型,多进程多线程,一个主进程,管理多个子进程,一个子进程管理多个线程,每个线程处理一个请求。 +- event:事件驱动模型,一个主进程,管理多个子进程,一个进程处理多个请求。 + +# 2. Apache 基础 + +## 2.1 安装部署 + +RockyLinux 软件仓库中存在此软件包,可以直接通过 yum 进行安装 + +```shell +[root@localhost ~]# yum -y install httpd +[root@localhost ~]# systemctl start httpd && systemctl enable httpd +[root@localhost ~]# httpd -v +Server version: Apache/2.4.62 (Rocky Linux) +Server built: Jan 29 2025 00:00:00 + +# 使用 curl 或者浏览器测试网页是否正常 +[root@localhost ~]# echo "Hello World!" > /var/www/html/index.html +[root@localhost ~]# curl -I 127.0.0.1 +HTTP/1.1 200 OK +``` + +## 2.2 httpd 命令 + +httpd 为 Apache HTTP 服务软件的命令 + +```shell +[root@localhost ~]# httpd -h +Usage: httpd [-D name] [-d Directory] [-f file] + [-C "directive"] [-c "directive"] + [-k start|restart|graceful|graceful-stop|stop] + [-v] [-V] [-h] [-l] [-L] [-t] [-T] [-S] [-X] +``` + +**常用选项** + +| 选项 | 说明 | +| :---------------- | :----------------------------------- | +| -c | 在读取配置文件前,先执行选项中的指令 | +| -C | 在读取配置文件后,再执行选项中的指令 | +| -d <服务器根目录> | 指定服务器的根目录 | +| -D <设定文件参数> | 指定要传入配置文件的参数 | +| -f <配置文件> | 指定配置文件 | +| -h | 显示帮助 | +| -l | 显示服务器编译时所包含的模块 | +| -L | 显示httpd指令的说明 | +| -S | 显示配置文件中的设定 | +| -t | 测试配置文件的语法是否正确 | +| -v | 显示版本信息 | +| -V | 显示办吧信息和运行环境 | +| -X | 以单一程序的方式来启动服务器 | + +## 2.3 相关文件 + +| 文件 | 说明 | +| :------------------------- | :------------------- | +| /etc/httpd/conf/httpd.conf | 主配置文件 | +| /etc/httpd/conf.d/ | 存放虚拟主机配置文件 | +| /etc/httpd/conf.modules.d/ | 存放模块配置文件 | +| /etc/httpd/modules/ | 存放模块文件 | +| /var/log/httpd/ | 存放日志文件 | +| /var/www/html/ | 存放默认的网页文件 | + +## 2.4 主配置文件 + +**主配置文件说明** + +```shell +[root@localhost ~]# grep -Ev '^$|^#|\s*#' /etc/httpd/conf/httpd.conf +ServerRoot "/etc/httpd" +Listen 80 +Include conf.modules.d/*.conf +User apache +Group apache +ServerAdmin root@localhost + + AllowOverride none + Require all denied + +Documentroot "/var/www/html" + + AllowOverride None + Require all granted + + + Options Indexes FollowSymLinks + AllowOverride None + Require all granted + + + DirectoryIndex index.html + + + Require all denied + +ErrorLog "logs/error_log" +LogLevel warn + + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined + LogFormat "%h %l %u %t \"%r\" %>s %b" common + + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio + + CustomLog "logs/access_log" combined + + + ScriptAlias /cgi-bin/ "/var/www/cgi-bin/" + + + AllowOverride None + Options None + Require all granted + + + TypesConfig /etc/mime.types + AddType application/x-compress .Z + AddType application/x-gzip .gz .tgz + AddType text/html .shtml + AddOutputFilter INCLUDES .shtml + +AddDefaultCharset UTF-8 + + MIMEMagicFile conf/magic + +EnableSendfile on +IncludeOptional conf.d/*.conf +``` + +**主配置项** + +| 主配置项 | 说明 | +| :---------------- | :--------------- | +| ServerRoot | 配置文件所在目录 | +| Listen | 监听端口 | +| ServerName | 服务器主机名 | +| Include | 包含其他配置文件 | +| User | 运行用户 | +| Group | 运行用户组 | +| ServerAdmin | 管理员邮箱 | +| Documentroot | 默认网站根目录 | +| Directory | 虚拟主机配置 | +| IfModule | 模块配置 | +| ErrorLog | 错误日志 | +| LogLevel | 日志级别 | +| CustomLog | 自定义日志 | +| StartServers | 初始子进程数 | +| EnableSendfile | 开启sendfile | +| AddDefaultCharset | 默认字符集 | + +## 2.5 虚拟主机配置文件 + +```shell +[root@localhost ~]# ls -l /etc/httpd/conf.d/ +total 16 +-rw-r--r--. 1 root root 400 Apr 29 03:43 README +-rw-r--r--. 1 root root 2916 Apr 29 03:43 autoindex.conf +-rw-r--r--. 1 root root 1252 Apr 29 03:41 userdir.conf +-rw-r--r--. 1 root root 653 Apr 29 03:41 welcome.conf + +``` + +## 2.6 模块配置文件 + +- httpd 有静态功能模块和动态功能模块组成,分别使用 httpd -l 和 httpd -M 查看 +- Dynamic Shared Object 加载动态模块配置,不需重启即生效 +- 动态模块所在路径:`/usr/lib64/httpd/modules/` +- 模块功能介绍:[http://httpd.apache.org/docs/2.4/zh-cn/mod/](http://httpd.apache.org/docs/2.4/zh-cn/mod/) + + +```shell +# 模块配置相关说明 +[root@localhost ~]# grep -A 11 'Dynamic' /etc/httpd/conf/httpd.conf +# Dynamic Shared Object (DSO) Support +# +# To be able to use the functionality of a module which was built as a DSO you +# have to place corresponding `LoadModule' lines at this location so the +# directives contained in it are actually available _before_ they are used. +# Statically compiled modules (those listed by `httpd -l') do not need +# to be loaded here. +# +# Example: +# LoadModule foo_module modules/mod_foo.so +# +Include conf.modules.d/*.conf + +# 动态模块存储位置 +[root@localhost ~]# ls -l /etc/httpd/modules +lrwxrwxrwx. 1 root root 29 Apr 29 03:43 /etc/httpd/modules -> ../../usr/lib64/httpd/modules +[root@localhost ~]# ls -l /etc/httpd/modules/ +total 3508 +-rwxr-xr-x. 1 root root 15488 Apr 29 03:43 mod_access_compat.so +...... + +# 动态模块加载相关配置 +[root@localhost ~]# ls -l /etc/httpd/conf.modules.d/ +total 48 +-rw-r--r--. 1 root root 3325 Apr 29 03:41 00-base.conf +...... + +[root@localhost ~]# grep LoadModule /etc/httpd/conf.modules.d/00-base.conf | head -n 1 +LoadModule access_compat_module modules/mod_access_compat.so + +``` + +## 2.7 案例分析1 + +修改默认网址目录为:`/data/www/html` + +## 2.8 案例分析2 + +修改 ServerName 为:`test.mysite.com`;修改 Listen 为:`8090` + + +# 3. 持久连接 + +Persistent Connection:连接建立,每个资源获取完成后不会断开连接,而是继续等待其它的请求完成,默认是开启持久连接。当开启持久连接后的断开条件是以超时时间为限制,默认为5s。 + +- 优势:可以显著提高包含许多图像的 HTML 文档的延迟时间,几乎加快了 50% +- 缺点:对并发访问量大的服务器,持久连接会使部分请求得不到响应而终止 + +## 3.1 案例分析 + +开启/关闭 Keepalive 进行测试验证 + +- 观察客户端TCP连接是否复用? +- [扩展] 使用压测工具进行测试观察响应延时? + +```shell +# 默认开启进行测试验证 +[root@localhost ~]# curl -v http://127.0.0.1 http://127.0.0.1 http://127.0.0.1 +[root@localhost ~]# ss -tan | grep 80 +TIME-WAIT 0 0 127.0.0.1:39486 127.0.0.1:80 + +# 关闭后进行测试验证 +[root@localhost ~]# echo "Keepalive Off" > /etc/httpd/conf.d/kp.conf +[root@localhost ~]# ss -tan | grep 80 +TIME-WAIT 0 0 [::ffff:127.0.0.1]:80 [::ffff:127.0.0.1]:33352 +TIME-WAIT 0 0 [::ffff:127.0.0.1]:80 [::ffff:127.0.0.1]:33342 +TIME-WAIT 0 0 [::ffff:127.0.0.1]:80 [::ffff:127.0.0.1]:33330 + +``` + +# 4. 虚拟主机 + +Httpd 支持在一台物理主机上实现多个网站,即多虚拟主机 + +- 网站的唯一标识,多虚拟主机有三种实现方案 + - IP 相同 + Port 不同 + - IP 不同 + Port 相同 + - FQDN 不同 + +## 4.1 案例分析1 + +基于 Port 虚拟主机 + +```shell +# 准备根目录和主页文件 +[root@localhost ~]# mkdir -pv /data/website{1,2,3} +[root@localhost ~]# echo "This is NO.1 website!" > /data/website1/index.html +[root@localhost ~]# echo "This is NO.2 website!" > /data/website2/index.html +[root@localhost ~]# echo "This is NO.3 website!" > /data/website3/index.html +# 基于 Port 虚拟主机配置文件 +[root@localhost ~]# cat << EOF > /etc/httpd/conf.d/site.conf +listen 8001 +listen 8002 +listen 8003 + +# vhost01 + + Documentroot /data/website1/ + + Require all granted + + + +# vhost02 + + Documentroot /data/website2/ + + Require all granted + + + +# vhost03 + + Documentroot /data/website3/ + + Require all granted + + +EOF + +# 重启服务并进行测试验证 +[root@localhost ~]# systemctl restart httpd +[root@localhost ~]# curl 192.168.88.10:8001 +This is NO.1 website! +[root@localhost ~]# curl 192.168.88.10:8002 +This is NO.2 website! +[root@localhost ~]# curl 192.168.88.10:8003 +This is NO.3 website! + +``` + +## 4.2 案例分析2 + +基于 IP 虚拟主机 + +```shell +# 配置临时IP +[root@localhost ~]# ip addr add 172.16.175.111/24 dev ens33 label ens33:1 +[root@localhost ~]# ip addr add 172.16.175.112/24 dev ens33 label ens33:2 +[root@localhost ~]# ip addr add 172.16.175.113/24 dev ens33 label ens33:3 +# 基于 IP 虚拟主机配置文件 +[root@localhost ~]# cat << EOF > /etc/httpd/conf.d/site.conf +# vhost01 + + Documentroot /data/website1/ + + Require all granted + + + +# vhost02 + + Documentroot /data/website2/ + + Require all granted + + + +# vhost03 + + Documentroot /data/website3/ + + Require all granted + + +EOF +# 重启服务并进行测试验证 +[root@localhost ~]# curl 172.16.175.111 +This is NO.1 website! +[root@localhost ~]# curl 172.16.175.112 +This is NO.2 website! +[root@localhost ~]# curl 172.16.175.113 +This is NO.3 website! + +``` + +## 4.3 案例分析3 + +基于 FQDN 虚拟主机 + +```shell +# 基于 FQDN 虚拟主机 +[root@localhost ~]# cat << EOF > /etc/httpd/conf.d/site.conf +# vhost01 + + ServerName website1.eagleslab.org + Documentroot /data/website1/ + + Require all granted + + + +# vhost02 + + ServerName website2.eagleslab.org + Documentroot /data/website2/ + + Require all granted + + + +# vhost03 + + ServerName website3.eagleslab.org + Documentroot /data/website3/ + + Require all granted + + +EOF + +# 本地添加解析 +[root@localhost ~]# cat <> /etc/hosts +172.16.175.129 website1.eagleslab.org +172.16.175.129 website2.eagleslab.org +172.16.175.129 website3.eagleslab.org +EOF + +# 重启服务并进行测试验证 +[root@localhost ~]# curl website1.eagleslab.org +This is NO.1 website! +[root@localhost ~]# curl website2.eagleslab.org +This is NO.2 website! +[root@localhost ~]# curl website3.eagleslab.org +This is NO.3 website! + +``` + +# 5. 服务日志 + +httpd 有两种常见日志类型:访问日志和错误日志,错误日志使用标准 syslog 级别,按严重性递增排序。 + +https://httpd.apache.org/docs/current/mod/core.html#loglevel + +## 5.1 日志配置 + +主配置文件日志配置相关定义块 + +```shell +[root@localhost ~]# grep -A27 '' /etc/httpd/conf/httpd.conf +# ErrorLog: The location of the error log file. +# If you do not specify an ErrorLog directive within a +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# container, that host's errors will be logged there and not here. +# +ErrorLog "logs/error_log" + +# +# LogLevel: Control the number of messages logged to the error_log. +# Possible values include: debug, info, notice, warn, error, crit, +# alert, emerg. +# +LogLevel warn + + + # + # The following directives define some format nicknames for use with + # a CustomLog directive (see below). + # + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined + LogFormat "%h %l %u %t \"%r\" %>s %b" common + + + # You need to enable mod_logio.c to use %I and %O + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio + + + # + # The location and format of the access logfile (Common Logfile Format). + # If you do not define any access logfiles within a + # container, they will be logged here. Contrariwise, if you *do* + # define per- access logfiles, transactions will be + # logged therein and *not* in this file. + # + #CustomLog "logs/access_log" common + + # + # If you prefer a logfile with access, agent, and referer information + # (Combined Logfile Format) you can use the following directive. + # + CustomLog "logs/access_log" combined + + +``` + +ErrorLog:目标文件名 +LogLevel:调整记录在错误日志中的消息的详细程度 +ErrorLogFormat:错误日志条目的格式规范 +LogFormat:设定自定义格式 +CustomLog:定义日志文件和格式 + +**常见自定义格式的变量参考** + +https://httpd.apache.org/docs/current/mod/mod_log_config.html#logformat + +- %h 客户端IP地址 +- %l 远程用户,启用mod_ident才有效,通常为减号“-” +- %u 验证(basic,digest)远程用户,非登录访问时,为一个减号“-” +- %t 服务器收到请求时的时间 +- %r First line of request,即表示请求报文的首行;记录了此次请求的“方法”,“URL”以及协议版本 +- %>s 对于已在内部重定向的请求,这是原始请求的状态。使用%>s 的最终状态。类型脚本中的exit 数字 +- %b 响应报文的大小,单位是字节;不包括响应报文http首部 +- %{Referer}i 请求报文中首部“referer”的值;即从哪个页面中的超链接跳转至当前页面。 { }里面内容就是报文中的一个键值对 +- %{User-Agent}i 请求报文中首部“User-Agent”的值;即发出请求的应用程序,多数为浏览器型号 + + +## 5.2 案例分析 + +新增自定义 LogFormat 并在 CustomLog 中引用 + + +# 6. URI 匹配规则 + +Apache httpd的URL匹配规则主要包括以下几种指令: + +1. ``: 基于URL路径进行匹配。 +2. ``: 使用正则表达式进行URL路径匹配。 +3. ``: 基于服务器文件系统的目录路径进行匹配。 +4. ``: 使用正则表达式进行目录路径匹配。 +5. ``: 基于文件名进行匹配。 +6. ``: 使用正则表达式进行文件名匹配。 + +**优先级:** + +这些指令的优先级从高到低为: + +1. `` 和 `` +2. `` 和 `` +3. `` 和 `` + +**指令常用选项:** + +1. `` 指令: + - `path`: 指定需要匹配的 URL 路径。可以使用通配符 + - `Require`: 指定需要通过身份验证才能访问的用户或组 +2. `` 指令: + - `regex`: 指定一个正则表达式来匹配 URL。 + - 其他选项同 `` 指令。 +3. `` 指令: + - `path`: 指定需要匹配的目录路径。可以使用通配符。 + - `Options`: 设置目录的访问选项,如 `FollowSymLinks`、`Indexes` 等。 + - `AllowOverride`: 控制 .htaccess 文件的覆盖范围。 + - 其他选项同 `` 指令。 +4. `` 指令: + - `filename`: 指定需要匹配的文件名。可以使用通配符。 + - 其他选项同 `` 指令。 +5. `` 指令: + - `regex`: 指定一个正则表达式来匹配文件名。 + - 其他选项同 `` 指令 + +## 6.1 配置方法 + +```shell +# 基于目录 + +... + +# 基于文件 + +... + +# 基于文件通配符 + +... + +# 基于正则表达式 + +... + +``` + +## 6.2 案例分析 + +1. **精确匹配**: + +```shell + + AuthType Basic + AuthName "Admin Area" + AuthUserFile "/path/to/htpasswd" + Require valid-user + +``` + +在这个例子中,任何访问 `/admin` URL 的请求都会被要求进行基本认证(Basic Authentication)。只有通过验证的用户才能访问这个 URL。 + +2. **前缀匹配**: + +```shell + + Options Indexes FollowSymLinks + AllowOverride None + Require all granted + +``` + + 在这个例子中,任何访问 `/documents` 的 URL 的请求都会被允许执行目录索引和跟踪符号链接。所有用户都被允许访问这些 URL。 + +3. **正则表达式匹配**: + +```shell + + SetHandler application/x-httpd-php + +``` + + 在这个例子中,任何访问以 `.php` 结尾的 URL 的请求都会被 Apache 识别为 PHP 脚本,并使用 PHP 处理器来执行它们。 + +4. **通配符匹配**: + +```shell + + Options Indexes FollowSymLinks + AllowOverride None + Require all granted + +``` + +在这个例子中,任何位于 `/var/www/*/images` 目录下的文件都会被允许通过目录索引和符号链接访问,所有用户都被允许访问这些文件 + +5. **特定文件类型匹配**: + +```shell + + SetOutputFilter INCLUDES + # SetOutputFilter INCLUDES 是 Apache httpd 中一个非常有用的配置指令,它可以启用 Server-Side Includes (SSI) 功能。 + +``` + +在这个例子中,任何以 `.html` 结尾的文件都会被 Apache 处理为包含服务器端包含指令(Server-Side Includes)的文件。 + +6. **主机名匹配**: + +```bash + + Documentroot "/var/www/example" + ServerName www.example.com + +``` + +在这个例子中,任何发送到 `www.example.com:80` 的请求都会被映射到 `/var/www/example` 目录,并由 Apache 配置为 `www.example.com` 的虚拟主机。 + + +# 7. Options 指令 + +## 7.1 相关介绍 + +用于控制特定目录下启用的服务器功能特性(如目录列表、符号链接等) + +语法:`Options [+|-]option [[+|-]option] ...` +作用域:`, , , .htaccess` +特性: + +- **+/- 前缀**​​:动态添加或移除选项,支持配置继承与合并(无前缀时直接覆盖) +- **优先级**:子目录配置优先于父目录,正则表达式匹配的配置最后生效 + +**常用选项** + +| 选项 | 功能说明 | 适用场景 | +| :------------------- | :----------------------------------------------------------- | :----------------------------- | +| Indexes | 当目录无默认文件(如 index.html)时,自动生成文件列表 | 开发环境调试,生产慎用 | +| FollowSymLinks | 允许通过符号链接访问目录外资源(在 `` 中无效) | 跨目录资源整合 | +| MultiViews | 内容协商:根据请求头(如语言)自动匹配文件(需 mod_negotiation) | 多语言站点适配 | +| ExecCGI | 允许执行 CGI 脚本(需 mod_cgi) | 动态脚本处理(如 Perl/Python) | +| Includes | 启用服务端包含(SSI)(需 mod_include) | 动态页面片段嵌入 | +| IncludesNOEXEC | 允许 SSI 但禁用 `#exec` 执行命令/CGI | 安全限制下的 SSI 功能 | +| SymLinksIfOwnerMatch | 仅当符号链接与目标文件所有者相同时允许访问(在 `` 中无效) | 高安全要求的共享环境 | +| All | 启用除 `MultiViews` 外的所有特性(默认值) | 快速启用常用功能 | +| None | 禁用所有额外特性 | 严格安全策略 | + +## 7.2 案例分析 + +使用 `Index` 和 `FollowSymLinks`,当访问无索引文件的目录时显示文件列表,允许符号链接跳转;依次去掉 `FollowSymLinks` 和 `Index` 观察现象。 + +```shell +# 准备实验环境 +[root@localhost ~]# mkdir -pv /data/site04/public +[root@localhost ~]# touch /data/site04/public/{1,2,3}.html +[root@localhost ~]# ln -s /etc/passwd /data/site04/public/passwd.tx +[root@localhost ~]# cat << EOF > /etc/httpd/conf.d/site01.conf +Listen 8001 + + Documentroot /data/site04 + + Options Indexes FollowSymLinks + Require all granted + + +EOF +[root@localhost ~]# systemctl restart httpd +# 通过浏览器访问测试验证 +``` + + +# 8. AllowOverride 指令 + +## 8.1 相关介绍 + +用于控制是否允许在 .htaccess 文件中覆盖主配置文件的设定,通常用于目录级的动态配置,无需重启服务即可生效。作用域限制​为:仅能在 `​​` 块​​中生效,在 ``、`` 等配置段中无效。 + + +**常用参数** + +| 参数值 | 说明 | +| :------------ | :----------------------------------------------------------- | +| None | 完全忽略 .htaccess 文件,服务器不读取其内容(默认推荐,提升性能与安全性)。 | +| All | 允许 .htaccess 覆盖所有支持的指令(需谨慎启用,可能引发安全风险)。 | +| AuthConfig | 允许覆盖认证相关指令(如 AuthName, Require)。 | +| FileInfo | 允许覆盖文件处理指令(如 ErrorDocument, RewriteRule, Header)。 | +| Indexes | 允许覆盖目录索引控制指令(如 DirectoryIndex, IndexIgnore)。 | +| Limit | 允许覆盖访问控制指令(如 Allow, Deny, Order)。 | +| Options[=...] | 允许覆盖目录特性控制指令(如 Options FollowSymLinks,可指定具体允许的选项)。 | + +## 8.2 案例分析 + +通过 `AllowOverride` 指令和 `.htaccess` 文件,动态为指定目录设定特性控制指令 + +```shell +# 准备实验环境 +[root@localhost ~]# mkdir -pv /data/site05/public +[root@localhost ~]# touch /data/site05/public/{1,2,3}.html +[root@localhost ~]# cat << EOF > /etc/httpd/conf.d/site02.conf +Listen 8002 + + Documentroot /data/site05 + + AllowOverride Options=Indexes + Require all granted + + +EOF +[root@localhost ~]# systemctl restart httpd +# 通过浏览器访问:禁止访问 +# 通过 .htaccess 文件覆盖设定 +[root@localhost ~]# echo "Options Indexes" > /data/site05/public/.htaccess +# 通过浏览器访问测试验证 +``` + +# 9. 访问控制 + +针对各种资源,通常基于两种方式:客户端来源地址和用户账号来进行访问控制。 + +## 9.1 基于IP地址 + +基于客户端IP地址的访问控制: + + - Apache 2.4 默认 **​​拒绝所有未显式授权**​ ​的访问,需明确配置 `Require all granted` 开放全局访问 + - `Require host` 可基于域名控制(如 Require host example.com),但需注意 DNS 解析延迟可能影响性能 + - 不带 `not` 的多个 `Require` 是“或”关系(任一满足即可) + - 带 `not` 和不带 `not` 的语句混合时是“与”关系(需同时满足) + +常见语法: + + - 允许所有主机访问:`Require all granted` + - 拒绝所有主机访问:`Require all denied` + - 授权/拒绝指定来源的IP/host访问:`Require [not] ` + +组合策略配置: + + - `​​` 内部所有条件必须​​同时满足​​(逻辑与) + - `` 内部任一条件满足即可(逻辑或) + +## 9.2 基于用户 + +基于用户的访问控制主要通过身份验证(Authentication)和授权(Authorization)机制实现: + +**认证类型**: + +- Basic 认证​​:用户名密码以 Base64 编码传输(明文,需配合 HTTPS 保证安全) +- Digest 认证​​:密码哈希传输(更安全,但配置复杂) + +**核心配置指令**: + +- AuthName​​:定义认证区域名称(浏览器弹窗提示文本) +- AuthUserFile​​:指定存储用户名密码的文本文件路径(如 .htpasswd) +- Require​​:授权规则(如 valid-user 允许所有认证用户,或指定用户/组) + +**密码文件** + +```shell +[root@localhost ~]# htpasswd --help +Usage: + htpasswd [-cimB25dpsDv] [-C cost] [-r rounds] passwordfile username + ... +# 创建文件并追加用户 +[root@localhost ~]# htpasswd -c /etc/httpd/.htpasswd user1 +# 追加用户 +[root@localhost ~]# htpasswd /etc/httpd/.htpasswd user2 +``` + +## 9.3 基于用户组 + +可以对 htpasswd 产生的多用户进行分组管理 + +组文件:`echo "admins: user1 user2" > /etc/httpd/.htgroup ` +配置授权组: + +```shell +AuthGroupFile /etc/apache2/.htgroup +Require group admins # 仅允许 admins 组成员 +``` + +## 9.4 案例分析 + +混合IP和用户认证:仅允许本地x.x.x.x地址可以通过eagleslab01用户访问 + +```shell +[root@localhost ~]# mkdir -pv /data/site06/admin +[root@localhost ~]# echo "This is NO.6 website!" > /data/site06/admin/index.html +[root@localhost ~]# htpasswd -c /etc/httpd/.htpasswd eagleslab01 +[root@localhost ~]# htpasswd /etc/httpd/.htpasswd eagleslab02 +[root@localhost ~]# cat << EOF > /etc/httpd/conf.d/site06.conf +Listen 8006 + + Documentroot /data/site06 + + AuthType Basic + AuthName "FBI warning" + AuthUserFile "/etc/httpd/.htpasswd" + + Require user eagleslab01 + # 客户端主机地址 + Require ip 172.16.175.1 + # 同时满足IP和用户认证 + + + +EOF +[root@localhost ~]# systemctl restart httpd +# 测试验证 +# 本地主机访问 403 +[root@localhost ~]# curl -u eagleslab01:123456 http://172.16.175.129:8006/admin +# 客户端主机访问 301/200 +client ~ % curl -u eagleslab01:123456 http://172.16.175.129:8006/admin +# 客户端主机访问 401 +client ~ % curl -u eagleslab02:123456 http://172.16.175.129:8006/admin + +``` + +# 10. URL 处理机制 + +**别名(Alias)​​ 和 ​​重定向(Redirect)**​​ 是两种不同的 URL 处理机制,核心区别在于​**​是否改变客户端浏览器地址栏的 URL​​** 以及​**​是否触发新的 HTTP 请求​​** + +## 10.1 别名 Alias + +用于将 URL 路径映射到文件系统的不同位置,实现灵活的资源管理和访问控制。 + +### 10.1.1 相关介绍 + +**路径资源解耦**:将 URL 路径(如 `/images`)映射到物理目录(如 `/var/www/media`),无需修改实际文件位置,示例如下: + +```shell +Alias "/images" "/var/www/html/media" +访问 http://domain.com/images/photo.jpg 实际返回 /var/www/html/media/photo.jpg +``` + +**简化URL结构**:隐藏复杂路径,提升用户体验和 SEO 友好性 +**多域名/路径管理**:同一服务器托管多个站点,示例如下: + +```shell + + ServerName blog.example.com + Alias "/" "/var/www/blog" + + + ServerName notes.example.com + Alias "/" "/var/www/notes" + +... +``` + +**相关指令** + +```shell +# 将 URL 映射到文件系统位置 +Alias [URL-path] file-path|directory-path + +# 使用正则表达式将 URL 映射到文件系统位置 +AliasMatch regex file-path|directory-path + +# 将 URL 映射到文件系统位置,并将目标指定为 CGI 脚本 +ScriptAlias [URL-path] file-path|directory-path +``` + + +### 10.1.2 案例分析 + +将 `/usr/share/httpd/icons` 独立资源通过 `/icons` 别名暴露简洁路径 + +```shell +[root@localhost ~]# cat << EOF > /etc/httpd/conf.d/site07.conf +Listen 8007 + + Alias /icons /usr/share/httpd/icons + + Options Indexes FollowSymLinks + AllowOverride none + Require all granted + + +EOF +# 通过浏览器访问 http://172.16.175.129:8007/icons/ +# [root@localhost ~]# curl -I http://172.16.175.129:8007/icons/ 返回 200 + +``` + +## 10.2 重定向 Redirect + +强制客户端跳转到另一个 URL,通常用于路径迁移或权限控制 + +### 10.2.1 相关介绍 + +**网站改版迁移**​​:旧路径 `http://old.com` 永久跳转到 `http://new.com`(301) +​**​权限控制**​​:未登录用户访问 `/admin` 时跳转到登录页(302) +**临时维护**​​:将流量临时导向通知页面(302) + +**相关指令**: + +```shell +Redirect [status] [URL-path] URL + permanent 永久,返回301 + temp 临时,返回302 + seeother 资源替换 返回303 + gone 永久移除,返回410 + +RedirectMatch [status] regex URL + 使用正则表达式代替简单的路径前缀匹配 + +RedirectPermanent URL-path URL + 等效 Redirect permanent + +RedirectTemp URL-path URL + 等效 Redirect temp + +# https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html +RewriteEngine on|off + 启用或禁用运行时重写引擎 + +RewriteCond TestString CondPattern [flags] + 定义了重写发生的条件 + +RewriteRule Pattern Substitution [flags] + 定义重写引擎的规则 + +``` + + +### 10.2.2 案例分析1 + +网站改版迁移:旧域名访问新网站 + +```shell +[root@localhost ~]# mkdir -pv /data/new_site08/ +[root@localhost ~]# echo "This is New NO.8 website!" > /data/new_site08/index.html +[root@localhost ~]# cat << EOF > /etc/httpd/conf.d/site08.conf +# old site + + ServerName cloud.eagleslabtest.com + Redirect 301 / http://ncloud.eagleslabtest.com/ + + +# new site + + ServerName ncloud.eagleslabtest.com + Documentroot /data/new_site08 + + Require all granted + + +EOF +[root@localhost ~]# echo "172.16.175.129 ncloud.eagleslabtest.com" >> /etc/hosts +[root@localhost ~]# echo "172.16.175.129 cloud.eagleslabtest.com" >> /etc/hosts +[root@localhost ~]# systemctl restart httpd +# 测试验证 +[root@localhost ~]# curl -L http://cloud.eagleslabtest.com/index.html +This is New NO.8 website! +[root@localhost ~]# curl -I http://cloud.eagleslabtest.com/index.html +HTTP/1.1 301 Moved Permanently +Date: Mon, 07 Jul 2025 19:20:27 GMT +Server: Apache/2.4.62 (Rocky Linux) +Location: http://ncloud.eagleslabtest.com/index.html +Connection: close +Content-Type: text/html; charset=iso-8859-1 + +``` + +### 10.2.3 案例分析2 + +用户访问需登录的资源(如个人中心、订单页),系统通过302重定向到登录页,并在登录成功后携带return_url参数跳回原页面 + +```shell +RewriteEngine On +RewriteCond %{REQUEST_URI} ^/profile +RewriteCond %{HTTP_COOKIE} !session_token +RewriteRule ^(.*)$ /login?return_url=$1 [R=302,L] +``` + +### 10.2.4 案例分析3 + +根据User-Agent将移动端用户临时重定向至移动版站点 + +```shell +RewriteCond %{HTTP_USER_AGENT} "android|iphone|ipad" [NC] +RewriteCond %{HTTP_HOST} ^www\.example\.com$ +RewriteRule ^(.*)$ http://m.example.com/$1 [R=302,L] +``` + +# 11. 网页压缩技术 + +网页压缩技术主要通过 ​​mod_deflate 模块​​实现,它能显著减少传输数据量,提升网站加载速度和用户体验 + +## 11.1 相关介绍 + +- 使用 mod_deflate 模块压缩页面优化传输速度 +- 适用场景 + - 节约带宽,额外消耗CPU;同时,可能有些较老浏览器不支持 + - 压缩适于压缩的资源,例如文本文件 + + +**压缩相关指令** + +```shell +# 可选项 +SetOutputFilter DEFLATE +# 指定对哪种 MIME 类型进行压缩,必须指定项 +AddOutputFilterByType DEFLATE text/plain +AddOutputFilterByType DEFLATE text/html +AddOutputFilterByType DEFLATE application/xhtml+xml +AddOutputFilterByType DEFLATE text/xml +AddOutputFilterByType DEFLATE application/xml +AddOutputFilterByType DEFLATE application/x-javascript +AddOutputFilterByType DEFLATE text/javascript +AddOutputFilterByType DEFLATE text/css + +# 也可以同时写多个 +AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/javascript + +# 压缩级别 (Highest 9 - Lowest 1) ,默认gzip  默认级别是由库决定 +DeflateCompressionLevel 9 +# 排除特定旧版本的浏览器,不支持压缩 +# Netscape 4.x 只压缩text/html +BrowserMatch ^Mozilla/4 gzip-only-text/html +# Netscape 4.06-08 三个版本 不压缩 +BrowserMatch ^Mozilla/4\.0[678] no-gzip +# Internet Explorer标识本身为“Mozilla / 4”,但实际上是能够处理请求的压缩。如果用户代理首部 +# 匹配字符串“MSIE”(“B”为单词边界”),就关闭之前定义的限制 +BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html + +SetOutputFilter DEFLATE # 启用Gzip压缩 +``` + +## 11.2 案例分析 + +```shell +# 准备实验环境 +[root@localhost ~]# mkdir -pv /data/site0901/ /data/site0902/ +[root@localhost ~]# tree /etc > /data/site0901/blog.html +[root@localhost ~]# tree /etc > /data/site0902/blog.html +# 配置对比vhost +[root@localhost ~]# cat << EOF > /etc/httpd/conf.d/site09.conf +Listen 8009 +Listen 8010 + + Documentroot /data/site0901/ + + Require all granted + + AddOutputFilterByType DEFLATE text/plain + AddOutputFilterByType DEFLATE text/html      + SetOutputFilter DEFLATE + + + + Documentroot /data/site0902/ + + Require all granted + + +EOF +[root@localhost ~]# systemctl restart httpd +# 默认curl没有压缩,需要加参数 --compressed,输出结果关注 Content-Encoding: gzip & Content-Length +[root@localhost ~]# curl -I --compressed 127.0.0.1:8009/blog.html +[root@localhost ~]# curl -I --compressed 127.0.0.1:8010/blog.html + +``` + +# 12. HTTPS + +- SSL是基于IP地址实现,单IP的httpd主机,仅可以使用一个https虚拟主机 +- 实现多个虚拟主机站点,apache不能支持,nginx支持 +- SSL实现过程 + - 客户端发送可供选择的加密方式,并向服务器请求证书 + - 服务器端发送证书以及选定的加密方式给客户端 + - 客户端取得证书并进行证书验证,如果信任给其发证书的CA + - 验证证书来源的合法性;用CA的公钥解密证书上数字签名 + - 验证证书内容的合法性:完整性验证 + - 检查证书的有效期限 + - 检查证书是否被吊销 + - 证书中拥有者的名字,与访问的目标主机要一致 +- 客户端生成临时会话密钥(对称密钥),并使用服务器端的公钥加密此数据发送给服务器,完成密钥交换 +- 服务用此密钥加密用户请求的资源,响应给客户端 + +## 12.1 HTTPS请求过程 + +Web网站的登录页面都是使用https加密传输的,加密数据以保障数据的安全,HTTPS能够加密信息,以免敏感信息被第三方获取,所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议,HTTPS其实是有两部分组成: HTTP + SSL/ TLS,也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会通过TLS进行加密,所以传输的数据都是加密后的数据。 + +![img-HTTPS请求过程](04.Apache/HTTPS请求过程.png) + +https 实现过程如下: + +1. **客户端发起HTTPS请求** + + 客户端访问某个web端的https地址,一般都是443端口 + +2. **服务端的配置** + + 采用https协议的服务器必须要有一套证书,可以通过**权威机构**申请,也可以自己制作,目前国内很多⽹站都⾃⼰做的,当你访问⼀个⽹站的时候提示证书不可信任就表示证书是⾃⼰做的,证书就是⼀个公钥和私钥匙,就像⼀把锁和钥匙,正常情况下只有你的钥匙可以打开你的锁,你可以把这个送给别⼈让他锁住⼀个箱⼦,⾥⾯放满了钱或秘密,别⼈不知道⾥⾯放了什么⽽且别⼈也打不开,只有你的钥匙是可以打开的。 + + ``` + 对称加密与非对称加密区别 + 对称加密只有密钥 + 非对称加密有公私钥,公钥加密,私钥解密 + ``` + +3. **传送证书** + + 服务端给客户端传递证书,其实就是公钥,⾥⾯包含了很多信息,例如证书得到颁发机构、过期时间等等。 + +4. **客户端解析证书** + + 这部分⼯作是有客户端完成的,⾸先回验证公钥的有效性,⽐如颁发机构、过期时间等等,如果发现异常则会弹出⼀个警告框提示证书可能存在问题,如果证书没有问题就⽣成⼀个随机值,然后⽤证书对该随机值进⾏加密,就像2步骤所说把随机值锁起来,不让别⼈看到。 + +5. **传送4步骤的加密数据** + + 就是将⽤证书加密后的随机值传递给服务器,⽬的就是为了让服务器得到这个随机值,以后客户端和服务端的通信就可以通过这个随机值进⾏加密解密了。 + +6. **服务端解密信息** + + 服务端用私钥解密5步骤加密后的随机值之后,得到了客户端传过来的随机值(私钥),然后把内容通过该值进⾏对称加密,对称加密就是将信息和私钥通过算法混合在⼀起,这样除非你知道私钥,不然是⽆法获取其内部的内容,而正好客户端和服务端都知道这个私钥,所以只要机密算法够复杂就可以保证数据的安全性。 + +7. **传输加密后的信息** + + 服务端将⽤私钥加密后的数据传递给客户端,在客户端可以被还原出原数据内容 + +8. **客户端解密信息** + + 客户端⽤之前⽣成的私钥获解密服务端传递过来的数据,由于数据⼀直是加密的,因此即使第三⽅获取到数据也⽆法知道其详细内容。 + +## 12.2 HTTPS加解密过程 + +**HTTPS 中使用的加密技术主要包括以下几个步骤:** + +1. **密钥协商**: + - 在 SSL/TLS 握手过程中,客户端和服务器协商出一个对称加密密钥。 + - 这个对称密钥是用于后续数据加密和解密的临时密钥。 +2. **对称加密**: + - 客户端和服务器使用协商好的对称密钥对 HTTP 请求和响应数据进行加密和解密。 + - 对称加密算法通常包括 AES、DES、3DES 等,速度快且计算开销小。 +3. **非对称加密**: + - 在握手过程中,服务器使用自己的私钥对称密钥进行加密,发送给客户端。 + - 客户端使用服务器的公钥解密获得对称密钥。 + - 非对称加密算法通常包括 RSA、ECC 等,安全性高但计算开销大。 +4. **摘要算法**: + - 客户端和服务器使用摘要算法(如 SHA-256)计算数据的数字签名。 + - 数字签名用于验证数据的完整性,确保数据在传输过程中未被篡改。 +5. **证书验证**: + - 客户端使用预先内置的受信任根证书颁发机构(CA)公钥,验证服务器证书的合法性。 + - 这确保连接的服务器是真实的,而不是中间人攻击者伪造的。 + +## 12.3 案例分析 + +自建 HTTPS 服务器:自签名证书生成、Apache 配置、​强制 HTTP 跳转 HTTPS及测试验证 + +```shell +[root@localhost ~]# mkdir -pv /data/site10/ +[root@localhost ~]# echo "This is NO.10 website!" > /data/site10/index.html +# 安装 mod_ssl 模块和 openssl 工具 +[root@localhost ~]# yum install mod_ssl openssl -y +# 确认 httpd 已经加载 ssl_module 模块 +[root@localhost ~]# httpd -M | grep ssl +ssl_module (shared) +# 备份默认 ssl.conf 文件避免互相影响 +[root@localhost ~]# mv /etc/httpd/conf.d/ssl.conf{,.bak} +[root@localhost ~]# grep -nr 'ssl' /etc/httpd/conf.modules.d/* +/etc/httpd/conf.modules.d/00-ssl.conf:1:LoadModule ssl_module modules/mod_ssl.so +# 创建 X509 自签名证书 +[root@localhost ~]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout /etc/httpd/ssl/server.key \ + -out /etc/httpd/ssl/server.crt \ + -subj "/C=XX/ST=Test/O=DevEnv/CN=ssl.eagleslabtest.com" \ + -addext "subjectAltName=DNS:ssl.eagleslabtest.com,IP:172.16.175.129" + +# HTTPS 相关虚拟主机配置 +[root@localhost ~]# cat << EOF > /etc/httpd/conf.d/site10.conf +# ​​强制 HTTP 跳转 HTTPS + + ServerName ssl.eagleslabtest.com + RewriteEngine On + RewriteCond %{HTTPS} off + RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] + + +Listen 443 https + + ServerName ssl.eagleslabtest.com + DocumentRoot /data/site10/ + + Require all granted + + SSLEngine on + SSLCertificateFile /etc/httpd/ssl/server.crt + SSLCertificateKeyFile /etc/httpd/ssl/server.key + + # 安全增强配置(可选但推荐) + # 禁用旧协议 + SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 + # 强加密套件 + SSLCipherSuite HIGH:!aNULL:!MD5:!3DES + +EOF + +[root@localhost ~]# systemctl restart httpd +[root@localhost ~]# echo "172.16.175.129 ssl.eagleslabtest.com" >> /etc/hosts +# 测试验证: -k 忽略证书警告测试 +[root@localhost ~]# curl -k https://ssl.eagleslabtest.com/index.html +This is NO.10 website! +# 测试验证: 强制跳转 +[root@localhost ~]# curl -k -L http://ssl.eagleslabtest.com/index.html +This is NO.10 website! +``` + +一、通过openssl工具来自己生成一个证书,然后颁发给自己 + +```shell +# 1. 安装mod_ssl和openssl +[root@localhost ~]# yum install mod_ssl openssl -y + +# 2.生成2048位的加密私钥 +[root@localhost ~]# openssl genrsa -out server.key 2048 + +# 3.生成证书签名请求(CSR) +[root@localhost ~]# openssl req -new -key server.key -out server.csr +# 一路回车到底,过程暂时不需要管 + +# 4.生成类型为X509的自签名证书。有效期设置3650天,即有效期为10年 +[root@localhost ~]# openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt + +# 5.复制文件到相应的位置 +[root@localhost ~]# cp server.crt /etc/pki/tls/certs/ +[root@localhost ~]# cp server.key /etc/pki/tls/private/ +[root@localhost ~]# cp server.csr /etc/pki/tls/private/ + +# 6.修改配置文件,指定我们自己的证书文件路径 +[root@localhost ~]# vim /etc/httpd/conf.d/ssl.conf +SSLCertificateFile /etc/pki/tls/certs/server.crt +SSLCertificateKeyFile /etc/pki/tls/private/server.key + +# 7.重启httpd +[root@node1 ~]# systemctl restart httpd   +``` + +二、检查443端口是否开放 + +```bash +[root@localhost ~]# ss -nlt +State Recv-Q Send-Q Local Address:Port Peer Address:Port Process +LISTEN 0 128 0.0.0.0:22 0.0.0.0:* +LISTEN 0 511 *:443 *:* +LISTEN 0 511 *:80 *:* +LISTEN 0 128 [::]:22 [::]:* +``` + +三、https访问测试 + +可以使用curl命令查看证书,可以看到我们的证书是一个自签名证书 + +```bash +[root@localhost ~]# curl -kv https://127.0.0.1 +#k表示支持https +#v表示显示详细的信息 +..... +SSL certificate verify result: self-signed certificate (18), continuing anyway. +..... +``` + +Windows访问测试 + +![image-20250118224232388](04.Apache/image-20250118224232388.png) + + +# 13. MPM 多路处理模块 + +httpd 支持三种 MPM 工作模式:prefork, worker, event + +**相关配置文件** + +```shell +[root@localhost ~]# cat /etc/httpd/conf.modules.d/00-mpm.conf |grep mpm +# LoadModule mpm_prefork_module modules/mod_mpm_prefork.so +# LoadModule mpm_worker_module modules/mod_mpm_worker.so +LoadModule mpm_event_module modules/mod_mpm_event.so +``` + +## 13.1 prefork 模式 + +Prefork MPM,这种模式采用的是预派生子进程方式,用单独的子进程来处理请求,子进程间互相独立,互不影响,大大的提高了稳定性,但每个进程都会占用内存,所以消耗系统资源过高。 + +![img](04.Apache/prefork模式.png) + +## 13.2 worker 模式 + +Worker MPM 是Apche 2.0 版本中全新的支持多进程多线程混合模型的 MPM,由于使用线程来处理 HTTP 请求,所以效率非常高,而对系统的开销也相对较低,Worker MPM 也是基于多进程的,但是每个进程会生成多个线程,由线程来处理请求,这样可以保证多线程可以获得进程的稳定性。 + +![img](04.Apache/worker工作模式.png) + +## 13.3 event 模式 + +这个是 Apache 中最新的模式,在现在版本里的已经是稳定可用的模式。它和 worker 模式很像,最大的区别在于,它解决了 keepalive 场景下 ,长期被占用的线程的资源浪费问题(某些线程因为被keep-alive,挂在那里等待,中间几乎没有请求过来,一直等到超时)。 + +event MPM 中,会有一个专门的线程来管理这些 keepalive 类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样,一个线程就能处理多个请求,实现了异步非阻塞。 + +event MPM 在遇到某些不兼容的模块时会失效,将会回退到 worker 模式,一个工作线程处理一个请求。官方自带的模块,全部是支持event MPM的。 + +![img](04.Apache/event工作模式.png) + +# 14. LAMP架构 + +LAMP就是由Linux+Apache+MySQL+PHP组合起来的架构 + +并且Apache默认情况下就内置了PHP解析模块,所以无需CGI即可解析PHP代码 + +**请求示意图:** + +image-LAMP架构 + +## 14.1 安装Apache + +```bash +[root@localhost ~]# yum install -y httpd + +# 启动httpd +[root@localhost ~]# systemctl enable --now httpd + +# 关闭防火墙和SElinux +[root@localhost ~]# systemctl stop firewalld +[root@localhost ~]# setenforce 0 +``` + +**访问测试:**`http://IP` + +image-20240719230216883 + +## 14.2 安装php环境 + +1. 安装php8.0全家桶 + +```bash +[root@localhost ~]# yum install -y php* + +# 启动php-fpm +[root@localhost ~]# systemctl enable --now php-fpm +[root@localhost ~]# systemctl status php-fpm +● php-fpm.service - The PHP FastCGI Process Manager + Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; enabled; preset: disabled) + Active: active (running) since Sat 2025-01-18 21:54:09 CST; 7s ago + Main PID: 34564 (php-fpm) + Status: "Ready to handle connections" + Tasks: 6 (limit: 10888) + Memory: 22.6M + CPU: 119ms + CGroup: /system.slice/php-fpm.service + ├─34564 "php-fpm: master process (/etc/php-fpm.conf)" + ├─34565 "php-fpm: pool www" + ├─34566 "php-fpm: pool www" + ├─34567 "php-fpm: pool www" + ├─34568 "php-fpm: pool www" + └─34569 "php-fpm: pool www" + +Jan 18 21:54:09 localhost.localdomain systemd[1]: Starting The PHP FastCGI Process Manage> +Jan 18 21:54:09 localhost.localdomain systemd[1]: Started The PHP FastCGI Process Manager. +``` + +2. 重启httpd服务以加载php相关模块 + +```bash +[root@localhost ~]# systemctl restart httpd +``` + +## 14.3 安装Mysql数据库 + +```bash +# 安装mariadb数据库软件 +[root@localhost ~]# yum install -y mariadb-server mariadb + +# 启动数据库并且设置开机自启动 +[root@localhost ~]# systemctl enable --now mariadb + +# 设置mariadb的密码 +[root@localhost ~]# mysqladmin password '123456' + +# 验证数据库是否工作正常 +[root@localhost ~]# mysql -uroot -p123456 -e "show databases;" ++--------------------+ +| Database | ++--------------------+ +| information_schema | +| mysql | +| performance_schema | ++--------------------+ +``` + +## 14.4 PHP探针测试 + +在默认的网站根目录下创建`info.php` + +```bash +[root@localhost ~]# vim /var/www/html/info.php + +``` + +写一个简单的php代码,可以使用phpinfo函数查看php的信息,从而检测是否成功解析php代码 + +编写好以后,我们访问:`http://IP/info.php`测试 + +image-20250118215910240 + +这里如果可以看到上述页面,说明我们的php代码成功被解析了 + +## 14.5 数据库连接测试 + +编写php代码,用php来连接数据库测试 + +```bash +[root@localhost ~]# vim /var/www/html/mysql.php + +``` + +编写好以后,我们访问:`http://IP/mysql.php`测试: + +image-20240719231237537 + +## 14.6 安装phpmyadmin + +由于我们还没有学习mysql如何管理,我们可以部署phpmyadmin工具,该工具可以让我们可视化管理我们的数据库 + +```bash +# 移动到网站根目录 +[root@localhost ~]# cd /var/www/html + +# 下载phpmyadmin源码 +[root@localhost ~]# wget https://files.phpmyadmin.net/phpMyAdmin/5.1.1/phpMyAdmin-5.1.1-all-languages.zip + +# 解压软件包,并且重命名 +[root@localhost ~]# unzip phpMyAdmin-5.1.1-all-languages.zip +[root@localhost ~]# mv phpMyAdmin-5.1.1-all-languages phpmyadmin +``` + +访问`http://IP/phpmyadmin`进行测试 + +image-20240719231636758 + +用户名和密码为我们刚才初始化数据库时设置的root和123456,登陆后,会进入图形化管理界面 + +image-20240719231744450 + +# 15. 个人博客 + +## 15.1 源码获取 + +下载typecho博客系统源码到`/var/www/html/typecho` + +```bash +[root@localhost ~]# cd /var/www/html + +# 创建typecho目录 +[root@localhost ~]# mkdir typecho +[root@localhost ~]# cd typecho + +[root@localhost ~]# wget https://github.com/typecho/typecho/releases/latest/download/typecho.zip + +# 解压源码 +[root@localhost ~]# unzip typecho.zip +``` + +## 15.2 创建数据库 + +点击数据库 + +image-20240719232259490 + +输入数据库名之后,就可以点击创建 + +![image-20211106110832539](04.Apache/image-20211106110832539.png) + +## 15.3 安装博客系统 + +下面就可以开始进入网站安装的部分了,访问博客系统页面 + +`http://IP/typecho` + +image-20250118220523743 + +提示安装目录下面的/usr/uploads没有权限,那么我们手动赋予该目录w权限 + +```bash +[root@localhost typecho]# chmod a+w usr/uploads/ +``` + +填写数据库信息,密码为部署mariadb时设置的123456 + +image-20250118220724260 + +遇到提示无法自动创建配置文件config.inc.php。我们手动在网站的根目录下创建该文件,并且将内容复制进去 + +```bash +[root@localhost typecho]# vim config.inc.php +``` + +之后完善如下网站信息后,点击继续安装 + +image-20250118221026614 + +到此,我们的typecho个人博客系统就部署完成了 + +image-20250118221109186 + +## 15.4 切换主题 + +默认的主题如下,界面比较的简洁,我们可以给这个网站替换主题,也可以借此加深熟悉我们对Linux命令行的熟练程度 + +image-20250118221202018 + +第三方主题商店:https://www.typechx.com/ + +我们尝试更换这个主题 + +image-20250118221344667 + +选择模板下载 + +image-20250118221414487 + +然后在打开的github仓库中下载ZIP压缩包 + +image-20250118221502963 + +将下载好的主题压缩包上传到博客主题的目录`/var/www/html/typecho/usr/themes` + +image-20250118221634385 + +然后解压主题包,并且将名称改为简单一点的 + +```bash +[root@localhost themes]# unzip Typecho-Butterfly-main.zip +[root@localhost themes]# ls +Typecho-Butterfly-main Typecho-Butterfly-main.zip default +[root@localhost themes]# mv Typecho-Butterfly-main butterfly +[root@localhost themes]# rm -rf Typecho-Butterfly-main.zip +``` + +然后登录到博客后台,在设置里更换主题 + +image-20250118221843976 + +然后回到博客首页刷新一下,就可以看到新的主题已经应用了~ + +![image-20250118221920089](04.Apache/image-20250118221920089.png) + +会有一些图片资源的丢失,稍微了解一点前端知识,就可以将其完善好了。不懂前端的同学,可以去找一些简单一点的主题。 + +![image-20250118221958932](04.Apache/image-20250118221958932-1763003655010-58.png) \ No newline at end of file diff --git a/02.企业服务/04.Apache/HTTPS请求过程.png b/02.企业服务/04.Apache/HTTPS请求过程.png new file mode 100644 index 0000000..b44cdfc Binary files /dev/null and b/02.企业服务/04.Apache/HTTPS请求过程.png differ diff --git a/02.企业服务/04.Apache/LAMP架构.png b/02.企业服务/04.Apache/LAMP架构.png new file mode 100644 index 0000000..66042cb Binary files /dev/null and b/02.企业服务/04.Apache/LAMP架构.png differ diff --git a/02.企业服务/04.Apache/event工作模式.png b/02.企业服务/04.Apache/event工作模式.png new file mode 100644 index 0000000..b70b836 Binary files /dev/null and b/02.企业服务/04.Apache/event工作模式.png differ diff --git a/02.企业服务/04.Apache/image-20211106110832539.png b/02.企业服务/04.Apache/image-20211106110832539.png new file mode 100644 index 0000000..d79cec3 Binary files /dev/null and b/02.企业服务/04.Apache/image-20211106110832539.png differ diff --git a/02.企业服务/04.Apache/image-20240719230216883.png b/02.企业服务/04.Apache/image-20240719230216883.png new file mode 100644 index 0000000..e86a4e8 Binary files /dev/null and b/02.企业服务/04.Apache/image-20240719230216883.png differ diff --git a/02.企业服务/04.Apache/image-20240719231237537.png b/02.企业服务/04.Apache/image-20240719231237537.png new file mode 100644 index 0000000..a2ce59f Binary files /dev/null and b/02.企业服务/04.Apache/image-20240719231237537.png differ diff --git a/02.企业服务/04.Apache/image-20240719231636758.png b/02.企业服务/04.Apache/image-20240719231636758.png new file mode 100644 index 0000000..6d0f345 Binary files /dev/null and b/02.企业服务/04.Apache/image-20240719231636758.png differ diff --git a/02.企业服务/04.Apache/image-20240719231744450.png b/02.企业服务/04.Apache/image-20240719231744450.png new file mode 100644 index 0000000..4fc6bb6 Binary files /dev/null and b/02.企业服务/04.Apache/image-20240719231744450.png differ diff --git a/02.企业服务/04.Apache/image-20240719232259490.png b/02.企业服务/04.Apache/image-20240719232259490.png new file mode 100644 index 0000000..4594ff6 Binary files /dev/null and b/02.企业服务/04.Apache/image-20240719232259490.png differ diff --git a/02.企业服务/04.Apache/image-20250118215910240.png b/02.企业服务/04.Apache/image-20250118215910240.png new file mode 100644 index 0000000..668ff83 Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118215910240.png differ diff --git a/02.企业服务/04.Apache/image-20250118220523743.png b/02.企业服务/04.Apache/image-20250118220523743.png new file mode 100644 index 0000000..5760ce9 Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118220523743.png differ diff --git a/02.企业服务/04.Apache/image-20250118220724260.png b/02.企业服务/04.Apache/image-20250118220724260.png new file mode 100644 index 0000000..29d0a8b Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118220724260.png differ diff --git a/02.企业服务/04.Apache/image-20250118221026614.png b/02.企业服务/04.Apache/image-20250118221026614.png new file mode 100644 index 0000000..3c86d6b Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118221026614.png differ diff --git a/02.企业服务/04.Apache/image-20250118221109186.png b/02.企业服务/04.Apache/image-20250118221109186.png new file mode 100644 index 0000000..0521f31 Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118221109186.png differ diff --git a/02.企业服务/04.Apache/image-20250118221202018.png b/02.企业服务/04.Apache/image-20250118221202018.png new file mode 100644 index 0000000..9c04f14 Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118221202018.png differ diff --git a/02.企业服务/04.Apache/image-20250118221344667.png b/02.企业服务/04.Apache/image-20250118221344667.png new file mode 100644 index 0000000..cd68b8d Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118221344667.png differ diff --git a/02.企业服务/04.Apache/image-20250118221414487.png b/02.企业服务/04.Apache/image-20250118221414487.png new file mode 100644 index 0000000..39da26f Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118221414487.png differ diff --git a/02.企业服务/04.Apache/image-20250118221502963.png b/02.企业服务/04.Apache/image-20250118221502963.png new file mode 100644 index 0000000..4b009f7 Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118221502963.png differ diff --git a/02.企业服务/04.Apache/image-20250118221634385.png b/02.企业服务/04.Apache/image-20250118221634385.png new file mode 100644 index 0000000..4da4c1a Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118221634385.png differ diff --git a/02.企业服务/04.Apache/image-20250118221843976.png b/02.企业服务/04.Apache/image-20250118221843976.png new file mode 100644 index 0000000..e1d1783 Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118221843976.png differ diff --git a/02.企业服务/04.Apache/image-20250118221920089.png b/02.企业服务/04.Apache/image-20250118221920089.png new file mode 100644 index 0000000..6d7a0e1 Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118221920089.png differ diff --git a/02.企业服务/04.Apache/image-20250118221958932-1763003655010-58.png b/02.企业服务/04.Apache/image-20250118221958932-1763003655010-58.png new file mode 100644 index 0000000..2ae3a7f Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118221958932-1763003655010-58.png differ diff --git a/02.企业服务/04.Apache/image-20250118224232388.png b/02.企业服务/04.Apache/image-20250118224232388.png new file mode 100644 index 0000000..f274f01 Binary files /dev/null and b/02.企业服务/04.Apache/image-20250118224232388.png differ diff --git a/02.企业服务/04.Apache/prefork模式.png b/02.企业服务/04.Apache/prefork模式.png new file mode 100644 index 0000000..d417fa1 Binary files /dev/null and b/02.企业服务/04.Apache/prefork模式.png differ diff --git a/02.企业服务/04.Apache/web_server_top.png b/02.企业服务/04.Apache/web_server_top.png new file mode 100644 index 0000000..411b16f Binary files /dev/null and b/02.企业服务/04.Apache/web_server_top.png differ diff --git a/02.企业服务/04.Apache/worker工作模式.png b/02.企业服务/04.Apache/worker工作模式.png new file mode 100644 index 0000000..495e6b2 Binary files /dev/null and b/02.企业服务/04.Apache/worker工作模式.png differ diff --git a/02.企业服务/05.Nginx.md b/02.企业服务/05.Nginx.md new file mode 100644 index 0000000..a6456f5 --- /dev/null +++ b/02.企业服务/05.Nginx.md @@ -0,0 +1,1690 @@ +# 1. Nginx 介绍 + +Nginx:engine X ,2002年开始开发,2004年开源,2019年3⽉11⽇,Nginx公司被F5 Networks以6.7亿美元收购。 + +Nginx 则是免费的、开源的、⾼性能的HTTP和反向代理服务器、邮件代理服务器、以及TCP/UDP代理服务器 解决[C10K问题](https://www.ideawu.net/blog/archives/740.html)(10K Connections) + +**Nginx官网:** http://nginx.org/ + +**Nginx 商业版为 Nginx Plus:** https://www.nginx.com/products/nginx/ + +**Nginx ⼆次发行版:** + +- Tengine:由淘宝⽹发起的Web服务器项⽬。它在Nginx的基础上,针对⼤访问量⽹站的需求,添加了很多⾼级功能和特性。Tengine的性能和稳定性已经在⼤型的⽹站如淘宝⽹,天猫商城等得到了很好的检验。它的最终⽬标是打造⼀个⾼效、稳定、安全、易⽤的Web平台,从2011年12⽉开始,Tengine成为⼀个开源项⽬,官⽹ http://tengine.taobao.org/ +- OpenResty:基于 Nginx 与 Lua 语⾔的⾼性能 Web 平台, 章亦春团队开发,官⽹ http://openresty.org/cn/ + +## 1.1 功能 + +- 静态的web资源服务器 html,image,js,css,txt 等静态资源 +- 结合 FastCGI/uWSGI/SCGI 等协议反向代理动态资源请求 +- http/https 协议的反向代理 +- imap4/pop3 协议的反向代理 +- tcp/udp 协议的反向代理 + +## 1.2 基本特征 + +- 模块化设计,较好的扩展性 +- ⾼可靠性高性能高于 apache +- ⽀持热部署:不停机更新配置⽂件,升级版本,更换⽇志⽂件 +- 低内存消耗:10000个 keep-alive 连接模式下的⾮活动连接,仅需2.5M内存 +- 事件驱动epoll,异步非阻塞IO,内存映射等等特性 + + +# 2. Nginx 基础 + +## 2.1 安装部署 + +Nginx 版本分为 Mainline version(主要开发版本)、Stable version (当前最新稳定版)、Legacy versions (旧的稳定版) + +**官方下载地址:** http://nginx.org/en/download.html + +对于 Nginx 的安装,我们常用的方法为在线安装和源码包编译安装 + +**其中:** + +- yum 下载的相关文件都是放在默认的位置,简单高效 +- 编译安装可以自定义版本和功能,更适合业务的定制化 + +**YUM 安装** + +```shell +# 安装 +[root@localhost ~]# yum provides nginx +[root@localhost ~]# yum install nginx -y +# 启动 +[root@localhost ~]# systemctl start nginx && systemctl enable nginx +# 关闭防火墙和SELINUX +# 测试 +[root@localhost ~]# curl -I 127.0.0.1 +HTTP/1.1 200 OK +Server: nginx/1.20.1 + +``` + +**编译安装** + +```shell +# 准备编译环境 +[root@localhost ~]# yum -y install gcc pcre-devel openssl-devel zlib-devel +# 如果需要编译安装,为避免冲突需要将yum部署的nginx版本先卸载掉! +# 准备源码包 +[root@localhost ~]# wget http://nginx.org/download/nginx-1.22.0.tar.gz -P /usr/local/src/ +[root@localhost ~]# cd /usr/local/src && tar xzvf nginx-1.22.0.tar.gz +# 编译 +[root@localhost ~]# useradd -r -s /sbin/nologin nginx +[root@localhost nginx-1.22.0]# ./configure --prefix=/apps/nginx \ + --user=nginx \ + --group=nginx \ + --with-http_ssl_module \ + --with-http_v2_module \ + --with-http_realip_module \ + --with-http_stub_status_module \ + --with-http_gzip_static_module \ + --with-pcre \ + --with-stream \ + --with-stream_ssl_module \ + --with-stream_realip_module \ + --with-file-aio +[root@localhost nginx-1.22.0]# make -j 2 && make install +# 善后工作 +[root@localhost nginx-1.22.0]# chown -R nginx.nginx /apps/nginx +[root@localhost nginx-1.22.0]# ln -s /apps/nginx/sbin/nginx /usr/sbin/ +[root@localhost nginx-1.22.0]# nginx -v +# systemd管理 +[root@localhost ~]# cat << EOF > /usr/lib/systemd/system/nginx.service +[Unit] +Description=The nginx HTTP and reverse proxy server +Documentation=http://nginx.org/en/docs/ +After=network.target remote-fs.target nss-lookup.target +Wants=network-online.target + +[Service] +Type=forking +PIDFile=/apps/nginx/run/nginx.pid +ExecStart=/apps/nginx/sbin/nginx -c /apps/nginx/conf/nginx.conf +ExecReload=/bin/kill -s HUP $MAINPID +ExecStop=/bin/kill -s TERM $MAINPID + +[Install] +WantedBy=multi-user.target +EOF +# 更新nginx.pid存储位置 +[root@localhost ~]# mkdir /apps/nginx/run/ +[root@localhost ~]# vim /apps/nginx/conf/nginx.conf +pid /apps/nginx/run/nginx.pid; +# 启动 +[root@localhost ~]# systemctl start nginx && systemctl enable nginx +# 关闭防火墙和SELINUX +[root@localhost ~]# curl -I 127.0.0.1 +HTTP/1.1 200 OK +Server: nginx/1.22.0 + +``` + +## 2.2 nginx 命令 + +nginx 为 Nginx 服务软件的命令 + +```shell +[root@localhost ~]# nginx -h +nginx version: nginx/1.20.1 +Usage: nginx [-?hvVtTq] [-s signal] [-p prefix] + [-e filename] [-c filename] [-g directives] +``` + +**常用选项** + +| 选项 | 说明 | +| :---------- | :----------------------- | +| -v | 输出版本 | +| -V | 编译相关选项 | +| -t | 测试验证配置文件的正确性 | +| -s | 发送信号指令给主进程 | + +## 2.3 相关文件 + +| 文件 | 说明 | +| :------------------------------------ | :--------------- | +| /etc/nginx/nginx.conf | 主配置文件 | +| /var/log/nginx/ | 日志文件目录 | +| /usr/share/nginx/html/ | 默认站点根目录 | +| /usr/lib/systemd/system/nginx.service | Systemd服务文件 | +| /var/run/nginx.pid | 主进程ID文件 | +| /usr/share/nginx/modules | 动态加载模块目录 | +| /etc/nginx/conf.d/ | 子配置文件目录 | + +## 2.4 主配置文件 + +```shell +[root@localhost ~]# grep -Ev '^$|^#|\s*#' /etc/nginx/nginx.conf +# 全局配置块 +## 运行用户 +user nginx; +## 工作进程数 +worker_processes auto; +## 错误日志 +error_log /var/log/nginx/error.log; +## pid文件存放位置 +pid /run/nginx.pid; +## 动态加载模块配置 +include /usr/share/nginx/modules/*.conf; +# events配置块 +events { + # 设置单个工作进程最大并发连接数 + worker_connections 1024; +} +# http配置块 +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() - 利用操作系统内核的零拷贝技术:即数据拷贝无需切换至用户空间 + sendfile on; + ## 启用 TCP_NOPUSH - 优化网络包传输:即数据包填满时发送 + tcp_nopush on; + ## 启用 TCP_NODELAY - 减少延迟:即基于keepalive连接,尽快发送小数据包 + tcp_nodelay on; + ## keepalive 超时时间 + keepalive_timeout 65; + ## 设置用于存储MIME类型映射的哈希表最大大小 + types_hash_max_size 4096; + ## 定义文件扩展名与MIME类型的映射 + include /etc/nginx/mime.types; + ## 定义响应包默认MIME类型 + default_type application/octet-stream; + ## 加载子配置文件 + include /etc/nginx/conf.d/*.conf; + # server配置块 + server { + ## 监听ipv4和ipv6 80端口 + listen 80; + listen [::]:80; + ## 定义虚拟主机的名称-域名 + server_name _; + ## 根目录 + root /usr/share/nginx/html; + ## 加载默认站点子配置文件 + include /etc/nginx/default.d/*.conf; + ## 定义错误页 + error_page 404 /404.html; + # location配置块 + location = /404.html { + } + error_page 500 502 503 504 /50x.html; + location = /50x.html { + } + } +} + +# 默认配置文件不包括下面两个块 +# mail协议相关配置段 +mail { + ... +} +# stream服务器相关配置段 +stream { + ... +} + +``` + +## 2.5 相关模块 + +**常见模块类型** + +- **核心模块**:是 Nginx 服务器正常运行必不可少的模块,提供错误日志记录、配置文件解析、事件驱动机制、进程管理等核心功能 +- **标准 HTTP 模块**: 提供 HTTP 协议解析相关的功能,比如:端口配置、网页编码设置、HTTP 响应头设置 等等 +- **可选 HTTP 模块**: 主要用于扩展标准的 HTTP 功能,让 Nginx 能处理一些特殊的服务, 比如:Flash 多媒体传输、解析GeolP 请求、网络传输压缩、安全协议 SSL 支持等 +- **邮件服务模块**: 主要用于支持 Nginx 的邮件服务,包括对 POP3 协议、IMAP 协议和 SMTP 协议的支持 +- **Stream 服务模块**: 实现反向代理功能包括TCP协议代理 +- **第三方模块**: 是为了扩展 Nginx 服务器应用,完成开发者自定义功能,比如: Json 支持、 Lua 支持等 + +**部分第三方模块官方文档:** http://nginx.org/en/docs/ + +```shell +# 编译时内置模块 +[root@localhost ~]# nginx -V 2>&1 | tr ' ' '\n' | grep module +# 动态加载模块 +/usr/lib64/nginx/modules +``` + +## 2.6 案例分析 + +- 修改默认网址目录为:`/data/www/html` && 修改 ServerName 为:`test.mysite.com`;修改 Listen 为:`8090` +- 自定义文件扩展名为 `.text` 的响应包中MIME类型为 `text/my_text` +- 响应报文 Server 首部不显示 nginx 版本:防止他人根据某个版本的漏洞进行攻击 + +```shell +# 待补充 +``` + +# 3. 虚拟主机 + +基于不同的IP、不同的端口以及不同的域名实现不同的虚拟主机,依赖于核心模块 ngx_http_ core_module 实现 + +## 3.1 案例分析 + +- 定义 PC 站点,根目录为 `/data/nginx/site01`,直接通过 `x.x.x.x:8001` +- 定义 Mobile 站点,根目录为 `/data/nginx/site02`,通过自定义域名访问 +- 定义 Test 站点,根目录为 `/data/nginx/site03`, 仅能通过本地 `127.0.0.1:8003` 访问 +- 定义 Test 站点,访问 `127.0.0.1:8003/status`,根目录为 `/data/nginx` + +```shell +[root@localhost ~]# mkdir -pv /data/nginx/site0{1..3}/ +[root@localhost ~]# mkdir -pv /data/nginx/status +[root@localhost ~]# echo "Running 3 websites" > /data/nginx/status/index.html +[root@localhost ~]# echo "Hello PC Website!" > /data/nginx/site01/index.html +[root@localhost ~]# echo "Hello Moblie Website!" > /data/nginx/site02/index.html +[root@localhost ~]# echo "Hello Local Test Website!" > /data/nginx/site03/index.html +[root@localhost ~]# cat << EOF > /etc/nginx/conf.d/vhost.conf +# PC +server { + listen 8001; + location / { + root /data/nginx/site01; + } +} +# Mobile +server { + listen 80; + server_name m.test.com; + location / { + root /data/nginx/site02; + } +} +# Test +server { + listen 127.0.0.1:8003; + location / { + root /data/nginx/site03; + } + location /status { + root /data/nginx; + } +} +EOF +# 检查配置文件并重新加载 +[root@localhost ~]# nginx -t +nginx: the configuration file /etc/nginx/nginx.conf syntax is ok +nginx: configuration file /etc/nginx/nginx.conf test is successful +[root@localhost ~]# nginx -s reload +# 测试验证 +[root@localhost ~]# curl 172.16.175.129:8001 +Hello PC Website! +[root@localhost ~]# curl -H 'Hostname: p.test.com' 172.16.175.129 +Hello Moblie Website! +[root@localhost ~]# curl 172.16.175.129:8003 +curl: (7) Failed to connect to 172.16.175.129 port 8003: Connection refused +# 定义 location 时:文件的绝对路径等于 root+location +[root@localhost ~]# curl -L 127.0.0.1:8003/status +Running 3 websites +``` + +# 4. Location + +Nginx 的 location 指令是配置请求路由的核心机制,其匹配规则基于 ​**​URI**​​ 和​**​修饰符​**​,优先级由匹配类型和顺序共同决定。 + +## 4.1 匹配类型与语法 + +```shell +location [ = | ~ | ~* | ^~ ] uri { ... } +``` + +| 修饰符 | 匹配类型 | 语法示例 | 说明 | +| :------- | :------- | :----------------------- | :----------------------------------------------- | +| = | 精确匹配 | location = /path | 仅当 URI 完全等于 /path 时匹配,优先级最高 | +| ^~ | 前缀匹配 | location ^~ /images/ | 匹配以 /images/ 开头的 URI,停止后续正则检查 | +| ~ | 正则匹配 | location ~ \.php$ | 区分大小写,正则表达式匹配,按配置文件顺序匹配 | +| ~* | 正则匹配 | location ~* \.(jpg,png)$ | 不区分大小写,正则表达式匹配,按配置文件顺序匹配 | +| 无修饰符 | 前缀匹配 | location /static/ | 匹配以 /static/ 开头的 URI,需继续正则检查 | +| / | 通用匹配 | location / | 匹配所有请求,优先级最低 | + +**优先级**:`= ^~ ~/~* 无修饰符 /` + +## 4.2 案例分析 + +**官方案例** + +```shell +location = / { + [ configuration A ] +} +location / { + [ configuration B ] +} +location /documents/ { + [ configuration C ] +} +location ^~ /images/ { + [ configuration D ] +} +location ~* \.(gif|jpg|jpeg)$ { + [ configuration E ] +} +# \: 转义字符 +# 实际访问路径:root + 请求的路径 +``` + +**测试验证** + +- The "`/`" request will match configuration A +- the "`/index.html`" request will match configuration B +- the "`/documents/document.html`" request will match configuration C +- the "`/images/1.gif`" request will match configuration D +- the "`/documents/1.jpg`" request will match configuration E + + + +# 5. 访问控制 + +Nginx 的访问控制是保障服务器安全的核心机制,通过多种方式精细化管控请求入口 + +## 5.1 基于 IP + +**典型场景** + +```shell +# 黑名单:默认允许,拒绝特定IP +location /admin { + deny 192.168.1.100; # 拒绝单个 IP + deny 10.0.0.0/8; # 拒绝网段 + allow all; # 允许其他 IP +} + +# 白名单:默认拒绝,允许可信IP +location /api { + allow 192.168.1.0/24; # 允许网段 + allow 172.16.1.1; # 允许单个 IP + deny all; # 拒绝其他 +} + +``` + +## 5.2 基于 HTTP 认证 + +**典型场景** + +```shell +# 生成密码文件 +## 首次创建 +htpasswd -c /etc/nginx/.htpasswd admin +## 追加用户 +htpasswd -c /etc/nginx/.htpasswd root + +# 后台管理 +location /admin { + auth_basic "Tip: input password!"; # 认证提示语 + auth_basic_user_file /etc/nginx/.htpasswd; # 密码文件路径 +} + +``` + +## 5.3 案例分析 + +仅能够通过 admin 用户本地访问后台管理,其他所有IP和用户均不允许 + +```shell +# 准备实验环境 +[root@localhost ~]# htpasswd -c /etc/nginx/.htpasswd admin +New password: +Re-type new password: +Adding password for user admin +[root@localhost ~]# mkdir -pv /data/nginx/site04/admin +[root@localhost ~]# echo "site04" > /data/nginx/site04/admin/index.html +[root@localhost ~]# cat << EOF > /etc/nginx/conf.d/site04.conf +server { + listen 8004; + # 精细化控制:基于请求属性 + if ($http_user_agent ~* bot) { + return 403; + } + location /admin { + root /data/nginx/site04; + auth_basic "Tip: input password!"; + auth_basic_user_file /etc/nginx/.htpasswd; + allow 127.0.0.1; + deny all; + } +} +EOF +[root@localhost ~]# nginx -t +nginx: the configuration file /etc/nginx/nginx.conf syntax is ok +nginx: configuration file /etc/nginx/nginx.conf test is successful +[root@localhost ~]# nginx -s reload +# 测试验证 +client01 ~ % curl -I 172.16.175.129:8004/admin +HTTP/1.1 403 Forbidden +[root@localhost ~]# curl -u admin:123 http://127.0.0.1:8004/admin/ +site04 +[root@localhost ~]# curl -I -u root:123 http://127.0.0.1:8004/admin/ +HTTP/1.1 401 Unauthorized +## 模拟 bot 访问 +[root@localhost ~]# curl -I -u root:123 -H 'User-agent: bot' http://127.0.0.1:8004/admin/ +HTTP/1.1 403 Forbidden +``` + +# 6. 日志相关 + +## 6.1 访问日志 + +记录客户端请求信息(IP、请求方法、状态码、响应大小等) + +**相关配置指令** + +```shell +Syntax: access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]]; + access_log off; +Default: access_log logs/access.log combined; +Context: http, server, location, if in location, limit_except + + +Syntax: log_format name [escape=default|json|none] string ...; +Default: log_format combined "..."; +Context: http + +``` + +**官方案例** + +```shell +# 定义日志格式 +log_format compression '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $bytes_sent ' + '"$http_referer" "$http_user_agent" "$gzip_ratio"'; +# 定义访问日志 +access_log /spool/logs/nginx-access.log compression buffer=32k; +``` + + +## 6.2 错误日志 + +记录服务器错误、警告及调试信息 + +**相关配置指令** + +```shell +Syntax: error_log file [level]; +Default: error_log logs/error.log error; +Context: main, http, mail, stream, server, location + +# 日志级别:debug info notice warn error crit alert emerg + +``` + +**案例分析**:参考主配置文件中日志相关配置 + +# 7. 常用变量 + +## 7.1 内置变量 + +常用内置变量 + +```shell +$remote_addr; +# 存放了客户端的地址,注意是客户端的公网IP +$proxy_add_x_forwarded_for; +# 此变量表示将客户端IP追加请求报文中X-Forwarded-For首部字段,多个IP之间用逗号分隔,如果请求中没有X-Forwarder-For,就使用$remote_addr +$args; +# 变量中存放了URL中的参数 +$document_root; +# 保存了针对当前资源的系统根目录 +$document_uri; +# 保存了当前请求中不包含参数的URI,注意是不包含请求的指令,比如/img/logo.png +$host; +# 存放了请求的host名称 +limit_rate 10240; +echo $limit_rate; +# 如果nginx服务器使用limit_rate配置了显示网络速率,则会显示,如果没有设置,则显示0 +$remote_port; +# 客户端请求Nginx服务器时随机打开的端口,这是每个客户端自己的端口 +$remote_user; +# 已经经过Auth Basic Module验证的用户名 +$request_body_file; +# 做反向代理时发给后端服务器的本地资源的名称 +$request_method; +# 请求资源的方式,GET/PUT等等 +$request_filename; +# 当前请求的资源文件的磁盘路径,由root或alias指令与URL请求生成的文件绝对路径 +# /apps/nginx/html/www/index.html +$request_uri; +# 包含请求参数的原始URI,不包含主机名,相当于:$document_uri?$args +$scheme; +# 请求的协议,例如:http,https,ftp等等 +$server_protocol; +# 保存了客户端请求资源使用的协议版本,例如:HTTP/1.0,HTTP/1.1,HTTP/2.0等等 +$server_addr; +# 保存了服务器的IP地址 +$server_name; +# 请求的服务器的主机名 +$server_port; +# 请求的服务器的端口号 +$http_ +# name为任意请求报文首部字段,表示记录请求报文的首部字段 +$http_user_agent; +# 客户端浏览器的详细信息 +$http_cookie; +# 客户端的cookie信息 +$cookie_ +# name为任意请求报文首部字段cookie的key名 +``` + +## 7.2 自定义变量 + +假如需要自定义变量名和值,使用指令set $variable value; + +语法格式: + +```shell +Syntax:set $varible value; +Default: - +Context: server, location, if +``` + +范例: + +```shell +[root@localhost ~]# vim /apps/nginx/conf.d/www.conf +set $name zs; +echo $name; +set $my_port $server_port; +echo $my_port; +echo "$server_name:$server_port"; +``` + +# 8. 网页压缩技术 + +网页压缩技术主要通过 ngx_http_gzip_module 模块​​实现,它能显著减少传输数据量,提升网站加载速度和用户体验 + +```shell +# 启用或禁用 gzip 压缩,默认关闭 +gzip on | off; +# 压缩比由低到高1到9,默认为1 +gzip_comp_level level; +# 禁用 IE6 gzip 功能 +gzip_disable "MSIE [1-6]\."; +# gzip 压缩的最小文件,小于设置值的文件将不会压缩 +gzip_min_length 1k; +# 启用压缩功能时,协议的最小版本,默认 HTTP/1.1 +gzip_http_version 1.0 | 1.1; +# 指定 Nginx 服务需要向服务器申请的缓存空间的个数和大小,平台不同,默认:32 4k或者 16 8k +gzip_buffers number size; +# 指明仅对哪些类型的资源执行压缩操作,默认为 gzip_types text/html,不用显示指定,否则出错 +gzip_types mime-type ...; +# 如果启用压缩,是否在相应报文首部插入 "vary: Accept-Encoding",一般建议打开 +gzip_vary on | off + +``` + +**案例分析**:参考 Apache 对应案例的测试验证思路 + +# 9. Nginx 第三方模块 + +第三方模块是对nginx的功能扩展,第三方模块需要在编译安装Nginx的时候使用参数--add-module=PATH指定路径添加,有的模块是由公司的开发人员针对业务需求定制开发的,有的模块是开源爱好者开发好之后上传到github进行开源的模块,nginx支持第三方模块需要从源码重新编译支持. + +开源 **echo模块**:https://github.com/openresty/echo-nginx-module + +```shell +[root@localhost nginx-1.22.0]# vim /apps/nginx/conf.d/echo.conf +server { + listen 80; + server_name echo.test.com; + + location /main { + index index.html; + default_type text/html; + echo "hello world,main-->"; + echo $remote_addr; + echo_reset_timer; + # 将计时器开始时间重置为当前时间 + + echo_location /sub1; + echo_location /sub2; + echo "took $echo_timer_elapsed sec for total."; +} + location /sub1 { + echo_sleep 2; + echo hello; +} + location /sub2 { + echo_sleep 1; + echo world; +} +} + + +# 重新编译nginx,以前的配置文件不会丢失 +[root@localhost ~]# cd /usr/local/src +[root@localhost src]# wget https://github.com/openresty/echo-nginx-module/archive/refs/heads/master.zip +[root@localhost src]# unzip echo-nginx-module-master.zip +[root@localhost src]# mv echo-nginx-module-master echo-nginx-module +[root@localhost src]# cd nginx-1.22.0 +[root@localhost nginx-1.22.0]# ./configure --prefix=/apps/nginx \ +--user=nginx \ +--group=nginx \ +--with-http_ssl_module \ +--with-http_v2_module \ +--with-http_realip_module \ +--with-http_stub_status_module \ +--with-http_gzip_static_module \ +--with-pcre \ +--with-stream \ +--with-stream_ssl_module \ +--with-stream_realip_module \ +--add-module=/usr/local/src/echo-nginx-module +[root@localhost nginx-1.22.0]# make -j 2 && make install + + +# 进行访问测试 +[root@localhost ~]# echo "192.168.88.10 echo.test.com" >> /etc/hosts +[root@localhost ~]# curl echo.test.com/main +hello world,main--> +192.168.88.140 +hello +world +took 3.008 sec for total. + +``` + +# 10. 其他配置项 + +## 10.1 Alias + +**alias:** 定义路径别名,会把访问的路径重新定义到其指定的路径,文档映射的另一种机制,仅能用于location上下文,此指令使用较少 + +**案例:** + +```shell +[root@localhost conf]# vim /apps/nginx/conf.d/test.conf +server { + listen 80; + server_name a.test.com; + + location /about/ { + # 使用alias的时候uri后面加了斜杠,下面的路径也必须加,不然403错误 + alias /apps/nginx/html/about/; + # 当访问about的时候,会显示alias定义的/apps/nginx/html/about/里面的内容 + } +} + +# 重启Nginx并访问测试 +[root@localhost ~]# systemctl reload nginx +[root@localhost ~]# curl a.test.com/about/ +about +``` + + +## 10.2 自定义错误页面 + +定义错误页,以指定的响应状态码进行响应,可用位置: http, server, location, if in location + +```shell +erro_page code ... [=[response]] uri; +``` + +**官方示例:** + +```shell +[root@localhost static3]# vim /apps/nginx/conf/nginx.conf +server { +listen 80; +server_name www.example.com; +error_page 500 502 503 504 /error.html; +location = /error.html { + root html; +} +``` + +**范例:** + +```shell +[root@localhost static3]# vim /apps/nginx/conf.d/test.conf +server { + listen 80; + server_name a.test.com; + auth_basic "login password"; + auth_basic_user_file /apps/nginx/conf/.htpasswd; + error_page 404 /40x.html; + location = /1.jpg { + index index.html; + root /apps/nginx/static1; + } + location /40x.html{ + root /apps/nginx/html; + } +} +[root@localhost html]# echo "

404 not found

" > /apps/nginx/html/40x.html +``` + +**范例:** + +```shell +error_page 404 /index.html; +# 如果404,就跳转到主页 +``` + + + +## 10.3 长连接配置 + +```shell +[root@localhost html]# vim /apps/nginx/conf/nginx.conf + +keepalive_timeout timeout [header_timeout]; +# 设定保持连接超时时长,0表示禁止长连接,默认为75s,通常配置在http字段作为站点全局配置 +keepalive_requests number; +# 在一次长连接上所允许请求的资源的最大数量,默认为100次,建议适当调大,比如:500 +``` + +范例: + +```shell +[root@localhost html]# vim /apps/nginx/conf/nginx.conf +http { +... +keepalive_requests 3; +keepalive_timeout 65 60; +# 开启长连接后,返回客户端的会话保持时间为60s,单次长连接累计请求达到指定次数请求或65秒就会被断开,后面的60为发送给客户端应答报文头部中显示的超时时间设置为60s,如不设置客户端将不显示超时时间。 +keep-Alive:timeout=60; +# 浏览器收到的服务器返回的报文 +# 如果设置为keepalive_timeout 0表示关闭会话保持功能,将如下显示: +Connection:close # 浏览器收到的服务器返回的报文 +# 使用命令测试 +[root@localhost html]# telnet a.test.com 80 +Trying 192.168.88.10... +Connected to a.test.com. +Escape character is '^]'. +GET / HTTP/1.1 +HOST: a.test.com +``` + +## 10.4 作为下载服务器 + +ngx_http_autoindex_module模块处理以斜杠字符"/"结尾的请求,并生成目录列表可以做为下载服务配置使用 + +官方文档:https://nginx.org/en/docs/http/ngx_http_autoindex_module.html + +相关指令: + +```shell +autoindex on|off; +# 自动文件索引功能,默认off +autoindex_exact_size on|off; +# 计算文件确切大小(单位bytes),off显示大概大小(单位K、M),默认on +autoindex_localtime on|off; +# 显示本机时间而非GMT(格林威治)时间,默认off +autoindex_format html|xml|json|jsonp; +# 显示索引的页面分割,默认html +limit_rate rate; +# 限制响应客户端传输速率(除GET和HEAD以外的所有方法),单位B/s,既bytes/second,默认值0,表示无限制,此指令由ngx_http_core_module提供 +``` + +范例:实现下载站点 + +```shell +[root@localhost ~]# mkdir -p /apps/nginx/html/www/download +[root@localhost ~]# cd /apps/nginx/html/www/download +[root@localhost download]# touch f1 +[root@localhost download]# touch f2 +[root@localhost ~]# vim /apps/nginx/conf.d/www.conf +server { + listen 80; + server_name file.test.com; + location /download { + autoindex on; + # 自动索引功能,开启才会展示出文件列表 + autoindex_exact_size off; + # 关闭详细文件大小统计,让文件大小显示MB,GB单位,默认为b + autoindex_localtime on; + # on表示显示本机时间 + limit_rate 1024k; + # 限速,默认不限速 + root /apps/nginx/html/www; + } +} + + +# 修改windwos里面的hosts +192.168.88.140 file.test.com +测试http://file.test.com/download/ +``` + +## 10.5 作为上传服务器 + +```shell +client_max_body_size 1m; +# 设置允许客户端上传单个文件的最大值,默认值为1m,上传文件超过此值会出现413错误 +client_body_buffer_size size; +# 用户接受每个客户端请求报文的body部分的缓冲区大小;默认16k;超出此大小时,其将被暂存到磁盘上client_body_temp_path指定所定义的位置 +client_body_temp_path path [level1 [level 2 [level 3]]]; +# 设定存储客户端请求报文的body部分的临时存储路径及子目录结构和数量,目录名为16进制的数字,使用hash之后的值从后往前截取1位、2位、2位作为目录名 +# 1级目录占1位16进制,即2^4=16个目录 0-f +# 2级默认占2位16进制,即2^8=256个目录 00-ff +# 3级目录占2位16进制,即2^8=256个目录 +#因为如果所有上传的文件都放在一个文件夹下,不仅很容易文件名冲突,并且容易导致一个文件夹特别大。所以有必要创建子目录这里的level1,2,3如果有值就代表存在一级,二级,三级子目录。目录名是由数字进行命名的,所以这里的具体的值就是代表目录名的数字位数,比如如果如下设置 +#client_body_temp_path /spool/nginx/client_temp 3 2; +#可能创建的文件路径为 +#/spool/nginx/client_temp/702/45/00000123457 +[root@localhost ~]# md5sum f1 +d41d8cd98f00b204e9800998ecf8427e f1 +[root@localhost ~]# md5sum f2 +b026324c6904b2a9cb4b88d6d61c81d1 f2 +``` + +**配置示例:**但是上传操作不太好展示,因为需要从前端通过POST和GET来提交上传的内容 + +```bash +server { + listen 80; + server_name upload.test.com; + + location /upload { + # 上传目录 + root /apps/nginx/upload; + # 允许上传的文件大小 + client_max_body_size 2m; + # 处理上传请求 + location ~ ^/upload/(?P.*) { + limit_rate 1m; + limit_rate_after 10m; + client_body_temp_path /tmp/nginx_upload; + client_body_in_file_only clean; + + # 保存上传的文件 + alias /apps/nginx/upload/$file; + } + } + } + +``` + +## 10.6 Nginx 状态页 + +基于nginx模块ngx_http_stub_status_ module实现,在编译安装nginx的时候需要添加编译参数--with-http_stub_status_module,否则配置完成之后监测会是提示语法错误 + +注意:状态页显示的是整个服务器的状态,而非虚拟主机的状态 + +```shell +[root@localhost ~]# vim /apps/nginx/conf.d/www.conf +server { + listen 80; + server_name status.test.com; + + location /status { + stub_status on; + # 开启nginx状态页显示 + allow 192.168.88.0/24; + deny all; + } +} + +[root@localhost ~]# vim /etc/hosts +192.168.88.140 status.test.com +[root@localhost download]# curl http://status.test.com/status +Active connections: 1 +server accepts handled requests + 1 1 1 +Reading: 0 Writing: 1 Waiting: 0 +``` + +img-状态页 + +- Active connections: 2 表示Nginx正在处理的活动连接数2个。 +- server 2 表示Nginx启动到现在共处理了2个连接 +- accepts 2 表示Nginx启动到现在共成功创建2次握手 +- handled requests 1 表示总共处理了 1 次请求 +- Reading:Nginx 读取到客户端的 Header 信息数,数值越大,说明排队越长,性能不足 +- Writing:Nginx 返回给客户端 Header 信息数,数值越大,说明访问量越大 +- Waiting:Nginx 已经处理完正在等候下一次请求指令的驻留链接(开启keep-alive的情况下,这个值等于Active-(Reading+Writing)) + + +## 10.7 其他 + +- 对哪种浏览器禁用长连接 + +```shell +keepalive_disable none | browser ...; +``` + +- 限制客户端使用除了指定的请求方法之外的其他方法 + +```shell +limit_except method ... { ... }; # 仅用于location +method: GET, HEAD, POST, PUT, DELETE, MKCOL, COPY, MOVE, OPTIONS, PROPFIND, PROPPATCH, LOCK, UNLOCK, PATCH +[root@localhost conf.d]# vim /apps/nginx/conf.d/www.conf +location /download { + root /apps/nginx/html/www; + autoindex on; + autoindex_exact_size off; + autoindex_localtime on; + limit_except POST { #相当于只允许底下允许列表里的使用除了post外的其他方法 + allow 192.168.112.1; #只有浏览器可以使用除了post外的其他方法(get delete),其他人只能用post + deny all; +} +} +[root@localhost conf.d]# systemctl restart nginx + + +# 观察现象 +curl file.test.com/download +curl file.test.com/download -X POST -d "hello" +两次报错不一致,一个403一个404 + +cd /apps/nginx/html/www +touch f1 +touch f2 +vim /apps/nginx/conf.d/www.conf + +将 root /apps/nginx/html/www;改成 alias /apps/nginx/html/www; +浏览器测试 http://file.test.com/download/,能否访问到get请求 +``` + +- 是否启用asynchronous file I/O(AIO)功能,需要编译开启`--with-file-aio` +- 在读取文件的时候使用异步可以提高效率 + +```shell +aio on | off; +``` + +```shell +[root@localhost nginx-1.22.0]# cd /usr/local/src/nginx-1.22.0 +[root@localhost nginx-1.22.0]# ./configure --prefix=/apps/nginx \ +--user=nginx \ +--group=nginx \ +--with-http_ssl_module \ +--with-http_v2_module \ +--with-http_realip_module \ +--with-http_stub_status_module \ +--with-http_gzip_static_module \ +--with-pcre \ +--with-stream \ +--with-stream_ssl_module \ +--with-stream_realip_module \ +--with-file-aio +[root@localhost nginx-1.22.0]# make -j 2 && make install +[root@localhost nginx-1.22.0]# nginx -V +nginx version: slsnginx/1.22.0 +built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) +built with OpenSSL 1.0.2k-fips 26 Jan 2017 +TLS SNI support enabled +configure arguments: --prefix=/apps/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module --with-file-aio +#支持file-aio了 +[root@localhost nginx-1.22.0]# vim /apps/nginx/conf.d/www.conf +server { + listen 80; + server_name file.test.com; + aio on; +... +} +``` + +- directio是在写文件到磁盘的时候大小大于size的时候,直接写磁盘,而非写缓存 + +```shell +directio size | off; +``` + +- Nginx支持对磁盘中的文件进行缓存 + +```shell +open_file_cache on; # 是否缓存打开过的文件信息 +open_file_cache max=N [inactive=time]; +#nginx可以缓存以下三种信息: +#1. 文件元数据,文件的描述符,文件大小和最近一次的修改时间 +#2. 打开的目录结构 +#3. 没有找到的或者没有权限访问的文件相关信息 +max=N; # 可缓存的缓存项上限数量;达到上限后会使用LRU(Least recently used,最近最少使用)算法实现管理 +inactive=time; # 缓存项的非活动时长,在此处指定的时长内未被命中的或命中的次数少于open_file_cache_min_uses指令所指定的次数的缓存项即为非活动项,将被删除 +open_file_cache_valid time; #缓存项有效性的检查验证频率,默认值为60s +open_file_cache_errors on | off; #是否缓存查找时发生错误的文件一类的信息,默认值为off +open_file_cache_min_uses number; # open_file_cache指令的inactive参数指定的时长内,至少被命中此处指定的次数方可被归类为活动项,默认值为1 +``` + +范例: + +```shell +open_file_cache max=10000 inactive=60s; +open_file_cache_vaild 60s; +open_file_cache_min_uses 5; +open_file_cache_errors off; +``` + + +# 11. HTTPS 功能 + +Web网站的登录页面都是使用https加密传输的,加密数据以保障数据的安全,HTTPS能够加密信息,以免敏感信息被第三方获取,所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议,HTTPS其实是有两部分组成: HTTP + SSL/ TLS,也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会通过TLS进行加密,所以传输的数据都是加密后的数据。 + + +## 11.1 配置参数 + +nginx的https功能基于模块ngx_http_ssl_module实现,因此如果是编译安装的nginx要使用参数ngx_http_ssl_module开启ssI功能,但是作为nginx的核心功能,yum安装的nginx默认就是开启的,编译安装的nginx需要指定编译参数--with-http_ssl_module开启 + +官方文档:https://nginx.org/en/docs/http/ngx_http_ssl_module.html + +配置参数如下: + +```shell +ssl on | off; +listen 443 ssl; +# 为指定的虚拟主机配置是否启用ssl功能,此功能在1.15.0废弃,使用listen [ssl]替代。 +ssl_certificate /path/to/file; +# 当前虚拟主机使用使用的公钥文件,一般是crt文件 +ssl_certificate_key /path/to/file; +# 当前虚拟主机使用的私钥文件,一般是key文件 +ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2]; +# 支持ssl协议版本,早期为ssl现在是TSL,默认为后三个 +ssl_session_cache off | none | [builtin[:size]] [shared:name:size]; +# 配置ssl缓存 + off: + # 关闭缓存 + none: + # 通知客户端支持ssl session cache,但实际不支持 + builtin[:size]: + # 使用OpenSSL内建缓存,为每worker进程私有 + [shared:name:size]: + # 在各worker之间使用一个共享的缓存,需要定义一个缓存名称和缓存空间大小,一兆可以存储4000个会话信息,多个虚拟主机可以使用相同的缓存名称。 +ssl_session_timeout time; +# 客户端连接可以复用ssl session cache中缓存的有效时长,默认5m +``` + +## 11.2 自签名证书 + +- 生成ca证书 + +```shell +[root@localhost ~]# cd /apps/nginx +[root@localhost nginx]# mkdir certs && cd certs +[root@localhost certs]# openssl req -newkey rsa:4096 -nodes -sha256 -keyout ca.key -x509 -days 3650 -out ca.crt +``` + +- 生成证书请求文件 + +```shell +[root@localhost certs]# openssl req -newkey rsa:4096 -nodes -sha256 -keyout iproute.cn.key -out iproute.cn.csr +``` + +- 签发证书 + +```shell +[root@localhost certs]# openssl x509 -req -days 36500 -in iproute.cn.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out iproute.cn.crt +[root@localhost certs]# cat iproute.cn.crt ca.crt > iproute.crt +``` + +- 验证证书内容 + +```shell +[root@localhost certs]# openssl x509 -in iproute.cn.crt -noout -text +``` + +## 11.3 证书配置 + +```shell +[root@localhost certs]# vim /apps/nginx/conf.d/ssl.conf +server { + listen 80; + listen 443 ssl; + ssl_certificate /apps/nginx/certs/iproute.crt; + ssl_certificate_key /apps/nginx/certs/iproute.cn.key; + ssl_session_cache shared:sslcache:20m; + ssl_session_timeout 10m; + root /apps/nginx/html; +} +``` + +## 11.4 测试验证 + +image-https测试验证 + +# 12. Nginx 架构和进程 + +## 12.1 Nginx 架构 + +Nginx 采用了 master-worker 架构,由 master 进程负责通信和调度,worker 进程响应具体的请求 + +image-Nginx架构 + +## 12.2 Nginx 进程模型 + +Nginx 的进程模型是其高性能和高并发的核心,采用​​多进程架构​​(默认模式),主要由 ​​Master 进程​​和多个 ​​Worker 进程​​组成,结合​**​异步非阻塞 I/O 模型​**​实现高效请求处理 + +image-Nginx进程模型 + +**Master 进程:** + +- 对外接口:接收外部的操作(信号) +- 对内转发:根据外部的操作的不同,通过信号管理 worker +- 监控:监控 worker 进程的运行状态,worker 进程异常终止后,自动重启 worker 进程 +- 配置加载:读取 Nginx 配置文件并验证其有效性和正确性 +- 管理连接:建立、绑定和关闭 socket 连接 +- 按照配置生成、管理和结束工作进程 +- 接受外界指令,比如重启、升级及退出服务器等指令 +- 不中断服务,实现平滑升级,重启服务并应用新的配置 +- 开启日志文件,获取文件描述符 +- 不中断服务,实现平滑升级,升级失败进行回滚处理 + +**Worker 进程:** + +- 所有 Worker 进程都是平等的,接受处理客户的请求 +- 实际处理:网络请求,由 Worker 进程处理 +- Worker进程数量:在nginx.conf 中配置,一般设置为核心数,充分利用 CPU 资源,同时,避免进程数量过多,避免进程竞争 CPU 资源,增加上下文切换的损耗。 +- 将请求依次送入各个功能模块进行处理 +- I/O调用,获取响应数据 +- 与后端服务器通信,接收后端服务器的处理结果 +- 缓存数据,访问缓存索引,查询和调用缓存数据 +- 发送请求结果,响应客户的请求 +- 接收主程序指令,比如重启、升级和退出等 + +**工作细节** + +image-Nginx进程模型工作细节01 + +image-Nginx进程模型工作细节02
+ + +## 12.3 Nginx 进程间通信 + +**图示如下** + +image-Nginx进程间通信.png + +**信号**:Master 进程通过信号控制 Worker 进程的生命周期及配置更新,典型场景:SIGHUP(重新加载配置文件)、SIGTERM(优雅关闭 Worker 进程)、SIGUSR1(重新打开日志文件) +**管道**:Master 进程与 Worker 进程之间单向传递指令,Master 进程传递 Worker ID、指令、索引、文件描述符等等,Worker 进程监听管道事件并执行具体事项 +**共享内存**:Worker 进程间共享数据,典型场景:限流计数、缓存状态等等 + +## 12.4 HTTP 请求处理流程 + +**图示如下** + +image-HTTP请求包处理流程 + +# 13. 性能优化 + + +## 13.1 CPU性能相关 + +```bash +user nginx nginx; #启动Nginx工作进程的用户和组 +worker_processes [number | auto]; #启动Nginx工作进程的数量,一般设为和CPU核心数相同 +worker_cpu_affinity 0001 0010 0100 1000; # 将Nginx工作进程绑定到指定的CPU核心,默认Nginx是不进行进程绑定的,绑定并不是意味着当前nginx进程独占一核心CPU,但是可以保障此进程不会运行在其他核心上,这就极大减少了nginx的工作进程在不同的cpu核心上的来回跳转,减少了cpu对进程的资源分配与回收以及内存管理等,因此可以有效的提升nginx服务器的性能。 +CPU MASK:0001 0号CPU + 0010 1号CPU + 0100 2号CPU + 1000 3号CPU +[root@localhost ~]# watch -n.5 'ps axo pid,cmd,psr |grep nginx' +# 查看nginx进程对应的CPU + 6834 nginx: master process /apps 2 + 47621 nginx: worker process 0 + 47622 nginx: worker process 1 + 47623 nginx: worker process 2 + 47624 nginx: worker process 3 +[root@localhost ~]# while true;do ab -c 1000 -n 2000 http://127.0.0.1/;done +# 压力测试 要先yum -y install httpd-tools +``` + +**案例分析** + +修改 cpu 数量 + +1. 使用top命令查看虚拟机中cpu的核心数 + +![image-20240715215937058](05.Nginx/image-20240715215937058-1763003817741-67.png) + +2. 修改nginx.conf,在全局配置中增加对cpu的控制字段 + +```bash +[root@localhost nginx-1.22.0]# vim /apps/nginx/conf/nginx.conf +#user nobody; +worker_processes 4; +worker_cpu_affinity 0001 0010 0100 1000; + +# 这里配置的时候,注意自己虚拟机上cpu的数量,不要超过自己虚拟机cpu核心数 + +#重载配置 +[root@localhost nginx-1.22.0]# nginx -t +[root@localhost nginx-1.22.0]# systemctl restart nginx +``` + +3. 查看nginx进程所使用对应的cpu + +```bash +[root@localhost nginx-1.22.0]# watch -n.5 'ps axo pid,cmd,psr |grep nginx' +# 查看nginx进程对应的CPU + 6834 nginx: master process /apps 2 + 47621 nginx: worker process 0 + 47622 nginx: worker process 1 + 47623 nginx: worker process 2 + 47624 nginx: worker process 3 +``` + +## 13.2 工作优先级与文件并发数 + +```shell +worker_priority 0; #工作进程优先级(-20~19) +worker_rlimit_nofile 65536; #所有worker进程能打开的文件数量上线,包括:Nginx的所有连接(例如与代理服务器的连接等),而不仅仅是与客户端的连接,另一个考虑因素是实际的并发连接数不能超过系统级别的最大打开文件数的限制,最好与ulimit -n的值保持一致 +[root@localhost ~]# vim /etc/security/limits.conf +* soft nofile 102400 +* hard nofile 102400 + + +# 案例-修改优先级 +root@localhost nginx-1.22.0]# vim /apps/nginx/conf/nginx.conf +worker_priority -20; + +#重载配置 +[root@localhost nginx-1.22.0]# nginx -t +[root@localhost nginx-1.22.0]# systemctl restart nginx + +# 查看优先级 +[root@localhost ~]# watch -n.5 'ps axo pid,cmd,psr,nice |grep nginx' +Every 0.5s: ps axo pid,cmd,psr,nice |grep nginx Mon Jul 15 08:11:44 2024 + + 22950 nginx: master process /apps 1 0 + 22951 nginx: worker process 0 -20 + 22952 nginx: worker process 1 -20 + 22953 nginx: worker process 2 -20 + 22954 nginx: worker process 3 -20 + 23004 grep nginx 3 0 +``` + +## 13.3 其他优化配置 + +```shell +daemon off; # 前台运行nginx服务,用于测试、docker等环境 +master_process off|on; # 是否开启Nginx的master-worker工作模式,仅用于开发调试场景,默认为on +events { + worker_connections 65536; # 设置单个工作进程的最大并发连接数 + use epoll; # 使用epoll事件驱动,Nginx支持众多的事件驱动,比如:select、poll、epoll,只能设置在events模块中 + accept_mutex on; # on为同一时刻一个请求轮流由work进程处理,而防止被同时唤醒所有worker,避免多个睡眠进程被唤醒的设置,默认为off,新请求会唤醒所有worker进程,此过程也被称为"惊群",因此nginx刚安装完以后要进行适当的优化,建议设置为on + multi_accept on; # on时Nginx服务器的每个工作进程可以同时接受多个新的网络连接,此指令默认为off,即默认为一个工作进程只能一次接受一个新的网络连接,打开后几个同时接受多个,建议设置为on +} +``` + +- 默认配置并不支持高并发,在压力测试下会报错 + +```shell +[root@localhost ~]# yum install -y httpd-tools +[root@localhost ~]# while true;do ab -c 1000 -n 10000 http://127.0.0.1/;sleep 0.5;done + +[root@localhost ~]# tail /apps/nginx/logs/error.log +2021/05/24 12:35:53 [crit] 6828#0: *10996 open() "/apps/nginx/html/index.html" failed (24: Too many open files), client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "127.0.0.1" +2021/05/24 12:35:53 [crit] 6828#0: *10996 open() "/apps/nginx/html/50x.html" failed (24: Too many open files), client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "127.0.0.1" + +[root@localhost ~]# vim /etc/security/limits.conf +* - nproc 100000 +[root@localhost ~]# vim /apps/nginx/conf/nginx.conf +worker_rlimit_nofile 65536; +events { + worker_connections 10240; +} +[root@localhost ~]# systemctl restart nginx +``` + + +# 14. LNMP 架构概述 + +LNMP是一套技术的组合,L=Linux、N=Nginx、M=MySQL、P=PHP + +## 14.1 如何工作 + +- 首先 nginx 服务是不能处理动态请求,那么当用户发起动态请求时,nginx 无法处理 +- 当用户发起 http 请求,请求会被 nginx 处理,如果是静态资源请求 nginx 则直接返回,如果是动态请求 nginx 则通过 fastcgi 协议转交给后端的 PHP 程序处理 + +![image-LNMP工作流程01](05.Nginx/LNMP工作流程01.png) + +## 14.2 工作流程 + +![image-LNMP工作流程02](05.Nginx/LNMP工作流程02.png) + +1. 用户通过 http 协议发起请求,请求会先抵达 LNM P架构中的nginx +2. nginx 会根据用户的请求进行location规则匹配 +3. location 如果匹配到请求是静态,则由 nginx 读取本地直接返回 +4. location 如果匹配到请求是动态,则由 nginx 将请求转发给 fastcgi 协议 +5. fastcgi 收到请求交给 php-fpm 管理进程,php-fpm 管理进程接收到后会调用具体的工作进程 wrapper +6. wrapper 进程会调用 PHP 程序进行解析,如果只是解析代码,php 直接返回 +7. 如果有查询数据库操作,则由 php 连接数据库(用户 密码 ip)发起查询的操作 + +## 14.3 部署安装 + + +```shell +# 安装 nginx:如果编译安装可以跳过 +[root@localhost ~]# yum install nginx -y +# 安装 php 全家桶 +[root@localhost ~]# yum install php* -y +# 更新 php-fmp listen 配置项 +[root@localhost ~]# vim /etc/php-fpm.d/www.conf +...... +;listen = /run/php-fpm/www.sock +listen = 9000 +...... +[root@localhost ~]# systemctl enable --now php-fpm +# 安装 marinedb 数据库 +[root@localhost ~]# yum install -y mariadb-server mariadb +[root@localhost ~]# systemctl enable --now mariadb +# 设置数据库 root 用户密码 +[root@localhost ~]# mysqladmin password '123456' +[root@localhost ~]# mysql -uroot -p123456 -e "show databases;" ++--------------------+ +| Database | ++--------------------+ +| information_schema | +| mysql | +| performance_schema | ++--------------------+ +# 准备测试页面 +[root@localhost ~]# mkdir -pv /data/nginx/lnmp +[root@localhost ~]# cat << EOF > /data/nginx/lnmp/index.php + +EOF +# 测试 index.php 运行正确 +[root@localhost ~]# php /data/nginx/lnmp/index.php +# 添加 nginx vhost 配置文件:将 php 请求转给 php-fpm 处理 +[root@localhost ~]# cat << EOF > /etc/nginx/conf.d/php.conf +server { + listen 80; + server_name php.iproot.cn; + root /data/nginx/lnmp; + + location / { + index index.php index.html; + } + location ~ \.php$ { + fastcgi_pass 127.0.0.1:9000; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } +} +EOF +[root@localhost ~]# nginx -s reload +# 测试验证 +[root@localhost ~]# curl -I -H 'Hostname: php.iproute.cn' 172.16.175.129 +HTTP/1.1 200 OK +Server: nginx/1.20.1 + +``` + +## 14.4 相关配置 + +**设置 fastcgi 服务器地址** + +```shell +Syntax: fastcgi_pass address; +Default:- +Context:location,if in location +# 语法示例:可以指定为域名或IP地址以及端口 +fastcgi_pass location:9000; +fastcgi_pass unix:/tmp/fastcgi.socket; +``` + +**设置 fastcgi 默认首页文件** + +```shell +Syntax: fastcgi_index name; +Default:- +Context:http,server,location +``` + +**设置 fastcgi_param 传递变量** + +```shell +Syntax: fastcgi_param parameter value [if_not_empty]; +Default:- +Context:http,server,location +#语法示例 +fastcgi_index index.php; +fastcgi_param SCRIPT_FILENAME /code$fastcgi_script_name; +``` + +# 15. 数据库管理应用 + +为了方便的使用数据库,我们可以安装数据库图形化管理工具 phpmyadmin + +## 15.1 安装部署 + +```shell +# 为数据库管理工具创建虚拟主机 +[root@localhost ~]# vim /apps/nginx/conf.d/mysql.conf +server { + listen 80; + server_name mysql.iproot.cn; + root /code/phpmyadmin; + + location / { + index index.php index.html; + } + + location ~ \.php$ { + fastcgi_pass 127.0.0.1:9000; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } +} +[root@localhost ~]# nginx -t +# 下载phpmyadmin源码 +[root@localhost ~]# cd /code/ +[root@localhost code]# wget https://files.phpmyadmin.net/phpMyAdmin/5.1.1/phpMyAdmin-5.1.1-all-languages.zip +# 解压软件包,并且重命名 +[root@localhost phpmyadmin]# unzip phpMyAdmin-5.1.1-all-languages.zip +[root@localhost phpmyadmin]# mv phpMyAdmin-5.1.1-all-languages phpmyadmin +# 添加session文件夹权限 +[root@localhost phpmyadmin]# chown nginx.nginx /var/lib/php/session +``` + +下面浏览器访问 phpmyadmin 页面,同样记得更改 windows下的 hosts 文件 + +image-phpmyadmin登录页面 + +输入数据库用户名 `root` 和密码 `123456` 就可以进入图形化数据库管理页面了 + +image-phpmyadmin管理页面 + + +# 16. 博客系统 + +## 16.1 部署虚拟主机 + +```bash +# 为博客创建虚拟主机 +[root@localhost ~]# vim /apps/nginx/conf.d/typecho.conf +server { + listen 80; + server_name blog.iproot.cn; + root /code/typecho; + index index.php index.html; + + location ~ .*\.php(\/.*)*$ { + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } +} + +# 检查nginx配置文件,并且重启 +[root@localhost ~]# nginx -t +[root@localhost ~]# systemctl restart nginx + +# 创建typecho目录 +[root@localhost ~]# mkdir /code/typecho +[root@localhost ~]# cd /code/typecho + +[root@localhost ~]# wget https://github.com/typecho/typecho/releases/latest/download/typecho.zip + +# 解压源码 +[root@localhost ~]# unzip typecho.zip +``` + +## 16.2 创建数据库 + +点击数据库,输入数据库名之后,就可以点击创建 + +image-创建typecho数据库 + +## 16.3 安装博客系统 + +下面就可以开始进入网站安装的部分了,访问博客系统页面 + +image-安装博客系统 + +赋予网站根目录下usr/uploads目录权限 + +```shell +[root@localhost typecho]# chmod a+w usr/uploads/ +``` + +继续下一步,填写数据库密码和网站后台管理员密码 + +image-设置网站后台管理员密码 + +点击开始安装之后,会出现了如下页面,这个是因为php的用户是nginx用户,而/code/typecho文件夹是root用户的,所以这个网站根本没有权限保存数据相关的配置到文件夹中 + +image-初始化配置 + +方法一:直接将typecho文件夹赋予nginx权限 + +方法二:手动去帮助网站创建网站没有权限的配置文件,下面将会演示方法二 + +直接在/code/typecho下创建`config.inc.php`文件,然后将网页提示内容写入这个文件中 + +```shell +[root@localhost typecho]# vim /code/typecho/config.inc.php +复制网页上的内容进去 +``` + +配置文件创建完成之后,可以点击`创建完毕,继续安装>>` + +下面是安装成功的页面 + +image-20250207200242332 + +image-20250207200300668 + +## 16.4 切换主题 + +默认的主题如下,界面比较的简洁,我们可以给这个网站替换主题,也可以借此加深熟悉我们对Linux命令行的熟练程度 + +image-20250118221202018 + +第三方主题商店:https://www.typechx.com/ + +我们尝试更换这个主题 + +image-20250118221344667 + +选择模板下载 + +image-20250118221414487 + +然后在打开的github仓库中下载ZIP压缩包 + +image-20250118221502963 + +将下载好的主题压缩包上传到博客主题的目录`/code/typecho/usr/themes` + +image-20250118221634385 + +然后解压主题包,并且将名称改为简单一点的 + +```bash +[root@localhost themes]# unzip Typecho-Butterfly-main.zip +[root@localhost themes]# ls +Typecho-Butterfly-main Typecho-Butterfly-main.zip default +[root@localhost themes]# mv Typecho-Butterfly-main butterfly +[root@localhost themes]# rm -rf Typecho-Butterfly-main.zip +``` + +然后登录到博客后台,在设置里更换主题 + +image-20250118221843976 + +然后回到博客首页刷新一下,就可以看到新的主题已经应用了~ + +![image-20250118221920089](05.Nginx/image-20250118221920089.png) + +会有一些图片资源的丢失,稍微了解一点前端知识,就可以将其完善好了。不懂前端的同学,可以去找一些简单一点的主题。 + +image-20250118221958932 + +# 17. 网盘服务 + +## 17.1 部署虚拟主机 + +```bash +# 为网盘创建虚拟主机 +[root@localhost themes]# vim /apps/nginx/conf.d/kod.conf +server { + listen 80; + server_name kod.iproot.cn; + root /code/kod; + index index.php index.html; + + location ~ .*\.php(\/.*)*$ { + root /code/kod; + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } +} + +# 检查nginx配置文件,并且重启 +[root@localhost ~]# nginx -t +[root@localhost ~]# systemctl restart nginx + +# 下载源代码然后解压重命名 +[root@localhost ~]# mkdir /code/kod +[root@localhost ~]# cd /code/kod +[root@localhost kod]# wget https://static.kodcloud.com/update/download/kodbox.1.23.zip + +# 解压源码 +[root@localhost kod]# unzip kodbox.1.23.zip +``` + +## 17.2 创建数据库 + +image-20250207200725398 + +## 17.3 安装网盘系统 + +浏览器访问此站点,我们发现目录权限,这个比较重要 + +image-20250207201018894 + +```bash +# 设置权限 +[root@localhost kod]# chown -R nginx.nginx /code/kod +``` + +添加完成之后,刷新页面,可以看到所有条件都已经符合,就可以直接点击下一步了 + +image-20250207201110920 + +填写数据库密码和数据库名 + +image-20250207201132377 + +设置系统密码 + +image-20250207201248174 + +完成网站安装 + +![image-20211106114747284](05.Nginx/image-20211106114747284-1763003737062-61.png) + +image-20250207201334236 + +下面根据自己的喜好,进行简单的设置就可以正常使用啦! + +image-20250207201405074 + +我们也可以直接在这个上面编辑Linux上的文件,比如我们之前创建的php文件 + +![image-20211106114944358](05.Nginx/image-20211106114944358-1763003744379-64.png) + +# 18. 友情提示 + +如何各位同学不是搭建在自己虚拟机上的,是去租用阿里云或者腾讯云,直接搭建,并且购买域名,就可以让自己的网站在互联网上永远在线 \ No newline at end of file diff --git a/02.企业服务/05.Nginx/HTTP请求包处理流程.png b/02.企业服务/05.Nginx/HTTP请求包处理流程.png new file mode 100644 index 0000000..fc763d8 Binary files /dev/null and b/02.企业服务/05.Nginx/HTTP请求包处理流程.png differ diff --git a/02.企业服务/05.Nginx/LNMP工作流程01.png b/02.企业服务/05.Nginx/LNMP工作流程01.png new file mode 100644 index 0000000..8b20541 Binary files /dev/null and b/02.企业服务/05.Nginx/LNMP工作流程01.png differ diff --git a/02.企业服务/05.Nginx/LNMP工作流程02.png b/02.企业服务/05.Nginx/LNMP工作流程02.png new file mode 100644 index 0000000..f51590e Binary files /dev/null and b/02.企业服务/05.Nginx/LNMP工作流程02.png differ diff --git a/02.企业服务/05.Nginx/Nginx架构.png b/02.企业服务/05.Nginx/Nginx架构.png new file mode 100644 index 0000000..6c203a4 Binary files /dev/null and b/02.企业服务/05.Nginx/Nginx架构.png differ diff --git a/02.企业服务/05.Nginx/Nginx进程模型.png b/02.企业服务/05.Nginx/Nginx进程模型.png new file mode 100644 index 0000000..eaa8139 Binary files /dev/null and b/02.企业服务/05.Nginx/Nginx进程模型.png differ diff --git a/02.企业服务/05.Nginx/Nginx进程模型工作细节01.png b/02.企业服务/05.Nginx/Nginx进程模型工作细节01.png new file mode 100644 index 0000000..b6f3c09 Binary files /dev/null and b/02.企业服务/05.Nginx/Nginx进程模型工作细节01.png differ diff --git a/02.企业服务/05.Nginx/Nginx进程间通信.png b/02.企业服务/05.Nginx/Nginx进程间通信.png new file mode 100644 index 0000000..caff134 Binary files /dev/null and b/02.企业服务/05.Nginx/Nginx进程间通信.png differ diff --git a/02.企业服务/05.Nginx/https测试验证.png b/02.企业服务/05.Nginx/https测试验证.png new file mode 100644 index 0000000..a3d0e2c Binary files /dev/null and b/02.企业服务/05.Nginx/https测试验证.png differ diff --git a/02.企业服务/05.Nginx/image-20211106114747284-1763003737062-61.png b/02.企业服务/05.Nginx/image-20211106114747284-1763003737062-61.png new file mode 100644 index 0000000..5846400 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20211106114747284-1763003737062-61.png differ diff --git a/02.企业服务/05.Nginx/image-20211106114944358-1763003744379-64.png b/02.企业服务/05.Nginx/image-20211106114944358-1763003744379-64.png new file mode 100644 index 0000000..5695ba8 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20211106114944358-1763003744379-64.png differ diff --git a/02.企业服务/05.Nginx/image-20240715215937058-1763003817741-67.png b/02.企业服务/05.Nginx/image-20240715215937058-1763003817741-67.png new file mode 100644 index 0000000..49dfd03 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20240715215937058-1763003817741-67.png differ diff --git a/02.企业服务/05.Nginx/image-20250118221202018.png b/02.企业服务/05.Nginx/image-20250118221202018.png new file mode 100644 index 0000000..9c04f14 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250118221202018.png differ diff --git a/02.企业服务/05.Nginx/image-20250118221344667.png b/02.企业服务/05.Nginx/image-20250118221344667.png new file mode 100644 index 0000000..cd68b8d Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250118221344667.png differ diff --git a/02.企业服务/05.Nginx/image-20250118221414487.png b/02.企业服务/05.Nginx/image-20250118221414487.png new file mode 100644 index 0000000..39da26f Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250118221414487.png differ diff --git a/02.企业服务/05.Nginx/image-20250118221502963.png b/02.企业服务/05.Nginx/image-20250118221502963.png new file mode 100644 index 0000000..4b009f7 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250118221502963.png differ diff --git a/02.企业服务/05.Nginx/image-20250118221634385.png b/02.企业服务/05.Nginx/image-20250118221634385.png new file mode 100644 index 0000000..4da4c1a Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250118221634385.png differ diff --git a/02.企业服务/05.Nginx/image-20250118221843976.png b/02.企业服务/05.Nginx/image-20250118221843976.png new file mode 100644 index 0000000..e1d1783 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250118221843976.png differ diff --git a/02.企业服务/05.Nginx/image-20250118221920089.png b/02.企业服务/05.Nginx/image-20250118221920089.png new file mode 100644 index 0000000..6d7a0e1 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250118221920089.png differ diff --git a/02.企业服务/05.Nginx/image-20250118221958932.png b/02.企业服务/05.Nginx/image-20250118221958932.png new file mode 100644 index 0000000..2ae3a7f Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250118221958932.png differ diff --git a/02.企业服务/05.Nginx/image-20250207200242332.png b/02.企业服务/05.Nginx/image-20250207200242332.png new file mode 100644 index 0000000..5246ae7 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250207200242332.png differ diff --git a/02.企业服务/05.Nginx/image-20250207200300668.png b/02.企业服务/05.Nginx/image-20250207200300668.png new file mode 100644 index 0000000..1ba2823 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250207200300668.png differ diff --git a/02.企业服务/05.Nginx/image-20250207200725398.png b/02.企业服务/05.Nginx/image-20250207200725398.png new file mode 100644 index 0000000..1a2292a Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250207200725398.png differ diff --git a/02.企业服务/05.Nginx/image-20250207201018894.png b/02.企业服务/05.Nginx/image-20250207201018894.png new file mode 100644 index 0000000..162c1d1 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250207201018894.png differ diff --git a/02.企业服务/05.Nginx/image-20250207201110920.png b/02.企业服务/05.Nginx/image-20250207201110920.png new file mode 100644 index 0000000..004c9d6 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250207201110920.png differ diff --git a/02.企业服务/05.Nginx/image-20250207201132377.png b/02.企业服务/05.Nginx/image-20250207201132377.png new file mode 100644 index 0000000..3df894c Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250207201132377.png differ diff --git a/02.企业服务/05.Nginx/image-20250207201248174.png b/02.企业服务/05.Nginx/image-20250207201248174.png new file mode 100644 index 0000000..18dd206 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250207201248174.png differ diff --git a/02.企业服务/05.Nginx/image-20250207201334236.png b/02.企业服务/05.Nginx/image-20250207201334236.png new file mode 100644 index 0000000..68a0f75 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250207201334236.png differ diff --git a/02.企业服务/05.Nginx/image-20250207201405074.png b/02.企业服务/05.Nginx/image-20250207201405074.png new file mode 100644 index 0000000..c49d877 Binary files /dev/null and b/02.企业服务/05.Nginx/image-20250207201405074.png differ diff --git a/02.企业服务/05.Nginx/phpmyadmin登录页面.png b/02.企业服务/05.Nginx/phpmyadmin登录页面.png new file mode 100644 index 0000000..5a4eaf4 Binary files /dev/null and b/02.企业服务/05.Nginx/phpmyadmin登录页面.png differ diff --git a/02.企业服务/05.Nginx/phpmyadmin管理页面.png b/02.企业服务/05.Nginx/phpmyadmin管理页面.png new file mode 100644 index 0000000..c4ffb34 Binary files /dev/null and b/02.企业服务/05.Nginx/phpmyadmin管理页面.png differ diff --git a/02.企业服务/05.Nginx/创建typecho数据库.png b/02.企业服务/05.Nginx/创建typecho数据库.png new file mode 100644 index 0000000..179c21b Binary files /dev/null and b/02.企业服务/05.Nginx/创建typecho数据库.png differ diff --git a/02.企业服务/05.Nginx/初始化配置.png b/02.企业服务/05.Nginx/初始化配置.png new file mode 100644 index 0000000..ae3ab79 Binary files /dev/null and b/02.企业服务/05.Nginx/初始化配置.png differ diff --git a/02.企业服务/05.Nginx/安装博客系统.png b/02.企业服务/05.Nginx/安装博客系统.png new file mode 100644 index 0000000..34c0916 Binary files /dev/null and b/02.企业服务/05.Nginx/安装博客系统.png differ diff --git a/02.企业服务/05.Nginx/状态页.png b/02.企业服务/05.Nginx/状态页.png new file mode 100644 index 0000000..cce2a92 Binary files /dev/null and b/02.企业服务/05.Nginx/状态页.png differ diff --git a/02.企业服务/05.Nginx/设置网站后台管理员密码.png b/02.企业服务/05.Nginx/设置网站后台管理员密码.png new file mode 100644 index 0000000..2cf1e7b Binary files /dev/null and b/02.企业服务/05.Nginx/设置网站后台管理员密码.png differ diff --git a/02.企业服务/06.反向代理.md b/02.企业服务/06.反向代理.md new file mode 100644 index 0000000..915dc08 --- /dev/null +++ b/02.企业服务/06.反向代理.md @@ -0,0 +1,242 @@ +# 1. Nginx代理服务 + +- 代理一词往往并不陌生, 该服务我们常常用到如(代理理财、代理租房、代理收货等等) + +![image-20210711132943764](06.反向代理/image-20210711132943764.png) + +- 在没有代理模式的情况下,客户端和Nginx服务端,都是客户端直接请求服务端,服务端直接响应客户端。 + +![image-20210711133242593](06.反向代理/image-20210711133242593.png) + +- 那么在互联网请求里面,客户端往往无法直接向服务端发起请求,那么就需要用到代理服务,来实现客户端和服务通信 + +![image-20210711141653692](06.反向代理/image-20210711141653692-1626395774911-1739587022188226.png) + +# 2. Nginx代理服务常见模式 + +- Nginx作为代理服务,按照应用场景模式进行总结,代理分为正向代理、反向代理 + +![image-20210711142214493](06.反向代理/image-20210711142214493.png) + +- 正向代理与反向代理的区别 + + - 区别在于形式上服务的”对象”不一样 + - 正向代理代理的对象是客户端,为客户端服务 + - 反向代理代理的对象是服务端,为服务端服务 + +# 3. Nginx代理服务支持协议 + +- Nginx作为代理服务,可支持的代理协议非常的多 + +![image-20210711143359615](06.反向代理/image-20210711143359615.png) + +- 如果将Nginx作为反向代理服务,常常会用到如下几种代理协议 + +![image-20210711143720275](06.反向代理/image-20210711143720275.png) + +# 4. Nginx反向代理配置语法 + +- 代理配置语法 + +```bash +Syntax: proxy_pass URL; +Default: — +Context: location, if in location, limit_except + +http://localhost:8000/uri/ +http://192.168.56.11:8000/uri/ +http://unix:/tmp/backend.socket:/uri/ +``` + +- url跳转修改返回Location[不常用] + +```bash +Syntax: proxy_redirect default; +proxy_redirect off;proxy_redirect redirect replacement; +Default: proxy_redirect default; +Context: http, server, location +``` + +- 添加发往后端服务器的请求头信息 + +```bash +Syntax: proxy_set_header field value; +Default: proxy_set_header Host $proxy_host; + proxy_set_header Connection close; +Context: http, server, location + +# 用户请求的时候HOST的值是www.test.com, 那么代理服务会像后端传递请求的还是www.test.com +proxy_set_header Host $http_host; +# 将$remote_addr的值放进变量X-Real-IP中,$remote_addr的值为客户端的ip +proxy_set_header X-Real-IP $remote_addr; +# 客户端通过代理服务访问后端服务, 后端服务通过该变量会记录真实客户端地址 +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +``` + +- 代理到后端的TCP连接、响应、返回等超时时间 + +```bash +//nginx代理与后端服务器连接超时时间(代理连接超时) +Syntax: proxy_connect_timeout time; +Default: proxy_connect_timeout 60s; +Context: http, server, location + +//nginx代理等待后端服务器的响应时间 +Syntax: proxy_read_timeout time; +Default: proxy_read_timeout 60s; +Context: http, server, location + +//后端服务器数据回传给nginx代理超时时间 +Syntax: proxy_send_timeout time; +Default: proxy_send_timeout 60s; +Context: http, server, location +``` + +- proxy_buffer代理缓冲区 + +```bash +//nignx会把后端返回的内容先放到缓冲区当中,然后再返回给客户端,边收边传, 不是全部接收完再传给客户端 +Syntax: proxy_buffering on | off; +Default: proxy_buffering on; +Context: http, server, location + +//设置nginx代理保存用户头信息的缓冲区大小 +Syntax: proxy_buffer_size size; +Default: proxy_buffer_size 4k|8k; +Context: http, server, location + +//proxy_buffers 缓冲区 +Syntax: proxy_buffers number size; +Default: proxy_buffers 8 4k|8k; +Context: http, server, location +``` + +- 常用优化配置 + - Proxy代理网站常用优化配置如下,将配置写入新文件,调用时使用include引用即可 + +```bash +[root@Nginx ~]# vim /etc/nginx/proxy_params +proxy_set_header Host $http_host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + +proxy_connect_timeout 30; +proxy_send_timeout 60; +proxy_read_timeout 60; + +proxy_buffering on; +proxy_buffer_size 32k; +proxy_buffers 4 128k; +``` + +- 重复使用配置 + - 代理配置location时调用方便后续多个Location重复使用 + +```bash +location / { + proxy_pass http://127.0.0.1:8080; + include proxy_params; +} +``` + +# 5. Nginx反向代理场景实践 + +- Nginx反向代理配置实例 + +![image-20210711151756044](06.反向代理/image-20210711151756044.png) + +- web01服务器,配置一个网站,监听在8080 + +```bash +[root@web01 ~]# cd /etc/nginx/conf.d/ +[root@web01 conf.d]# vim web.conf +server { + listen 8080; + server_name localhost; + + location / { + root /code/8080; + index index.html; + allow all; + } +} +[root@web01 conf.d]# nginx -t +nginx: the configuration file /etc/nginx/nginx.conf syntax is ok +nginx: configuration file /etc/nginx/nginx.conf test is successful +[root@web01 conf.d]# systemctl restart nginx +[root@web01 ~]# mkdir -p /code/8080 +[root@web01 ~]# echo "listening 8080 ..." > /code/8080/index.html +``` + +- proxy代理服务,配置监听80端口,使能够通过代理服务器访问到后端的192.168.175.20的8080端口站点内容 + +```bash +[root@proxy ~]# cd /etc/nginx/conf.d/ +[root@proxy conf.d]# vim proxy_web_node1.conf +server { + listen 80; + server_name proxy.test.com; + + location / { + proxy_pass http://192.168.175.20:8080; + } +} +[root@proxy conf.d]# nginx -t +[root@proxy conf.d]# systemctl restart nginx +``` + +- 存在的问题,通过抓包可以看到客户端是使用域名对网站进行访问的,但是代理却是使用的IP地址加端口号 +- 当访问80端口的时候,没有域名的情况下,默认会去找排在最上面的那个配置文件。 +- 所以我们需要解决这个问题,保留住最开始的请求头部信息。 + +![image-20210711154444696](06.反向代理/image-20210711154444696.png) + +![image-20210711154508713](06.反向代理/image-20210711154508713.png) + +- 修改配置文件,使用`proxy_set_header`模块 + +```bash +[root@proxy conf.d]# vim proxy_web_node1.conf +server { + listen 80; + server_name proxy.test.com; + + location / { + proxy_pass http://192.168.175.20:8080; + proxy_set_header Host $http_host; + } +} +``` + +- 使用http1.1协议 + +```bash +server { + listen 80; + server_name proxy.test.com; + + location / { + proxy_pass http://192.168.175.20:8080; + proxy_set_header Host $http_host; + proxy_http_version 1.1; + } +} +``` + +- 在生产环境中,我们必须要记录客户端的来源IP,如果所有的访问日志,全都来源于代理,那么我们根本不知道都有哪些地区的用户访问了我们什么页面。 + - 还需要使用`proxy_set_header` + +```bash +server { + listen 80; + server_name proxy.test.com; + + location / { + proxy_pass http://192.168.175.20:8080; + proxy_set_header Host $http_host; + proxy_http_version 1.1; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` + diff --git a/02.企业服务/06.反向代理/image-20210711132943764.png b/02.企业服务/06.反向代理/image-20210711132943764.png new file mode 100644 index 0000000..3fd22d4 Binary files /dev/null and b/02.企业服务/06.反向代理/image-20210711132943764.png differ diff --git a/02.企业服务/06.反向代理/image-20210711133242593.png b/02.企业服务/06.反向代理/image-20210711133242593.png new file mode 100644 index 0000000..6bc3209 Binary files /dev/null and b/02.企业服务/06.反向代理/image-20210711133242593.png differ diff --git a/02.企业服务/06.反向代理/image-20210711141653692-1626395774911-1739587022188226.png b/02.企业服务/06.反向代理/image-20210711141653692-1626395774911-1739587022188226.png new file mode 100644 index 0000000..6d63a52 Binary files /dev/null and b/02.企业服务/06.反向代理/image-20210711141653692-1626395774911-1739587022188226.png differ diff --git a/02.企业服务/06.反向代理/image-20210711142214493.png b/02.企业服务/06.反向代理/image-20210711142214493.png new file mode 100644 index 0000000..3816d7e Binary files /dev/null and b/02.企业服务/06.反向代理/image-20210711142214493.png differ diff --git a/02.企业服务/06.反向代理/image-20210711143359615.png b/02.企业服务/06.反向代理/image-20210711143359615.png new file mode 100644 index 0000000..1f9ff8e Binary files /dev/null and b/02.企业服务/06.反向代理/image-20210711143359615.png differ diff --git a/02.企业服务/06.反向代理/image-20210711143720275.png b/02.企业服务/06.反向代理/image-20210711143720275.png new file mode 100644 index 0000000..43fffd9 Binary files /dev/null and b/02.企业服务/06.反向代理/image-20210711143720275.png differ diff --git a/02.企业服务/06.反向代理/image-20210711151756044.png b/02.企业服务/06.反向代理/image-20210711151756044.png new file mode 100644 index 0000000..ccb8114 Binary files /dev/null and b/02.企业服务/06.反向代理/image-20210711151756044.png differ diff --git a/02.企业服务/06.反向代理/image-20210711154444696.png b/02.企业服务/06.反向代理/image-20210711154444696.png new file mode 100644 index 0000000..6840d7c Binary files /dev/null and b/02.企业服务/06.反向代理/image-20210711154444696.png differ diff --git a/02.企业服务/06.反向代理/image-20210711154508713.png b/02.企业服务/06.反向代理/image-20210711154508713.png new file mode 100644 index 0000000..1f90981 Binary files /dev/null and b/02.企业服务/06.反向代理/image-20210711154508713.png differ diff --git a/02.企业服务/07.负载均衡.md b/02.企业服务/07.负载均衡.md new file mode 100644 index 0000000..dfa6d50 --- /dev/null +++ b/02.企业服务/07.负载均衡.md @@ -0,0 +1,2869 @@ +# 1. Nginx负载均衡基本概述 + +## 1.1 为什么要使用负载均衡 + +- 当我们的Web服务器直接面向用户,往往要承载大量并发请求,单台服务器难以负荷,我使用多台Web服务器组成集群,前端使用Nginx负载均衡,将请求分散的打到我们的后端服务器集群中,实现负载的分发。那么会大大提升系统的吞吐率、请求性能、高容灾 + +![image-20210711155724840](07.负载均衡/image-20210711155724840.png) + +- 往往我们接触的最多的是`SLB(Server Load Balance)`负载均衡,实现最多的也是`SLB`、那么`SLB`它的调度节点和服务节点通常是在一个地域里面。那么它在这个小的逻辑地域里面决定了他对部分服务的实时性、响应性是非常好的。 +- 所以说当海量用户请求过来以后,它同样是请求调度节点,调度节点将用户的请求转发给后端对应的服务节点,服务节点处理完请求后在转发给调度节点,调度节点最后响应给用户节点。这样也能实现一个均衡的作用,那么**Nginx**则是一个典型的`SLB` + +- 负载均衡的叫法有很多 + - 负载均衡 + - Load Balance + - LB +- 公有云中叫法 + - SLB 阿里云负载均衡 + - QLB 青云负载均衡 + - CLB 腾讯云负载均衡 + - ULB ucloud负载均衡 +- 常见的负载均衡的软件 + - Nginx + - Haproxy + - LVS + +## 1.2 四层负载均衡 + +​ 所谓四层负载均衡指的是`OSI`七层模型中的传输层,那么传输层**Nginx**已经能支持**TCP/IP**的控制,所以只需要对客户端的请求进行**TCP/IP**协议的包转发就可以实现负载均衡,那么它的好处是性能非常快、只需要底层进行应用处理,而不需要进行一些复杂的逻辑。 + +![image-20210711160121942](07.负载均衡/image-20210711160121942.png) + +## 1.3 七层负载均衡 + +​ 七层负载均衡它是在应用层,那么它可以完成很多应用方面的协议请求,比如我们说的**http**应用的负载均衡,它可以实现**http**信息的改写、头信息的改写、安全应用规则控制、**URL**匹配规则控制、以及转发、**rewrite**等等的规则,所以在应用层的服务里面,我们可以做的内容就更多,那么**Nginx**则是一个典型的七层负载均衡`SLB` + +![image-20210711160208468](07.负载均衡/image-20210711160208468.png) + +## 1.4 四层负载均衡与七层负载均衡区别 + +​ 四层负载均衡数据包在底层就进行了分发,而七层负载均衡数据包则是在最顶层进行分发、由此可以看出,七层负载均衡效率没有四负载均衡高。 + +​ 但七层负载均衡更贴近于服务,如:**http**协议就是七层协议,我们可以用**Nginx**可以作会话保持,**URL**路径规则匹配、**head**头改写等等,这些是四层负载均衡无法实现的。 + +​ **注意:四层负载均衡不识别域名,七层负载均衡识别域名** + +# 2. Nginx负载均衡配置场景 + +​ Nginx要实现负载均衡需要用到`proxy_pass`代理模块配置. + +​ Nginx负载均衡与**Nginx**代理不同地方在于,**Nginx**的一个`location`仅能代理一台服务器,而**Nginx**负载均衡则是将客户端请求代理转发至一组**upstream**虚拟服务池. + +![image-20210711160627756](07.负载均衡/image-20210711160627756.png) + +## 2.1 Nginx upstream虚拟配置语法 + +```bash +Syntax: upstream name { ... } +Default: - +Context: http + +#upstream例 +upstream backend { + server backend1.example.com weight=5; + server backend2.example.com:8080; + server unix:/tmp/backend3; + server backup1.example.com:8080 backup; +} +server { + location / { + proxy_pass http://backend; + } +} +``` + +## 2.2 环境准备 + +| 角色 | 地址 | 主机名 | +| :---- | :---------------------- | :----- | +| LB01 | ens33:**192.168.88.10** | lb01 | +| web01 | ens33:**192.168.88.20** | web01 | +| web02 | ens33:**192.168.88.30** | web02 | + +## 2.3 Web01服务器上配置nginx + +```bash +[root@web01 ~]# cd /etc/nginx/conf.d/ +[root@web01 conf.d]# vim node.conf +server { + listen 80; + server_name node.test.com; + location / { + root /code/node; + index index.html; + } +} +[root@web01 conf.d]# mkdir -p /code/node +[root@web01 conf.d]# echo "web01 ..." > /code/node/index.html +[root@web01 conf.d]# systemctl restart nginx +# 关闭防火墙和SElinux +[root@web02 conf.d]# setenforce 0 +[root@web02 conf.d]# systemctl stop firewalld +``` + +## 2.4 Web02服务器上配置nginx + +```bash +[root@web02 ~]# cd /etc/nginx/conf.d/ +[root@web02 conf.d]# vim node.conf +server { + listen 80; + server_name node.test.com; + location / { + root /code/node; + index index.html; + } +} +[root@web02 conf.d]# mkdir -p /code/node +[root@web02 conf.d]# echo "web02 ..." > /code/node/index.html +[root@web02 conf.d]# systemctl restart nginx +# 关闭防火墙和SElinux +[root@web02 conf.d]# setenforce 0 +[root@web02 conf.d]# systemctl stop firewalld +``` + +## 2.5 配置Nginx负载均衡 + +```bash +[root@lb01 ~]# cd /etc/nginx/conf.d/ + +[root@lb01 conf.d]# vim /etc/nginx/proxy_params +proxy_set_header Host $http_host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + +proxy_connect_timeout 30; +proxy_send_timeout 60; +proxy_read_timeout 60; + +proxy_buffering on; +proxy_buffer_size 32k; +proxy_buffers 4 128k; + +[root@lb01 conf.d]# vim node_proxy.conf +upstream node { + server 192.168.88.20:80; + server 192.168.88.30:80; +} +server { + listen 80; + server_name node.test.com; + + location / { + proxy_pass http://node; + include proxy_params; + } +} + +[root@lb01 conf.d]# nginx -t +[root@lb01 conf.d]# systemctl restart nginx + +# 配置hosts解析 +[root@lb01 conf.d]# vim /etc/hosts +192.168.88.10 node.test.com +``` + +windows访问,更改windows下的hosts文件 + +- 打开浏览器访问:http://node.test.com + +![image-20210711201349945](07.负载均衡/image-20210711201349945.png) + +![image-20210711201400731](07.负载均衡/image-20210711201400731.png) + +## 2.6 负载均衡常见典型故障 + +​ 如果后台服务连接超时,Nginx是本身是有机制的,如果出现一个节点down掉的时候,Nginx会更据你具体负载均衡的设置,将请求转移到其他的节点上,但是,如果后台服务连接没有down掉,但是返回错误异常码了如:504、502、500,这个时候你需要加一个负载均衡的设置,如下:proxy_next_upstream http_500 | http_502 | http_503 | http_504 |http_404;意思是,当其中一台返回错误码404,500...等错误时,可以分配到下一台服务器程序继续处理,提高平台访问成功率。 + +```bash +server { + listen 80; + server_name node.test.com; + + location / { + proxy_pass http://node; + proxy_next_upstream error timeout http_500 http_502 http_503 http_504 http_404; + } +} +``` + +# 3. Nginx负载均衡调度算法 + +| **调度算法** | **概述** | +| :----------- | :----------------------------------------------------------- | +| 轮询 | 按时间顺序逐一分配到不同的后端服务器(默认) | +| weight | 加权轮询,weight值越大,分配到的访问几率越高 | +| ip_hash | 每个请求按访问IP的hash结果分配,这样来自同一IP的固定访问一个后端服务器 | +| url_hash | 按照访问URL的hash结果来分配请求,是每个URL定向到同一个后端服务器 | +| least_conn | 最少链接数,那个机器链接数少就分发 | + +## 3.1 Nginx负载均衡[rr]轮询具体配置 + +```bash +upstream load_pass { + server 192.168.88.10:80; + server 192.168.88.20:80; +} +``` + +## 3.2 Nginx负载均衡[wrr]权重轮询具体配置 + +```bash +upstream load_pass { + server 192.168.88.10:80 weight=5; + server 192.168.88.20:80; +} +``` + +## 3.3 Nginx负载均衡ip_hash + +- **ip_hash**: 根据客户端IP地址进行哈希,确保来自同一IP的客户端总是被转发到同一台后端服务器 +- 具体配置不能和weight一起使用。 + +```bash +#如果客户端都走相同代理, 会导致某一台服务器连接过多 +upstream load_pass { + ip_hash; + server 192.168.88.10:80; + server 192.168.88.20:80; +} +``` + +# 4. Nginx负载均衡后端状态 + +- 后端Web服务器在前端Nginx负载均衡调度中的状态 + +| **状态** | **概述** | +| :----------- | :-------------------------------- | +| down | 当前的server暂时不参与负载均衡 | +| backup | 预留的备份服务器 | +| max_fails | 允许请求失败的次数 | +| fail_timeout | 经过max_fails失败后, 服务暂停时间 | +| max_conns | 限制最大的接收连接数 | + +## 4.1 测试down状态测试该Server不参与负载均衡的调度 + +```bash +upstream load_pass { + #不参与任何调度, 一般用于停机维护 + server 192.168.88.10:80 down; +} +``` + +## 4.2 backup以及down状态 + +```bash +upstream load_pass { + server 192.168.88.10:80 backup; + server 192.168.88.20:80 max_fails=1 fail_timeout=10s; +} + +location / { + proxy_pass http://load_pass; + include proxy_params; +} +``` + +## 4.3 max_fails失败次数和fail_timeout多少时间内失败多少次则标记down + +```bash +upstream load_pass { + server 192.168.88.10:80; + server 192.168.88.20:80 max_fails=2 fail_timeout=10s; +} +``` + +## 4.4 max_conns最大TCP连接数 + +```bash +upstream load_pass { + server 192.168.88.10:80; + server 192.168.88.20:80 max_conns=1; +} +``` + +# 5. Nginx负载均衡健康检查 + +- 在Nginx官方模块提供的模块中,没有对负载均衡后端节点的健康检查模块,但可以使用第三方模块。 + `nginx_upstream_check_module`来检测后端服务的健康状态。 +- 第三方模块项目地址:https://github.com/yaoweibin/nginx_upstream_check_module + +- 安装依赖包 + +```bash +[root@lb01 ~]# yum install -y gcc glibc gcc-c++ pcre-devel openssl-devel patch +``` + +- 下载nginx源码包以及nginx_upstream_check模块第三方模块 + +```bash +[root@lb01 ~]# wget http://nginx.org/download/nginx-1.20.1.tar.gz +[root@lb01 ~]# wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/master.zip +``` + +- 解压nginx源码包以及第三方模块 + +```bash +[root@lb01 ~]# tar xf nginx-1.20.1.tar.gz +[root@lb01 ~]# unzip master.zip +``` + +- 进入nginx目录,打补丁(nginx的版本是1.14补丁就选择1.14的,p1代表在nginx目录,p0是不在nginx目录) + +```bash +[root@lb01 ~]# cd nginx-1.20.1 +[root@lb01 nginx-1.14.2]# patch -p1 <../nginx_upstream_check_module-master/check_1.20.1+.patch +[root@lb01 nginx-1.14.2]# ./configure --prefix=/etc/nginx \ +--sbin-path=/usr/sbin/nginx \ +--modules-path=/usr/lib64/nginx/modules \ +--conf-path=/etc/nginx/nginx.conf \ +--error-log-path=/var/log/nginx/error.log \ +--http-log-path=/var/log/nginx/access.log \ +--pid-path=/var/run/nginx.pid \ +--lock-path=/var/run/nginx.lock \ +--http-client-body-temp-path=/var/cache/nginx/client_temp \ +--http-proxy-temp-path=/var/cache/nginx/proxy_temp \ +--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \ +--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \ +--http-scgi-temp-path=/var/cache/nginx/scgi_temp \ +--user=nginx --group=nginx --with-compat \ +--with-file-aio --with-threads --with-http_addition_module \ +--with-http_auth_request_module --with-http_dav_module \ +--with-http_flv_module --with-http_gunzip_module \ +--with-http_gzip_static_module --with-http_mp4_module \ +--with-http_random_index_module --with-http_realip_module \ +--with-http_secure_link_module --with-http_slice_module \ +--with-http_ssl_module --with-http_stub_status_module \ +--with-http_sub_module --with-http_v2_module --with-mail \ +--with-mail_ssl_module --with-stream --with-stream_realip_module \ +--with-stream_ssl_module --with-stream_ssl_preread_module \ +--add-module=/root/nginx_upstream_check_module-master \ +--with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' \ +--with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' +[root@lb01 nginx-1.14.2]# make -j 2 && make install +[root@lb01 nginx-1.14.2]# nginx -v +nginx version: nginx/1.14.2 +[root@lb01 nginx-1.14.2]# mkdir -p /var/cache/nginx/client_temp +``` + +- 在已有的负载均衡上增加健康检查的功能 + +```bash +[root@lb01 conf.d]# vim node_proxy.conf +upstream node { + server 192.168.88.10:80 max_fails=2 fail_timeout=10s; + server 192.168.88.20:80 max_fails=2 fail_timeout=10s; + check interval=3000 rise=2 fall=3 timeout=1000 type=tcp; + #interval 检测间隔时间,单位为毫秒 + #rise 表示请求2次正常,标记此后端的状态为up + #fall 表示请求3次失败,标记此后端的状态为down + #type 类型为tcp + #timeout 超时时间,单位为毫秒 +} + +server { + listen 80; + server_name node.test.com; + location / { + proxy_pass http://node; + include proxy_params; + } + + location /upstream_check { + check_status; + } +} +``` + +# 6. Nginx负载均衡会话保持 + +- 在使用负载均衡的时候会遇到会话保持的问题,可通过如下方式进行解决。 + - 使用nginx的ip_hash,根据客户端的IP,将请求分配到对应的IP上 + - 基于服务端的session会话共享(NFS,MySQL,memcache,redis,file) +- 在解决负载均衡会话问题,我们需要了解session和cookie的区别。 + - 浏览器端存的是cookie每次浏览器发请求到服务端时,报文头是会自动添加cookie信息的。 + - 服务端会查询用户的cookie作为key去存储里找对应的value(session) + - 同一域名下的网站的cookie都是一样的,所以无论几台服务器,无论请求分配到哪一台服务器上同一用户的cookie是不变的。也就是说cookie对应的session也是唯一的。所以,这里只要保证多台业务服务器访问同一个共享存储服务器(NFS,MySQL,memcache,redis,file)就行了。 + +## 6.1 会话保持配置 + +测试网站选择我们之前搭建过的phpmyadmin,所以我们先再web01和web02上部署phpmyadmin + +- 配置php环境(web01和web02都要) + +```bash +# 导入英格提供的php源,这个比较快 +vim /etc/yum.repos.d/eagle.repo +[eagle] +name=Eagle's lab +baseurl=http://file.eagleslab.com:8889/%E8%AF%BE%E7%A8%8B%E7%9B%B8%E5%85%B3%E8%BD%AF%E4%BB%B6/%E4%BA%91%E8%AE%A1%E7%AE%97%E8%AF%BE%E7%A8%8B/Centos7%E6%BA%90/ +gpgcheck=0 +enabled=1 + +# 安装php环境,php所需的组件比较多,我们可以一次性安装全面了 +yum -y install php71w php71w-cli php71w-common php71w-devel php71w-embedded php71w-gd php71w-mcrypt php71w-mbstring php71w-pdo php71w-xml php71w-fpm php71w-mysqlnd php71w-opcache php71w-pecl-memcached php71w-pecl-redis php71w-pecl-mongodb + +# 配置php-fpm用于与nginx的运行用户保持一致 +sed -i '/^user/c user = nginx' /etc/php-fpm.d/www.conf +sed -i '/^group/c group = nginx' /etc/php-fpm.d/www.conf + +# 启动php-fpm +systemctl start php-fpm +``` + + + +- 配置Nginx(web01和web02都要) + +```bash +[root@web01 ~]# vim /etc/nginx/conf.d/php.conf +server { + listen 80; + server_name php.test.com; + root /code/phpMyAdmin-4.8.4-all-languages; + + location / { + index index.php index.html; + } + + location ~ \.php$ { + fastcgi_pass 127.0.0.1:9000; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } +} +[root@web01 ~]# nginx -t +[root@web01 ~]# systemctl restart nginx +``` + +- 安装phpmyadmin (web01和web02都要) + +```bash +[root@web01 ~]# cd /code +[root@web01 code]# wget https://files.phpmyadmin.net/phpMyAdmin/4.8.4/phpMyAdmin-4.8.4-all-languages.zip +[root@web01 code]# unzip phpMyAdmin-4.8.4-all-languages.zip +``` + +- 配置phpmyadmin连接远程的数据库,这个数据库可以用代理服务器安装后做测试(web01和web02都要) + +```bash +[root@web01 code]# cd phpMyAdmin-4.8.4-all-languages/ +[root@web01 phpMyAdmin-4.8.4-all-languages]# cp config.sample.inc.php config.inc.php +[root@web01 phpMyAdmin-4.8.4-all-languages]# vim config.inc.php +$cfg['Servers'][$i]['host'] = '192.168.88.10'; +``` + +- 配置权限(web01和web02都要) + +```bash +[root@web01 ~]# chown -R nginx.nginx /var/lib/php/ +``` + +- 在lb01上部署mariadb数据库,提供phpmyadmin访问 + +```bash +# 安装mariadb数据库软件 +yum install mariadb-server mariadb -y + +# 启动数据库并且设置开机自启动 +systemctl start mariadb +systemctl enable mariadb + +# 设置mariadb的密码 +mysqladmin password '123456' + +# 验证数据库是否工作正常 +mysql -uroot -p123456 -e "show databases;" ++--------------------+ +| Database | ++--------------------+ +| information_schema | +| mysql | +| performance_schema | +| test | ++--------------------+ + +# 创建访问用户 +grant all privileges on *.* to root@'192.168.88.%' identified by '123456' with grant option; +flush privileges; +``` + +## 6.2 使用浏览器访问页面,获取cookie信息 + +![image-20210711212248164](07.负载均衡/image-20210711212248164.png) + +![image-20210711212943898](07.负载均衡/image-20210711212943898.png) + +- 查看服务器上的session + +```bash +[root@web01 ~]# ll /var/lib/php/session/ +总用量 4 +-rw------- 1 www www 2625 7月 11 20:08 sess_aa3bdfab93197f49bbfddb15cb41a595 +``` + +- 将web01上配置好的phpmyadmin以及nginx的配置文件推送到web02主机上 + +```bash +[root@web01 code]# scp -rp phpMyAdmin-4.8.4-all-languages root@192.168.88.30:/code/ +[root@web01 code]# scp /etc/nginx/conf.d/php.conf root@192.168.88.30:/etc/nginx/conf.d/ +``` + +- 在web02上重启Nginx服务 + +```bash +[root@web02 ~]# systemctl restart nginx +``` + +- 在web02上配置权限 + +```bash +[root@web02 ~]# chown -R nginx. /var/lib/php/ +``` + +- 接入负载均衡 + +```bash +[root@lb01 ~]# vim /etc/nginx/conf.d/proxy_php.com.conf +upstream php { + server 192.168.88.20:80; + server 192.168.88.30:80; +} +server { + listen 80; + server_name php.test.com; + location / { + proxy_pass http://php; + include proxy_params; + } +} + +[root@lb01 ~]# nginx -t +[root@lb01 ~]# systemctl restart nginx +``` + +- 使用负载均衡的轮询功能之后,会发现,如果将session保存在本地文件的话,永远都登录不上去。 + +![image-20210711222302235](07.负载均衡/image-20210711222302235.png) + +## 6.3 使用redis解决会话登录问题 + +- 安装redis内存数据库 + +```bash +[root@lb01 ~]# yum -y install redis +``` + +- 配置redis监听在0.0.0.0网段上 + +```bash +[root@lb01 ~]# sed -i '/^bind/c bind 127.0.0.1 0.0.0.0' /etc/redis.conf +``` + +- 启动redis + +```bash +[root@lb01 ~]# systemctl start redis +[root@lb01 ~]# systemctl enable redis +``` + +- php配置session连接redis + +```bash +[root@web01 ~]# vim /etc/php.ini +session.save_handler = redis +session.save_path = "tcp://192.168.88.10:6379" +;session.save_path = "tcp://192.168.88.10:6379?auth=centos" #如果redis存在密码,则使用该方式 +session.auto_start = 1 + +[root@web01 ~]# vim /etc/php-fpm.d/www.conf +#注释php-fpm.d/www.conf里面的两条内容,否则session内容会一直写入/var/lib/php/session目录中 +;php_value[session.save_handler] = files +;php_value[session.save_path] = /var/lib/php/session +``` + +- 重启php-fpm + +```bash +[root@web01 ~]# systemctl restart php-fpm +``` + +- 将web01上配置好的文件推送到web02 + +```bash +[root@web01 ~]# scp /etc/php.ini root@192.168.88.30:/etc/php.ini +[root@web01 ~]# scp /etc/php-fpm.d/www.conf root@192.168.88.30:/etc/php-fpm.d/www.conf +``` + +- 在web02上重启php-fpm + +```bash +[root@web02 ~]# systemctl restart php-fpm +``` + +- redis查看数据 + +```bash +[root@lb01 ~]# redis-cli +127.0.0.1:6379> keys * +1) "PHPREDIS_SESSION:7e772fe37b1488069e3c54e419df7eda" +``` + +![image-20210711223529884](07.负载均衡/image-20210711223529884.png) + +# 7. Nginx四层负载均衡概述 + +​ 四层负载均衡是基于传输层协议包来封装的(如:TCP/IP),那我们前面使用到的七层是指的应用层,他的组装在四层的基础之上,无论四层还是七层都是指的OSI网络模型。 + +​ 四层+七层来做负载均衡,四层可以保证七层的负载均衡的高可用性;如:nginx就无法保证自己的服务高可用,需要依赖LVS或者keepalive。 + +​ 如:tcp协议的负载均衡,有些请求是TCP协议的(mysql、ssh),或者说这些请求只需要使用四层进行端口的转发就可以了,所以使用四层负载均衡。 + +## 7.1 四层+七层构建大规模集群架构使用场 + +![image-20210711225314039](07.负载均衡/image-20210711225314039.png) + +## 7.2 四层负载均衡总结 + +- 四层负载均衡仅能转发TCP/IP协议、UDP协议、通常用来转发端口,如:tcp/22、udp/53; +- 四层负载均衡可以用来解决七层负载均衡端口限制问题;(七层负载均衡最大使用65535个端口号) +- 四层负载均衡可以解决七层负载均衡高可用问题;(多台后端七层负载均衡能同事的使用) +- 四层的转发效率比七层的高得多,但仅支持tcp/ip协议,不支持http和https协议; +- 通常大并发场景通常会选择使用在七层负载前面增加四层负载均衡。 + +## 7.3 Nginx四层负载均衡场景实践 + +Nginx如何配置四层负载均衡 + +1、通过访问负载均衡的5555端口,实际是后端的web01的22端口在提供服务; + +2、通过访问负载均衡的6666端口,实际是后端的mysql的3306端口在提供服务。 + +- 创建存放四层负载均衡配置文件的目录 + +```bash +[root@lb01 ~]# yum install nginx-mod-stream +[root@lb01 ~]# vim /etc/nginx/nginx.conf +include /etc/nginx/conf.c/*.conf; +#include /etc/nginx/conf.d/*.conf; +[root@lb01 ~]# mkdir /etc/nginx/conf.c +``` + +- 配置四层负载均衡 + +```bash +[root@lb01 ~]# vim /etc/nginx/conf.c/lb_domain.conf +stream { + upstream lb { + server 192.168.88.20:80 weight=5 max_fails=3 fail_timeout=30s; + server 192.168.88.30:80 weight=5 max_fails=3 fail_timeout=30s; + } + + server { + listen 80; + proxy_connect_timeout 3s; + proxy_timeout 3s; + proxy_pass lb; + } +} +``` + +- 四层负载均衡开启日志 + - 四层负载均衡是没有access的日志的,因为在nginx.conf的配置中,access的日志格式是配置在http下的,而四层负载均衡配置实在http以外的 + - 如果需要日志则需要配置在stream下面 + +```bash +[root@lb01 ~]# cd /etc/nginx/conf.c/ +[root@lb01 conf.c]# vim lb_domain.conf +stream { + log_format proxy '$remote_addr $remote_port - [$time_local] $status $protocol ' + '"$upstream_addr" "$upstream_bytes_sent" "$upstream_connect_time"' ; + access_log /var/log/nginx/proxy.log proxy; + upstream lb { + server 192.168.88.20:80 weight=5 max_fails=3 fail_timeout=30s; + server 192.168.88.30:80 weight=5 max_fails=3 fail_timeout=30s; + } + + server { + listen 80; + proxy_connect_timeout 3s; + proxy_timeout 3s; + proxy_pass lb; + } +} + +[root@lb01 conf.c]# nginx -t +[root@lb01 conf.c]# systemctl restart nginx +``` + +- 浏览器访问域名或者IP,查看日志 + +```bash +[root@lb01 conf.c]# tail -f /var/log/nginx/proxy.log +192.168.88.1 55295 - [11/Jul/2021:23:27:09 +0800] 200 TCP "192.168.88.20:80" "0" "0.001" +192.168.88.1 64066 - [11/Jul/2021:23:27:13 +0800] 200 TCP "192.168.88.30:80" "0" "0.000" +192.168.88.1 58528 - [11/Jul/2021:23:27:16 +0800] 200 TCP "192.168.88.20:80" "0" "0.001" +192.168.88.1 65099 - [11/Jul/2021:23:27:20 +0800] 200 TCP "192.168.88.30:80" "0" "0.000" +192.168.88.1 55919 - [11/Jul/2021:23:27:20 +0800] 200 TCP "192.168.88.20:80" "6460" "0.001" +``` + +## 7.4 Nginx四层负载均衡端口转发 + +- 使用nginx四层负载均衡实现tcp的转发 + +```bash +请求负载均衡 5555 ---> 192.168.88.20:22; +请求负载均衡 6666 ---> 192.168.88.30:3306; +``` + +- 配置nginx四层负载均衡实现tcp的转发 + +```bash +[root@lb01 ~]# vim /etc/nginx/conf.c/lb_domain.conf +stream { + log_format proxy '$remote_addr $remote_port - [$time_local] $status $protocol ' + '"$upstream_addr" "$upstream_bytes_sent" "$upstream_connect_time"' ; + access_log /var/log/nginx/proxy.log proxy; + +#定义转发ssh的22端口 + upstream ssh { + server 192.168.88.20:22; + } +#定义转发mysql的3306端口 + upstream mysql { + server 192.168.88.30:3306; + } + server { + listen 5555; + proxy_connect_timeout 3s; + proxy_timeout 300s; + proxy_pass ssh; + } + + server { + listen 6666; + proxy_connect_timeout 3s; + proxy_timeout 3s; + proxy_pass mysql; + } +} + +[root@lb01 ~]# nginx -t +[root@lb01 ~]# setenforce 0 +[root@lb01 ~]# systemctl restart nginx +``` + +- 测试 + - 略 + +# 8. Keepalived 高可用基本概述 + +​ 一般是指2台机器启动着完全相同的业务系统,当有一台机器down机了,另外一台服务器就能快速的接管,对于访问的用户是无感知的。 + +​ 硬件通常使用 F5 + +​ 软件通常使用 keepalived + +​ keepalived软件是基于VRRP协议实现的,VRRP虚拟路由冗余协议,主要用于解决单点故障问题 + +## 8.1 VRRP原理 + +- VRRP协议是一种容错的主备模式的协议,保证当主机的下一跳路由出现故障时,由另一台路由器来代替出现故障的路由器进行工作,通过VRRP可以在网络发生故障时透明的进行设备切换而不影响主机之间的数据通信。 +- 虚拟路由器:VRRP组中所有的路由器,拥有虚拟的IP+MAC(00-00-5e-00-01-VRID)地址 +- 主路由器:虚拟路由器内部通常只有一台物理路由器对外提供服务,主路由器是由选举算法产生,对外提供各种网络功能。 +- 备份路由器:VRRP组中除主路由器之外的所有路由器,不对外提供任何服务,只接受主路由的通告,当主路由器挂掉之后,重新进行选举算法接替master路由器。 +- 选举机制 + - 优先级 + - 抢占模式下,一旦有优先级高的路由器加入,即成为Master + - 非抢占模式下,只要Master不挂掉,优先级高的路由器只能等待 +- 三种状态 + - Initialize状态:系统启动后进入initialize状态 + - Master状态 + - Backup状态 + +# 9. Keepalived高可用安装配置 + +## 9.1 安装keepalived + +```bash +[root@lb01 ~]# yum install -y keepalived +[root@lb02 ~]# yum install -y keepalived +``` + +## 9.2 配置master + +- 找到配置文件 + +```bash +[root@lb02 ~]# rpm -qc keepalived +/etc/keepalived/keepalived.conf +/etc/sysconfig/keepalived +``` + +- 修改配置 + +```bash +[root@lb01 ~]# vim /etc/keepalived/keepalived.conf +global_defs { #全局配置 + router_id lb01 #标识身份->名称 +} + +vrrp_instance VI_1 { + state MASTER #标识角色状态 + interface ens33 #网卡绑定接口 + virtual_router_id 50 #虚拟路由id + priority 150 #优先级 + advert_int 1 #监测间隔时间 + #use_vmac #使用虚拟mac地址,因为路由问题,可能导致原本的IP不可用 + authentication { #认证 + auth_type PASS #认证方式 + auth_pass 1111 #认证密码 + } + virtual_ipaddress { + 192.168.88.100 #虚拟的VIP地址 + } +} +``` + +## 9.3 配置backup + +```bash +[root@lb02 ~]# vim /etc/keepalived/keepalived.conf +global_defs { + router_id lb02 +} + +vrrp_instance VI_1 { + state BACKUP + interface ens33 + virtual_router_id 50 + priority 100 + advert_int 1 + authentication { + auth_type PASS + auth_pass 1111 + } + virtual_ipaddress { + 192.168.88.100 + } +} +``` + +## 9.4 对比master与Backup的keepalived配置区别 + +| Keepalived配置区别 | Master节点配置 | Backup节点配置 | +| :------------------- | :------------- | :------------- | +| route_id(唯一标识) | router_id lb01 | router_id lb02 | +| state(角色状态) | state MASTER | state BACKUP | +| priority(竞选优先级) | priority 150 | priority 100 | + +## 9.5 启动Master和Backup节点的keepalived + +```bash +[root@lb01 ~]# systemctl start keepalived +[root@lb01 ~]# systemctl enable keepalived + +[root@lb02 ~]# systemctl start keepalived +[root@lb02 ~]# systemctl enable keepalived +``` + +# 10. 高可用keepalived抢占式与非抢占式 + +- 由于节点1的优先级高于节点2,所以VIP在节点1上面 + +```bash +[root@lb01 ~]# ip addr |grep 192.168.88.100 + inet 192.168.88.100/32 scope global ens33 +``` + +- 关闭节点1的keepalived + +```bash +[root@lb01 ~]# systemctl stop keepalived +``` + +- 节点2联系不上节点1,主动接管VIP + +```bash +[root@lb02 ~]# ip addr |grep 192.168.88.100 + inet 192.168.88.100/32 scope global ens33 +``` + +- 此时重新启动Master上的keepalived,会发现VIP被强行抢占 + +```bash +[root@lb01 ~]# systemctl start keepalived +[root@lb01 ~]# ip addr |grep 192.168.88.100 + inet 192.168.88.100/32 scope global ens33 +``` + +- 配置非抢占式 + + - 两个节点的state都必须配置为BACKUP + - 两个节点都必须加上配置 nopreempt + - 其中一个节点的优先级必须要高于另外一个节点的优先级。 + - 两台服务器都角色状态启用nopreempt后,必须修改角色状态统一为BACKUP,唯一的区分就是优先级。 + +```bash +Master配置 + vrrp_instance VI_1 { + state BACKUP + priority 150 + nopreempt + } + +Backup配置 + vrrp_instance VI_1 { + state BACKUP + priority 100 + nopreempt + } +``` + +- 通过windows的arp去验证,是否会切换MAC地址 + +```bash +# 当前master在lb01上 +[root@lb01 ~]# ip addr |grep 192.168.88.100 + inet 192.168.88.100/32 scope global ens33 + +# windows查看mac地址 +C:\Users\Aaron>arp -a |findstr 192.168.88.100 + 192.168.88.100 00-0c-29-bb-9a-bb 动态 +``` + +- 关闭lb01的keepalive + +```bash +[root@lb01 ~]# systemctl stop keepalived +``` + +- lb02接管vIP + +```bash +[root@lb02 ~]# ip addr |grep 192.168.88.100 + inet 192.168.88.100/32 scope global ens33 +``` + +- 再次查看windows的mac地址 + +```bash +C:\Users\Aaron>arp -a |findstr 192.168.88.100 + 192.168.88.100 00-0c-29-bb-9a-bb 动态 +``` + +# 11. 高可用keepalived故障脑裂 + +​ 由于某些原因,导致两台keepalived高可用服务器在指定时间内,无法检测到对方的心跳,各自取得资源及服务的所有权,而此时的两台高可用服务器又都还活着。 + +## 11.1 脑裂故障原因 + +- 服务器网线松动等网络故障 +- 服务器硬件故障发生损坏现象而崩溃 +- 主备都开启firewalld防火墙 + +## 11.2 脑裂故障现象 + +- 正常情况下backup以监听为主,所以抓包会看到只有master在发送vrrp的数据包 + +![image-20210712035352209](07.负载均衡/image-20210712035352209.png) + +- 打开设备的防火墙,查看抓包情况,可以看到两台设备认为自己是master + +![image-20210712035539886](07.负载均衡/image-20210712035539886.png) + +```bash +[root@lb01 ~]# ip a |grep 192.168.88.100 + inet 192.168.88.100/24 scope global secondary ens33 + +[root@lb02 ~]# ip a |grep 192.168.88.100 + inet 192.168.88.100/24 scope global secondary ens33 +``` + +## 11.3 解决脑裂故障方案 + +- 如果发生脑裂,则随机kill掉一台即可 +- 在backup上编写检测脚本, 测试如果能ping通master并且backup节点还有vIP的话则认为产生了脑裂 + +```bash +[root@lb02 ~]# vim check_split_brain.sh +#!/bin/sh +vip=192.168.88.100 +lb01_ip=192.168.88.10 +while true;do + ping -c 2 $lb01_ip &>/dev/null + if [ $? -eq 0 -a `ip add|grep "$vip"|wc -l` -eq 1 ];then + echo "ha is split brain.warning." + else + echo "ha is ok" + fi +sleep 5 +done +``` + +# 12. 高可用keepalived与nginx + +​ Nginx默认监听在所有的IP地址上,VIP会飘到一台节点上,相当于那台nginx多了VIP这么一个网卡,所以可以访问到nginx所在机器 + +​ 但是.....如果nginx宕机,会导致用户请求失败,但是keepalived没有挂掉不会进行切换,所以需要编写一个脚本检测Nginx的存活状态,如果不存活则kill掉keepalived + +```bash +[root@lb01 ~]# vim check_web.sh +#!/bin/sh +nginxpid=$(ps -C nginx --no-header|wc -l) + +#1.判断Nginx是否存活,如果不存活则尝试启动Nginx +if [ $nginxpid -eq 0 ];then + systemctl start nginx + sleep 3 + #2.等待3秒后再次获取一次Nginx状态 + nginxpid=$(ps -C nginx --no-header|wc -l) + #3.再次进行判断, 如Nginx还不存活则停止Keepalived,让地址进行漂移,并退出脚本 + if [ $nginxpid -eq 0 ];then + systemctl stop keepalived + fi +fi +``` + +## 12.1 在lb01主机的keepalived配置文件中调用此脚本 + +```bash +[root@lb01 ~]# vim /etc/keepalived/keepalived.conf +global_defs { + router_id lb01 +} + +#每5秒执行一次脚本,脚本执行内容不能超过5秒,否则会中断再次重新执行脚本 +vrrp_script check_web { + script "/root/check_web.sh" + interval 5 +} + +vrrp_instance VI_1 { + state MASTER + interface ens33 + virtual_router_id 50 + priority 150 + advert_int 1 + authentication { + auth_type PASS + auth_pass 1111 + } + virtual_ipaddress { + 192.168.88.100 + } +} + +#调用并运行脚本 +track_script { + check_web +} +``` + +# 13. 集群和分布式 + +系统性能扩展方式: + +- Scale UP:垂直扩展,向上扩展,增强,性能更强的计算机运行同样的服务,成本高。 +- Scale Out:水平扩展,向外扩展,增加设备,并行地运行多个服务调度分配问题,Cluster + +垂直扩展不再提及: + +- 随着计算机性能的增长,其价格会成倍增长 +- 单台计算机的性能是有上限的,不可能无限制地垂直扩展 +- 多核CPU意味着即使是单台计算机也可以并行的。 + +## 13.1 集群 Cluster + +Cluster:集群,为解决某个特定问题将多台计算机组合起来形成的单个系统 + +Cluster分为三种类型: + +- LB:Load Balancing,负载均衡。调度负载,按照算法调度。 +- HA:High Availiablity,高可用,避免SPOF(single Point Of failure)(单点失败) + - MTBF:Mean Time Between Failure 平均无故障时间 + - MTTR:Mean Time To Restoration( repair)平均恢复前时间 + - A=MTBF/(MTBF+MTTR) (0,1):99%,99.5%,99.9%,99.99%,99.999% +- HPC:High-performance computing,高性能 www.top500.org + +## 13.2 分布式系统 + +分布式存储: Ceph,GlusterFS,FastDFS,MogileFS +分布式计算:hadoop,Spark +分布式常见应用 +分布式应用-服务按照功能拆分,使用微服务 +分布式静态资源–静态资源放在不同的存储集群上 +分布式数据和存储–使用key-value缓存系统 +分布式计算–对特殊业务使用分布式计算,比如Hadoop集群 + +## 13.3 集群和分布式 + +集群:同一个业务系统,部署在多台服务器上。集群中,每一台服务器实现的功能没有差别,数据和代 +码都是一样的 + +分布式:一个业务被拆成多个子业务,或者本身就是不同的业务,部署在多台服务器上。分布式中,每 + 一台服务器实现的功能是有差别的,数据和代码也是不一样的,分布式每台服务器功能加起来,才是完整的业务。 + +​ 分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率。 +​ 对于大型网站,访问用户很多,实现一个群集,在前面部署一个负载均衡服务器,后面几台服务器完成 +​ 同一业务。如果有用户进行相应业务访问时,负载均衡器根据后端哪台服务器的负载情况,决定由给哪一台去完成响应,并且一台服务器垮了,其它的服务器可以顶上来。分布式的每一个节点,都完成不同的业务,如果一个节点垮了,那这个业务可能就会失败。 + +## 13.4 集群设计原则 + +可扩展性—集群的横向扩展能力。小型机横向扩展小,面临淘汰 +可用性—无故障时间(SLA) +性能—访问响应时间 +容量—单位时间内的最大并发吞吐量(C10K 并发问题) 。LVS内核级,并发好。 + +## 13.5 集群设计实现 + +### 13.5.1 基础设施层面 + +提升硬件资源性能—从入口防火墙到后端web server均使用更高性能的硬件资源 +多域名—DNS 轮询A记录解析。指向不同IP 访问入口增多 +多入口—将A记录解析到多个公网IP入口 +多机房—同城+异地容灾 +CDN(Content Delivery Network)—基于GSLB(Global Server Load Balance)实现全局负载均衡,如:DNS +就近分配地址,提高效率 + +### 13.5.2 业务层面 + +分层:安全层、负载层、静态层、动态层、(缓存层、存储层)持久化与非持久化 +分割:基于功能分割大业务为小服务 +分布式:对于特殊场景的业务,使用分布式计算 + +## 13.6 LB Cluster 负载均衡集群 + +### 13.6.1 按实现方式划分 + +- 硬件(大公司) + - F5 Big-IP + - Citrix Netscaler + - A10 + +- 软件(小公司) + - lvs:Linux Virtual Server,阿里四层SLB (Server Load Balance)使用下四层功能:物理层 数据链路层 + - nginx:支持七层调度,阿里七层SLB使用Tengine + - haproxy:支持七层调度 + - ats:Apache Traffic Server,yahoo捐助给apache + - perlbal:Perl 编写 + - pound + +### 13.6.2 基于工作的协议层次划分 + +- 传输层(通用):DNAT和DPORT + - LVS:Linux Virtual Server + - nginx:stream + - haproxy:mode tcp + +- 应用层(专用):针对特定协议,常称为 proxy server + - http:nginx, httpd, haproxy(mode http), … + - fastcgi:nginx, httpd, … + - mysql:mysql-proxy, … + +### 13.6.3 负载均衡的会话保持 + +- session sticky:同一用户调度固定服务器 + - Source IP:LVS sh算法(对某一特定服务而言) + - Cookie +- session replication:每台服务器拥有全部session + - session multicast cluster (内存消耗大) +- session server:专门的session服务器 + - Memcached,Redis (只放session,共享)也存在单点失败,即也要做集群哨兵机制 + +## 13.7 HA 高可用集群实现 + +keepalived:vrrp协议 +Ais:应用接口规范 +heartbeat +cman+rgmanager(RHCS) +coresync_pacemaker + +# 14. Linux Virtual Server简介 + +## 14.1 LVS介绍 + +- LVS:Linux Virtual Server,负载调度器,内核集成,章文嵩(花名 正明), 阿里的四层SLB(Server Load Balance)是基于LVS+keepalived实现。 +- LVS 官网:http://www.linuxvirtualserver.org/ +- LVS 相关术语 + - VS: Virtual Server,负责调度 + - RS: Real Server,负责真正提供服务 + +## 14.2 LVS工作原理 + +​ VS根据请求报文的目标IP和目标协议及端口将其调度转发至某RS,根据调度算法来挑选RS。LVS是内核级功能,工作在INPUT链的位置,将发往INPUT的流量进行“处理” + +​ 查看内核支持LVS + +```bash +[root@localhost ~]# grep -i -C 10 ipvs /boot/config-3.10.0-957.el7.x86_64 +``` + +## 14.3 LVS集群体系架构 + +![image-20210712073700965](07.负载均衡/image-20210712073700965.png) + +## 14.4 LVS 功能及组织架构 + +下面摘自于阿里云:https://help.aliyun.com/document_detail/27543.html?spm=5176.21213303.J_6028563670.7.505a3edaQq9WLJ&scm=20140722.S_help%40%40%E6%96%87%E6%A1%A3%40%4027543.S_0.ID_27543-OR_s%2Bhelpproduct-V_1-P0_0 + +负载均衡的应用场景为高访问量的业务,提高应用程序的可用性和可靠性。 + +## 14.5 应用于高访问量的业务 + +如果您的应用访问量很高,您可以通过配置监听规则将流量分发到不同的云服务器ECS(Elastic Compute Service)实例上。此外,您可以使用会话保持功能将同一客户端的请求转发到同一台后端ECS,提高访问效率。 + +## 14.6 扩展应用程序 + +您可以根据业务发展的需要,随时添加和移除ECS实例来扩展应用系统的服务能力,适用于各种Web服务器和App服务器。 + +## 14.7 消除单点故障 + +您可以在CLB实例下添加多台ECS实例。当其中一部分ECS实例发生故障后,CLB会自动屏蔽故障的ECS实例,将请求分发给正常运行的ECS实例,保证应用系统仍能正常工作。 + +## 14.8 同城容灾 (多可用区容灾) + +为了提供更加稳定可靠的CLB服务,CLB已在各地域部署了多可用区以实现同地域容灾。当主可用区出现机房故障或不可用时,CLB仍然有能力在非常短的时间内(大约30s中断)切换到另外一个备可用区恢复服务能力;当主可用区恢复时,CLB同样会自动切换到主可用区提供服务。 + +使用CLB时,您可以将CLB实例部署在支持多可用区的地域以实现同城容灾。此外,建议您结合自身的应用需要,综合考虑后端服务器的部署。如果您的每个可用区均至少添加了一台ECS实例,那么此种部署模式下的CLB服务的效率是最高的。 + +如下图所示,在CLB实例下绑定不同可用区的ECS实例。正常情况下,用户访问流量将同时转发至主、备可用区内的ECS实例;当可用区A发生故障时,用户访问流量将只转发至备可用区内的ECS实例。此种部署既可以避免因为单个可用区的故障而导致对外服务的不可用,也可以通过不同产品间可用区的选择来降低延迟。 + +![img](../../../../课程/课件git版/SRE/Linux服务/负载均衡/p947-17228280245661.png) + +如果您采取如下图所示的部署方案,即在CLB实例的主可用区下绑定多台ECS实例,而在备可用区没有任何ECS实例。正常情况下,用户访问流量将只转发至主可用区内的ECS实例,比较于上图,流量传输延时低;当可用区A发生故障时会造成业务中断,因为备可用区没有ECS实例来接收请求。这样的部署方式很明显是以牺牲高可用性为代价来获取低延时。 + +![img](../../../../课程/课件git版/SRE/Linux服务/负载均衡/p948-17228280245663.png) + +## 14.9 跨地域容灾 + +您可以在不同地域下部署CLB实例,并分别挂载相应地域内不同可用区的ECS。上层利用云解析做智能DNS,将域名解析到不同地域的CLB实例服务地址下,可实现全局CLB。当某个地域出现不可用时,暂停对应解析即可实现所有用户访问不受影响。 + +![img](../../../../课程/课件git版/SRE/Linux服务/负载均衡/p949-17228280245665.png) + +## 14.10 LVS应用场景 + +### 14.10.1 音视频/游戏等大并发流量场景 + +​ 音视频/游戏等行业经常面临突发访问,海量流量,后端服务压力大。如短视频/长视频/直播/在校教育/游戏等业务中,由于服务端与用户端之间需要实时大量的互动,因此,用户流量非常大,而音视频业务的波峰波谷效应明显,这对整个系统的性能、弹性、稳定性和可用性带来了巨大的挑战,需要使用负载均衡进行流量分发 + +![img](07.负载均衡/O1CN01LSQXdk23trU4Dhqsn_!!6000000007314-2-tps-1530-1140.png) + +### 14.10.2 零售/金融/企业等弹性高可靠场景 + +​ 新零售新金融业务变化快,新业务上线、老业务调整时常发生,大促大型活动常态化;因此,对即开即用网络,快速交付能力,弹性伸缩能力,安全可靠、灵活计费等需求显著,需要使用负载均衡搭建高可靠架构。 + +![img](07.负载均衡/O1CN01Lnlvbb1ixt2Ma0OEg_!!6000000004480-2-tps-1530-1140.png) + +### 14.10.3 云原生网络应用场景 + +​ 随着云原生逐步成熟,互联网/金融/企业等诸多行业新建业务时选择云原生部署,或对现有业务进行云原生化改造。无论是使用阿里云ACK/ASK/SAE还是开源K8S,云原生网络均可用到负载均衡服务来实现流量调度。 + +![img](07.负载均衡/O1CN01gbRLLp208HUej1u2p_!!6000000006804-2-tps-1530-1140.png) + +### 14.10.4 跨地域网络应用场景 + +​ 跨地域跨可用区的容灾方案。互联网/金融/企业等业务逐步遍及全球,需要将不同地域用户智能调度访问到相应的业务系统,为了降本增效,线下IDC业务需要与云上业务互通,需要使用负载均衡构建跨地域或混合云容灾架构。 + +img + +## 14.11 LVS集群类型中的术语 + +VS:Virtual Server,Director Server(DS), Dispatcher(调度器),Load Balancer +RS:Real Server(lvs), upstream server(nginx)上游服务器, backend server(haproxy) +CIP:Client IP +VIP:Virtual serve IP VS外网的IP +DIP:Director IP VS内网的IP +RIP:Real server IP +访问流程:CIP <–> VIP == DIP <–> RIP + +# 15. LVS 工作模式和相关命令 + +## 15.1 LVS集群的工作模式 + +lvs-nat:修改请求报文的目标IP,多目标IP的DNAT +lvs-dr:操纵封装新的MAC地址。MAC头的修改 +lvs-tun:在原请求IP报文之外新加一个IP首部。 +lvs-fullnat:修改请求报文的源和目标IP + +### 15.1.1 LVS的NAT模式 + +image-20210712080133506 + +lvs-nat:本质是多目标IP的DNAT,通过将请求报文中的目标地址和目标端口修改为某挑出的RS的RIP和PORT实现转发 + +- RIP和DIP应在同一个IP网络,且应使用私网地址;RS的网关要指向DIP +- 请求报文和响应报文都必须经由Director转发,Director易于成为系统瓶颈 +- 支持端口映射,可修改请求报文的目标PORT +- VS必须是Linux系统,RS可以是任意OS系统 + +image-20210712080410013 + +image-20210712080545578 + +### 15.1.2 LVS的DR模式 + +image-20210712080949453 + +​ LVS-DR:Direct Routing,直接路由,LVS默认模式,应用最广泛,通过为请求报文重新封装一个MAC首部 +进行转发,源MAC是DIP所在的接口的MAC,目标MAC是某挑选出的RS的RIP所在接口的MAC地址;源 +IP/PORT,以及目标IP/PORT均保持不变 + +![image-20210712081307431](07.负载均衡/image-20210712081307431.png) + +DR模式的特点 + +- Director和各RS都配置有VIP +- 确保前端路由器将目标IP为VIP的请求报文发往Director三种方法 + +在前端网关做静态绑定VIP和Director的MAC地址 + +此方法一般不使用,过于固定 + +在RS上使用arptables工具 + +```bash +arptables -A IN -d $VIP -j DROP +arptables -A OUT -s $VIP -j mangle --mangle-ip-s $RIP +``` + +在RS上修改内核参数以限制arp通告及应答级别 + +```bash +/proc/sys/net/ipv4/conf/all/arp_ignore +/proc/sys/net/ipv4/conf/all/arp_announce +``` + +- RS的RIP可以使用私网地址,也可以是公网地址;RIP与DIP在同一IP网络;RIP的网关不能指向DIP,以确保响应报文不会经由Director +- RS和Director要在同一个物理网络 +- 请求报文要经由Director,但响应报文不经由Director,而由RS直接发往Client +- 不支持端口映射(端口不能修改) +- RS可使用大多数OS系统 + +### 15.1.3 LVS的TUN模式 + +![image-20210712082509947](07.负载均衡/image-20210712082509947.png) + +​ 转发方式:不修改请求报文的IP首部(源IP为CIP,目标IP为VIP),而在原IP报文之外再封装一个IP首部(源IP是DIP,目标IP是RIP),将报文发往挑选出的目标RS;RS直接响应给客户端(源IP是VIP,目标IP是CIP) + +![image-20210712084005949](07.负载均衡/image-20210712084005949.png) + +TUN模式特点 + +- DIP, VIP, RIP可以是公网地址 +- RS的网关一般不能指向DIP +- 请求报文要经由Director,但响应不经由Director +- 不支持端口映射 +- RS的OS须支持隧道功能 + +### 15.1.4 LVS的FULLNAT模式 + +![image-20210712084209645](07.负载均衡/image-20210712084209645.png) + +- 通过同时修改请求报文的源IP地址和目标IP地址进行转发 +- CIP --> DIP +- VIP --> RIP + +- fullnat模式特点: + - VIP是公网地址,RIP和DIP是私网地址,且通常不在同一IP网络;因此,RIP的网关一般不会指向DIP + - RS收到的请求报文源地址是DIP,因此,只需响应给DIP;但Director还要将其发往Client + - 请求和响应报文都经由Director + - 支持端口映射 + - 注意:此类型kernel默认不支持 + +### 15.1.5 LVS工作模式总结和比较 + +| | NAT | TUN | DR | +| ------------------- | ------------- | ---------- | -------------- | +| Real server | any | Tunneling | Non-arp device | +| Real server network | private | LAN/WAN | LAN | +| Real Server number | low(10-20) | High(100) | High(100) | +| Real server gateway | load balancer | own router | own router | +| 优点 | 端口转换 | WAN | 性能最好 | +| 缺点 | 性能瓶颈 | 支持隧道 | 不支持跨网段 | + +- lvs-nat与lvs-fullnat: + - 请求和响应报文都经由Director + - lvs-nat:RIP的网关要指向DIP + - lvs-fullnat:RIP和DIP未必在同一IP网络,但要能通信 +- lvs-dr与lvs-tun: + - 请求报文要经由Director,但响应报文由RS直接发往Client + - lvs-dr:通过封装新的MAC首部实现,通过MAC网络转发 + - lvs-tun:通过在原IP报文外封装新IP头实现转发,支持远距离通信 + +## 15.2 LVS调试算法 + +ipvs scheduler:根据其调度时是否考虑各RS当前的负载状态 +分为两种:静态方法和动态方法 + +### 15.2.1 静态方法 + +- 仅根据算法本身进行调度 + - RR:roundrobin,轮询 不考虑机器的性能好坏,轮询调度 + - WRR:Weighted RR,加权轮询 权重越高,优先级高,调度更多资源给它 优先级高 + - SH:Source Hashing,实现session sticky,源IP地址hash;将来自于同一个IP地址的请求始终发往第一次挑中的RS,从而实现会话绑定 + - DH:Destination Hashing;目标地址哈希,第一次轮询调度至RS,后续将发往同一个目标地址的请求始终转发至第一次挑中的RS,典型使用场景是正向代理缓存场景中的负载均衡,如:宽带运营商 + +### 15.2.2 动态方法 + +主要根据每RS当前的负载状态及调度算法进行调度Overhead=value 较小的RS将被调度 + +- LC:least connections 适用于长连接应用(最少连接 ) + - `Overhead=activeconns *256+inactiveconns` + +- WLC:Weighted LC,默认调度方法(加权最少连接 ) + - `Overhead=(activeconns * 256+inactiveconns)/weight` + +- SED:Shortest Expection Delay,初始连接高权重优先(最少期望延迟) + - `Overhead=(activeconns+1)*256/weight` + +- NQ:Never Queue,第一轮均匀分配,后续SED(从不排队调度方法) + +- LBLC:Locality-Based LC,动态的DH算法,使用场景:根据负载状态实现正向代理 + +- LBLCR:LBLC with Replication,带复制功能的LBLC,解决LBLC负载不均衡问题,从负载重的复制到负载轻的RS + +### 15.2.3 内核版本 4.15版本后新增调度算法 + +- FO(Weighted Fail Over)调度算法,在此FO算法中,遍历虚拟服务所关联的真实服务器链表,找到还未过载(未设置IP_VS_DEST_F_OVERLOAD标志)的且权重最高的真实服务器,进行调度。方便调式上下线。 + +- OVF(Overflow-connection)调度算法,基于真实服务器的活动连接数量和权重值实现。将新连接调度到权重值最高的真实服务器,直到其活动连接数量超过权重值位置,之后调度到下一个权重值最高的真实服务器,在此OVF算法中,遍历虚拟服务相关联的真实服务器链表,找到权重值最高的可用真实服务器。一个可用的真实服务器需要同时满足以下条件: + - 未过载(未设置IP_VS_DEST_F_OVERLOAD标志) + - 真实服务器当前的活动连接数量小于其权重值 + - 其权重值不为零 + +## 15.3 LVS 相关软件 + +### 15.3.1 程序包 + +- Unit File: ipvsadm.service +- 主程序:/usr/sbin/ipvsadm +- 规则保存工具:/usr/sbin/ipvsadm-save +- 规则重载工具:/usr/sbin/ipvsadm-restore +- 配置文件:/etc/sysconfig/ipvsadm-config + +```bash +[root@localhost ~]# yum -y install ipvsadm +[root@localhost ~]# cat /usr/lib/systemd/system/ipvsadm.service +[Unit] +Description=Initialise the Linux Virtual Server +After=syslog.target network.target + +[Service] +Type=oneshot +ExecStart=/bin/bash -c "exec /sbin/ipvsadm-restore < /etc/sysconfig/ipvsadm" +ExecStop=/bin/bash -c "exec /sbin/ipvsadm-save -n > /etc/sysconfig/ipvsadm" +ExecStop=/sbin/ipvsadm -C +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +``` + +### 15.3.2 ipvsadm 命令 + +- ipvsadm核心功能: + - 集群服务管理:增、删、改 + - 集群服务的RS管理:增、删、改 + - 查看 + +```bash +#管理集群服务 +ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] [-M netmask] +[--pe persistence_engine] [-b sched-flags] +ipvsadm -A #创建集群 +ipvsadm -E #修改集群 +ipvsadm -D -t|u|f service-address #删除 +ipvsadm –C #清空 +ipvsadm –R #重载 +ipvsadm -S [-n] #保存 +ipvsadm -L #查看 + +#管理集群中的RS +ipvsadm -a|e -t|u|f service-address -r server-address [options] +ipvsadm -d -t|u|f service-address -r server-address +ipvsadm -L|l [options] +ipvsadm -Z [-t|u|f service-address] #清空计数器 + +#保存规则 +‐S +# ipvsadm ‐S > /path/to/somefile +载入此前的规则: +‐R +# ipvsadm ‐R < /path/form/somefile +``` + +- 说明 + +```bash +service-address: + +-t|u|f: + -t: TCP协议的端口,VIP:TCP_PORT 如:-t 192.168.88.20:80 + -u: UDP协议的端口,VIP:UDP_PORT 如:-u 192.168.88.20:80 + -f:firewall MARK,标记,一个数字 +[-s scheduler]:指定集群的调度算法,默认为wlc + +server-address: + +rip[:port] 如省略port,不作端口映射 +选项: + lvs类型: + -g: gateway, dr类型,默认 + -i: ipip, tun类型 + -m: masquerade, nat类型 + -w weight:权重 +``` + +- 举例 + +```bash +ipvsadm -A -t 192.168.88.100:80 -s wrr # 创建集群 +ipvsadm -D -t 192.168.88.100:80 # 删除集群 + +ipvsadm -a -t 192.168.88.100:80 -r 192.168.88.20:8080 -m -w 3 +# 往集群中添加RS,并且设置nat,配置权重为3 +ipvsadm -d -t 192.168.88.100:80 -r 192.168.88.20:8080 +# 删除RS +``` + +- 查看 + +```bash +ipvsadm -L|l [options] + –numeric, -n:以数字形式输出地址和端口号 + –exact:扩展信息,精确值 + –connection,-c:当前IPVS连接输出 + –stats:统计信息 + –rate :输出速率信息 +``` + +# 16. LVS实战案例 + +## 16.1 LVS-NAT模式案例 + +image-20210712100318910 + +实验环境说明: + +LVS:两张网卡,一张nat网卡做为vIP与客户端建立连接,一张仅主机网卡用于对内部服务器的连接 + +RS1和RS2:两个RS的网卡都使用仅主机的网卡,并且网关指向LVS的仅主机网卡 + +- 开启LVS流量转发功能,并且配置NAT + +```bash +[root@lvs ~]# vim /etc/sysctl.conf +net.ipv4.ip_forward = 1 +[root@lvs ~]# sysctl -p +net.ipv4.ip_forward = 1 + +[root@lvs ~]# iptables -t nat -A POSTROUTING -s 192.168.153.0/24 -j MASQUERADE +[root@lvs ~]# iptables -t nat -L -n +``` + + + +- 先搭建web服务器 + +```bash +[root@rs1 ~]# yum -y install httpd +[root@rs1 ~]# systemctl start httpd +[root@rs1 ~]# systemctl enable httpd +[root@rs1 ~]# echo "RS1 ..." > /var/www/html/index.html +[root@rs1 ~]# firewall-cmd --add-service=http +[root@rs1 ~]# firewall-cmd --add-service=http --permanent + +[root@rs2 ~]# yum -y install httpd +[root@rs2 ~]# systemctl start httpd +[root@rs2 ~]# systemctl enable httpd +[root@rs2 ~]# echo "RS2 ..." > /var/www/html/index.html +[root@rs2 ~]# firewall-cmd --add-service=http +[root@rs2 ~]# firewall-cmd --add-service=http --permanent +``` + +- 配置LVS + +```bash +[root@lvs ~]# ipvsadm -A -t 192.168.88.10:80 -s rr +[root@lvs ~]# ipvsadm -a -t 192.168.88.10:80 -r 192.168.153.20:80 -m +[root@lvs ~]# ipvsadm -a -t 192.168.88.10:80 -r 192.168.153.30:80 -m +[root@lvs ~]# ipvsadm -L -n +IP Virtual Server version 1.2.1 (size=4096) +Prot LocalAddress:Port Scheduler Flags + -> RemoteAddress:Port Forward Weight ActiveConn InActConn +TCP 192.168.88.100:80 rr + -> 192.168.153.20:80 Masq 1 0 0 + -> 192.168.153.30:80 Masq 1 0 0 +``` + +- 验证结果 + +![image-20210712102932211](07.负载均衡/image-20210712102932211.png) + +![image-20210712102939649](07.负载均衡/image-20210712102939649.png) + +- 抓包可以观察到客户端都是与LVS进行通信,LVS对流量进行NAT + +![image-20210712103059127](07.负载均衡/image-20210712103059127.png) + +## 16.2 LVS-DR模式案例 + +![image-20210712111708684](07.负载均衡/image-20210712111708684.png) + +- 做这个实验之前,需要清空LVS之前配置的NAT规则 + +```bash +# 由于我们后面做实验要用到ifconfig命令,该命令需要安装net-tools工具。所以我们趁现在有网,可以先安装一下,三台机器都需要安装 +[root@lvs ~]# yum install -y net-tools +[root@lvs ~]# iptables -t nat -F +``` + +- 修改RS1和RS2的网关地址 + +```bash +[root@rs1 ~]# ip route +default via 192.168.153.2 dev ens33 proto static metric 100 + +[root@rs2 ~]# ip route +default via 192.168.153.2 dev ens33 proto static metric 100 +``` + +- 在RS上修改内核参数以限制arp通告及应答级别 + +```bash +[root@rs1 ~]# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore +[root@rs1 ~]# echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce + +[root@rs2 ~]# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore +[root@rs2 ~]# echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce +``` + +- 给RS1和RS2设置VIP 192.168.153.100 + +```bash +[root@rs1 ~]# vim rs.sh +#!/bin/bash +vip=192.168.153.100/32 +ifconfig lo:1 $vip +echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore +echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce +echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore +echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce + +[root@rs1 ~]# . rs.sh +# 在RS2上也需要执行 +``` + +- 配置LVS服务器 + +```bash +[root@lvs ~]# ifconfig lo:1 192.168.153.100/32 +[root@lvs ~]# ipvsadm -A -t 192.168.153.100:80 -s rr +[root@lvs ~]# ipvsadm -a -t 192.168.153.100:80 -r 192.168.153.20 +[root@lvs ~]# ipvsadm -a -t 192.168.153.100:80 -r 192.168.153.30 +[root@lvs ~]# ipvsadm -Ln +IP Virtual Server version 1.2.1 (size=4096) +Prot LocalAddress:Port Scheduler Flags + -> RemoteAddress:Port Forward Weight ActiveConn InActConn +TCP 192.168.153.100:80 rr + -> 192.168.153.20:80 Route 1 0 0 + -> 192.168.153.30:80 Route 1 0 0 +``` + +- 验证,当多次访问192.168.153.100的时候可以得到不同的主机回复 + +![image-20210712111555645](07.负载均衡/image-20210712111555645.png) + +![image-20210712111611821](07.负载均衡/image-20210712111611821.png) + +# 17. HAProxy介绍 + +​ HAProxy是法国开发者威利塔罗(Willy Tarreau)在2000年使用C语言开发的一个开源软件,是一款具备高并发(一万以上)、高性能的TCP和HTTP负载均衡器,支持基于cookie的持久性,自动故障切换,支持正则表达式及web状态统计,目前最新TLS版本为2.0 + +历史版本: + +- 历史版本更新功能:1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2-dev +- 1.8:多线程,HTTP/2缓存…… +- 1.7:服务器动态配置,多类型证书…… +- 1.6:DNS解析支持,HTTP连接多路复用…… +- 1.5:开始支持SSL,IPV6,会话保持…… + +从2013年HAProxy 分为社区版和企业版,企业版将提供更多的特性和功能以及全天24小时的技术支持等服务。 + +## 17.1 企业版 + +企业版网站:https://www.haproxy.com/ + +![image-20210713094311973](07.负载均衡/image-20210713094311973.png) + +## 17.2 社区版 + +社区版网站:http://www.haproxy.org/ + +github:https://github.com/haproxy + +![image-20210713094402494](07.负载均衡/image-20210713094402494.png) + +## 17.3 版本对比 + +| 功能 | 社区版 | 企业版 | +| ----------------------------------------- | ------ | ------ | +| 高级HTTP / TCP负载平衡和持久性 | 支持 | 支持 | +| 高级健康检查 | 支持 | 支持 | +| 应用程序加速 | 支持 | 支持 | +| 高级安全特性 | 支持 | 支持 | +| 高级管理 | 支持 | 支持 | +| HAProxy Dev Branch新功能 | | 支持 | +| 24*7 支持服务 | | 支持 | +| 实时仪表盘 | | 支持 | +| VRRP和Route Health Injection HA工具 | | 支持 | +| ACL,映射和TLS票证密钥同步 | | 支持 | +| 基于应用程序的高级DDoS和Bot保护(自动保护) | | 支持 | +| Bot(机器人)监测 | | 支持 | +| Web应用防火墙 | | 支持 | +| HTTP协议验证 | | 支持 | +| 实时集群追踪 | | 支持 | + +## 17.4 HAProxy功能 + +image-20210713100058631 + +## 17.5 支持功能 + +- TCP 和 HTTP反向代理 +- SSL/TSL服务器 +- 可以针对HTTP请求添加cookie,进行路由后端服务器 +- 可平衡负载至后端服务器,并支持持久连接 +- 支持所有主服务器故障切换至备用服务器 +- 支持专用端口实现监控服务 +- 支持停止接受新连接请求,而不影响现有连接 +- 可以在双向添加,修改或删除HTTP报文首部 +- 响应报文压缩 +- 支持基于pattern实现连接请求的访问控制 +- 通过特定的URI为授权用户提供详细的状态信息 +- 支持http反向代理 +- 支持动态程序的反向代理 +- 支持基于数据库的反向代理 + +## 17.6 不具备的功能 + +- 正向代理--squid,nginx +- 缓存代理--varnish +- web服务--nginx、tengine、apache、php、tomcat +- UDP--目前不支持UDP协议 +- 单机性能--相比LVS性能较差 + +# 18. 编译安装HAProxy + +- 编译安装HAProxy 2.0 LTS版本,更多源码包下载地址:http://www.haproxy.org/download/ +- centos7自带的源中yum安装的版本是较老的1.5的版本 + +## 18.1 解决lua环境 + +​ HAProxy 支持基于lua实现功能扩展,lua是一种小巧的脚本语言,于1993年由巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组开发,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。 + +Lua 官网:www.lua.org + +- Lua 应用场景 + + - 游戏开发 + - 独立应用脚本 + - Web 应用脚本 + - 扩展和数据库插件,如MySQL Proxy + - 安全系统,如入侵检测系统 + +### 18.1.1 Centos 基础环境 + +参考链接:http://www.lua.org/start.html + +![image-20210713101859303](07.负载均衡/image-20210713101859303.png) + +​ 由于CentOS7 之前版本自带的lua版本比较低并不符合HAProxy要求的lua最低版本(5.3)的要求,因此需要编译安装较新版本的lua环境,然后才能编译安装HAProxy,过程如下 + +- 当前系统版本 + +```bash +[root@localhost ~]# lua -v +Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio +``` + +- 安装基础命令及编译依赖环境 + +```bash +[root@localhost ~]# yum -y install gcc readline-devel +[root@localhost ~]# wget http://www.lua.org/ftp/lua-5.3.5.tar.gz +[root@localhost ~]# tar xvf lua-5.3.5.tar.gz -C /usr/local/src +[root@localhost ~]# cd /usr/local/src/lua-5.3.5 +[root@localhost lua-5.3.5]# make linux test +``` + +- 查看编译安装的版本 + +```bash +[root@localhost lua-5.3.5]# src/lua -v +Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio +``` + +## 18.2 编译安装HAProxy + +- HAProxy 2.0以上版本编译参数 + +```bash +[root@localhost ~]# yum -y install gcc openssl-devel pcre-devel systemd-devel +[root@localhost ~]# wget http://www.haproxy.org/download/2.1/src/haproxy-2.1.3.tar.gz +[root@localhost ~]# tar xvf haproxy-2.1.3.tar.gz -C /usr/local/src +[root@localhost ~]# cd /usr/local/src/haproxy-2.1.3/ +[root@localhost haproxy-2.1.3]# cat README +``` + +- 参考INSTALL文件进行编译安装 + +```bash +[root@localhost haproxy-2.1.3]# make -j 2 ARCH=x86_64 TARGET=linux-glibc \ +USE_PCRE=1 \ +USE_OPENSSL=1 \ +USE_ZLIB=1 \ +USE_SYSTEMD=1 \ +USE_LUA=1 \ +LUA_INC=/usr/local/src/lua-5.3.5/src/ \ +LUA_LIB=/usr/local/src/lua-5.3.5/src/ +[root@localhost haproxy-2.1.3]# make install PREFIX=/apps/haproxy +[root@localhost haproxy-2.1.3]# ln -s /apps/haproxy/sbin/haproxy /usr/sbin/ +``` + +- 查看生成的文件 + +```bash +[root@localhost ~]# tree -C /apps/haproxy/ +/apps/haproxy/ +├── doc +│   └── haproxy +│   ├── 51Degrees-device-detection.txt +│   ├── architecture.txt +│   ├── close-options.txt +│   ├── configuration.txt +│   ├── cookie-options.txt +│   ├── DeviceAtlas-device-detection.txt +│   ├── intro.txt +│   ├── linux-syn-cookies.txt +│   ├── lua.txt +│   ├── management.txt +│   ├── netscaler-client-ip-insertion-protocol.txt +│   ├── network-namespaces.txt +│   ├── peers.txt +│   ├── peers-v2.0.txt +│   ├── proxy-protocol.txt +│   ├── regression-testing.txt +│   ├── seamless_reload.txt +│   ├── SOCKS4.protocol.txt +│   ├── SPOE.txt +│   └── WURFL-device-detection.txt +├── sbin +│   └── haproxy +└── share + └── man + └── man1 + └── haproxy.1 + +6 directories, 22 files +``` + +## 18.3 验证HAProxy版本 + +```bash +[root@localhost ~]# which haproxy +/usr/sbin/haproxy +[root@localhost ~]# haproxy -v +HA-Proxy version 2.1.3 2020/02/12 - https://haproxy.org/ +Status: stable branch - will stop receiving fixes around Q1 2021. +Known bugs: http://www.haproxy.org/bugs/bugs-2.1.3.html + +[root@localhost ~]# haproxy -vv +HA-Proxy version 2.1.3 2020/02/12 - https://haproxy.org/ +Status: stable branch - will stop receiving fixes around Q1 2021. +Known bugs: http://www.haproxy.org/bugs/bugs-2.1.3.html +Build options : + TARGET = linux-glibc + CPU = generic + CC = gcc + CFLAGS = -m64 -march=x86-64 -O2 -g -fno-strict-aliasing -Wdeclaration-after-statement -fwrapv -Wno-unused-label -Wno-sign-compare -Wno-unused-parameter -Wno-old-style-declaration -Wno-ignored-qualifiers -Wno-clobbered -Wno-missing-field-initializers -Wtype-limits + OPTIONS = USE_PCRE=1 USE_OPENSSL=1 USE_LUA=1 USE_ZLIB=1 USE_SYSTEMD=1 + +Feature list : +EPOLL -KQUEUE -MY_EPOLL -MY_SPLICE +NETFILTER +PCRE -PCRE_JIT -PCRE2 -PCRE2_JIT +POLL -PRIVATE_CACHE +THREAD -PTHREAD_PSHARED -REGPARM -STATIC_PCRE -STATIC_PCRE2 +TPROXY +LINUX_TPROXY +LINUX_SPLICE +LIBCRYPT +CRYPT_H -VSYSCALL +GETADDRINFO +OPENSSL +LUA +FUTEX +ACCEPT4 -MY_ACCEPT4 +ZLIB -SLZ +CPU_AFFINITY +TFO +NS +DL +RT -DEVICEATLAS -51DEGREES -WURFL +SYSTEMD -OBSOLETE_LINKER +PRCTL +THREAD_DUMP -EVPORTS + +Default settings : + bufsize = 16384, maxrewrite = 1024, maxpollevents = 200 + +Built with multi-threading support (MAX_THREADS=64, default=4). +Built with OpenSSL version : OpenSSL 1.0.2k-fips 26 Jan 2017 +Running on OpenSSL version : OpenSSL 1.0.2k-fips 26 Jan 2017 +OpenSSL library supports TLS extensions : yes +OpenSSL library supports SNI : yes +OpenSSL library supports : SSLv3 TLSv1.0 TLSv1.1 TLSv1.2 +Built with Lua version : Lua 5.3.5 +Built with network namespace support. +Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT IP_FREEBIND +Built with PCRE version : 8.32 2012-11-30 +Running on PCRE version : 8.32 2012-11-30 +PCRE library supports JIT : no (USE_PCRE_JIT not set) +Encrypted password support via crypt(3): yes +Built with zlib version : 1.2.7 +Running on zlib version : 1.2.7 +Compression algorithms supported : identity("identity"), deflate("deflate"), raw-deflate("deflate"), gzip("gzip") + +Available polling systems : + epoll : pref=300, test result OK + poll : pref=200, test result OK + select : pref=150, test result OK +Total: 3 (3 usable), will use epoll. + +Available multiplexer protocols : +(protocols marked as cannot be specified using 'proto' keyword) + h2 : mode=HTTP side=FE|BE mux=H2 + fcgi : mode=HTTP side=BE mux=FCGI + : mode=HTTP side=FE|BE mux=H1 + : mode=TCP side=FE|BE mux=PASS + +Available services : none + +Available filters : + [SPOE] spoe + [CACHE] cache + [FCGI] fcgi-app + [TRACE] trace + [COMP] compression +``` + +## 18.4 HAProxy启动脚本 + +```bash +[root@localhost ~]# vim /usr/lib/systemd/system/haproxy.service +[Unit] +Description=HAProxy Load Balancer +After=syslog.target network.target + +[Service] +ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -q +ExecStart=/usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/haproxy.pid +ExecReload=/bin/kill -USR2 $MAINPID + +[Install] +WantedBy=multi-user.target +``` + +- 默认缺少配置文件,无法启动 + +```bash +[root@localhost ~]# systemctl daemon-reload +[root@localhost ~]# systemctl start haproxy +Job for haproxy.service failed because the control process exited with error code. See "systemctl status haproxy.service" and "journalctl -xe" for details. + + +[root@localhost ~]# tail /var/log/messages +Jul 13 10:39:33 localhost systemd: Starting HAProxy Load Balancer... +Jul 13 10:39:33 localhost haproxy: [ALERT] 193/103933 (8156) : Cannot open configuration file/directory /etc/haproxy/haproxy.cfg : No such file or directory +Jul 13 10:39:33 localhost systemd: haproxy.service: control process exited, code=exited status=1 +Jul 13 10:39:33 localhost systemd: Failed to start HAProxy Load Balancer. +Jul 13 10:39:33 localhost systemd: Unit haproxy.service entered failed state. +Jul 13 10:39:33 localhost systemd: haproxy.service failed. +``` + +## 18.5 配置文件 + +- 查看配置文件范例 + +```bash +[root@localhost ~]# tree -C /usr/local/src/haproxy-2.1.3/examples/ +/usr/local/src/haproxy-2.1.3/examples/ +├── acl-content-sw.cfg +├── content-sw-sample.cfg +├── errorfiles +│   ├── 400.http +│   ├── 403.http +│   ├── 408.http +│   ├── 500.http +│   ├── 502.http +│   ├── 503.http +│   ├── 504.http +│   └── README +├── haproxy.init +├── option-http_proxy.cfg +├── socks4.cfg +├── transparent_proxy.cfg +└── wurfl-example.cfg + +1 directory, 15 files +``` + +- 创建自定义的配置文件 + +```bash +[root@localhost ~]# mkdir /etc/haproxy +[root@localhost ~]# vim /etc/haproxy/haproxy.cfg +global + maxconn 100000 + chroot /apps/haproxy + stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin + #uid 99 + #gid 99 + user haproxy + group haproxy + daemon + #nbproc 4 + #cpu-map 1 0 + #cpu-map 2 1 + #cpu-map 3 2 + #cpu-map 4 3 + pidfile /var/lib/haproxy/haproxy.pid + log 127.0.0.1 local2 info + +defaults + option http-keep-alive + option forwardfor + maxconn 100000 + mode http + timeout connect 300000ms + timeout client 300000ms + timeout server 300000ms + +listen stats + mode http + bind 0.0.0.0:9999 + stats enable + log global + stats uri /haproxy-status + stats auth admin:123456 + +listen web_port + bind 192.168.88.10:80 + mode http + log global + server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5 +``` + +## 18.6 启动haproxy + +```bash +[root@localhost ~]# mkdir /var/lib/haproxy +[root@localhost ~]# useradd -r -s /sbin/nologin -d /var/lib/haproxy haproxy +[root@localhost ~]# systemctl enable --now haproxy +``` + +## 18.7 验证haproxy状态 + +​ aproxy.cfg文件中定义了chroot、pidfile、user、group等参数,如果系统没有相应的资源会导致haproxy无法启动,具体参考日志文件 /var/log/messages + +```bash +[root@localhost ~]# systemctl status haproxy +● haproxy.service - HAProxy Load Balancer + Loaded: loaded (/usr/lib/systemd/system/haproxy.service; enabled; vendor preset: disabled) + Active: active (running) since 二 2021-07-13 10:45:39 CST; 38s ago + Process: 8360 ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -q (code=exited, status=0/SUCCESS) + Main PID: 8363 (haproxy) + CGroup: /system.slice/haproxy.service + ├─8363 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/hap... + └─8365 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/hap... + +7月 13 10:45:39 localhost.localdomain systemd[1]: Starting HAProxy Load Balancer... +7月 13 10:45:39 localhost.localdomain systemd[1]: Started HAProxy Load Balancer. +7月 13 10:45:39 localhost.localdomain haproxy[8363]: [NOTICE] 193/104539 (8363) : New wor...d +7月 13 10:45:39 localhost.localdomain haproxy[8363]: [WARNING] 193/104539 (8365) : Server.... +7月 13 10:45:39 localhost.localdomain haproxy[8363]: [ALERT] 193/104539 (8365) : proxy 'w...! +Hint: Some lines were ellipsized, use -l to show in full. + +[root@localhost ~]# pstree -p |grep haproxy + |-haproxy(8363)---haproxy(8365)-+-{haproxy}(8366) + | |-{haproxy}(8367) + | `-{haproxy}(8368) +``` + +## 18.8 查看haproxy的状态页面 + +- 浏览器访问 + +![image-20210713104814134](07.负载均衡/image-20210713104814134.png) + +![image-20210713104830656](07.负载均衡/image-20210713104830656.png) + +# 19. HAProxy基础配置详解 + +官方文档:http://cbonte.github.io/haproxy-dconv/2.1/configuration.html + +HAProxy 的配置文件haproxy.cfg由两大部分组成,分别是global和proxies部分 + +- global:全局配置段 + + - 进程及安全配置相关的参数 + - 性能调整相关参数 + - Debug参数 + +- proxies:代理配置段 + + - defaults:为frontend, backend, listen提供默认配置 + - frontend:前端,相当于nginx中的server {} + - backend:后端,相当于nginx中的upstream {} + - listen:同时拥有前端和后端配置 + +## 19.1 global配置 + +#### 19.1.1 global 配置参数说明 + +官方文档:http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#3 + +```bash +chroot #锁定运行目录 +deamon #以守护进程运行 +stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin process 1 #socket文件 +user, group, uid, gid #运行haproxy的用户身份 +nbproc n #开启的haproxy work 进程数,默认进程数是一个 +#nbthread 1 #指定每个haproxy进程开启的线程数,默认为每个进程一个线程,和nbproc互斥(版本有关) +#如果同时启用nbproc和nbthread 会出现以下日志的错误,无法启动服务 +Apr 7 14:46:23 haproxy haproxy: [ALERT] 097/144623 (1454) : config : cannot enable multiple processes if multiple threads are configured. Please use either nbproc or nbthread but not both. + +cpu-map 1 0 #绑定haproxy 进程至指定CPU,将第一个work进程绑定至0号CPU +maxconn n #每个haproxy进程的最大并发连接数 +maxsslconn n #每个haproxy进程ssl最大连接数,用于haproxy配置了证书的场景下 +maxconnrate n #每个进程每秒创建的最大连接数量 +spread-checks n #后端server状态check随机提前或延迟百分比时间,建议2-5(20%-50%)之间,默认值0 +pidfile #指定pid文件路径 +log 127.0.0.1 local2 info #定义全局的syslog服务器;日志服务器需要开启UDP协议,最多可以定义两个 +``` + +#### 19.1.2 多进程和线程 + +范例:多进程和socket文件 + +```bash +[root@localhost ~]# vim /etc/haproxy/haproxy.cfg +global + maxconn 100000 + chroot /apps/haproxy + stats socket /var/lib/haproxy/haproxy.sock1 mode 600 level admin process 1 + stats socket /var/lib/haproxy/haproxy.sock2 mode 600 level admin process 2 + user haproxy + group haproxy + daemon + nbproc 2 + +[root@localhost ~]# systemctl restart haproxy +[root@localhost ~]# pstree -p |grep haproxy + |-haproxy(8395)-+-haproxy(8399) + | `-haproxy(8400) +[root@localhost ~]# ll /var/lib/haproxy/ +总用量 4 +-rw-r--r--. 1 root root 5 7月 13 10:56 haproxy.pid +srw-------. 1 root root 0 7月 13 10:56 haproxy.sock1 +srw-------. 1 root root 0 7月 13 10:56 haproxy.sock2 +``` + +## 19.2 日志配置 + +### 19.2.1 HAProxy配置 + +```bash +# 在global配置项定义 +log 127.0.0.1 local{1-7} info #基于syslog记录日志到指定设备,级别有(err、warning、info、debug) + +listen web_port + bind 127.0.0.1:80 + mode http + log global #开启当前web_port的日志功能,默认不记录日志 + server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5 + +# systemctl restart haproxy +``` + +### 19.2.2 Rsyslog配置 + +```bash +[root@localhost ~]# vim /etc/rsyslog.conf + +ModLoad imudpUDPServerRun 514 +...... +local3.* /var/log/haproxy.log +...... + +# systemctl restart rsyslog +``` + +### 19.2.3 验证HAProxy日志 + +- 重启syslog服务并访问app页面,然后验证是否生成日志 + +```bash +[root@localhost ~]# tail -f /var/log/haproxy.log +Aug 14 20:21:06 localhost haproxy[18253]: Connect from 192.168.0.1:3050 to 10.0.0.7:80 (web_host/HTTP) +Aug 14 20:21:06 localhost haproxy[18253]: Connect from 192.168.0.1:3051 to 10.0.0.7:80 (web_host/HTTP) +Aug 14 20:21:06 localhost haproxy[18253]: Connect from 192.168.0.1:3050 to 10.0.0.7:80 (web_host/HTTP) +``` + +# 20. Proxies配置 + +官方文档:http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#4 + +- `defaults []` + - 默认配置项,针对以下的frontend、backend和listen生效,可以多个name也可以没有name +- `frontend ` + - 前端servername,类似于Nginx的一个虚拟主机 server和LVS服务集群。 +- `backend ` + - 后端服务器组,等于nginx的upstream和LVS中的RS服务器 +- `listen ` + - 将frontend和backend合并在一起配置,相对于frontend和backend配置更简洁,生产常用 + +注意:name字段只能使用大小写字母,数字,‘-’(dash),’_‘(underscore),’.’ (dot)和 ‘:'(colon),并且严格区分大小写 + +![image-20210713125712794](07.负载均衡/image-20210713125712794.png) + +## 20.1 Proxies配置-defaults + +- defaults 配置参数 + +```bash +option redispatch #当server Id对应的服务器挂掉后,强制定向到其他健康的服务器,重新派发 +option abortonclose #当服务器负载很高时,自动结束掉当前队列处理比较久的链接,针对业务情况选择开启 +option http-keep-alive #开启与客户端的会话保持 +option forwardfor #透传客户端真实IP至后端web服务器 +mode http|tcp #设置默认工作类型,使用TCP服务器性能更好,减少压力 +timeout http-keep-alive 120s #session 会话保持超时时间,此时间段内会转发到相同的后端服务器 +timeout connect 120s #客户端请求从haproxy到后端server最长连接等待时间(TCP连接之前),默认单位ms +timeout server 600s #客户端请求从haproxy到后端服务端的请求处理超时时长(TCP连接之后),默认单位ms,如果超时,会出现502错误,此值建议设置较大些,访止502错误 +timeout client 600s #设置haproxy与客户端的最长非活动时间,默认单位ms,建议和timeout server相同 +timeout check 5s #对后端服务器的默认检测超时时间 +default-server inter 1000 weight 3 #指定后端服务器的默认设置 +``` + + + +## 20.2 Proxies配置-frontend + +- frontend 配置参数 + +```bash +bind: #指定HAProxy的监听地址,可以是IPV4或IPV6,可以同时监听多个IP或端口,可同时用于listen字段中 + +#格式: +bind [
]: [, ...] [param*] + +#注意:如果需要绑定在非本机的IP,需要开启内核参数:net.ipv4.ip_nonlocal_bind=1 +``` + +- 范例 + +```bash +listen http_proxy #监听http的多个IP的多个端口和sock文件 + bind :80,:443,:8801-8810 + bind 10.0.0.1:10080,10.0.0.1:10443 + bind /var/run/ssl-frontend.sock user root mode 600 accept-proxy + +listen http_https_proxy #https监听 + bind :80 + bind :443 ssl crt /etc/haproxy/site.pem #公钥和私钥公共文件 + +listen http_https_proxy_explicit #监听ipv6、ipv4和unix sock文件 + bind ipv6@:80 + bind ipv4@public_ssl:443 ssl crt /etc/haproxy/site.pem + bind unix@ssl-frontend.sock user root mode 600 accept-proxy + +listen external_bind_app1 #监听file descriptor + bind "fd@${FD_APP1}" +``` + +- 生产示例 + +```bash +frontend web_port #可以采用后面形式命名:业务-服务-端口号 + bind :80,:8080 + bind 10.0.0.7:10080,:8801-8810,10.0.0.17:9001-9010 + mode http|tcp #指定负载协议类型 + use_backend #调用的后端服务器组名称 +``` + +## 20.3 Proxies配置-backend + +- 定义一组后端服务器,backend服务器将被frontend进行调用。 + +```bash +mode http|tcp #指定负载协议类型,和对应的frontend必须一致 +option #配置选项 +server #定义后端real server +``` + +注意:option后面加 httpchk,smtpchk,mysql-check,pgsql-check,ssl-hello-chk方法,可用于实现更多应用层检测功能。 + +### 20.3.1 option 配置 + +```bash +check #对指定real进行健康状态检查,如果不加此设置,默认不开启检查 + addr #可指定的健康状态监测IP,可以是专门的数据网段,减少业务网络的流量 + port #指定的健康状态监测端口 + inter #健康状态检查间隔时间,默认2000 ms + fall #后端服务器从线上转为线下的检查的连续失效次数,默认为3 + rise #后端服务器从下线恢复上线的检查的连续有效次数,默认为2 +weight #默认为1,最大值为256,0表示不参与负载均衡,但仍接受持久连接 +backup #将后端服务器标记为备份状态,只在所有非备份主机down机时提供服务,类似Sorry Server +disabled #将后端服务器标记为不可用状态,即维护状态,除了持久模式,将不再接受连接 +redirect prefix http://www.baidu.com/ #将请求临时(302)重定向至其它URL,只适用于http模式 +redir http://www.baidu.com #将请求临时(302)重定向至其它URL,只适用于http模式 +maxconn #当前后端server的最大并发连接数 +backlog #当前端服务器的连接数达到上限后的后援队列长度,注意:不支持backend +``` + +## 20.4 frontend+backend配置实例 + +![image-20210713170548860](07.负载均衡/image-20210713170548860.png) + +- node1和node2搭建一个网站 + +```bash +[root@node1 ~]# yum -y install httpd +[root@node1 ~]# echo "node1 ..." > /var/www/html/index.html +[root@node1 ~]# systemctl start httpd +[root@node1 ~]# systemctl enable httpd +``` + +- haproxy配置文件 + +```bash +[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg +global + maxconn 100000 + chroot /apps/haproxy + stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin + #uid 99 + #gid 99 + user haproxy + group haproxy + daemon + #nbproc 4 + #cpu-map 1 0 + #cpu-map 2 1 + #cpu-map 3 2 + #cpu-map 4 3 + pidfile /var/lib/haproxy/haproxy.pid + log 127.0.0.1 local2 info + +defaults + option http-keep-alive + option forwardfor + maxconn 100000 + mode http + timeout connect 300000ms + timeout client 300000ms + timeout server 300000ms + +listen stats + mode http + bind 0.0.0.0:9999 + stats enable + log global + stats uri /haproxy-status + stats auth admin:123456 + +frontend test-http + bind :80 + mode tcp + use_backend test-http-nodes + +backend test-http-nodes + mode tcp + default-server inter 1000 weight 6 + server web1 192.168.88.20:80 check inter 3000 fall 3 rise 5 weight 2 addr 192.168.88.20 port 3306 + server web2 192.168.88.30:80 check inter 3000 fall 3 rise 5 + +[root@haproxy ~]# systemctl restart haproxy +``` + +- 验证结果 + +![image-20210713171531848](07.负载均衡/image-20210713171531848.png) + +- 因为检查node1的3306接口并没有启动,所以node1始终是down的 + +![image-20210713172303463](07.负载均衡/image-20210713172303463.png) + +## 20.5 listen替代frontend+backend + +​ 使用listen替换上面的frontend和backend的配置方式,可以简化设置,通常只用于TCP协议的应用 + +```bash +listen WEB_PORT_80 + bind :80 + mode http + option forwardfor + # 这个选项的作用是启用或禁用将原始客户端的IP地址添加到HTTP请求的X-Forwarded-For头部中。 + server web1 192.168.88.20:80 check inter 3000 fall 3 rise 5 + server web2 192.168.88.30:80 check inter 3000 fall 3 rise 5 + +[root@haproxy ~]# systemctl restart haproxy +``` + +## 20.6 使用子配置文件保存配置 + +​ 当业务众多时,将所有配置都放在一个配置文件中,会造成维护困难。可以考虑按业务分类,将配置信息拆分,放在不同的子配置文件中,从而达到方便维护的目的。 + +- 创建子配置目录 + +```bash +[root@haproxy ~]# mkdir /etc/haproxy/conf.d/ +``` + +- 创建子配置文件,注意:必须为cfg后缀 + +```bash +[root@haproxy ~]# vim /etc/haproxy/conf.d/test.cfg +listen WEB_PORT_80 + bind :80 + mode http + option forwardfor + balance roundrobin + server web1 192.168.88.20:80 check inter 3000 fall 3 rise 5 + server web2 192.168.88.30:80 check inter 3000 fall 3 rise 5 +``` + +- 添加子配置目录到unit文件中 + +```bash +[root@haproxy ~]# vim /lib/systemd/system/haproxy.service +[Unit] +Description=HAProxy Load Balancer +After=syslog.target network.target + +[Service] +ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/conf.d/ -c -q +ExecStart=/usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/conf.d/ -p /var/lib/haproxy/haproxy.pid +ExecReload=/bin/kill -USR2 $MAINPID + +[Install] +WantedBy=multi-user.target + +[root@haproxy ~]# systemctl daemon-reload +[root@haproxy ~]# systemctl restart haproxy +``` + +# 21. HAProxy调度算法 + +HAProxy通过固定参数`balance`指明对后端服务器的调度算法,该参数可以配置在listen或backend选项中。 + +HAProxy的调度算法分为静态和动态调度算法,但是有些算法可以根据参数在静态和动态算法中相互转换。 + +官方文档:[http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#4-balance](http://www.yunweipai.com/go?_=55fbd0b441aHR0cDovL2Nib250ZS5naXRodWIuaW8vaGFwcm94eS1kY29udi8yLjEvY29uZmlndXJhdGlvbi5odG1sIzQtYmFsYW5jZQ==) + +## 21.1 静态算法 + +​ 静态算法:按照事先定义好的规则轮询公平调度,不关心后端服务器的当前负载、链接数和响应速度等,且无法实时修改权重,只能靠重启HAProxy生效。 + +### 21.1.1 static-rr + +​ static-rr:基于权重的轮询调度,不支持权重的运行时利用socat进行动态调整及后端服务器慢启动,其后端主机数量没有限制,相当于LVS中的 wrr + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode http + log global + balance static-rr + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5 + server web2 10.0.0.27:80 weight 2 check inter 3000 fall 2 rise 5 +``` + +### 21.1.2 first + +​ first:根据服务器在列表中的位置,自上而下进行调度,但是其只会当第一台服务器的连接数达到上限,新请求才会分配给下一台服务,因此会忽略服务器的权重设置,此方式使用较少 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode http + log global + balance first + server web1 192.168.88.20:80 maxconn 2 weight 1 check inter 3000 fall 2 rise 5 + server web2 192.168.88.30:80 weight 1 check inter 3000 fall 2 rise 5 +``` + +测试访问效果,同时运行下面命令,观察结果 + +```bash +while true;do curl http://192.168.88.10/index.html ; sleep 0.1;done +``` + +## 21.2 动态算法 + +​ 动态算法:基于后端服务器状态进行调度适当调整,优先调度至当前负载较低的服务器,且权重可以在haproxy运行时动态调整无需重启。 + +### 21.2.1 roundrobin + +​ roundrobin:基于权重的轮询动态调度算法,支持权重的运行时调整,不同于lvs中的rr轮询模式,HAProxy中的roundrobin支持慢启动(新加的服务器会逐渐增加转发数),其每个后端backend中最多支持4095个real server,支持对real server权重动态调整,roundrobin为默认调度算法 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode http + log global + balance roundrobin + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5 + server web2 10.0.0.27:80 weight 2 check inter 3000 fall 2 rise 5 +``` + +​ 支持动态调整权重 + +```bash +# echo "get weight web_host/web1" | socat stdio /var/lib/haproxy/haproxy.sock +1 (initial 1) + +# echo "set weight web_host/web1 3" | socat stdio /var/lib/haproxy/haproxy.sock + +# echo "get weight web_host/web1" | socat stdio /var/lib/haproxy/haproxy.sock +3 (initial 1) +``` + +### 21.2.2 leastconn + +​ leastconn加权的最少连接的动态,支持权重的运行时调整和慢启动,即当前后端服务器连接最少的优先调度(新客户端连接),比较适合长连接的场景使用,比如:MySQL等场景。 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode http + log global + balance leastconn + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5 + server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5 +``` + +### 21.2.3 random + +​ 在1.9版本开始增加一个叫做random的负载平衡算法,其基于随机数作为一致性hash的key,随机负载平衡对于大型服务器场或经常添加或删除服务器非常有用,支持weight的动态调整,weight较大的主机有更大概率获取新请求 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode http + log global + balance random + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5 + server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5 +``` + +## 21.3 其他算法 + +其它算法即可作为静态算法,又可以通过选项成为动态算法 + +### 21.3.1 source + +​ 源地址hash,基于用户源地址hash并将请求转发到后端服务器,后续同一个源地址请求将被转发至同一个后端web服务器。此方式当后端服务器数据量发生变化时,会导致很多用户的请求转发至新的后端服务器,默认为静态方式,但是可以通过hash-type支持的选项更改 + +​ 这个算法一般是在不插入Cookie的TCP模式下使用,也可给拒绝会话cookie的客户提供最好的会话粘性,适用于session会话保持但不支持cookie和缓存的场景 + +​ 源地址有两种转发客户端请求到后端服务器的服务器选取计算方式,分别是取模法和一致性hash + +#### 21.3.1.1 map-base取模法 + +​ map-based:取模法,对source地址进行hash计算,再基于服务器总权重的取模,最终结果决定将此请求转发至对应的后端服务器。此方法是静态的,即不支持在线调整权重,不支持慢启动,可实现对后端服务器均衡调度。缺点是当服务器的总权重发生变化时,即有服务器上线或下线,都会因总权重发生变化而导致调度结果整体改变,hash-type 指定的默认值为此算法 + +> 所谓取模运算,就是计算两个数相除之后的余数,10%7=3, 7%4=3 map-based算法:基于权重取模,hash(source_ip)%所有后端服务器相加的总权重 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode tcp + log global + balance source + hash-type map-based + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 3 + server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 3 + +[root@haproxy ~]#echo "set weight web_host/10.0.0.27 10" | socat stdio /var/lib/haproxy/haproxy.sock +Backend is using a static LB algorithm and only accepts weights '0%' and '100%'. + +[root@haproxy ~]#echo "set weight web_host/10.0.0.27 0" | socat stdio /var/lib/haproxy/haproxy.sock + +[root@haproxy conf.d]#echo "get weight web_host/10.0.0.27" | socat stdio /var/lib/haproxy/haproxy.sock +0 (initial 1) +``` + +#### 21.3.1.2 一致性hash + +​ 一致性哈希,当服务器的总权重发生变化时,对调度结果影响是局部的,不会引起大的变动,hash(o)mod n ,该hash算法是动态的,支持使用 socat等工具进行在线权重调整,支持慢启动 + +> 1、key1=hash(source_ip)%(2^32) [0---4294967295] +> 2、keyA=hash(后端服务器虚拟ip)%(2^32) +> 3、将key1和keyA都放在hash环上,将用户请求调度到离key1最近的keyA对应的后端服务器 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode tcp + log global + balance source + hash-type consistent + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5 + server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5 +``` + +### 21.3.2 uri + +​ 基于对用户请求的URI的左半部分或整个uri做hash,再将hash结果对总权重进行取模后,根据最终结果将请求转发到后端指定服务器,适用于后端是缓存服务器场景,默认是静态,也可以通过hash-type指定map-based和consistent,来定义使用取模法还是一致性hash。 + +​ 注意:此算法是应用层,所有只支持 mode http ,不支持 mode tcp + +```bash +://:@:/;?# +左半部分:/; +整个uri:/;?# +``` + +- uri 取模法配置示例 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode http + log global + balance uri + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5 + server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5 +``` + +- uri 一致性hash配置示例 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode http + log global + balance uri + hash-type consistent + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5 + server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5 +``` + +### 21.3.3 url_param + +​ url_param对用户请求的url中的 params 部分中的一个参数key对应的value值作hash计算,并由服务器总权重相除以后派发至某挑出的服务器;通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个real server,如果无没key,将按roundrobin算法 + +```bash +假设: +url = http://www.test.com/foo/bar/index.php?key=value + +则: +host = "www.test.com" +url_param = "key=value" +``` + +- url_param取模法配置示例 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode http + log global + balance url_param userid #url_param hash + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5 + server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5 +``` + +- url_param一致性hash配置示例 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode http + log global + balance url_param userid #对url_param的值取hash + hash-type consistent + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5 + server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5 +``` + +### 21.3.4 hdr + +​ 针对用户每个http头部(header)请求中的指定信息做hash,此处由 name 指定的http首部将会被取出并做hash计算,然后由服务器总权重取模以后派发至某挑出的服务器,如无有效的值,则会使用默认的轮询调度。 + +- hdr取模法配置示例 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode http + log global + balance hdr(User-Agent) + #balance hdr(host) + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5 + server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5 +``` + +- 一致性hash配置示例 + +```bash +listen web_host + bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010 + mode http + log global + balance hdr(User-Agent) + hash-type consistent + server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5 + server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5 +``` + +### 21.3.5 rdp-cookie + +​ rdp-cookie对远windows远程桌面的负载,使用cookie保持会话,默认是静态,也可以通过hash-type指定map-based和consistent,来定义使用取模法还是一致性hash。 + +- rdp-cookie取模法配置示例 + +```bash +listen RDP + bind 10.0.0.7:3389 + balance rdp-cookie + mode tcp + server rdp0 10.0.0.17:3389 check fall 3 rise 5 inter 2000 weight 1 +``` + +- rdp-cookie一致性hash配置示例 + +```bash +[root@haproxy ~]#cat /etc/haproxy/conf.d/windows_rdp.cfg +listen magedu_RDP_3389 + bind 172.16.0.100:3389 + balance rdp-cookie + hash-type consistent + mode tcp + server rdp0 10.0.0.200:3389 check fall 3 rise 5 inter 2000 weight 1 + +[root@haproxy ~]#hostname -I +10.0.0.100 172.16.0.100 +``` + +## 21.4 算法总结 + +```bash +static-rr--------->tcp/http 静态 +first------------->tcp/http 静态 + +roundrobin-------->tcp/http 动态 +leastconn--------->tcp/http 动态 +random------------>tcp/http 动态 + +以下静态和动态取决于hash_type是否consistent +source------------>tcp/http +Uri--------------->http +url_param--------->http +hdr--------------->http +rdp-cookie-------->tcp +``` + +## 21.5 各算法使用场景 + +```bash +first #使用较少 + +static-rr #做了session共享的web集群 +roundrobin +random + +leastconn #数据库 +source #基于客户端公网IP的会话保持 + +uri--------------->http #缓存服务器,CDN服务商,蓝汛、百度、阿里云、腾讯 +url_param--------->http + +hdr #基于客户端请求报文头部做下一步处理 + +rdp-cookie #很少使用 +``` + +# 22. HAProxy状态页 + +通过web界面,显示当前HAProxy的运行状态 + +官方帮助:http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#4-stats%20admin + +- 状态页配置项 + +```bash +stats enable #基于默认的参数启用stats page +stats hide-version #将状态页中haproxy版本隐藏 +stats refresh #设定自动刷新时间间隔,默认不自动刷新 +stats uri #自定义stats page uri,默认值:/haproxy?stats +stats realm #账户认证时的提示信息,示例:stats realm HAProxy\ Statistics +stats auth : #认证时的账号和密码,可使用多次,默认:no authentication,可有多行用户 +stats admin { if | unless } #启用stats page中的管理功能 +``` + +- 启用状态页 + +```bash +listen stats + bind :9999 + stats enable + #stats hide-version + stats uri /haproxy-status + stats realm HAPorxy\ Stats\ Page + stats auth haadmin:123456 #两个用户 + stats auth admin:123456 + #stats refresh 30s + stats admin if TRUE #安全原因,不建议打开 +``` + +- 状态页信息 + +```bash +pid = 27134 (process #1, nbproc = 1, nbthread = 1) #pid为当前pid号,process为当前进程号,nbproc和nbthread为一共多少进程和每个进程多少个线程 +uptime = 0d 0h00m04s #启动了多长时间 +system limits: memmax = unlimited; ulimit-n = 200029 #系统资源限制:内存/最大打开文件数/ +maxsock = 200029; maxconn = 100000; maxpipes = 0 #最大socket连接数/单进程最大连接数/最大管道数maxpipes +current conns = 2; current pipes = 0/0; conn rate = 2/sec; bit rate = 0.000 kbps #当前连接数/当前管道数/当前连接速率 +Running tasks: 1/14; idle = 100 % #运行的任务/当前空闲率 +active UP: #在线服务器 +backup UP: #标记为backup的服务器 +active UP, going down: #监测未通过正在进入down过程 +backup UP, going down: #备份服务器正在进入down过程 +active DOWN, going up: #down的服务器正在进入up过程 +backup DOWN, going up: #备份服务器正在进入up过程 +active or backup DOWN: #在线的服务器或者是backup的服务器已经转换成了down状态 +not checked: #标记为不监测的服务器 +active or backup DOWN for maintenance (MAINT) #active或者backup服务器人为下线的 +active or backup SOFT STOPPED for maintenance #active或者backup被人为软下线(人为将weight改成0) +``` + +![image-20210713183017871](07.负载均衡/image-20210713183017871.png) + +## 22.1 backend server信息 + +| session rate(每秒的连接会话信息): | Errors(错误统计信息): | +| ---------------------------------- | --------------------------------- | +| cur:每秒的当前会话数量 | Req:错误请求量 | +| max:每秒新的最大会话数量 | conn:错误链接量 | +| limit:每秒新的会话限制量 | Resp:错误响应量 | +| sessions(会话信息): | Warnings(警告统计信息): | +| cur:当前会话量 | Retr:重新尝试次数 | +| max:最大会话量 | Redis:再次发送次数 | +| limit: 限制会话量 | | +| Total:总共会话量 | Server(real server信息): | +| LBTot:选中一台服务器所用的总时间 | Status:后端机的状态,包括UP和DOWN | +| Last:和服务器的持续连接时间 | LastChk:持续检查后端服务器的时间 | +| Wght:权重 | | +| Bytes(流量统计): | Act:活动链接数量 | +| In:网络的字节输入总量 | Bck:备份的服务器数量 | +| Out:网络的字节输出总量 | Chk:心跳检测时间 | +| Dwn:后端服务器连接后都是DOWN的数量 | | +| Denied(拒绝统计信息): | Dwntme:总的downtime时间 | +| Req:拒绝请求量 | Thrtle:server 状态 | +| Resp:拒绝回复量 | | + +## 22.2 利用状态页实现haproxy服务器的健康性检查 + +- 通过curl 命令对haproxy的状态页的访问实现健康检查 + +```bash +[root@haproxy ~]# curl -I http://admin:123456@192.168.88.10:9999/haproxy-status +HTTP/1.1 200 OK +cache-control: no-cache +content-type: text/html + +[root@haproxy ~]# curl -I -u admin:123456 http://192.168.88.10:9999/haproxy-status +HTTP/1.1 200 OK +cache-control: no-cache +content-type: text/html +``` \ No newline at end of file diff --git a/02.企业服务/07.负载均衡/O1CN01LSQXdk23trU4Dhqsn_!!6000000007314-2-tps-1530-1140.png b/02.企业服务/07.负载均衡/O1CN01LSQXdk23trU4Dhqsn_!!6000000007314-2-tps-1530-1140.png new file mode 100644 index 0000000..4924809 Binary files /dev/null and b/02.企业服务/07.负载均衡/O1CN01LSQXdk23trU4Dhqsn_!!6000000007314-2-tps-1530-1140.png differ diff --git a/02.企业服务/07.负载均衡/O1CN01Lnlvbb1ixt2Ma0OEg_!!6000000004480-2-tps-1530-1140.png b/02.企业服务/07.负载均衡/O1CN01Lnlvbb1ixt2Ma0OEg_!!6000000004480-2-tps-1530-1140.png new file mode 100644 index 0000000..c3a3821 Binary files /dev/null and b/02.企业服务/07.负载均衡/O1CN01Lnlvbb1ixt2Ma0OEg_!!6000000004480-2-tps-1530-1140.png differ diff --git a/02.企业服务/07.负载均衡/O1CN01Uom3sx1DY0ZKQmtk4_!!6000000000227-2-tps-1530-1140.png b/02.企业服务/07.负载均衡/O1CN01Uom3sx1DY0ZKQmtk4_!!6000000000227-2-tps-1530-1140.png new file mode 100644 index 0000000..2b571a4 Binary files /dev/null and b/02.企业服务/07.负载均衡/O1CN01Uom3sx1DY0ZKQmtk4_!!6000000000227-2-tps-1530-1140.png differ diff --git a/02.企业服务/07.负载均衡/O1CN01gbRLLp208HUej1u2p_!!6000000006804-2-tps-1530-1140.png b/02.企业服务/07.负载均衡/O1CN01gbRLLp208HUej1u2p_!!6000000006804-2-tps-1530-1140.png new file mode 100644 index 0000000..45c24f8 Binary files /dev/null and b/02.企业服务/07.负载均衡/O1CN01gbRLLp208HUej1u2p_!!6000000006804-2-tps-1530-1140.png differ diff --git a/02.企业服务/07.负载均衡/image-20210711155724840.png b/02.企业服务/07.负载均衡/image-20210711155724840.png new file mode 100644 index 0000000..8c2bbe1 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210711155724840.png differ diff --git a/02.企业服务/07.负载均衡/image-20210711160121942.png b/02.企业服务/07.负载均衡/image-20210711160121942.png new file mode 100644 index 0000000..b45ab26 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210711160121942.png differ diff --git a/02.企业服务/07.负载均衡/image-20210711160208468.png b/02.企业服务/07.负载均衡/image-20210711160208468.png new file mode 100644 index 0000000..9285af3 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210711160208468.png differ diff --git a/02.企业服务/07.负载均衡/image-20210711160627756.png b/02.企业服务/07.负载均衡/image-20210711160627756.png new file mode 100644 index 0000000..4b2e88b Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210711160627756.png differ diff --git a/02.企业服务/07.负载均衡/image-20210711201349945.png b/02.企业服务/07.负载均衡/image-20210711201349945.png new file mode 100644 index 0000000..737caf7 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210711201349945.png differ diff --git a/02.企业服务/07.负载均衡/image-20210711201400731.png b/02.企业服务/07.负载均衡/image-20210711201400731.png new file mode 100644 index 0000000..2e57a8b Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210711201400731.png differ diff --git a/02.企业服务/07.负载均衡/image-20210711212248164.png b/02.企业服务/07.负载均衡/image-20210711212248164.png new file mode 100644 index 0000000..2a4367f Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210711212248164.png differ diff --git a/02.企业服务/07.负载均衡/image-20210711212943898.png b/02.企业服务/07.负载均衡/image-20210711212943898.png new file mode 100644 index 0000000..db39658 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210711212943898.png differ diff --git a/02.企业服务/07.负载均衡/image-20210711222302235.png b/02.企业服务/07.负载均衡/image-20210711222302235.png new file mode 100644 index 0000000..d5e4d4b Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210711222302235.png differ diff --git a/02.企业服务/07.负载均衡/image-20210711223529884.png b/02.企业服务/07.负载均衡/image-20210711223529884.png new file mode 100644 index 0000000..24a9dda Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210711223529884.png differ diff --git a/02.企业服务/07.负载均衡/image-20210711225314039.png b/02.企业服务/07.负载均衡/image-20210711225314039.png new file mode 100644 index 0000000..f5564e0 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210711225314039.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712035352209.png b/02.企业服务/07.负载均衡/image-20210712035352209.png new file mode 100644 index 0000000..b3d9f77 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712035352209.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712035539886.png b/02.企业服务/07.负载均衡/image-20210712035539886.png new file mode 100644 index 0000000..75e0755 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712035539886.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712073700965.png b/02.企业服务/07.负载均衡/image-20210712073700965.png new file mode 100644 index 0000000..8c72243 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712073700965.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712080133506.png b/02.企业服务/07.负载均衡/image-20210712080133506.png new file mode 100644 index 0000000..8676f2a Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712080133506.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712080410013.png b/02.企业服务/07.负载均衡/image-20210712080410013.png new file mode 100644 index 0000000..561d66c Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712080410013.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712080545578.png b/02.企业服务/07.负载均衡/image-20210712080545578.png new file mode 100644 index 0000000..af75776 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712080545578.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712080949453.png b/02.企业服务/07.负载均衡/image-20210712080949453.png new file mode 100644 index 0000000..7c6e572 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712080949453.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712081307431.png b/02.企业服务/07.负载均衡/image-20210712081307431.png new file mode 100644 index 0000000..882d688 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712081307431.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712082509947.png b/02.企业服务/07.负载均衡/image-20210712082509947.png new file mode 100644 index 0000000..88cc855 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712082509947.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712084005949.png b/02.企业服务/07.负载均衡/image-20210712084005949.png new file mode 100644 index 0000000..13f68c3 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712084005949.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712084209645.png b/02.企业服务/07.负载均衡/image-20210712084209645.png new file mode 100644 index 0000000..e00e9d3 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712084209645.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712100318910.png b/02.企业服务/07.负载均衡/image-20210712100318910.png new file mode 100644 index 0000000..74ec94c Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712100318910.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712102932211.png b/02.企业服务/07.负载均衡/image-20210712102932211.png new file mode 100644 index 0000000..a846c56 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712102932211.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712102939649.png b/02.企业服务/07.负载均衡/image-20210712102939649.png new file mode 100644 index 0000000..12a5dcf Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712102939649.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712103059127.png b/02.企业服务/07.负载均衡/image-20210712103059127.png new file mode 100644 index 0000000..b84bc76 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712103059127.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712111555645.png b/02.企业服务/07.负载均衡/image-20210712111555645.png new file mode 100644 index 0000000..5ead85e Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712111555645.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712111611821.png b/02.企业服务/07.负载均衡/image-20210712111611821.png new file mode 100644 index 0000000..20f835d Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712111611821.png differ diff --git a/02.企业服务/07.负载均衡/image-20210712111708684.png b/02.企业服务/07.负载均衡/image-20210712111708684.png new file mode 100644 index 0000000..0b00558 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210712111708684.png differ diff --git a/02.企业服务/07.负载均衡/image-20210713094311973.png b/02.企业服务/07.负载均衡/image-20210713094311973.png new file mode 100644 index 0000000..70eba45 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210713094311973.png differ diff --git a/02.企业服务/07.负载均衡/image-20210713094402494.png b/02.企业服务/07.负载均衡/image-20210713094402494.png new file mode 100644 index 0000000..5dafd2d Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210713094402494.png differ diff --git a/02.企业服务/07.负载均衡/image-20210713100058631.png b/02.企业服务/07.负载均衡/image-20210713100058631.png new file mode 100644 index 0000000..8573b64 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210713100058631.png differ diff --git a/02.企业服务/07.负载均衡/image-20210713101859303.png b/02.企业服务/07.负载均衡/image-20210713101859303.png new file mode 100644 index 0000000..620f3c2 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210713101859303.png differ diff --git a/02.企业服务/07.负载均衡/image-20210713104814134.png b/02.企业服务/07.负载均衡/image-20210713104814134.png new file mode 100644 index 0000000..1c1a8d7 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210713104814134.png differ diff --git a/02.企业服务/07.负载均衡/image-20210713104830656.png b/02.企业服务/07.负载均衡/image-20210713104830656.png new file mode 100644 index 0000000..2914278 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210713104830656.png differ diff --git a/02.企业服务/07.负载均衡/image-20210713125712794.png b/02.企业服务/07.负载均衡/image-20210713125712794.png new file mode 100644 index 0000000..869307a Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210713125712794.png differ diff --git a/02.企业服务/07.负载均衡/image-20210713170548860.png b/02.企业服务/07.负载均衡/image-20210713170548860.png new file mode 100644 index 0000000..50362fc Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210713170548860.png differ diff --git a/02.企业服务/07.负载均衡/image-20210713171531848.png b/02.企业服务/07.负载均衡/image-20210713171531848.png new file mode 100644 index 0000000..edac6d0 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210713171531848.png differ diff --git a/02.企业服务/07.负载均衡/image-20210713172303463.png b/02.企业服务/07.负载均衡/image-20210713172303463.png new file mode 100644 index 0000000..0141459 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210713172303463.png differ diff --git a/02.企业服务/07.负载均衡/image-20210713183017871.png b/02.企业服务/07.负载均衡/image-20210713183017871.png new file mode 100644 index 0000000..ea68128 Binary files /dev/null and b/02.企业服务/07.负载均衡/image-20210713183017871.png differ diff --git a/03.数据库/01.Mysql.md b/03.数据库/01.Mysql.md new file mode 100644 index 0000000..4986b89 --- /dev/null +++ b/03.数据库/01.Mysql.md @@ -0,0 +1,4065 @@ +# 1. MySQL介绍 + +## 1.1 DBA工作内容 + +![img](01.Mysql/R5j17V2j8h153YXZ.png!thumbnail) + +- 数据管理 + - 增删改查 +- 用户管理 + - grant all on *.* to all@'%' identified by '123'; + - 敲完这条命令就可以等着被开除了( • ̀ω•́ )✧ + - root,运维用户ops,程序连接用户(只读用户,读写用户) +- 集群管理 +- 数据备份、恢复 + - 逻辑备份 + - 物理备份 + - 冷备 + - 热备 + - 温备 + - 全备 + - 增量备份 + - 差异备份 +- 监控 + - 进程,端口 + - 集群状态 + - 主从复制 延时情况 + - SQL读写速率 + - slowlog + +## 1.2 什么是数据 + +数据(data)是事实或观察的结果,是对客观事物的逻辑归纳,是用于表示客观事物的未经加工的的原始素材。 + +数据可以是连续的值,比如声音、图像,称为模拟数据。也可以是离散的,如符号、文字,称为数字数据。 + +在计算机系统中,数据以二进制信息单元0,1的形式表示。 + +**数据的定义:**数据是指对客观事件进行记录并可以鉴别的符号,是对客观事物的性质、状态以及相互关系等进行记载的物理符号或这些物理符号的组合。它是可识别的、抽象的符号。* + +## 1.3 什么是数据库管理系统 + +DBMS(database management system) + +![img](01.Mysql/L8Ndjn2BuNL9IUl2.png!thumbnail) + +## 1.4 数据库管理系统种类 + +### 1.4.1 RDBMS + +以多张二维表的方式来存储,又给多张表建立了一定的关系(关系型数据库) + +### 1.4.2 NoSQL + +nosql 很多以json格式进行存储数据的(mogodb) + +### 1.4.3 RDMS与NoSQL对比 + +#### 1.4.3.1 功能对比 + +| | 关系型数据库 | 非关系型数据库 | +| :------------- | :----------- | :------------- | +| 强大的查询功能 | √ | × | +| 强一致性 | √ | × | +| 二级索引 | √ | × | +| 灵活模式 | × | √ | +| 扩展性 | × | √ | +| 性能 | × | √ | + +#### 1.4.3.2 特点对比 + +- 关系型数据库(RDBMS)的特点: + - 二维表 + - 典型产品Oracle传统企业,MySQL互联网企业 + - 数据存取是通过SQL(Structured Query Language结构化查询语言) + - 最大特点数据安全性方面强(ACID) +- 非关系型数据库(NoSQL:Not only SQL)的特点: + - 不是否定关系型数据库,而是做关系型数据库的补充 + +### 1.4.4 数据库市场 + +#### 1.4.4.1 MySQL的市场应用 + +- 中、大型互联网公司 +- 市场空间:互联网领域第一 +- 趋势明显 +- 同源产品:MariaDB、PerconaDB + +#### 1.4.4.2 类似产品 + +- 微软:SQLserver + - 微软和sysbase合作开发的产品,后来自己开发,windows平台 + - 三、四线小公司,传统行业在用 +- IBM:DB2 + - 市场占有量小 + - 目前只有:国有银行(人行,中国银行,工商银行等)、中国移动应用 +- PostgreSQL +- MongoDB +- Redis + +## 1.5 MySQL发展史 + +- 1979年,报表工具Unireg出现。 +- 1985年,以瑞典David Axmark为首,成立了一家公司(AB前身),ISAM引擎出现。 +- 1990年,提供SQL支持。 +- 1999年-2000年,MySQL AB公司成立,并公布源码,开源化。 +- 2000年4月BDB引擎出现,支持事务。 +- 2008年1月16日 MySQL被Sun公司收购。 +- 2009年4月20日Oracle收购Sun公司,MySQL转入Oracle门下。 + +# 2. MySQL安装 + +## 2.1 MySQL安装方式 + +- rpm、yum安装 + - 安装方便、安装速度快、无法定制 +- 二进制 + - 不需要安装,解压即可使用,不能定制功能 +- 编译安装 + - 可定制,安装慢 + - 四个步骤 + - 解压(tar) + - 生成(./configure) cmake + - 编译(make) + - 安装(make install) + +## 2.2 源码安装 + +- MySQL版本选择: + - https://downloads.mysql.com/archives/community/ + - 5.6:GA(稳定版) 6-12个月 小版本是偶数版是稳定版,奇数版本是开发版 + - 5.7:选择5.17版本以上,支持MGR(MySQL自带的高可用) +- 下载源码,并且配置编译环境 + +```shell +wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.40.tar.gz +tar xzvf mysql-5.6.40.tar.gz +cd mysql-5.6.40 +yum install -y ncurses-devel libaio-devel cmake gcc gcc-c++ glibc +``` + +- 创建mysql用户 + +```shell +useradd mysql -s /sbin/nologin -M +``` + +- 编译并安装 + +```shell +[root@localhost mysql-5.6.40]# mkdir /application +[root@localhost mysql-5.6.40]# cmake . -DCMAKE_INSTALL_PREFIX=/application/mysql-5.6.38 \ +-DMYSQL_DATADIR=/application/mysql-5.6.38/data \ +-DMYSQL_UNIX_ADDR=/application/mysql-5.6.38/tmp/mysql.sock \ +-DDEFAULT_CHARSET=utf8 \ +-DDEFAULT_COLLATION=utf8_general_ci \ +-DWITH_EXTRA_CHARSETS=all \ +-DWITH_INNOBASE_STORAGE_ENGINE=1 \ +-DWITH_FEDERATED_STORAGE_ENGINE=1 \ +-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \ +-DWITHOUT_EXAMPLE_STORAGE_ENGINE=1 \ +-DWITH_ZLIB=bundled \ +-DWITH_SSL=bundled \ +-DENABLED_LOCAL_INFILE=1 \ +-DWITH_EMBEDDED_SERVER=1 \ +-DENABLE_DOWNLOADS=1 \ +-DWITH_DEBUG=0 + + +[root@localhost mysql-5.6.40]# echo $? #查看上条命令是否运行正确,0表示正确 +[root@localhost mysql-5.6.40]# make +[root@localhost mysql-5.6.40]# make install +``` + +- 创建配置文件 + +```shell +ln -s /application/mysql-5.6.38/ /application/mysql +cd /application/mysql/support-files/ +cp my-default.cnf /etc/my.cnf +cp:是否覆盖"/etc/my.cnf"? y +``` + +- 创建启动脚本 + +```shell +cp mysql.server /etc/init.d/mysqld +``` + +- 初始化数据库 + +```shell +cd /application/mysql/scripts/ +yum -y install autoconf +./mysql_install_db --user=mysql --basedir=/application/mysql --datadir=/application/mysql/data/ +``` + +- 启动数据库 + +```shell +mkdir /application/mysql/tmp +chown -R mysql.mysql /application/mysql* +/etc/init.d/mysqld start +``` + +- 配置环境变量 + +```shell +vim /etc/profile.d/mysql.sh +export PATH="/application/mysql/bin:$PATH" +source /etc/profile +``` + +- systemd管理mysql + +```shell +vim /usr/lib/systemd/system/mysqld.service +[Unit] +Description=MySQL Server +Documentation=man:mysqld(8) +Documentation=https://dev.mysql.com/doc/refman/en/using-systemd.html +After=network.target +After=syslog.target +[Install] +WantedBy=multi-user.target +[Service] +User=mysql +Group=mysql +ExecStart=/application/mysql/bin/mysqld --defaults-file=/etc/my.cnf +LimitNOFILE = 5000 + +vim /etc/my.cnf + basedir = /application/mysql/ + datadir = /application/mysql/data + +systemctl daemon-reload +``` + +- 设置mysql开机启动,并且开始mysql服务 + +```shell +systemctl start mysqld +systemctl enable mysqld +``` + +- 设置mysql密码,并且登录测试 + +```shell +mysqladmin -uroot password '123456' +mysql -uroot -p123456 +mysql> show databases; +mysql> \q +Bye +``` + +## 2.3 二进制安装 + +当前运维和开发最常见的做法是二进制安装mysql + +- 下载二进制包并且解压 + +```shell +wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.40-linux-glibc2.12-x86_64.tar.gz +tar xzvf mysql-5.6.40-linux-glibc2.12-x86_64.tar.gz +``` + +- 然后的步骤就和编译安装一样 + +```shell +mkdir /application +mv mysql-5.6.40-linux-glibc2.12-x86_64 /application/mysql-5.6.40 +ln -s /application/mysql-5.6.40 /application/mysql #为了后续写的脚本,方便监控mysql +cd /application/mysql/support-files #该文件夹有mysql初始化(预设)配置文件,覆盖文件是因为注释更全。 +cp my-default.cnf /etc/my.cnf +cp:是否覆盖"/etc/my.cnf"? y +cp mysql.server /etc/init.d/mysqld #mysql.server包含如何启动mysql的脚本命令,让系统知道通过该命令启动mysql时的动作,该目录存放系统中各种服务的启动/停止脚本 +cd /application/mysql/scripts +useradd mysql -s /sbin/nologin -M + yum -y install autoconf #不然会报错 +./mysql_install_db --user=mysql --basedir=/application/mysql --data=/application/mysql/data +vim /etc/profile.d/mysql.sh #写到环境变量的子配置文件内,未修改前只能使用/bin/mysql的命令才能启用,而不能全局使用 +export PATH="/application/mysql/bin:$PATH" + +source /etc/profile #否则要重启系统才生效 +``` + +- 需要注意,官方编译的二进制包默认是在`/usr/local`目录下的,我们需要修改配置文件 + +```shell +sed -i 's#/usr/local#/application#g' /etc/init.d/mysqld /application/mysql/bin/mysqld_safe +``` + +此时不可以通过systemctl命令启动,只能通过/etc/init.d/mysql start启动(nginx也是,如果此时通过这样的命令启动Nginx,会导致systemctl start Nginx失败,因为冲突。) + +- 创建systemd管理文件,并且测试是否正常使用 + +```shell +vim /usr/lib/systemd/system/mysqld.service +[Unit] +Description=MySQL Server +Documentation=man:mysqld(8) +Documentation=https://dev.mysql.com/doc/refman/en/using-systemd.html +After=network.target +After=syslog.target +[Install] +WantedBy=multi-user.target +[Service] +User=mysql +Group=mysql +ExecStart=/application/mysql/bin/mysqld --defaults-file=/etc/my.cnf #强制从my.cnf读配置,不然会从多个路径读配置 +LimitNOFILE = 5000 +server_id = 1 #用作主从的时候生效 + +vim /etc/my.cnf + basedir = /application/mysql/ + datadir = /application/mysql/data + +systemctl daemon-reload +systemctl start mysqld +systemctl enable mysqld +#此时ss -ntl 可以看到3306端口 +mysqladmin -uroot password '123456' +mysql -uroot -p123456 +``` + +# 3. MySQL体系结构管理 + +## 3.1 客户端与服务器模型 + +![img](01.Mysql/tL76EP1rBEQKcpeU.png!thumbnail) + +- mysql是一个典型的C/S服务结构 + + - mysql自带的客户端程序(/application/mysql/bin) + - mysql + - mysqladmin + - mysqldump + +- mysqld一个二进制程序,后台的守护进程 + + - 单进程 + - 多线程 + +- 应用程连接MySQL方式 + + - TCP/IP的连接方式,远程方式,PHP应用可安装于另一台机器 + + 测试需要服务器数据库 + + mysql> grant all privileges on *.* to root@'192.168.128.%' identified by '123456'; + + 客户端数据库 + + yum -y install mariadb #安装客户端 + + mysql -uroot -p123456 -h192.168.128.X -P3306 + + - socket,**默认连接方式,**本地连接,快速无需建立TCP连接,通过/application/mysql/tmp下的mysql.sock文件 + + - `mysql -uroot -p123456 -h127.0.0.1 #TCP连接方式` + +```shell +[root@localhost ~]# mysql -uroot -p123456 -h127.0.0.1 +Warning: Using a password on the command line interface can be insecure. +Welcome to the MySQL monitor. Commands end with ; or \g. +Your MySQL connection id is 4 +Server version: 5.6.40 MySQL Community Server (GPL) +Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. +Oracle is a registered trademark of Oracle Corporation and/or its +affiliates. Other names may be trademarks of their respective +owners. +Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. +mysql> status +-------------- +mysql Ver 14.14 Distrib 5.6.40, for linux-glibc2.12 (x86_64) using EditLine wrapper +Connection id: 4 +Current database: +Current user: root@localhost +SSL: Not in use +Current pager: stdout +Using outfile: '' +Using delimiter: ; +Server version: 5.6.40 MySQL Community Server (GPL) +Protocol version: 10 +Connection: 127.0.0.1 via TCP/IP +Server characterset: latin1 +Db characterset: latin1 +Client characterset: utf8 +Conn. characterset: utf8 +TCP port: 3306 +Uptime: 1 hour 55 min 9 sec +Threads: 2 Questions: 18 Slow queries: 0 Opens: 67 Flush tables: 1 Open tables: 60 Queries per second avg: 0.002 +* 套接字连接方式 + * `mysql -uroot -p123456 -S/tmp/mysql.sock` +``` + +## 3.2 MySQL服务器构成 + +### 3.2.1 mysqld服务结构 + +- 实例=mysqld后台守护进程+Master Thread +干活的Thread+预分配的内存 + - 公司=老板+经理+员工+办公室 + +![img](01.Mysql/k1P7hTxOM3vKCIG2.png!thumbnail) + +- 连接层 + - 验证用户的合法性(ip,端口,用户名) + - 提供两种连接方式(socket,TCP/IP) + - 验证操作权限 + - 提供一个与SQL层交互的专用线程 +- SQL层 + - 接受连接层传来的SQL语句 + - 检查语法 + - 检查语义(DDL,DML,DQL,DCL) + - 解析器,解析SQL语句,生成多种执行计划 + - 优化器,根据多种执行计划,选择最优方式 + - 执行器,执行优化器传来的最优方式SQL + - 提供与存储引擎交互的线程 + - 接收返回数据,优化成表的形式返回SQL + - 将数据存入缓存 + - 记录日志,binlog +- 存储引擎 + - 接收上层的执行结构 + - 取出磁盘文件和相应数据 + - 返回给SQL层,结构化之后生成表格,由专用线程返回给客户端 + +### 3.2.2 mysql逻辑结构 + +MySQL的逻辑对象:做为管理人员或者开发人员操作的对象 + +- 库 +- 表:元数据+真实数据行 +- 元数据:列+其它属性(行数+占用空间大小+权限) +- 列:列名字+数据类型+其他约束(非空、唯一、主键、非负数、自增长、默认值) + +最直观的数据:二维表,必须用库来存放 + +![img](01.Mysql/DsbxGtxBZzy6d1kN.png!thumbnail) + +mysql逻辑结构与Linux系统对比 + +use test; + +create table db01(id int); + +| MySQL | Linux | +| :----------------------- | :------------------- | +| 库 | 目录 | +| show databases; | ls -l / | +| use mysql | cd /mysql | +| 表 | 文件 | +| show tables; | ls | +| 二维表=元数据+真实数据行 | 文件=文件名+文件属性 | + +### 3.2.3 mysql的物理结构 + +- MySQL的最底层的物理结构是数据文件,也就是说,存储引擎层,打交道的文件,是数据文件。 +- 存储引擎分为很多种类(Linux中的FS) +- 不同存储引擎的区别:存储方式、安全性、性能 + +myisam: + +- mysql自带的表部分就是使用的myisam + +```shell +mysql> show create table mysql.user\G; +... +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges' +``` + +![img](01.Mysql/JnL2YEThjuHERQPZ.png!thumbnail) + +innodb: + +- 自己创建一个表,在编译的时候已经默认指定使用innodb + +![img](01.Mysql/lK3mQW6m9VMwIgVD.png!thumbnail) + +![img](01.Mysql/WCtzPCFRsaTUTp3A.png!thumbnail) + +### 3.2.4 段、区、页(块) + +- 段:理论上一个表就是一个段,由多个区构成,(分区表是一个分区一个段) +- 区:连续的多个页构成 + - 页:最小的数据存储单元,默认是16k #写数据就是替换这16k的数据,不然整个文件重写代价太大 + + +# 4. Mysql用户权限管理 + +## 4.1 Mysql用户基础操作 + +- Linux用户的作用 + + - 登录系统 + - 管理系统文件 + +- Linux用户管理 + + - 创建用户:useradd adduser + - 删除用户:userdel + - 修改用户:usermod + +- Mysql用户的作用 + + - 登录Mysql数据库 + - 管理数据库对象 + +- Mysql用户管理 + + - 创建用户:create user + - 删除用户:delete user drop user + - 修改用户:update + +- 用户的定义 + + - username@'主机域' + - 主机域:可以理解为是Mysql登录的白名单 + - 主机域格式: + - 10.1.1.12 + - 10.1.0.1% + - 10.1.0.% + - 10.1.%.% + - % + - localhost + - 192.168.1.1/255.255.255.0 + +- 刚装完mysql数据库该做的事情 + + 先truncate mysql.user;再重启mysql发现进不去了 + + - 设定初始密码 + +```shell +mysqladmin -uroot password '123456' +* 忘记root密码 +/etc/init.d/mysqld stop +mysqld_safe --skip-grant-tables --skip-networking #skip-networking禁止掉3306端口,不允许网络登录 +# 修改root密码 +update user set password=PASSWORD('123456') where user='root' and host='localhost'; +flush privileges; +``` + +## 4.2 用户管理 + +- 创建用户 + +```sql +mysql> create user user01@'192.168.175.%' identified by '123456'; +``` + +- 查看用户 + +```sql +mysql> select user,host from mysql.user; +``` + +- 查看当前用户 + + ```sql + mysql> select user(); + ``` + +- 查看当前数据库 + + ```sql + mysql> select database(); + ``` + +- 删除用户 + +```sql +mysql> drop user user01@'192.168.175.%'; +``` + +- 修改密码 + +```sql +mysql> set password=PASSOWRD('123456') +mysql> update user set password=PASSWORD('user01') where user='root' and host='localhost'; +mysql> grant all privileges on *.* to user01@'192.168.175.%' identified by '123456'; +``` + +## 4.3 用户权限介绍 + +- 权限 + +```sql +INSERT,SELECT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE +``` + +- 每次设定只能有一个属主,没有属组或其他用户的概念 + +```sql +grant all privileges on *.* to user01@''192.168.175.%'' identified by ''123''; + 权限 作用对象 归属 密码 +``` + +作用对象分解 + +- *.* [当前MySQL实例中所有库下的所有表] +- wordpress.* [当前MySQL实例中wordpress库中所有表(单库级别)] +- wordpress.user [当前MySQL实例中wordpress库中的user表(单表级别)] + +企业中权限的设定 + +- 开发人员说:给我开一个用户 +- 沟通 + - 你需要对哪些库、表进行操作 + - 你从哪里连接过来 + - 用户名有没有要求 + - 密码要求 + - 发邮件 +- 一般给开发创建用户权限 + +```sql +grant select,update,delete,insert on *.* to 'user01'@'192.168.175.%' identified by '123456'; +``` + +实验思考问题 + +```sql +#创建wordpress数据库 +create database wordpress; +#使用wordpress库 +use wordpress; +#创建t1、t2表 +create table t1 (id int); +create table t2 (id int); +#创建blog库 +create database blog; +#使用blog库 +use blog; +#创建t1表 +create table tb1 (id int); +``` + +授权 + +```sql +1、grant select on *.* to wordpress@’10.0.0.5%’ identified by ‘123’; +2、grant insert,delete,update on wordpress.* to wordpress@’10.0.0.5%’ identified by ‘123’; +3、grant all on wordpress.t1 to wordpress@’10.0.0.5%’ identified by ‘123’; +``` + +- 一个客户端程序使用wordpress用户登陆到10.0.0.51的MySQL后, + - 对t1表的管理能力? + - 对t2表的管理能力? + - 对tb1表的管理能力? +- 解 + - 同时满足1,2,3,最终权限是1+2+3 + - 同时满足了1和2两个授权,最终权限是1+2 + - 只满足1授权,所以只能select +- 结论 + - 如果在不同级别都包含某个表的管理能力时,权限是相加关系。 + - 但是我们不推荐在多级别定义重复权限。 + - 最常用的权限设定方式是单库级别授权,即:wordpress.* + +# 5. MySQL连接管理 + +- MySQL自带的连接工具 + + mysql -uroot -p123456 -h127.0.0.1 -P3306 -e "show databaese;" + + **不建议使用的方法:** + + ``` + vim /etc/my.cnf + [client] + user = root + password = 123456 + ``` + + + + - mysql + - -u:指定用户 + - -p:指定密码 + - -h:指定主机 + - -P:指定端口 + - -S:指定sock + - -e:指定SQL + +- 第三方连接工具 + + - sqlyog + - navicat + +![img](01.Mysql/agMqU13UrbmuyFz0.png!thumbnail) + +# 6. Mysql启动关闭流程 + +![img](01.Mysql/uCoOhC6Rn5HuTfxg.png!thumbnail) + +- 启动 + + /usr/lib/systemd/system/mysqld.service告知系统从哪执行mysqld + + mysld_safe是个脚本,mysqld才是binary + +```shell +/etc/init.d/mysqld start ------> mysqld_safe ------> mysqld +``` + +- 关闭 + +```shell +/etc/init.d/mysqld stop +mysqladmin -uroot -p123456 shutdown +kill -9 pid ? +killall mysqld ? +pkill mysqld ? +pkill mysqld_safe +``` + +出现问题: + +1. 如果在业务繁忙的情况下,数据库不会释放pid和sock文件 +2. 号称可以达到和Oracle一样的安全性,但是并不能100%达到 +3. 在业务繁忙的情况下,丢数据(补救措施,高可用) + +# 7. Mysql实例初始化配置 + +- 在启动一个实例的时候,必须要知道如下的问题 + - 我不知道我的程序在哪? + - 我也不知道我将来启动后去哪找数据库? + - 将来我启动的时候启动信息和错误信息放在哪? + - 我启动的时候sock文件pid文件放在哪? + - 我启动,你们给了我多少内存? + - ......若干问题 + +![img](01.Mysql/Tw0AJl9FjrygD1S4.png!thumbnail) + +- 预编译:cmake去指定,硬编码到程序当中去 +- 在命令行设定启动初始化配置 + +```shell +--skip-grant-tables +--skip-networking +--datadir=/application/mysql/data +--basedir=/application/mysql +--defaults-file=/etc/my.cnf +--pid-file=/application/mysql/data/db01.pid +--socket=/application/mysql/data/mysql.sock +--user=mysql +--port=3306 +--log-error=/application/mysql/data/db01.err +``` + +- 初始化配置文件(/etc/my.cnf) + - 配置文件读取顺序,通过mysql命令:show variables like 'server_id'; + - /etc/my.cnf + - /etc/mysql/my.cnf + - $MYSQL_HOME/my.cnf(前提是在环境变量中定义了MYSQL_HOME变量 /application/mysql/my.conf) + - defaults-extra-file (类似include) + - ~/my.cnf(隐藏文件) +- --defaults-file:默认配置文件 + - 如果使用./bin/mysqld_safe 守护进程启动mysql数据库时,使用了 --defaults-file=<配置文件的绝对路径>参数,这时只会使用这个参数指定的配置文件。 + +```shell +#cmake: +socket=/application/mysql/tmp/mysql.sock +#命令行: +--socket=/tmp/mysql.sock +#配置文件: +/etc/my.cnf中[mysqld]标签下:socket=/opt/mysql.sock +#default参数: +--defaults-file=/tmp/a.txt配置文件中[mysqld]标签下:socket=/tmp/test.sock +``` + +- 优先级结论 + - 命令行 + - defaults-file + - 配置文件 + - 预编译 +- 初始化配置文件功能 + - 影响实例的启动(mysqld) + - 影响到客户端 +- 配置标签分类 + - [client]所有客户端程序 + - [server]所有服务器程序 + +# 8. MySQL多实例配置 + +## 8.1 多实例 + +- 多套后台进程+线程+内存结构 +- 多个配置文件 + - 多端口 + - 多socket文件 + - 多个日志文件 + - 多个server_id +- 多套数据 + +## 8.2 实战配置 + +```shell +#创建数据目录 +mkdir -p /data/330{7..9} +#创建配置文件 +touch /data/330{7..9}/my.cnf +touch /data/330{7..9}/mysql.log +#编辑3307配置文件 +vim /data/3307/my.cnf +[mysqld] +basedir=/application/mysql +datadir=/data/3307/data +socket=/data/3307/mysql.sock +log_error=/data/3307/mysql.log +log-bin=/data/3307/mysql-bin +server_id=7 +port=3307 +[client] +socket=/data/3307/mysql.sock +#编辑3308配置文件 +vim /data/3308/my.cnf +[mysqld] +basedir=/application/mysql +datadir=/data/3308/data +socket=/data/3308/mysql.sock +log_error=/data/3308/mysql.log +log-bin=/data/3308/mysql-bin +server_id=8 +port=3308 +[client] +socket=/data/3308/mysql.sock +#编辑3309配置文件 +vim /data/3309/my.cnf +[mysqld] +basedir=/application/mysql +datadir=/data/3309/data +socket=/data/3309/mysql.sock +log_error=/data/3309/mysql.log +log-bin=/data/3309/mysql-bin +server_id=9 +port=3309 +[client] +socket=/data/3309/mysql.sock +#初始化3307数据,每条命令需要间隔若干秒,否则失败 +/application/mysql/scripts/mysql_install_db \ +--user=mysql \ +--defaults-file=/data/3307/my.cnf \ +--basedir=/application/mysql --datadir=/data/3307/data +#初始化3308数据,每条命令需要间隔若干秒,否则失败 +/application/mysql/scripts/mysql_install_db \ +--user=mysql \ +--defaults-file=/data/3308/my.cnf \ +--basedir=/application/mysql --datadir=/data/3308/data +#初始化3309数据,每条命令需要间隔若干秒,否则失败 +/application/mysql/scripts/mysql_install_db \ +--user=mysql \ +--defaults-file=/data/3309/my.cnf \ +--basedir=/application/mysql --datadir=/data/3309/data +#修改目录权限 +chown -R mysql.mysql /data/330* +#启动多实例 +mysqld_safe --defaults-file=/data/3307/my.cnf & +mysqld_safe --defaults-file=/data/3308/my.cnf & +mysqld_safe --defaults-file=/data/3309/my.cnf & +#设置每个实例的密码 +#mysqladmin -S /data/3307/mysql.sock -uroot password '123456' +#查看server_id +mysql -S /data/3307/mysql.sock -e "show variables like 'server_id'" +mysql -S /data/3308/mysql.sock -e "show variables like 'server_id'" +mysql -S /data/3309/mysql.sock -e "show variables like 'server_id'" +# 进入单独的mysql实例 +mysql -S /data/3307/mysql.sock -uroot +# 关闭实例,如果设置了密码登录就需要密码了 用参数-p +mysqladmin -S /data/3307/mysql.sock -uroot shutdown +mysqladmin -S /data/3308/mysql.sock -uroot shutdown +mysqladmin -S /data/3309/mysql.sock -uroot shutdown +``` + +# 9. 客户端工具 + +## 9.1 mysql + +- 作用 + - 连接 + - 管理 +- 自带的命令 + +```shell +\h 或 help 或? 查看帮助 +\G 格式化查看数据(key:value) +\T 或 tee 记录日志 \T /tmp/temp.log,临时一次登录有效、永久vim /etc/my.cnf tee= +\c(5.7可以ctrl+c) 结束命令 +\s 或 status 查看状态信息 +\. 或 source 导入SQL数据 +\u或 use 使用数据库 +\q 或 exit 或 quit 退出 +! 或 system 执行shell命令 +``` + +- 接收用户的SQL语句 + +## 9.2 mysqladmin + +- “强制回应 (Ping)”服务器 +- 关闭服务器 +- 创建和删除数据库 +- 显示服务器和版本信息 +- 显示或重置服务器状态变量 +- 设置口令 +- 重新刷新授权表 +- 刷新日志文件和高速缓存 +- 启动和停止复制 + +```shell +[root@localhost ~]# mysqladmin -uroot -p1 create hellodb +[root@localhost ~]# mysqladmin -uroot -p1 drop hellodb +[root@localhost ~]# mysqladmin -uroot -p1 ping 检查服务端状态的 +[root@localhost ~]# mysqladmin -uroot -p1 status 服务器运行状态 +[root@localhost ~]# mysqladmin -uroot -p1 status 服务器状态 --sleep 2 --count 10 每两秒钟显示 +⼀次服务器实时状态⼀共显示10次 +[root@localhost ~]# mysqladmin -uroot -p1 extended-status 显示状态变量 +[root@localhost ~]# mysqladmin -uroot -p1 variables 显示服务器变量 +[root@localhost ~]# mysqladmin -uroot -p1 flush-privileges 数据库重读授权表,等同于reload +[root@localhost ~]# mysqladmin -uroot -p1 flush-tables 关闭所有已经打开的表 +[root@localhost ~]# mysqladmin -uroot -p1 flush-threds 重置线程池缓存 +[root@localhost ~]# mysqladmin -uroot -p1 flush-status 重置⼤多数服务器状态变量 +[root@localhost ~]# mysqladmin -uroot -p1 flush-logs ⽇志滚动。主要实现⼆进制和中继⽇志滚动 +[root@localhost ~]# mysqladmin -uroot -p1 flush-hosts 清楚主机内部信息 +[root@localhost ~]# mysqladmin -uroot -p1 kill 杀死线程 +[root@localhost ~]# mysqladmin -uroot -p1 refresh 相当于同时执⾏flush-hosts flush-logs +[root@localhost ~]# mysqladmin -uroot -p1 shutdown 关闭服务器进程 +[root@localhost ~]# mysqladmin -uroot -p1 version 服务器版本以及当前状态信息 +[root@localhost ~]# mysqladmin -uroot -p1 start-slave 启动复制,启动从服务器复制线程 +[root@localhost ~]# mysqladmin -uroot -p1 stop-slave 关闭复制线程 +``` + +## 9.3 mysqldump + +- 备份数据库和表的内容 + +```shell +mysqldump -uroot -p --all-databases > /backup/mysqldump/all.db +# 备份所有数据库 +mysqldump -uroot -p test > /backup/mysqldump/test.db +# 备份指定数据库 +mysqldump -uroot -p mysql db event > /backup/mysqldump/2table.db +# 备份指定数据库指定表(多个表以空格间隔) +mysqldump -uroot -p test --ignore-table=test.t1 --ignore-table=test.t2 > /backup/mysqldump/test2.db +# 备份指定数据库排除某些表 +``` + +- 还原的方法 + +```shell +mysqladmin -uroot -p create db_name +mysql -uroot -p db_name < /backup/mysqldump/db_name.db +# 注:在导入备份数据库前,db_name如果没有,是需要创建的; 而且与db_name.db中数据库名是一样的才可以导入。 +mysql > use db_name +mysql > source /backup/mysqldump/db_name.db +# source也可以还原数据库 +``` + +# 10. SQL语句 + +- SQL是结构化的查询语句 +- SQL的种类 + - DDL:数据定义语句 + - DCL:数据控制语言 + - DML:数据操作语言 + - DQL:数据查询语言 + +## 10.1 DDL数据定义语句 + +- 对库或者表进行操作的语句 +- 创建数据库 + +```sql +create database db01; +# 创建数据库 +create database DB01; +# 数据库名区分大小写(注意windows里面不区分) +# show variables like 'lower%'; 可以看到lower_case_table_names默认0是区分大小写通过/etc/my.cnf [mysqld]下可以永久修改 +show databases; +# 查看数据库(DQL) +show create database db01; +# 查看创建数据库语句 +help create database; +# 查看创建数据库语句帮助 +create database db02 charset utf8; +# 创建数据库的时候添加属性 +``` + +- 删除数据库 + +```sql +drop database db02; +# 删除数据库db02 +``` + +- 修改定义库 + +```sql +alter database db01 charset utf8; +show create database db01; +``` + +- 创建表 + +```sql +help create table; +# 查看创表语句的帮助 +create table student( +sid int, +sname varchar(20), +sage tinyint, +sgender enum('m','f'), +comtime datetime; +) +# 创建表,并且定义每一列 +``` + +- 数据类型(下面有完整的) + + | int | 整数 -231~230 | + | :------- | :-------------------- | + | tinyint | 整数 -128~127 | + | varchar | 字符类型(可变长) | + | char | 字符类型(定长) | + | enum | 枚举类型 | + | datetime | 时间类型 年月日时分秒 | + +```sql +create table student( +sid int not null primary key auto_increment comment '学号', +sname varchar(20) not null comment '学生姓名', +sgender enum('m','f') not null default 'm' comment '学生性别', +cometime datetime not null comment '入学时间' +)charset utf8 engine innodb; +# 带数据属性创建学生表 +show create table student; +# 查看建表语句 +show tables; +# 查看表 +desc student; +# 查看表中列的定义信息 +``` + +- 数据属性 + + | not null | 不允许是空 | + | :------------- | :---------------------------------------- | + | primary key | 主键(唯一且非空) | + | auto_increment | 自增,此列必须是primary key或者unique key | + | unique key | 单独的唯一的 | + | default | 默认值 | + | unsigned | 非负数 | + | comment | 注释 | + +- 删除表 + +```sql +drop table student; +``` + +- 修改表的定义 + +```sql +alter table student rename stu; +# 修改表名 +alter table stu add age int; +# 添加列和列数据类型的定义 +alter table stu add test varchar(20),add qq int; +# 添加多个列 +alter table stu add classid varchar(20) first; +# 指定位置进行添加列(表首) +alter table stu add phone int after age; +# 指定位置进行添加列(指定列) +alter table stu drop qq; +# 删除指定的列及定义 +alter table stu modify sid varchar(20); +# 修改列及定义(列属性) +alter table stu change phone telphone char(20); +# 修改列及定义(列名及属性) +``` + +## 10.2 DCL数据控制语言 + +- DCL是针对权限进行控制 +- 授权 + +```sql +grant all on *.* to root@'192.168.175.%' identified by '123456' +# 授予root@'192.168.175.%'用户所有权限(非超级管理员) +grant all on *.* to root@'192.168.175.%' identified by '123456' with grant option; +# 授权一个超级管路员 +max_queries_per_hour:一个用户每小时可发出的查询数量 +max_updates_per_hour:一个用户每小时可发出的更新数量 +max_connections_per_hour:一个用户每小时可连接到服务器的次数 +max_user_connections:允许同时连接数量 +``` + +- 收回权限 + +```sql +revoke select on *.* from root@'192.168.175.%'; +# 收回select权限 +show grants for root@'192.168.175.%'; +# 查看权限 +``` + +## 10.3 DML数据操作语言 + +- 操作表中的数据 +- 插入数据 + +```sql +insert into stu valus('linux01',1,NOW(),'zhangsan',20,'m',NOW(),110,123456); +# 基础用法,插入数据 +insert into stu(classid,birth.sname,sage,sgender,comtime,telnum,qq) values('linux01',1,NOW(),'zhangsan',20,'m',NOW(),110,123456); +# 规范用法,插入数据 +insert into stu(classid,birth.sname,sage,sgender,comtime,telnum,qq) values('linux01',1,NOW(),'zhangsan',20,'m',NOW(),110,123456), +('linux02',2,NOW(),'zhangsi',21,'f',NOW(),111,1234567); +# 插入多条数据 +insert into stu(classid,birth.sname,sage,sgender,comtime,qq) values('linux01',1,NOW(),'zhangsan',20,'m',NOW(),123456); +#少了电话 +``` + +- 更新数据 + +```sql +update student set sgender='f'; +# 不规范 +update student set sgender='f' where sid=1; +# 规范update修改 +update student set sgender='f' where 1=1; +# 如果非要全表修改 +update mysql.user set password=PASSWORD('123456') where user='root' and host='localhost'; +# 修改密码,需要刷新权限flush privileges +``` + +- 删除数据 + +```sql +delete from student; +# 不规范 +delete from student where sid=3; +# 规范删除(危险) +truncate table student; +# DDL清空表中的内容,恢复表原来的状态,自增重新从1开始,否则delete依旧正常增长 +``` + +- 使用伪删除 + - 有时候一些重要数据不能直接删除,只能伪删除,因为以后还得使用呢 + - 使用update代替delete,将状态改成删除状态,在查询的时候就可以不显示被标记删除的数据 + +```sql +alter table student add status enum(1,0) default 1; +# 额外添加一个状态列 +update student set status='0' where sid=1; +# 使用update +select * from student where status=1; +# 应用查询存在的数据 +``` + +## 10.4 DQL数据查询语言 + +- select:基础用法 +- 演示用的SQL文件下载:https://download.s21i.faiusr.com/23126342/0/0/ABUIABAAGAAgzcXwhQYozuPv2AE?f=world.sql&v=1622942413 + +```sql +mysql -uroot -p123 < world.sql +或者mysql> \. /root/world.sql +# 常用用法 +select countrycode,district from city; +# 常用用法 +select countrycode from city; +# 查询单列 +select countrycode,district from city limit 2; +select id,countrycode,district from city limit 2,2; #从第二个开始继续显示2个 +# 行级查询 +select name,population from city where countrycode='CHN'; +# 条件查询 +select name,population from city where countrycode='CHN' and district='heilongjiang'; +# 多条件查询 +select name,population,countrycode from city where countrycode like '%H%' limit 10; +# 模糊查询 +select id,name,population,countrycode from city order by countrycode limit 10; +# 排序查询(顺序) +select id,name,population,countrycode from city order by countrycode desc limit 10; +# 排序查询(倒序) +select * from city where population>=1410000; +# 范围查询(>,<,>=,<=,<>) +select * from city where countrycode='CHN' or countrycode='USA'; +# 范围查询OR语句 +select * from city where countrycode in ('CHN','USA'); +# 范围查询IN语句 +select country.name,city.name,city.population,country.code from city,country where city.countrycode=country.code and city.population < 100; +# 多表查询 +``` + +# 11. 字符集定义 + +- 什么是字符集(Charset) +- 字符集:是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。 + +![img](01.Mysql/157ueBBseL6MvenH.png!thumbnail) + +- MySQL数据库的字符集 + - 字符集(CHARACTER) + - 校对规则(COLLATION) +- MySQL中常见的字符集 + - UTF8 + - LATIN1 + - GBK +- 常见校对规则 + - ci:大小写不敏感 + - cs或bin:大小写敏感 +- 我们可以使用以下命令查看 + +```sql +show charset; +show collation; +``` + +字符集设置 + +- 操作系统级别 + +```shell +source /etc/sysconfig/i18n +echo $LANG +``` + +- Mysql实例级别 + +```shell +cmake . +-DDEFAULT_CHARSET=utf8 \ +-DDEFAULT_COLLATION=utf8_general_ci \ +-DWITH_EXTRA_CHARSETS=all \ +# 在编译的时候指定 +[mysqld] +character-set-server=utf8 +# 在配置文件中指定 +mysql> create database db01 charset utf8 default collate = utf8_general_ci; +# 建库的时候 +mysql> CREATE TABLE `test` ( +`id` int(4) NOT NULL AUTO_INCREMENT, +`name` char(20) NOT NULL, +PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8; +# 建表的时候 +mysql> alter database db01 CHARACTER SET utf8 collate utf8_general_ci; +mysql> alter table t1 CHARACTER SET utf8; +# 修改字符集 +``` + +# 12. select的高级用法 + +- 多表连接查询(连表查询) + +```sql +create table t1(id int primary key auto_increment,name varchar(20)) ENGINE=InnoDB CHARSET=utf8; +create table t2(id int primary key auto_increment,score int) ENGINE=InnoDB CHARSET=utf8; +insert into t1(name) values('cs'),('tj'),('lz'); +insert into t2(score) values(30),(80),(82); +select * from t1; +select * from t2; +``` + +- 传统连接(只能内连接,只能取交集) + +```sql +select t1.name,t2.score from t1,t2 where t1.id=t2.id and t2.score > 60; +# 查出及格 +#世界上小于100人的人口城市是哪个国家的? +select city.name,city.countrycode,country.name +from city,country +where city.countrycode=country.code +and city.population<100; +# 世界上小于100人的人口城市是哪个国家,说的什么语言? +国家人口数量 城市名 国家名 语言 +country.population, city.name, country.name, countrylanguage.Language +select country.population,city.name,country.name,countrylanguage.Language from city,country,countrylanguage where city.countrycode=country.code and countrylanguage.countrycode=country.code and country.population<100; +``` + +- NATURAL JOIN(自连接的表要有共同的列名字) + +```sql +SELECT city.name,city.countrycode ,countrylanguage.language ,city.population +FROM city NATURAL JOIN countrylanguage +WHERE population > 1000000 +ORDER BY population; +``` + +- 企业中多表连接查询(内连接) + +```sql +select city.name,city.countrycode,country.name +from city join country on city.countrycode=country.code +where city.population<100; +``` + +建议:使用join语句时,小表在前,大表在后。 + +- 外连接 + +```sql +select city.name,city.countrycode,country.name +from city left join country +on city.countrycode=country.code +and city.population<100; +``` + +- UNION(合并查询) + +```sql +mysql> select * from city where countrycode='CHN' or countrycode='USA'; +#范围查询OR语句 +mysql> select * from city where countrycode in ('CHN','USA'); +#范围查询IN语句 +替换为: +mysql> select * from city where countrycode='CHN' +union all +select * from city where countrycode='USA' limit 10 +``` + +- union:去重复合并 +- union all :不去重复 +- 使用情况:union explain select name,countrycode from city where id=1; +``` + +- MySQL查询数据的方式 + - 全表扫描(在explain语句结果中type为ALL,例如select * from XXX) + - 业务确实要获取所有数据 + - 不走索引导致的全表扫描 + - 没索引 + - 索引创建有问题 + - 语句有问题 + - 生产中,mysql在使用全表扫描时的性能是极其差的,所以MySQL尽量避免出现全表扫描 + - 索引扫描 + - 常见的索引扫描类型 + - index + - range + - ref + - eq_ref + - const + - system + - null + - 从上到下,性能从最差到最好,我们认为至少要达到range级别 + +### 14.4.1 index + +- Full Index Scan,index与ALL区别为index类型只遍历索引树。 + +### 14.4.2 range + +- 索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行。显而易见的索引范围扫描是带有between或者where子句里带有<,>查询。 + +```sql +mysql> alter table city add index idx_city(population); +mysql> explain select * from city where population>30000000; +``` + +### 14.4.3 ref + +- 用非唯一索引扫描或者唯一索引的前缀扫描,返回匹配某个单独值的记录行。 + +```sql +mysql> alter table city drop key idx_code; +mysql> explain select * from city where countrycode='chn'; +mysql> explain select * from city where countrycode in ('CHN','USA'); +mysql> explain select * from city where countrycode='CHN' union all select * from city where countrycode='USA'; +``` + +### 14.4.4 eq_ref + +- 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件A + +```sql +join B +on A.sid=B.sid +``` + +### 14.4.5 const、system + +- 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。 +- 如将主键置于where列表中,MySQL就能将该查询转换为一个常量 + +```sql +mysql> explain select * from city where id=1000; +``` + +### 14.4.6 NULL + +- MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。 + +```sql +mysql> explain select * from city where id=1000000000000000000000000000; +``` + +### 14.4.7 Extra(扩展) + +- Using temporary +- Using filesort 使用了默认的文件排序(如果使用了索引,会避免这类排序) +- Using join buffer +- 如果出现Using filesort请检查order by ,group by ,distinct,join 条件列上没有索引 + +```sql +mysql> explain select * from city where countrycode='CHN' order by population; +``` + +- 当order by语句中出现Using filesort,那就尽量让排序值在where条件中出现 + +```sql +mysql> explain select * from city where population>30000000 order by population; +mysql> select * from city where population=2870300 order by population; +* key_len: 越小越好 +* 前缀索引去控制,rows: 越小越好 +``` + +## 14.5 建立索引的原则(规范) + +- 为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索引和创建什么类型的索引。 +- 选择唯一性索引 + - 唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。 + +> 例如: +> 学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。 +> 如果使用姓名的话,可能存在同名现象,从而降低查询速度。 +> 主键索引和唯一键索引,在查询中使用是效率最高的。 + +```sql +select count(*) from world.city; +select count(distinct countrycode) from world.city; +select count(distinct countrycode,population ) from world.city; +* 注意:如果重复值较多,可以考虑采用联合索引 +``` + +- 为经常需要排序、分组和联合操作的字段建立索引 + - 经常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作会浪费很多时间。 + - 如果为其建立索引,可以有效地避免排序操作 +- 为常作为查询条件的字段建立索引 + - 如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。 + - 因此,为这样的字段建立索引,可以提高整个表的查询速度。 + - 如果经常作为条件的列,重复值特别多,可以建立联合索引 +- 尽量使用前缀来索引 + - 如果索引字段的值很长,最好使用值的前缀来索引。例如,TEXT和BLOG类型的字段,进行全文检索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度。 +- 限制索引的数目 + - 索引的数目不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。 + - 修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。 +- 删除不再使用或者很少使用的索引 + - 表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。 + +重点关注: + +- 没有查询条件,或者查询条件没有建立索引 + +```sql +select * from table; +select * from tab where 1=1; +# 全表扫描 +``` + +- 在业务数据库中,特别是数据量比较大的表,是没有全表扫描这种需求。 + - 对用户查看是非常痛苦的。 + - 对服务器来讲毁灭性的。 + - SQL改写成以下语句 + +```sql +# 情况1 +select * from table; +#全表扫描 +selec * from tab order by price limit 10; +#需要在price列上建立索引 +# 情况2 +select * from table where name='zhangsan'; +#name列没有索引 +1、换成有索引的列作为查询条件 +2、将name列建立索引 +``` + +- 查询结果集是原表中的大部分数据,应该是25%以上 + +```sql +mysql> explain select * from city where population>3000 order by population; +* 如果业务允许,可以使用limit控制 +* 结合业务判断,有没有更好的方式。如果没有更好的改写方案就尽量不要在mysql存放这个数据了,放到redis里面。 +``` + +- 索引本身失效,统计数据不真实 + - 索引有自我维护的能力。 + - 对于表内容变化比较频繁的情况下,有可能会出现索引失效。 + - 重建索引就可以解决 +- 查询条件使用函数在索引列上或者对索引列进行运算,运算包括(+,-,*等) + +```sql +错误的例子:select * from test where id-1=9; +正确的例子:select * from test where id=10; +``` + +- 隐式转换导致索引失效.这一点应当引起重视,也是开发中经常会犯的错误 + +```sql +mysql> create table test (id int ,name varchar(20),telnum varchar(10)); +# 例如列类型为整形,非要在检索条件中用字符串 +mysql> insert into test values(1,'zs','110'),(2,'l4',120),(3,'w5',119),(4,'z4',112); +mysql> explain select * from test where telnum=120; +mysql> alter table test add index idx_tel(telnum); +mysql> explain select * from test where telnum=120; +mysql> explain select * from test where telnum=120; +mysql> explain select * from test where telnum='120'; +``` + +- <> ,not in 不走索引 + +```sql +mysql> select * from tab where telnum <> '1555555'; +mysql> explain select * from tab where telnum <> '1555555'; +``` + +- 单独的>,<,in 有可能走,也有可能不走,和结果集有关,尽量结合业务添加limit +- or或in尽量改成union + +```sql +EXPLAIN SELECT * FROM teltab WHERE telnum IN ('110','119'); +#改写成 +EXPLAIN SELECT * FROM teltab WHERE telnum='110' +UNION ALL +SELECT * FROM teltab WHERE telnum='119' +``` + +- like "%_" 百分号在最前面不走索引 + +```sql +#走range索引扫描 +EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%'; +#不走索引 +EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110'; +``` + +%linux%类的搜索需求,可以使用Elasticsearch -------> ELK + +- 单独引用联合索引里非第一位置的索引列 + +```sql +CREATE TABLE t1 (id INT,NAME VARCHAR(20),age INT ,sex ENUM('m','f'),money INT); +ALTER TABLE t1 ADD INDEX t1_idx(money,age,sex); +DESC t1 +SHOW INDEX FROM t1 +#走索引的情况测试 +EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30 AND sex='m'; +#部分走索引 +EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30; +EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND sex='m'; +#不走索引 +EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=20 +EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=30 AND sex='m'; +EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE sex='m'; +``` + +# 15. MySQL的存储引擎 + +## 15.1 存储引擎简介 + +![img](01.Mysql/KkaagjFIBB8C0CCF.png!thumbnail) + +- 文件系统: + - 操作系统组织和存取数据的一种机制。 + - 文件系统是一种软件。 +- 文件系统类型:ext2 3 4 ,xfs 数据 + - 不管使用什么文件系统,数据内容不会变化 + - 不同的是,存储空间、大小、速度。 +- MySQL引擎: + - 可以理解为,MySQL的“文件系统”,只不过功能更加强大。 +- MySQL引擎功能: + - 除了可以提供基本的存取功能,还有更多功能事务功能、锁定、备份和恢复、优化以及特殊功能 + - 总之,存储引擎的各项特性就是为了保障数据库的安全和性能设计结构。 + +## 15.2 MySQL自带的存储引擎类型 + +- MySQL 提供以下存储引擎: + - InnoDB + - MyISAM + - MEMORY + - ARCHIVE + - FEDERATED + - EXAMPLE + - BLACKHOLE + - MERGE + - NDBCLUSTER + - CSV +- 还可以使用第三方存储引擎: + - MySQL当中插件式的存储引擎类型 + - MySQL的两个分支 + - perconaDB + - mariaDB + +```sql +mysql> show engines +#查看当前MySQL支持的存储引擎类型 +mysql> select table_schema,table_name,engine from information_schema.tables where engine='innodb'; +#查看innodb的表有哪些 +mysql> select table_schema,table_name,engine from information_schema.tables where engine='myisam'; +#查看myisam的表有哪些 +``` + +- innodb和myisam的区别 + +```shell +#进入mysql目录 +[root@localhost~l]# cd /application/mysql/data/mysql +#查看所有user的文件 +[root@localhost mysql]# ll user.* +-rw-rw---- 1 mysql mysql 10684 Mar 6 2017 user.frm +-rw-rw---- 1 mysql mysql 960 Aug 14 01:15 user.MYD +-rw-rw---- 1 mysql mysql 2048 Aug 14 01:15 user.MYI +#进入word目录 +[root@localhost world]# cd /application/mysql/data/world/ +#查看所有city的文件 +[root@localhost world]# ll city.* +-rw-rw---- 1 mysql mysql 8710 Aug 14 16:23 city.frm +-rw-rw---- 1 mysql mysql 688128 Aug 14 16:23 city.ibd +``` + +## 15.3 innodb存储引擎的简介 + +- 在MySQL5.5版本之后,默认的存储引擎,提供高可靠性和高性能。 + +- 优点: + + - 事务安全(遵从 ACID) + + - MVCC(Multi-Versioning Concurrency Control,多版本并发控制) + + - InnoDB 行级别锁定,另一种DB是表级别的 + + - Oracle 样式一致非锁定读取 + + - 表数据进行整理来优化基于主键的查询 + + - 支持外键引用完整性约束 + + - 大型数据卷上的最大性能 + + - 将对表的查询与不同存储引擎混合 + + - 出现故障后快速自动恢复 + + - 用于在内存中缓存数据和索引的缓冲区池 + + | 功能 | 支持 | 功能 | 支持 | + | :----------- | :--- | :----------------- | :--- | + | 存储限制 | 64TB | 索引高速缓存 | 是 | + | MVCC | 是 | 数据高速缓存 | 是 | + | B树索引 | 是 | 自适应散列索引 | 是 | + | 群集索引 | 是 | 复制 | 是 | + | 压缩数据 | 是 | 更新数据字典 | 是 | + | 加密数据 | 是 | 地理空间数据类型 | 是 | + | 查询高速缓存 | 是 | 地理空间索引 | 否 | + | 事务 | 是 | 全文搜索索引 | 是 | + | 锁定粒度 | 行 | 群集数据库 | 否 | + | 外键 | 是 | 备份和恢复 | 是 | + | 文件格式管理 | 是 | 快速索引创建 | 是 | + | 多个缓冲区池 | 是 | performance_schema | 是 | + | 更改缓冲 | 是 | 自动故障恢复 | 是 | + +- innodb核心特性 + + - MVCC + - 事务 + - 行级锁 + - 热备份 + - Crash Safe Recovery(自动故障恢复) + +- 查看存储引擎 + + - 使用 SELECT 确认会话存储引擎 + +```sql +SELECT @@default_storage_engine; +# 查询默认存储引擎 +``` + +- 使用 SHOW 确认每个表的存储引擎 + +```sql +SHOW CREATE TABLE City\G +SHOW TABLE STATUS LIKE 'CountryLanguage'\G +# 查看表的存储引擎 +``` + +- 使用 INFORMATION_SCHEMA 确认每个表的存储引擎 + +```sql +SELECT TABLE_NAME, ENGINE FROM INFORMATION_SCHEMA.TABLESWHERE TABLE_NAME = 'City'AND TABLE_SCHEMA = 'world'\G +# 查看表的存储引擎 +``` + +- 存储引擎的设置 + - 在启动配置文件中设置服务器存储引擎 + +```sql +[mysqld] +default-storage-engine= +# 在配置文件的[mysqld]标签下添加 +``` + +- 使用 SET 命令为当前客户机会话设置 + +```sql +SET @@storage_engine= +# 在MySQL命令行中临时设置 +``` + +- 在 CREATE TABLE 语句指定 + +```sql +CREATE TABLE t (i INT) ENGINE = ; +# 建表的时候指定存储引擎 +``` + +## 15.4 【实战】存储引擎切换 + +- 项目背景: + - 公司原有的架构:一个展示型的网站,LAMT,MySQL5.1.77版本(MYISAM),50M数据量。 +- 小问题不断: + - 表级锁:对表中任意一行数据修改类操作时,整个表都会锁定,对其他行的操作都不能同时进行。 + - 不支持故障自动恢复(CSR):当断电时有可能会出现数据损坏或丢失的问题。 +- 解决方案: + - 提建议将现有的MYISAM引擎替换为Innodb,将版本替换为5.6.38 + - 如果使用MYISAM会产生”小问题”,性能安全不能得到保证,使用innodb可以解决这个问题。 + - 5.1.77版本对于innodb引擎支持不够完善,5.6.38版本对innodb支持非常完善了。 +- 实施过程和注意要素 + - 备份生产库数据(mysqldump) + +```sql +#[root@db01 ~]# mysqldump -uroot -p123 -A --triggers -R --master-data=2 >/tmp/full.sql +#由于没有开启bin logging所以去掉 --master-data=2 +[root@db01 ~]# mysqldump -uroot -p123 -A --triggers -R >/tmp/full.sql +``` + +- 准备一个5.6.38版本的新数据库 + - 对备份数据进行处理(将engine字段替换) + +```shell +[root@db01 ~]# sed -i 's#ENGINE=MYISAM#ENGINE=INNODB#g' /tmp/full.sql +``` + +- 将修改后的备份恢复到新库 +- 应用测试环境连接新库,测试所有功能 +- 停应用,将备份之后的生产库发生的新变化,补偿到新库 +- 应用割接到新数据库 + +## 15.5 表空间介绍 + +![img](01.Mysql/5TsFS9HElaslP5ss.png!thumbnail) + +- 5.5版本以后出现共享表空间概念 +- 表空间的管理模式的出现是为了数据库的存储更容易扩展 +- 5.6版本中默认的是独立表空间 + - 共享表空间 + +```sql +[root@localhost ~]# ll /application/mysql/data/ +-rw-rw----. 1 mysql mysql 12582912 6月 8 09:43 ibdata1 +# 物理查看 +mysql> show variables like '%path%'; +innodb_data_file_path = ibdata1:12M:autoextend +``` + +- 5.6版本中默认存储 + - 系统数据 + - undo + - 临时表 + - 5.7版本中默认会将undo和临时表独立出来,5.6版本也可以独立,只不过需要在初始化的时候进行配置 +- 共享表空间扩展配置方法 + +```shell +#编辑配置文件 +[root@db01 ~]# vim /etc/my.cnf +[mysqld] +#注意,idata1文件已存在,可能超过50M导致mysqld重启不成功,建议重命名了再重启。 +innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend +``` + +- 独立表空间 + - 对于用户自主创建的表,会采用此种模式,每个表由一个独立的表空间进行管理 + +```shell +[root@localhost ~]# ll /application/mysql/data/world/ +-rw-rw----. 1 mysql mysql 589824 6月 6 10:23 city.ibd +# 物理查看 +mysql> show variables like '%per_table%'; +innodb_file_per_table = ON +``` + +## 15.6 【实战】数据库服务损坏 + +- 在没有备份数据的情况下,突然断电导致表损坏,打不开数据库。 + +1. 拷贝库目录到新库中 + +```shell +[root@db01 ~]# cp -r /application/mysql/data/world/ /data/3307/data/ +[root@db01 ~]# chown -R mysql.mysql /application/mysql/data/world +``` + +1. 启动新数据库 + +```shell +#pkill mysqld 先干掉mysql +[root@db01 ~]# mysqld_safe --defaults-file=/data/3307/my.cnf & +``` + +1. 登陆数据库查看 + +```sql +mysql> show databases; +``` + +1. 查询表中数据 + +```sql +mysql> select * from city; +ERROR 1146 (42S02): Table 'world.city' doesn't exist +#先 use world; + +``` + +1. 找到**以前的表**结构在新库中创建表,此处演示用的show命令实际需要从原来的开发文档查看 + +```sql +mysql> show create table world.city; +#删掉外键创建语句 +CREATE TABLE `city` ( + `ID` int(11) NOT NULL AUTO_INCREMENT, + `Name` char(35) NOT NULL DEFAULT '', + `CountryCode` char(3) NOT NULL DEFAULT '', + `District` char(20) NOT NULL DEFAULT '', + `Population` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`), + KEY `CountryCode` (`CountryCode`), + KEY `idx_city` (`Population`,`CountryCode`), + CONSTRAINT `city_ibfk_1` FOREIGN KEY (`CountryCode`) REFERENCES `country` (`Code`) +) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1; + + +mysql> CREATE TABLE `city_new` ( + `ID` int(11) NOT NULL AUTO_INCREMENT, + `Name` char(35) NOT NULL DEFAULT '', + `CountryCode` char(3) NOT NULL DEFAULT '', + `District` char(20) NOT NULL DEFAULT '', + `Population` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`), + KEY `CountryCode` (`CountryCode`) + # KEY `idx_city` (`Population`,`CountryCode`), + #干掉外键的约束,否则失败 + #CONSTRAINT `city_ibfk_1` FOREIGN KEY (`CountryCode`) REFERENCES `country` (`Code`) +) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1; + + +``` + +1. 删除表空间文件 + +```sql +mysql> alter table city_new discard tablespace; +``` + +1. 拷贝旧表空间文件 + +```sql +[root@db01 world]# cp /data/3307/data/world/city.ibd /data/3307/data/world/city_new.ibd +``` + +1. 授权 + +```sql +[root@db01 world]# cd /data/3307/data/world +[root@db01 world]# chown -R mysql.mysql * +``` + +1. 导入表空间 + +```sql +mysql> alter table city_new import tablespace; +mysql> alter table city_new rename city; +``` + +## 15.7 事务 + +- 事务的定义 + - 主要针对DML语句(update,delete,insert)一组数据操作执行步骤,这些步骤被视为一个工作单元 + - 用于对多个语句进行分组 + - 可以在多个客户机并发访问同一个表中的数据时使用 + - 所有步骤都成功或都失败 + - 如果所有步骤正常,则执行 + - 如果步骤出现错误或不完整,则取消 +- 交易的概念 + - 物与物的交换(古代) + - 货币现金与实物的交换(现代1) + - 虚拟货币与实物的交换(现代2) + - 虚拟货币与虚拟实物交换(现代3) +- 事务ACID特性 + - Atomic(原子性) + - 所有语句作为一个单元全部成功执行或全部取消。 + - Consistent(一致性) + - 如果数据库在事务开始时处于一致状态,则在执行该事务期间将保留一致状态。 + - Isolated(隔离性) + - 事务之间不相互影响。 + - Durable(持久性) + - 事务成功完成后,所做的所有更改都会准确地记录在数据库中。所做的更改不会丢失。 + +![img](01.Mysql/uxH9iBpInyy8pwrk.png!thumbnail) + +- 事务的控制语句 + +```sql +START TRANSACTION(或 BEGIN):显式开始一个新事务 +SAVEPOINT:分配事务过程中的一个位置,以供将来引用 +COMMIT:永久记录当前事务所做的更改 +ROLLBACK:取消当前事务所做的更改 +ROLLBACK TO SAVEPOINT:取消在 savepoint 之后执行的更改 +RELEASE SAVEPOINT:删除 savepoint 标识符 +SET AUTOCOMMIT:为当前连接禁用或启用默认 autocommit 模式 +``` + +- 一个成功事务的生命周期 + +```sql +begin; +sql1 +sql2 +sql3 +... +commit; +``` + +- 一个失败事务的生命周期 + +```sql +begin; +sql1 +sql2 +sql3 +... +rollback; +``` + +- 自动提交 + +```sql +#默认自动提交,关闭后需要命令commit才生效,此时另一个客户端除非重新登录才能看到变化(隔离性) +#开启两个窗口,不同update,不同时间commit演示 +mysql> show variables like 'autocommit'; +#查看自动提交 +mysql> set autocommit=0; +#临时关闭 +[root@db01 world]# vim /etc/my.cnf +[mysqld] +autocommit=0 +#永久关闭 +``` + +- 事务演示 + + - 成功事务 + + 需要打开另一个mysql终端查看,分别在执行完语句以及commit后查看 + +```sql +mysql> create table stu(id int,name varchar(10),sex enum('f','m'),money int); +mysql> begin; +mysql> insert into stu(id,name,sex,money) values(1,'zhang3','m',100), (2,'zhang4','m',110); +mysql> commit; +* 事务回滚 +mysql> begin; +mysql> update stu set name='zhang3'; +mysql> delete from stu; +mysql> rollback; +``` + +- 事务隐式提交情况 + - 现在版本在开启事务时,不需要手工begin,只要你输入的是DML语句,就会自动开启事务。 + - 有些情况下事务会被隐式提交 + - 在事务运行期间,手工执行begin的时候会自动提交上个事务 + - 在事务运行期间,加入DDL、DCL操作会自动提交上个事务 + - 在事务运行期间,执行锁定语句(lock tables、unlock tables) + - load data infile + - select for update + - 在autocommit=1的时候 + +### 15.7.1 事务日志redo + +- redo,顾名思义“重做日志”,是事务日志的一种。 +- 在事务ACID过程中,实现的是“D”持久化的作用。 +- 特性:WAL(Write Ahead Log)日志优先写 +- REDO:记录的是,内存数据页的变化过程 +- REDO工作过程 + +```sql +update t1 set num=2 where num=1; +# 执行步骤 +* 首先将t1表中num=1的行所在数据页加载到内存中buffer page +* MySQL实例在内存中将num=1的数据页改成num=2 +* num=1变成num=2的变化过程会记录到,redo内存区域,也就是redo buffer page中 +commit; +# 提交事务执行步骤 +* 当敲下commit命令的瞬间,MySQL会将redo buffer page写入磁盘区域redo log +* 当写入成功之后,commit返回ok +``` + +### 15.7.2 事务日志undo + +- undo,顾名思义“回滚日志”,是事务日志的一种。 +- 在事务ACID过程中,实现的是“A”原子性的作用。当然CI的特性也和undo有关 +- redo和undo的存储位置 + +```shell +[root@db01 data]# ll /application/mysql/data/ +-rw-rw---- 1 mysql mysql 50331648 Aug 15 06:34 ib_logfile0 +-rw-rw---- 1 mysql mysql 50331648 Mar 6 2021 ib_logfile1 +# redo位置 +[root@db01 data]# ll /application/mysql/data/ +-rw-rw---- 1 mysql mysql 79691776 Aug 15 06:34 ibdata1 +-rw-rw---- 1 mysql mysql 79691776 Aug 15 06:34 ibdata2 +# undo位置 +``` + +- 在MySQL5.6版本中undo是在ibdata文件中,在MySQL5.7版本会独立出来。 + +### 15.7.3 事务中的锁 + +- “锁”顾名思义就是锁定的意思 +- 在事务ACID特性过程中,“锁”和“隔离级别”一起来实现“I”隔离性的作用。 +- 排他锁:保证在多事务操作时,数据的一致性。 +- 共享锁:保证在多事务工作期间,数据查询时不会被阻塞。 +- 多版本并发控制(MVCC) + - 只阻塞修改类操作,不阻塞查询类操作 + - 乐观锁的机制(谁先提交谁为准) +- 锁的粒度 + - MyIsam:低并发锁(表级锁) + - Innodb:高并发锁(行级锁) +- 事务的隔离级别 +- 四种隔离级别 + - READ UNCOMMITTED(独立提交) + - 允许事务查看其他事务所进行的未提交更改,一个用户update命令后未commit,另一个用户看到的是修改的内存里的,即使没有写入硬盘 + - READ COMMITTED + - 允许事务查看其他事务所进行的已提交更改 + - REPEATABLE READ****** + - 确保每个事务的 SELECT 输出一致,一个用户update命令即使commit。另一个用户看到的未修改的,除非重新登录或者commit+ + - InnoDB 的默认级别 + - SERIALIZABLE + - 将一个事务的结果与其他事务完全隔离,一个用户update命令后未commit,另一个用户即使select都看不到。 + +```sql +mysql> show variables like '%iso%'; +#查看隔离级别 +[mysqld] +transaction_isolation=read-uncommit +#修改隔离级别为RU +mysql> use test +mysql> select * from stu; +mysql> insert into stu(id,name,sex,money) values(2,'li4','f',123); +[mysqld] +transaction_isolation=read-commit +#修改隔离级别为RC +``` + +# 16. MySQL日志管理 + +## 16.1 日志简介 + +| 日志文件 | 选项 | 文件名,表名 | 程序 | +| :------- | :--------------------------------- | :------------------------ | :------------ | +| 错误 | --log-error | host_name.err | | +| 常规 | --general_log | host_name.log general_log | | +| 慢速查询 | --slow_query_log --long_query_time | host_name-slow.log | mysqldumpslow | +| 二进制 | --log-bin --expire-logs-days | host_name-bin.000001 | mysqlbinlog | +| 审计 | --audit_log --audit_log_file ... | audit.log | | + +## 16.2 错误日志 + +- 记录mysql数据库的一般状态信息及报错信息,是我们对于数据库常规报错处理的常用日志。 +- 默认位置 + - $MYSQL_HOME/data/ +- 开启方式 + - MySQL安装完后默认开启 + +```shell +[root@db01 ~]# vim /etc/my.cnf +# 编辑配置文件 +[mysqld] +log_error=/application/mysql/data/$hostname.err +mysql> show variables like 'log_error'; +# 查看方式 +``` + +## 16.3 一般查询日志 + +- 记录mysql所有执行成功的SQL语句信息,可以做审计用,但是我们很少开启。 +- 默认位置 + - $MYSQL_HOME/data/ +- 开启方式 + - MySQL安装完之后默认不开启 + +```shell +[root@db01 ~]# vim /etc/my.cnf +[mysqld] +general_log=on +general_log_file=/application/mysql/data/$hostnamel.log +# 编辑配置文件 +mysql> show variables like '%gen%'; +# 查看方式 +``` + +## 16.4 二进制日志 + +- 记录已提交的DML事务语句,并拆分为多个事件(event)来进行记录 + +- 记录所有DDL、DCL等语句 + +- 总之,二进制日志会记录所有对数据库发生修改的操作 + +- 二进制日志模式 + + - statement:语句模式 + - row:行模式,即数据行的变化过程 + - mixed:以上两者的混合模式。 + - 企业推荐使用row模式 + +- 二进制日志模式优缺点 + + - statement模式 + + - 优点:简单明了,容易被看懂,就是sql语句,记录时不需要太多的磁盘空间 + - 缺点:记录不够严谨 + + - row模式 + + 记录了底层操作的所有事情 + + - 优点:记录更加严谨 + - 缺点:有可能会需要更多的磁盘空间,不太容易被读懂 + +- binlog的作用 + + - 如果我拥有数据库搭建开始所有的二进制日志,那么我可以把数据恢复到任意时刻 + - 数据的备份恢复 + - 数据的复制 + +### 16.4.1 二进制日志的管理操作实战 + +- 开启方式 + +```shell +[root@db01 data]# vim /etc/my.cnf +[mysqld] +log-bin=mysql-bin +binlog_format=row +``` + +注意:在mysql5.7中开启binlog必须要加上server-id。 + +```shell +[root@db01 data]# vim /etc/my.cnf +[mysqld] +log-bin=mysql-bin +binlog_format=row +server_id=1 +``` + +- 二进制日志的操作 + +```shell +[root@db01 data]# ll /application/mysql/data/ +-rw-rw---- 1 mysql mysql 285 Mar 6 2021 mysql-bin.000001 +#物理查看 +mysql> show binary logs; +mysql> show master status; +#命令行查看 +mysql> show binlog events in 'mysql-bin.000007'; +#查看binlog事件 +``` + +- 事件介绍 + - 在binlog中最小的记录单元为event + - 一个事务会被拆分成多个事件(event) +- 事件(event)特性 + - 每个event都有一个开始位置(start position)和结束位置(stop position)。 + - 所谓的位置就是event对整个二进制的文件的相对位置。 + - 对于一个二进制日志中,前120个position是文件格式信息预留空间。 + - MySQL第一个记录的事件,都是从120开始的。 +- row模式下二进制日志分析及数据恢复 + +```sql +mysql> show master status; +# 查看binlog信息 +mysql> create database binlog; +# 创建一个binlog库 +mysql> use binlog +# 使用binlog库 +mysql> create table binlog_table(id int); +# 创建binglog_table表 +mysql> show master status; +# 查看binlog信息 +mysql> insert into binlog_table values(1); +# 插入数据1 +mysql> show master status; +# 查看binlog信息 +mysql> commit; +# 提交 +mysql> show master status; +# 查看binlog信息 +mysql> insert into binlog_table values(2); +# 插入数据2 +mysql> insert into binlog_table values(3); +#插入数据3 +mysql> show master status; +# 查看binlog信息 +mysql> commit; +# 提交 +mysql> delete from binlog_table where id=1; +# 删除数据1 +mysql> show master status; +# 查看binlog信息 +mysql> commit; +# 提交 +mysql> update binlog_table set id=22 where id=2; +# 更改数据2为22 +mysql> show master status; +# 查看binlog +mysql> commit; +# 提交 +mysql> show master status; +# 查看binlog信息 +mysql> select * from binlog_table; +# 查看数据 +mysql> drop table binlog_table; +# 删表 +mysql> drop database binlog; +# 删库 +``` + +- 恢复数据之前 + +```shell +mysql> show binlog events in 'mysql-bin.000013'; +# 查看binlog事件 +# 使用mysqlbinlog来查看 +[root@db01 data]# mysqlbinlog /application/mysql/data/mysql-bin.000013 +[root@db01 data]# mysqlbinlog /application/mysql/data/mysql-bin.000013|grep -v SET +[root@db01 data]# mysqlbinlog --base64-output=decode-rows -vvv mysql-bin.000013 +# 查看二进制日志后,发现删除开始位置是858 +# binlog某些内容是以二进制放进去的,加入base64-output用于解码 +[root@db01 data]# mysqlbinlog --start-position=120 --stop-position=858 /application/mysql/data/mysql-bin.000013 > /tmp/binlog.sql +mysql> set sql_log_bin=0; +#临时关闭binlog,必须关闭,否则source倒入的内容也会被记录进binlog,binlog就脏了。 +mysql> source /tmp/binlog.sql +#执行sql文件 +mysql> show databases; +#查看删除的库 +mysql> use binlog +#进binlog库 +mysql> show tables; +#查看删除的表 +mysql> select * from binlog_table; +#查看表中内容 +``` + +- 只查看某个数据库的binlog文件 + +```sql +mysql> flush logs; +#刷新一个新的binlog,原来的bin000X.log作废 +mysql> create database db1; +mysql> create database db2; +#创建db1、db2两个库 + +mysql> use db1 +#库db1操作 +mysql> create table t1(id int); +#创建t1表 +mysql> insert into t1 values(1),(2),(3),(4),(5); +#插入5条数据 +mysql> commit; +#提交 +mysql> use db2 +#库db2操作 +mysql> create table t2(id int); +#创建t2表 +mysql> insert into t2 values(1),(2),(3); +#插入3条数据 +mysql> commit; +#提交 +mysql> show binlog events in 'mysql-bin.000014'; +#查看binlog事件 +[root@db01 data]# mysqlbinlog -d db1 --base64-output=decode-rows -vvv /application/mysql/data/mysql-bin.000014 +#查看db1的操作 +``` + +- 刷新binlog日志 + - flush logs; + - 重启数据库时会刷新 + - 二进制日志上限(max_binlog_size) +- 删除二进制日志 + - 原则 + - 在存储能力范围内,能多保留则多保留 + - 基于上一次全备前的可以选择删除 +- 删除方式 + - 根据存在时间删除日志 + +```shell +#临时生效 +SET GLOBAL expire_logs_days = 7; +#永久生效 +[root@db01 data]# vim /etc/my.cnf +[mysqld] +expire_logs_days = 7 +* 使用purge命令删除 +PURGE BINARY LOGS BEFORE now() - INTERVAL 3 day; +* 根据文件名删除 +PURGE BINARY LOGS TO 'mysql-bin.000010'; +* 用reset master +mysql> reset master; +``` + +## 16.5 慢查询日志 + +- 是将mysql服务器中影响数据库性能的相关SQL语句记录到日志文件 +- 通过对这些特殊的SQL语句分析,改进以达到提高数据库性能的目的 +- 默认位置: + - MYSQL_HOME/data/*M**Y**S**Q**L**H**O**M**E*/*d**a**t**a*/hostname-slow.log +- 开启方式(默认没有开启) + +```shell +[root@db01 ~]# vim /etc/my.cnf +[mysqld] +#指定是否开启慢查询日志 +slow_query_log = 1 +#指定慢日志文件存放位置(默认在data) +slow_query_log_file=/application/mysql/data/slow.log +#设定慢查询的阀值(默认10s) +long_query_time=0.05 +#不使用索引的慢查询日志是否记录到索引 +log_queries_not_using_indexes +#查询检查返回少于该参数指定行的SQL不被记录到慢查询日志 +min_examined_row_limit=100(鸡肋) +``` + +- 模拟慢查询语句 + +```sql +mysql> use world +#进入world库 +mysql> show tables; +#查看表 +mysql> create table t1 select * from city; +#将city表中所有内容加到t1表中 +mysql> desc t1; +#查看t1的表结构 +mysql> insert into t1 select * from t1; +mysql> insert into t1 select * from t1; +mysql> insert into t1 select * from t1; +mysql> insert into t1 select * from t1; +#将t1表所有内容插入到t1表中(多插入几次) +mysql> commit; +#提交 +mysql> delete from t1 where id>2000; +#删除t1表中id>2000的数据 +[root@db01 ~]# cat /application/mysql/data/mysql-db01 +#查看慢日志 +``` + +- 使用mysqldumpslow命令来分析慢查询日志 + +```shell +$PATH/mysqldumpslow -s c -t 10 /database/mysql/slow-log +#输出记录次数最多的10条SQL语句 +``` + +- 参数说明: + - -s: + - 是表示按照何种方式排序,c、t、l、r分别是按照记录次数、时间、查询时间、返回的记录数来排序,ac、at、al、ar,表示相应的倒叙; + - -t: + - 是top n的意思,即为返回前面多少条的数据; + - -g: + - 后边可以写一个正则匹配模式,大小写不敏感的; + +```shell +$PATH/mysqldumpslow -s r -t 10 /database/mysql/slow-log +#得到返回记录集最多的10个查询 +$PATH/mysqldumpslow -s t -t 10 -g "left join" /database/mysql/slow-log +#得到按照时间排序的前10条里面含有左连接的查询语句 +``` + +- 第三方推荐(扩展): + +```shell +yum install -y percona-toolkit-3.0.11-1.el6.x86_64.rpm +* 使用percona公司提供的pt-query-digest工具分析慢查询日志 +[root@mysql-db01 ~]# pt-query-digest /application/mysql/data/mysql-db01-slow.log +``` + +有能力的可以做成可视化界面: +Anemometer基于pt-query-digest将MySQL慢查询可视化 + +慢日志分析工具下载 https://[www.percona.com/downloads/percona-toolkit/LATEST/](http://www.percona.com/downloads/percona-toolkit/LATEST/) + +可视化代码下载 https://github.com/box/Anemometer + +![img](01.Mysql/yhWTJJz8GZkilbZN.png!thumbnail) + +# 17. 备份与恢复 + +- 备份的原因 + - 第一个是保护公司的数据. + - 第二个是让网站能7*24小时提供服务(用户体验)。 + - 备份就是为了恢复。 + - 尽量减少数据的丢失(公司的损失) + +## 17.1 备份的类型 + +- 冷备份: + - 这些备份在用户不能访问数据时进行,因此无法读取或修改数据。这些脱机备份会阻止执行任何使用数据的活动。这些类型的备份不会干扰正常运行的系统的性能。但是,对于某些应用程序,会无法接受必须在一段较长的时间里锁定或完全阻止用户访问数据。 +- 温备份: + - 这些备份在读取数据时进行,但在多数情况下,在进行备份时不能修改数据本身。这种中途备份类型的优点是不必完全锁定最终用户。但是,其不足之处在于无法在进行备份时修改数据集,这可能使这种类型的备份不适用于某些应用程序。在备份过程中无法修改数据可能产生性能问题。 +- 热备份: + - 这些动态备份在读取或修改数据的过程中进行,很少中断或者不中断传输或处理数据的功能。使用热备份时,系统仍可供读取和修改数据的操作访问。 + +## 17.2 备份的方式 + +- 逻辑备份 + + SQL软件自带的功能 + + - 基于SQL语句的备份 + - binlog + - into outfile + +```sql +[root@localhost ~]# vim /etc/my.cnf +[mysqld] +secure_file_priv=/tmp +mysql> select * from world.city into outfile '/tmp/world_city.data'; + * mysqldump + * replication +``` + +- 物理备份 + + 通过二进制方式直接拖走所有数据、配置文件 + + - 基于数据文件的备份 + - Xtrabackup(percona公司) + +## 17.3 备份工具 + +- 备份策略 + - 全量备份 full + - 增量备份 increamental +- 备份工具 + - mysqldump(逻辑) + - mysql原生自带很好用的逻辑备份工具 + - mysqlbinlog(逻辑) + - 实现binlog备份的原生态命令 + - xtrabackup(物理) + - precona公司开发的性能很高的物理备份工具 +- 备份工具使用 + - mysqldump + - 连接服务端参数(基本参数):-u -p -h -P -S + - -A, --all-databases:全库备份 + +```shell +[root@db01 ~]# mysqldump -uroot -p123456 -A > /backup/full.sql + * 不加参数:单库、单表多表备份 +[root@db01 ~]# mysqldump -uroot -p123456 db1 > /backup/db1.sql +[root@mysql-db01 backup]# mysqldump -uroot -p123456 world city > /backup/city.sql + * -B:指定库备份 +[root@db01 ~]# mysqldump -uroot -p123 -B db1 > /backup/db1.sql +[root@db01 ~]# mysqldump -uroot -p123 -B db1 db2 > /backup/db1_db2.sql + * -F:flush logs在备份时自动刷新binlog(不怎么常用) +[root@db01 backup]# mysqldump -uroot -p123 -A -R –triggers -F > /backup/full_2.sql + * -d:仅表结构 + * -t:仅数据 +``` + +- 备份额外扩展项 + - -R, --routines:备份存储过程和函数数据 + - --triggers:备份触发器数据 + +```shell +[root@db01 backup]# mysqldump -uroot -p123 -A -R --triggers > /backup/full_2.sql +``` + +- mysqldump特殊参数 + - -x:锁表备份(myisam温备份) + - --single-transaction:快照备份 + +```shell +[root@db01 backup]# mysqldump -uroot -p123 -A -R --triggers --master-data=2 –-single-transaction>/backup/full.sql +# 加了文件末尾会多一行:CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=120; +# 不加master-data就不会记录binlog的位置,导致后续不利于增量备份 + +``` + +- gzip:压缩备份 + +```shell +[root@db01 ~]# mysqldump -uroot -p123 -A -R --triggers --master-data=2 –single-transaction|gzip>/backup/full.sql.gz +[root@db01 ~]# gzip -d /backup/full.sql.gz +[root@db01 ~]# zcat /backup/full.sql.gz > linshi.sql +``` + +- 常用的热备份备份语句 + +```shell +mysqldump -uroot -p3307 -A -R --triggers --master-data=2 --single-transaction |gzip > /tmp/full_$(date +%F).sql.gz +``` + +- mysqldump的恢复 + +```sql +mysql> set sql_log_bin=0; +#先不记录二进制日志 +mysql> source /backup/full.sql +#库内恢复操作 +[root@db01 ~]# mysql -uroot -p123 < /backup/full.sql +#库外恢复操作 +``` + +- 注意 + - mysqldump在备份和恢复时都需要MySQL实例启动为前提 + - 一般数据量级100G以内,大约15-30分钟可以恢复(PB、EB就需要考虑别的方式) + - mysqldump是以覆盖的形式恢复数据的 + +## 17.4 【实战】企业故障恢复 + +- 背景: + - 正在运行的网站系统,MySQL数据库,数据量25G,日业务增量10-15M。 +- 备份策略: + - 每天23:00,计划任务调用mysqldump执行全备脚本 +- 故障时间点: + - 上午10点开发人员误删除一个核心业务表,需要恢复 +- 思路 + - **停业务避免数据的二次伤害**! + - 找一个临时的库,恢复前一天的全备 + - 截取前一天23:00到第二天10点误删除之间的binlog,恢复到临时库 + - 测试可用性和完整性 + - 开启业务前的两种方式 + - 直接使用临时库顶替原生产库,前端应用割接到新库 + - 将误删除的表单独导出,然后导入到原生产环境 +- 开启业务 + - 故障模拟 + +```sql +mysql> flush logs; +#刷新binlog使内容更清晰 +mysql> show master status; +#查看当前使用的binlog +mysql> create database backup; +#创建backup库 +mysql> use backup +#进入backup库 +mysql> create table full select * from world.city; +#创建full表 +mysql> create table full_1 select * from world.city; +#创建full_1表 +mysql> show tables; +#查看表 +``` + +- 全备 + +```sql +[root@db01 ~]# mysqldump -uroot -p123 -A -R --triggers --master-data=2 --single-transaction|gzip > /backup/full_$(date +%F).sql.gz +``` + +- 模拟数据变化 + +```sql +mysql> use backup +#进入backup库 +mysql> create table new select * from mysql.user; +#创建new表 +mysql> create table new_1 select * from world.country; +#创建new_1表 +mysql> show tables; +#查看表 +mysql> select * from full; +#查看full表中所有数据 +mysql> update full set countrycode='CHN' where 1=1; +#把full表中所有的countrycode都改成CHN +mysql> commit; +#提交 +mysql> delete from full where id>200; +#删除id大于200的数据 +mysql> commit; +#提交 +``` + +- 模拟故障 + +```sql +mysql> drop table new; +#删除new表 +mysql> show tables; +#查看表 +``` + +- 恢复过程 + - 准备临时数据库 + +```shell +#开启一个新的实例 +[root@db02 ~]# mysqld_safe --defaults-file=/data/3307/my.cnf & +#拷贝数据到新库上 +[root@db02 ~]# scp /backup/full_2018-08-16.sql.gz root@10.0.0.52:/tmp +#解压全备数据文件,如果用同一个机器的不同实例则不需要这步 +[root@db02 ~]# cd /tmp/ +#进入tmp目录 +[root@db02 tmp]# gzip -d full_2018-08-16.sql.gz +#解压全备数据文件 +截取二进制 +[root@db02 tmp]# head -50 full_2018-08-16.sql |grep -i 'change master to' +#查看全备的位置点(起始位置点),忽略大小写,假设找到起始的位置为268002 +mysql> show binlog events in 'mysql-bin.000017'\G +#生产环境db01找到drop语句执行的位置点(结束位置点) +[root@db01 tmp]#mysqlbinlog -uroot -p123 --start-position=268002 --stop-position=671148 /application/mysql/data/mysql-bin.000017 > /tmp/inc.sql +#截取二进制日志,把备份点和drop直接的信息倒入到inc.sql +[root@db01 tmp]# scp /tmp/inc.sql root@10.0.0.52:/tmp +#发送增量数据到新库 + +#在新库内恢复数据 +mysql> set sql_log_bin=0; +#不记录二进制日志 +mysql> source /tmp/full_2018-08-16.sql +#恢复全备数据 +mysql> use backup +#进入backup库 +mysql> show tables; +# 查看表,此时没有new表 +mysql> source /tmp/inc.sql +#恢复增量数据 +mysql> show tables; +#查看表,此时有new表 +``` + +- 将故障表导出并恢复到生产 + +```sql +#将现在已经恢复好的数据库backup里的new表备份到/tmp/new.sql文件中 +#此时/tmp/目录下有昨天23:00前完整备份文件full_2018-08-16.sql、昨天23:00到drop的增量文件inc.sql。 +#通过这两个文件恢复了数据库出事前的所有内容 +[root@db02 ~]# mysqldump -uroot -p123 -S /data/3307/mysql.sock backup new > /tmp/new.sql +#导出new表 +[root@db02 ~]# scp /tmp/new.sql root@10.0.0.51:/tmp/ +#发送到db01的库,此时db01的生产环境backup库里没有new表(被无良开发drop掉了);可直接从new.sql倒入 +mysql> use backup +#进入backup库,此时生产环境的backup +mysql> source /tmp/new.sql +#在生产库恢复数据 +mysql> show tables; +#查看表 +``` + +## 17.5 物理备份(Xtrabackup) + +- Xtrabackup安装 + +```shell +yum -y install epel-release +#安装epel源 +yum -y install perl perl-devel libaio libaio-devel perl-Time-HiRes perl-DBD-MySQL +#安装依赖 +wget httpss://www.percona.com/downloads/XtraBackup/Percona-XtraBackup-2.4.4/binary/redhat/6/x86_64/percona-xtrabackup-24-2.4.4-1.el6.x86_64.rpm +#下载Xtrabackup +yum localinstall -y percona-xtrabackup-24-2.4.4-1.el6.x86_64.rpm +# 安装 +``` + +- 备份方式(物理备份) + - 对于非innodb表(比如myisam)是直接锁表cp数据文件,属于一种温备。 + - 对于innodb的表(支持事务),不锁表,cp数据页最终以数据文件方式保存下来,并且把redo和undo一并备走,属于热备方式。 + - 备份时读取配置文件/etc/my.cnf,需要注明socket=/application/mysql/tmp/mysql.sock文件的位置 +- 全量备份 + +```shell +[root@db01 data]# innobackupex --user=root --password=123 /backup +#全备 +[root@db01 ~]# innobackupex --user=root --password=123 --no-timestamp /backup/full +#避免时间戳,自定义路径名 +[root@db01 backup]# ll /backup/full +#查看备份路径中的内容 +-rw-r----- 1 root root 21 Aug 16 06:23 xtrabackup_binlog_info +#记录binlog文件名和binlog的位置点 +-rw-r----- 1 root root 117 Aug 16 06:23 xtrabackup_checkpoints +#备份时刻,立即将已经commit过的内存中的数据页刷新到磁盘 +#备份时刻有可能会有其他数据写入,已备走的数据文件就不会再发生变化了 +#在备份过程中,备份软件会一直监控着redo和undo,一旦有变化会将日志一并备走 +-rw-r----- 1 root root 485 Aug 16 06:23 xtrabackup_info +#备份汇总信息 +-rw-r----- 1 root root 2560 Aug 16 06:23 xtrabackup_logfile +#备份的redo文件 +* 准备备份 +* 将redo进行重做,已提交的写到数据文件,未提交的使用undo回滚,模拟CSR的过程 +[root@db01 full]# innobackupex --user=root --password=123 --apply-log /backup/full +``` + +- 恢复备份 + - 前提1:被恢复的目录是空的 + - 前提2:被恢复的数据库的实例是关闭的 + +```shell +[root@db01 full]# /etc/init.d/mysqld stop +#停库 +[root@db01 full]# cd /application/mysql +#进入mysql目录 +[root@db01 mysql]# rm -fr data/ +#删除data目录(在生产中可以备份一下) +[root@db01 mysql]# innobackupex --copy-back /backup/full +#拷贝数据 +[root@db01 mysql]# chown -R mysql.mysql /application/mysql/data/ +#授权 +[root@db01 mysql]# /etc/init.d/mysqld start +#启动MySQL +``` + +- 增量备份及恢复 + - 基于上一次备份进行增量 + - 增量备份无法单独恢复,必须基于全备进行恢复 + - 所有增量必须要按顺序合并到全备当中 + +```shell +[root@mysql-db01 ~]# innobackupex --user=root --password=123 --no-timestamp /backup/full +#不使用之前的全备,执行一次全备 +``` + +- 模拟数据变化 + +```sql +mysql> create database inc1; +mysql> use inc1 +mysql> create table inc1_tab(id int); +mysql> insert into inc1_tab values(1),(2),(3); +mysql> commit; +mysql> select * from inc1_tab; +``` + +- 第一次增量备份 + +```shell +[root@db01 ~]# innobackupex --user=root --password=123 --no-timestamp --incremental --incremental-basedir=/backup/full/ /backup/inc1 +参数说明: +--incremental:开启增量备份功能 +--incremental-basedir:上一次备份的路径 +``` + +- 再次模拟数据变化 + +```sql +mysql> create database inc2; +mysql> use inc2 +mysql> create table inc2_tab(id int); +mysql> insert into inc2_tab values(1),(2),(3); +mysql> commit; +``` + +- 第二次增量备份 + +```shell +[root@db01 ~]# innobackupex --user=root --password=123 --no-timestamp --incremental --incremental-basedir=/backup/inc1/ /backup/inc2 +``` + +- 增量恢复 + +```shell +[root@db01 ~]# rm -fr /application/mysql/data/ +#破坏数据 +``` + +- 准备备份 + - full+inc1+inc2 + - 需要将inc1和inc2按顺序合并到full中 + - 分步骤进行--apply-log +- 第一步:在全备中apply-log时,只应用redo,不应用undo + +```shell +[root@db01 ~]# innobackupex --apply-log --redo-only /backup/full/ +``` + +- 第二步:合并inc1合并到full中,并且apply-log,只应用redo,不应用undo + +```shell +[root@db01 ~]# innobackupex --apply-log --redo-only --incremental-dir=/backup/inc1/ /backup/full/ +``` + +- 第三步:合并inc2合并到full中,redo和undo都应用 + +```shell +[root@db01 ~]# innobackupex --apply-log --incremental-dir=/backup/inc2/ /backup/full/ +``` + +- 第四步:整体full执行apply-log,redo和undo都应用 + +```shell +[root@db01 mysql]# innobackupex --apply-log /backup/full/ +copy-back +[root@db01 ~]# innobackupex --copy-back /backup/full/ +[root@db01 ~]# chown -R mysql.mysql /application/mysql/data/ +[root@db01 ~]# /etc/init.d/mysqld start +``` + +# 18. MySQL的主从复制 + +## 18.1 主从复制原理 + +![img](01.Mysql/sql_MS.jpg) + +- 复制是 MySQL 的一项功能,允许服务器将更改从一个实例复制到另一个实例。 + - 主服务器将所有数据和结构更改记录到二进制日志中。 + - 从属服务器从主服务器请求该二进制日志并在本地应用其内容。 + - IO:请求主库,获取上一次执行过的新的事件,并存放到relaylog + - SQL:从relaylog中将sql语句翻译给从库执行 +- 主从复制的前提 + - 两台或两台以上的数据库实例 + - 主库要开启二进制日志 + - 主库要有复制用户 + - 主库的server_id和从库不同 + - 从库需要在开启复制功能前,要获取到主库之前的数据(主库备份,并且记录binlog当时位置) + - 从库在第一次开启主从复制时,时必须获知主库:ip,port,user,password,logfile,pos + - 从库要开启相关线程:IO、SQL + - 从库需要记录复制相关用户信息,还应该记录到上次已经从主库请求到哪个二进制日志 + - 从库请求过来的binlog,首先要存下来,并且执行binlog,执行过的信息保存下来 +- 主从复制涉及到的文件和线程 + - 主库: + - 主库binlog:记录主库发生过的修改事件 + - dump thread:给从库传送(TP)二进制日志线程 + - 从库: + - relay-log(中继日志):存储所有主库TP过来的binlog事件,在SQL thread执行完毕,数据持久化后清空,但写入relaylog.info文件 + - relaylog.info:类似于master的binlog文件 + - [master.info](http://master.info/):存储复制用户信息,**上次请求到的主库binlog位置点** + - IO thread:接收主库发来的binlog日志,也是从库请求主库的线程 + - SQL thread:执行主库TP过来的日志 +- 原理 + - 通过change master to语句告诉从库主库的ip,port,user,password,file,pos + - 从库通过start slave命令开启复制必要的IO线程和SQL线程 + - 从库通过IO线程拿着change master to用户密码相关信息,连接主库,验证合法性 + - 从库连接成功后,会根据binlog的pos问主库,有没有比这个更新的 + - 主库接收到从库请求后,比较一下binlog信息,如果有就将最新数据通过dump线程给从库IO线程 + - 从库通过IO线程接收到主库发来的binlog事件,存储到TCP/IP缓存中,[并返回ACK更新master.info](http://xn--ackmaster-fr4ph28do0tlydq13q.info/) + - 将TCP/IP缓存中的内容存到relay-log中 + - [SQL线程读取relay-log.info](http://xn--sqlrelay-log-fo0uq362b8zo333c.info/),读取到上次已经执行过的relay-log位置点,继续执行后续的relay-log日志,执行完成后,[更新relay-log.info](http://xn--relay-log-fq5sl5i.info/) + +## 18.2 【实战】MySQL主从复制 + +本实例中db01是master,db02,03是slave,MHA-manager是db03 + +- 保证两台数据库数据一致性 + + ```shell + [root@db01 ~]#mysqldump -uroot -p123 -A -R --triggers --master-data=2 --single-transaction > /tmp/full.sql + [root@db01 ~]#scp /tmp/full.sql root@10.0.0.52:/tmp + [root@db02 ~]#mysql -uroot -p123456 + sql> source /tmp/full.sql + ``` + + + +- 主库操作 + + - 修改配置文件 + +```shell +[root@db01 ~]# vim /etc/my.cnf +#编辑mysql配置文件 +[mysqld] +#在mysqld标签下配置 +server_id =1 +#主库server-id为1,从库不等于1 +log_bin=mysql-bin +#开启binlog日志 +* 创建主从复制用户 +[root@db01 ~]# mysql -uroot -p123456 +#登录数据库 +mysql> grant replication slave on *.* to rep@'10.0.0.%' identified by '123456'; +#创建rep用户 +``` + +- 从库操作 + - 修改配置文件 + +```shell +[root@db02 ~]# vim /etc/my.cnf +#修改db02配置文件 +[mysqld] +#在mysqld标签下配置 +server_id =5 +#主库server-id为1,从库不等于1 +log_bin=mysql-bin +#开启binlog日志 +[root@db02 ~]# /etc/init.d/mysqld restart +#重启mysql +mysql> show master status; +#记录主库binlog及位置点 +[root@db02 ~]# mysql -uroot -p123456 +#登陆数据库 +mysql> change master to +-> master_host='10.0.0.51', +-> master_user='rep', +-> master_password='123456', +-> master_log_file='mysql-bin.000004', +-> master_log_pos=120; +#head -50 /tmp/full.sql内的master_log_file与master_log_pos +-># master_auto_position=1; # 与前两句冲突,表示自动适配master的file和position,MariaDB5.5不支持GTID特性。此时未开启GTID +#执行change master to 语句 +mysql> start slave; +mysql> show slave status\G +#看到Slave_IO_Running: Yes Slave_SQL_Running: Yes表示运行成功 +#从数据库:/application/mysql/data目录下出现master.info文件,记录了同步的索引号 +#测试方法:在主里面库建表插入内容,从里面可以看到主新增的内容表示同步成功。 +``` + +## 18.3 主从复制基本故障处理 + +- IO线程报错 + - user password ip port + - 网络:不同,延迟高,防火墙 + - 没有跳过反向解析[mysqld]里面加入skip-name-resolve +- 请求binlog + - binlog不存在或者损坏 +- 更新relay-log和master.info + - SQL线程 + - relay-log出现问题 + - 从库做写入了 +- 操作对象已存在(create) +- 操作对象不存在(insert update delete drop truncate alter) +- 约束问题、数据类型、列属性 + +### 18.3.1 处理方法一 + +```sql +mysql> stop slave; +#临时停止同步 +mysql> set global sql_slave_skip_counter=1; +#将同步指针向下移动一个(可重复操作),告诉这个执行不了的binlog条目不执行,去执行下一条。 +mysql> start slave; +#开启同步 +``` + +### 18.3.2 处理方法二 + +```shell +[root@db01 ~]# vim /etc/my.cnf +#编辑配置文件 +slave-skip-errors=1032,1062,1007 +#在[mysqld]标签下添加以下参数 +``` + +但是以上操作都是有风险存在的 + +### 18.3.3 处理方法三 + +```sql +set global read_only=1; +#在命令行临时设置,在从库设置 +read_only=1 +#在配置文件中永久生效[mysqld] +``` + +## 18.4 延时从库 + +- 普通的主从复制可能存在不足 + - 逻辑损坏怎么办? + - 不能保证主库的操作,从库一定能做 + - 高可用?自动failover? + - 过滤复制 +- 企业中一般会延时3-6小时 +- 延时从库配置方法 + +```sql +mysql>stop slave; +#停止主从 +mysql>CHANGE MASTER TO MASTER_DELAY = 180; +#设置延时为180秒 +mysql>start slave; +#开启主从 +mysql> show slave status \G +SQL_Delay: 60 +#查看状态 + +mysql> stop slave; +#停止主从 +mysql> CHANGE MASTER TO MASTER_DELAY = 0; +#设置延时为0 +mysql> start slave; +#开启主从 +``` + +- 恢复数据 + - 停止主从 + - 导出从库数据 + - 主库导入数据 + +## 18.5 半同步复制 + +从MYSQL5.5开始,支持半自动复制。之前版本的MySQL Replication都是异步(asynchronous)的,主库在执行完一些事务后,是不会管备库的进度的。如果备库不幸落后,而更不幸的是主库此时又出现Crash(例如宕机),这时备库中的数据就是不完整的。简而言之,在主库发生故障的时候,我们无法使用备库来继续提供数据一致的服务了。 + +![img](01.Mysql/remote_mem_larger_local_disk.jpg) + +半同步复制(Semi synchronous Replication)则一定程度上保证提交的事务已经传给了至少一个备库。 + +IO在收到了读写后会发送ACK报告已经收到了。 + +![img](01.Mysql/semi_synchronized_dual_master.jpg) + +出发点是保证主从数据一致性问题,安全的考虑。 + +- 半同步复制开启方法 + - 安装(主库) + +```shell +[root@db01 ~]# mysql -uroot -p123456 +#登录数据库 +mysql> show global variables like 'have_dynamic_loading'; +#查看是否有动态支持 have_dynamic_loading=YES +mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME'semisync_master.so'; +#安装自带插件 +mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1; +#启动插件 +mysql> SET GLOBAL rpl_semi_sync_master_timeout = 1000; +#设置超时 +[root@db01 ~]# vim /etc/my.cnf +#修改配置文件 +[mysqld] +rpl_semi_sync_master_enabled=1 +rpl_semi_sync_master_timeout=1000 #多长时间认为超时,单位ms +#在[mysqld]标签下添加如下内容(不用重启库) +mysql> show variables like'rpl%'; +mysql> show global status like 'rpl_semi%'; +#检查安装 +* 安装(从库) +[root@mysql-db02 ~]# mysql -uroot -p123456 +#登录数据库 +mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME'semisync_slave.so'; +#安装slave半同步插件 +mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1; +#启动插件 +mysql> stop slave io_thread; +mysql> start slave io_thread; +#重启io线程使其生效 +[root@mysql-db02 ~]# vim /etc/my.cnf +#编辑配置文件(不需要重启数据库) +[mysqld] +rpl_semi_sync_slave_enabled =1 +#在[mysqld]标签下添加如下内容 +``` + +- 注:相关参数说明 + - rpl_semi_sync_master_timeout=milliseconds + - 设置此参数值(ms),为了防止半同步复制在没有收到确认的情况下发生堵塞,如果Master在超时之前没有收到任何确认,将恢复到正常的异步复制,并继续执行没有半同步的复制操作。 + - rpl_semi_sync_master_wait_no_slave={ON|OFF} + - 如果一个事务被提交,但Master没有任何Slave的连接,这时不可能将事务发送到其它地方保护起来。默认情况下,Master会在时间限制范围内继续等待Slave的连接,并确认该事务已经被正确的写到磁盘上。 + - 可以使用此参数选项关闭这种行为,在这种情况下,如果没有Slave连接,Master就会恢复到异步复制。 +- 测试半同步 + +```sql +mysql> create database test1; +Query OK, 1 row affected (0.04 sec) +mysql> create database test2; +Query OK, 1 row affected (0.00 sec) +#创建两个数据库,test1和test2 +mysql> show global status like 'rpl_semi%'; +#查看复制状态,Rpl_semi_sync_master_status状态是ON ++--------------------------------------------+-------+ +| Variable_name | Value | ++--------------------------------------------+-------+ +| Rpl_semi_sync_master_clients | 1 | +| Rpl_semi_sync_master_net_avg_wait_time | 768 | +| Rpl_semi_sync_master_net_wait_time | 1497 | +| Rpl_semi_sync_master_net_waits | 2 | +| Rpl_semi_sync_master_no_times | 0 | +| Rpl_semi_sync_master_no_tx | 0 | +| Rpl_semi_sync_master_status | ON | +| Rpl_semi_sync_master_timefunc_failures | 0 | +| Rpl_semi_sync_master_tx_avg_wait_time | 884 | +| Rpl_semi_sync_master_tx_wait_time | 1769 | +| Rpl_semi_sync_master_tx_waits | 2 | +| Rpl_semi_sync_master_wait_pos_backtraverse | 0 | +| Rpl_semi_sync_master_wait_sessions | 0 | +#此行显示2,表示刚才创建的两个库执行了半同步 +| Rpl_semi_sync_master_yes_tx | 2 | ++--------------------------------------------+-------+ +14 rows in set (0.06 sec) +mysql> show databases; +#从库查看 ++--------------------+ +| Database | ++--------------------+ +| information_schema | +| mysql | +| performance_schema | +| test | +| test1 | +| test2 | ++--------------------+ +mysql> SET GLOBAL rpl_semi_sync_master_enabled = 0; +#关闭半同步(1:开启 0:关闭) +mysql> show global status like 'rpl_semi%'; +#查看半同步状态 ++--------------------------------------------+-------+ +| Variable_name | Value | ++--------------------------------------------+-------+ +| Rpl_semi_sync_master_clients | 1 | +| Rpl_semi_sync_master_net_avg_wait_time | 768 | +| Rpl_semi_sync_master_net_wait_time | 1497 | +| Rpl_semi_sync_master_net_waits | 2 | +| Rpl_semi_sync_master_no_times | 0 | +| Rpl_semi_sync_master_no_tx | 0 | +| Rpl_semi_sync_master_status | OFF | #状态为关闭 +| Rpl_semi_sync_master_timefunc_failures | 0 | +| Rpl_semi_sync_master_tx_avg_wait_time | 884 | +| Rpl_semi_sync_master_tx_wait_time | 1769 | +| Rpl_semi_sync_master_tx_waits | 2 | +| Rpl_semi_sync_master_wait_pos_backtraverse | 0 | +| Rpl_semi_sync_master_wait_sessions | 0 | +| Rpl_semi_sync_master_yes_tx | 2 | ++--------------------------------------------+-------+ +14 rows in set (0.00 sec) + +mysql> create database test3; +Query OK, 1 row affected (0.00 sec) +mysql> create database test4; +Query OK, 1 row affected (0.00 sec) +#再一次创建两个库 +mysql> show global status like 'rpl_semi%'; +#再一次查看半同步状态,此时Rpl_semi_sync_master_status变成OFF ++--------------------------------------------+-------+ +| Variable_name | Value | ++--------------------------------------------+-------+ +| Rpl_semi_sync_master_clients | 1 | +| Rpl_semi_sync_master_net_avg_wait_time | 768 | +| Rpl_semi_sync_master_net_wait_time | 1497 | +| Rpl_semi_sync_master_net_waits | 2 | +| Rpl_semi_sync_master_no_times | 0 | +| Rpl_semi_sync_master_no_tx | 0 | +| Rpl_semi_sync_master_status | OFF | +| Rpl_semi_sync_master_timefunc_failures | 0 | +| Rpl_semi_sync_master_tx_avg_wait_time | 884 | +| Rpl_semi_sync_master_tx_wait_time | 1769 | +| Rpl_semi_sync_master_tx_waits | 2 | +| Rpl_semi_sync_master_wait_pos_backtraverse | 0 | +| Rpl_semi_sync_master_wait_sessions | 0 | +#此行还是显示2,则证明,刚才的那两条并没有执行半同步否则应该是4 +| Rpl_semi_sync_master_yes_tx | 2 | ++--------------------------------------------+-------+ +14 rows in set (0.00 sec) +注:不难发现,在查询半同步状态是,开启半同步,查询会有延迟时间,关闭之后则没有 +``` + +![img](01.Mysql/mysql_goal.jpg) + +## 18.6 过滤复制 + +- 主库: + - 白名单:只记录白名单中列出的库的二进制日志 + - binlog-do-db + - 黑名单:不记录黑名单列出的库的二进制日志 + - binlog-ignore-db +- 从库: + - 白名单:只执行白名单中列出的库或者表的中继日志 + - --replicate-do-db=test + - --replicate-do-table=test.t1 + - --replicate-wild-do-table=test.t2 + - 黑名单:不执行黑名单中列出的库或者表的中继日志 + - --replicate-ignore-db + - --replicate-ignore-table + - --replicate-wild-ignore-table +- 复制过滤配置 + +```shell +[root@db01 data]# vim /data/3307/my.cnf +replicate-do-db=world +#在[mysqld]标签下添加 +mysqladmin -S /data/3307/mysql.sock shutdown +#关闭MySQL +mysqld_safe --defaults-file=/data/3307/my.cnf & +#启动MySQL +#设置主从的server_id,如法炮制设置主从关系,记得change master to时候加上参数master_port = ‘3306’, +``` + +- 测试复制过滤: +- 第一次测试: + - 主库: + +```sql +[root@db02 ~]# mysql -uroot -p123 -S /data/3308/mysql.sock +mysql> use world +mysql> create table t1(id int); +* 从库查看结果 +[root@db02 ~]# mysql -uroot -p123 -S /data/3307/mysql.sock +mysql> use world +mysql> show tables; +``` + +- 第二次测试 + - 主库 + +```shell +[root@db02 ~]# mysql -uroot -p123 -S /data/3308/mysql.sock +mysql> use test +mysql> create table tb1(id int); +* 从库查看结果 +[root@db02 ~]# mysql -uroot -p123 -S /data/3307/mysql.sock +mysql> use test +mysql> show tables; +``` + +# 19. MHA高可用架构 + +MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshimaton(现就职于Facebook公司)开发,是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中,MHA能做到在0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA能在最大程度上保证数据的一致性,以达到真正意义上的高可用。 + +![img](01.Mysql/MHA_topo.jpg) + +MHA能够在较短的时间内实现自动故障检测和故障转移,通常在10-30秒以内;在复制框架中,MHA能够很好地解决复制过程中的数据一致性问题,由于不需要在现有的replication中添加额外的服务器,仅需要一个manager节点,而一个Manager能管理多套复制,所以能大大地节约服务器的数量;另外,安装简单,无性能损耗,以及不需要修改现有的复制部署也是它的优势之处。 + +MHA还提供在线主库切换的功能,能够安全地切换当前运行的主库到一个新的主库中(通过将从库提升为主库),大概0.5-2秒内即可完成。 + +MHA由两部分组成:MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以独立部署在一台独立的机器上管理多个Master-Slave集群,也可以部署在一台Slave上。当Master出现故障时,它可以自动将最新数据的Slave提升为新的Master,然后将所有其他的Slave重新指向新的Master。整个故障转移过程对应用程序是完全透明的。 + +## 19.1 工作流程 + +1. 把宕机的master二进制日志保存下来。 +2. 找到binlog位置点最新的slave。 +3. 在binlog位置点最新的slave上用relay log(差异日志)修复其它slave。(因为relay log修复比bin log快,所以不用master的bin log,slave没有bin log) +4. 将宕机的master上保存下来的二进制日志恢复到含有最新位置点的slave上。 +5. 将含有最新位置点binlog所在的slave提升为master。 +6. 将其它slave重新指向新提升的master,并开启主从复制。 + +![img](01.Mysql/MHA_process.jpg) + +## 19.2 MHA工具介绍 + +- MHA软件由两部分组成,Manager工具包和Node工具包 + +- Manager工具包主要包括以下几个工具 + + | masterha_check_ssh | 检查MHA的ssh-key | + | :----------------------- | :---------------------- | + | masterha_check_repl | 检查主从复制情况 | + | masterha_manger | 启动MHA | + | masterha_check_status | 检测MHA的运行状态 | + | masterha_master_monitor | 检测master是否宕机 | + | masterha_master_switch | 手动故障转移 | + | masterha_conf_host | 手动添加server信息 | + | masterha_secondary_check | 建立TCP连接从远程服务器 | + | masterha_stop | 停止MHA | + +- Node工具包主要包括以下几个工具 + + | save_binary_logs | 保存宕机的master的binlog | + | :-------------------- | :----------------------- | + | apply_diff_relay_logs | 识别relay log的差异 | + | filter_mysqlbinlog | 防止回滚事件 | + | purge_relay_logs | 清除中继日志 | + +- MHA优点总结 + + - Masterfailover and slave promotion can be done very quickly + - 自动故障转移快 + - Mastercrash does not result in data inconsistency + - 主库崩溃不存在数据一致性问题 + - Noneed to modify current MySQL settings (MHA works with regular MySQL) + - 不需要对当前mysql环境做重大修改 + - Noneed to increase lots of servers + - 不需要添加额外的服务器(仅一台manager就可管理上百个replication) + - Noperformance penalty + - 性能优秀,可工作在半同步复制和异步复制,当监控mysql状态时,仅需要每隔N秒向master发送ping包(默认3秒),所以对性能无影响。你可以理解为MHA的性能和简单的主从复制框架性能一样。 + - Works with any storage engine + - 只要replication支持的存储引擎,MHA都支持,不会局限于innodb + +## 19.3 MHA实验环境 + +- 搭建三台mysql数据库 + +```shell +wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.40-linux-glibc2.12-x86_64.tar.gz +tar xzvf mysql-5.6.40-linux-glibc2.12-x86_64.tar.gz +mkdir /application +mv mysql-5.6.40-linux-glibc2.12-x86_64 /application/mysql-5.6.40 +ln -s /application/mysql-5.6.40 /application/mysql +cd /application/mysql/support-files +cp my-default.cnf /etc/my.cnf +cp:是否覆盖"/etc/my.cnf"? y +cp mysql.server /etc/init.d/mysqld +cd /application/mysql/scripts +useradd mysql -s /sbin/nologin -M +yum -y install autoconf +./mysql_install_db --user=mysql --basedir=/application/mysql --data=/application/mysql/data +vim /etc/profile.d/mysql.sh +export PATH="/application/mysql/bin:$PATH" +source /etc/profile +sed -i 's#/usr/local#/application#g' /etc/init.d/mysqld /application/mysql/bin/mysqld_safe +vim /usr/lib/systemd/system/mysqld.service +[Unit] +Description=MySQL Server +Documentation=man:mysqld(8) +Documentation=https://dev.mysql.com/doc/refman/en/using-systemd.html +After=network.target +After=syslog.target +[Install] +WantedBy=multi-user.target +[Service] +User=mysql +Group=mysql +ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf +LimitNOFILE = 5000 +systemctl start mysqld +systemctl enable mysqld +mysqladmin -uroot password '123456' +mysql -uroot -p123456 +``` + +## 19.4 基于GTID的主从复制 + +- 先决条件 +- 主库和从库都要开启binlog +- 主库和从库server-id不同 +- 要有主从复制用户 + +主库操作 + +- 修改配置文件 + +```shell +[root@mysql-db01 ~]# vim /etc/my.cnf +#编辑mysql配置文件 +[mysqld] +#在mysqld标签下配置 +server_id =1 +#主库server-id为1,从库不等于1 +log_bin=mysql-bin +#开启binlog日志 +#如果3台设备是在装了mysql后克隆的,mysql的UUID则相同,需要修改为不同值 +[root@mysql-db01 ~]# vim /application/mysql/data/auto.cnf +server-uuid=8108d02e-be0a-11ec-8a15-000c2956d2e2 +``` + +- 创建主从复制用户,**每台设备都要配置,因为slave有可能变成master** + +```shell +[root@mysql-db01 ~]# mysql -uroot -p123456 +#登录数据库 +mysql> grant replication slave on *.* to rep@'10.0.0.%' identified by '123456'; +#创建rep用户 +``` + +从库操作 + +- 修改配置文件 + +```shell +[root@mysql-db02 ~]# vim /etc/my.cnf +#修改mysql-db02配置文件 +[mysqld] +#在mysqld标签下配置 +server_id =5 +#主库server-id为1,从库必须大于1 +log_bin=mysql-bin +#开启binlog日志 +[root@mysql-db02 ~]# /etc/init.d/mysqld restart +#重启mysql +[root@mysql-db03 ~]# vim /etc/my.cnf +#修改mysql-db03配置文件 +[mysqld] +#在mysqld标签下配置 +server_id =10 +#主库server-id为1,从库必须大于1 +log_bin=mysql-bin +#开启binlog日志 +[root@mysql-db03 ~]# /etc/init.d/mysqld restart +#重启mysql +``` + +注:在以往如果是基于binlog日志的主从复制,则必须要记住主库的master状态信息。 + +```sql +mysql> show master status; ++------------------+----------+ +| File | Position | ++------------------+----------+ +| mysql-bin.000002 | 120 | ++------------------+----------+ +``` + +**主、从库都要开启GTID** + +```shell +mysql> show global variables like '%gtid%'; +#没开启之前先看一下GTID的状态 ++--------------------------+-------+ +| Variable_name | Value | ++--------------------------+-------+ +| enforce_gtid_consistency | OFF | +| gtid_executed | | +| gtid_mode | OFF | +| gtid_owned | | +| gtid_purged | | ++--------------------------+-------+ +[root@mysql-db01 ~]# vim /etc/my.cnf +#编辑mysql配置文件(主库从库都需要修改) +[mysqld] +#在[mysqld]标签下添加 +gtid_mode=ON +log_slave_updates +#重要:开启slave的binlog同步 +enforce_gtid_consistency +#开启GTID特性 +[root@mysql-db01 ~]# /etc/init.d/mysqld restart +#重启数据库 +mysql> show global variables like '%gtid%'; +#检查GTID状态 ++--------------------------+-------+ +| Variable_name | Value | ++--------------------------+-------+ +| enforce_gtid_consistency | ON | #执行GTID一致 +| gtid_executed | | +| gtid_mode | ON | #开启GTID模块 +| gtid_owned | | +| gtid_purged | | ++--------------------------+-------+ +``` + +**注:主库从库都需要开启GTID否则在做主从复制的时候就会报错,因为slave可能变成master** + +```sql +[root@mysql-db02 ~]# mysql -uroot -123456 +mysql> change master to +-> master_host='10.0.0.51', +-> master_user='rep', +-> master_password='123456', +-> master_auto_position=1; +#如果GTID没有开的话 +ERROR 1777 (HY000): CHANGE MASTER TO MASTER_AUTO_POSITION = 1 can only be executed when @@GLOBAL.GTID_MODE = ON. +``` + +配置主从复制 + +```shell +[root@mysql-db02 ~]# mysql -uroot -p123456 +#登录数据库 +mysql> change master to +#配置复制主机信息 +-> master_host='10.0.0.51', +#主库IP +-> master_user='rep', +#主库复制用户 +-> master_password='123456', +#主库复制用户的密码 +-> master_auto_position=1; +#GTID位置点 +mysql> start slave; +#开启slave +mysql> show slave status\G +#查看slave状态 +*************************** 1. row *************************** + Slave_IO_State: Waiting for master to send event + Master_Host: 10.0.0.51 + Master_User: rep + Master_Port: 3306 + Connect_Retry: 60 + Master_Log_File: mysql-bin.000003 + Read_Master_Log_Pos: 403 + Relay_Log_File: mysql-db02-relay-bin.000002 + Relay_Log_Pos: 613 + Relay_Master_Log_File: mysql-bin.000003 + Slave_IO_Running: Yes + Slave_SQL_Running: Yes + Replicate_Do_DB: + Replicate_Ignore_DB: + Replicate_Do_Table: + Replicate_Ignore_Table: + Replicate_Wild_Do_Table: + Replicate_Wild_Ignore_Table: + Last_Errno: 0 + Last_Error: + Skip_Counter: 0 + Exec_Master_Log_Pos: 403 + Relay_Log_Space: 822 + Until_Condition: None +``` + +- 从库设置 + +```shell +[root@mysql-db02 ~]# mysql -uroot -p123456 +#登录从库 +mysql> set global relay_log_purge = 0; +#禁用自动删除relay log 功能 +mysql> set global read_only=1; +#设置只读 +[root@mysql-db02 ~]# vim /etc/my.cnf +#编辑配置文件 +[mysqld] +#在mysqld标签下添加 +relay_log_purge = 0 +#禁用自动删除relay log 永久生效 +``` + +## 19.5 部署MHA + +环境准备(所有节点) + +记得要这个MHA安装包 + +https://download.s21i.faiusr.com/23126342/0/0/ABUIABBPGAAg30HUiAYolpPt7AQ.zip?f=mysql-master-ha.zip&v=1628778716 + +```shell +[root@mysql-db01 ~]# yum install perl-DBD-MySQL -y +#安装依赖包 +[root@mysql-db01 ~]# unzip mysql-master-ha.zip /home/user1/tools/ +[root@mysql-db01 ~]# cd /home/user1/tools/ +#进入安装包存放目录 +[root@mysql-db01 tools]# ll +mha4mysql-manager-0.56-0.el6.noarch.rpm +mha4mysql-manager-0.56.tar.gz +mha4mysql-node-0.56-0.el6.noarch.rpm +mha4mysql-node-0.56.tar.gz +[root@mysql-db01 tools]# rpm -ivh mha4mysql-node-0.56-0.el6.noarch.rpm +Preparing... ########################################### [100%] + 1:mha4mysql-node ########################################### [100%] +#安装node包,所有节点都要安装node包 +[root@mysql-db01 tools]# mysql -uroot -p123456 +#登录数据库,主库,从库会自动同步 +mysql> grant all privileges on *.* to mha@'10.0.0.%' identified by 'mha'; +#添加mha管理账号 +mysql> select user,host from mysql.user; +#查看是否添加成功 +mysql> select user,host from mysql.user; +#主库上创建,从库会自动复制(在从库上查看) +``` + +- 命令软连接(所有节点) + +```shell +[root@mysql-db01 ~]# ln -s /application/mysql/bin/mysqlbinlog /usr/bin/mysqlbinlog +[root@mysql-db01 ~]# ln -s /application/mysql/bin/mysql /usr/bin/mysql +#如果不创建命令软连接,检测mha复制情况的时候会报错,写入环境变量 +``` + +- 部署管理节点(mha-manager:mysql-db03)最好用第四个节点安装mha-manager + +```shell +[root@mysql-db03 ~]# yum -y install epel-release +#使用epel源 +[root@mysql-db03 ~]# yum install -y perl-Config-Tiny epel-release perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes +#安装manager依赖包 +[root@mysql-db03 tools]# rpm -ivh mha4mysql-manager-0.56-0.el6.noarch.rpm +Preparing... ########################################### [100%] +1:mha4mysql-manager ########################################### [100%] +#安装manager包 +``` + +- 编辑配置文件 + +```shell +[root@mysql-db03 ~]# mkdir -p /etc/mha +#创建配置文件目录 +[root@mysql-db03 ~]# mkdir -p /var/log/mha/app1 +#创建日志目录 +[root@mysql-db03 ~]# vim /etc/mha/app1.cnf +#编辑mha配置文件 +[server default] +manager_log=/var/log/mha/app1/manager +manager_workdir=/var/log/mha/app1 +master_binlog_dir=/application/mysql/data +user=mha +password=mha +ping_interval=2 +repl_password=123456 +repl_user=rep +ssh_user=root +[server1] +hostname=10.0.0.51 +port=3306 +[server2] +candidate_master=1 +check_repl_delay=0 +hostname=10.0.0.52 +port=3306 +[server3] +hostname=10.0.0.53 +port=3306 +``` + +配置文件详解 + +```shell +[server default] +manager_workdir=/var/log/masterha/app1 +#设置manager的工作目录 +manager_log=/var/log/masterha/app1/manager.log +#设置manager的日志 +master_binlog_dir=/data/mysql +#设置master 保存binlog的位置,以便MHA可以找到master的日志,我这里的也就是mysql的数据目录 +master_ip_failover_script= /usr/local/bin/master_ip_failover +#设置自动failover时候的切换脚本 +master_ip_online_change_script= /usr/local/bin/master_ip_online_change +#设置手动切换时候的切换脚本 +password=123456 +#设置mysql中root用户的密码,这个密码是前文中创建监控用户的那个密码 +user=root +#设置监控用户root +ping_interval=1 +#设置监控主库,发送ping包的时间间隔,尝试三次没有回应的时候自动进行failover +remote_workdir=/tmp +#设置远端mysql在发生切换时binlog的保存位置 +repl_password=123456 +#设置复制用户的密码 +repl_user=rep +#设置复制环境中的复制用户名 +report_script=/usr/local/send_report +#设置发生切换后发送的报警的脚本 +#一旦MHA到server02的监控之间出现问题,MHA Manager将会尝试从server03登录到server02 +secondary_check_script= /usr/local/bin/masterha_secondary_check -s server03 -s server02 --user=root --master_host=server02 --master_ip=192.168.0.50 --master_port=3306 +shutdown_script="" +#设置故障发生后关闭故障主机脚本(该脚本的主要作用是关闭主机防止发生脑裂,这里没有使用) +ssh_user=root +#设置ssh的登录用户名 +[server1] +hostname=10.0.0.51 +port=3306 +[server2] +hostname=10.0.0.52 +port=3306 +candidate_master=1 +#设置为候选master,如果设置该参数以后,发生主从切换以后将会将此从库提升为主库,即使这个主库不是集群中事件最新的slave。 +check_repl_delay=0 +#默认情况下如果一个slave落后master 100M的relay logs的话,MHA将不会选择该slave作为一个新的master,因为对于这个slave的恢复需要花费很长时间,通过设置check_repl_delay=0,MHA触发切换在选择一个新的master的时候将会忽略复制延时,这个参数对于设置了candidate_master=1的主机非常有用,因为这个候选主在切换的过程中一定是新的master +``` + +- 配置ssh信任(所有节点) + +```shell +[root@mysql-db01 ~]# ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa >/dev/null 2>&1 +#创建秘钥对 +[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.0.0.51 +[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.0.0.52 +[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.0.0.53 +#发送公钥,包括自己 +``` + +- 启动测试 + +```shell +[root@mysql-db03 ~]# masterha_check_ssh --conf=/etc/mha/app1.cnf +#测试ssh +#看到如下字样,则测试成功 +Tue Mar 7 01:03:33 2017 - [info] All SSH connection tests passed successfully. +[root@mysql-db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf +#测试复制 +#看到如下字样,则测试成功 +#若不在slave库上创建用户会失败,按理说应该slave会同步master的库但创建rep用户是创建主从关系前 +#mysql> grant replication slave on *.* to rep@'10.0.0.%' identified by '123456'; +MySQL Replication Health is OK. +``` + +- 启动MHA + +```shell +[root@mysql-db03 ~]# nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/mha/app1/manager.log 2>&1 & +#启动 +[root@mysql-db03 ~]# masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover --shutdown +#关闭 +[root@mysql-db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf +#查看mha是否运行正常,正常会显示master的IP +``` + +- 切换master测试 + +```shell +[root@mysql-db02 ~]# mysql -uroot -p123456 +#登录数据库(db02) +mysql> show slave status\G +#检查复制情况 +*************************** 1. row *************************** + Slave_IO_State: Waiting for master to send event + Master_Host: 10.0.0.51 + Master_User: rep + Master_Port: 3306 + Connect_Retry: 60 + Master_Log_File: mysql-bin.000006 + Read_Master_Log_Pos: 191 + Relay_Log_File: mysql-db02-relay-bin.000002 + Relay_Log_Pos: 361 + Relay_Master_Log_File: mysql-bin.000006 + Slave_IO_Running: Yes + Slave_SQL_Running: Yes +[root@mysql-db03 ~]# mysql -uroot -p123456 +#登录数据库(db03) +mysql> show slave status\G +#检查复制情况 +*************************** 1. row *************************** + Slave_IO_State: Waiting for master to send event + Master_Host: 10.0.0.51 + Master_User: rep + Master_Port: 3306 + Connect_Retry: 60 + Master_Log_File: mysql-bin.000006 + Read_Master_Log_Pos: 191 + Relay_Log_File: mysql-db03-relay-bin.000002 + Relay_Log_Pos: 361 + Relay_Master_Log_File: mysql-bin.000006 + Slave_IO_Running: Yes + Slave_SQL_Running: Yes + +[root@mysql-db01 ~]# /etc/init.d/mysqld stop +#停掉主库 +Shutting down MySQL..... SUCCESS! +[root@mysql-db02 ~]# mysql -uroot -p123456 +#登录数据库(db02) +mysql> show slave status\G +#查看slave状态 +Empty set (0.00 sec) +#db02的slave已经为空 +[root@mysql-db03 ~]# mysql -uroot -p123456 +#登录数据库(db03) +mysql> show slave status\G +#查看slave状态 +*************************** 1. row *************************** + Slave_IO_State: Waiting for master to send event + Master_Host: 10.0.0.52 + Master_User: rep + Master_Port: 3306 + Connect_Retry: 60 + Master_Log_File: mysql-bin.000006 + Read_Master_Log_Pos: 191 + Relay_Log_File: mysql-db03-relay-bin.000002 + Relay_Log_Pos: 361 + Relay_Master_Log_File: mysql-bin.000006 + Slave_IO_Running: Yes + Slave_SQL_Running: Yes +``` + +此时停掉主库后再开启db01,db01正常了只能通过**手动方式以从库身份**加入 + +```sql +[root@mysql-db01 ~]# mysql -uroot -123456 +mysql> change master to +-> master_host='10.0.0.52', +-> master_user='rep', +-> master_password='123456', +-> master_auto_position=1; +-> start slave; +``` + + + +## 19.6 配置vIP漂移 + +- VIP漂移的两种方式 + - 通过keepalived的方式,管理虚拟IP的漂移,与后面Nginx负载均衡有关 + - 通过MHA自带脚本方式,管理虚拟IP的漂移 +- MHA脚本方式 + - 修改配置文件,failover文件http://mc.iproute.cn:31010/file/%E4%BA%91%E8%AE%A1%E7%AE%97%E8%AF%BE%E7%A8%8B%E8%BD%AF%E4%BB%B6%E5%B7%A5%E5%85%B7/master_ip_failover + +```shell +[root@mysql-db03 ~]# vim /etc/mha/app1.cnf +#编辑配置文件 +[server default] +#在[server default]标签下添加 +master_ip_failover_script=/etc/mha/master_ip_failover + + + +#使用MHA自带脚本,在下载的mha文件mysql-master-ha.zip解压后的文件里 +#tar xzvf mha4mysql-manager-0.56.tar.gz +#cd mha4mysql-manager-0.56/sample/script +#master-ip-failover文件就是自带的 + + + +#编辑脚本,该文件从wget而来 +[root@mysql-db03 ~]# vim /etc/mha/master_ip_failover +#根据配置文件中脚本路径编辑 +my $vip = '10.0.0.55/24'; +my $key = '0'; +#网卡名要改对,可能是ens33 +my $ssh_start_vip = "/sbin/ifconfig eth0:$key $vip"; +my $ssh_stop_vip = "/sbin/ifconfig eth0:$key down"; +#修改以下几行内容 +[root@mysql-db03 ~]# chmod +x /etc/mha/master_ip_failover +#添加执行权限,否则mha无法启动 +[root@mysql-db03 ~]# yum install net-tools +#安装IFconfig,每台设备都要安装否则脚本执行失败 + +* 手动绑定vIP,假设db01是master +[root@mysql-db01 ~]# ifconfig eth0:0 10.0.0.55/24 +#绑定vip,第一次要在master上手工配置,后面不需要了 +[root@mysql-db01 ~]# ip a |grep eth0 +#查看vip +2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 + inet 10.0.0.51/24 brd 10.0.0.255 scope global eth0 + inet 10.0.0.55/24 brd 10.0.0.255 scope global secondary eth0:0 + +*安装dos2unix,因为从wget获取的master_ip_failover在windows下编辑,换行符与linux不一样,需要转换 +[root@mysql-db03 mha]# yum install dos2unix +[root@mysql-db03 mha]# dos2unix master_ip_failover + +*重启mha + +[root@mysql-db03 ~]#masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover --shutdown +#关闭 +[root@mysql-db03 ~]# nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/mha/app1/manager.log 2>&1 & +#启动 +[root@mysql-db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf +#查看mha是否运行正常,正常会显示master的IP + + + + + +* 测试ip漂移 +#登录db02 +[root@mysql-db02 ~]# mysql -uroot -p123456 +#查看slave信息 +mysql> show slave status\G +*************************** 1. row *************************** + Slave_IO_State: Waiting for master to send event + Master_Host: 10.0.0.51 + Master_User: rep + Master_Port: 3306 + Connect_Retry: 60 + Master_Log_File: mysql-bin.000007 + Read_Master_Log_Pos: 191 + Relay_Log_File: mysql-db02-relay-bin.000002 + Relay_Log_Pos: 361 + Relay_Master_Log_File: mysql-bin.000007 + Slave_IO_Running: Yes + Slave_SQL_Running: Yes +#停掉主库 +[root@mysql-db01 ~]# /etc/init.d/mysqld stop +Shutting down MySQL..... SUCCESS! +#在db03上查看从库slave信息 +mysql> show slave status\G +*************************** 1. row *************************** + Slave_IO_State: Waiting for master to send event + Master_Host: 10.0.0.52 + Master_User: rep + Master_Port: 3306 + Connect_Retry: 60 + Master_Log_File: mysql-bin.000006 + Read_Master_Log_Pos: 191 + Relay_Log_File: mysql-db03-relay-bin.000002 + Relay_Log_Pos: 361 + Relay_Master_Log_File: mysql-bin.000006 + Slave_IO_Running: Yes + Slave_SQL_Running: Yes +#在db01上查看vip信息 +[root@mysql-db01 ~]# ip a |grep eth0 +2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 +inet 10.0.0.51/24 brd 10.0.0.255 scope global eth0 +#在db02上查看vip信息 +[root@mysql-db02 ~]# ip a |grep eth0 +2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 + inet 10.0.0.52/24 brd 10.0.0.255 scope global eth0 + inet 10.0.0.55/24 brd 10.0.0.255 scope global secondary eth0:0 +``` \ No newline at end of file diff --git a/03.数据库/01.Mysql/157ueBBseL6MvenH.png!thumbnail b/03.数据库/01.Mysql/157ueBBseL6MvenH.png!thumbnail new file mode 100644 index 0000000..0f39627 Binary files /dev/null and b/03.数据库/01.Mysql/157ueBBseL6MvenH.png!thumbnail differ diff --git a/03.数据库/01.Mysql/5TsFS9HElaslP5ss.png!thumbnail b/03.数据库/01.Mysql/5TsFS9HElaslP5ss.png!thumbnail new file mode 100644 index 0000000..50ab4d5 Binary files /dev/null and b/03.数据库/01.Mysql/5TsFS9HElaslP5ss.png!thumbnail differ diff --git a/03.数据库/01.Mysql/DsbxGtxBZzy6d1kN.png!thumbnail b/03.数据库/01.Mysql/DsbxGtxBZzy6d1kN.png!thumbnail new file mode 100644 index 0000000..9d1b43e Binary files /dev/null and b/03.数据库/01.Mysql/DsbxGtxBZzy6d1kN.png!thumbnail differ diff --git a/03.数据库/01.Mysql/G1WukGpm4kWFCtKy.png!thumbnail b/03.数据库/01.Mysql/G1WukGpm4kWFCtKy.png!thumbnail new file mode 100644 index 0000000..d7a8e70 Binary files /dev/null and b/03.数据库/01.Mysql/G1WukGpm4kWFCtKy.png!thumbnail differ diff --git a/03.数据库/01.Mysql/HET6YsqLtNayuI6Q.png!thumbnail b/03.数据库/01.Mysql/HET6YsqLtNayuI6Q.png!thumbnail new file mode 100644 index 0000000..274b2f1 Binary files /dev/null and b/03.数据库/01.Mysql/HET6YsqLtNayuI6Q.png!thumbnail differ diff --git a/03.数据库/01.Mysql/JnL2YEThjuHERQPZ.png!thumbnail b/03.数据库/01.Mysql/JnL2YEThjuHERQPZ.png!thumbnail new file mode 100644 index 0000000..f3971c9 Binary files /dev/null and b/03.数据库/01.Mysql/JnL2YEThjuHERQPZ.png!thumbnail differ diff --git a/03.数据库/01.Mysql/KkaagjFIBB8C0CCF.png!thumbnail b/03.数据库/01.Mysql/KkaagjFIBB8C0CCF.png!thumbnail new file mode 100644 index 0000000..0a3d697 Binary files /dev/null and b/03.数据库/01.Mysql/KkaagjFIBB8C0CCF.png!thumbnail differ diff --git a/03.数据库/01.Mysql/L8Ndjn2BuNL9IUl2.png!thumbnail b/03.数据库/01.Mysql/L8Ndjn2BuNL9IUl2.png!thumbnail new file mode 100644 index 0000000..05fdf37 Binary files /dev/null and b/03.数据库/01.Mysql/L8Ndjn2BuNL9IUl2.png!thumbnail differ diff --git a/03.数据库/01.Mysql/MHA_process.jpg b/03.数据库/01.Mysql/MHA_process.jpg new file mode 100644 index 0000000..b1bc3b3 Binary files /dev/null and b/03.数据库/01.Mysql/MHA_process.jpg differ diff --git a/03.数据库/01.Mysql/MHA_topo.jpg b/03.数据库/01.Mysql/MHA_topo.jpg new file mode 100644 index 0000000..1d2bd66 Binary files /dev/null and b/03.数据库/01.Mysql/MHA_topo.jpg differ diff --git a/03.数据库/01.Mysql/R5j17V2j8h153YXZ.png!thumbnail b/03.数据库/01.Mysql/R5j17V2j8h153YXZ.png!thumbnail new file mode 100644 index 0000000..d9b0785 Binary files /dev/null and b/03.数据库/01.Mysql/R5j17V2j8h153YXZ.png!thumbnail differ diff --git a/03.数据库/01.Mysql/Tw0AJl9FjrygD1S4.png!thumbnail b/03.数据库/01.Mysql/Tw0AJl9FjrygD1S4.png!thumbnail new file mode 100644 index 0000000..280b2b6 Binary files /dev/null and b/03.数据库/01.Mysql/Tw0AJl9FjrygD1S4.png!thumbnail differ diff --git a/03.数据库/01.Mysql/WCtzPCFRsaTUTp3A.png!thumbnail b/03.数据库/01.Mysql/WCtzPCFRsaTUTp3A.png!thumbnail new file mode 100644 index 0000000..ac1b24f Binary files /dev/null and b/03.数据库/01.Mysql/WCtzPCFRsaTUTp3A.png!thumbnail differ diff --git a/03.数据库/01.Mysql/agMqU13UrbmuyFz0.png!thumbnail b/03.数据库/01.Mysql/agMqU13UrbmuyFz0.png!thumbnail new file mode 100644 index 0000000..d868e34 Binary files /dev/null and b/03.数据库/01.Mysql/agMqU13UrbmuyFz0.png!thumbnail differ diff --git a/03.数据库/01.Mysql/k1P7hTxOM3vKCIG2.png!thumbnail b/03.数据库/01.Mysql/k1P7hTxOM3vKCIG2.png!thumbnail new file mode 100644 index 0000000..6b741b1 Binary files /dev/null and b/03.数据库/01.Mysql/k1P7hTxOM3vKCIG2.png!thumbnail differ diff --git a/03.数据库/01.Mysql/lK3mQW6m9VMwIgVD.png!thumbnail b/03.数据库/01.Mysql/lK3mQW6m9VMwIgVD.png!thumbnail new file mode 100644 index 0000000..f9080b8 Binary files /dev/null and b/03.数据库/01.Mysql/lK3mQW6m9VMwIgVD.png!thumbnail differ diff --git a/03.数据库/01.Mysql/mysql_goal.jpg b/03.数据库/01.Mysql/mysql_goal.jpg new file mode 100644 index 0000000..5ab52d6 Binary files /dev/null and b/03.数据库/01.Mysql/mysql_goal.jpg differ diff --git a/03.数据库/01.Mysql/remote_mem_larger_local_disk.jpg b/03.数据库/01.Mysql/remote_mem_larger_local_disk.jpg new file mode 100644 index 0000000..ccf1377 Binary files /dev/null and b/03.数据库/01.Mysql/remote_mem_larger_local_disk.jpg differ diff --git a/03.数据库/01.Mysql/semi_synchronized_dual_master.jpg b/03.数据库/01.Mysql/semi_synchronized_dual_master.jpg new file mode 100644 index 0000000..663e3b1 Binary files /dev/null and b/03.数据库/01.Mysql/semi_synchronized_dual_master.jpg differ diff --git a/03.数据库/01.Mysql/sql_MS.jpg b/03.数据库/01.Mysql/sql_MS.jpg new file mode 100644 index 0000000..6561945 Binary files /dev/null and b/03.数据库/01.Mysql/sql_MS.jpg differ diff --git a/03.数据库/01.Mysql/tL76EP1rBEQKcpeU.png!thumbnail b/03.数据库/01.Mysql/tL76EP1rBEQKcpeU.png!thumbnail new file mode 100644 index 0000000..da23166 Binary files /dev/null and b/03.数据库/01.Mysql/tL76EP1rBEQKcpeU.png!thumbnail differ diff --git a/03.数据库/01.Mysql/uCoOhC6Rn5HuTfxg.png!thumbnail b/03.数据库/01.Mysql/uCoOhC6Rn5HuTfxg.png!thumbnail new file mode 100644 index 0000000..af5f847 Binary files /dev/null and b/03.数据库/01.Mysql/uCoOhC6Rn5HuTfxg.png!thumbnail differ diff --git a/03.数据库/01.Mysql/uxH9iBpInyy8pwrk.png!thumbnail b/03.数据库/01.Mysql/uxH9iBpInyy8pwrk.png!thumbnail new file mode 100644 index 0000000..c044005 Binary files /dev/null and b/03.数据库/01.Mysql/uxH9iBpInyy8pwrk.png!thumbnail differ diff --git a/03.数据库/01.Mysql/yhWTJJz8GZkilbZN.png!thumbnail b/03.数据库/01.Mysql/yhWTJJz8GZkilbZN.png!thumbnail new file mode 100644 index 0000000..1b37974 Binary files /dev/null and b/03.数据库/01.Mysql/yhWTJJz8GZkilbZN.png!thumbnail differ diff --git a/03.数据库/02.Redis.md b/03.数据库/02.Redis.md new file mode 100644 index 0000000..27666b6 --- /dev/null +++ b/03.数据库/02.Redis.md @@ -0,0 +1,3619 @@ +# 1. 缓存的概念 + +缓存是为了调节速度不一致的两个或多个不同的物质的速度,在中间对速度较快的一方起到一个加速访问速度较慢的一方的作用 + +比如 CPU 的一级、二级缓存是保存了 CPU 最近经常访问的数据,内存是保存 CPU 经常访问硬盘的数据,而且硬盘也有大小不一的缓存,甚至是物理服务器的 raid 卡有也缓存 + +为了起到加速 CPU 访问硬盘数据的目的,因为 CPU 的速度太快了, CPU 需要的数据硬盘往往不能在短时间内满足 CPU 的需求, + +因此 PCU 缓存、内存、 Raid 卡以及硬盘缓存就在一定程度上满足了 CPU 的数据需求,即 CPU 从缓存读取数据可以大幅提高 CPU 的工作效率。 + +![img-缓存](02.Redis/缓存.png) + +## 1.1 系统缓存 + +### 1.1.1 buffer 与 cache + +buffer:缓冲也叫写缓冲,一般用于写操作,可以将数据先写入内存再写入磁盘,buffer 一般用于写缓冲,用于解决不同介质的速度不一致的缓冲,先将数据临时写入到里自己最近的地方,以提高写入速度,CPU 会把数据先写到内存的磁盘缓冲区,然后就认为数据已经写入完成看,然后由内核在后续的时间再写入磁盘,所以服务器突然断电会丢失内存中的部分数据。 + +cache:缓存也叫读缓存,一般用于读操作,CPU 读文件从内存读,如果内存没有就先从硬盘读到内存再读到 CPU,将需要频繁读取的数据放在里自己最近的缓存区域,下次读取的时候即可快速读取。 + +### 1.1.2 cache 的保存位置 + +- 客户端:浏览器 +- 内存:本地服务器、远程服务器 +- 硬盘:本机硬盘、远程服务器硬盘 + +### 1.1.3 cache 的特性 + +- 自动过期:给缓存的数据加上有效时间,超出时间后自动过期删除 +- 过期时间:强制过期,源网站更新图片后 CDN 是不会更新的,需要强制使图片缓存过期 +- 命中率:即缓存的读取命中率 + +## 1.2 用户层缓存 + +### 1.2.1 DNS 缓存 + +- 默认为 60 秒,即 60 秒之内再访问同一个域名就不再进行 DNS 解析 +- 查看 chrome 浏览器的 DNS 缓存:chrome://net-internals/#dns +- DNS 预获取,仅在HTML5中支持,当一个页面中包含多个域名的时候浏览器会先尝试解析域名并进行缓存,之后再使用的时候即可直接使用不需要再进行DNS 解析 + +## 1.3 浏览器缓存过期机制 + +### 1.3.1 最后修改时间 + +系统调用会获取文件的最后修改时间,如果没有发生变化就返回给浏览器304 的状态码,表示没有发生变化,然后浏览器就使用的本地的缓存展示资源。 + +### 1.3.2 Etag标记 + +基于Etag标记是否一直做判断页面是否发生过变化,比如基于Nginx的Etag on来实现 + +### 1.3.3 过期时间 expires + +以上两种都需要发送请求,即不管资源是否过期都要发送请求进行协商,这样会消耗不必要的时间,因此有了缓存的过期时间 + +Expire 是 HttpHeader 中代表资源的过期时间,由服务器端设置。如果带有 Expire ,则在 Expire 过期前不会发生 Http 请求,直接从缓存中读取。用户强制 F5 例外 + +第一次请求资源时,响应报文带有资源的过期时间,默认为30天,当前此方式使用的比较多,但是无法保证客户的时间都是准确并且一致的,因此会加入一个最大生存周期,使用用户本地的时间计算缓存数据是否超过多少天,假如过期时间Expires:为2031年,但是缓存的最大生存周期Cache-Control: max-age=315360000,计算为天等于3650天即10年 + +### 1.3.4 混合使用和缓存刷新 + +通常 Last-Modified,Etag,Expire 是一起混合使用的 + +- 特别是 Last-Modified 和 Expire 经常一起使用,因为 Expire 可以让浏览器完全不发起 Http 请求,而当浏览器强制 F5 的时候又有 Last-Modified ,这样就很好的达到了浏览器段缓存的效果。 +- Etag 和 Expire 一起使用时,先判断 Expire ,如果已经过期,再发起 Http 请求,如果 Etag 变化了,则返回 200 响应。如果 Etag 没有变化,则返回 304 响应。 +- Last-Modified,Etag,Expires 三个同时使用时。先判断 Expire ,然后发送 Http 请求,服务器先判断 last-modified ,再判断 Etag ,必须都没有过期,才能返回 304 响应。 + +#### 1.3.4.1 缓存刷新 + +- 第一次访问,获取最新数据,返回 200响应码 +- 鼠标点击二次访问 (Cache),输入地址后回车,浏览器对所有没有过期的内容直接使用本地缓存。 +- F5或点刷新按钮, 会向服务器发送请求缓存协商信息,last-modified和etag会有影响,但expires本地过期时间不受影响,无变化返回304 +- 按shift+F5强制刷新,所有缓存不再使用,直接连接服务器,获取最新数据,返回200响应码 + +### 1.3.5 cookie 和 session + +Cookie是访问某些网站以后在本地存储的一些网站相关的信息,下次再访问的时候减少一些步骤,比如加密后的账户名密码等信息 + +Cookies是服务器在客户端浏览器上存储的小段文本并随每一个请求发送至同一个服务器,是一种实现客户端保持状态的方案。 + +session称为会话信息,位于web服务器上,主要负责访问者与网站之间的交互,当浏览器请求http地址时,可以基于之前的session实现会话保持、session共享等。 + +## 1.4 CDN 缓存 + +### 1.4.1 什么是CDN + +![img-CDN缓存](02.Redis/CDN缓存.png) + +内容分发网络(Content Delivery Network,CDN)是建立并覆盖在承载网上,由不同区域的服务器组成的分布式网络。将源站资源缓存到全国各地的边缘服务器,利用全球调度系统使用户能够就近获取,有效降低访问延迟,降低源站压力,提升服务可用性。 + +常见的CDN服务商 + +- 百度CDN:https://cloud.baidu.com/product/cdn.html +- 阿里CDN:https://www.aliyun.com/product/cdn?spm=5176.8269123.416540.50.728y8n +- 腾讯CDN:https://www.qcloud.com/product/cdn +- 腾讯云CDN收费介绍:https://cloud.tencent.com/document/product/228/2949 + +### 1.4.2 用户请求CDN流程 + +假设您的业务源站域名为[www.test.com](http://www.test.com/),域名接入 CDN 开始使用加速服务后,当您的用户发起HTTP 请求时,实际的处理流程如下图所示: + +![img-CDN请求流程01](02.Redis/CDN请求流程01.png) + +详细说明如下: + +1. 用户向[www.test.com](http://www.test.com/)下的某图片资源(如:1.jpg)发起请求,会先向 Local DNS 发起域名解析请求。 +2. 当 Local DNS 解析[www.test.com](http://www.test.com/)时,会发现已经配置了 CNAME[www.test.com.cdn.dnsv1.com](http://www.test.com.cdn.dnsv1.com/),解析请求会发送至 Tencent DNS(GSLB),GSLB 为腾讯云自主研发的调度体系,会为请求分配最佳节点 IP。 +3. Local DNS 获取 Tencent DNS 返回的解析 IP。 +4. 用户获取解析 IP。 +5. 用户向获取的 IP 发起对资源 1.jpg 的访问请求。 +6. 若该 IP 对应的节点缓存有 1.jpg,则会将数据直接返回给用户(10),此时请求结束。若该节点未缓存 1.jpg,则节点会向业务源站发起对 1.jpg 的请求(6、7、8),获取资源后,结合用户自定义配置的缓存策略,将资源缓存至节点(9),并返回给用户(10),此时请求结束。 + +![img-CDN请求流程02](02.Redis/CDN请求流程02.png) + +### 1.4.3 利用 302 实现转发请求重定向至最优服务器集群 + +因为中国网络较为复杂,依赖DNS就近解析的调度,仍然会存在部分请求调度失效、调度生效慢等问题。 + +比如:腾讯云利用在全国部署的302重定向服务器集群,能够为每一个请求实时决策最优的服务器资源,精准解决小运营商的调度问题,提升用户访问质量, 能最快地把用户引导到最优的服务器节点上,避开性能差或者异常的节点。 + +### 1.4.4 CDN 分层缓存 + +提前对静态内容进行预缓存,避免大量的请求回源,导致主站网络带宽被打满而导致数据无法更新,另外CDN可以将数据根据访问的热度不同而进行不同级别的缓存,例如:访问量最高的资源访问CDN 边缘节点的内存,其次的放在SSD或者SATA,再其次的放在云存储,这样兼顾了速度与成本。 + +比如: 腾讯云CDN节点,根据用户的数据冷热不同,动态的进行识别,按照cache层次进行数据的存储,在访问频率到40%-90%的数据,首先放在OC边缘节点内存cache中,提供8G-64G的数据空间存储;在访问频率到30%-50%的数据,放在OC节点SSD/SATA硬盘cache中,提供1T-15T的数据空间存猪,其他的比较冷的数据,放在云存储中,采用回源拉取的方式进行处理。这样在成本和效率中计算出最优平衡点,为客户提供服务。 + +### 1.4.5 CDN主要优势 + +CDN 有效地解决了目前互联网业务中网络层面的以下问题: + +- 用户与业务服务器地域间物理距离较远,需要进行多次网络转发,传输延时较高且不稳定。 +- 用户使用运营商与业务服务器所在运营商不同,请求需要运营商之间进行互联转发。 +- 业务服务器网络带宽、处理能力有限,当接收到海量用户请求时,会导致响应速度降低、可用性降低。 +- 利用CDN防止和抵御DDos等攻击,实现安全保护 + +## 1.5 应用层缓存 + +Nginx、PHP等web服务可以设置应用缓存以加速响应用户请求,另外有些解释性语言,比如:PHP/Python/Java不能直接运行,需要先编译成字节码,但字节码需要解释器解释为机器码之后才能执行,因此字节码也是一种缓存,有时候还会出现程序代码上线后字节码没有更新的现象。所以一般上线新版前,需要先将应用缓存清理,再上线新版 + +另外可以利用动态页面静态化技术,加速访问,比如:将访问数据库的数据的动态页面,提前用程序生成静态页面文件html.电商网站的商品介绍,评论信息非实时数据等皆可利用此技术实现 + +## 1.6 数据层缓存 + +分布式缓存服务 + +- Redis +- Memcached + +数据库 + +- MySQL 查询缓存 +- innodb缓存、MyISAM缓存 + +## 1.7 硬件缓存 + +### 1.7.1 CPU缓存 + +CPU缓存(L1的数据缓存和L1的指令缓存)、二级缓存、三级缓存 + +![img-CPU缓存01](02.Redis/CPU缓存01.png) + +![img-CPU缓存02](02.Redis/CPU缓存02.png) + +## 1.8 磁盘相关缓存 + +- 磁盘缓存:Disk Cache +- 磁盘阵列缓存:Raid Cache,可使用电池防止断电丢失数据 + +# 2. redis 基础 + +## 2.1 redis 简介 + +短短几年,Redis就有了很大的用户群体,目前国内外使用的公司众多,比如:阿里,百度,新浪微博,知乎网,GitHub,Twitter 等 + +Redis是一个开源的、遵循BSD协议的、基于内存的而且目前比较流行的键值数据库(key-value database),是一个非关系型数据库,redis 提供将内存通过网络远程共享的一种服务,提供类似功能的还有memcached,但相比memcached,redis还提供了易扩展、高性能、具备数据持久性等功能。 + +Redis 在高并发、低延迟环境要求比较高的环境使用量非常广泛,目前redis在DB-Engine月排行榜https://db-engines.com/en/ranking 中一直比较靠前,而且一直是键值型存储类的首位 + +![img-Redis_rank](02.Redis/Redis_rank.png) + + +官网地址:https://redis.io/ + +## 2.2 Redis 特性 + +- 速度快: 10W QPS,基于内存,C语言实现 +- 单线程 +- 持久化 +- 支持多种数据结构 +- 支持多种编程语言 +- 功能丰富: 支持Lua脚本,发布订阅,事务,pipeline等功能 +- 简单: 代码短小精悍(单机核心代码只有23000行左右),单线程开发容易,不依赖外部库,使用简单 +- 主从复制 +- 支持高可用和分布式 + +## 2.3 单线程 + +Redis 6.0版本前一直是单线程方式处理用户的请求 + +![img-redis单线程01](02.Redis/redis单线程01.png) + +单线程为何如此快? + +- 纯内存 +- 非阻塞 +- 避免线程切换和竞态消耗 + +![img-redis单线程02](02.Redis/redis单线程02.png) + +注意事项: + +- 一次只运行一条命令 +- 拒绝长(慢)命令:keys, flushall, flushdb, slow lua script, mutil/exec, operate big value(collection) +- 其实不是单线程: 早期版本是单进程单线程,3版本后实际还有其它的线程, fysnc file descriptor,close file descriptor + +## 2.4 redis 对比 memcached + +- 支持数据的持久化:可以将内存中的数据保持在磁盘中,重启redis服务或者服务器之后可以从备份文件中恢复数据到内存继续使用 +- 支持更多的数据类型:支持string(字符串)、hash(哈希数据)、list(列表)、set(集合)、zset(有序集合) +- 支持数据的备份:可以实现类似于数据的master-slave模式的数据备份,另外也支持使用快照+AOF +- 支持更大的value数据:memcache单个key value最大只支持1MB,而redis最大支持512MB(生产不建议超过2M,性能受影响) +- 在Redis6版本前,Redis 是单线程,而memcached是多线程,所以单机情况下没有memcached 并发高,性能更好,但redis 支持分布式集群以实现更高的并发,单Redis实例可以实现数万并发 +- 支持集群横向扩展:基于redis cluster的横向扩展,可以实现分布式集群,大幅提升性能和数据安全性 +- 都是基于 C 语言开发 + +## 2.5 redis 典型应用场景 + +- Session 共享:常见于web集群中的Tomcat或者PHP中多web服务器session共享 +- 缓存:数据查询、电商网站商品信息、新闻内容 +- 计数器:访问排行榜、商品浏览数等和次数相关的数值统计场景 +- 微博/微信社交场合:共同好友,粉丝数,关注,点赞评论等 +- 消息队列:ELK的日志缓存、部分业务的订阅发布系统 +- 地理位置: 基于GEO(地理信息定位),实现摇一摇,附近的人,外卖等功能 + +**数据更新操作流程:** + +![img-redis业务更新操作](02.Redis/redis业务更新操作.png) + +**数据读操作流程:** + +![img-redis业务读操作](02.Redis/redis业务读操作.png) + +# 3. Redis 安装及连接 + +官方下载地址:http://download.redis.io/releases/ + +## 3.1 yum安装redis + +在centos系统上需要安装epel源 + +### 3.1.1 查看yum仓库redis版本 + +```shell +[root@localhost ~]# yum -y install epel-release +[root@localhost ~]# yum info redis +``` + +### 3.1.2 yum安装 redis + +```shell +[root@localhost ~]# yum -y install redis +[root@localhost ~]# systemctl enable --now redis +[root@localhost ~]# pstree -p |grep redis +[root@localhost ~]# redis-cli +127.0.0.1:6379> ping +PONG +127.0.0.1:6379> info +``` + +## 3.2 编译安装 redis + +下载当前最新release版本 redis 源码包 + +网站:http://download.redis.io/releases/ + +### 3.2.1 编译安装 + +- 获取软件安装包,安装编译环境 + +```shell +[root@localhost ~]# yum -y install make gcc tcl +[root@localhost ~]# wget http://download.redis.io/releases/redis-5.0.9.tar.gz +[root@localhost ~]# tar xf redis-5.0.9.tar.gz +``` + +- 编译安装 + +```shell +[root@localhost ~]# cd redis-5.0.9/ +[root@localhost redis-5.0.9]# cd src/ +[root@localhost src]# make +[root@localhost src]# make PREFIX=/apps/redis install +``` + +- 配置变量 + +```shell +[root@localhost src]# echo "PATH=/apps/redis/bin:$PATH" > /etc/profile.d/redis.sh +[root@localhost src]# . /etc/profile.d/redis.sh +``` + +- 目录结构 + +```shell +[root@localhost src]# tree /apps/redis/ +/apps/redis/ +└── bin + ├── redis-benchmark + ├── redis-check-aof + ├── redis-check-rdb + ├── redis-cli + ├── redis-sentinel -> redis-server + └── redis-server +1 directory, 6 files + +redis-benchmark: 这是 Redis 的性能测试工具,用于测试 Redis 服务器的性能。 +redis-check-aof: 这个工具用于检查和修复 Redis 的 Append-Only File (AOF)。AOF 是 Redis 持久化的一种方式,用于记录所有对 Redis 数据库的写入操作。 +redis-check-rdb: 这个工具用于检查和修复 Redis 的 RDB 文件。RDB 是 Redis 另一种持久化方式,它会定期将数据库中的数据保存到磁盘上。 +redis-cli: 这是 Redis 的命令行客户端工具,可用于连接 Redis 服务器并执行各种操作。 +redis-sentinel: 这实际上是一个指向 redis-server 的符号链接。在 Redis 的高可用架构中,Sentinel 是用于监控 Redis 主从复制拓扑并在出现故障时进行自动failover的组件。 +redis-server: 这是 Redis 服务器的主程序文件,用于启动和运行 Redis 服务器。 +``` + +准备相关目录和文件 + +```shell +[root@localhost ~]# mkdir /apps/redis/{etc,log,data,run} +[root@localhost ~]# cp redis-5.0.9/redis.conf /apps/redis/etc/ +``` + +### 3.2.2 前台启动 redis + +redis-server 是redis 服务器程序 + +```shell +[root@localhost src]# redis-server --help +Usage: ./redis-server [/path/to/redis.conf] [options] + ./redis-server - (read config from stdin) + ./redis-server -v or --version + ./redis-server -h or --help + ./redis-server --test-memory +Examples: + ./redis-server (run the server with default conf) + ./redis-server /etc/redis/6379.conf + ./redis-server --port 7777 + ./redis-server --port 7777 --replicaof 127.0.0.1 8888 + ./redis-server /etc/myredis.conf --loglevel verbose +Sentinel mode: + ./redis-server /etc/sentinel.conf --sentinel +``` + +前台启动 redis + +```shell +[root@localhost src]# redis-server /apps/redis/etc/redis.conf + _._ + _.-``__ ''-._ + _.-`` `. `_. ''-._ Redis 5.0.9 (00000000/0) 64 bit + .-`` .-```. ```\/ _.,_ ''-._ + ( ' , .-` | `, ) Running in standalone mode + |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 + | `-._ `._ / _.-' | PID: 11791 + `-._ `-._ `-./ _.-' _.-' + |`-._`-._ `-.__.-' _.-'_.-'| + | `-._`-._ _.-'_.-' | http://redis.io + `-._ `-._`-.__.-'_.-' _.-' + |`-._`-._ `-.__.-' _.-'_.-'| + | `-._`-._ _.-'_.-' | + `-._ `-._`-.__.-'_.-' _.-' + `-._ `-.__.-' _.-' + `-._ _.-' + `-.__.-' +[root@localhost ~]# ss -ntl +State Recv-Q Send-Q Local Address:Port Peer Address:Port +LISTEN 0 128 127.0.0.1:6379 *:* +``` + +### 3.2.3 启动多实例 + +- 刚刚启动的案例是6379端口,我们可以在6380端口上开启第二个redis服务 +- 为6380准备相关目录和文件 + +```shell +[root@localhost ~]# mkdir /apps/redis/6380 +[root@localhost ~]# cp -ar /apps/redis/* /apps/redis/6380/ +[root@localhost ~]# tree -d /apps/redis/6380 +/apps/redis/6380 +├── 6380 +├── bin +├── data +├── etc +├── log +└── run +6 directories +[root@localhost ~]# vim /apps/redis/6380/etc/redis.conf +port 6380 +``` + +- 前端启动6380 + +```shell +[root@localhost ~]# redis-server --port 6380 +[root@localhost ~]# ss -ntl +State Recv-Q Send-Q Local Address:Port Peer Address:Port +LISTEN 0 128 *:6379 *:* +LISTEN 0 128 *:6380 *:* +LISTEN 0 128 :::6379 :::* +LISTEN 0 128 :::6380 :::* +[root@localhost ~]# redis-cli -p 6380 +127.0.0.1:6380> exit +[root@localhost ~]# redis-cli -p 6379 +127.0.0.1:6379> exit +``` + +### 3.2.4 解决启动时的三个警告提示 + +- tcp-backlog:控制的是三次握手的时候server端收到client.ack确认号之后的队列值,即全连接队列 + +```shell +[root@localhost ~]# echo "net.core.somaxconn = 1024" >> /etc/sysctl.conf +[root@localhost ~]# sysctl -p +net.core.somaxconn = 1024 +``` + +- vm.overcommit_memory + - 查看警告信息有提示,建议将其值改为1 + - 0 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。 + - 1 表示内核允许分配所有的物理内存,而不管当前的内存状态如何 + - 2 表示内核允许分配超过所有物理内存和交换空间总和的内存 + +```shell +[root@localhost ~]# echo "vm.overcommit_memory=1" >> /etc/sysctl.conf +[root@localhost ~]# sysctl -p +net.core.somaxconn = 1024 +vm.overcommit_memory = 1 +``` + +- transparent huge pages + - 警告:您在内核中启用了透明大页面(THP,不同于一般内存页的4k为2M)支持。 这将在Redis中造成延迟和内存使用问题。 要解决此问题,请以root 用户身份运行命令`echo never> /sys/kernel/mm/transparent_hugepage/enabled`,并将其添加到您的`/etc/rc.local`中,以便在重启后保留设置。禁用THP后,必须重新启动Redis。 + +```shell +[root@localhost ~]# echo never > /sys/kernel/mm/transparent_hugepage/enabled +[root@localhost ~]# echo "echo never > /sys/kernel/mm/transparent_hugepage/enabled" >> /etc/rc.d/rc.local +[root@localhost ~]# chmod +x /etc/rc.d/rc.local +``` + +- 再次启动redis可以看到警告消除,建议在其它redis服务器上做以上配置 + +### 3.2.5 创建 redis 用户 + +```shell +[root@localhost ~]# useradd -r -s /sbin/nologin redis +[root@localhost ~]# chown -R redis.redis /apps/redis/ +``` + +### 3.2.6 编辑 redis 服务启动文件 + +- 复制其它主机yum安装生成的redis.service文件,进行修改 + +```shell +[root@localhost ~]# vim /lib/systemd/system/redis.service +[Unit] +Description=Redis persistent key-value database +After=network.target +[Service] +ExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis.conf --supervised systemd +ExecStop=/bin/kill -s QUIT $MAINPID +Type=notify +User=redis +Group=redis +RuntimeDirectory=redis +RuntimeDirectoryMode=0755 +[Install] +WantedBy=multi-user.target +``` + +### 3.2.7 验证 redis 启动 + +```shell +[root@localhost ~]# systemctl daemon-reload +[root@localhost ~]# systemctl enable --now redis +[root@localhost ~]# ss -ntl +State Recv-Q Send-Q Local Address:Port Peer Address:Port +LISTEN 0 511 127.0.0.1:6379 *:* +``` + +### 3.2.8 使用客户端连接 redis + +- 格式 + +```shell +redis-cli -h IP/HOSTNAME -p PORT -a PASSWORD +``` + +- 连接示例 + +```shell +[root@localhost ~]# redis-cli +127.0.0.1:6379> info +127.0.0.1:6379> exit +``` + +### 3.2.9 创建命令软链接 + +```shell +[root@localhost ~]# ln -s /apps/redis/bin/ /usr/bin/ +``` + +### 3.2.10 编译安装后的命令 + +```shell +[root@localhost ~]# ll /apps/redis/bin/ +总用量 32772 +-rwxr-xr-x. 1 redis redis 4367128 7月 3 21:19 redis-benchmark +-rwxr-xr-x. 1 redis redis 8125424 7月 3 21:19 redis-check-aof +-rwxr-xr-x. 1 redis redis 8125424 7月 3 21:19 redis-check-rdb +-rwxr-xr-x. 1 redis redis 4808096 7月 3 21:19 redis-cli +lrwxrwxrwx. 1 redis redis 12 7月 3 21:19 redis-sentinel -> redis-server +-rwxr-xr-x. 1 redis redis 8125424 7月 3 21:19 redis-server +``` + +- 工具作用 + + | 工具 | 作用 | + | :----------------------------- | :------------------- | + | redis-benchmark | redis 性能测试工具 | + | redis-check-aof | AOF文件检查工具 | + | redis-check-rdb | RDB文件检查工具 | + | redis-cli | 客户端工具 | + | redis-sentinel -> redis-server | 哨兵,软链接到server | + | redis-server | redis 服务启动命令 | + +### 3.2.11 一键编译安装Redis脚本 + +```shell +#!/bin/bash +. /etc/init.d/functions +VERSION=redis-5.0.9 +DIR1=/apps/redis +PASSWORD=centos +install() { +yum -y install make wget gcc tcl &> /dev/null || { action "安装所需包失败,请检测包或网络配置" false;exit;} +wget http://download.redis.io/releases/${VERSION}.tar.gz &> /dev/null || { action "Redis 源码下载失败" false; exit; } +tar xf $VERSION.tar.gz +cd $VERSION/ +make -j 2 &> /dev/null && make PREFIX=${DIR1} install &> /dev/null && action "Redis 编译安装成功" || { action "Redis 编译安装失败" false;exit; } +ln -s ${DIR1}/bin/* /usr/bin/ +mkdir -p ${DIR1}/{etc,data,log,run} +cd +cp $VERSION/redis.conf $DIR1/etc +sed -i -e "s/bind 127.0.0.1/bind 0.0.0.0/" -e "/# requirepass/a requirepass ${PASSWORD}" -e "/^dir .*/c dir ${DIR1}/data/" -e "/logfile .*/c logfile ${DIR1}/log/redis_6379.log" -e "/^pidfile .*/c pidfile ${DIR1}/run/redis_6379.pid" ${DIR1}/etc/redis.conf + +if id redis &> /dev/null;then + action "redis 用户已经存在" false +else + useradd -r -s /sbin/nologin redis + action "redis 用户创建成功" +fi +chown -R redis.redis ${DIR1} +cat >> /etc/sysctl.conf < /sys/kernel/mm/transparent_hugepage/enabled +echo "echo never > /sys/kernel/mm/transparent_hugepage/enabled" >> /etc/rc.d/rc.local +chmod +x /etc/rc.d/rc.local +/etc/rc.d/rc.local +cat > /lib/systemd/system/redis.service < /dev/null && action "redis 服务启动成功" || { action "redis 服务启动失败" false;exit; } +} +install +``` + +## 3.3 连接到 Redis + +主要分为客户端连接和程序的连接 + +### 3.3.1 客户端连接 redis + +- 本机无密码连接 + +```shell +[root@localhost ~]# redis-cli +127.0.0.1:6379> +``` + +- 跨主机无密码连接 + +```shell +[root@localhost ~]# redis-cli -h 192.168.88.140 -p 6379 +192.168.175.149:6379> +``` + +- 跨主机密码连接 + +```shell +[root@localhost ~]# vim /apps/redis/etc/redis.conf +requirepass centos +[root@localhost ~]# systemctl restart redis +[root@localhost ~]# redis-cli -h 192.168.88.140 -p 6379 -a centos --no-auth-warning +192.168.88.140:6379> + +-a centos: 使用密码 centos 进行身份验证。 +--no-auth-warning: 不显示身份验证警告消息。 +``` + +### 3.3.2 程序连接 Redis + +- redis 支持多种开发语言访问 https://redis.io/clients + +![img-redis客户端](02.Redis/redis客户端.png) + +### 3.3.3 shell 连接方式 + +```shell +[root@localhost ~]# vim redis_test.sh +#!/bin/bash +NUM=`seq 1 10000` +PASS=centos +for i in ${NUM};do +redis-cli -h 127.0.0.1 -a "$PASS" --no-auth-warning set key-${i} value-${i} +echo "key-${i} value-${i} 写入完成" +done +echo "一万个key写入到Redis完成" +[root@localhost ~]# vim /apps/redis/etc/redis.conf#这边临时关闭RDB,不然会报错 +save "" +#save 900 1 +#save 300 10 +#save 60 10000 +[root@localhost ~]# systemctl restart redis +[root@localhost ~]# time bash redis_test.sh +一万个key写入到Redis完成 +real 0m22.874s +user 0m3.885s +sys 0m17.958s +[root@localhost ~]# redis-cli +127.0.0.1:6379> auth centos +OK +127.0.0.1:6379> keys * +127.0.0.1:6379> get key-996 +"value-996" +127.0.0.1:6379> flushdb # 清空当前库的数据 +OK +127.0.0.1:6379> keys * +(empty list or set) +127.0.0.1:6379> flushall # 清空所有的数据 +OK +``` + +### 3.3.4 python 连接方式 + +- python 多种开发库,可以支持连接redis +- 安装python运行环境 + +```shell +[root@localhost ~]# yum -y install python3 python3-redis +``` + +- 编写python程序 + +```python +[root@localhost ~]# vim redis_test.py +#!/bin/env python3 +import redis +#import time +pool = redis.ConnectionPool(host="127.0.0.1",port=6379,password="centos") +r = redis.Redis(connection_pool=pool) +for i in range(100): + r.set("k%d" % i,"v%d" % i) +# time.sleep(1) + data=r.get("k%d" % i) + print(data) +[root@localhost ~]# python3 redis_test.py +``` + +- 验证数据是否正确插入 + +```shell +[root@localhost ~]# redis-cli -a centos --no-auth-warning +127.0.0.1:6379> get k88 +"v88" +``` + +## 3.4 redis 的多实例 + +- 测试环境中经常使用多实例,需要指定不同实例的相应的端口,配置文件,日志文件等相关配置 +- 以编译安装为例实现 redis 多实例 +- 修改配置文件和启动服务 + +```shell +[root@localhost ~]# cd /apps/redis/ +[root@localhost redis]# mv etc/redis.conf etc/redis_6379.conf +[root@localhost redis]# vim etc/redis_6379.conf +port 6379 +pidfile /apps/redis/run/redis_6379.pid +logfile "/apps/redis/log/redis_6379.log" +dbfilename dumpi_6379.rdb +dir /apps/redis/data +appendfilename "appendonlyi_6379.aof" +[root@localhost redis]# mv /lib/systemd/system/redis.service /lib/systemd/system/redis_6379.service +[root@localhost redis]# vim /lib/systemd/system/redis_6379.service +ExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis_6379.conf --supervised systemd +[root@localhost redis]# systemctl restart redis_6379.service +``` + +- 按照同样的方式,启动6380和6381实例 + +```shell +cp -a etc/redis_6379.conf etc/redis_6380.conf +sed -i "s/6379/6380/g" etc/redis_6380.conf +cp -a /lib/systemd/system/redis_6379.service /lib/systemd/system/redis_6380.service +sed -i "s/6379/6380/g" /lib/systemd/system/redis_6380.service +systemctl enable --now redis_6380.service +cp -a etc/redis_6379.conf etc/redis_6381.conf +sed -i "s/6379/6381/g" etc/redis_6381.conf +cp -a /lib/systemd/system/redis_6379.service /lib/systemd/system/redis_6381.service +sed -i "s/6379/6381/g" /lib/systemd/system/redis_6381.service +systemctl daemon-reload +systemctl enable --now redis_6381.service +``` + +- 验证端口号 + +```shell +[root@localhost redis]# ss -ntl +State Recv-Q Send-Q Local Address:Port Peer Address:Port +LISTEN 0 511 *:6379 *:* +LISTEN 0 511 *:6380 *:* +LISTEN 0 511 *:6381 *:* +``` + +- 查看文件结构目录 + +```shell +[root@localhost redis]# tree +. +├── bin +│ ├── redis-benchmark +│ ├── redis-check-aof +│ ├── redis-check-rdb +│ ├── redis-cli +│ ├── redis-sentinel -> redis-server +│ └── redis-server +├── data +├── etc +│ ├── redis_6379.conf +│ ├── redis_6380.conf +│ └── redis_6381.conf +├── log +│ ├── redis_6379.log +│ ├── redis_6380.log +│ └── redis_6381.log +└── run + ├── redis_6379.pid + ├── redis_6380.pid + └── redis_6381.pid +5 directories, 15 files +``` + +# 4. redis 配置和优化 + +## 4.1 redis 主要配置项 + +- redis常用配置参数 + +```shell +bind 0.0.0.0 #监听地址,可以用空格隔开后多个监听IP +protected-mode yes #redis3.2之后加入的新特性,在没有设置bind IP和密码的时候,redis只允许访问127.0.0.1:6379,可以远程连接,但当访问将提示警告信息并拒绝远程访问 +port 6379 #监听端口,默认6379/tcp +tcp-backlog 511 #三次握手的时候server端收到client ack确认号之后的队列值,即全连接队列长度 +timeout 0 #客户端和Redis服务端的连接超时时间,默认是0,表示永不超时 +tcp-keepalive 300 #tcp 会话保持时间300s +daemonize no #默认no,即直接运行redis-server程序时,不作为守护进程运行,而是以前台方式运行,如果想在后台运行需改成yes,当redis作为守护进程运行的时候,它会写一个 pid 到/var/run/redis.pid 文件 +supervised no #和OS相关参数,可设置通过upstart和systemd管理Redis守护进程,centos7后都使用systemdpidfile /var/run/redis_6379.pid #pid文件路径,可以修改为/apps/redis/run/redis_6379.pid +loglevel notice #日志级别 +logfile "/path/redis.log" #日志路径,示例:logfile"/apps/redis/log/redis_6379.log" +databases 16 #设置数据库数量,默认:0-15,共16个库 +always-show-logo yes #在启动redis 时是否显示或在日志中记录记录redis的logo +``` + +- 快照配置 + +```shell +save 900 1 #在900秒内有1个key内容发生更改,就执行快照机制 +save 300 10 #在300秒内有10个key内容发生更改,就执行快照机制 +save 60 10000 #60秒内如果有10000个key以上的变化,就自动快照备份 +stop-writes-on-bgsave-error yes #默认为yes时,可能会因空间满等原因快照无法保存出错时,会禁止redis写入操作,生产建议为no #此项只针对配置文件中的自动save有效 +rdbcompression yes #持久化到RDB文件时,是否压缩,"yes"为压缩,"no"则反之 +rdbchecksum yes #是否对备份文件开启RC64校验,默认是开启 +dbfilename dump.rdb #快照文件名 +dir ./ #快照文件保存路径,示例:dir "/apps/redis/data" +``` + +- 主从复制相关配置 + +```shell +# replicaof #指定复制的master主机地址和端口,5.0版之前的指令为slaveof +# masterauth #指定复制的master主机的密码 +replica-serve-stale-data yes #当从库同主库失去连接或者复制正在进行,从机库有两种运行方式: +1、设置为yes(默认设置),从库会继续响应客户端的读请求,此为建议值 +2、设置为no,除去特定命令外的任何请求都会返回一个错误"SYNC with master in progress"。 +replica-read-only yes #是否设置从库只读,建议值为yes,否则主库同步从库时可能会覆盖数据,造成数据丢失 +repl-diskless-sync no #是否使用socket方式复制数据(无盘同步),新slave第一次连接master时需要做数据的全量同步,redis server就要从内存dump出新的RDB文件,然后从master传到slave,有两种方式把RDB文件传输给客户端: +1、基于硬盘(disk-backed):为no时,master创建一个新进程dump生成RDB磁盘文件,RDB完成之后由 +父进程(即主进程)将RDB文件发送给slaves,此为默认值 +2、基于socket(diskless):master创建一个新进程直接dump RDB至slave的网络socket,不经过主进程和硬盘 +#推荐使用基于硬盘(为no),是因为RDB文件创建后,可以同时传输给更多的slave,但是基于socket(为yes), 新slave连接到master之后得逐个同步数据。只有当磁盘I/O较慢且网络较快时,可用diskless(yes),否则一般建议使用磁盘(no) +repl-diskless-sync-delay 5 #diskless时复制的服务器等待的延迟时间,设置0为关闭,在延迟时间内到达的客户端,会一起通过diskless方式同步数据,但是一旦复制开始,master节点不会再接收新slave的复制请求,直到下一次同步开始才再接收新请求。即无法为延迟时间后到达的新副本提供服务,新副本将排队等待下一次RDB传输,因此服务器会等待一段时间才能让更多副本到达。推荐值:30-60 +repl-ping-replica-period 10 #slave根据master指定的时间进行周期性的PING master,用于监测master状态,默认10s +repl-timeout 60 #复制连接的超时时间,需要大于repl-ping-slave-period,否则会经常报超时 +repl-disable-tcp-nodelay no #是否在slave套接字发送SYNC之后禁用 TCP_NODELAY,如果选择"yes",Redis将合并多个报文为一个大的报文,从而使用更少数量的包向slaves发送数据,但是将使数据传输到slave上有延迟,Linux内核的默认配置会达到40毫秒,如果 "no" ,数据传输到slave的延迟将会减少,但要使用更多的带宽 +repl-backlog-size 512mb #复制缓冲区内存大小,当slave断开连接一段时间后,该缓冲区会累积复制副本数据,因此当slave 重新连接时,通常不需要完全重新同步,只需传递在副本中的断开连接后没有同步的部分数据即可。只有在至少有一个slave连接之后才分配此内存空间,建议建立主从时此值要调大一些或在低峰期配置,否则会导致同步到slave失败 +repl-backlog-ttl 3600 #多长时间内master没有slave连接,就清空backlog缓冲区 +replica-priority 100 #当master不可用,哨兵Sentinel会根据slave的优先级选举一个master,此值最低的slave会当选master,而配置成0,永远不会被选举,一般多个slave都设为一样的值,让其自动选择 +#min-replicas-to-write 3 #至少有3个可连接的slave,mater才接受写操作 +#min-replicas-max-lag 10 #和上面至少3个slave的ping延迟不能超过10秒,否则master也将停止写操作 +requirepass foobared #设置redis连接密码,之后需要AUTH pass,如果有特殊符号,用" "引起来,生产建议设置 +rename-command #重命名一些高危命令,示例:rename-command FLUSHALL "" 禁用命令 + #示例: rename-command del areyouok +``` + +- 客户端配置 + +```shell +maxclients 10000 #Redis最大连接客户端 +maxmemory #redis使用的最大内存,单位为bytes字节,0为不限制,建议设为物理内存一半,8G内存的计算方式8(G)*1024(MB)1024(KB)*1024(Kbyte),需要注意的是缓冲区是不计算在maxmemory内,生产中如果不设置此项,可能会导致BOOM +appendonly no #是否开启AOF日志记录,默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了,但是redis如果中途宕机,会导致可能有几分钟的数据丢失(取决于dump数据的间隔时间),根据save来策略进行持久化,Append Only File是另一种持久化方式,可以提供更好的持久化特性,Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。默认不启用此功能 +appendfilename "appendonly.aof" #文本文件AOF的文件名,存放在dir指令指定的目录中 +appendfsync everysec #aof持久化策略的配置 +#no表示由操作系统保证数据同步到磁盘,Linux的默认fsync策略是30秒,最多会丢失30s的数据 +#always表示每次写入都执行fsync,以保证数据同步到磁盘,安全性高,性能较差 +#everysec表示每秒执行一次fsync,可能会导致丢失这1s数据,此为默认值,也生产建议值 +#同时在执行bgrewriteaof操作和主进程写aof文件的操作,两者都会操作磁盘,而bgrewriteaof往往会涉及大量磁盘操作,这样就会造成主进程在写aof文件的时候出现阻塞的情形,以下参数实现控制 +no-appendfsync-on-rewrite no #在aof rewrite期间,是否对aof新记录的append暂缓使用文件同步策略,主要考虑磁盘IO开支和请求阻塞时间。 +#默认为no,表示"不暂缓",新的aof记录仍然会被立即同步到磁盘,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题 +#为yes,相当于将appendfsync设置为no,这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞(因为没有竞争磁盘),但是如果这个时候redis挂掉,就会丢失数据。丢失多少数据呢?Linux的默认fsync策略是30秒,最多会丢失30s的数据,但由于yes性能较好而且会避免出现阻塞因此比较推荐 +#rewrite 即对aof文件进行整理,将空闲空间回收,从而可以减少恢复数据时间 +auto-aof-rewrite-percentage 100 #当Aof log增长超过指定百分比例时,重写AOF文件,设置为0表示不自动重写Aof日志,重写是为了使aof体积保持最小,但是还可以确保保存最完整的数据 +auto-aof-rewrite-min-size 64mb #触发aof rewrite的最小文件大小 +aof-load-truncated yes #是否加载由于某些原因导致的末尾异常的AOF文件(主进程被kill/断电等),建议yes +aof-use-rdb-preamble no #redis4.0新增RDB-AOF混合持久化格式,在开启了这个功能之后,AOF重写产生的文件将同时包含RDB格式的内容和AOF格式的内容,其中RDB格式的内容用于记录已有的数据,而AOF格式的内容则用于记录最近发生了变化的数据,这样Redis就可以同时兼有RDB持久化和AOF持久化的优点(既能够快速地生成重写文件,也能够在出现问题时,快速地载入数据),默认为no,即不启用此功能 +lua-time-limit 5000 #lua脚本的最大执行时间,单位为毫秒 +cluster-enabled yes #是否开启集群模式,默认不开启,即单机模式 +cluster-config-file nodes-6379.conf #由node节点自动生成的集群配置文件名称 +cluster-node-timeout 15000 #集群中node节点连接超时时间,单位ms,超过此时间,会踢出集群 +cluster-replica-validity-factor 10 #单位为次,在执行故障转移的时候可能有些节点和master断开一段时间导致数据比较旧,这些节点就不适用于选举为master,超过这个时间的就不会被进行故障转移,不能当选master,计算公式:(node-timeout * replica-validity-factor) + repl-pingreplica-period +cluster-migration-barrier 1 #集群迁移屏障,一个主节点至少拥有1个正常工作的从节点,即如果主节点的slave节点故障后会将多余的从节点分配到当前主节点成为其新的从节点。 +cluster-require-full-coverage yes #集群请求槽位全部覆盖,如果一个主库宕机且没有备库就会出现集群槽位不全,那么yes时redis集群槽位验证不全,就不再对外提供服务(对key赋值时,会出现CLUSTERDOWN The cluster is down的示,cluster_state:fail,但ping 仍PONG),而no则可以继续使用,但是会出现查询数据查不到的情况(因为有数据丢失)。生产建议为no +cluster-replica-no-failover no #如果为yes,此选项阻止在主服务器发生故障时尝试对其主服务器进行故障转移。 但是,主服务器仍然可以执行手动强制故障转移,一般为no +#Slow log 是 Redis 用来记录超过指定执行时间的日志系统,执行时间不包括与客户端交谈,发送回复等I/O操作,而是实际执行命令所需的时间(在该阶段线程被阻塞并且不能同时为其它请求提供服务),由于slow log 保存在内存里面,读写速度非常快,因此可放心地使用,不必担心因为开启 slow log 而影响Redis 的速度 +slowlog-log-slower-than 10000 #以微秒为单位的慢日志记录,为负数会禁用慢日志,为0会记录每个命令操作。默认值为10ms,一般一条命令执行都在微秒级,生产建议设为1ms +slowlog-max-len 128 #最多记录多少条慢日志的保存队列长度,达到此长度后,记录新命令会将最旧的命令从命令队列中删除,以此滚动删除,即,先进先出,队列固定长度,默认128,值偏小,生产建议设为1000以上 +``` + +## 4.2 CONFIG 动态修改配置 + +- config 命令用于查看当前redis配置、以及不重启redis服务实现动态更改redis配置等 +- 注意:不是所有配置都可以动态修改,且此方式无法持久保存 + +```shell +redis 127.0.0.1:6379> CONFIG SET parameter value +时间复杂度:O(1) +CONFIG SET 命令可以动态地调整 Redis 服务器的配置(configuration)而无须重启。 +redis 127.0.0.1:6379> CONFIG GET slowlog-max-len +1) "slowlog-max-len" +2) "1024" + +redis 127.0.0.1:6379> CONFIG SET slowlog-max-len 10086 +OK + +redis 127.0.0.1:6379> CONFIG GET slowlog-max-len +1) "slowlog-max-len" +2) "10086" +``` + +### 4.2.1 设置连接密码 + +```shell +#设置连接密码 +127.0.0.1:6379> CONFIG SET requirepass centos +OK +#查看连接密码 +127.0.0.1:6379> CONFIG GET requirepass +1) "requirepass" +2) "centos" +``` + +### 4.2.2 获取当前配置 + +```shell +#奇数行为键,偶数行为值 +127.0.0.1:6379> CONFIG GET * +#查看bind +127.0.0.1:6379> CONFIG GET bind +1) "bind" +2) "0.0.0.0" +#有些设置无法修改 +127.0.0.1:6379> CONFIG SET bind 127.0.0.1 +(error) ERR Unsupported CONFIG parameter: bind +``` + +### 4.2.3 更改最大内存 + +```shell +127.0.0.1:6379> CONFIG SET maxmemory 8589934592 +OK +127.0.0.1:6379> CONFIG GET maxmemory +1) "maxmemory" +2) "8589934592" +``` + +## 4.3 慢查询 + +![img-慢查询](02.Redis/慢查询.png) + +- 可以通过config set命令动态修改参数,并使配置持久化到配置文件中 + +```shell +config set slowlog-log-slower-than 20000 +config set slowlog-max-len 1000 +config rewrite +``` + +- 获取慢查询日志,将慢查询的时间改为1微秒 + +```shell +127.0.0.1:6379> config set slowlog-log-slower-than 1 +OK +127.0.0.1:6379> config set slowlog-max-len 1000 +OK +127.0.0.1:6379> SLOWLOG len +# 获取慢查询日志列表当前的长度 +(integer) 2 +127.0.0.1:6379> SLOWLOG get +1) 1) (integer) 2 + 2) (integer) 1625544379 + 3) (integer) 1 + 4) 1) "SLOWLOG" + 2) "len" + 5) "127.0.0.1:40294" + 6) "" +2) 1) (integer) 1 + 2) (integer) 1625544372 + 3) (integer) 4 + 4) 1) "config" + 2) "set" + 3) "slowlog-max-len" + 4) "1000" + 5) "127.0.0.1:40294" + 6) "" +3) 1) (integer) 0 + 2) (integer) 1625544363 + 3) (integer) 3 + 4) 1) "config" + 2) "set" + 3) "slowlog-log-slower-than" + 4) "1" + 5) "127.0.0.1:40294" + 6) "" +``` + +- 可以看到每个慢查询日志有4个属性组成,分别是慢查询日志的标识id、发生时间戳、命令耗时、执行命令和参数 +- 慢查询日志重置 + +```shell +127.0.0.1:6379> slowlog reset +OK +127.0.0.1:6379> slowlog len +(integer) 0 +``` + +## 4.4 redis持久化 + +Redis 虽然是一个内存级别的缓存程序,也就是redis 是使用内存进行数据的缓存的,但是其可以将内存的数据按照一定的策略保存到硬盘上,从而实现数据持久保存的目的 + +目前redis支持两种不同方式的数据持久化保存机制,分别是RDB和AOF + +![img-redis持久化](02.Redis/redis持久化.png) + +### 4.4.1 RDB 模式 + +#### 4.4.1.1 RDB 模式工作原理 + +![img-RDB](02.Redis/RDB.png) + +RDB(Redis DataBase):基于时间的快照,其默认只保留当前最新的一次快照,特点是执行速度比较快,缺点是可能会丢失从上次快照到当前时间点之间未做快照的数据 + +RDB bgsave 实现快照的具体过程 + +![img-RDB工作原理01](02.Redis/RDB工作原理01.png) + +- Redis从master主进程先fork出一个子进程,使用写时复制机制,子进程将内存的数据保存为一个临时文件 +- 当数据保存完成之后再将上一次保存的RDB文件替换掉,然后关闭子进程,这样可以保证每一次做RDB快照保存的数据都是完整的 +- 因为直接替换RDB文件的时候,可能会出现突然断电等问题,而导致RDB文件还没有保存完整就因为突然关机停止保存,而导致数据丢失的情况.后续可以手动将每次生成的RDB文件进行备份,这样可以最大化保存历史数据 + +```shell +[root@localhost redis]# pstree -p |grep redis-server;ll -h /apps/redis/data/ +|-redis-server(6831)-+-{redis-server}(6849) +| |-{redis-server}(6850) +| `-{redis-server}(6851) +|-redis-server(34300)-+-{redis-server}(34301) +| |-{redis-server}(34302) +| `-{redis-server}(34303) +总用量 204K +-rw-r--r--. 1 redis redis 204K 7月 6 12:28 dumpi_6379.rdb +``` + +#### 4.4.1.2 RDB 相关配置 + +```shell +save 900 1 +save 300 10 +save 60 10000 +stop-writes-on-bgsave-error yes +rdbcompression yes +rdbchecksum yes +dbfilename dump.rdb +dir /var/lib/redis #编译安装,默认RDB文件存放在启动redis的工作目录,建议明确指定存入目录 +``` + +#### 4.4.1.3 实现RDB方式 + +- bgsave:异步后台执行,不影响其它命令的执行 +- 自动: 制定规则,自动执行 + +#### 4.4.1.4 RDB 模式优点 + +- RDB快照保存了某个时间点的数据,可以通过脚本执行redis指令bgsave(非阻塞,后台执行)或者save(会阻塞写操作,不推荐)命令自定义时间点备份,可以保留多个备份,当出现问题可以恢复到不同时间点的版本,很适合备份,并且此文件格式也支持有不少第三方工具可以进行后续的数据分析 +- 比如: 可以在最近的24小时内,每小时备份一次RDB文件,并且在每个月的每一天,也备份一个ROB文件。这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。 +- RDB可以最大化Redis的性能,父进程在保存 RDB文件时唯一要做的就是fork出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘工/0操作。 +- RDB在大量数据,比如几个G的数据,恢复的速度比AOF的快 + +#### 4.4.1.5 RDB 模式缺点 + +- 不能实时保存数据,可能会丢失自上一次执行RDB备份到当前的内存数据 +- 如果你需要尽量避免在服务器故障时丢失数据,那么RDB不适合你。虽然Redis允许你设置不同的保存点(save point)来控制保存RDB文件的频率,但是,因为RDB文件需要保存整个数据集的状态,所以它并不是一个轻松的操作。因此你可能会至少5分钟才保存一次RDB文件。在这种情况下,一旦发生故障停机,你就可能会丢失好几分钟的数据。 +- 当数据量非常大的时候,从父进程fork子进程进行保存至RDB文件时需要一点时间,可能是毫秒或者秒,取决于磁盘IO性能 +- 在数据集比较庞大时,fork()可能会非常耗时,造成服务器在一定时间内停止处理客户端﹔如果数据集非常巨大,并且CPU时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒或更久。虽然 AOF重写也需要进行fork(),但无论AOF重写的执行间隔有多长,数据的持久性都不会有任何损失。 + +![img-RDB工作原理02](02.Redis/RDB工作原理02.png) + +1. 执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进 程,如RDB/AOF子进程,如果存在bgsave命令直接返回。 +2. 父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,通 过info stats命令查看latest_fork_usec选项,可以获取最近一个fork操作的耗时,单位为微秒。 +3. 父进程fork完成后,bgsave命令返回“Background saving started”信息 并不再阻塞父进程,可以继续响应其他命令。 +4. 子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。执行lastsave命令可以获取最后一次生成RDB的 时间,对应info统计的rdb_last_save_time选项。 +5. 进程发送信号给父进程表示完成,父进程更新统计信息, + +### 4.4.2 AOF 模式 + +#### 4.4.2.1 AOF 模式工作原理 + +![img-AOF工作原理01](02.Redis/AOF工作原理01.png) + +- AOF:AppendOnylFile,按照操作顺序依次将操作追加到指定的日志文件末尾 +- AOF 和 RDB 一样使用了写时复制机制,AOF默认为每秒钟 fsync一次,即将执行的命令保存到AOF文件当中,这样即使redis服务器发生故障的话最多只丢失1秒钟之内的数据,也可以设置不同的fsync策略always,即设置每次执行命令的时候执行fsync,fsync会在后台执行线程,所以主线程可以继续处理用户的正常请求而不受到写入AOF文件的I/O影响 +- 同时启用RDB和AOF,进行恢复时,默认AOF文件优先级高于RDB文件,即会使用AOF文件进行恢复 +- 注意: AOF 模式默认是关闭的,第一次开启AOF后,并重启服务生效后,会因为AOF的优先级高于RDB,而AOF默认没有文件存在,从而导致所有数据丢失 + +#### 4.4.2.2 AOF rewrite 重写 + +将一些重复的,可以合并的,过期的数据重新写入一个新的AOF文件,从而节约AOF备份占用的硬盘空间,也能加速恢复过程 + +可以手动执行bgrewriteaof 触发AOF,或定义自动rewrite 策略 + +#### 4.4.2.3 AOF rewrite 过程 + +![img-AOF工作原理02](02.Redis/AOF工作原理02.png) + +**1.**执行AOF重写请求。 + +**2.**父进程执行fork创建子进程,开销等同于bgsave过程。 + +**3.1**主进程fork操作完成后,继续响应其他命令。所有修改命令依然写 入AOF缓冲区并根据appendfsync策略同步到硬盘,保证原有AOF机制正确 性。 + +**3.2**由于fork操作运用写时复制技术,子进程只能共享fork操作时的内 存数据。由于父进程依然响应命令,Redis使用“AOF重写缓冲区”保存这部 分新数据,防止新AOF文件生成期间丢失这部分数据。 + +**4.**子进程根据内存快照,按照命令合并规则写入到新的AOF文件。每次批量写入硬盘数据量由配置aof-rewrite-incremental-fsync控制,默认为 32MB,防止单次刷盘数据过多造成硬盘阻塞。 + +**5.1**新AOF文件写入完成后,子进程发送信号给父进程,父进程更新 统计信息,具体见info persistence下的aof_*相关统计。 + +**5.2**父进程把AOF重写缓冲区的数据写入到新的AOF文件。 + +**5.3**使用新AOF文件替换老文件,完成AOF重写。 + +```shell +# 用AOF功能的正确方式 +[root@localhost data]# ll +总用量 204 +-rw-r--r--. 1 redis redis 207886 7月 6 12:28 dumpi_6379.rdb +[root@localhost data]# redis-cli +127.0.0.1:6379> config get appendonly +1) "appendonly" +2) "no" +127.0.0.1:6379> config set appendonly yes +127.0.0.1:6379> CONFIG REWRITE +OK +[root@localhost data]# ll +总用量 408 +-rw-r--r--. 1 redis redis 207886 7月 6 13:45 appendonlyi_6379.aof +-rw-r--r--. 1 redis redis 207886 7月 6 12:28 dumpi_6379.rdb +``` + +#### 4.4.2.4 AOF相关配置 + +```shell +appendonly yes +appendfilename "appendonly-${port}.aof" +appendfsync everysec +dir /path +no-appendfsync-on-rewrite yes +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb +aof-load-truncated yes +``` + +#### 4.4.2.5 AOF 模式优点 + +- 数据安全性相对较高,根据所使用的fsync策略(fsync是同步内存中redis所有已经修改的文件到存储设备),默认是appendfsync everysec,即每秒执行一次 fsync,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据( fsync会在后台线程执行,所以主线程可以继续努力地处理命令请求) +- 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中不需要seek, 即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,可以通过 redis-check-aof 工具来解决数据一致性的问题 +- Redis可以在 AOF文件体积变得过大时,自动地在后台对AOF进行重写,重写后的新AOF文件包含了恢复当前数据集所需的最小命令集合。整个重写操作是绝对安全的,因为Redis在创建新 AOF文件的过程中,append模式不断的将修改数据追加到现有的 AOF文件里面,即使重写过程中发生停机,现有的 AOF文件也不会丢失。而一旦新AOF文件创建完毕,Redis就会从旧AOF文件切换到新AOF文件,并开始对新AOF文件进行追加操作。 +- AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,也可以通过该文件完成数据的重建。 +- AOF文件有序地保存了对数据库执行的所有写入操作,这些写入操作以Redis协议的格式保存,因此 AOF文件的内容非常容易被人读懂,对文件进行分析(parse)也很轻松。导出(export)AOF文件也非常简单:举个例子,如果你不小心执行了FLUSHALL.命令,但只要AOF文件未被重写,那么只要停止服务器,移除 AOF文件末尾的FLUSHAL命令,并重启Redis ,就可以将数据集恢复到FLUSHALL执行之前的状态。 + +#### 4.4.2.6 AOF 模式缺点 + +- 即使有些操作是重复的也会全部记录,AOF 的文件大小要大于 RDB 格式的文件 +- AOF 在恢复大数据集时的速度比 RDB 的恢复速度要慢 +- 根据fsync策略不同,AOF速度可能会慢于RDB +- bug 出现的可能性更多 + +### 4.4.3 RDB和AOF 的选择 + +如果主要充当缓存功能,或者可以承受数分钟数据的丢失, 通常生产环境一般只需启用RDB即可,此也是默认值 + +如果数据需要持久保存,一点不能丢失,可以选择同时开启RDB和AOF,一般不建议只开启AOF + +# 5. Redis 常用命令 + +参考链接:http://redisdoc.com/ + +## 5.1 INFO + +- 显示当前节点redis运行状态信息 + +```shell +127.0.0.1:6379> info +``` + +## 5.2 SELECT + +- 切换数据库,相当于在MySQL的 USE DBNAME 指令 + +```shell +127.0.0.1:6379> select 0 +OK +127.0.0.1:6379> select 15 +OK +127.0.0.1:6379[15]> select 16 +(error) ERR DB index is out of range +``` + +- 注意: 在 redis cluster 模式下不支持多个数据库,会出现下面错误 + +```shell +127.0.0.1:6379> info cluster +# Cluster +cluster_enabled:1 +127.0.0.1:6379> select 0 +OK +127.0.0.1:6379> select 1 +(error) ERR SELECT is not allowed in cluster mode +``` + +## 5.3 KEYS + +- 查看当前库下的所有key,此命令慎用! + +| 命令 | 时间复杂度 | +| :----- | :--------- | +| keys | O(n) | +| dbsize | O(1) | +| del | O(1) | +| exists | O(1) | +| expire | O(1) | +| type | O(1) | + +```shell +127.0.0.1:6379[1]> SELECT 0 +OK +127.0.0.1:6379> KEYS * #匹配数据库内所有key +1) "port1" +2) "name" +3) "port2" +4) "port" +127.0.0.1:6379> SELECT 1 +OK +127.0.0.1:6379[1]> KEYS * +(empty list or set) +127.0.0.1:6379[1]> +127.0.0.1:6379> SELECT 1 +OK +#一次设置4个key +127.0.0.1:6379[1]> MSET one 1 two 2 three 3 four 4 +OK +127.0.0.1:6379[1]> KEYS * +1) "two" +2) "one" +3) "four" +``` + +## 5.4 BGSAVE + +- 手动在后台执行RDB持久化操作 + +```shell +#交互式执行 +127.0.0.1:6379[1]> BGSAVE +Background saving started +[root@centos8 ~]#ll /apps/redis/data/ +total 664 +-rw-r--r-- 1 redis redis 204 Oct 22 21:30 dump_6379.rdb +#非交互式执行 +[root@centos8 ~]#redis-cli -a centos --no-auth-warning bgsave +Background saving started +[root@centos8 ~]#ll /apps/redis/data/ +total 664 +-rw-r--r-- 1 redis redis 204 Oct 22 21:32 dump_6379.rdb +``` + +## 5.5 DBSIZE + +- 返回当前库下的所有key 数量 + +```shell +127.0.0.1:6379> DBSIZE +(integer) 8 +127.0.0.1:6379> SELECT 1 +OK +127.0.0.1:6379[1]> DBSIZE +(integer) 4 +127.0.0.1:6379[1]> SELECT 2 +OK +127.0.0.1:6379[2]> DBSIZE +(integer) 0 +``` + +## 5.6 FLUSHDB + +- 强制清空当前库中的所有key,此命令慎用! + +```shell +127.0.0.1:6379[2]> SELECT 1 +OK +127.0.0.1:6379[1]> DBSIZE +(integer) 4 +127.0.0.1:6379[1]> FLUSHDB +OK +127.0.0.1:6379[1]> DBSIZE +(integer) 0 +``` + +## 5.7 FLUSHALL + +- 强制清空当前redis服务器所有数据库中的所有key,即删除所有数据,此命令慎用! + +```shell +127.0.0.1:6379> FLUSHALL +OK +#生产建议修改配置 /etc/redis.conf,禁用或改为别名 +rename-command FLUSHALL "" +``` + +## 5.8 SHUTDOWN + +- SHUTDOWN 命令执行以下操作: + - 停止所有客户端 + - 如果有至少一个保存点在等待,执行 SAVE 命令 + - 如果 AOF 选项被打开,更新 AOF 文件 + - 关闭 redis 服务器(server) +- 如果持久化被打开的话, SHUTDOWN 命令会保证服务器正常关闭而不丢失任何数据。 +- 另一方面,假如只是单纯地执行 SAVE 命令,然后再执行 QUIT 命令,则没有这一保证因为在执行SAVE 之后、执行 QUIT 之前的这段时间中间,其他客户端可能正在和服务器进行通讯,这时如果执行QUIT 就会造成数据丢失。 + +# 6. redis 数据类型 + +![img-redis数据类型01](02.Redis/redis数据类型01.png) + +![img-redis数据类型02](02.Redis/redis数据类型02.png) + +## 6.1 字符串 string + +字符串是所有编程语言中最常见的和最常用的数据类型,而且也是redis最基本的数据类型之一,而且redis 中所有的 key 的类型都是字符串。常用于保存 Session 信息场景,此数据类型比较常用 + +- 添加一个key + - set 指令可以创建一个key 并赋值, 使用格式 + +```shell +127.0.0.1:6379> set key1 value1 +OK +127.0.0.1:6379> set title ceo ex 5 #设置自动过期时间为5秒 +OK +127.0.0.1:6379> get title +"ceo" +127.0.0.1:6379> get title +(nil) +127.0.0.1:6379> set NAME eagle #大小写敏感 +OK +127.0.0.1:6379> get name +(nil) +#key不存在才设置,相当于add +127.0.0.1:6379> set name eagle +OK +127.0.0.1:6379> get name +"eagle" +127.0.0.1:6379> set name xwz nx +(nil) +127.0.0.1:6379> get name +"eagle" +#key存在才设置,相当于update +127.0.0.1:6379> get name +"eagle" +127.0.0.1:6379> set name xwz xx +OK +127.0.0.1:6379> get name +"xwz" +127.0.0.1:6379> get age +(nil) +127.0.0.1:6379> set age 18 xx +(nil) +127.0.0.1:6379> get age +(nil) +``` + +- 获取一个key的内容 + +```shell +127.0.0.1:6379> get name +"xwz" +127.0.0.1:6379> get name age +(error) ERR wrong number of arguments for 'get' command +``` + +- 删除一个和多个key + +```shell +127.0.0.1:6379> del name +(integer) 1 +127.0.0.1:6379> get name +(nil) +127.0.0.1:6379> del age NAME +(integer) 2 +``` + +- 批量设置多个key + +```shell +127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 +OK +``` + +- 批量获取多个key + +```shell +127.0.0.1:6379> mget k1 k2 +1) "v1" +2) "v2" +127.0.0.1:6379> keys * +1) "k2" +2) "k3" +3) "k1" +127.0.0.1:6379> keys k* +1) "k2" +2) "k3" +3) "k1" +``` + +- 追加数据 + +```shell +127.0.0.1:6379> append k2 " append new value" +(integer) 19 +127.0.0.1:6379> get k2 +"v2 append new value" +``` + +- 设置新值并返回旧值 + +```shell +127.0.0.1:6379> set name eagle +OK +127.0.0.1:6379> getset name xwz +"eagle" +127.0.0.1:6379> get name +"xwz" +``` + +- 返回字符串 key 对应值的字节数 + +```shell +127.0.0.1:6379> set name eagle +OK +127.0.0.1:6379> strlen name #返回字节数 +(integer) 5 +127.0.0.1:6379> append name " xwz" +(integer) 9 +127.0.0.1:6379> get name +"eagle xwz" +127.0.0.1:6379> strlen name +(integer) 9 +127.0.0.1:6379> set name 好好学习 +OK +127.0.0.1:6379> strlen name +(integer) 12 +``` + +- 判断 key 是否存在 + +```shell +127.0.0.1:6379> set name eagle ex 5 +OK +127.0.0.1:6379> exists name +(integer) 1 +127.0.0.1:6379> exists name +(integer) 0 +``` + +- 查看 key 的过期时间 + - ttl key #查看key的剩余生存时间,如果key过期后,会自动删除 + - -1 #返回值表示永不过期,默认创建的key是永不过期,重新对key赋值,也会从有剩余生命周期变成永不过期 + - -2 #返回值表示没有此key + - num #key的剩余有效期 + +```shell +127.0.0.1:6379> ttl age +(integer) -2 +127.0.0.1:6379> ttl name +(integer) -2 +127.0.0.1:6379> set name eagle ex 20 +OK +127.0.0.1:6379> ttl name +(integer) 18 +127.0.0.1:6379> ttl k1 +(integer) -1 +``` + +- 重新设置key的过期时间 + +```shell +127.0.0.1:6379> set name eagle ex 20 +OK +127.0.0.1:6379> expire name 30 +(integer) 1 +127.0.0.1:6379> ttl name +(integer) 29 +``` + +- 取消key的过期时间 + +```shell +127.0.0.1:6379> ttl name +(integer) 5 +127.0.0.1:6379> persist name +(integer) 1 +127.0.0.1:6379> ttl name +(integer) -1 +``` + +- 数值递增 + - 利用INCR命令簇(INCR, DECR, [INCRBY],DECRBY)来把字符串当作原子计数器使用 + +```shell +127.0.0.1:6379> set num 21 +OK +127.0.0.1:6379> incr num +(integer) 22 +127.0.0.1:6379> get num +"22" +``` + +- 数值递减 + +```shell +127.0.0.1:6379> set num 21 +OK +127.0.0.1:6379> decr num +(integer) 20 +127.0.0.1:6379> get num +"20" +``` + +- 数值增加 + +将key对应的数字加decrement(可以是负数)。如果key不存在,操作之前,key就会被置为0。如果key的value类型错误或者是个不能表示成数字的字符串,就返回错误。这个操作最多支持64位有符号的正型数字。 + +```shell +127.0.0.1:6379> set num 30 +OK +127.0.0.1:6379> incrby num 20 +(integer) 50 +127.0.0.1:6379> get num +"50" +127.0.0.1:6379> incrby num -20 +(integer) 30 +127.0.0.1:6379> get num +"30" +127.0.0.1:6379> get num1 +(nil) +127.0.0.1:6379> incrby num1 5 +(integer) 5 +127.0.0.1:6379> get num1 +"5" +``` + +## 6.2 列表 list + +![img-redis数据类型-列表](02.Redis/redis数据类型-列表.png) + +列表是一个双向可读写的管道,其头部是左侧,尾部是右侧,一个列表最多可以包含2^32-1(4294967295)个元素,下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 + +也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,元素值可以重复,常用于存入日志等场景,此数据类型比较常用 + +- 列表特点: + - 有序 + - 可重复 + - 左右都可以操作 +- 生成列表并插入数据 + +- LPUSH和RPUSH都可以插入列表 + +```plain +127.0.0.1:6379> lpush name eagle cs xwz tj +(integer) 4 +127.0.0.1:6379> type name +list +127.0.0.1:6379> rpush course linux python go +(integer) 3 +127.0.0.1:6379> type course +list +``` + +- 向列表追加数据 + +```shell +127.0.0.1:6379> lpush name abc +(integer) 5 +127.0.0.1:6379> rpush name tom +(integer) 6 +``` + +- 获取列表长度(元素个数) + +```shell +127.0.0.1:6379> llen name +(integer) 6 +``` + +- 获取列表指定位置数据 + +```shell +127.0.0.1:6379> lpush list1 a b c d +(integer) 4 +127.0.0.1:6379> lindex list1 0 #获取0编号的元素 +"d" +127.0.0.1:6379> lindex list1 3 #获取3编号的元素 +"a" +127.0.0.1:6379> lindex list1 -1 #获取最后一个的元素 +"a" +127.0.0.1:6379> lrange list1 1 2 +1) "c" +2) "b" +127.0.0.1:6379> lrange list1 0 3 #所有元素 +1) "d" +2) "c" +3) "b" +4) "a" +127.0.0.1:6379> lrange list1 0 -1 #所有元素 +1) "d" +2) "c" +3) "b" +4) "a" +``` + +- 修改列表指定索引值 + +```shell +127.0.0.1:6379> lrange list1 0 -1 +1) "d" +2) "c" +3) "b" +4) "a" +127.0.0.1:6379> rpush listkey a b c d e f +(integer) 6 +127.0.0.1:6379> lrange listkey 0 -1 +1) "a" +2) "b" +3) "c" +4) "d" +5) "e" +6) "f" +127.0.0.1:6379> lset listkey 2 redis +OK +127.0.0.1:6379> lrange listkey 0 -1 +1) "a" +2) "b" +3) "redis" +4) "d" +5) "e" +6) "f" +``` + +- 移除列表数据 + +```shell +127.0.0.1:6379> lpush list1 a b c d +(integer) 4 +127.0.0.1:6379> lrange list1 0 3 +1) "d" +2) "c" +3) "b" +4) "a" +127.0.0.1:6379> lpop list1 #弹出左边第一个元素,即删除第一个 +"d" +127.0.0.1:6379> rpop list1 #弹出右边第一个元素,即删除最后一个 +"a" +127.0.0.1:6379> llen list1 +(integer) 2 +127.0.0.1:6379> lrange list1 0 -1 +1) "c" +2) "b" +* LTRIM 对一个列表进行修剪(trim),让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除 +127.0.0.1:6379> lrange list1 0 3 +1) "d" +2) "c" +3) "b" +4) "a" +127.0.0.1:6379> ltrim list1 1 2 +OK +127.0.0.1:6379> lrange list1 0 -1 +1) "c" +2) "b" +``` + +- 删除list + +```sql +127.0.0.1:6379> del list1 +(integer) 1 +127.0.0.1:6379> exists list1 +(integer) 0 +``` + +## 6.3 集合 set + +Set 是 String 类型的无序集合,集合中的成员是唯一的,这就意味着集合中不能出现重复的数据,可以在两个不同的集合中对数据进行对比并取值,常用于取值判断,统计,交集等场景 + +- 集合特点 + - 无序 + - 无重复 + - 集合间操作 +- 生成集合key + +```shell +127.0.0.1:6379> sadd set1 v1 +(integer) 1 +127.0.0.1:6379> sadd set2 v2 v3 +(integer) 2 +127.0.0.1:6379> type set1 +set +127.0.0.1:6379> type set2 +set +``` + +- 追加数值 + - 追加时,只能追加不存在的数据,不能追加已经存在的数据 + +```shell +127.0.0.1:6379> sadd set1 v1 #已存在的值,无法再次添加 +(integer) 0 +127.0.0.1:6379> sadd set1 v3 v4 v5 +(integer) 3 +``` + +- 查看集合的所有数据 + +```shell +127.0.0.1:6379> smembers set1 +1) "v3" +2) "v5" +3) "v4" +4) "v1" +127.0.0.1:6379> smembers set2 +1) "v3" +2) "v2" +``` + +- 删除集合中的元素 + +```shell +127.0.0.1:6379> sadd goods mobile car laptop +(integer) 3 +127.0.0.1:6379> srem goods car +(integer) 1 +127.0.0.1:6379> smembers goods +1) "mobile" +2) "laptop" +``` + +- 获取集合的交集 + - 交集:已属于A且属于B的元素称为A与B的交集 + +```shell +127.0.0.1:6379> smembers set1 +1) "v3" +2) "v5" +3) "v4" +4) "v1" +127.0.0.1:6379> smembers set2 +1) "v3" +2) "v2" +127.0.0.1:6379> sinter set1 set2 +1) "v3" +``` + +- 获取集合的并集 + - 并集:已属于A或属于B的元素称为A与B的并集 + +```shell +127.0.0.1:6379> sunion set1 set2 +1) "v1" +2) "v4" +3) "v5" +4) "v3" +5) "v2" +``` + +- 获取集合的差集 + - 差集:已属于A而不属于B的元素称为A与B的差(集) + +```shell +127.0.0.1:6379> sdiff set1 set2 +1) "v1" +2) "v4" +3) "v5" +``` + +## 6.4 有序集合 sorted set + +Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员,不同的是每个元素都会关联一个double(双精度浮点型)类型的分数,redis正是通过该分数来为集合中的成员进行从小到大的排序,有序集合的成员是唯一的,但分数(score)却可以重复,集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1), 集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员),经常用于排行榜的场景 + +- 有序集合特点 + - 有序 + - 无重复元素 + - 每个元素是由score和value组成 + - score 可以重复 + - value 不可以重复 +- 生成有序集合 + +```shell +127.0.0.1:6379> zadd zset1 1 v1 #分数为1 +(integer) 1 +127.0.0.1:6379> zadd zset1 2 v2 +(integer) 1 +127.0.0.1:6379> zadd zset1 2 v3 #分数可重复,元素值不可以重复 +(integer) 1 +127.0.0.1:6379> zadd zset1 3 v4 +(integer) 1 +127.0.0.1:6379> type zset1 +zset +127.0.0.1:6379> zadd zset2 1 v1 2 v2 3 v3 4 v4 5 v5 #一次生成多个数据 +(integer) 5 +``` + +- 有序集合实现排行榜 + +```shell +127.0.0.1:6379> zadd phb 90 xls 95 tj 30 sg +(integer) 3 +127.0.0.1:6379> zrange phb 0 -1 +#正序排序后显示集合内所有的key,score从小到大显示 +1) "sg" +2) "xls" +3) "tj" +127.0.0.1:6379> zrevrange phb 0 -1 +#倒叙排序后显示集合内所有的key,score从大到小显示 +1) "tj" +2) "xls" +3) "sg" +127.0.0.1:6379> zrevrange phb 0 -1 withscores +#正序显示指定集合内所有key和得分情况 +1) "tj" +2) "95" +3) "xls" +4) "90" +5) "sg" +6) "30" +``` + +- 获取集合的个数 + +```shell +127.0.0.1:6379> zcard phb +(integer) 3 +``` + +- 基于索引返回数值 + +```shell +127.0.0.1:6379> zrange phb 0 2 +1) "sg" +2) "xls" +3) "tj" +127.0.0.1:6379> zrange phb 0 -1 +1) "sg" +2) "xls" +3) "tj" +``` + +- 返回某个数值的索引(排名) + +```shell +127.0.0.1:6379> zadd phb 90 xls 95 tj 30 sg +(integer) 3 +127.0.0.1:6379> zrange phb 0 -1 +1) "sg" +2) "xls" +3) "tj" +127.0.0.1:6379> zrank phb sg +(integer) 0 +127.0.0.1:6379> zrank phb tj +(integer) 2 +``` + +- 获取分数 + +```shell +127.0.0.1:6379> zscore phb tj +"95" +127.0.0.1:6379> zscore phb sg +"30" +``` + +- 删除元素 + +```shell +127.0.0.1:6379> zrem phb sg tj +(integer) 2 +127.0.0.1:6379> zrange phb 0 -1 +1) "xls" +``` + +## 6.5 哈希 hash + +hash 是一个string类型的字段(field)和值(value)的映射表,Redis 中每个 hash 可以存储 2^32 -1 键值对,类似于字典,存放了多个k/v 对,hash特别适合用于存储对象场景 + +- 生成 hash key + +```shell +127.0.0.1:6379> hset 9527 name sg age 30 +(integer) 2 +127.0.0.1:6379> type 9527 +hash +127.0.0.1:6379> hgetall 9527 +1) "name" +2) "sg" +3) "age" +4) "30" +#增加字段 +127.0.0.1:6379> hset 9527 gender man +(integer) 1 +127.0.0.1:6379> hgetall 9527 +1) "name" +2) "sg" +3) "age" +4) "30" +5) "gender" +6) "man" +``` + +- 获取hash key的对应字段的值 + +```shell +127.0.0.1:6379> hget 9527 name +"sg" +127.0.0.1:6379> hget 9527 gender +"man" +127.0.0.1:6379> hmget 9527 age gender #获取多个值 +1) "30" +2) "man" +``` + +- 删除一个hash key 的对应字段 + +```shell +127.0.0.1:6379> hdel 9527 age +(integer) 1 +127.0.0.1:6379> hget 9527 age +(nil) +``` + +- 批量设置hash key的多个field和value + +```shell +127.0.0.1:6379> hmset 1024 name xwz age 18 city changzhou +OK +127.0.0.1:6379> hgetall 1024 +1) "name" +2) "xwz" +3) "age" +4) "18" +5) "city" +6) "changzhou" +``` + +- 获取hash中指定字段的值 + +```shell +127.0.0.1:6379> hmget 1024 name city +1) "xwz" +2) "changzhou" +``` + +- 获取hash中的所有字段名field + +```shell +127.0.0.1:6379> hkeys 1024 +1) "name" +2) "age" +3) "city" +``` + +- 获取hash key对应所有field的value + +```shell +127.0.0.1:6379> hvals 1024 +1) "xwz" +2) "18" +3) "changzhou" +``` + +- 获取指定hash key 的所有field及value + +```shell +127.0.0.1:6379> hgetall 1024 +1) "name" +2) "xwz" +3) "age" +4) "18" +5) "city" +6) "changzhou" +``` + +- 删除 hash + +```shell +127.0.0.1:6379> del 1024 +(integer) 1 +127.0.0.1:6379> hmget 1024 name age +1) (nil) +2) (nil) +127.0.0.1:6379> exists 1024 +(integer) 0 +``` + +# 7. 消息队列 + +- 消息队列: 把要传输的数据放在队列中 +- 功能: 可以实现多个系统之间的解耦,异步,削峰/限流等 +- 常用的消息队列应用: kafka,rabbitMQ,redis + +![img-redis消息队列](02.Redis/redis消息队列.png) + + +消息队列主要分为两种,这两种模式Redis都支持 + +- 生产者/消费者模式 +- 发布者/订阅者模式 + +## 7.1 生产者消费者模式 + +在生产者/消费者(Producer/Consumer)模式下,上层应用接收到的外部请求后开始处理其当前步骤的操作,在执行完成后将已经完成的操作发送至指定的频道(channel,逻辑队列)当中,并由其下层的应用监听该频道并继续下一步的操作,如果其处理完成后没有下一步的操作就直接返回数据给外部请求,如果还有下一步的操作就再将任务发布到另外一个频道,由另外一个消费者继续监听和处理。此模式应用广泛 + +### 7.1.1 模式介绍 + +生产者消费者模式下,多个消费者同时监听一个队列,但是一个消息只能被最先抢到消息的消费者消费,即消息任务是一次性读取和处理,此模式在分布式业务架构中很常用,比较常用的消息队列软件还有RabbitMQ、Kafka、RocketMQ、ActiveMQ等。 + +![img-redis消息队列-模式介绍](02.Redis/redis消息队列-模式介绍.png) + +### 7.1.2 队列介绍 + +队列当中的消息由不同的生产者写入,也会有不同的消费者取出进行消费处理,但是一个消息一定是只能被取出一次也就是被消费一次。 + +![img-redis消息队列-队列介绍](02.Redis/redis消息队列-队列介绍.png) + +- 生产者发布消息 + +```shell +[root@localhost ~]# redis-cli +127.0.0.1:6379> auth centos +OK +127.0.0.1:6379> lpush channel1 msg1 +(integer) 1 +127.0.0.1:6379> lpush channel1 msg2 +(integer) 2 +127.0.0.1:6379> lpush channel1 msg3 +(integer) 3 +127.0.0.1:6379> lpush channel1 msg4 +(integer) 4 +127.0.0.1:6379> lpush channel1 msg5 +(integer) 5 +``` + +- 查看队列所有消息 + +```shell +127.0.0.1:6379> lrange channel1 0 -1 +1) "msg5" +2) "msg4" +3) "msg3" +4) "msg2" +5) "msg1" +``` + +- 消费者消费消息 + +```shell +127.0.0.1:6379> rpop channel1 +"msg1" +127.0.0.1:6379> rpop channel1 +"msg2" +127.0.0.1:6379> rpop channel1 +"msg3" +127.0.0.1:6379> rpop channel1 +"msg4" +127.0.0.1:6379> rpop channel1 +"msg5" +127.0.0.1:6379> rpop channel1 +(nil) +``` + +## 7.2 发布者订阅模式 + +### 7.2.1 模式简介 + +在发布者订阅者模式下,发布者将消息发布到指定的channel里面,凡是监听该channel的消费者都会收到同样的一份消息,这种模式类似于是收音机的广播模式,即凡是收听某个频道的听众都会收到主持人发布的相同的消息内容。此模式常用于群聊天、群通知、群公告等场景 + +- Publisher:发布者 +- Subscriber:订阅者 +- Channel:频道 + +![img-redis消息队列-发布订阅](02.Redis/redis消息队列-发布订阅.png) + +- 订阅者监听频道 + +```shell +127.0.0.1:6379> subscribe channel1 +Reading messages... (press Ctrl-C to quit) +1) "subscribe" +2) "channel1" +3) (integer) 1 +``` + +- 发布者发布消息 + +```shell +127.0.0.1:6379> publish channel1 test1 +(integer) 1 #订阅者个数 +127.0.0.1:6379> publish channel1 test2 +(integer) 1 +``` + +- 各个订阅者都能收到消息 + +```shell +127.0.0.1:6379> subscribe channel1 +Reading messages... (press Ctrl-C to quit) +1) "subscribe" +2) "channel1" +3) (integer) 1 +1) "message" +2) "channel1" +3) "test1" +1) "message" +2) "channel1" +3) "test2" +``` + +- 订阅多个频道 + +```shell +127.0.0.1:6379> subscribe channel1 channel2 +``` + +- 订阅所有频道 + +```shell +127.0.0.1:6379> psubscribe * #支持通配符* +``` + +- 订阅匹配的频道 + +```shell +127.0.0.1:6379> PSUBSCRIBE chann* #匹配订阅多个频道 +``` + +- 取消订阅 + +```shell +127.0.0.1:6379> unsubscribe channel1 +1) "unsubscribe" +2) "channel1" +3) (integer) 0 +``` + +# 8. redis 主从复制 + +虽然Redis可以实现单机的数据持久化,但无论是RDB也好或者AOF也好,都解决不了单点宕机问题,即一旦单台 redis服务器本身出现系统故障、硬件故障等问题后,就会直接造成数据的丢失 + +此外,单机的性能也是有极限的,因此需要使用另外的技术来解决单点故障和性能扩展的问题。 + +![img-redis主从复制](02.Redis/redis主从复制.png) + +## 8.1 redis 主从复制架构 + +主从模式(master/slave),可以实现Redis数据的跨主机备份。 + +程序端连接到高可用负载的VIP,然后连接到负载服务器设置的Redis后端real server,此模式不需要在程序里面配置Redis服务器的真实IP地址,当后期Redis服务器IP地址发生变更只需要更改redis 相应的后端real server即可, 可避免更改程序中的IP地址设置。 + +- 一个master可以有多个slave +- 一个slave只能有一个master +- 数据流向是单向的,master到slave + +## 8.2 主从复制实现 + +Redis Slave 也要开启持久化并设置和master同样的连接密码,因为后期slave会有提升为master的可能,Slave 端切换master同步后会丢失之前的所有数据,而通过持久化可以恢复数据 + +一旦某个Slave成为一个master的slave,Redis Slave服务会清空当前redis服务器上的所有数据并将master的数据导入到自己的内存,但是如果只是断开同步关系后,则不会删除当前已经同步过的数据。 + +当配置Redis复制功能时,强烈建议打开主服务器的持久化功能。否则的话,由于延迟等问题,部署的服务应该要避免自动启动。 + +1. 假设节点A为主服务器,并且关闭了持久化。并且节点B和节点c从节点A复制数据 +2. 节点A崩溃,然后由自动拉起服务重启了节点A.由于节点A的持久化被关闭了,所以重启之后没有任何数据 +3. 节点B和节点c将从节点A复制数据,但是A的数据是空的,于是就把自身保存的数据副本删除。 + +在关闭主服务器上的持久化,并同时开启自动拉起进程的情况下,即便使用Sentinel来实现Redis的高可用性,也是非常危险的。因为主服务器可能拉起得非常快,以至于Sentinel在配置的心跳时间间隔内没有检测到主服务器已被重启,然后还是会执行上面的数据丢失的流程。无论何时,数据安全都是极其重要的,所以应该禁止主服务器关闭持久化的同时自动启动。 + +### 8.2.1 命令行配置 + +- 启用主从同步 + - 默认redis 状态为master,需要转换为slave角色并指向master服务器的IP+PORT+Password + - REPLICAOF MASTER_IP PORT 指令可以启用主从同步复制功能,早期版本使用 SLAVEOF 指令 +- 在master上设置key1 + +```shell +[root@master ~]# redis-cli -a centos +Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. +127.0.0.1:6379> info replication +# Replication +role:master +connected_slaves:0 +master_replid:75166c08ad870d6eaa5c27e003bc2b36488a3cae +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:0 +second_repl_offset:-1 +repl_backlog_active:0 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:0 +repl_backlog_histlen:0 +127.0.0.1:6379> set key1 v1-master +OK +127.0.0.1:6379> keys * +1) "key1" +127.0.0.1:6379> get key1 +"v1-master" +``` + +- 以下都在slave上执行,登录,设置key1 + +```shell +[root@slave1 ~]# redis-cli -a centos --no-auth-warning +127.0.0.1:6379> info replication +# Replication +role:master +connected_slaves:0 +master_replid:1a274a8475450314d9ebb232717fa6cc3720e62f +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:0 +second_repl_offset:-1 +repl_backlog_active:0 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:0 +repl_backlog_histlen:0 +127.0.0.1:6379> set key1 v1-slave1 +OK +127.0.0.1:6379> keys * +1) "key1" +127.0.0.1:6379> get key1 +"v1-slave1" +``` + +- 在第二个slave2上,也设置相同的key1,但值不同 + +```shell +[root@slave2 ~]# redis-cli -a centos --no-auth-warning +127.0.0.1:6379> info replication +# Replication +role:master +connected_slaves:0 +master_replid:2b8d846c9f01eea950100d75caf1e8e3600a8d4a +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:0 +second_repl_offset:-1 +repl_backlog_active:0 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:0 +repl_backlog_histlen:0 +127.0.0.1:6379> set key1 v1-slave2 +OK +127.0.0.1:6379> keys * +1) "key1" +127.0.0.1:6379> get key1 +"v1-slave2" +``` + +- 在所有的slave上设置master的IP和端口,4.0版本之前的指令为slaveof + - 在slave1上 + +```shell +127.0.0.1:6379> replicaof 192.168.175.10 6379 +# 仍可使用SLAVEOF MasterIP Port +OK +127.0.0.1:6379> config set masterauth centos +#在slave上设置master的密码,才可以同步 +OK +127.0.0.1:6379> info replication +# Replication +role:slave #角色变为slave +master_host:192.168.175.10 #指向master +master_port:6379 +master_link_status:down +master_last_io_seconds_ago:-1 +master_sync_in_progress:0 +slave_repl_offset:0 +master_link_down_since_seconds:1625794777 +slave_priority:100 +slave_read_only:1 +connected_slaves:0 +master_replid:1a274a8475450314d9ebb232717fa6cc3720e62f +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:0 +second_repl_offset:-1 +repl_backlog_active:0 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:0 +repl_backlog_histlen:0 +127.0.0.1:6379> get key1 +# 查看数据是否同步成功 +"v1-master" +* 在slave2上 +127.0.0.1:6379> replicaof 192.168.175.10 6379 +OK +127.0.0.1:6379> config set masterauth centos +OK +127.0.0.1:6379> info replication +# Replication +role:slave +master_host:192.168.175.10 +master_port:6379 +master_link_status:down +master_last_io_seconds_ago:-1 +master_sync_in_progress:0 +slave_repl_offset:0 +master_link_down_since_seconds:1625794971 +slave_priority:100 +slave_read_only:1 +connected_slaves:0 +master_replid:2b8d846c9f01eea950100d75caf1e8e3600a8d4a +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:0 +second_repl_offset:-1 +repl_backlog_active:0 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:0 +repl_backlog_histlen:0 +127.0.0.1:6379> get key1 +"v1-master" +``` + +- 在master上可以看到所有slave的信息 + +```shell +127.0.0.1:6379> info replication +# Replication +role:master +connected_slaves:2 +slave0:ip=192.168.175.30,port=6379,state=online,offset=224,lag=1 +slave1:ip=192.168.175.20,port=6379,state=online,offset=224,lag=0 +master_replid:d6f1142f22e456f4e4093fcb81648cab127ebc7f +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:224 +second_repl_offset:-1 +repl_backlog_active:1 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:1 +repl_backlog_histlen:224 +``` + +- 删除主从同步 + - REPLICAOF NO ONE 指令可以取消主从复制 + +```shell +127.0.0.1:6379> replicaof no one +OK +127.0.0.1:6379> info replication +# Replication +role:master #角色变回了master +connected_slaves:0 +master_replid:9507932f3addde3a76b51c5d6ecbb5daac398caa +master_replid2:f68659a5c6e8381dbbdd6841f5b89d1a60157af8 +master_repl_offset:1064 +second_repl_offset:1065 +repl_backlog_active:1 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:1 +repl_backlog_histlen:1064 +``` + +- 在master上看到slave数量减少 + +```shell +127.0.0.1:6379> info replication +# Replication +role:master +connected_slaves:1 +``` + +### 8.2.2 同步日志 + +- 在 master 上观察日志 + +```shell +[root@master ~]# tail /apps/redis/log/redis_6379.log +6815:M 09 Jul 2021 09:44:03.398 * Synchronization with replica 192.168.175.20:6379 succeeded +6815:M 09 Jul 2021 09:53:26.312 # Connection with replica 192.168.175.20:6379 lost. +6815:M 09 Jul 2021 09:54:04.298 * Replica 192.168.175.20:6379 asks for synchronization +6815:M 09 Jul 2021 09:54:04.298 * Partial resynchronization not accepted: Replication ID mismatch (Replica asked for '2feb7cc9c49ff2a52f4749a0cd26218d9ca6577a', my replication IDs are 'd6f1142f22e456f4e4093fcb81648cab127ebc7f' and '0000000000000000000000000000000000000000') +6815:M 09 Jul 2021 09:54:04.298 * Starting BGSAVE for SYNC with target: disk +6815:M 09 Jul 2021 09:54:04.299 * Background saving started by pid 7234 +7234:C 09 Jul 2021 09:54:04.300 * DB saved on disk +7234:C 09 Jul 2021 09:54:04.300 * RDB: 0 MB of memory used by copy-on-write +6815:M 09 Jul 2021 09:54:04.394 * Background saving terminated with success +6815:M 09 Jul 2021 09:54:04.395 * Synchronization with replica 192.168.175.20:6379 succeeded +``` + +- 在 slave 节点观察日志 + +```shell +[root@slave2 ~]# tail /apps/redis/log/redis_6379.log +6838:S 09 Jul 2021 09:44:02.914 * MASTER <-> REPLICA sync started +6838:S 09 Jul 2021 09:44:02.914 * Non blocking connect for SYNC fired the event. +6838:S 09 Jul 2021 09:44:02.915 * Master replied to PING, replication can continue... +6838:S 09 Jul 2021 09:44:02.916 * Trying a partial resynchronization (request 2b8d846c9f01eea950100d75caf1e8e3600a8d4a:1). +6838:S 09 Jul 2021 09:44:02.918 * Full resync from master: d6f1142f22e456f4e4093fcb81648cab127ebc7f:0 +6838:S 09 Jul 2021 09:44:02.918 * Discarding previously cached master state. +6838:S 09 Jul 2021 09:44:02.992 * MASTER <-> REPLICA sync: receiving 196 bytes from master +6838:S 09 Jul 2021 09:44:02.993 * MASTER <-> REPLICA sync: Flushing old data +6838:S 09 Jul 2021 09:44:02.993 * MASTER <-> REPLICA sync: Loading DB in memory +6838:S 09 Jul 2021 09:44:02.993 * MASTER <-> REPLICA sync: Finished with success +``` + +### 8.2.3 修改slave节点配置文件 + +- 在slave1上 + +```shell +[root@slave1 ~]# grep replicaof /apps/redis/etc/redis.conf +# Master-Replica replication. Use replicaof to make a Redis instance a copy of +# replicaof +[root@slave1 ~]# echo "replicaof 192.168.175.10 6379" >> /apps/redis/etc/redis.conf +[root@slave1 ~]# echo "masterauth centos" >> /apps/redis/etc/redis.conf +[root@slave1 ~]# systemctl restart redis +``` + +- 在slave2上 + +```shell +[root@slave2 ~]# echo "replicaof 192.168.175.10 6379" >> /apps/redis/etc/redis.conf +[root@slave2 ~]# echo "masterauth centos" >> /apps/redis/etc/redis.conf +[root@slave2 ~]# systemctl restart redis +``` + +- master和slave查看状态 + +```shell +# 在master上 +127.0.0.1:6379> info replication +# Replication +role:master +connected_slaves:2 +slave0:ip=192.168.175.20,port=6379,state=online,offset=4452,lag=1 +slave1:ip=192.168.175.30,port=6379,state=online,offset=4452,lag=1 +# 在slave1上 +127.0.0.1:6379> info replication +# Replication +role:slave +master_host:192.168.175.10 +master_port:6379 +127.0.0.1:6379> get key1 +"v1-master" +#在slave2上 +127.0.0.1:6379> info replication +# Replication +role:slave +master_host:192.168.175.10 +master_port:6379 +127.0.0.1:6379> get key1 +"v1-master" +``` + +- 停止master的redis服务:systemctl stop redis,在slave上可以看到以下现象 + +```shell +# 在master上停止服务 +[root@master ~]# systemctl stop redis +#在slave上可以看到 +127.0.0.1:6379> info replication +# Replication +role:slave +master_host:192.168.175.10 +master_port:6379 +master_link_status:down #显示down,表示无法连接master +``` + +- slave 观察日志 + +```shell +[root@slave1 ~]# tail /apps/redis/log/redis_6379.log +15506:S 09 Jul 2021 10:41:56.295 # Error condition on socket for SYNC: Connection refused +15506:S 09 Jul 2021 10:41:57.311 * Connecting to MASTER 192.168.175.10:6379 +15506:S 09 Jul 2021 10:41:57.311 * MASTER <-> REPLICA sync started +15506:S 09 Jul 2021 10:41:57.311 # Error condition on socket for SYNC: Connection refused +15506:S 09 Jul 2021 10:41:58.333 * Connecting to MASTER 192.168.175.10:6379 +15506:S 09 Jul 2021 10:41:58.333 * MASTER <-> REPLICA sync started +15506:S 09 Jul 2021 10:41:58.334 # Error condition on socket for SYNC: Connection refused +15506:S 09 Jul 2021 10:41:59.359 * Connecting to MASTER 192.168.175.10:6379 +15506:S 09 Jul 2021 10:41:59.359 * MASTER <-> REPLICA sync started +15506:S 09 Jul 2021 10:41:59.360 # Error condition on socket for SYNC: Connection refused +``` + +- slave 状态只读无法写入数据 + +```shell +127.0.0.1:6379> set key1 v1-slave1 +(error) READONLY You can't write against a read only replica. +``` + +## 8.3 主从复制故障恢复 + +### 8.3.1 主从复制故障恢复过程介绍 + +- slave 节点故障和恢复 + - client指向另一个从节点即可,并及时修复故障从节点 + +![img-redis主从复制-故障恢复01](02.Redis/redis主从复制-故障恢复01.png) + +- master 节点故障和恢复 + - 需要提升slave为新的master + +![img-redis主从复制-故障恢复02](02.Redis/redis主从复制-故障恢复02.png) + +![img-redis主从复制-故障恢复03](02.Redis/redis主从复制-故障恢复03.png) + +- master故障后,只能手动提升一个slave为新master,不支持自动切换。master的切换会导致master_replid发生变化,slave之前的master_replid就和当前master不一致从而会引发所有slave的全量同步 + +### 8.3.2 主从复制故障恢复实现 + +- 假设当前主节点192.168.175.10故障,提升192.168.175.20为新的master + +```shell +[root@master ~]# systemctl stop redis +``` + +- 查看slave1 + +```shell +[root@slave1 ~]# redis-cli -a centos --no-auth-warning +127.0.0.1:6379> info replication +# Replication +role:slave +master_host:192.168.175.10 +master_port:6379 +master_link_status:down +``` + +- 停止slave1(192.168.175.20)同步并提升为新的master + +```shell +127.0.0.1:6379> replicaof no one +OK +127.0.0.1:6379> info replication +# Replication +role:master +connected_slaves:0 +master_replid:46804bd951a74f28dbd453d4cab68ca6fbd2e6bc +master_replid2:d6f1142f22e456f4e4093fcb81648cab127ebc7f +master_repl_offset:4648 +second_repl_offset:4649 +repl_backlog_active:1 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:827 +repl_backlog_histlen:3822 +127.0.0.1:6379> set keytest1 vtest1 +OK +``` + +- 修改所有的slave指向新的master节点 + +```shell +127.0.0.1:6379> replicaof 192.168.175.20 6379 +OK +127.0.0.1:6379> config set masterauth centos +OK +127.0.0.1:6379> set key100 v100 +(error) READONLY You can't write against a read only replica. +127.0.0.1:6379> info replication +# Replication +role:slave +master_host:192.168.175.20 +master_port:6379 +master_link_status:up +127.0.0.1:6379> get keytest1 +"vtest1" +``` + +- 在新master(192.168.175.20)上可看到slave + +```shell +127.0.0.1:6379> info replication +# Replication +role:master +connected_slaves:1 +slave0:ip=192.168.175.30,port=6379,state=online,offset=4836,lag=1 +``` + +## 8.4 实现redis的级联复制 + +![img-redis主从复制-联级](02.Redis/redis主从复制-联级.png) + +在前面搭建好的一主一从架构中,master和slave1节点无需修改,只需要修改slave2及slave3指向slave1作为master即可 + +```shell +[root@slave2 ~]# redis-cli -a centos +Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. +127.0.0.1:6379> REPLICAOF 192.168.175.20 6379 +OK +127.0.0.1:6379> config set masterauth centos +OK +127.0.0.1:6379> info replication +# Replication +role:slave +master_host:192.168.175.20 +master_port:6379 +[root@slave3 ~]# redis-cli -a centos +Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. +127.0.0.1:6379> replicaof 192.168.175.20 6379 +OK +127.0.0.1:6379> config set masterauth centos +OK +127.0.0.1:6379> info replication +# Replication +role:slave +master_host:192.168.175.20 +master_port:6379 +``` + +- 在master上设置key,观察是否同步 + +```shell +# master设置key +127.0.0.1:6379> set name eagle +OK +127.0.0.1:6379> get name +"eagle" +#在slave上验证key +127.0.0.1:6379> get name +"eagle" +``` + +- 在中间那个slave(即级联salve1 192.168.175.20)上查看状态 + +```shell +127.0.0.1:6379> info replication +# Replication +role:slave +master_host:192.168.175.10 +master_port:6379 +master_link_status:up +master_last_io_seconds_ago:9 #最近一次与master通信已经过去多少秒 +master_sync_in_progress:0 #是否正在与master通信 +slave_repl_offset:799 #当前同步的偏移量 +slave_priority:100 #slave优先级,master故障后优先级值越小越优先同步 +slave_read_only:1 +connected_slaves:2 +slave0:ip=192.168.175.30,port=6379,state=online,offset=799,lag=0 +slave1:ip=192.168.175.40,port=6379,state=online,offset=799,lag=0 +master_replid:e262f6e6fdd1d02222afc48ade1860163e3c096b +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:799 +second_repl_offset:-1 +repl_backlog_active:1 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:1 +repl_backlog_histlen:799 +``` + +## 8.5 主从复制优化 + +### 8.5.1 主从复制过程 + +Redis主从复制分为全量同步和增量同步 + +- 全量复制过程 + +![img-redis主从复制-全量](02.Redis/redis主从复制-全量.png) + +首次同步是全量同步,主从同步可以让从服务器从主服务器同步数据,而且从服务器还可再有其它的从服务器,即另外一台redis服务器可以从一台从服务器进行数据同步,redis 的主从同步是非阻塞的,master收到从服务器的psync(2.8版本之前是SYNC)命令,会fork一个子进程在后台执行bgsave命令,并将新写入的数据写入到一个缓冲区中,bgsave执行完成之后,将生成的RDB文件发送给slave,然后master再将缓冲区的内容以redis协议格式再全部发送给slave,slave 先删除旧数据,slave将收到后的RDB文件载入自己的内存,再加载所有收到缓冲区的内容 从而这样一次完整的数据同步 + +Redis全量复制一般发生在Slave首次初始化阶段,这时Slave需要将Master上的所有数据都复制一份。 + +- 增量复制过程 + +![img-redis主从复制-增量](02.Redis/redis主从复制-增量.png) + +全量同步之后再次需要同步时,从服务器只要发送当前的offset位置(等同于MySQL的binlog的位置)给主服务器,然后主服务器根据相应的位置将之后的数据(包括写在缓冲区的积压数据)发送给从服务器,其再次保存到其内存即可。 + +- 主从同步完整过程 + 1. 从服务器连接主服务器,发送PSYNC命令 + 2. 主服务器接收到PSYNC命令后,开始执行BGSAVE命令生成RDB快照文件并使用缓冲区记录此后执行的所有写命令 + 3. 主服务器BGSAVE执行完后,向所有从服务器发送RDB快照文件,并在发送期间继续记录被执行的写命令 + 4. 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照至内存 + 5. 主服务器快照发送完毕后,开始向从服务器发送缓冲区中的写命令 + 6. 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令 + 7. 后期同步会先发送自己slave_repl_offset位置,只同步新增加的数据,不再全量同步 + +![img-redis主从复制-过程01](02.Redis/redis主从复制-过程01.png) + +![img-redis主从复制-过程02](02.Redis/redis主从复制-过程02.png) + +![img-redis主从复制-过程03](02.Redis/redis主从复制-过程03.png) + +![img-redis主从复制-过程04](02.Redis/redis主从复制-过程04.png) + +- 复制缓冲区(环形队列)配置参数 + +```shell +#复制缓冲区大小,建议要设置足够大 +rep-backlog-size 1mb +#Redis同时也提供了当没有slave需要同步的时候,多久可以释放环形队列: +repl-backlog-ttl 3600 +``` + +- 避免全量复制 + - 第一次全量复制不可避免,后续的全量复制可以利用小主节点(内存小),业务低峰时进行全量 + - 节点运行 run-id 不匹配:主节点重启会导致RUNID变化,可能会触发全量复制,可以利用故障转移,例如哨兵或集群,而从节点重新启动,不会导致全量复制 + - 复制积压缓冲区不足: 当主节点生成的新数据大于缓冲区大小,从节点恢复和主节点连接后,会导致全量复制.解决方法将repl-backlog-size 调大 +- 避免复制风暴 + - 单节点复制风暴 + - 当主节点重启,多从节点复制 + - 解决方法:更换复制拓扑 + +![img-redis主从复制-单节点复制风暴](02.Redis/redis主从复制-单节点复制风暴.png) + + - 单机器复制风暴 + - 机器宕机后,大量全量复制 + - 解决方法:主节点分散多机器 + +![img-redis主从复制-单机器复制风暴](02.Redis/redis主从复制-单机器复制风暴.png) + +### 8.5.2 主从同步优化配置 + +Redis在2.8版本之前没有提供增量部分复制的功能,当网络闪断或者slave Redis重启之后会导致主从之间的全量同步,即从2.8版本开始增加了部分复制的功能。 + +- 性能相关配置 + +```shell +repl-diskless-sync no # 是否使用无盘同步RDB文件,默认为no,no为不使用无盘,需要将RDB文件保存到磁盘后再发送给slave,yes为支持无盘,支持无盘就是RDB文件不需要保存至本地磁盘,而且直接通过socket文件发送给slave +repl-diskless-sync-delay 5 #diskless时复制的服务器等待的延迟时间 +repl-ping-slave-period 10 #slave端向server端发送ping的时间间隔,默认为10秒 +repl-timeout 60 #设置主从ping连接超时时间,超过此值无法连接,master_link_status显示为down,并记录错误日志 +repl-disable-tcp-nodelay no #是否启用TCP_NODELAY,如设置成yes,则redis会合并小的TCP包从而节省带宽, 但会增加同步延迟(40ms),造成master与slave数据不一致,假如设置成no,则redismaster会立即发送同步数据,没有延迟,yes关注性能,no关注redis服务中的数据一致性 +repl-backlog-size 1mb #master的写入数据缓冲区,用于记录自上一次同步后到下一次同步过程中间的写入命令,计算公式:repl-backlog-size = 允许从节点最大中断时长 * 主实例offset每秒写入量,比如master每秒最大写入64mb,最大允许60秒,那么就要设置为64mb*60秒=3840MB(3.8G),建议此值是设置的足够大 +repl-backlog-ttl 3600 #如果一段时间后没有slave连接到master,则backlog size的内存将会被释放。如果值为0则表示永远不释放这部份内存。 +slave-priority 100 #slave端的优先级设置,值是一个整数,数字越小表示优先级越高。当master故障时将会按照优先级来选择slave端进行恢复,如果值设置为0,则表示该slave永远不会被选择。 +min-replicas-to-write 1 #设置一个master的可用slave不能少于多少个,否则master无法执行写 +min-slaves-max-lag 20 #设置至少有上面数量的slave延迟时间都大于多少秒时,master不接收写操作(拒绝写入) +``` + +## 8.6 常见主从复制故障汇总 + +### 8.6.1 master密码不对 + +即配置的master密码不对,导致验证不通过而无法建立主从同步关系。 + +```shell +[root@slave1 ~]#tail -f /var/log/redis/redis.log +3939:S 24 Oct 2020 16:13:57.552 # Server initialized +3939:S 24 Oct 2020 16:13:57.552 * DB loaded from disk: 0.000 seconds +3939:S 24 Oct 2020 16:13:57.552 * Ready to accept connections +3939:S 24 Oct 2020 16:13:57.554 * Connecting to MASTER 0.0.18:6379 +3939:S 24 Oct 2020 16:13:57.554 * MASTER <-> REPLICA sync started +3939:S 24 Oct 2020 16:13:57.556 * Non blocking connect for SYNC fired the event. +3939:S 24 Oct 2020 16:13:57.558 * Master replied to PING, replication can continue... +3939:S 24 Oct 2020 16:13:57.559 # Unable to AUTH to MASTER: -ERR invalid password +``` + +### 8.6.2 无法远程连接 + +在开启了安全模式情况下,没有设置bind地址或者密码 + +```shell +[root@slave1 ~]#redis-cli -h 192.168.175.10 +192.168.175.10:6379> KEYS * +(error) DENIED Redis is running in protected mode because protected mode is enabled +``` + +### 8.6.3 配置不一致 + +主从节点的maxmemory不一致,主节点内存大于从节点内存,主从复制可能丢失数据 + +rename-command 命令不一致,如在主节点定义了fushall,flushdb,从节点没定义,结果执行flushdb,不同步 + +# 9. redis 哨兵(Sentinel) + +## 9.1 redis 集群介绍 + +主从架构无法实现master和slave角色的自动切换,即当master出现redis服务异常、主机断电、磁盘损坏等问题导致master无法使用,而redis主从复制无法实现自动的故障转移(将slave 自动提升为新master),需要手动修改环境配置,才能切换到slave redis服务器,另外也无法横向扩展Redis服务的并行写入性能,当单台Redis服务器性能无法满足业务写入需求的时候,也需要解决以上的两个核心问题 + +1. master和slave角色的无缝切换,让业务无感知从而不影响业务使用 +2. 可横向动态扩展Redis服务器,从而实现多台服务器并行写入以实现更高并发的目的。 + +Redis 集群实现方式 + +- 客户端分片: 由应用决定将不同的KEY发送到不同的Redis服务器 +- 代理分片: 由代理决定将不同的KEY发送到不同的Redis服务器,代理程序如:codis,twemproxy等 +- Redis Cluster + +## 9.2 哨兵 (Sentinel) 工作原理 + +### 9.2.1 sentinel 架构和故障转移 + +![img-redis哨兵模式01](02.Redis/redis哨兵模式01.png) + +![img-redis哨兵模式02](02.Redis/redis哨兵模式02.png) + +Sentinel 进程是用于监控redis集群中Master主服务器工作的状态,在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换,保证系统的高可用,此功能在redis2.6+的版本已引用,Redis的哨兵模式到了2.8版本之后就稳定了下来。一般在生产环境也建议使用Redis的2.8版本的以后版本 + +哨兵(Sentinel) 是一个分布式系统,可以在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议(gossip protocols)来接收关于Master主服务器是否下线的信息,并使用投票协议(Agreement Protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master + +每个哨兵(Sentinel)进程会向其它哨兵(Sentinel)、Master、Slave定时发送消息,以确认对方是否”活”着,如果发现对方在指定配置时间(此项可配置)内未得到回应,则暂时认为对方已离线,也就是所谓的”主观认为宕机” (主观:是每个成员都具有的独自的而且可能相同也可能不同的意识),英文名称:Subjective Down,简称SDOWN + +有主观宕机,对应的有客观宕机。当“哨兵群”中的多数Sentinel进程在对Master主服务器做出SDOWN的判断,并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后,得出的Master Server下线判断,这种方式就是“客观宕机”(客观:是不依赖于某种意识而已经实际存在的一切事物),英文名称是:Objectively Down, 简称 ODOWN + +通过一定的vote算法,从剩下的slave从服务器节点中,选一台提升为Master服务器节点,然后自动修改相关配置,并开启故障转移(failover) + +Sentinel 机制可以解决master和slave角色的自动切换问题,但单个 Master 的性能瓶颈问题无法解决,类似于MySQL中的MHA功能 + +Redis Sentinel中的Sentinel节点个数应该为大于等于3且最好为奇数 + +客户端初始化时连接的是Sentinel节点集合,不再是具体的Redis节点,但Sentinel只是配置中心不是代理。 + +Redis Sentinel 节点与普通redis 没有区别,要实现读写分离依赖于客户端程序 + +redis 3.0 之前版本中,生产环境一般使用哨兵模式,但3.0后推出redis cluster功能后,可以支持更大规模的生产环境 + +### 9.2.2 sentinel中的三个定时任务 + +- 每10秒每个sentinel对master和slave执行info + - 发现slave节点 + - 确认主从关系 +- 每2秒每个sentinel通过master节点的channel交换信息(pub/sub) + - 通过sentinel_hello频道交互 + - 交互对节点的“看法”和自身信息 +- 每1秒每个sentinel对其他sentinel和redis执行ping + +## 9.3 实现哨兵 + +![img-redis哨兵实现](02.Redis/redis哨兵实现.png) + +- 哨兵的准备实现主从复制架构 + - 哨兵的前提是已经实现了一个redis的主从复制的运行环境,从而实现一个一主两从基于哨兵的高可用redis架构 + - 注意: master 的配置文件中masterauth 和slave 都必须相同 + +```shell +# 在所有主从节点执行,这里以master为例 +[root@master ~]# vim /apps/redis/etc/redis.conf +bind 0.0.0.0 +masterauth "centos" +requirepass centos +[root@master ~]# echo -e "net.core.somaxconn = 1024\nvm.overcommit_memory = 1" >> /etc/sysctl.conf + +net.core.somaxconn: 这个参数控制系统同时监听的最大连接数。将其设置为 1024 可以提高系统的并发能力。 +vm.overcommit_memory: 这个参数控制内存overcommit策略。设置为 1 表示允许overcommit,即允许进程申请比实际可用内存更多的内存。这在一些内存紧张的场景下会很有用。 + +[root@master ~]# sysctl -p +net.core.somaxconn = 1024 +vm.overcommit_memory = 1 +net.core.somaxconn = 1024 +vm.overcommit_memory = 1 +[root@master ~]# echo never > /sys/kernel/mm/transparent_hugepage/enabled +这条命令是用来禁用 Transparent Huge Pages (THP) 功能。 +THP 是一种 Linux 内核特性,可以动态合并内存页面以减少页表的数量,从而提高性能。但是在某些场景下它可能会带来副作用,所以需要禁用。 +[root@master ~]# echo "echo never > /sys/kernel/mm/transparent_hugepage/enabled" >> /etc/rc.d/rc.local +[root@master ~]# chmod +x /etc/rc.d/rc.local +[root@master ~]# systemctl restart redis +``` + +- 设置主从复制 + +```shell +#在所有从节点执行,这里以slave1为例 +[root@slave1 ~]# echo "replicaof 192.168.175.10 6379" >> /apps/redis/etc/redis.conf +[root@slave1 ~]# systemctl restart redis +``` + +- master服务器状态 + +```shell +[root@master ~]# redis-cli +127.0.0.1:6379> auth centos +OK +127.0.0.1:6379> info replication +# Replication +role:master +connected_slaves:2 +slave0:ip=192.168.175.20,port=6379,state=online,offset=112,lag=0 +slave1:ip=192.168.175.30,port=6379,state=online,offset=112,lag=0 +master_replid:ec21203b0f3034282e18d6532055379b3de45f82 +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:112 +second_repl_offset:-1 +repl_backlog_active:1 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:1 +repl_backlog_histlen:112 +``` + +- 编辑哨兵的配置文件 + - Sentinel实际上是一个特殊的redis服务器,有些redis指令支持,但很多指令并不支持.默认监听在26379/tcp端口 + - 哨兵可以不和Redis服务器部署在一起,但一般部署在一起,所有redis节点使用相同的配置文件 + +```shell +# 如果是编译安装,在源码目录有sentinel.conf,复制到安装目录即可 +[root@master ~]# cp redis-5.0.9/sentinel.conf /apps/redis/etc/ +[root@master ~]# grep -Ev "^(#|$)" /apps/redis/etc/sentinel.conf +port 26379 +daemonize no +pidfile /var/run/redis-sentinel.pid +logfile "" +dir /tmp #工作目录 +sentinel monitor mymaster 127.0.0.1 6379 2 +#指定当前mymaster集群中master服务器的地址和端口 +#2为法定人数限制(quorum),即有几个sentinel认为master down了就进行故障转移,一般此值是所有sentinel节点(一般总数是>=3的 奇数,如:3,5,7等)的一半以上的整数值,比如,总数是3,即3/2=1.5,取整为2,是master的ODOWN客观下线的依据 +sentinel auth-pass +#mymaster集群中master的密码,注意此行要在上面行的下面 +sentinel down-after-milliseconds mymaster 30000 +#(SDOWN)判断mymaster集群中所有节点的主观下线的时间,单位:毫秒,建议3000 +sentinel parallel-syncs mymaster 1 +#发生故障转移后,同时向新master同步数据的slave数量,数字越小总同步时间越长,但可以减轻新master的负载压力 +sentinel failover-timeout mymaster 180000 +#所有slaves指向新的master所需的超时时间,单位:毫秒 +sentinel deny-scripts-reconfig yes +#禁止修改脚本 +``` + +- 三个哨兵服务器的配置都如下,以master为例 + +```shell +[root@master ~]# grep -Ev "^(#|$)" /apps/redis/etc/sentinel.conf +port 26379 +daemonize no +pidfile /apps/redis/run/redis-sentinel.pid +logfile "/apps/redis/log/sentinel.log" +dir /tmp +sentinel monitor mymaster 192.168.88.136 6379 2 +sentinel auth-pass mymaster centos +sentinel down-after-milliseconds mymaster 3000 +sentinel parallel-syncs mymaster 1 +sentinel failover-timeout mymaster 180000 +sentinel deny-scripts-reconfig yes +[root@master ~]# chown redis.redis /apps/redis/etc/sentinel.conf +``` + +### 9.3.1 启动哨兵 + +三台哨兵服务器都要启动 + +```shell +# 添加哨兵服务 +[root@master ~]# cat << EOF > /lib/systemd/system/redis-sentinel.service +[Unit] +Description=Redis Sentinel +After=network.target +After=network-online.target +Wants=network-online.target +[Service] +ExecStart=/apps/redis/bin/redis-sentinel /apps/redis/etc/sentinel.conf --supervised systemd +ExecStop=/usr/libexec/redis-shutdown redis-sentinel +Type=notify +User=redis +Group=redis +RuntimeDirectory=redis +RuntimeDirectoryMode=0755 +[Install] +WantedBy=multi-user.target +EOF +[root@master ~]# systemctl daemon-reload +# 确保每个哨兵主机myid不同 +#在master上 +[root@master ~]# systemctl enable --now redis-sentinel +[root@master ~]# grep myid /apps/redis/etc/sentinel.conf +sentinel myid 4e119d69b1b0bee2a660bcb3162b6116907672e3 +# 在slave1上 +[root@slave1 ~]# systemctl enable --now redis-sentinel +[root@slave1 ~]# grep myid /apps/redis/etc/sentinel.conf +sentinel myid 946a5a7cb7ab39f46043e2db4f2f41ca1a04ec45 +#在slave2上 +[root@slave2 ~]# systemctl enable --now redis-sentinel +[root@slave2 ~]# grep myid /apps/redis/etc/sentinel.conf +sentinel myid 8d40b041b85bd197b33a34358169ee7b08de0ecc +# 以下内容在服务启动后自动生成,不需要修改 +[root@master ~]# grep mymaster /apps/redis/etc/sentinel.conf +# sentinel auth-pass mymaster MySUPER--secret-0123passw0rd +sentinel monitor mymaster 192.168.175.10 6379 2 +sentinel down-after-milliseconds mymaster 3000 +sentinel auth-pass mymaster centos +# sentinel notification-script mymaster /var/redis/notify.sh +# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh +sentinel config-epoch mymaster 0 +# instead of the normal ones. For example if the master "mymaster", and the +# SENTINEL rename-command mymaster CONFIG GUESSME +# SENTINEL rename-command mymaster CONFIG CONFIG +sentinel leader-epoch mymaster 0 +sentinel known-replica mymaster 192.168.175.30 6379 +sentinel known-replica mymaster 192.168.175.20 6379 +sentinel known-sentinel mymaster 192.168.175.30 26379 8d40b041b85bd197b33a34358169ee7b08de0ecc +sentinel known-sentinel mymaster 192.168.175.20 26379 946a5a7cb7ab39f46043e2db4f2f41ca1a04ec45 +``` + +### 9.3.2 验证哨兵端口 + +```shell +[root@master ~]# ss -ntl +State Recv-Q Send-Q Local Address:Port Peer Address:Port +LISTEN 0 511 *:26379 *:* +``` + +### 9.3.3 查看哨兵日志 + +```shell +[root@master ~]# tail /apps/redis/log/sentinel.log +18077:X 10 Jul 2021 11:23:34.486 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'. +18077:X 10 Jul 2021 11:23:34.487 * Running mode=sentinel, port=26379. +18077:X 10 Jul 2021 11:23:34.487 # Sentinel ID is 4e119d69b1b0bee2a660bcb3162b6116907672e3 +18077:X 10 Jul 2021 11:23:34.487 # +monitor master mymaster 192.168.175.10 6379 quorum 2 +18077:X 10 Jul 2021 11:23:37.553 # +sdown slave 192.168.175.30:6379 192.168.175.30 6379 @ mymaster 192.168.175.10 6379 +18077:X 10 Jul 2021 11:23:37.553 # +sdown slave 192.168.175.20:6379 192.168.175.20 6379 @ mymaster 192.168.175.10 6379 +18077:X 10 Jul 2021 11:33:18.954 * +sentinel sentinel 946a5a7cb7ab39f46043e2db4f2f41ca1a04ec45 192.168.175.20 26379 @ mymaster 192.168.175.10 6379 +18077:X 10 Jul 2021 11:33:22.007 # +sdown sentinel 946a5a7cb7ab39f46043e2db4f2f41ca1a04ec45 192.168.175.20 26379 @ mymaster 192.168.175.10 6379 +18077:X 10 Jul 2021 11:33:48.674 * +sentinel sentinel 8d40b041b85bd197b33a34358169ee7b08de0ecc 192.168.175.30 26379 @ mymaster 192.168.175.10 6379 +18077:X 10 Jul 2021 11:33:51.763 # +sdown sentinel 8d40b041b85bd197b33a34358169ee7b08de0ecc 192.168.175.30 26379 @ mymaster 192.168.175.10 6379 +``` + +### 9.3.4 当前sentinel状态 + +在sentinel状态中尤其是最后一行,涉及到masterIP是多少,有几个slave,有几个sentinels,必须是符合全部服务器数量 + +```shell +[root@master ~]# redis-cli -p 26379 +127.0.0.1:26379> info sentinel +# Sentinel +sentinel_masters:1 +sentinel_tilt:0 +sentinel_running_scripts:0 +sentinel_scripts_queue_length:0 +sentinel_simulate_failure_flags:0 +master0:name=mymaster,status=ok,address=192.168.175.10:6379,slaves=2,sentinels=3 +``` + +### 9.3.5 停止Redis Master测试故障转移 + +```shell +[root@master ~]# killall redis-server +``` + +- 查看各节点上哨兵信息 + +```shell +[root@slave1 ~]# redis-cli -a centos -p 26379 +127.0.0.1:26379> INFO sentinel +# Sentinel +sentinel_masters:1 +sentinel_tilt:0 +sentinel_running_scripts:0 +sentinel_scripts_queue_length:0 +sentinel_simulate_failure_flags:0 +master0:name=mymaster,status=ok,address=192.168.175.30:6379,slaves=2,sentinels=3 +``` + +- 故障转移后的redis配置文件会被自动修改 + +```shell +[root@slave1 ~]# grep "^replicaof" /apps/redis/etc/redis.conf +replicaof 192.168.175.30 6379 +``` + +- 哨兵配置文件的sentinel monitor IP 同样也会被修改 + +```shell +[root@slave1 ~]# grep monitor /apps/redis/etc/sentinel.conf +sentinel monitor mymaster 192.168.175.30 6379 2 +``` + +- 新的master 状态 + +```shell +[root@slave2 ~]# redis-cli -a centos --no-auth-warning +127.0.0.1:6379> info replication +# Replication +role:master +connected_slaves:2 +slave0:ip=192.168.175.20,port=6379,state=online,offset=289741,lag=1 +slave1:ip=192.168.175.10,port=6379,state=online,offset=289741,lag=1 +master_replid:de66459a2ecc3f0652fcefc6be633855ccda5c1e +master_replid2:ec21203b0f3034282e18d6532055379b3de45f82 +master_repl_offset:289741 +second_repl_offset:214083 +repl_backlog_active:1 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:43 +repl_backlog_histlen:289699 +``` + +### 9.3.6 恢复故障的原master重新加入redis集群 + +```shell +[root@master ~]# systemctl start redis +[root@master ~]#grep "^replicaof" /apps/redis/etc/redis.conf +replicaof 192.168.175.30 6379 +[root@master ~]# redis-cli -a centos --no-auth-warning +127.0.0.1:6379> info replication +# Replication +role:slave +master_host:192.168.175.30 +master_port:6379 +master_link_status:up +master_last_io_seconds_ago:2 +master_sync_in_progress:0 +slave_repl_offset:306155 +slave_priority:100 +slave_read_only:1 +connected_slaves:0 +master_replid:de66459a2ecc3f0652fcefc6be633855ccda5c1e +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:306155 +second_repl_offset:-1 +repl_backlog_active:1 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:261652 +repl_backlog_histlen:44504 +``` + +### 9.3.7 sentinel 运维 + +- 手动让主节点下线 + +```shell +# 指定优先级,值越小sentinel会优先将之选为新的master,默为值为100 +[root@master ~]# vim /apps/redis/etc/redis.conf +replica-priority 10 +[root@master ~]# systemctl restart redis + +[root@master ~]# redis-cli -a centos -p 26379 +127.0.0.1:26379> sentinel failover mymaster +OK +127.0.0.1:26379> info sentinel +# Sentinel +sentinel_masters:1 +sentinel_tilt:0 +sentinel_running_scripts:0 +sentinel_scripts_queue_length:0 +sentinel_simulate_failure_flags:0 +master0:name=mymaster,status=ok,address=192.168.175.10:6379,slaves=2,sentinels=3 +``` + +### 9.3.8 python连接redis + +- 客户端链接sentinel工作原理:选举出一个sentinel +- 由这个sentinel 通过masterName 获取master节点信息 +- sentinel 发送 role 指令确认 mater 的信息 +- 客户端订阅 sentinel 的相关频道,获取新的 master 信息变化,并自动连接新的 master + +#### 9.3.8.1 python 连接Sentinel哨兵 + +```shell +[root@master ~]# yum -y install python3 python3-redis +[root@master ~]# vim sentinel_test.py +#!/usr/bin/python3 +import redis +from redis.sentinel import Sentinel +#连接哨兵服务器(主机名也可以用域名) +sentinel = Sentinel([('192.168.175.10', 26379), + ('192.168.175.20', 26379), + ('192.168.175.30', 26379)], + socket_timeout=0.5) +redis_auth_pass = 'centos' +#mymaster 是配置哨兵模式的redis集群名称,此为默认值,实际名称按照个人部署案例来填写 +#获取主服务器地址 +master = sentinel.discover_master('mymaster') +print(master) +#获取从服务器地址 +slave = sentinel.discover_slaves('mymaster') +print(slave) +#获取主服务器进行写入 +master = sentinel.master_for('mymaster', socket_timeout=0.5, +password=redis_auth_pass, db=0) +w_ret = master.set('name', 'eagle') +#输出:True +#获取从服务器进行读取(默认是round-roubin) +slave = sentinel.slave_for('mymaster', socket_timeout=0.5, +password=redis_auth_pass, db=0) +r_ret = slave.get('name') +print(r_ret) +#输出:eagle +[root@master ~]# python3 sentinel_test.py +('192.168.175.10', 6379) +[('192.168.175.20', 6379), ('192.168.175.30', 6379)] +b'eagle' +``` + +# 10. Redis Cluster + +## 10.1 Redis Cluster 工作原理 + +在哨兵sentinel机制中,可以解决redis高可用问题,即当master故障后可以自动将slave提升为master,从而可以保证redis服务的正常使用,但是无法解决redis单机写入的瓶颈问题,即单机redis写入性能受限于单机的内存大小、并发数量、网卡速率等因素。 + +- 早期Redis 分布式集群部署方案 + - 客户端分区:由客户端程序决定key写分配和写入的redis node,但是需要客户端自己处理写入分配、高可用管理和故障转移等 + - 代理方案:基于三方软件实现redis proxy,客户端先连接至代理层,由代理层实现key的写入分配,对客户端来说是有比较简单,但是对于集群管理节点增减相对比较麻烦,而且代理本身也是单点和性能瓶颈。 + +redis 3.0版本之后推出了无中心架构的redis cluster机制,在无中心的redis集群当中,其每个节点保存当前节点数据和整个集群状态,每个节点都和其他所有节点连接 + +- Redis Cluster特点如下 + - 所有Redis节点使用(PING机制)互联 + - 集群中某个节点的是否失效,是由整个集群中超过半数的节点监测都失效,才能算真正的失效 + - 客户端不需要proxy即可直接连接redis,应用程序中需要配置有全部的redis服务器IP + - redis cluster把所有的redis node 平均映射到 0-16383个槽位(slot)上,读写需要到指定的redis node上进行操作,因此有多少个redis node相当于redis 并发扩展了多少倍,每个redis node 承担16384/N个槽位 + - Redis cluster预先分配16384个(slot)槽位,当需要在redis集群中写入一个key -value的时候,会使用CRC16(key) mod 16384之后的值,决定将key写入值哪一个槽位从而决定写入哪一个Redis节点上,从而有效解决单机瓶颈。 + +![img-redis集群模式](02.Redis/redis集群模式.png) + +### 10.1.1 Redis cluster 基本架构 + +假如三个主节点分别是:A,B,C三个节点,采用哈希槽(hash slot)的方式来分配16384个slot的活,它们三个节点分别承担的slot 区间可以是: + +- 节点A覆盖:0-5460 +- 节点B覆盖:5461-10922 +- 节点C覆盖:10923-16383 + +![img-redis集群模式slot](02.Redis/redis集群模式slot.png) + +### 10.1.2 Redis cluster 主从架构 + +Redis cluster的架构虽然解决了并发的问题,但是又引入了一个新的问题,每个Redis master的高可用如何解决? + +那就是对每个master 节点都实现主从复制,从而实现 redis 高可用性 + +![img-redis集群模式架构01](02.Redis/redis集群模式架构01.png) + +### 10.1.3 Redis Cluster 部署架构说明 + +- 环境A:3台服务器,每台服务器启动6379和6380两个redis 服务实例,适用于测试环境 + +![img-redis集群模式架构02](02.Redis/redis集群模式架构02.png) + +- 环境B:6台服务器,分别是三组master/slave,适用于生产环境 + +![img-redis集群模式架构03](02.Redis/redis集群模式架构03.png) + +### 10.1.4 部署方式介绍 + +- redis cluster 有多种部署方法 +- 原生命令安装 + - 理解Redis Cluster架构 + - 生产环境不使用 +- 官方工具安装 + - 高效、准确 + - 生产环境可以使用 +- 自主研发 + - 可以实现可视化的自动化部署 + +## 10.2 原生命令手动部署 + +- 在所有节点安装redis,并配置开启cluster功能 +- 各个节点执行meet,实现所有节点的相互通信 +- 为各个master 节点指派槽位范围 +- 指定各个节点的主从关系 + +### 10.2.1 原生命令 + +- 集群 + - cluster info :打印集群的信息 + - cluster nodes :列出集群当前已知的所有节点( node),以及这些节点的相关信息。 +- 节点 + - cluster meet :将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。 + - cluster forget :从集群中移除 node_id 指定的节点。 + - cluster replicate :将当前从节点设置为 node_id 指定的master节点的slave节点。只能针对slave节点操作。 + - cluster saveconfig :将节点的配置文件保存到硬盘里面。 +- 槽(slot) + - cluster addslots [slot ...] :将一个或多个槽( slot)指派( assign)给当前节点。 + - cluster delslots [slot ...] :移除一个或多个槽对当前节点的指派。 + - cluster flushslots :移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。 + - cluster setslot node :将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给 + - 另一个节点,那么先让另一个节点删除该槽>,然后再进行指派。 + - cluster setslot migrating :将本节点的槽 slot 迁移到 node_id 指定的节点中。 + - cluster setslot importing :从 node_id 指定的节点中导入槽 slot 到本节点。 + - cluster setslot stable :取消对槽 slot 的导入( import)或者迁移( migrate)。 +- 键 + - cluster keyslot :计算键 key 应该被放置在哪个槽上。 + - cluster countkeysinslot :返回槽 slot 目前包含的键值对数量。 + - cluster getkeysinslot :返回 count 个 slot 槽中的键 。 + +### 10.2.2 实战案例: 利用原生命令手动部署redis cluster + +- 准备6台虚拟机 + +![img-redis集群模式-实验环境](02.Redis/redis集群模式-实验环境.png) + +- 在所有节点安装redis并启动cluster功能 + +```shell +#在克隆之前将如下操作在模板上操作完成 +[root@localhost ~]# vim redis_install.sh +#参照一键编译安装redis脚本 +[root@localhost ~]# . redis_install.sh +# 开始编译安装redis +``` + +- 将克隆过的主机的IP地址配置完成,防火墙放行对应的端口号 + +```shell +firewall-cmd --add-port=1-65535/tcp --permanent +firewall-cmd --add-port=1-65535/udp --permanent +firewall-cmd --reload +hostnamectl set-hostname master1 +``` + +- 启用redis集群支持 + +```shell +[root@master1 ~]# vim /apps/redis/etc/redis.conf +cluster-enabled yes +[root@master1 ~]# systemctl restart redis +``` + +- 执行 meet 操作实现相互通信 + +```shell +# 在任意一节点上和其它所有节点进行meet通信,以master1为例 +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning cluster meet 192.168.175.11 6379 +OK +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning cluster meet 192.168.175.20 6379 +OK +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning cluster meet 192.168.175.21 6379 +OK +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning cluster meet 192.168.175.30 6379 +OK +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning cluster meet 192.168.175.31 6379 +OK +#可以看到所有节点之间可以相互连接通信,以master1为例 +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning cluster nodes +76da144ef9ea65664f9ba5ac431ed9e26b07b132 192.168.175.31:6379@16379 master - 0 1625909926530 4 connected +fae9fe337678873ec0f3e0a44c39bd8064c85fb0 192.168.175.30:6379@16379 master - 0 1625909925000 0 connected +5eeb5ce103710ec5108d3257fdcd019b401548d9 192.168.175.11:6379@16379 master - 0 1625909926000 3 connected +3ccb32a3e79572a16a1aa3e8188fff07121b1d1e 192.168.175.20:6379@16379 master - 0 1625909927549 2 connected +48000fa51e653b3dbf3f70829fc9142c40195ff6 192.168.175.10:6379@16379 myself,master - 0 1625909927000 1 connected +e1b815df722f7c202e28215fa23ff20913d88479 192.168.175.21:6379@16379 master - 0 1625909924486 5 connected +``` + +- 由于没有分配槽位,无法创建key + +```shell +[root@master1 ~]# redis-cli -a centos --no-auth-warning set name eagle +(error) CLUSTERDOWN Hash slot not served +``` + +- 查看当前状态,以master1为例 + +```shell +[root@master1 ~]# redis-cli -a centos --no-auth-warning cluster info +cluster_state:fail +cluster_slots_assigned:0 +cluster_slots_ok:0 +cluster_slots_pfail:0 +cluster_slots_fail:0 +cluster_known_nodes:6 +cluster_size:0 +cluster_current_epoch:5 +cluster_my_epoch:1 +cluster_stats_messages_ping_sent:289 +cluster_stats_messages_pong_sent:290 +cluster_stats_messages_meet_sent:5 +cluster_stats_messages_sent:584 +cluster_stats_messages_ping_received:289 +cluster_stats_messages_pong_received:294 +cluster_stats_messages_meet_received:1 +cluster_stats_messages_received:584 +``` + +- 为每个master 节点指派槽位范围 + +```shell +[root@master1 ~]# vim addslots.sh +#!/bin/bash +HOST=$1 +PORT=$2 +START=$3 +END=$4 +PASS=centos +for slot in `seq ${START} ${END}`;do + echo "slot: ${slot}" + redis-cli -h ${HOST} -p ${PORT} -a ${PASS} --no-auth-warning cluster addslots ${slot} +done +* 为三个master分配槽位,共16364/3=5461.33333,平均每个master分配5461个槽位 +[root@master1 ~]# bash addslots.sh 192.168.175.10 6379 0 5461 +#当master1分配完槽位后,可以看到下面信息 +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning cluster info +cluster_state:fail +cluster_slots_assigned:5462 +cluster_slots_ok:5462 +cluster_slots_pfail:0 +cluster_slots_fail:0 +cluster_known_nodes:6 +cluster_size:1 +cluster_current_epoch:5 +cluster_my_epoch:1 +cluster_stats_messages_ping_sent:504 +cluster_stats_messages_pong_sent:500 +cluster_stats_messages_meet_sent:5 +cluster_stats_messages_sent:1009 +cluster_stats_messages_ping_received:499 +cluster_stats_messages_pong_received:509 +cluster_stats_messages_meet_received:1 +cluster_stats_messages_received:1009 +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning cluster nodes +76da144ef9ea65664f9ba5ac431ed9e26b07b132 192.168.175.31:6379@16379 master - 0 1625910366128 4 connected +fae9fe337678873ec0f3e0a44c39bd8064c85fb0 192.168.175.30:6379@16379 master - 0 1625910366000 0 connected +5eeb5ce103710ec5108d3257fdcd019b401548d9 192.168.175.11:6379@16379 master - 0 1625910365000 3 connected +3ccb32a3e79572a16a1aa3e8188fff07121b1d1e 192.168.175.20:6379@16379 master - 0 1625910367145 2 connected +48000fa51e653b3dbf3f70829fc9142c40195ff6 192.168.175.10:6379@16379 myself,master - 0 1625910366000 1 connected 0-5461 +e1b815df722f7c202e28215fa23ff20913d88479 192.168.175.21:6379@16379 master - 0 1625910368168 5 connected +# 当所有的三个master分配完槽位后,可以看到如下信息,以master1为例,在其它主机看到的信息一样 +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning cluster nodes +76da144ef9ea65664f9ba5ac431ed9e26b07b132 192.168.175.31:6379@16379 master - 0 1625912355000 4 connected +3ccb32a3e79572a16a1aa3e8188fff07121b1d1e 192.168.175.20:6379@16379 master - 0 1625912353000 2 connected 5462-10922 +fae9fe337678873ec0f3e0a44c39bd8064c85fb0 192.168.175.30:6379@16379 master - 0 1625912355795 0 connected 10923-16383 +5eeb5ce103710ec5108d3257fdcd019b401548d9 192.168.175.11:6379@16379 master - 0 1625912354000 3 connected +48000fa51e653b3dbf3f70829fc9142c40195ff6 192.168.175.10:6379@16379 myself,master - 0 1625912351000 1 connected 0-5461 +e1b815df722f7c202e28215fa23ff20913d88479 192.168.175.21:6379@16379 master - 0 1625912354000 5 connected +``` + +- 指定各个节点的主从关系 + +```shell +# 通过上面的cluster nodes 查看master的ID信息,执行下面操作,将对应的slave 指定相应的master节点,实现三对主从节点 +[root@master1 ~]# redis-cli -h 192.168.175.11 -a centos --no-auth-warning cluster replicate 48000fa51e653b3dbf3f70829fc9142c40195ff6 +OK +[root@master1 ~]# redis-cli -h 192.168.175.21 -a centos --no-auth-warning cluster replicate 3ccb32a3e79572a16a1aa3e8188fff07121b1d1e +OK +[root@master1 ~]# redis-cli -h 192.168.175.31 -a centos --no-auth-warning cluster replicate fae9fe337678873ec0f3e0a44c39bd8064c85fb0 +OK +``` + +- 查看主从节点关系及槽位信息 + +```shell +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning cluster slots +1) 1) (integer) 5462 + 2) (integer) 10922 + 3) 1) "192.168.175.20" + 2) (integer) 6379 + 3) "3ccb32a3e79572a16a1aa3e8188fff07121b1d1e" + 4) 1) "192.168.175.21" + 2) (integer) 6379 + 3) "e1b815df722f7c202e28215fa23ff20913d88479" +2) 1) (integer) 10923 + 2) (integer) 16383 + 3) 1) "192.168.175.30" + 2) (integer) 6379 + 3) "fae9fe337678873ec0f3e0a44c39bd8064c85fb0" + 4) 1) "192.168.175.31" + 2) (integer) 6379 + 3) "76da144ef9ea65664f9ba5ac431ed9e26b07b132" +3) 1) (integer) 0 + 2) (integer) 5461 + 3) 1) "192.168.175.10" + 2) (integer) 6379 + 3) "48000fa51e653b3dbf3f70829fc9142c40195ff6" + 4) 1) "192.168.175.11" + 2) (integer) 6379 + 3) "5eeb5ce103710ec5108d3257fdcd019b401548d9" +``` + +- 验证 redis cluster 访问 + - -c 表示以集群方式连接 + +```shell +[root@master1 ~]# redis-cli -c -h 192.168.175.10 -a centos --no-auth-warning set name eagle +OK +[root@master1 ~]# redis-cli -c -h 192.168.175.10 -a centos --no-auth-warning get name +"eagle" +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning get name +(error) MOVED 5798 192.168.175.20:6379 +[root@master1 ~]# redis-cli -h 192.168.175.20 -a centos --no-auth-warning get name +"eagle" +[root@master1 ~]# redis-cli -h 192.168.175.30 -a centos --no-auth-warning get name +(error) MOVED 5798 192.168.175.20:6379 +``` + +## 10.3 实战案例:基于Redis 5 的 redis cluster 部署 + +官方文档:https://redis.io/topics/cluster-tutorial + +```shell +# cluste选项帮助 +[root@master1 ~]# redis-cli --cluster help +Usage: redis-trib +create host1:port1 ... hostN:portN #创建集群 + --replicas #指定master的副本数量 +check host:port #检查集群信息 +info host:port #查看集群主机信息 +fix host:port #修复集群 + --timeout +reshard host:port #在线热迁移集群指定主机的slots数据 + --from + --to + --slots + --yes + --timeout + --pipeline +rebalance host:port #平衡集群中各主机的slot数量 + --weight + --auto-weights + --use-empty-masters + --timeout + --simulate + --pipeline + --threshold +add-node new_host:new_port existing_host:existing_port #添加主机到集群 + --slave + --master-id +del-node host:port node_id #删除主机 +set-timeout host:port milliseconds #设置节点的超时时间 +call host:port command arg arg .. arg #在集群上的所有节点上执行命令 +import host:port #导入外部redis服务器的数据到当前集群 + --from + --copy + --replace +help (show this help) +For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster. +``` + +- 创建 redis cluster集群的环境准备 + - 每个redis 节点采用相同的硬件配置、相同的密码、相同的redis版本 + - 所有redis服务器必须没有任何数据 + - 准备6台主机,开启cluster配置 + +### 10.3.1 创建集群 + +```shell +# redis-cli --cluster-replicas 1 表示每个master对应一个slave节点 +[root@master1 ~]# redis-cli -a centos --no-auth-warning --cluster create \ +192.168.175.10:6379 192.168.175.20:6379 192.168.175.30:6379 \ +192.168.175.11:6379 192.168.175.21:6379 192.168.175.31:6379 \ +--cluster-replicas 1 +# 观察运行结果,可以看到3组master/slave +``` + +### 10.3.2 查看主从状态 + +```shell +[root@master1 ~]# redis-cli -c -h 192.168.175.10 -a centos info replication +Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. +# Replication +role:master +connected_slaves:0 +master_replid:19f199fca5cf6fcd15aa6db3d7482c302923f84d +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:0 +second_repl_offset:-1 +repl_backlog_active:0 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:0 +repl_backlog_histlen:0 +[root@master1 ~]# redis-cli -c -h 192.168.175.11 -a centos info replication +Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. +# Replication +role:slave +master_host:192.168.175.30 +master_port:6379 +master_link_status:down +master_last_io_seconds_ago:-1 +master_sync_in_progress:0 +slave_repl_offset:1 +master_link_down_since_seconds:1625915270 +slave_priority:100 +slave_read_only:1 +connected_slaves:0 +master_replid:d322eda9ed8308deae297ad5920a22e43a1d24ab +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:0 +second_repl_offset:-1 +repl_backlog_active:0 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:0 +repl_backlog_histlen:0 +``` + +- 查看指定master节点的slave节点信息 + +```shell +[root@master1 ~]# redis-cli -a centos cluster nodes +Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. +449ef170cd83b0d42c47e6504210699770b7a9fa 192.168.175.30:6379@16379 master - 0 1625915361650 3 connected 10923-16383 +1329d448ec1f4abcf39ba03cdeed484c869a27b5 192.168.175.10:6379@16379 myself,master - 0 1625915359000 1 connected 0-5460 +464d5cbe572a69ae6c95c82f2ce5942dbc8a848a 192.168.175.31:6379@16379 slave f8f0d5dec65001f4082c6db805fff98777636e00 0 1625915361000 6 connected +09f2f31bf45b91a04bab1e798a3f053104edfcd5 192.168.175.21:6379@16379 slave 1329d448ec1f4abcf39ba03cdeed484c869a27b5 0 1625915362668 5 connected +74a4f661379361a38f4bef603bc1807025527b4b 192.168.175.11:6379@16379 slave 449ef170cd83b0d42c47e6504210699770b7a9fa 0 1625915361000 4 connected +f8f0d5dec65001f4082c6db805fff98777636e00 192.168.175.20:6379@16379 master - 0 1625915362000 2 connected 5461-10922 +``` + +### 10.3.3 验证集群状态 + +```shell +[root@master1 ~]# redis-cli -a centos --no-auth-warning cluster info +cluster_state:ok +cluster_slots_assigned:16384 +cluster_slots_ok:16384 +cluster_slots_pfail:0 +cluster_slots_fail:0 +cluster_known_nodes:6 #节点数 +cluster_size:3 #3个集群 +cluster_current_epoch:6 +cluster_my_epoch:1 +cluster_stats_messages_ping_sent:342 +cluster_stats_messages_pong_sent:362 +cluster_stats_messages_sent:704 +cluster_stats_messages_ping_received:357 +cluster_stats_messages_pong_received:342 +cluster_stats_messages_meet_received:5 +cluster_stats_messages_received:704 +``` + +### 10.3.4 查看集群node对应关系 + +```shell +[root@master1 ~]# redis-cli -a centos --no-auth-warning cluster nodes +449ef170cd83b0d42c47e6504210699770b7a9fa 192.168.175.30:6379@16379 master - 0 1625915444000 3 connected 10923-16383 +1329d448ec1f4abcf39ba03cdeed484c869a27b5 192.168.175.10:6379@16379 myself,master - 0 1625915446000 1 connected 0-5460 +464d5cbe572a69ae6c95c82f2ce5942dbc8a848a 192.168.175.31:6379@16379 slave f8f0d5dec65001f4082c6db805fff98777636e00 0 1625915446000 6 connected +09f2f31bf45b91a04bab1e798a3f053104edfcd5 192.168.175.21:6379@16379 slave 1329d448ec1f4abcf39ba03cdeed484c869a27b5 0 1625915447384 5 connected +74a4f661379361a38f4bef603bc1807025527b4b 192.168.175.11:6379@16379 slave 449ef170cd83b0d42c47e6504210699770b7a9fa 0 1625915443298 4 connected +f8f0d5dec65001f4082c6db805fff98777636e00 192.168.175.20:6379@16379 master - 0 1625915447000 2 connected 5461-10922 +``` + +### 10.3.5 验证集群写入key + +![img-redis集群模式-客户端](02.Redis/redis集群模式-客户端.png) + +- redis cluster 写入key + +```shell +[root@master1 ~]# redis-cli -h 192.168.175.10 -a centos --no-auth-warning set name eagle +(error) MOVED 5798 192.168.175.20:6379 #槽位不在当前node所以无法写入 +[root@master1 ~]# redis-cli -h 192.168.175.20 -a centos --no-auth-warning set name eagle +OK +[root@master1 ~]# redis-cli -h 192.168.175.20 -a centos --no-auth-warning get name +"eagle" +``` + +### 10.3.6 模拟master故障 + +- 对应的slave节点自动提升为新master +- 模拟master3节点出故障,需要相应的数秒故障转移时间 + +```shell +[root@master3 ~]# redis-cli -a centos --no-auth-warning +127.0.0.1:6379> shutdown +not connected> exit +[root@master3 ~]# redis-cli -a centos --no-auth-warning --cluster info 192.168.175.10:6379 +Could not connect to Redis at 192.168.175.30:6379: Connection refused +192.168.175.10:6379 (c5fd0661...) -> 3333 keys | 5461 slots | 1 slaves. +192.168.175.20:6379 (1cd30e11...) -> 3341 keys | 5462 slots | 1 slaves. +192.168.175.31:6379 (4e42a7da...) -> 3330 keys | 5461 slots | 0 slaves. #192.168.175.31为新的master +[OK] 10004 keys in 3 masters. +0.61 keys per slot on average. +``` \ No newline at end of file diff --git a/03.数据库/02.Redis/AOF工作原理01.png b/03.数据库/02.Redis/AOF工作原理01.png new file mode 100644 index 0000000..a45826c Binary files /dev/null and b/03.数据库/02.Redis/AOF工作原理01.png differ diff --git a/03.数据库/02.Redis/AOF工作原理02.png b/03.数据库/02.Redis/AOF工作原理02.png new file mode 100644 index 0000000..1e9dc16 Binary files /dev/null and b/03.数据库/02.Redis/AOF工作原理02.png differ diff --git a/03.数据库/02.Redis/CDN缓存.png b/03.数据库/02.Redis/CDN缓存.png new file mode 100644 index 0000000..aa87783 Binary files /dev/null and b/03.数据库/02.Redis/CDN缓存.png differ diff --git a/03.数据库/02.Redis/CDN请求流程01.png b/03.数据库/02.Redis/CDN请求流程01.png new file mode 100644 index 0000000..32e352d Binary files /dev/null and b/03.数据库/02.Redis/CDN请求流程01.png differ diff --git a/03.数据库/02.Redis/CDN请求流程02.png b/03.数据库/02.Redis/CDN请求流程02.png new file mode 100644 index 0000000..344dccd Binary files /dev/null and b/03.数据库/02.Redis/CDN请求流程02.png differ diff --git a/03.数据库/02.Redis/CPU缓存01.png b/03.数据库/02.Redis/CPU缓存01.png new file mode 100644 index 0000000..3cc6d5c Binary files /dev/null and b/03.数据库/02.Redis/CPU缓存01.png differ diff --git a/03.数据库/02.Redis/CPU缓存02.png b/03.数据库/02.Redis/CPU缓存02.png new file mode 100644 index 0000000..889ddf1 Binary files /dev/null and b/03.数据库/02.Redis/CPU缓存02.png differ diff --git a/03.数据库/02.Redis/RDB.png b/03.数据库/02.Redis/RDB.png new file mode 100644 index 0000000..83f1e3a Binary files /dev/null and b/03.数据库/02.Redis/RDB.png differ diff --git a/03.数据库/02.Redis/RDB工作原理01.png b/03.数据库/02.Redis/RDB工作原理01.png new file mode 100644 index 0000000..e93519c Binary files /dev/null and b/03.数据库/02.Redis/RDB工作原理01.png differ diff --git a/03.数据库/02.Redis/RDB工作原理02.png b/03.数据库/02.Redis/RDB工作原理02.png new file mode 100644 index 0000000..0dd0017 Binary files /dev/null and b/03.数据库/02.Redis/RDB工作原理02.png differ diff --git a/03.数据库/02.Redis/Redis_rank.png b/03.数据库/02.Redis/Redis_rank.png new file mode 100644 index 0000000..46406b9 Binary files /dev/null and b/03.数据库/02.Redis/Redis_rank.png differ diff --git a/03.数据库/02.Redis/redis业务更新操作.png b/03.数据库/02.Redis/redis业务更新操作.png new file mode 100644 index 0000000..c7327d1 Binary files /dev/null and b/03.数据库/02.Redis/redis业务更新操作.png differ diff --git a/03.数据库/02.Redis/redis业务读操作.png b/03.数据库/02.Redis/redis业务读操作.png new file mode 100644 index 0000000..f1b3267 Binary files /dev/null and b/03.数据库/02.Redis/redis业务读操作.png differ diff --git a/03.数据库/02.Redis/redis主从复制-全量.png b/03.数据库/02.Redis/redis主从复制-全量.png new file mode 100644 index 0000000..1d1e53d Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-全量.png differ diff --git a/03.数据库/02.Redis/redis主从复制-单机器复制风暴.png b/03.数据库/02.Redis/redis主从复制-单机器复制风暴.png new file mode 100644 index 0000000..06618cd Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-单机器复制风暴.png differ diff --git a/03.数据库/02.Redis/redis主从复制-单节点复制风暴.png b/03.数据库/02.Redis/redis主从复制-单节点复制风暴.png new file mode 100644 index 0000000..939b7c2 Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-单节点复制风暴.png differ diff --git a/03.数据库/02.Redis/redis主从复制-增量.png b/03.数据库/02.Redis/redis主从复制-增量.png new file mode 100644 index 0000000..76b60d1 Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-增量.png differ diff --git a/03.数据库/02.Redis/redis主从复制-故障恢复01.png b/03.数据库/02.Redis/redis主从复制-故障恢复01.png new file mode 100644 index 0000000..84b8796 Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-故障恢复01.png differ diff --git a/03.数据库/02.Redis/redis主从复制-故障恢复02.png b/03.数据库/02.Redis/redis主从复制-故障恢复02.png new file mode 100644 index 0000000..d39c0e4 Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-故障恢复02.png differ diff --git a/03.数据库/02.Redis/redis主从复制-故障恢复03.png b/03.数据库/02.Redis/redis主从复制-故障恢复03.png new file mode 100644 index 0000000..0a65423 Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-故障恢复03.png differ diff --git a/03.数据库/02.Redis/redis主从复制-联级.png b/03.数据库/02.Redis/redis主从复制-联级.png new file mode 100644 index 0000000..586baf8 Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-联级.png differ diff --git a/03.数据库/02.Redis/redis主从复制-过程01.png b/03.数据库/02.Redis/redis主从复制-过程01.png new file mode 100644 index 0000000..2c7e26e Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-过程01.png differ diff --git a/03.数据库/02.Redis/redis主从复制-过程02.png b/03.数据库/02.Redis/redis主从复制-过程02.png new file mode 100644 index 0000000..4d303c2 Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-过程02.png differ diff --git a/03.数据库/02.Redis/redis主从复制-过程03.png b/03.数据库/02.Redis/redis主从复制-过程03.png new file mode 100644 index 0000000..1839818 Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-过程03.png differ diff --git a/03.数据库/02.Redis/redis主从复制-过程04.png b/03.数据库/02.Redis/redis主从复制-过程04.png new file mode 100644 index 0000000..c81092d Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制-过程04.png differ diff --git a/03.数据库/02.Redis/redis主从复制.png b/03.数据库/02.Redis/redis主从复制.png new file mode 100644 index 0000000..25d7ba3 Binary files /dev/null and b/03.数据库/02.Redis/redis主从复制.png differ diff --git a/03.数据库/02.Redis/redis单线程01.png b/03.数据库/02.Redis/redis单线程01.png new file mode 100644 index 0000000..fd520de Binary files /dev/null and b/03.数据库/02.Redis/redis单线程01.png differ diff --git a/03.数据库/02.Redis/redis单线程02.png b/03.数据库/02.Redis/redis单线程02.png new file mode 100644 index 0000000..ec2bfef Binary files /dev/null and b/03.数据库/02.Redis/redis单线程02.png differ diff --git a/03.数据库/02.Redis/redis哨兵实现.png b/03.数据库/02.Redis/redis哨兵实现.png new file mode 100644 index 0000000..f5f4b90 Binary files /dev/null and b/03.数据库/02.Redis/redis哨兵实现.png differ diff --git a/03.数据库/02.Redis/redis哨兵模式01.png b/03.数据库/02.Redis/redis哨兵模式01.png new file mode 100644 index 0000000..82f2145 Binary files /dev/null and b/03.数据库/02.Redis/redis哨兵模式01.png differ diff --git a/03.数据库/02.Redis/redis哨兵模式02.png b/03.数据库/02.Redis/redis哨兵模式02.png new file mode 100644 index 0000000..39048cd Binary files /dev/null and b/03.数据库/02.Redis/redis哨兵模式02.png differ diff --git a/03.数据库/02.Redis/redis客户端.png b/03.数据库/02.Redis/redis客户端.png new file mode 100644 index 0000000..24612d1 Binary files /dev/null and b/03.数据库/02.Redis/redis客户端.png differ diff --git a/03.数据库/02.Redis/redis持久化.png b/03.数据库/02.Redis/redis持久化.png new file mode 100644 index 0000000..6fb128a Binary files /dev/null and b/03.数据库/02.Redis/redis持久化.png differ diff --git a/03.数据库/02.Redis/redis数据类型-列表.png b/03.数据库/02.Redis/redis数据类型-列表.png new file mode 100644 index 0000000..c63095d Binary files /dev/null and b/03.数据库/02.Redis/redis数据类型-列表.png differ diff --git a/03.数据库/02.Redis/redis数据类型01.png b/03.数据库/02.Redis/redis数据类型01.png new file mode 100644 index 0000000..7d355fe Binary files /dev/null and b/03.数据库/02.Redis/redis数据类型01.png differ diff --git a/03.数据库/02.Redis/redis数据类型02.png b/03.数据库/02.Redis/redis数据类型02.png new file mode 100644 index 0000000..732e969 Binary files /dev/null and b/03.数据库/02.Redis/redis数据类型02.png differ diff --git a/03.数据库/02.Redis/redis消息队列-发布订阅.png b/03.数据库/02.Redis/redis消息队列-发布订阅.png new file mode 100644 index 0000000..8e595a2 Binary files /dev/null and b/03.数据库/02.Redis/redis消息队列-发布订阅.png differ diff --git a/03.数据库/02.Redis/redis消息队列-模式介绍.png b/03.数据库/02.Redis/redis消息队列-模式介绍.png new file mode 100644 index 0000000..3d8a19d Binary files /dev/null and b/03.数据库/02.Redis/redis消息队列-模式介绍.png differ diff --git a/03.数据库/02.Redis/redis消息队列-队列介绍.png b/03.数据库/02.Redis/redis消息队列-队列介绍.png new file mode 100644 index 0000000..2087760 Binary files /dev/null and b/03.数据库/02.Redis/redis消息队列-队列介绍.png differ diff --git a/03.数据库/02.Redis/redis消息队列.png b/03.数据库/02.Redis/redis消息队列.png new file mode 100644 index 0000000..2580436 Binary files /dev/null and b/03.数据库/02.Redis/redis消息队列.png differ diff --git a/03.数据库/02.Redis/redis集群模式-实验环境.png b/03.数据库/02.Redis/redis集群模式-实验环境.png new file mode 100644 index 0000000..e5babc8 Binary files /dev/null and b/03.数据库/02.Redis/redis集群模式-实验环境.png differ diff --git a/03.数据库/02.Redis/redis集群模式-客户端.png b/03.数据库/02.Redis/redis集群模式-客户端.png new file mode 100644 index 0000000..46ab1dd Binary files /dev/null and b/03.数据库/02.Redis/redis集群模式-客户端.png differ diff --git a/03.数据库/02.Redis/redis集群模式.png b/03.数据库/02.Redis/redis集群模式.png new file mode 100644 index 0000000..88d83d2 Binary files /dev/null and b/03.数据库/02.Redis/redis集群模式.png differ diff --git a/03.数据库/02.Redis/redis集群模式slot.png b/03.数据库/02.Redis/redis集群模式slot.png new file mode 100644 index 0000000..da26979 Binary files /dev/null and b/03.数据库/02.Redis/redis集群模式slot.png differ diff --git a/03.数据库/02.Redis/redis集群模式架构01.png b/03.数据库/02.Redis/redis集群模式架构01.png new file mode 100644 index 0000000..9c9b7fe Binary files /dev/null and b/03.数据库/02.Redis/redis集群模式架构01.png differ diff --git a/03.数据库/02.Redis/redis集群模式架构02.png b/03.数据库/02.Redis/redis集群模式架构02.png new file mode 100644 index 0000000..458d93c Binary files /dev/null and b/03.数据库/02.Redis/redis集群模式架构02.png differ diff --git a/03.数据库/02.Redis/redis集群模式架构03.png b/03.数据库/02.Redis/redis集群模式架构03.png new file mode 100644 index 0000000..cb7ef84 Binary files /dev/null and b/03.数据库/02.Redis/redis集群模式架构03.png differ diff --git a/03.数据库/02.Redis/慢查询.png b/03.数据库/02.Redis/慢查询.png new file mode 100644 index 0000000..d3508ee Binary files /dev/null and b/03.数据库/02.Redis/慢查询.png differ diff --git a/03.数据库/02.Redis/缓存.png b/03.数据库/02.Redis/缓存.png new file mode 100644 index 0000000..544c4e6 Binary files /dev/null and b/03.数据库/02.Redis/缓存.png differ diff --git a/04.自动化运维/01.文本三剑客.md b/04.自动化运维/01.文本三剑客.md new file mode 100644 index 0000000..5e7839a --- /dev/null +++ b/04.自动化运维/01.文本三剑客.md @@ -0,0 +1,1458 @@ +# 1. 通配符 + +Linux 中的通配符是一种特殊的符号,用于匹配文件名或目录名。这些通配符可以帮助用户更快地输入命令和文件路径。或者可以理解为用户文件名和路径的匹配。由shell进行解析,一般情况下与某些命令配合使用,如:find,cp,mv,ls,rm等。 + +## 1.1 **Shell中常见的通配符如下:** + +```bash +*: 匹配0或多个字符 +?: 匹配任意一个字符 +[....]: 匹配方括号中任意单个字符 +[!....]: 匹配除了方括号中指定字符之外的字符 +范围 [a-z]: 匹配指定范围内的任意一个字符 +大括号 {}: 用于匹配由逗号分隔的单个字符串 +``` + +## 1.2 Shell元字符 + +在 Shell 脚本中,除了通配符,还有一些特殊的元字符需要了解。这些元字符有特殊的含义,在编写 Shell 脚本时需要特别注意。以下是 Shell 中常见的一些元字符及其用法 + +``` +IFS:// +=:设定变量 +$:取变量值 +>/< :重定向 +|:管道 +&:后台执行命令 +():在子shell中执行命令/运算或命令替换 +{}:用于扩展变量、生成序列等。 +例如 echo {A..Z} 会输出 A 到 Z 的大写字母序列。 +;:命令结束后,忽略其返回值,继续执行下一个命令 +&&:命令结束后,若为true,继续执行下一个命令 +||:命令结束后,若为false,继续执行下一个命令 +!:非 +#:注释 +\:转义符 +``` + +**Shell中的转义符:** + +``` +1. 反斜杠 \: 是最常用的转义符,用于转义其后的一个字符 +例如 echo "This is a backslash: \\" 会输出 This is a backslash: \ +2. 单引号'...': 可以将整个字符串括起来,使其中的特殊字符失去特殊含义,硬转义 +3. 双引号"...": 内部的大部分特殊字符仍然保留特殊含义,软转义 + +示例: +echo 'Hello, $USER' 输出---> Hello, $USER +echo "Hello, $USER" 输出---> Hello, root +``` + +# 2. find文件查找 + +实时查找工具,通过遍历指定路径下的文件系统完成文件查找 + +工作特点: + +* 查找速度略慢 +* 精确查找 +* 实时查找 +* 可以满足多种条件匹配 + +```shell +find [选项] [路径] [查找条件 + 处理动作] + +查找路径:指定具体目录路径,默认是当前文件夹 +查找条件:指定的查找标准(文件名/大小/类型/权限等),默认是找出所有文件 +处理动作:对符合条件的文件做什么操作,默认输出屏幕 +``` + +## 2.1 查找条件 + +``` +根据文件名查找: + ‐name "filename" 支持global + ‐iname "filename" 忽略大小写 + ‐regex "PATTERN" 以Pattern匹配整个文件路径字符串,而不仅仅是文件名称 +根据属主和属组查找: + ‐user USERNAME:查找属主为指定用户的文件 + ‐group GROUPNAME:查找属组为指定属组的文件 + ‐uid UserID:查找属主为指定的ID号的文件 + ‐gid GroupID:查找属组为指定的GID号的文件 + ‐nouser:查找没有属主的文件 + ‐nogroup:查找没有属组的文件 +根据文件类型查找: + ‐type Type: + f/d/l/s/b/c/p +根据文件大小来查找: + ‐size [+|‐]N[bcwkMG] +根据时间戳: + 天: + ‐atime [+|‐]N + ‐mtime + ‐ctime + 分钟: + ‐amin N + ‐cmin N + ‐mmin N +根据权限查找: + ‐perm [+|‐]MODE + MODE:精确权限匹配 + /MODE:任何一类(u,g,o)对象的权限中只要能一位匹配即可 + ‐MODE:每一类对象都必须同时拥有为其指定的权限标准 +组合条件: + 与:‐a 多个条件and并列 + 或:‐o 多个条件or并列 + 非:‐not 条件取反 +``` + +### 2.1.1 示例: + +* 根据文件名查找 + +```shell +[root@localhost ~]# find /etc -name "ifcfg-ens33" +[root@localhost ~]# find /etc -iname "ifcfg-ens33" # 忽略大小写 +[root@localhost ~]# find /etc -iname "ifcfg*" +``` + +* 按文件大小 + +```shell +[root@localhost ~]# find /etc -size +5M # 大于5M +[root@localhost ~]# find /etc -size 5M # 等于5M +[root@localhost ~]# find /etc -size -5M # 小于5M +[root@localhost ~]# find /etc -size +5M -ls # 找到的处理动作-ls +``` + +* 指定查找的目录深度 + +```shell +[root@localhost ~]# find / -maxdepth 3 -a -name "ifcfg-ens33" # 最大查找深度 +# -a是同时满足,-o是或 +[root@localhost ~]# find / -mindepth 3 -a -name "ifcfg-ens33" # 最小查找深度 +``` + +* 按时间找 + +```shell +[root@localhost ~]# find /etc -mtime +5 # 修改时间超过5天 +[root@localhost ~]# find /etc -mtime 5 # 修改时间等于5天 +[root@localhost ~]# find /etc -mtime -5 # 修改时间5天以内 +``` + +* 按照文件属主、属组找,文件的属主和属组,会在下一篇详细讲解。 + +```shell +[root@localhost ~]# find /home -user xwz # 属主是xwz的文件 +[root@localhost ~]# find /home -group xwz +[root@localhost ~]# find /home -user xwz -group xwz +[root@localhost ~]# find /home -user xwz -a -group root +[root@localhost ~]# find /home -user xwz -o -group root +[root@localhost ~]# find /home -nouser # 没有属主的文件 +[root@localhost ~]# find /home -nogroup # 没有属组的文件 +``` + +* 按文件类型 + +```shell +[root@localhost ~]# find /dev -type d +``` + +* 按文件权限 + +```shell +[root@localhost ~]# find / -perm 644 -ls +[root@localhost ~]# find / -perm -644 -ls # 权限小于644的 +[root@localhost ~]# find / -perm 4000 -ls +[root@localhost ~]# find / -perm -4000 -ls +``` + +* 按正则表达式 + +```shell +[root@localhost ~]# find /etc -regex '.*ifcfg-ens[0-9][0-9]' +# .* 任意多个字符 +# [0-9] 任意一个数字 +``` + +## 2.2 处理动作 + +```bash +‐print:默认的处理动作,显示至屏幕 +‐ls:类型于对查找到的文件执行“ls ‐l”命令 +‐delete:删除查找到的文件 +‐fls /path/to/somefile:查找到的所有文件的长格式信息保存至指定文件中 +‐ok COMMAND {}\:对查找到的每个文件执行由COMMAND指定的命令 + 并且对于每个文件执行命令之前,都会交换式要求用户确认 +‐exec COMMAND {} \:对查找到的每个文件执行由COMMAND指定的命令 +{}:用于引用查找到的文件名称自身 +[root@server1 ~]# find /etc/init.d/ -perm -111 -exec cp -r {} dir1/ \; +``` + +# 3. 正则表达式 + +正则表达式是一种强大的文本匹配和处理工具。它允许你定义复杂的匹配模式,在文本中查找、替换和操作数据。正则表达式被广泛应用于各种文本处理工具和命令中,如 sed、awk、grep 等..... + +```bash +1. 字符匹配 +- `.`:匹配任意单个字符 +- `[]`:匹配指定范围内任意单个字符 `[a-z]` `[0-9]` +- `[^]`:匹配指定范围外任意单个字符 `[^a-z]` `[^0-9]` +- `[:alnum:]`:字母与数字字符 +- `[:alpha:]`:字母 +- `[:ascii:]`:ASCII 字符 +- `[:blank:]`:空格或制表符 +- `[:cntrl:]`:ASCII 控制字符 +- `[:digit:]`:数字 +- `[:graph:]`:非控制、非空格字符 +- `[:lower:]`:小写字母 +- `[:print:]`:可打印字符 +- `[:punct:]`:标点符号字符 +- `[:space:]`:空白字符,包括垂直制表符 +- `[:upper:]`:大写字母 +- `[:xdigit:]`:十六进制数字 + +2. 匹配次数 +- `*`:匹配前面的字符任意次数 +- `.*`:匹配任意长度的字符 +- `\?`:匹配其前面字符 0 或 1 次,即前面的可有可无 `'a\?b'` +- `\+`:匹配其前面的字符至少 1 次 `'a\+b'` +- `\{m\}`:匹配前面的字符 m 次 +- `\{m,n\}`:匹配前面的字符至少 m 次,至多 n 次 +- `\{0,n\}`:匹配前面的字符至多 n 次 +- `\{m,\}`:匹配前面的字符至少 m 次 + +3. 位置锚定 +- `^`:行首锚定,用于模式的最左侧 +- `$`:行末锚定,用于模式的最右侧 +- `^PATTERN$`:用于模式匹配整行 +- `^$`:空行 +- `\<` 或 `\b`:词首锚定,用于单词模式的左侧 +- `\>` 或 `\b`:词尾锚定,用于单词模式的右侧 +- `\`:匹配整个单词 `'\'` + +4. 分组 +- `()`: 用于分组,可以对一组字符应用量词等操作 +- 分组括号中的模式匹配到的内容会被正则表达式引擎记录于内部的变量中 +- `\1`、`\2` 等: 用于引用前面匹配的分组内容 +- `\1`:从左侧起,第一个左括号以及与之匹配右括号之间的模式所匹配到的字符; +``` + + + +# 4. 文本三剑客之grep + +grep作用:过滤文本内容 + +| 选项 | 描述 | +| :----------------------- | :------------------------------- | +| -E :--extended--regexp | 模式是扩展正则表达式(ERE) | +| -i :--ignore--case | 忽略大小写 | +| -n: --line--number | 打印行号 | +| -o:--only--matching | 只打印匹配的内容 | +| -c:--count | 只打印每个文件匹配的行数 | +| -B:--before--context=NUM | 打印匹配的前几行 | +| -A:--after--context=NUM | 打印匹配的后几行 | +| -C:--context=NUM | 打印匹配的前后几行 | +| --color[=WHEN] | 匹配的字体颜色,别名已定义了 | +| -v:--invert--match | 打印不匹配的行 | +| -e | 多点操作eg:grep -e "^s" -e "s$" | + +## 4.1 案例 + +**文本内容:** + +```bash +[root@localhost ~]# python -c "import this" > file +[root@localhost ~]# cat file +The Zen of Python, by Tim Peters + +Beautiful is better than ugly. +Explicit is better than implicit. +Simple is better than complex. +Complex is better than complicated. +Flat is better than nested. +Sparse is better than dense. +Readability counts. +Special cases aren't special enough to break the rules. +Although practicality beats purity. +Errors should never pass silently. +Unless explicitly silenced. +In the face of ambiguity, refuse the temptation to guess. +There should be one-- and preferably only one --obvious way to do it. +Although that way may not be obvious at first unless you're Dutch. +Now is better than never. +Although never is often better than *right* now. +If the implementation is hard to explain, it's a bad idea. +If the implementation is easy to explain, it may be a good idea. +Namespaces are one honking great idea -- let's do more of those! + +``` + +案例1:过滤出所有包含a的行,无论大小写 + +```bash +[root@localhost ~]# grep -i "a" file +[root@localhost ~]# grep "a" file +结果..... +``` + +案例2:过滤出所有包含a的行,无论大小写,并且显示该行所在的行号 + +```bash +[root@localhost ~]# grep -in "a" file +3:Beautiful is better than ugly. +4:Explicit is better than implicit. +5:Simple is better than complex. +6:Complex is better than complicated. +7:Flat is better than nested. +8:Sparse is better than dense. +9:Readability counts. +10:Special cases aren't special enough to break the rules. +11:Although practicality beats purity. +12:Errors should never pass silently. +14:In the face of ambiguity, refuse the temptation to guess. +15:There should be one-- and preferably only one --obvious way to do it. +16:Although that way may not be obvious at first unless you're Dutch. +17:Now is better than never. +18:Although never is often better than *right* now. +19:If the implementation is hard to explain, it's a bad idea. +20:If the implementation is easy to explain, it may be a good idea. +21:Namespaces are one honking great idea -- let's do more of those! +``` + +案例3:仅仅打印出所有匹配的字符或者字符串 + +```bash +[root@localhost ~]# grep -o "a" file +结果.... +``` + +案例4:统计匹配到的字符或字符串总共的行数 + +```bash +[root@localhost ~]# grep -c "a" file +18 +``` + +案例5:打印所匹配到的字符串的前几行 + +```bash +[root@localhost ~]# grep -B 2 "Simple" file +Beautiful is better than ugly. +Explicit is better than implicit. +Simple is better than complex. +``` + +案例6:打印所匹配到的字符串的后几行 + +```bash +[root@localhost ~]# grep -A 3 "Simple" file +Simple is better than complex. +Complex is better than complicated. +Flat is better than nested. +Sparse is better than dense. +``` + +案例7:打印所匹配到的字符串的前后几行 + +```bash +[root@localhost ~]# grep -C 1 "Simple" file +Explicit is better than implicit. +Simple is better than complex. +Complex is better than complicated. +``` + +案例8:取反,过滤出不包含Simple的行 + +```bash +[root@localhost ~]# grep -vn "Simple" file +1:The Zen of Python, by Tim Peters +2: +3:Beautiful is better than ugly. +4:Explicit is better than implicit. +6:Complex is better than complicated. +7:Flat is better than nested. +8:Sparse is better than dense. +9:Readability counts. +10:Special cases aren't special enough to break the rules. +11:Although practicality beats purity. +12:Errors should never pass silently. +13:Unless explicitly silenced. +14:In the face of ambiguity, refuse the temptation to guess. +15:There should be one-- and preferably only one --obvious way to do it. +16:Although that way may not be obvious at first unless you're Dutch. +17:Now is better than never. +18:Although never is often better than *right* now. +19:If the implementation is hard to explain, it's a bad idea. +20:If the implementation is easy to explain, it may be a good idea. +21:Namespaces are one honking great idea -- let's do more of those! +``` + +## 4.2 正则表达式(基于grep) + +- 功能就是用来检索、替换那些符合某个模式(规则)的文本,正则表达式在每种语言中都会有; +- 正则表达式就是为了处理大量的文本或字符串而定义的一套规则和方法 +- 通过定义的这些特殊符号的辅助,系统管理员就可以快速过滤,替换或输出需要的字符串 +- Linux正则表达式一般以行为单位处理 + +### 4.2.1 基础正则表达式 + +| 符号 | 描述 | +| :-----: | :----------------------------------------------- | +| . | 匹配任意单个字符(必须存在) | +| ^ | 匹配以某个字符开头的行 | +| $ | 配以什么字符结尾的行 | +| * | 匹配前面的一个字符出现0次或者多次;eg:a*b | +| .* | 表示任意长度的任意字符 | +| [] | 表示匹配括号内的一个字符 | +| \[^\] | 匹配\[^字符\]之外的任意一个字符 | +| [] | 匹配非\[^字符\]内字符开头的行 | +| < | 锚定 单词首部;eg:\\ | 锚定 单词尾部:eg:root\\> | +| \{m,n\} | 表示匹配前面的字符出现至少m次,至多n次 | +| \(\) | 表示对某个单词进行分组;\1表示第一个分组进行调用 | + +### 4.2.2 扩展正则 + +- egrep ... +- grep -E ... + +- 扩展正则支持所有基础正则;并有补充 +- 扩展正则中{}和[]不用转义可以直接使用; + +| 符号 | 描述 | +| :--: | :------------------------------ | +| + | 表示前面的字符至少出现1次的情况 | +| \| | 表示“或” | +| ? | 表示前面的字符至多出现1次的情况 | + +**最常用:**查看配置文件时去除所有的注释和空行 + +```bash +[root@localhost ~]# grep -Ev "^#|^$" /etc/ssh/sshd_config +``` + +**获取IP地址:** + +```bash +[root@localhost ~]# ifconfig ens33 | grep inet | grep -E '\.' | grep -oE '([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}' | head -n 1 +192.168.88.10 +``` + +# 5. 文本三剑客之sed + +(文本处理工具) + +sed 是一个强大的文本处理工具,它可以用于对文本进行搜索、替换、删除、插入等操作。 + +sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。 + +## 5.1 语法 + +```bash +sed的命令格式: sed [option] 'sed command' filename +sed的脚本格式:sed [option] ‐f 'sed script' filename + +常用选项: +‐n :只打印模式匹配的行 +‐e :直接在命令行模式上进行sed动作编辑,此为默认选项 +‐f :将sed的动作写在一个文件内,用–f filename 执行filename内的sed动作 +‐r :支持扩展表达式 +‐i :直接修改文件内容 +查询文本的方式 +使用行号和行号范围 +x:行号 +x,y:从x行到y行 +x,y!:x行到y行之外 +/pattern:查询包含模式的行 +/pattern/, /pattern/:查询包含两个模式的行 +/pattern/,x:x行内查询包含模式的行 +x,/pattern/:x行后查询匹配模式的行 +``` + +## 5.2 动作说明 + +``` +常用选项: +p:打印匹配的行(‐n) +=:显示文件行号 +a\:指定行号后添加新文本 +i\:指定行号前添加新文本 +d:删除定位行 +c\:用新文本替换定位文本 +w filename:写文本到一个文件 +r filename:从另一个文件读文本 +s///:替换 +替换标记: +g:行内全局替换 +p:显示替换成功的行 +w:将替换成功的结果保存至指定文件 +q:第一个模式匹配后立即退出 +{}:在定位行执行的命令组,用逗号分隔 +g:将模式2粘贴到/pattern n/ +``` + +## 5.3 案例 + +在testfile文件的第四行后添加一行,并将结果输出到标准输出 + +```bash +[root@localhost ~]# vim testfile +[root@localhost ~]# sed -e 4a\newline testfile +line one +line two +line three +line four +newline +line five +``` + +### 5.3.1 以行为单位的新增/删除 + +1. 将 /etc/passwd 的内容列出并且列印行号,同时,请将第 2~5 行删除 + +```bash +[root@localhost ~]# nl /etc/passwd | sed '2,5d' + 1 root:x:0:0:root:/root:/bin/bash + 6 sync:x:5:0:sync:/sbin:/bin/sync + 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown + 8 halt:x:7:0:halt:/sbin:/sbin/halt + 9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin + 10 operator:x:11:0:operator:/root:/sbin/nologin + 11 games:x:12:100:games:/usr/games:/sbin/nologin + 12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin + 13 nobody:x:99:99:Nobody:/:/sbin/nologin + 14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin + 15 dbus:x:81:81:System message bus:/:/sbin/nologin + 16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin + 17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin + 18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin + 19 chrony:x:998:996::/var/lib/chrony:/sbin/nologin +``` + +2. 只删除第2行 + +```bash +[root@localhost ~]# nl /etc/passwd | sed '2d' + 1 root:x:0:0:root:/root:/bin/bash + 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin + 4 adm:x:3:4:adm:/var/adm:/sbin/nologin + 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin + 6 sync:x:5:0:sync:/sbin:/bin/sync + 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown + 8 halt:x:7:0:halt:/sbin:/sbin/halt + 9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin + 10 operator:x:11:0:operator:/root:/sbin/nologin + 11 games:x:12:100:games:/usr/games:/sbin/nologin + 12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin + 13 nobody:x:99:99:Nobody:/:/sbin/nologin + 14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin + 15 dbus:x:81:81:System message bus:/:/sbin/nologin + 16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin + 17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin + 18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin + 19 chrony:x:998:996::/var/lib/chrony:/sbin/nologin +``` + +3. 删除第3行到最后一行的内容 + +```bash +[root@localhost ~]# nl /etc/passwd | sed '3,$d' + 1 root:x:0:0:root:/root:/bin/bash + 2 bin:x:1:1:bin:/bin:/sbin/nologin +``` + +4. 在第2行后面新增helloworld字符 + +```bash +[root@localhost ~]# nl /etc/passwd | sed '2a\hello world' + 1 root:x:0:0:root:/root:/bin/bash + 2 bin:x:1:1:bin:/bin:/sbin/nologin +hello world + 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin + 4 adm:x:3:4:adm:/var/adm:/sbin/nologin + 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin + 6 sync:x:5:0:sync:/sbin:/bin/sync + 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown + 8 halt:x:7:0:halt:/sbin:/sbin/halt + 9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin + 10 operator:x:11:0:operator:/root:/sbin/nologin + 11 games:x:12:100:games:/usr/games:/sbin/nologin + 12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin + 13 nobody:x:99:99:Nobody:/:/sbin/nologin + 14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin + 15 dbus:x:81:81:System message bus:/:/sbin/nologin + 16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin + 17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin + 18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin + 19 chrony:x:998:996::/var/lib/chrony:/sbin/nologin +``` + +5. 加到第2行的前面 + +```bash +[root@localhost ~]# nl /etc/passwd | sed '2i\hello world' + 1 root:x:0:0:root:/root:/bin/bash +hello world + 2 bin:x:1:1:bin:/bin:/sbin/nologin + 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin + 4 adm:x:3:4:adm:/var/adm:/sbin/nologin + 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin + 6 sync:x:5:0:sync:/sbin:/bin/sync + 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown + 8 halt:x:7:0:halt:/sbin:/sbin/halt + 9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin + 10 operator:x:11:0:operator:/root:/sbin/nologin + 11 games:x:12:100:games:/usr/games:/sbin/nologin + 12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin + 13 nobody:x:99:99:Nobody:/:/sbin/nologin + 14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin + 15 dbus:x:81:81:System message bus:/:/sbin/nologin + 16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin + 17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin + 18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin + 19 chrony:x:998:996::/var/lib/chrony:/sbin/nologin +``` + +6. 添加多行内容 + +```bash +[root@localhost ~]# nl /etc/passwd | sed "2a\hello world\nnihao" + 1 root:x:0:0:root:/root:/bin/bash + 2 bin:x:1:1:bin:/bin:/sbin/nologin +hello world +nihao + 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin + 4 adm:x:3:4:adm:/var/adm:/sbin/nologin + 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin + 6 sync:x:5:0:sync:/sbin:/bin/sync + 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown + 8 halt:x:7:0:halt:/sbin:/sbin/halt + 9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin + 10 operator:x:11:0:operator:/root:/sbin/nologin + 11 games:x:12:100:games:/usr/games:/sbin/nologin + 12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin + 13 nobody:x:99:99:Nobody:/:/sbin/nologin + 14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin + 15 dbus:x:81:81:System message bus:/:/sbin/nologin + 16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin + 17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin + 18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin + 19 chrony:x:998:996::/var/lib/chrony:/sbin/nologin +``` + +### 5.3.2 以行为单位的替换与显示 + +1. 将第2-5行的内容取代为”No 2-5 number“ + +```bash +[root@localhost ~]# nl /etc/passwd | sed '2,5c\No 2-5 number' + 1 root:x:0:0:root:/root:/bin/bash +No 2-5 number + 6 sync:x:5:0:sync:/sbin:/bin/sync + 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown + 8 halt:x:7:0:halt:/sbin:/sbin/halt + 9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin + 10 operator:x:11:0:operator:/root:/sbin/nologin + 11 games:x:12:100:games:/usr/games:/sbin/nologin + 12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin + 13 nobody:x:99:99:Nobody:/:/sbin/nologin + 14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin + 15 dbus:x:81:81:System message bus:/:/sbin/nologin + 16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin + 17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin + 18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin + 19 chrony:x:998:996::/var/lib/chrony:/sbin/nologin +``` + +2. 修改SElinux的模式 + +```bash +[root@localhost ~]# nl /etc/selinux/config | sed '7c\SELINUX=permissive' + + 1 # This file controls the state of SELinux on the system. + 2 # SELINUX= can take one of these three values: + 3 # enforcing - SELinux security policy is enforced. + 4 # permissive - SELinux prints warnings instead of enforcing. + 5 # disabled - No SELinux policy is loaded. +SELINUX=permissive + 7 # SELINUXTYPE= can take one of three values: + 8 # targeted - Targeted processes are protected, + 9 # minimum - Modification of targeted policy. Only selected processes are protected. + 10 # mls - Multi Level Security protection. + 11 SELINUXTYPE=targeted +``` + +3. 仅列出 /etc/passwd 文件内的第 5-7 行 + +```bash +[root@localhost ~]# nl /etc/passwd | sed -n '5,7p' + 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin + 6 sync:x:5:0:sync:/sbin:/bin/sync + 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown +``` + +### 5.3.3 数据的搜寻与显示 + +1. 搜索 /etc/passwd有root关键字的行 + +```bash +[root@localhost ~]# nl /etc/passwd | sed -n '/root/p' + 1 root:x:0:0:root:/root:/bin/bash + 10 operator:x:11:0:operator:/root:/sbin/nologin +``` + +### 5.3.4 数据的搜寻并删除 + +1. 删除/etc/passwd所有包含root的行,其他行输出 + +```bash +[root@localhost ~]# nl /etc/passwd | sed '/root/d' + 2 bin:x:1:1:bin:/bin:/sbin/nologin + 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin + 4 adm:x:3:4:adm:/var/adm:/sbin/nologin + 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin + 6 sync:x:5:0:sync:/sbin:/bin/sync + 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown + 8 halt:x:7:0:halt:/sbin:/sbin/halt + 9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin + 11 games:x:12:100:games:/usr/games:/sbin/nologin + 12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin + 13 nobody:x:99:99:Nobody:/:/sbin/nologin + 14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin + 15 dbus:x:81:81:System message bus:/:/sbin/nologin + 16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin + 17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin + 18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin + 19 chrony:x:998:996::/var/lib/chrony:/sbin/nologin +``` + +### 5.3.5 数据的搜寻并执行命令 + +1. 搜索/etc/passwd,找到root对应的行,执行后面花括号中的一组命令,每个命令之间用分号分隔,这里把bash替换为blueshell,再输出这行 + +```bash +[root@localhost ~]# nl /etc/passwd | sed -n '/root/{s/bash/blueshell/p;q}' + 1 root:x:0:0:root:/root:/bin/blueshell +``` + +### 5.3.6 数据的搜寻并替换 + +除了整行的处理模式之外, sed 还可以用行为单位进行部分数据的搜寻并取代 + +```bash +sed 's/要被取代的字串/新的字串/g' +``` + +**案例:替换IP信息** + +先使用ifconfig命令查看当前的IP信息 + +```bash +ens33: flags=4163 mtu 1500 + inet 192.168.88.10 netmask 255.255.255.0 broadcast 192.168.88.255 + inet6 fe80::aee0:741:927e:335b prefixlen 64 scopeid 0x20 + ether 00:0c:29:ae:c9:0f txqueuelen 1000 (Ethernet) + RX packets 4160 bytes 727537 (710.4 KiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 2660 bytes 362687 (354.1 KiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +lo: flags=73 mtu 65536 + inet 127.0.0.1 netmask 255.0.0.0 + inet6 ::1 prefixlen 128 scopeid 0x10 + loop txqueuelen 1000 (Local Loopback) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 +``` + +1. 取出IP地址所在的行 + +```bash +[root@localhost ~]# ifconfig | sed -n '/netmask/p' + inet 192.168.88.10 netmask 255.255.255.0 broadcast 192.168.88.255 + inet 127.0.0.1 netmask 255.0.0.0 +``` + +2. 取出IP地址前后不需要的内容 + +```bash +[root@localhost ~]# ifconfig | sed -n '/netmask/p' | sed 's/^.*inet//g' + 192.168.88.10 netmask 255.255.255.0 broadcast 192.168.88.255 + 127.0.0.1 netmask 255.0.0.0 +[root@localhost ~]# ifconfig | sed -n '/netmask/p' | sed 's/^.*inet//g' | sed 's/netmask.*$//g' + 192.168.88.10 + 127.0.0.1 +``` + +3. 取出第一个IP + +```bash +[root@localhost ~]# ifconfig | sed -n '/netmask/p' | sed 's/^.*inet//g' | sed 's/netmask.*$//g'|sed -n '1p' + 192.168.88.10 +``` + +最终获得当前主机的IP地址 + +其他方式: + +```bash +[root@localhost ~]# ip a|sed -n '/inet /p'| sed 's/^.*inet //g'|sed 's/\/.*$//g'| sed -n '2p' +192.168.88.10 +``` + +### 5.3.7 多点编辑 + +一条sed命令,删除/etc/passwd第三行到末尾的数据,并把bash替换为blueshell + +```bash +[root@localhost ~]# nl /etc/passwd | sed -e '3,$d' -e 's/bash/blueshell/' + 1 root:x:0:0:root:/root:/bin/blueshell + 2 bin:x:1:1:bin:/bin:/sbin/nologin +``` + +-e 表示多点编辑,第一个编辑命令删除/etc/passwd第三行到末尾的数据,第二条命令搜索bash替换为blueshell + +### 5.3.8 直接修改文件内容(危险动作) + +在sed的选项中加入`-i`,就是直接修改文件内容,实时生效,所以必须慎重使用。最好是先不加`-i`参数在测试环境中测试好了以后,在去修改文件本身 + +```bash +[root@localhost ~]# cat testfile +line one +line two +line three +line four +line five +[root@localhost ~]# sed -i 's/line/hello/g' testfile +[root@localhost ~]# cat testfile +hello one +hello two +hello three +hello four +hello five +``` + + + +# 6. 文本三剑客之awk + +(文本分析工具) + +awk 是一种编程语言,它可以进行更加复杂的数据处理和分析。 + +## 6.1 使用方法 + +```bash +awk '{pattern + action}' {filenames} +``` + +其中 pattern 表示 AWK 在数据中查找的内容,而 action 是在找到匹配内容时所执行的一系列命令。花括号({})不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组。 pattern就是要表示的正则表达式,用斜杠括起来。 + +awk语言的最基本功能是在文件或者字符串中基于指定规则浏览和抽取信息,awk抽取信息后,才能进行其他文本操作。完整的awk脚本通常用来格式化文本文件中的信息。 + +通常,awk是以文件的一行为处理单位的。awk每接收文件的一行,然后执行相应的命令,来处理文本。 + +关注点:**1、分隔符;2、具体的分析(定制化输出、数学与逻辑运算)** + +## 6.2 awk工作原理 + +执行 awk 时,它依次对/etc/passwd 中的每一行执行 print 命令 + +```bash +[root@localhost ~]# awk -F: '{print $0}' /etc/passwd +[root@localhost ~]# awk -F: '{print "用户名:" $1}' /etc/passwd +用户名:root +用户名:bin +用户名:daemon +用户名:adm +用户名:lp +用户名:sync +用户名:shutdown +用户名:halt +用户名:mail +用户名:operator +用户名:games +用户名:ftp +用户名:nobody +用户名:systemd-network +用户名:dbus +用户名:polkitd +用户名:sshd +用户名:postfix +用户名:chrony +``` + +awk + +```bash +[root@localhost ~]# awk -F":" '{print $1}' /etc/passwd +[root@localhost ~]# awk -F":" '{print $1 $3}' /etc/passwd +[root@localhost ~]# awk -F":" '{print $1 " " $3}' /etc/passwd +[root@localhost ~]# awk -F":" '{print "username:"$1 "\t tuid:" $3}' /etc/passwd +``` + +-F参数:指定分隔符,可指定一个或多个 + +print 后面做字符串的拼接 + +## 6.3 案例 + +### 6.3.1 查看文件内容 + +只查看test.txt文件(100行)内第20到第30行的内容**(企业面试)** + +```bash +[root@localhost ~]# seq 100 > test.txt +[root@localhost ~]# awk '{if(NR>=20 && NR<=30) print $1}' test.txt +# NR为内置变量,表示行号,从1开始 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +``` + +### 6.3.2 过滤指定字符 + +已知文本内容如下: + +```bash +[root@localhost ~]# cat testfile +I am nls, my qq is 12345678 +``` + +从该文本中过滤出姓名和qq号,要求最后输出结果为:Name: QQ + +```bash +[root@localhost ~]# awk -F '[ ,]' '{print "Name: " $3, "\nQQ: " $8}' testfile +Name: nls +QQ: 12345678 +``` + +## 6.4 BEGIN 和 END 模块 + +1. **BEGIN 模块**: + - `BEGIN` 模块是在 awk 开始处理输入数据之前执行的。 + - 它通常用于初始化一些变量或者打印一些提示信息。 + - 比如在处理文件之前,先打印一行 "Processing the file..."。 +2. **END 模块**: + - `END` 模块是在 awk 处理完所有输入数据之后执行的。 + - 它通常用于输出一些最终的统计信息或者结果。 + - 比如在处理完文件后,打印出总共处理了多少行数据。 + +### 6.4.1 案例一:统计当前系统中存在的账户数量 + +```bash +[root@localhost ~]# awk 'BEGIN {count=0;print "[start] user count is: "count}{count++;print $0} END{print "[end] user count is: " count}' /etc/passwd + +[start] user count is: 0 +root:x:0:0:root:/root:/bin/bash +bin:x:1:1:bin:/bin:/sbin/nologin +daemon:x:2:2:daemon:/sbin:/sbin/nologin +adm:x:3:4:adm:/var/adm:/sbin/nologin +lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin +sync:x:5:0:sync:/sbin:/bin/sync +shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown +halt:x:7:0:halt:/sbin:/sbin/halt +mail:x:8:12:mail:/var/spool/mail:/sbin/nologin +operator:x:11:0:operator:/root:/sbin/nologin +games:x:12:100:games:/usr/games:/sbin/nologin +ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin +nobody:x:99:99:Nobody:/:/sbin/nologin +systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin +dbus:x:81:81:System message bus:/:/sbin/nologin +polkitd:x:999:998:User for polkitd:/:/sbin/nologin +sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin +postfix:x:89:89::/var/spool/postfix:/sbin/nologin +chrony:x:998:996::/var/lib/chrony:/sbin/nologin +[end] user count is: 19 +``` + +在BEGIN中先定义一个初始的变量count=0,并且打印一段话,然后第二个{}中是具体执行的语句。最后在END中定义结束的操作,打印count的值..... + +```bash +awk 'BEGIN {一开始执行的内容,只执行一遍}{反复执行的内容} END{最后执行的内容,仅一遍}' /etc/passwd +``` + +### 6.4.2 实例二:统计某个文件夹下的文件占用的字节数 + +```bash +[root@localhost ~]# ll | awk 'BEGIN {size=0} {size=size+$5} END{print "size is ",size}' +size is 2226 +[root@localhost ~]# ll | awk 'BEGIN {size=0} {size=size+$5} END{print "size is ",size/1024/1024,"M"}' +size is 0.00212288 M +``` + +## 6.5 awk运算符 + +| 运算符 | 描述 | +| -------------------------- | -------------------------------- | +| **赋值运算符** | | +| = += -= \*= /= %= ^= \*\*= | 赋值语句 | +| **逻辑运算符** | | +| ¦¦ | 逻辑或 | +| && | 逻辑与 | +| **正则运算符** | | +| ~ !~ | 匹配正则表达式和不匹配正则表达式 | +| **关系运算符** | | +| < <= > >= != == | 关系运算符 | +| **算数运算符** | | +| + - | 加,减 | +| \* / & | 乘,除与求余 | +| + - ! | 一元加,减和逻辑非 | +| ^ \*\*\* | 求幂 | +| ++ -- | 增加或减少,作为前缀或后缀 | +| **其他运算符** | | +| $ | 字段引用 | +| 空格 | 字符串链接符 | +| ?: | 三目运算符 | +| ln | 数组中是否存在某键值 | + +### 6.5.1 案例 + +1. awk 赋值运算符:a+=5;等价于: a=a+5;其他同类 + +```bash +[root@localhost ~]# awk 'BEGIN{a=5;a+=5;print a}' +10 +``` + +2. awk逻辑运算符:判断表达式 a>2&&b>1为真还是为假,后面的表达式同理 + +```bash +[root@localhost ~]# awk 'BEGIN{a=1;b=2;print (a>2 && b>1,a=1 || b>1)}' +0 1 +``` + +3. awk正则运算符: + +```bash +[root@localhost ~]# awk 'BEGIN{a="100testaa";if(a~/100/) {print "OK"}else {print "NO"}}' +OK +``` + +4. 关系运算符: + + 如: > < 可以作为字符串比较,也可以用作数值比较,关键看操作数如果是字符串就会转换为字符串比较。两个都为数字 才转为数值比较。字符串比较:按照ascii码顺序比较。 + +```bash +# 如果是字符的话,就会按照ASCII码的顺序进行比较 +[root@localhost ~]# awk 'BEGIN{a=11;if(a>=9){print"OK"}}' +OK +[root@localhost ~]# awk 'BEGIN{a;if(a>=b){print"OK"}}' +OK +``` + +5. 算术运算符: + + 说明:所有用作算术运算符进行操作,操作数自动转为数值,所有非数值都变为0 + +```bash +[root@localhost ~]# awk 'BEGIN{a="b";print a++,++a}' +0 2 +[root@localhost ~]# awk 'BEGIN{a="20b4";print a++,++a}' +20 22 +``` + +这里的a++ , ++a与其他语言一样:a++是先赋值加++;++a是先++再赋值 + +6. 三目运算符`?` + +```bash +[root@localhost ~]# awk 'BEGIN{a="b";print a=="b"?"ok":"err"}' +ok +[root@localhost ~]# awk 'BEGIN{a="b";print a=="c"?"ok":"err"}' +err +``` + +## 6.6 常用 awk 内置变量 + +| 变量名 | 属性 | +| ------ | ----------------------------------- | +| $0 | 当前记录 | +| \$1~$n | 当前记录的第n个字段 | +| FS | 输入字段分割符 默认是空格 | +| RS | 输入记录分割符 默认为换行符 | +| NF | 当前记录中的字段个数,就是有多少列 | +| NR | 已经读出的记录数,就是行号,从1开始 | +| OFS | 输出字段分割符 默认也是空格 | +| ORS | 输出的记录分割符 默认为换行符 | + +注:内置变量很多,参阅相关资料 + +1. 字段分隔符 FS FS="\t+" 一个或多个 Tab 分隔 + + +```bash +[root@localhost ~]# cat testfile +aa bb cc +[root@localhost ~]# awk 'BEGIN{FS="\t+"}{print $1,$2,$3}' testfile +aa bb cc +``` + +2. FS="[[:space:]+]" 一个或多个空白空格,默认的,匹配到不符合的就停止 + +```bash +[root@localhost ~]# cat testfile +aa bb cc +[root@localhost ~]# awk 'BEGIN{FS="[[:space:]+]"}{print $1,$2,$3}' testfile +aa bb +[root@localhost ~]# awk -F [[:space:]+] '{print $1,$2}' testfile +aa bb +``` + +3. FS="[" "]+" 以一个或多个空格分隔 + +```bash +[root@localhost ~]# cat testfile +aa bb cc +[root@localhost ~]# awk -F [" "]+ '{print $1,$2,$3}' testfile +aa bb cc +``` + +4. 字段数量 NF:显示满足用:分割,并且有8个字段的 + +```bash +[root@localhost ~]# cat testfile +bin:x:1:1:bin:/bin:/sbin/nologin:888 +bin:x:1:1:bin:/bin:/sbin/nologin +[root@localhost ~]# awk -F ":" 'NF==8{print $0}' testfile +bin:x:1:1:bin:/bin:/sbin/nologin:888 +``` + +5. 记录数量 NR (行号) + +```bash +[root@localhost ~]# ifconfig ens33 | awk -F [" "]+ 'NR==2{print $3}' +192.168.88.10 +``` + +6. RS 记录分隔符变量 + + +```bash +# 写法一(命令行) +[root@localhost ~]# awk 'BEGIN{FS=":";RS="\n"}{ print $1","$2","$3}' testfile +bin,x,1 +bin,x,1 + +# 写法二(awk脚本) +[root@localhost ~]# cat awk.txt +#!/bin/awk +BEGIN { + FS=":" + RS="\n" +} +{ + print $1","$2","$3 +} +[root@localhost ~]# awk -f awk.txt testfile +bin,x,1 +bin,x,1 +``` + +7. OFS:输出字段分隔符 + +```bash +[root@localhost ~]# awk 'BEGIN{FS=":";OFS="#"}{print $1,$2,$3}' testfile +bin#x#1 +bin#x#1 +``` + +8. ORS:输出记录分隔符 + +```bash +[root@localhost ~]# awk 'BEGIN{FS=":";ORS="\n\n"}{print $1,$2,$3}' testfile +bin x 1 + +bin x 1 +``` + +## 6.7 awk正则 + + + +| 元字符 | 功能 | 示例 | 解释 | +| :-------------: | :---------------------------------------------- | ------------------------------------ | ------------------------------------------------------------ | +| ^ | 首航定位符 | /^root/ | 匹配所有以root开头的行 | +| $ | 行尾定位符 | /root$/ | 匹配所有以root结尾的行 | +| . | 匹配任意单个字符 | /r..t/ | 匹配字母r,然后两个任意字符,再以t结尾的行 | +| * | 匹配0个或多个前导字符(包括回车) | /a*ool/ | 匹配0个或多个a之后紧跟着ool的行,比如ool,aaaaool等 | +| + | 匹配1个或多个前导字符 | /a+b/ | ab, aaab | +| ? | 匹配0个或1个前导字符 | /a?b/ | b,ab | +| [] | 匹配指定字符组内的任意一个字符 | /^[abc]/ | 匹配以a或b或c开头的行 | +| \[^\] | 匹配不在指定字符组内任意一个字符 | /\^[\^abc]/ | 匹配不以字母a或b或c开头的行 | +| () | 子表达式组合 | /(rool)+/ | 表示一个或多个rool组合,当有一些字符需要组合时,使用括号括起来 | +| ¦ | 或者的意思 | /(root)\¦B/ | 匹配root或者B的行 | +| \ | 转义字符 | /a\/\// | 匹配a// | +| \~,!\~ | 匹配,不匹配的条件语句 | $1~/root/ | 匹配第一个字段包含字符root的所有记录 | +| x{m}x{m,}x{m,n} | x重复m次x重复至少m次x重复至少m次,但是不超过n次 | /(root){3}//(root){3,}//(root){3,6}/ | | + +awk使用正则表达式 + +1. 规则表达式 + + `awk '/REG/{action} ' file` ,其中/REG/为正则表达式,可以将满足条件的记录送入到:action 进行处理 + +```bash +[root@localhost ~]# awk '/root/{print$0}' /etc/passwd +root:x:0:0:root:/root:/bin/bash +operator:x:11:0:operator:/root:/sbin/nologin +[root@localhost ~]# awk -F ":" '$5~/root/{print$0}' /etc/passwd +root:x:0:0:root:/root:/bin/bash +[root@localhost ~]# ifconfig ens33 | awk 'BEGIN{FS="[[:space:]:]+"} NR==2{print$3}' +192.168.88.10 +``` + +2. 布尔表达式 + + `awk '布尔表达式{action}' file` 仅当对前面的布尔表达式求值为真时, awk 才执行代码块 + +```bash +[root@localhost ~]# awk -F: '$1=="root"{print$0}' /etc/passwd +root:x:0:0:root:/root:/bin/bash +[root@localhost ~]# awk -F: '($1=="root")&&($5=="root"){print$0}' /etc/passwd +root:x:0:0:root:/root:/bin/bash +``` + +## 6.8 awk的if、循环、和数组 + +### 6.8.1 if 条件判断 + +awk 提供了非常好的类似于 C 语言的 if 语句 + +```bash +{ + if ($1=="foo"){ + if ($2=="foo"){ + print"uno" + }else{ + print"one" + } + }elseif($1=="bar"){ + print "two" + }else{ + print"three" + } +} +``` + +还可以转换为: + +```bash +{ +  if ( $0 !~ /matchme/ ) { +    print $1 $3 $4 +  } +} +``` + +### 6.8.2 while 循环 + +awk 的 while 循环结构,它等同于相应的 C 语言 while 循环。 awk 还有"do...while"循环,它在代码块结尾处对条件求值,而不像标准 while 循环那样在开始处求值。 + +它类似于其它语言中的"repeat...until"循环。以下是一个示例: + +do...while 示例: + +```bash +{ + count=1 do { + print "I get printed at least once no matter what" + } while ( count !=1 ) +} +``` + +与一般的 while 循环不同,由于在代码块之后对条件求值, "do...while"循环永远都至少执行一次。换句话说,当第一次遇到普通 while 循环时,如果条件为假,将永远不执行该循环 + +### 6.8.3 for 循环 + +awk 允许创建 for 循环,它就象 while 循环,也等同于 C 语言的 for 循环: + +```bash +for ( initial assignment; comparison; increment ) { + code block +} +``` + +以下是一个简短示例: + +```bash +[root@localhost ~]# cat awk.txt +#!/bin/awk +BEGIN{for ( x=1; x<=4; x++ ) { + print "iteration", x +}} +[root@localhost ~]# awk -f awk.txt +iteration 1 +iteration 2 +iteration 3 +iteration 4 +``` + +**break 和 continue** + +如同 C 语言一样, awk 提供了 break 和 continue 语句。使用这些语句可以更好地控制 awk 的循环结构。 + +**break语句:** + +```bash +[root@localhost ~]# cat awk.txt +#!/bin/awk +BEGIN{ +x=1 +while(1) { + print "iteration",x + if ( x==10 ){ + break + } + x++ +} +} +[root@localhost ~]# awk -f awk.txt +iteration 1 +iteration 2 +iteration 3 +iteration 4 +iteration 5 +iteration 6 +iteration 7 +iteration 8 +iteration 9 +iteration 10 +``` + +**continue 语句:** + +```bash +[root@localhost ~]# cat awk.txt +#!/bin/awk +BEGIN{ +x=1 +while (1) { + if ( x==4 ) { + x++ + continue + } + print "iteration", x + if ( x>20 ) { + break + } + x++ +} +} +[root@localhost ~]# awk -f awk.txt +iteration 1 +iteration 2 +iteration 3 +iteration 5 +iteration 6 +iteration 7 +iteration 8 +iteration 9 +iteration 10 +iteration 11 +iteration 12 +iteration 13 +iteration 14 +iteration 15 +iteration 16 +iteration 17 +iteration 18 +iteration 19 +iteration 20 +iteration 21 +``` + +### 6.8.4 数组 + +AWK 中的数组都是关联数组,数字索引也会转变为字符串索引 + +在awk中,数组叫关联数组,与我们在其它编程语言中的数组有很大的区别。关联数组,简单来说,类似于python语言中的dict、java语言中的map,其下标不再局限于数值型,而可以是字符串,即下标为key,value=array[key]。既然为key,那其下标也不再是有序的啦。 + +```shell +#!/bin/awk +BEGIN{ + cities[1]="beijing" + cities[2]="shanghai" + cities["three"]="guangzhou" + for( c in cities) { + print cities[c] + } + print cities[1] + print cities["1"] + print cities["three"] +} + +awk -f awk.txt +``` + +用 awk 中查看服务器连接状态并汇总 + +```shell +[root@localhost ~]# netstat -an|awk '/^tcp/{++s[$NF]}END{for(a in s)print a,s[a]}' +LISTEN 4 +ESTABLISHED 2 +``` + +## 6.9 常用字符串函数 + +| 函数 | 说明 | +| :-------------- | :---------------------------------- | +| gsub(r,s) | 在整个$0中用s替代r | +| gsub(r,s,t) | 在整个t中用s替代r | +| index(s,t) | 返回s中字符串t的第一位置 | +| length(s) | 返回s长度 | +| match(s,r) | 测试s是否包含匹配r的字符串 | +| split(s,a,fs) | 在fs上将s分成序列a | +| sprint(fmt,exp) | 返回经fmt格式化后的exp | +| sub(r,s) | 用s替代$0中最左边最长的r | +| substr(s,p) | 返回字符串s从p开始的后缀部分 | +| substr(s,p,n) | 返回字符串s从p开始长度为n的后缀部分 | +| tolower(s) | 返回小写字符串 | +| toupper(s) | 返回大写字符串 | + +字符串函数的应用: + +1. 在 info 中查找满足正则表达式, /[0-9]+/ 用”!”替换,并且替换后的值,赋值给 info + +```shell +[root@localhost ~]# awk 'BEGIN{info="this is a test2010test!";gsub(/[0-9]+/,"!",info);print info}' +this is a test!test! +``` + +2. 如果查找到数字则匹配成功返回 ok,否则失败,返回未找到 + +```shell +[root@localhost ~]# awk 'BEGIN{info="this is a test2010test!";print index(info,"test")?"ok":"no found";}' +ok +``` + +3. 从第 4 个 字符开始,截取 10 个长度字符串 + +```shell +[root@localhost ~]# awk 'BEGIN{info="this is a test2010test!";print substr(info,4,10);}' +s is a tes +``` + +4. 分割 info,动态创建数组 tA,awk for …in 循环,是一个无序的循环。 并不是从数组下标1…n 开始 + +```shell +[root@localhost ~]# awk 'BEGIN{info="this is a test";split(info,tA," ");print length(tA);for(k in tA){print k,tA[k];}}' +4 +4 test +1 this +2 is +3 a +``` \ No newline at end of file diff --git a/04.自动化运维/01.文本三剑客/awk.png b/04.自动化运维/01.文本三剑客/awk.png new file mode 100644 index 0000000..acb850e Binary files /dev/null and b/04.自动化运维/01.文本三剑客/awk.png differ diff --git a/04.自动化运维/02.Shell脚本.md b/04.自动化运维/02.Shell脚本.md new file mode 100644 index 0000000..a0e2681 --- /dev/null +++ b/04.自动化运维/02.Shell脚本.md @@ -0,0 +1,1426 @@ +# 1. Shell脚本编程 + +# 2. 简介 + +- Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。 +- Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。(翻译官,帮你翻译命令给内核执行) + +img + +- Linux 的 Shell 种类众多,常见的有: + + - Bourne Shell(/usr/bin/sh或/bin/sh) + - Bourne Again Shell(/bin/bash) + - C Shell(/usr/bin/csh) + - K Shell(/usr/bin/ksh) + - Shell for Root(/sbin/sh) +- 程序编程风格 + + - 过程式:以指令为中心,数据服务于命令 + - 对象式:以数据为中心,命令服务于数据 + - shell是一种过程式编程 +- 过程式编程 + + - 顺序执行 + - 循环执行 + - 选择执行 +- 编程语言分类 + + - 编译型语言 + - 解释型语言(shell是一种解释型语言) + +image.png + +- 运行脚本 + + - 给予执行权限,通过具体的文件路径指定文件执行 + - 直接运行解释器,将脚本作为解释器程序的参数运行 +- bash退出状态码 +- 范围是0-255 + - 脚本中一旦遇到exit命令,脚本会立即终止,终止退出状态取决于exit命令后面的数字 + - 如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态 + +# 3. 变量 + +## 3.1 变量命名 + +- 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。 +- 中间不能有空格,可以使用下划线(_)。 +- 不能使用标点符号。 +- 不能使用 bash 里的关键字(可用 help 命令查看保留关键字)。 + +## 3.2 声明变量 + +访问变量的语法形式为:`${var}` 和 `$var` 。 + +变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,所以推荐加花括号。 + +```bash +#!/bin/bash +word="hello" +echo ${word} +# Output: hello +``` + +## 3.3 只读变量 + +使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。 + +``` +#!/bin/bash +rword="hello" +echo ${rword} +readonly rword +# rword="bye" # 如果放开注释,执行时会报错 +``` + +## 3.4 删除变量 + +```bash +dword="hello" # 声明变量 +echo ${dword} # 输出变量值 +# Output: hello + +unset dword # 删除变量 +echo ${dword} +# Output: (空) +``` + +## 3.5 变量类型 + +- **局部变量** - 局部变量是仅在某个脚本内部有效的变量。它们不能被其他的程序和脚本访问。 +- **环境变量** - 环境变量是对当前 shell 会话内所有的程序或脚本都可见的变量。创建它们跟创建局部变量类似,但使用的是 `export` 关键字,shell 脚本也可以定义环境变量。 + +**常见的环境变量:** + +| 变量 | 描述 | +| --------- | -------------------------------------------------- | +| `$HOME` | 当前用户的用户目录 | +| `$PATH` | 用分号分隔的目录列表,shell 会到这些目录中查找命令 | +| `$PWD` | 当前工作目录 | +| `$RANDOM` | 0 到 32767 之间的整数 | +| `$UID` | 数值类型,当前用户的用户 ID | +| `$PS1` | 主要系统输入提示符 | +| `$PS2` | 次要系统输入提示符 | + +- **本地变量** - 生效范围仅为当前shell进程;(其他shell,当前的子sehll进程均无效) + + - 变量赋值:name = “value” + +- **位置变量** - shell 脚本中用来引用命令行参数的特殊变量。当你运行一个 shell 脚本时,可以在命令行上传递参数,这些参数可以在脚本中使用位置变量引用。 + + 位置变量包括以下几种: + + 1. `$0`: 表示脚本本身的名称。 + 2. `$1`, `$2`, `$3`, ..., `$n`: 分别表示第1个、第2个、第3个...第n个参数。 + 3. `$#`: 表示传递给脚本的参数个数。 + 4. `$*`: 表示所有参数,将所有参数当作一个整体。 + 5. `$@`: 表示所有参数,但是每个参数都是独立的。 + +```bash +[root@localhost ~]# cat hello.sh +#!/bin/bash +echo "Script name: $0" +echo "First argument: $1" +echo "Second argument: $2" +echo "Total arguments: $#" +echo "All arguments: $*" +echo "All arguments (separately): $@" + +[root@localhost ~]# ./hello.sh world 2023 +``` + +案例:统计给出指定文件的行数 + +```bash +[root@localhost ~]# cat hello.sh +#!/bin/bash +linecount="$(wc -l /etc/passwd | awk -F" " '{print $1}')" +echo "This file have ${linecount} lines" + +[root@localhost ~]# bash hello.sh +This file have 21 lines +``` + +# 4. 字符串 + +shell 字符串可以用单引号 `' '`,也可以用双引号 `" "`,也可以不用引号。 + +- 单引号的特点 + - 单引号里不识别变量 + - 单引号里不能出现单独的单引号(使用转义符也不行),但可成对出现,作为字符串拼接使用。 +- 双引号的特点 + - 双引号里识别变量 + - 双引号里可以出现转义字符 + +综上,推荐使用双引号。 + +## 4.1 字符串的拼接 + +```bash +# 使用单引号拼接 +name1='white' +str1='hello, '${name1}'' +str2='hello, ${name1}' +echo ${str1}_${str2} +# Output: +# hello, white_hello, ${name1} + +# 使用双引号拼接 +name2="black" +str3="hello, "${name2}"" +str4="hello, ${name2}" +echo ${str3}_${str4} +# Output: +# hello, black_hello, black +``` + +## 4.2 获取字符串的长度 + +```bash +text="12345" +echo ${#text} + +# Output: +# 5 +``` + +## 4.3 截取子字符串 + +`${variable:start:length}` + +```bash +text="12345" +echo ${text:2:2} + +# Output: +# 34 +``` + +# 5. 数组 + +bash 只支持一维数组。 + +数组下标从 0 开始,下标可以是整数或算术表达式,其值应大于或等于 0。 + +## 5.1 创建/访问数组 + +```bash +array_name=(value1 value2 value3 ...) +array_name=([0]=value1 [1]=value2 ...) + +# 案例一 +[root@localhost ~]# cat a.sh +#!/bin/bash +# 创建数组 +fruits=("apple" "banana" "orange") + +# 访问元素 +echo "First fruit: ${fruits[0]}" +echo "All fruits: ${fruits[@]}" + +[root@localhost ~]# bash a.sh +First fruit: apple +All fruits: apple banana orange + +# 案例二 +[root@localhost ~]# cat a.sh +nums=([0]="nls" [1]="18" [2]="teacher") +echo ${nums[1]} + +[root@localhost ~]# bash a.sh +18 +``` + +访问数组中所有的元素: + +```bash +[root@localhost ~]# cat a.sh +nums=([0]="nls" [1]="18" [2]="teacher") +echo ${nums[*]} +echo ${nums[@]} + +[root@localhost ~]# bash a.sh +nls 18 teacher +nls 18 teacher +``` + +## 5.2 获取数组的长度 + +```bash +[root@localhost ~]# cat a.sh +nums=([0]="nls" [1]="18" [2]="teacher") +echo "数组元素个数为: ${#nums[*]}" + +[root@localhost ~]# bash a.sh +数组元素个数为: 3 +``` + +## 5.3 删除元素 + +用`unset`命令来从数组中删除一个元素: + +```bash +[root@localhost ~]# cat a.sh +nums=([0]="nls" [1]="18" [2]="teacher") +echo "数组元素个数为: ${#nums[*]}" +unset nums[0] +echo "数组元素个数为: ${#nums[*]}" + +[root@localhost ~]# bash a.sh +数组元素个数为: 3 +数组元素个数为: 2 +``` + +# 6. 运算符 + +## 6.1 算数运算符 + +下表列出了常用的算术运算符,假定变量 x 为 10,变量 y 为 20: + +| 运算符 | 说明 | 举例 | +| ------ | --------------------------------------------- | ------------------------------ | +| + | 加法 | `expr $x + $y` 结果为 30。 | +| - | 减法 | `expr $x - $y` 结果为 -10。 | +| * | 乘法 | `expr $x * $y` 结果为 200。 | +| / | 除法 | `expr $y / $x` 结果为 2。 | +| % | 取余 | `expr $y % $x` 结果为 0。 | +| = | 赋值 | `x=$y` 将把变量 y 的值赋给 x。 | +| == | 相等。用于比较两个数字,相同则返回 true。 | `[ $x == $y ]` 返回 false。 | +| != | 不相等。用于比较两个数字,不相同则返回 true。 | `[ $x != $y ]` 返回 true。 | + +**注意:**条件表达式要放在方括号之间,并且要有空格,例如: `[$x==$y]` 是错误的,必须写成 `[ $x == $y ]` + +**示例:** + +- expr本身是一个命令,可以直接进行运算 + +```bash +x=10 +y=20 + +echo "x=${x}, y=${y}" + +val=`expr ${x} + ${y}` +echo "${x} + ${y} = $val" + +val=`expr ${x} - ${y}` +echo "${x} - ${y} = $val" + +val=`expr ${x} \* ${y}` +echo "${x} * ${y} = $val" + +val=`expr ${y} / ${x}` +echo "${y} / ${x} = $val" + +val=`expr ${y} % ${x}` +echo "${y} % ${x} = $val" + +if [[ ${x} == ${y} ]] +then + echo "${x} = ${y}" +fi +if [[ ${x} != ${y} ]] +then + echo "${x} != ${y}" +fi + +# Execute: ./operator-demo.sh +# Output: +# x=10, y=20 +# 10 + 20 = 30 +# 10 - 20 = -10 +# 10 * 20 = 200 +# 20 / 10 = 2 +# 20 % 10 = 0 +# 10 != 20 +``` + +### 6.1.1 案例一:计算ID之和 + +计算/etc/passwd文件中第10个用户和第15个用户的ID之和 + +```bash +[root@localhost ~]# cat id.sh +#!/bin/bash +# userid1=$(cat /etc/passwd | sed -n '10p'| awk -F: '{print $3}') +# userid2=$(cat /etc/passwd | sed -n '15p'| awk -F: '{print $3}') +userid1=$(awk -F: '{if (NR==10) print $3}' /etc/passwd) +userid2=$(awk -F: '{if (NR==15) print $3}' /etc/passwd) + +userid_sum=$[$userid1 + $userid2] +echo $userid_sum + +# Execute: +[root@localhost ~]# bash id.sh +92 +``` + +### 6.1.2 案例二:统计文件数量 + +统计/etc/,/var/,/usr/目录下有多少目录和文件 + +```bash +[root@localhost ~]# cat file.sh +#!/bin/bash +sum_etc=$(find /etc | wc -l) +sum_var=$(find /var | wc -l) +sum_usr=$(find /usr | wc -l) + +sum=$[$sum_etc + $sum_var + $sum_usr] +echo $sum + +# Execute: +[root@localhost ~]# bash file.sh +35686 +``` + +## 6.2 关系运算符 + +关系运算符只支持数字,不支持字符串,除非字符串的值是数字。 + +下表列出了常用的关系运算符,假定变量 x 为 10,变量 y 为 20: + +| 运算符 | 说明 | 举例 | +| ------ | ----------------------------------------------------- | ---------------------------- | +| `-eq` | 检测两个数是否相等,相等返回 true。 | `[ $a -eq $b ]`返回 false。 | +| `-ne` | 检测两个数是否相等,不相等返回 true。 | `[ $a -ne $b ]` 返回 true。 | +| `-gt` | 检测左边的数是否大于右边的,如果是,则返回 true。 | `[ $a -gt $b ]` 返回 false。 | +| `-lt` | 检测左边的数是否小于右边的,如果是,则返回 true。 | `[ $a -lt $b ]` 返回 true。 | +| `-ge` | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | `[ $a -ge $b ]` 返回 false。 | +| `-le` | 检测左边的数是否小于等于右边的,如果是,则返回 true。 | `[ $a -le $b ]`返回 true。 | + +**示例:** + +```bash +x=10 +y=20 + +echo "x=${x}, y=${y}" + +if [[ ${x} -eq ${y} ]]; then + echo "${x} -eq ${y} : x 等于 y" +else + echo "${x} -eq ${y}: x 不等于 y" +fi + +if [[ ${x} -ne ${y} ]]; then + echo "${x} -ne ${y}: x 不等于 y" +else + echo "${x} -ne ${y}: x 等于 y" +fi + +if [[ ${x} -gt ${y} ]]; then + echo "${x} -gt ${y}: x 大于 y" +else + echo "${x} -gt ${y}: x 不大于 y" +fi + +if [[ ${x} -lt ${y} ]]; then + echo "${x} -lt ${y}: x 小于 y" +else + echo "${x} -lt ${y}: x 不小于 y" +fi + +if [[ ${x} -ge ${y} ]]; then + echo "${x} -ge ${y}: x 大于或等于 y" +else + echo "${x} -ge ${y}: x 小于 y" +fi + +if [[ ${x} -le ${y} ]]; then + echo "${x} -le ${y}: x 小于或等于 y" +else + echo "${x} -le ${y}: x 大于 y" +fi + +# Execute: ./operator-demo2.sh +# Output: +# x=10, y=20 +# 10 -eq 20: x 不等于 y +# 10 -ne 20: x 不等于 y +# 10 -gt 20: x 不大于 y +# 10 -lt 20: x 小于 y +# 10 -ge 20: x 小于 y +# 10 -le 20: x 小于或等于 y +``` + +### 6.2.1 案例:猜数字小游戏 + +```bash +[root@localhost ~]# vim guess.sh +#!/bin/bash +num2=66 +while true +do + read -p "请输入你要猜的数字:" num1 + if [ $num1 -gt $num2 ];then + echo "你猜大了" + elif [ $num1 -lt $num2 ];then + echo "你猜小了" + else + echo "你猜对了" + break + fi +done + +# Execute: +[root@localhost ~]# bash guess.sh +请输入你要猜的数字:60 +你猜小了 +请输入你要猜的数字:66 +你猜对了 +``` + +## 6.3 字符串运算符 + +下表列出了常用的字符串运算符,假定变量 a 为 "abc",变量 b 为 "efg": + +| 运算符 | 说明 | 举例 | +| ------ | ------------------------------------------ | -------------------------- | +| `=` | 检测两个字符串是否相等,相等返回 true。 | `[ $a = $b ]` 返回 false。 | +| `!=` | 检测两个字符串是否相等,不相等返回 true。 | `[ $a != $b ]` 返回 true。 | +| `-z` | 检测字符串长度是否为 0,为 0 返回 true。 | `[ -z $a ]` 返回 false。 | +| `-n` | 检测字符串长度是否为 0,不为 0 返回 true。 | `[ -n $a ]` 返回 true。 | +| `str` | 检测字符串是否为空,不为空返回 true。 | `[ $a ]` 返回 true。 | + +示例: + +```bash +x="abc" +y="xyz" + + +echo "x=${x}, y=${y}" + +if [[ ${x} = ${y} ]]; then + echo "${x} = ${y} : x 等于 y" +else + echo "${x} = ${y}: x 不等于 y" +fi + +if [[ ${x} != ${y} ]]; then + echo "${x} != ${y} : x 不等于 y" +else + echo "${x} != ${y}: x 等于 y" +fi + +if [[ -z ${x} ]]; then + echo "-z ${x} : 字符串长度为 0" +else + echo "-z ${x} : 字符串长度不为 0" +fi + +if [[ -n "${x}" ]]; then + echo "-n ${x} : 字符串长度不为 0" +else + echo "-n ${x} : 字符串长度为 0" +fi + +if [[ ${x} ]]; then + echo "${x} : 字符串不为空" +else + echo "${x} : 字符串为空" +fi + +# Execute: ./operator-demo5.sh +# Output: +# x=abc, y=xyz +# abc = xyz: x 不等于 y +# abc != xyz : x 不等于 y +# -z abc : 字符串长度不为 0 +# -n abc : 字符串长度不为 0 +# abc : 字符串不为空 +``` + +## 6.4 逻辑运算符 + +以下介绍 Shell 的逻辑运算符,假定变量 x 为 10,变量 y 为 20: + +| 运算符 | 说明 | 举例 | +| ------ | ---------- | ----------------------------------------------- | +| `&&` | 逻辑的 AND | `[[ ${x} -lt 100 && ${y} -gt 100 ]]` 返回 false | +| `||` | 逻辑的 OR | `[[ ${x} -lt 100 && ${y} -gt 100 ]]`返回 true | + +示例: + +```bash +x=10 +y=20 + +echo "x=${x}, y=${y}" + +if [[ ${x} -lt 100 && ${y} -gt 100 ]] +then + echo "${x} -lt 100 && ${y} -gt 100 返回 true" +else + echo "${x} -lt 100 && ${y} -gt 100 返回 false" +fi + +if [[ ${x} -lt 100 || ${y} -gt 100 ]] +then + echo "${x} -lt 100 || ${y} -gt 100 返回 true" +else + echo "${x} -lt 100 || ${y} -gt 100 返回 false" +fi + +# Execute: ./operator-demo4.sh +# Output: +# x=10, y=20 +# 10 -lt 100 && 20 -gt 100 返回 false +# 10 -lt 100 || 20 -gt 100 返回 true +``` + +## 6.5 布尔运算符 + +下表列出了常用的布尔运算符,假定变量 x 为 10,变量 y 为 20: + +| 运算符 | 说明 | 举例 | +| ------ | --------------------------------------------------- | ------------------------------------------ | +| `!` | 非运算,表达式为 true 则返回 false,否则返回 true。 | `[ ! false ]` 返回 true。 | +| `-o` | 或运算,有一个表达式为 true 则返回 true。 | `[ $a -lt 20 -o $b -gt 100 ]` 返回 true。 | +| `-a` | 与运算,两个表达式都为 true 才返回 true。 | `[ $a -lt 20 -a $b -gt 100 ]` 返回 false。 | + +示例: + +```bash +x=10 +y=20 + +echo "x=${x}, y=${y}" + +if [[ ${x} != ${y} ]]; then + echo "${x} != ${y} : x 不等于 y" +else + echo "${x} != ${y}: x 等于 y" +fi + +if [[ ${x} -lt 100 && ${y} -gt 15 ]]; then + echo "${x} 小于 100 且 ${y} 大于 15 : 返回 true" +else + echo "${x} 小于 100 且 ${y} 大于 15 : 返回 false" +fi + +if [[ ${x} -lt 100 || ${y} -gt 100 ]]; then + echo "${x} 小于 100 或 ${y} 大于 100 : 返回 true" +else + echo "${x} 小于 100 或 ${y} 大于 100 : 返回 false" +fi + +if [[ ${x} -lt 5 || ${y} -gt 100 ]]; then + echo "${x} 小于 5 或 ${y} 大于 100 : 返回 true" +else + echo "${x} 小于 5 或 ${y} 大于 100 : 返回 false" +fi + +# Execute: ./operator-demo3.sh +# Output: +# x=10, y=20 +# 10 != 20 : x 不等于 y +# 10 小于 100 且 20 大于 15 : 返回 true +# 10 小于 100 或 20 大于 100 : 返回 true +# 10 小于 5 或 20 大于 100 : 返回 false +``` + +## 6.6 文件测试运算符 + +文件测试运算符用于检测 Unix 文件的各种属性。 + +属性检测描述如下: + +| 操作符 | 说明 | 举例 | +| ------- | ------------------------------------------------------------ | --------------------------- | +| -b file | 检测文件是否是块设备文件,如果是,则返回 true。 | `[ -b $file ]` 返回 false。 | +| -c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | `[ -c $file ]` 返回 false。 | +| -d file | 检测文件是否是目录,如果是,则返回 true。 | `[ -d $file ]` 返回 false。 | +| -f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | `[ -f $file ]` 返回 true。 | +| -g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | `[ -g $file ]` 返回 false。 | +| -k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | `[ -k $file ]`返回 false。 | +| -p file | 检测文件是否是有名管道,如果是,则返回 true。 | `[ -p $file ]` 返回 false。 | +| -u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | `[ -u $file ]` 返回 false。 | +| -r file | 检测文件是否可读,如果是,则返回 true。 | `[ -r $file ]` 返回 true。 | +| -w file | 检测文件是否可写,如果是,则返回 true。 | `[ -w $file ]` 返回 true。 | +| -x file | 检测文件是否可执行,如果是,则返回 true。 | `[ -x $file ]` 返回 true。 | +| -s file | 检测文件是否为空(文件大小是否大于 0),不为空返回 true。 | `[ -s $file ]` 返回 true。 | +| -e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | `[ -e $file ]` 返回 true。 | + +**⌨️ 『示例源码』** [operator-demo6.sh](https://github.com/dunwu/os-tutorial/blob/master/codes/shell/demos/operator/operator-demo6.sh) + +```bash +file="/etc/hosts" + +if [[ -r ${file} ]]; then + echo "${file} 文件可读" +else + echo "${file} 文件不可读" +fi +if [[ -w ${file} ]]; then + echo "${file} 文件可写" +else + echo "${file} 文件不可写" +fi +if [[ -x ${file} ]]; then + echo "${file} 文件可执行" +else + echo "${file} 文件不可执行" +fi +if [[ -f ${file} ]]; then + echo "${file} 文件为普通文件" +else + echo "${file} 文件为特殊文件" +fi +if [[ -d ${file} ]]; then + echo "${file} 文件是个目录" +else + echo "${file} 文件不是个目录" +fi +if [[ -s ${file} ]]; then + echo "${file} 文件不为空" +else + echo "${file} 文件为空" +fi +if [[ -e ${file} ]]; then + echo "${file} 文件存在" +else + echo "${file} 文件不存在" +fi + +# Execute: ./operator-demo6.sh +# Output:(根据文件的实际情况,输出结果可能不同) +# /etc/hosts 文件可读 +# /etc/hosts 文件可写 +# /etc/hosts 文件不可执行 +# /etc/hosts 文件为普通文件 +# /etc/hosts 文件不是个目录 +# /etc/hosts 文件不为空 +# /etc/hosts 文件存在 +``` + +# 7. 用户交互read + +## 7.1 常用选项 + +| 选项 | 描述 | +| :--- | :------------------------- | +| `-p` | 在读取输入之前显示提示信息 | +| `-n` | 限制输入的字符数 | +| `-s` | 隐藏用户输入 | +| `-a` | 将输入存储到数组变量中 | +| `-d` | 指定用于终止输入的分隔符 | +| `-t` | 设置超时时间(以秒为单位) | +| `-e` | 允许使用 Readline 编辑键 | +| `-i` | 设置默认值 | + +示例: + +```bash +#!/bin/bash +read -p "input you name:" name +echo $name + +# Output: +nls +``` + +## 7.2 案例:计算器 + +```bash +#!/bin/bash + +echo "Enter the first number:" +read num1 +echo "Enter the second number:" +read num2 + +echo "The sum is: $((num1 + num2))" +echo "The difference is: $((num1 - num2))" +echo "The product is: $((num1 * num2))" +echo "The quotient is: $((num1 / num2))" + +# Output: +[root@localhost ~]# bash read.sh +Enter the first number: +10 +Enter the second number: +10 +The sum is: 20 +The difference is: 0 +The product is: 100 +The quotient is: 1 +``` + +# 8. 控制语句 + +## 8.1 条件语句 + +跟其它程序设计语言一样,Bash 中的条件语句让我们可以决定一个操作是否被执行。结果取决于一个包在`[[ ]]`里的表达式。 + +由`[[ ]]`(`sh`中是`[ ]`)包起来的表达式被称作 **检测命令** 或 **基元**。这些表达式帮助我们检测一个条件的结果 + +1. `if` 语句 + +`if`在使用上跟其它语言相同。如果中括号里的表达式为真,那么`then`和`fi`之间的代码会被执行。`fi`标志着条件代码块的结束。 + +```bash +# 写成一行 +if [[ 1 -eq 1 ]]; then echo "1 -eq 1 result is: true"; fi +# Output: 1 -eq 1 result is: true + +# 写成多行 +if [[ "abc" -eq "abc" ]] +then + echo ""abc" -eq "abc" result is: true" +fi +# Output: abc -eq abc result is: true +``` + +2. `if else` 语句 + +同样,我们可以使用`if..else`语句,例如: + +```bash +if [[ 2 -ne 1 ]]; then + echo "true" +else + echo "false" +fi +# Output: true +``` + +2. `if elif else` 语句 + +有些时候,`if..else`不能满足我们的要求。别忘了`if..elif..else`,使用起来也很方便。 + +```bash +x=10 +y=20 +if [[ ${x} > ${y} ]]; then + echo "${x} > ${y}" +elif [[ ${x} < ${y} ]]; then + echo "${x} < ${y}" +else + echo "${x} = ${y}" +fi +# Output: 10 < 20 +``` + +## 8.2 循环语句 + +循环其实不足为奇。跟其它程序设计语言一样,bash 中的循环也是只要控制条件为真就一直迭代执行的代码块。Bash 中有四种循环:`for`,`while`,`until`和`select`。 + +### 8.2.1 for循环 + +`for`与 C 语言中非常像。看起来是这样: + +```bash +for arg in elem1 elem2 ... elemN +do + ### 语句 +done +``` + +在每次循环的过程中,`arg`依次被赋值为从`elem1`到`elemN`。这些值还可以是通配符或者[大括号扩展](https://github.com/denysdovhan/bash-handbook/blob/master/translations/zh-CN/README.md#大括号扩展)。 + +当然,我们还可以把`for`循环写在一行,但这要求`do`之前要有一个分号,就像下面这样: + +```bash +for i in {1..5}; do echo $i; done +``` + +还有,如果你觉得`for..in..do`对你来说有点奇怪,那么你也可以像 C 语言那样使用`for`,比如: + +```bash +for (( i = 0; i < 10; i++ )); do + echo $i +done +``` + +当我们想对一个目录下的所有文件做同样的操作时,`for`就很方便了。举个例子,如果我们想把所有的`.bash`文件移动到`script`文件夹中,并给它们可执行权限,我们的脚本可以这样写: + +```bash +DIR=/home/zp +for FILE in ${DIR}/*.sh; do + mv "$FILE" "${DIR}/scripts" +done +# 将 /home/zp 目录下所有 sh 文件拷贝到 /home/zp/scripts +``` + +#### 8.2.1.1 案例一:创建用户 + +创建用户user1‐user10家目录,并且在user1‐10家目录下创建1.txt‐10.txt + +```bash +[root@localhost ~]# cat adduser.sh +#!/bin/bash +for i in {1..10} +do + mkdir /home/user$i + for j in $(seq 10) + do + touch /home/user$i/$j.txt + done +done + +# Output: +[root@localhost ~]# bash adduser.sh +[root@localhost ~]# ls /home/ +user01 user10 user3 user5 user7 user9 +user1 user2 user4 user6 user8 +[root@localhost ~]# ls /home/user1 +10.txt 2.txt 4.txt 6.txt 8.txt +1.txt 3.txt 5.txt 7.txt 9.txt +``` + +#### 8.2.1.2 案例二:检查磁盘占用 + +列出/var/目录下各个子目录占用磁盘大小 + +```bash +[root@localhost ~]# cat size.sh +#!/bin/bash +for i in `ls /var/` +do + path="/var/$i" + if [ -d $path ];then + du -sh $path + fi +done + +# Output: +[root@localhost ~]# bash size.sh +0 /var/adm +654M /var/cache +0 /var/crash +8.0K /var/db +0 /var/empty +0 /var/games +0 /var/gopher +0 /var/kerberos +54M /var/lib +0 /var/local +0 /var/lock +3.2M /var/log +0 /var/mail +0 /var/nis +0 /var/opt +0 /var/preserve +0 /var/run +16K /var/spool +0 /var/tmp +0 /var/www +0 /var/yp +``` + +#### 8.2.1.3 案例三:测试连通性 + +批量测试地址是否在线 + +```bash +[root@localhost ~]# cat ping.sh +#!/bin/bash +for i in {1..10} +do + ping -c 2 192.168.88.$i &> /dev/null + if [ $? -eq 0 ];then + echo 192.168.88.$i >> /root/host.txt + fi +done + +# Output: +[root@localhost ~]# cat host.txt +192.168.88.1 +192.168.88.2 +192.168.88.10 +``` + +### 8.2.2 while循环 + +`while`循环检测一个条件,只要这个条件为 *真*,就执行一段命令。被检测的条件跟`if..then`中使用的[基元](https://github.com/denysdovhan/bash-handbook/blob/master/translations/zh-CN/README.md#基元和组合表达式)并无二异。因此一个`while`循环看起来会是这样: + +```bash +while 循环条件 +do + ### 语句 +done +``` + +#### 8.2.2.1 案例一:数字累加 + +计算1+2+..10的总和 + +```bash +[root@localhost ~]# cat sum.sh +#!/bin/bash +i=1 +sum=0 +while [ $i -lt 10 ] +do + let sum+=$i + let i++ +done +echo $sum + +# Output: +[root@localhost ~]# bash sum.sh +45 +``` + +#### 8.2.2.2 案例二:猜数字小游戏 + +加上随机数 + +```bash +[root@localhost ~]# cat guess.sh +#!/bin/bash +num2=$((RANDOM%100+1)) +while true +do + read -p "请输入你要猜的数字:" num1 + if [ $num1 -gt $num2 ];then + echo "你猜大了" + elif [ $num1 -lt $num2 ];then + echo "你猜小了" + else + echo "你猜对了" + break + fi +done + +# Output: +[root@localhost ~]# bash guess.sh +请输入你要猜的数字:50 +你猜小了 +请输入你要猜的数字:70 +你猜小了 +请输入你要猜的数字:90 +你猜大了 +请输入你要猜的数字:80 +你猜大了 +``` + +### 8.2.3 until循环 + +`until`循环跟`while`循环正好相反。它跟`while`一样也需要检测一个测试条件,但不同的是,只要该条件为 *假* 就一直执行循环: + +```bash +until 条件测试 +do + ##循环体 +done +``` + +示例: + +```bash +[root@localhost ~]# cat until.sh +x=0 +until [ ${x} -ge 5 ]; do + echo ${x} + x=`expr ${x} + 1` +done + +# Output +[root@localhost ~]# bash until.sh +0 +1 +2 +3 +4 +``` + +### 8.2.4 退出循环 + +`break` 和 `continue` + +如果想提前结束一个循环或跳过某次循环执行,可以使用 shell 的`break`和`continue`语句来实现。它们可以在任何循环中使用。 + +> `break`语句用来提前结束当前循环。 +> +> `continue`语句用来跳过某次迭代。 + +示例: + +```bash +# 查找 10 以内第一个能整除 2 和 3 的正整数 +i=1 +while [[ ${i} -lt 10 ]]; do + if [[ $((i % 3)) -eq 0 ]] && [[ $((i % 2)) -eq 0 ]]; then + echo ${i} + break; + fi + i=`expr ${i} + 1` +done +# Output: 6 +``` + +示例: + +```bash +# 打印10以内的奇数 +for (( i = 0; i < 10; i ++ )); do + if [[ $((i % 2)) -eq 0 ]]; then + continue; + fi + echo ${i} +done +# Output: +# 1 +# 3 +# 5 +# 7 +# 9 +``` + +# 9. 函数 + +## 9.1 函数定义 + +bash 函数定义语法如下: + +```bash +[ function ] funname [()] { + action; + [return int;] +} +``` + +```bash +function FUNNAME(){ +函数体 +返回值 +} +FUNNME #调用函数 +``` + +> 💡 说明: +> +> 1. 函数定义时,`function` 关键字可有可无。 +> 2. 函数返回值 - return 返回函数返回值,返回值类型只能为整数(0-255)。如果不加 return 语句,shell 默认将以最后一条命令的运行结果,作为函数返回值。 +> 3. 函数返回值在调用该函数后通过 `$?` 来获得。 +> 4. 所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至 shell 解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。 + +示例: + +```bash +[root@localhost ~]# cat func.sh +#!/bin/bash +func(){ + echo "这是我的第一个函数" +} + +echo "------函数执行之前-------" +func +echo "------函数执行之前-------" + +# Output: +[root@localhost ~]# bash func.sh +------函数执行之前------- +这是我的第一个函数 +------函数执行之前------- +``` + +## 9.2 返回值 + +示例: + +```bash +func(){ + echo "这个函数会对输入的两个数字进行相加运算..." + echo "输入第一个数字: " + read aNum + echo "输入第二个数字: " + read anotherNum + echo "两个数字分别为 $aNum 和 $anotherNum !" + return $(($aNum+$anotherNum)) +} +func +echo "输入的两个数字之和为 $? !" +#可以使用$?来获取返回值 +``` + +## 9.3 函数参数 + +**位置参数**是在调用一个函数并传给它参数时创建的变量 + +| 变量 | 描述 | +| -------------- | ------------------------------ | +| `$0` | 脚本名称 | +| `$1 … $9` | 第 1 个到第 9 个参数列表 | +| `${10} … ${N}` | 第 10 个到 N 个参数列表 | +| `$*` or `$@` | 除了`$0`外的所有位置参数 | +| `$#` | 不包括`$0`在内的位置参数的个数 | +| `$FUNCNAME` | 函数名称(仅在函数内部有值) | + +示例: + +```bash +#!/bin/bash + +x=0 +if [[ -n $1 ]]; then + echo "第一个参数为:$1" + x=$1 +else + echo "第一个参数为空" +fi + +y=0 +if [[ -n $2 ]]; then + echo "第二个参数为:$2" + y=$2 +else + echo "第二个参数为空" +fi + +paramsFunction(){ + echo "函数第一个入参:$1" + echo "函数第二个入参:$2" +} +paramsFunction ${x} ${y} +``` + +执行结果: + +```bash +[root@localhost ~]# vim func1.sh +[root@localhost ~]# bash func1.sh +第一个参数为空 +第二个参数为空 +函数第一个入参:0 +函数第二个入参:0 +[root@localhost ~]# bash func1.sh 10 20 +第一个参数为:10 +第二个参数为:20 +函数第一个入参:10 +函数第二个入参:20 +``` + +## 9.4 函数处理参数 + +另外,还有几个特殊字符用来处理参数: + +| 参数处理 | 说明 | +| -------- | ------------------------------------------------ | +| `$#` | 返回参数个数 | +| `$*` | 返回所有参数 | +| `$ | 参数处理 | +| -------- | ------------------------------------------------ | + +| `$!` | 后台运行的最后一个进程的 ID 号 | +| `$@` | 返回所有参数 | +| `$-` | 返回 Shell 使用的当前选项,与 set 命令功能相同。 | +| `$?` | 函数返回值 | + +```bash +runner() { + return 0 +} + +name=zp +paramsFunction(){ + echo "函数第一个入参:$1" + echo "函数第二个入参:$2" + echo "传递到脚本的参数个数:$#" + echo "所有参数:" + printf "+ %s\n" "$*" + echo "脚本运行的当前进程 ID 号:$$" + echo "后台运行的最后一个进程的 ID 号:$!" + echo "所有参数:" + printf "+ %s\n" "$@" + echo "Shell 使用的当前选项:$-" + runner + echo "runner 函数的返回值:$?" +} +paramsFunction 1 "abc" "hello, \"zp\"" +# Output: +# 函数第一个入参:1 +# 函数第二个入参:abc +# 传递到脚本的参数个数:3 +# 所有参数: +# + 1 abc hello, "zp" +# 脚本运行的当前进程 ID 号:26400 +# 后台运行的最后一个进程的 ID 号: +# 所有参数: +# + 1 +# + abc +# + hello, "zp" +# Shell 使用的当前选项:hB +# runner 函数的返回值:0 +``` + +# 10. 实际案例 + +## 10.1 案例一:开机显示系统信息脚本 + +```bash +[root@localhost ~]# cat os.sh +#!/bin/bash +yum install -y net-tools &> /dev/null +wangka=`ip a | grep ens | head -1 | cut -d: -f2` +System=$(hostnamectl | grep System | awk '{print $3,$4,$5}') +Kernel=$(hostnamectl | grep Kernel | awk -F: '{print $2}') +Virtualization=$(hostnamectl | grep Virtualization| awk '{print $2}') +Statichostname=$(hostnamectl | grep Static|awk -F: '{print $2}') +Ens32=$(ifconfig $wangka | awk 'NR==2 {print $2}') +Lo=$(ifconfig lo0 | awk 'NR==2 {print $2}') +NetworkIp=$(curl -s icanhazip.com) +echo "当前系统版本是:$System" +echo "当前系统内核是:$Kernel" +echo "当前虚拟平台是:$Virtualization" +echo "当前主机名是:$Statichostname" +echo "当前网卡$wangka的地址是:$Ens32" +echo "当前lo0接口的地址是:$Lo" +echo "当前公网地址是:$NetworkIp" + +# Output: +[root@localhost ~]# bash os.sh +当前系统版本是:CentOS Linux 7 +当前系统内核是: Linux 3.10.0-957.el7.x86_64 +当前虚拟平台是:vmware +当前主机名是: localhost +当前网卡 ens33的地址是:192.168.88.10 +当前lo0接口的地址是:127.0.0.1 +当前公网地址是:153.101.189.87 + +``` + +## 10.2 案例二:监控httpd进程 + +**需求:** + +1.每隔10s监控httpd的进程数,若进程数大于等于500,则自动重启Apache服务,并检测服务是否重启成功 + +2.若未成功则需要再次启动,若重启5次依旧没有成功,则向管理员发送告警邮件(使用echo输出已发送即可),并退出检测 + +3.如果启动成功,则等待1分钟后再次检测httpd进程数,若进程数正常,则恢复正常检测(10s一次),否则放弃重启并向管理员发送告警邮件,并退出检测 + +```bash +[root@localhost ~]# cat httpd.sh +#!/bin/bash +function check_httpd_process_number() { +process_num=`ps -ef | grep httpd| wc -l` + +if [ $process_num -gt 50 ];then + systemctl restart httpd &> /dev/null + # 重启五次httpd确保服务启动 + systemctl status httpd &> /dev/null + if [ $? -ne 0 ];then + num_restart_httpd=0 + while true;do + let num_restart_httpd++ + systemctl restart httpd &> /dev/null + systemctl status httpd &> /dev/null + [ $? -eq 0 ] && break + [ $num_restart_httpd -eq 6 ] && break + done + fi + + # 判断重启服务的结果 + systemctl status httpd &> /dev/null + [ $? -ne 0 ] && echo "apache未正常重启,已发送邮件给管理员" && return 1 + sleep 60 + return 0 + + # 再次判断进程是否正常 + process_num=`ps -ef | grep httpd| wc -l` + if [ $process_num -gt 50 ] ;then + echo "apache经过重启进程数依然大于50" + return 1 + else + return 0 + fi + +else + echo "进程数小于50" + sleep 3 + return 0 +fi +} + +# 每十秒钟执行一次函数,检查进程是否正常 +while true;do +check_httpd_process_number +[ $? -eq 1 ] && exit +done + +# Output: +[root@localhost ~]# bash http.sh +进程数小于50 +进程数小于50 +进程数小于50 +进程数小于50 + +# 复制窗口进行压力测试 +[root@localhost ~]# for i in {1..10}; do ab -c $((10000/$i)) -n 2000 http://127.0.0.1/ & done +``` + +## 10.3 案例三:统计文件 + +统计两个目录下的相同文件,以及不同文件 + +```bash +#!/bin/bash +# server1的文件在/test/目录中,server2的文件在/root/demo中,通过md5值来判断文件一致性,最终输出相同文件以及各自的不同文件 +#定义两个数组的索引 +point1=0 +point2=0 +echo "/test/的文件:" +# 将server1上的文件的散列值记录到数组当中 +for i in `ls /root/demo`;do + md5=`md5sum /root/demo/$i | awk '{print $1}'` + arrar1[$point1]=$md5:$i + echo ${arrar1[$point1]} + let point1++ +done +echo "/root/demo的文件:" +# 将server2上的文件的散列值记录到数组当中 +for i in `ls /test`;do + md5=`md5sum /test/$i | awk '{print $1}'` + arrar2[$point2]=$md5:$i + echo ${arrar2[$point2]} + let point2++ +done + +# 找出相同文件以及server1上的独立文件,server1的每个文件都和server2上进行比较 +echo "-------------------------------" +for i in ${arrar1[@]};do + for j in ${arrar2[@]};do + temp_flag=0 #定义一个标志位,表示没找到相同的文件 + server1_md5=`echo $i | awk -F: '{print $1}'` + server2_md5=`echo $j | awk -F: '{print $1}'` + server1_filename=`echo $i | awk -F: '{print $2}'` + server2_filename=`echo $j | awk -F: '{print $2}'` + if [ $server1_md5 == $server2_md5 ];then + echo -e "两边共同文件\t\t\t$server1_filename" + temp_flag=1 #找到了相同的文件 + break + fi + done + if [ $temp_flag -eq 0 ];then + echo -e "server1不同文件\t\t\t$i" + fi +done + +# 找出server2上的独立文件 +for i in ${arrar2[@]};do + for j in ${arrar1[@]};do + temp_flag=0 + server1_md5=`echo $i | awk -F: '{print $1}'` + server2_md5=`echo $j | awk -F: '{print $1}'` + server1_filename=`echo $i | awk -F: '{print $2}'` + server2_filename=`echo $j | awk -F: '{print $2}'` + if [ $server1_md5 == $server2_md5 ];then + temp_flag=1 + break + fi + done + if [ $temp_flag -eq 0 ];then + echo -e "server2不同文件\t\t\t$i" + fi +done + +``` + +## 10.4 练习:基于文件的用户登录注册功能 + +用户名和密码保存在文件中,格式为username:password \ No newline at end of file diff --git a/04.自动化运维/02.Shell脚本/1603244500462-e952ebe4-1ece-41cf-aa88-4feb0825ec43.png b/04.自动化运维/02.Shell脚本/1603244500462-e952ebe4-1ece-41cf-aa88-4feb0825ec43.png new file mode 100644 index 0000000..60ec9bc Binary files /dev/null and b/04.自动化运维/02.Shell脚本/1603244500462-e952ebe4-1ece-41cf-aa88-4feb0825ec43.png differ diff --git a/04.自动化运维/02.Shell脚本/u=3041729381,733868634&fm=26&gp=0.jpg b/04.自动化运维/02.Shell脚本/u=3041729381,733868634&fm=26&gp=0.jpg new file mode 100644 index 0000000..9a9e932 Binary files /dev/null and b/04.自动化运维/02.Shell脚本/u=3041729381,733868634&fm=26&gp=0.jpg differ diff --git a/04.自动化运维/03.Ansible.md b/04.自动化运维/03.Ansible.md new file mode 100644 index 0000000..29ff0bb --- /dev/null +++ b/04.自动化运维/03.Ansible.md @@ -0,0 +1,1368 @@ +# 1. ansible自动化运维 + +## 1.1 ansible介绍 + +ansible 是一个开源的自动化运维工具,主要用于系统配置管理、应用部署、任务编排等场景。它使用 YAML 语法编写配置文件,语法简单易懂,学习曲线平缓。ansible 的任务是幂等的,意味着多次执行结果是一致的,不会产生意外结果,非常适合于持续部署和集成。 + +ansible 支持众多常见操作系统和中间件,具有良好的扩展性。同时它还支持自定义模块,可以满足各种复杂的自动化需求。另一个特点是 ansible 不需要在远程主机上安装任何代理,只需要有 SSH 访问权限即可,并且不需要中央控制节点,使用 SSH 协议直接连接远程主机,部署和维护相对简单。ansible 使用 SSH 进行远程连接和命令执行,保证了数据传输的安全性。 + +ansible由python开发,集合了众多自动化运维工具的优点,实现了批量系统部署、批量程序部署,批量运行命令等功能。ansible是基于模块工作的,本身没有批量部署的能力,真正具有批量部署能力的是ansible运行的模块,ansible只是提供一个框架。 + +## 1.2 核心组件 + +![1](03.Ansible/1.png) + +ansible:ansible核心程序。 HostInventory:记录由ansible管理的主机信息,包括端口、密码、ip等。 Playbooks:“剧本”YAML格式文件,多个任务定义在一个文件中,定义主机需要调用哪些模块来完成的功能。 CoreModules:核心模块,主要操作是通过调用核心模块来完成管理任务。 CustomModules:自定义模块,完成核心模块无法完成的功能,支持多种语言。 ConnectionPlugins:连接插件,ansible和Host通信使用 + +## 1.3 任务执行方式 + +- ad-HOC 点对点模式 使用单个模块,支持批量执行单条命令。ad-hoc 命令是一种可以快速输入的命令 例如对大量服务器执行shell 或者执行某个linux命令。 +- playbook 模式(剧本模式) 通过编写yaml格式文件组合多个task任务,实现一个想要达到的功能,相对于多个点对点模式组合操作配置,playbook这个功能非常强大 + +## 1.4 特点 + +- 不需要在被监控端上安装任何服务程序 +- 无服务器端,使用时直接运行命令即可 +- 基于模块工作,可以使用任意语言开发 +- 使用yaml语言编写playbook +- 基于ssh工作 +- 可实现多级指挥 +- 具有幂等性,一种操作重复多次执行结果相同 + +## 1.5 执行过程 + +- 加载自己的配置文件,默认为/etc/ansible/ansible.cfg +- 加载自己对应的模块文件 +- 通过ansible将模块或命令生成对应的临时py文件,并将该文件传输至远程服务器 +- 对应执行用户的家目录的.ansible/tmp/xx.py文件 +- 给文件+x执行 +- 执行并将返回结果,删除临时py文件,然后退出 + +# 2. ansible部署 + +- 安装ansible + +``` +[root@localhost ~]# yum install -y epel-release +[root@localhost ~]# yum install -y ansible +``` + +**说明:**ansible只是一个工具,不需要启动,安装好以后,直接使用即可。并且只有服务端需要安装,客户端不需要安装.... + +## 2.1 参数说明 + +``` +`Inventory 文件参数: +-i 或 --inventory: 指定 Inventory 文件的路径 +-l 或 --limit: 限制操作的主机范围 +-g 或 --groups: 指定要操作的主机组 +`剧本(Playbook)参数: +-p 或 --playbook-dir: 指定 Playbook 所在目录 +-e 或 --extra-vars: 传递额外的变量 +`任务(Task)参数: +-m 或 --module-name: 指定要使用的模块名称 +-a 或 --args: 传递模块的参数 +`连接参数: +-c 或 --connection: 指定连接类型,如 ssh、local 等 +-u 或 --user: 指定远程连接的用户 +`输出参数: +-v 或 --verbose: 增加输出信息的详细程度 +--check: 进行一次"试运行",不会实际改变任何状态 +--diff: 显示配置文件的改动情况 +`其他参数: +-f 或 --forks: 指定并行执行的进程数 +-t 或 --tags: 只执行带有指定 tag 的任务 +--list-hosts: 列出受管主机 +--list-tasks: 列出所有任务 +``` + +## 2.2 快速开始 + +**实验环境:**四台Linux虚拟机,HOSTNAME分别为:ansible、server1、server2、server3 + +其中,ansible做为服务端,其他server均作为客户端 + +1. 免密登录 + +``` +# 在ansible上修改hosts文件,方便使用主机名管理主机 +[root@ansible ~]# vim /etc/hosts +....... +192.168.88.10 server1 +192.168.88.20 server2 +192.168.88.30 server3 + + +# 生成密钥对 +[root@ansible ~]# ssh-keygen -P "" -t rsa +..... +# 将公钥发送给需要被管理端,以实现免密登录 + + +[root@ansible ~]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@server1 +[root@ansible ~]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@server2 +[root@ansible ~]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@server3 +``` + +### 2.2.1 常用工具 + +- ansible:临时命令执行工具,常用于执行临时命令 +- ansible-doc:常用于模块功能的查询 +- ansible-playbook:用于执行剧本 + +### 2.2.2 主要配置文件 + +- /etc/ansible/ansible.cfg:主配置文件 +- /etc/ansible/hosts:主机清单文件 +- /etc/ansible/roles:角色目录 + +### 2.2.3 配置主机清单 + +编辑`/etc/ansible/hosts`文件,再最后面添加上被管理端 + +``` +[root@ansible ~]# vim /etc/ansible/hosts +...... +...... +...... +## [dbservers] +## +## db01.intranet.mydomain.net +## db02.intranet.mydomain.net +## 10.25.1.56 +## 10.25.1.57 + + +# Here's another example of host ranges, this time there are no +# leading 0s: + + +## db-[99:101]-node.example.com +# 定义自己的主机组 +[all_servers] +server1 +server2 +server3 + + +[node1] +server1 + + +[node2] +server2 + + +[node3] +server3 +``` + +- 主配置文件`ansible.cfg`,默认可以不用改 + +``` +[root@localhost ~]# vim /etc/ansible/ansible.cfg +[defaults] + + +# some basic default values... + + +#inventory = /etc/ansible/hosts # 定义主机清单文件 +#library = /usr/share/my_modules/ # 库文件的存放位置 +#module_utils = /usr/share/my_module_utils/ +#remote_tmp = ~/.ansible/tmp # 生成的临时py文件在远程主机的目录 +#local_tmp = ~/.ansible/tmp # 生成的临时py文件在本地主机的目录 +#plugin_filters_cfg = /etc/ansible/plugin_filters.yml +#forks = 5 # 默认的并发数 +#poll_interval = 15 # 默认的线程池 +#sudo_user = root # 默认的sudo用户 +#ask_sudo_pass = True +#ask_pass = True +#transport = smart +#remote_port = 22 +#module_lang = C +#module_set_locale = False +transport = smart +在ansible配置中,transport = smart 是指定 ansible 用于远程连接的传输机制。smart 是 ansible 的默认传输选项,它会尝试根据环境自动选择最佳的传输机制。 + + +当 smart 被设置时,ansible 会按照以下顺序尝试不同的传输机制: + + +如果已经建立了 SSH 连接(例如,通过 SSH Agent 或者在 ansible.cfg 中配置了 SSH 连接参数),则使用 SSH 传输。 +如果未建立 SSH 连接,并且目标主机是本地主机,则使用本地传输(即直接在本地执行命令)。 +如果未建立 SSH 连接,并且目标主机是远程主机,则使用 Paramiko 传输(基于 Python 的 SSH2 实现)。 +通过使用 smart 选项,ansible 可以自动选择合适的传输机制,以确保在不同的环境中都能正常工作。如果您希望显式地指定传输机制,可以将 transport 设置为 ssh、local 或 paramiko,以强制使用相应的传输方式。 +``` + +### 2.2.4 执行状态 + +ansible的执行状态 + +- 绿色:执行成功并且不需要做改变的操作 +- 黄色:执行成功并且对目标主机做变更 +- 红色:执行失败 +- 粉色:警告信息 +- 蓝色:显示ansible命令执行的过程 + +## 2.3 常用模块 + +### 2.3.1 ping模块 + +测试与主机的连通性 + +示例: + +``` +[root@ansible ~]# ansible -m ping all_servers +server1 | SUCCESS => { + "ansible_facts": { + "discovered_interpreter_python": "/usr/bin/python" + }, + "changed": false, + "ping": "pong" +} +server3 | SUCCESS => { + "ansible_facts": { + "discovered_interpreter_python": "/usr/bin/python" + }, + "changed": false, + "ping": "pong" +} +server2 | SUCCESS => { + "ansible_facts": { + "discovered_interpreter_python": "/usr/bin/python" + }, + "changed": false, + "ping": "pong" +} + + +# 返回说明: +"SUCCESS" 表示 ansible 成功执行了任务,没有遇到错误。 +"ansible_facts" 是一个包含 ansible 任务执行期间收集到的事实(facts)的字典。 +"discovered_interpreter_python" 是一个收集到的事实,它指示目标主机上的 Python 解释器的路径为 /usr/bin/python。这对于后续的 ansible 任务可能需要使用 Python 的情况很有用。 +"changed" 表示 ansible 是否对目标主机进行了更改。在这种情况下,值为 false 表示没有进行任何更改。 +"ping" 是一个简单的回应,用于测试与目标主机的连通性。如果值为 "pong",表示与目标主机的连接正常。 +``` + +### 2.3.2 Group模块 + +用户创建和修改用户组 + +示例:对node1主机组的成员创建一个IT组,组ID为111 + +``` +[root@ansible ~]# ansible-doc -s group +action: group +gid # 设置组的GID号 +name= # 管理组的名称 +state # 指定组状态,默认为创建,设置值为absent为删除 +system # 设置值为yes,表示为创建系统组 + + +[root@ansible ~]# ansible -m group -a "name=IT gid=111 system=yes" node1 +server1 | CHANGED => { + "ansible_facts": { + "discovered_interpreter_python": "/usr/bin/python" + }, + "changed": true, + "gid": 111, + "name": "IT", + "state": "present", + "system": true +} +``` + +### 2.3.3 User模块 + +用于对用户的创建,修改和删除等操作 + +``` +# 查看某个模块的具体用法 +[root@ansible ~]# ansible-doc -l|wc -l +3387 #共有3387个模块 +[root@ansible ~]# ansible‐doc ‐s user + comment # 用户的描述信息 + createhom # 是否创建家目录 + force # 在使用`state=absent'是, 行为与`userdel ‐‐force'一致. + group # 指定基本组 + groups # 指定附加组,如果指定为('groups=')表示删除所有组 + home # 指定用户家目录 + name # 指定用户名 + password # 指定用户密码 + remove # 在使用 `state=absent'时, 行为是与 `userdel ‐‐remove'一致. + shell # 指定默认shell + state #设置帐号状态,不指定为创建,指定值为absent表示删除 + system # 当创建一个用户,设置这个用户是系统用户。这个设置不能更改现有用户 + uid #指定用户的uid + update_password # 更新用户密码 + expires #指明密码的过期时间 + ...... +``` + +示例:在主机组node1上创建一个系统用户张三,家目录为/home/zhangsan,uid为111,附加组为IT,以及给一个注释 + +``` +[root@ansible ~]# ansible -m user -a "system=yes name=zhangsan home=/home/zhangsan uid=111 groups=IT comment='hello zhangsan'" node1 +server1 | CHANGED => { + "ansible_facts": { + "discovered_interpreter_python": "/usr/bin/python" + }, + "changed": true, + "comment": "hello zhangsan", + "create_home": true, + "group": 995, + "groups": "IT", + "home": "/home/zhangsan", + "name": "zhangsan", + "shell": "/bin/bash", + "state": "present", + "system": true, + "uid": 111 +} + + + + + + +# 删除用户及家目录 +[root@ansible ~]# ansible -m user -a "name=zhangsan state=absent remove=yes" node1 + + +# 添加系统用户,指定uid、家目录、主组及注释、密码 +[root@ansible ~]# ansible -m user -a "system=yes name=zhangsan home=/home/zhangsan uid=111 group=root comment='hello zhangsan' password='123456' shell=/bin/cbash " node1 +``` + +### 2.3.4 Command模块 + +command模块是ansible默认使用的模块。不支持管道,变量及重定向等 + +示例: + +``` +[root@ansible ~]# ansible-doc ‐s command +...... +...... +[root@ansible ~]# ansible -a "touch /root/ansible.txt" all_servers +[WARNING]: Consider using the file module with state=touch rather than running +'touch'. If you need to use command because file is insufficient you can add +'warn: false' to this command task or set 'command_warnings=False' in +ansible.cfg to get rid of this message. +server2 | CHANGED | rc=0 >> + + +server1 | CHANGED | rc=0 >> + + +server3 | CHANGED | rc=0 >> + + + + +[root@ansible ~]# ansible -a "find / -name ifcfg-ens33" all_servers +server1 | CHANGED | rc=0 >> +/etc/sysconfig/network-scripts/ifcfg-ens33 +server2 | CHANGED | rc=0 >> +/etc/sysconfig/network-scripts/ifcfg-ens33 +server3 | CHANGED | rc=0 >> +/etc/sysconfig/network-scripts/ifcfg-ens33 +``` + +### 2.3.5 Shell模块 + +在远程主机上执行bash命令 + +相对于command而言,支持性更好一点,但是对于某些复杂的命令,也可能会执行失败 + +解决方法:可以把命令卸载脚本中,使用script模块执行脚本到远程主机 + +``` +[root@ansible ~]# ansible -m shell -a "hostname" all_servers +server1 | CHANGED | rc=0 >> +server1 +server2 | CHANGED | rc=0 >> +server2 +server3 | CHANGED | rc=0 >> +server3 +``` + +### 2.3.6 Script模块 + +可以发送shell脚本到远程主机上并执行 + +示例: + +``` +[root@ansible ~]# vim test.sh +#!/bin/bash +for i in `seq 5` +do + touch /root/test_${i}.txt +done + + +# script模块 +[root@ansible ~]# ansible -m script -a "/root/test.sh" node2 +server2 | CHANGED => { + "changed": true, + "rc": 0, + "stderr": "Shared connection to server2 closed.\r\n", + "stderr_lines": [ + "Shared connection to server2 closed." + ], + "stdout": "", + "stdout_lines": [] +} + + +# server2验证 +[root@server2 ~]# ls +anaconda-ks.cfg test_1.txt test_3.txt test_5.txt +ansible.txt test_2.txt test_4.txt + + +# 参数说明 +chdir参数: 此参数的作用就是指定一个远程主机中的目录,在执行对应的脚本之前,会先进入到 chdir 参数指定的目录中。 +creates参数: 使用此参数指定一个远程主机中的文件,当指定的文件存在时,就不执行对应脚本 +removes参数: 使用此参数指定一个远程主机中的文件,当指定的文件不存在时,就不执行对应脚本 +``` + +### 2.3.7 Copy模块 + +用于向复制文件到主机组中 + +参数解释: + +``` +[root@ansible ~]# ansible-doc -s copy +backup:在覆盖之前,将源文件备份,备份文件包含时间信息。 +content:用于替代“src”,可以直接设定指定文件的值 +dest:必选项。要将源文件复制到的远程主机的绝对路径 +directory_mode:递归设定目录的权限,默认为系统默认权限 +force:强制覆盖目的文件内容,默认为yes +others:所有的file模块里的选项都可以在这里使用 +src:被复制到远程主机的本地文件,可以是绝对路径,也可以是相对路径。如果路径是一个目录,它将递归复制 + + +ansible -m copy -a "src=/本地文件 dest=/远程文件" nodes +``` + +示例: + +- 在创建文件时修改文件的属主和属组信息 + +``` +[root@ansible ~]# ansible -m copy -a "src=/root/test.sh dest=/root/test1 owner=zhangsan group=ansibles" node1 +``` + +- 在传输文件时修改文件的权限信息,并且备份远程主机文件 + +``` +[root@ansible ~]# ansible -m copy -a "src=/root/test.sh dest=/root/test2 backup=yes mode=777" node1 +``` + +- 创建一个文件并直接编辑文件 + +``` +[root@ansible ~]# ansible -m copy -a "content='hello ansibles\n' dest=/root/test3" node1 +``` + +### 2.3.8 File模块 + +用于对文件进行相关操作 + +参数解释: + +``` +[root@ansible ~]# ansible‐doc ‐s file +‐ name: Sets attributes of files + force:需要在两种情况下强制创建软链接,一种是源文件不存在,但之后会建立的情况下;另一种是目标软链接已存在,需要先取消之前的软链,然后创建新的软链,有两个选项:yes|no + group:定义文件/目录的属组 + mode:定义文件/目录的权限 + owner:定义文件/目录的属主 + path:必选项,定义文件/目录的路径 + recurse:递归设置文件的属性,只对目录有效 + src:被链接的源文件路径,只应用于state=link的情况 + dest:被链接到的路径,只应用于state=link的情况 + state: + absent: 删除文件 + directory:如果目录不存在,就创建目录 + file:验证文件是否存在,即使文件不存在,也不会被创建 + link:创建软链接 + hard:创建硬链接 + touch:如果文件不存在,则会创建一个新的文件,如果文件或目录已存在,则更新其后修改时间 +``` + +示例: + +- 创建目录 + +``` +[root@ansible ~]# ansible -m file -a "name=test1 owner=root group=root mode=644 state=directory " node1 +``` + +- 创建文件 + +``` +[root@ansible ~]# ansible -m file -a "path=/root/test2 owner=root group=root mode=644 state=touch" node1 +``` + +- 删除文件/目录 + +``` +[root@ansible ~]# ansible -m file -a "path=/root/test2 state=absent" node1 +``` + +- 创建软链接文件 + +``` +[root@ansible ~]# ansible -m file -a "src=/root/test1 dest=/root/test2 state=link" node1 +``` + +- 创建硬链接文件 + +``` +[root@ansible ~]# ansible -m file -a "src=/root/test.txt dest=/root/test2 state=hard" node2 +``` + +### 2.3.9 Yum模块 + +用于远程操作主机下载软件包 + +参数说明: + +``` +[root@ansible ~]# ansible‐doc ‐s yum + conf_file #设定远程yum安装时所依赖的配置文件。如配置文件没有在默认的位置。 + disable_gpg_check #是否禁止GPG checking,只用于`present' or `latest'。 + disablerepo #临时禁止使用yum库。 只用于安装或更新时。 + enablerepo #临时使用的yum库。只用于安装或更新时。 + name= #所安装的包的名称 + state #present安装, latest安装最新的, absent 卸载软件。 + update_cache #强制更新yum的缓存 +``` + +示例: + +``` +[root@ansible ~]# ansible -m yum -a "name=httpd state=latest" node3 +server3 | CHANGED => { + "ansible_facts": { + "discovered_interpreter_python": "/usr/bin/python" + }, + "changed": true, + "changes": { + "installed": [ + "httpd" + ], + "updated": [] + }, + "msg": "", + "rc": 0, +``` + +### 2.3.10 Service模块 + +用于远程管理主机上的service服务类 + +参数说明: + +``` +[root@ansible ~]# ansible-doc -s service +> SERVICE (/usr/lib/python2.7/site‐packages/ansible/modules/system/service.py) + Controls services on remote hosts. Supported init systems include BSD init, OpenRC, SysV, Solaris + SMF, systemd, upstart. For Windows targets, use the [win_service] module instead. + * note: This module has a corresponding action plugin. + ...... + ...... + arguments #命令行提供额外的参数 + enabled #设置开机启动,可以设置为yes或者no。 + name= #服务名称 + runlevel #开机启动的级别,一般不用指定。 + sleep #在重启服务的过程中,是否等待。如在服务关闭以后等待2秒再启动。 + state #started启动服务, stopped停止服务, restarted重启服务, reloaded重载配置 +``` + +示例: + +``` +[root@ansible ~]# ansible -m service -a "name=httpd state=started" node3 +server3 | CHANGED => { + "ansible_facts": { + "discovered_interpreter_python": "/usr/bin/python" + }, + "changed": true, + "name": "httpd", + "state": "started" + ..... + ..... +``` + +### 2.3.11 Selinux模块 + +用于管理远程主机的selinux设置 + +参考说明: + +``` +[root@node1 ~]# ansible-doc -s selinux +# selinux模块针对selinux的修改操作是针对配置文件进行修改的 +‐ name: Change policy and state of SELinux +configfile: +描述: SELinux 配置文件的路径,如果不是标准路径。 +参数类型: 字符串 +policy: +描述: 要使用的 SELinux 策略的名称。 +参数类型: 字符串 +state: +描述: (必需) SELinux 的模式。 +参数类型: 字符串 +可选值: +enforcing: 强制 SELinux 策略生效。 +permissive: 以警告模式运行 SELinux,不会阻止任何操作。 +disabled: 完全禁用 SELinux。 +``` + +示例: + +``` +[root@ansible ~]# ansible -m selinux -a "state=enforcing policy=targeted" node1 +[WARNING]: Reboot is required to set SELinux state to 'enforcing' +server1 | CHANGED => { + "ansible_facts": { + "discovered_interpreter_python": "/usr/bin/python" + }, + "changed": true, + "configfile": "/etc/selinux/config", + "msg": "Config SELinux state changed from 'disabled' to 'enforcing'", + "policy": "targeted", + "reboot_required": true, + "state": "enforcing" +} +``` + +# 3. Playbook(剧本) + +## 3.1 介绍 + +Ansible playbook是一种可执行的YAML文件,用于描述如何部署和配置一个系统或应用程序。一个playbook由一个或多个play组成,每个play都针对特定的主机或主机组执行一系列任务。 + +一个playbook的基本结构如下: + +``` +- hosts: all + vars: + package_name: nginx + config_file: /etc/nginx/nginx.conf + tasks: + - name: Install Nginx + yum: + name: "{{ package_name }}" + state: present + - name: Copy Nginx configuration + copy: + src: nginx.conf + dest: "{{ config_file }}" + notify: + - restart nginx + handlers: + - name: restart nginx + service: + name: nginx + state: restarted +``` + +在上面的例子中,我们定义了以下几个主要字段: + +- `hosts`: 指定要运行任务的主机或主机组。 +- `vars`: 定义要在playbook中使用的变量。 +- `tasks`: 定义要执行的任务列表。每个任务都有一个名称和一个模块。 +- `handlers`: 定义当某些任务触发时需要执行的处理程序,比如重启服务。 + +执行playbook剧本: + +`ansible-playbook xxxxx.yaml`即可 + +## 3.2 快速开始 + +### 3.2.1 案例:服务安装 + +安装nginx并且修改配置文件 + +1. 先编写一个nginx的子配置文件 + +``` +[root@ansible ~]# mkdir -p playbook/conf +[root@ansible ~]# cd playbook/conf +[root@ansible conf]# cat site.conf +server { + listen 666; + server_name localhost; + + + location / { + root /data; + index index.html + } +} +``` + +1. 编写playbook,要求是为目标机器安装nginx并且拷贝配置文件到该机器上,然后启动nginx。 + +``` +[root@ansible playbook]# vim nginx.yaml +- name: install nginx web server + hosts: node1 + remote_user: root + + + tasks: + - name: Install epel-release + yum: + name: epel-release + state: latest + + + - name: Install Nginx + yum: + name: nginx + state: latest + + + - name: Copy conf to nginx.conf.d + copy: + src: /root/playbook/conf/site.conf + dest: /etc/nginx/conf.d/site.conf + + + - name: Create "data" directory + file: + name: /data + state: directory + + + - name: Start Nginx service + service: + name: nginx + state: started + + + - name: create web index file + shell: echo "Install Nginx use Ansible...." > /data/index.html +``` + +![2](03.Ansible/2.png) + +## 3.3 高级配置 + +### 3.3.1 fact(事实变量) + +Ansible 内置了大量的事实(fact)变量,可以在 Playbook 中使用。这些事实变量可以帮助我们更好地了解目标主机的环境和配置信息,从而编写更加智能和动态的自动化脚本。 + +常用的内置事实变量包括: + +1. **操作系统信息**: + - `ansible_distribution`: 操作系统发行版名称,如 "CentOS"、"Ubuntu" + - `ansible_distribution_version`: 操作系统版本号 + - `ansible_os_family`: 操作系统家族,如 "RedHat"、"Debian" + - `ansible_kernel`: 内核版本 +2. **硬件信息**: + - `ansible_processor`: CPU 型号 + - `ansible_processor_vcpus`: 虚拟 CPU 核数 + - `ansible_memtotal_mb`: 内存总量(MB) + - `ansible_architecture`: CPU 架构,如 "x86_64" +3. **网络信息**: + - `ansible_default_ipv4`: 默认 IPv4 地址和网关 + - `ansible_all_ipv4_addresses`: 所有 IPv4 地址 + - `ansible_interfaces`: 所有网络接口名称 + - `ansible_hostname`: 主机名 +4. **其他信息**: + - `ansible_user_id`: 当前执行 Ansible 的用户 ID + - `ansible_date_time`: 主机当前日期和时间 + - `ansible_env`: 主机环境变量 + - `ansible_play_hosts`: 当前 play 中涉及的所有主机 + +这些事实变量可以帮助我们编写出更加智能和定制化的 Playbook。比如,我们可以根据操作系统的不同,执行不同的软件包安装任务;根据 CPU 架构,选择合适的软件包版本;根据内存大小,调整应用程序的配置等。 + +### 3.3.2 循环迭代 + +在playbook中,可以使用循环进行数据的迭代。这样一个模块就可以执行多次任务,因为往往我们部署一个服务的时候,都需要安装多个软件包的。 + +示例:使用yum循环安装软件包 + +``` +- name: Install packages + yum: + name: "{{ item }}" + state: present + loop: + - nginx + - mysql + - php +``` + +或者: + +``` +- name: Install packages + yum: + name: "{{ item }}" + state: present + with_items: + - httpd + - mysql + - php +``` + +这样就可以实现一个yum安装多个软件包了,避免了playbook过于臃肿。 + +#### 3.3.2.1 RHCE真题讲解(2023-10) + +创建一个名为 /home/student/ansible/packages.yml的 playbook: + +1. 将 php 和 mariadb 软件包安装到 dev、test 和 prod 主机组中的主机上 +2. 将 Development Tools 软件包组安装到 dev 主机组中的主机上 +3. 将 dev 主机组中主机上的所有软件包更新为最新版本 + +``` +[root@ansible ~]# vim playbook/packages.yml +- name: install pkgs + hosts: dev,test,prod + tasks: + - name: install mariadb php + yum: + name: "{{ item }}" + state: present + loop: + - php + - mariadb + + +- name: install group pkgs + hosts: dev + tasks: + - name: install Development Tools + yum: + name: "@Development Tools" + state: present + + +- name: update pkgs + hosts: dev + tasks: + - name: update pkgs + yum: + name: "*" + state: latest + + +``` + +#### 3.3.2.2 案例:循环创建用户 + +循环创建用户,用户信息如下 名称、组、家目录、shell、描述信息 zhangsan xsb /home/xsb/zhangsan /bin/bash 销售 lisi xsb /home/xsb/lisi /bin/bash 销售 wangwu jsb /home/jsb/wangwu /bin/sh java工程师 maliu jsb /home/jsb/maliu /bin/sh linux工程师 zhaoqi cwb /home/cwb/zhaoqi /bin/sh 会计 + +循环创建出以上用户并指定用户信息: + +``` +[root@ansible ~]# vim playbook/user.yml +- name: Manage user + hosts: node1 + remote_user: root + tasks: + - name: Ensure groups xsb, jsb, cwb exist + group: + name: "{{ item.group }}" + with_items: + - { group: xsb } + - { group: jsb } + - { group: cwb } + + + - name: Create users zhangsan, lisi, wangwu, maliu, zhaoqi + user: + name: "{{ item.name }}" + group: "{{ item.group }}" + shell: "{{ item.shell }}" + comment: "{{ item.comment }}" + home: "{{ item.home }}" + with_items: + - { name: 'zhangsan', group: 'xsb', home: '/home/xsb/zhangsan', shell: '/bin/bash', comment: '销售' } + - { name: 'lisi', group: 'xsb', home: '/home/xsb/lisi', shell: '/bin/bash', comment: '销售' } + - { name: 'wangwu', group: 'jsb', home: '/home/jsb/wangwu', shell: '/bin/sh', comment: 'java工程师' } + - { name: 'maliu', group: 'jsb', home: '/home/jsb/maliu', shell: '/bin/sh', comment: 'linux工程师' } + - { name: 'zhaoqi', group: 'cwb', home: '/home/cwb/zhaoqi', shell: '/bin/sh', comment: '会计' } + + +``` + + + +### 3.3.3 条件判断 + +在 Ansible Playbook 中,我们可以使用条件判断语句来根据不同的条件执行不同的任务。 + +**when 语句:** + +`when` 语句是最常用的条件判断语句。它可以根据变量的值、事实(facts)或者 Jinja2 表达式来决定是否执行某个任务。 + +``` +- name: Install packages on CentOS 7 + yum: + name: + - httpd + - mariadb-server + state: present + when: ansible_distribution == 'CentOS' and ansible_distribution_major_version|int == 7 + + +- name: Install packages on CentOS 8 + yum: + name: + - nginx + - mysql-server + state: present + when: ansible_distribution == 'CentOS' and ansible_distribution_major_version|int == 8 +``` + +在这个例子中: + +1. 第一个任务会在 CentOS 7 系统上安装 httpd 和 mariadb-server 软件包。`when` 语句确保了只有在 `ansible_distribution` 等于 'CentOS' 且 `ansible_distribution_major_version` 等于 7 时,这个任务才会执行。 +2. 第二个任务会在 CentOS 8 系统上安装 nginx 和 mysql-server 软件包。同样的,`when` 语句确保了只有在 `ansible_distribution` 等于 'CentOS' 且 `ansible_distribution_major_version` 等于 8 时,这个任务才会执行。 + +#### 3.3.3.1 RHCE真题讲解(2023-10-) + +**考试原题(第八题):** + +创建一个名为/home/student/ansible/parted.yml 的playbook,它将在dev主机组上运行下列任务 + +1. 如果磁盘/dev/vdd存在,则创建1500m分区 +2. 如果无法创建请求的分区大小,应显示错误消息 Could not create partition of that size,并且应改为使用大小 800m。 +3. 如果磁盘/dev/vdd不存在 ,应显示错误消息 disk /dev/vdd does not exist。 +4. 如果磁盘/dev/vdb存在,则创建1500m分区 +5. 如果无法创建请求的分区大小,应显示错误消息 Could not create partition of that size,并且应改为使用大小 800m。 +6. 最后分区都要格式化为ext4文件系统,并挂载在/mnt/fs01上 + +**简化题目:**在所有机器上创建sdb1分区,大小为1Gib,前提是sdb存在,如果不存在,请提示....... + +``` +[root@ansible playbook]# vim disk.yaml +- name: Create sdb1 partition + hosts: all_servers + tasks: + - name: Check if sdb block device exists + stat: + path: /dev/sdb + register: sdb_stat + + + - name: Create 1GB partition on sdb + parted: + device: /dev/sdb + number: 1 + state: present + part_end: 1GB + when: sdb_stat.stat.exists + + + - name: sdb block device not exists + debug: + msg: "sdb block device does not exist, cannot create partition." + when: not sdb_stat.stat.exists + + + + +# Output: +TASK [Create 1GB partition on sdb] ************************************ ********* +skipping: [server1] +skipping: [server3] +changed: [server2] + + +TASK [sdb block device not exists] ************************************ ********* +ok: [server1] => { + "msg": "sdb block device does not exist, cannot create partition." +} +skipping: [server2] +ok: [server3] => { + "msg": "sdb block device does not exist, cannot create partition." +} +``` + +**验证:** + +``` +[root@server2 ~]# lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT +sda 8:0 0 20G 0 disk +├─sda1 8:1 0 1G 0 part /boot +└─sda2 8:2 0 19G 0 part + ├─centos-root 253:0 0 17G 0 lvm / + └─centos-swap 253:1 0 2G 0 lvm [SWAP] +sdb 8:16 0 2G 0 disk +└─sdb1 8:17 0 953M 0 part +sr0 11:0 1 918M 0 rom +``` + +## 3.4 Jinjia2模板 + +Jinja2是一个功能强大的Python模板引擎,它被广泛应用于Ansible的playbook中。Jinja2模板语法提供了丰富的功能,使得在playbook中插入动态内容变得更加容易和灵活。 + +简单来讲,就是将原本静态的playbook转变为动态的。 + +### 3.4.1 RHCE真题讲解(2023-10) + +**原题(第九题):**生成主机文件 + +1. 编写模板文件/home/student/ansible/hosts.j2 ,针对每个清单主机包含一行内容,其格式与 /etc/hosts 相同。 +2. 创建名为 /home/student/ansible/hosts.yml 的playbook,它将使用此模板在 dev 主机组中的主 机上生成文件 /etc/myhosts。 +3. 该 playbook 运行后,dev 主机组中主机上的文件/etc/myhosts 应针对每个受管主机包含一行内 容。 + +**题目变更如下:** + +我们使用jinjia2和ansible内置变量动态的生成hosts文件,并且发送给远程主机 + +1. 编写模板文件 + +``` +[root@ansible playbook]# vim hosts.j2 +127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 +::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 + + +{% for host in groups.all_servers %} +{{hostvars[host].ansible_ens33.ipv4.address}} {{hostvars[host].ansible_hostname}} +{% endfor %} +``` + +1. 编写hosts.yaml剧本来渲染并且发送该模板文件到目标主机,使之替换原本的hosts + +``` +[root@ansible playbook]# vim hosts.yaml +- name: Config hosts file + hosts: all_servers + remote_user: root + + + tasks: + - name: copy hosts.j2 to group servers + template: + src: hosts.j2 + dest: /etc/hosts + +# 执行该playbook +[root@ansible playbook]# ansible-playbook hosts.yaml +``` + +1. 检查并验证: + +``` +[root@server1 ~]# cat /etc/hosts +127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 +::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 + + +192.168.88.10 server1 +192.168.88.20 server2 +192.168.88.30 server3 +``` + +### 3.4.2 案例:生成nginx配置文件 + +我们可以在playbook中自定义变量,然后更具自定义的变量使用jinjia2模板渲染nginx的配置文件 + +1. 先编写nginx.yaml剧本,在里面定义变量 + +``` +[root@ansible ~]# mkdir ansible +[root@ansible ~]# cd ansible +[root@ansible ansible]# vim nginx.yaml +- name: nginx conf + hosts: node1 + remote_user: root + vars: + nginx_vhosts: + - web1: + listen: 8080 + root: "/var/www/nginx/web1/" + - web2: + listen: 8080 + server_name: "web2.baidu.com" + root: "/var/www/nginx/web2/" + - web3: + listen: 8080 + server_name: "web3.baidu.com" + root: "/var/www/nginx/web3/" + tasks: + - name: mkdir /data + file: + name: /data + state: directory + - name: template config + template: + src: /root/ansible/site.conf.j2 + dest: /data/nginx.conf +``` + +1. 编写nginx子配置文件site.conf,子配置文件中使用jinjia2语法进行渲染 + +``` +[root@ansible ansible]# vim site.conf.j2 +{% for vhost in nginx_vhosts %} +server { + listen {{ vhost.listen }} + {% if vhost.server_name is defined %} +server_name {{ vhost.server_name }} + {% endif %} +root {{ vhost.root }} +} +{% endfor %} +``` + +1. server1上验证配置文件是否渲染成功 + +``` +[root@server1 ~]# cat /data/nginx.conf +server { + listen 8080 + root /var/www/nginx/web1/ +} +server { + listen 8080 + server_name web2.baidu.com + root /var/www/nginx/web2/ +} +server { + listen 8080 + server_name web3.baidu.com + root /var/www/nginx/web3/ +} +``` + +# 4. Role(角色) + +Ansible 中的 Role 是一种组织和重用代码的强大方式。角色可以帮助你将相关的任务、变量、文件等集中管理,使得代码更加模块化和可重用。 + +如果将所有的play都写在一个playbook中,很容易导致这个playbook文件变得臃肿庞大,且不易读。因此,可以将多个不同任务分别写在不同的playbook中,然后使用include将其包含进去即可。而role则是整合playbook的方式。无论是include还是role,其目的都是分割大playbook以及复用某些细化的play甚至是task。 + +## 4.1 目录结构 + +在角色中,将task,templates,handlers,files等内容都分开存放,然后再playbook中直接调用角色即可..... + +``` +[root@ansible roles]# tree apache/ +apache/ +├── defaults +│ └── main.yml +├── files +├── handlers +│ └── main.yml +├── meta +│ └── main.yml +├── README.md +├── tasks +│ └── main.yml +├── templates +├── tests +│ ├── inventory +│ └── test.yml +└── vars + └── main.yml +``` + +- `defaults/main.yml`: 定义角色的默认变量 +- `handlers/main.yml`: 定义角色的处理程序 +- `meta/main.yml`: 定义角色的元数据,如依赖关系、作者信息等 +- `tasks/main.yml`: 定义角色的主要任务 +- `templates/`: 存放角色使用的模板文件 +- `tests/`: 存放角色的测试相关文件 +- `vars/main.yml`: 定义角色的变量 + +## 4.2 初始化自定义Role + +可以使用ansible-galaxy工具通过init选项初始化一个角色 + +``` +[root@ansible roles]# ansible-galaxy init apache +- Role apache was created successfully +[root@ansible role]# ls apache/ +defaults files handlers meta README.md tasks templates tests vars +``` + +## 4.3 案例:使用Role安装httpd + +1. 初始化httpd角色 + +``` +[root@ansible roles]# ansible-galaxy init httpd +- Role httpd was created successfully +[root@ansible role]# tree httpd/ +httpd/ +├── defaults +│ └── main.yml +├── files +├── handlers +│ └── main.yml +├── meta +│ └── main.yml +├── README.md +├── tasks +│ └── main.yml +├── templates +├── tests +│ ├── inventory +│ └── test.yml +└── vars + └── main.yml + + +8 directories, 8 files +``` + +1. 编写角色中的task + +``` +[root@ansible httpd]# vim tasks/main.yml +# tasks file for httpd +- name: Install httpd + yum: + name: httpd + state: present +- name: copy site2.conf to apache web server + copy: + src: site.conf + dest: /etc/httpd/conf.d/site2.conf + + +- name: create directory1 for apache web server + file: + name: /data/site1/ + state: directory + + +- name: create directory2 for apache web server + file: + name: /data/site2/ + state: directory + + +- name: Start httpd + service: + name: httpd + state: started +- name: Write index file + shell: echo "site1" > /data/site1/index.html && echo "site2" > /data/site2/index.html +``` + +1. 编写角色中的file + +``` +[root@ansible httpd]# vim files/site.conf +Listen 8080 +Listen 9090 + + + +Require all granted + + + + +DocumentRoot "/data/site1/" + + + + +DocumentRoot "/data/site2/" + +``` + +1. 编写一个playbook剧本调用该角色 + +``` +[root@ansible roles]# vim httpd.yaml +- name: Install httpd web server + hosts: node1 + roles: + - httpd +``` + +1. 验证 + +``` +[root@server1 ~]# ss -nlt +State Recv-Q Send-Q Local Address:Port Peer Address:Port +LISTEN 0 128 *:22 *:* +LISTEN 0 100 127.0.0.1:25 *:* +LISTEN 0 128 :::8080 :::* +LISTEN 0 128 :::80 :::* +LISTEN 0 128 :::22 :::* +LISTEN 0 100 ::1:25 :::* +LISTEN 0 128 :::9090 :::* +``` + +![3](03.Ansible/3.png) + +## 4.4 RHCE真题讲解(2023-10) + +**题目(第六题):** + +根据下列要求,在 /home/student/ansible/roles中创建名为 apache 的角色: + +1. httpd软件包已安装,设为在系统启动时启用 +2. 防火墙已启用并正在运行,并使用允许访问 Web 服务器的规则 +3. 模板文件 index.html.j2 已存在,用于创建具有以下输出的文件 /var/www/html/index.html: Welcome to HOSTNAME on IPADDRESS 其中,HOSTNAME 是受管节点的完全限定域名,IPADDRESS 则是受管节点的 IP 地址。 +4. 按照下方所述,创建一个使用此角色的 playbook /home/student/ansible/newrole.yml: 该 playbook 在 webservers 主机组中的主机上运行 + +```bash +[student@workstation ansible# cd roles/ +[student@workstation roles]# ansible-galaxy init apache +[student@workstation roles]# vim apache/tasks/main.yml +--- +# tasks file for apache +- name: install http + yum: + name: httpd + state: present +- name: config system service + service: + name: "{{ item }}" + state: started + enabled: yes + loop: + - httpd + - firewalld +- name: firewalld service + firewalld: + zone: public + service: http + permanent: yes + immediate: yes + state: enabled +- name: user templates + template: + src: index.html.j2 + dest: /var/www/html/index.html +[student@workstation roles]# vim apache/templates/index.html.j2 +Welcome to {{ ansible_fqdn }} on {{ ansible_default_ipv4.address }} + + +[student@workstation roles]# cd .. +[student@workstation ansible]# vim newrole.yml +- name: use apache role + hosts: webservers + roles: + - apache + + +# 运行脚本 +[student@workstation ansible]# ansible-playbook newrole.yml +# 访问测试 +[student@workstation ansible]# curl serverc +Welcome to serverc.lab.example.com on 172.25.250.12 +[student@workstation ansible]# curl serverd +Welcome to serverd.lab.example.com on 172.25.250.13 +``` \ No newline at end of file diff --git a/04.自动化运维/03.Ansible/1.png b/04.自动化运维/03.Ansible/1.png new file mode 100644 index 0000000..b084ccf Binary files /dev/null and b/04.自动化运维/03.Ansible/1.png differ diff --git a/04.自动化运维/03.Ansible/2.png b/04.自动化运维/03.Ansible/2.png new file mode 100644 index 0000000..04911ce Binary files /dev/null and b/04.自动化运维/03.Ansible/2.png differ diff --git a/04.自动化运维/03.Ansible/3.png b/04.自动化运维/03.Ansible/3.png new file mode 100644 index 0000000..e905796 Binary files /dev/null and b/04.自动化运维/03.Ansible/3.png differ diff --git a/04.自动化运维/04.Rsync.md b/04.自动化运维/04.Rsync.md new file mode 100644 index 0000000..8387291 --- /dev/null +++ b/04.自动化运维/04.Rsync.md @@ -0,0 +1,503 @@ +# 1. RSYNC + +# 2. 什么是RSYNC + +rsync是类unix下的一款数据镜像备份工具——remote sync。 + +Rsync 的基本特点如下: + +- 可以镜像保存整个目录树和文件系统; +- 可以很容易做到保持原来文件的权限、时间、软硬链接等; +- 无须特殊权限即可安装; +- 优化的流程,文件传输效率高; +- 可以使用 rcp、ssh 等方式来传输文件,当然也可以通过直接的 socket 连接; +- 支持匿名传输; +- rsync的主要特点就是**增量传输**,只对变更的部分进行传送。 + +# 3. RSYNC原理 + +## 3.1 `rsync`原理 + +`rsync`是`linux`下同步文件的一个高效算法,用于同步更新两处计算机的文件和目录,并适当利用查找文件中的不同块以减少数据传输。`rsync`的主要特点就是增量传输,只对变更的部分进行传送。 + +```shell +vim 1.txt +1 2 3 4 +vim 2.txt +1 2 3 4 + +diff 1.txt 2.txt + +``` + + + +### 3.1.1 增量同步算法 + +假如我们现在需要同步两个文件保持一致,并且只想传送不同的部分,那么我们就需要对两边的文件做`diff`,但是这两个文件在两台不同的机器上,无法做`diff`。如果我们做`diff`,就要把一个文件传到另一台机器上做`diff`,但这样一来,我们就传了整个文件,这与我们只想传输不同部分的初衷相背。于是我们就要想一个办法,让这两边的文件见不到面,但还能知道它们间有什么不同。这就是`rsync`的算法。 + +### 3.1.2 `rsync`同步算法 + +我们将同步源文件名称为`fileSrc`,同步目的文件叫`fileDst`。 + +#### 3.1.2.1 分块`Checksum`算法 + +**找到文件不同的地方** + +首先,我们会把`fileDst`的文件平均切分成若干个小块,比如每块`512`个字节(最后一块会小于这个数),然后对每块计算两个`checksum`: + +1. 一个叫`rolling checksum`,是弱`checksum`,`32`位的`checksum`(相对粗略,但是快) +2. 另一个是强`checksum`,`128`位的,以前用`md4`,现在用`md5 hash`算法。 + +为什么要这样?因为若干年前的硬件上跑`md4`的算法太慢了,所以,我们需要一个快算法来鉴别文件块的不同,但是弱的`adler32`算法碰撞概率太高了,所以我们还要引入强的`checksum`算法以保证两文件块是相同的。也就是说,弱的`checksum`是用来**区别不同**,而强的是用来**确认相同**。 + +#### 3.1.2.2 传输算法 + +**同步目标端会把`fileDst`的一个`checksum`列表传给同步源**,这个列表里包括了三个东西,`rolling checksum(32bits),md5 checksume(128bits)`,文件块编号。 + +同步源机器拿到了这个列表后,会对`fileSrc`做同样的`checksum`,然后和`fileDst`的`checksum`做对比,这样就知道哪些文件块改变了。 + +但是,聪明的你一定会有以下两个疑问: + +如果我`fileSrc`这边**在文件中间加了一个字符,这样后面的文件块都会位移一个字符,这样就完全和`fileDst`这边的不一样了,但理论上来说,我应该只需要传一个字符就好了**。这个怎么解决? +如果这个`checksum`列表特别长,而我的两边的相同的文件块可能并不是一样的顺序,那就需要查找,线性的查找起来应该特别慢吧。这个怎么解决? +很好,让我们来看一下同步源端的算法。 + +#### 3.1.2.3 `checksum`查找算法 + +同步源端拿到`fileDst`的`checksum`数组后,会把这个数据存到一个`hash table`(特殊的数据结构体,可以快速检索)中,用`rolling checksum`做`hash`,以便获得`O(1)`时间复杂度的查找性能。这个`hash table`是`16bits`的,所以,`hash table`的尺寸是`2的16次方`,对`rolling checksum`的`hash`会被散列到`0 到 2^16 – 1`中的某个整数值。 + +#### 3.1.2.4 比对算法 + +![图片描述](04.Rsync/bVbpkxQ) + +1. 取`fileSrc`的第一个文件块(我们假设的是`512`个长度),也就是从`fileSrc`的第`1`个字节到第`512`个字节,取出来后做`rolling checksum`计算。计算好的值到`hash`表中查。 +2. 如果查到了,说明发现在`fileDst`中有潜在相同的文件块,于是就再比较`md5`的`checksum`,因为`rolling checksume`太弱了,可能发生碰撞。于是还要算`md5`的`128bits`的`checksum`,这样一来,我们就有` 2^-(32+128) = 2^-160`的概率发生碰撞,这太小了可以忽略。如果`rolling checksum`和`md5 checksum`都相同,这说明在`fileDst`中有相同的块,我们需要记下这一块在`fileDst`下的文件编号。 +3. 如果`fileSrc`的`rolling checksum` 没有在`hash table`中找到,那就不用算`md5 checksum`了。表示这一块中有不同的信息。总之,只要`rolling checksum` 或 `md5 checksum` 其中有一个在`fileDst`的`checksum hash`表中找不到匹配项,那么就会触发算法对`fileSrc`的`rolling`动作。于是,算法会住后`step 1`个字节,取`fileSrc`中字节`2-513`的文件块要做`checksum,go to (1.) `– 现在你明白什么叫`rolling checksum`了吧。(主动往后一位) +4. 这样,我们就可以找出`fileSrc`相邻两次匹配中的那些文本字符,这些就是我们要往同步目标端传的文件内容了。 + +#### 3.1.2.5 传输 + +![图片描述](04.Rsync/bVbpkyQ) +最终在同步源这端,我们的`rsync`算法可能会得到这个样子的一个数据数组,图中,红色块表示在目标端已匹配上,不用传输(注:我专门在其中显示了两块`chunk #5`,代表数据中有复制的地方,不用传输),而白色的地方就是需要传输的内容(注意:这些白色的块是不定长的),这样,同步源这端把这个数组(白色的就是实际内容,红色的就放一个标号)压缩传到目的端,在目的端的`rsync`会根据这个表重新生成文件,这样,同步完成。 + + + +参考博客: + +https://segmentfault.com/a/1190000018391604?utm_source=tag-newest + +# 4. 安装部署服务端 + +- 安装xineted服务 + +```bash +[root@server1 ~]# yum install rsync.x86_64 -y +[root@server1 ~]# yum install xinetd -y +``` + +- 修改xineted配置文件 + +```bash +[root@server1 ~]# vim /etc/xinetd.d/rsync +service rsync +{ +disable = no +socket_type = stream +wait = no +user = root +server = /usr/bin/rsync +server_args = --daemon +port = 873 +log_on_failure = USERID +} +``` + +- 修改rsync配置文件 + +```bash +[root@server1 ~]# vim /etc/rsyncd.conf +[test] // 模块名,主要是定义服务器哪个目录要被同步 +path = /test // 指定文件目录所在位置,这是必须指定的 +uid = root +gid = root +max connections = 2 +timeout = 300 +read only = false +auth users = root // 认证用户是 root,必须是服务器上真实存在的用户 +secrets file = /etc/rsync.passwd // 密码存在哪个文件 +strict modes = yes +use chroot = yes // 在传输文件之前,服务器守护程序在将chroot 到文件系统中的目录中 +``` + +- 准备密码文件 + +```bash +[root@server1 ~]# vim /etc/rsync.passwd +root:123456 +[root@server1 ~]# chmod 600 /etc/rsync.passwd +``` + +- 启动服务 + +```bash +[root@server1 ~]# systemctl start xinetd.service +[root@server1 ~]# ss -tanl | grep 873 +LISTEN 0 64 :::873 :::* +``` + +- 准备文件 + +```bash +[root@server1 ~]# mkdir /test +[root@server1 ~]# touch /test/123 +``` + +# 5. 六种不同的工作模式 + +- 在server2上安装rsync + +```bash +[root@server2 ~]# yum install rsync.x86_64 -y +``` + +rysnc工具的参数 + +``` +rsync 相关参数 + + -v --verbose详细 + -a --avchive归档模式,表示递归方式传输文件,并保持所有文件属性,等于-rlptgoD + -z 传递过程中使用zip压缩传递,速度更快 + -p, --perms 保持文件权限 + -P --partial 保留那些因故没有完全传输的文件,以便加快随后的再次传输 + -r --recursiv递归目录 + -e --rsh=COMMAND指定使用rsh、ssh方式进行数据同步 + --progress 在传输时现实传输过程(显示备份过程) + -topg 保持文件原有属性,o=owner,t=time,p=perms(权限),g=group + -b --backup创建备份,也就是对于目的已经存在有同样的文件名时,将老的文件重新命名为~filename + -u --update仅仅进行更新,也就是跳过已经存在的文件 + -l--links保留软连接 + --delete 删除那些DST中SRC没有的文件(就是在目的目录中只保留传输过去的文件,其它的都删除),保持和源文件相同 +-q, --quiet 精简输出模式 +-c, --checksum 打开校验开关,强制对文件传输进行校验 +-R, --relative 使用相对路径信息 +-b, --backup 创建备份,也就是对于目的已经存在有同样的文件名时,将老的文件重新命名为~filename。可以使用--suffix选项来指定不同的备份文件前缀。 +--backup-dir 将备份文件(如~filename)存放在在目录下。 +-suffix=SUFFIX 定义备份文件前缀 +-u, --update 仅仅进行更新,也就是跳过所有已经存在于DST,并且文件时间晚于要备份的文件。(不覆盖更新的文件) +-l, --links 保留软链结 +-L, --copy-links 想对待常规文件一样处理软链结 +--copy-unsafe-links 仅仅拷贝指向SRC路径目录树以外的链结 +--safe-links 忽略指向SRC路径目录树以外的链结 +-H, --hard-links 保留硬链结 +-o, --owner 保持文件属主信息 +-g, --group 保持文件属组信息 +-D, --devices 保持设备文件信息 +-t, --times 保持文件时间信息 +-S, --sparse 对稀疏文件进行特殊处理以节省DST的空间 +-n, --dry-run现实哪些文件将被传输 +-W, --whole-file 拷贝文件,不进行增量检测 +-x, --one-file-system 不要跨越文件系统边界 +-B, --block-size=SIZE 检验算法使用的块尺寸,默认是700字节 +-e, --rsh=COMMAND 指定使用rsh、ssh方式进行数据同步 +--rsync-path=PATH 指定远程服务器上的rsync命令所在路径信息 +-C, --cvs-exclude 使用和CVS一样的方法自动忽略文件,用来排除那些不希望传输的文件 +--existing 仅仅更新那些已经存在于DST的文件,而不备份那些新创建的文件 +--delete-excluded 同样删除接收端那些被该选项指定排除的文件 +--delete-after 传输结束以后再删除 +--ignore-errors 及时出现IO错误也进行删除 +--max-delete=NUM 最多删除NUM个文件 +--partial 保留那些因故没有完全传输的文件,以是加快随后的再次传输 +--force 强制删除目录,即使不为空 +--numeric-ids 不将数字的用户和组ID匹配为用户名和组名 +--timeout=TIME IP超时时间,单位为秒 +-I, --ignore-times 不跳过那些有同样的时间和长度的文件 +--size-only 当决定是否要备份文件时,仅仅察看文件大小而不考虑文件时间 +--modify-window=NUM 决定文件是否时间相同时使用的时间戳窗口,默认为0 +-T --temp-dir=DIR 在DIR中创建临时文件 +--compare-dest=DIR 同样比较DIR中的文件来决定是否需要备份 +--exclude=PATTERN 指定排除不需要传输的文件模式 +--include=PATTERN 指定不排除而需要传输的文件模式 +--exclude-from=FILE 排除FILE中指定模式的文件。 +如果排除单个文件或者目录,可以直接指定 --exclude-from=File_Name +如果是多个文件或目录,可以新建一个文件 exclude,里面写上要排除的文件名或目录名,可以使用正则,然后使用--exclude-from='/../exclude'指定 +--include-from=FILE 不排除FILE指定模式匹配的文件。用法同上 +--version 打印版本信息 +--address 绑定到特定的地址 +--config=FILE 指定其他的配置文件,不使用默认的rsyncd.conf文件 +--port=PORT 指定其他的rsync服务端口 +--blocking-io 对远程shell使用阻塞IO +-stats 给出某些文件的传输状态 +--log-format=formAT 指定日志文件格式 +--password-file=FILE 从FILE中得到密码 +--bwlimit=KBPS 限制I/O带宽,KBytes per second +-h, --help 显示帮助信息 +``` + + + +- 模式一,查看服务端有哪些可用数据源 + +```bash +[root@server2 ~]# rsync --list-only root@192.168.88.10:: +test +``` + +- 模式二,本地文件拷贝到本地,当src和dest都不包含有冒号时就启动从本地进行拷贝 + +```bash +[root@server2 ~]# mkdir /backup +[root@server2 ~]# touch local.txt +[root@server2 ~]# rsync local.txt /backup/ +[root@server2 ~]# ls /backup/ +local.txt +``` + +- 模式三,本地文件拷贝到远程,当dest包含冒号时就启动拷贝到远程 + +```bash +[root@server2 ~]# rsync local.txt root@192.168.88.10:/test +The authenticity of host '192.168.175.10 (192.168.175.10)' can't be established. +ECDSA key fingerprint is SHA256:x573vWoEULGOYwloNT7s9EqxZa6lA1k5zZMFk7bU0xg. +ECDSA key fingerprint is MD5:60:21:e0:bf:3c:c0:d8:09:74:b8:23:26:55:4e:d1:0e. +Are you sure you want to continue connecting (yes/no)? yes +Warning: Permanently added '192.168.175.10' (ECDSA) to the list of known hosts. +root@192.168.175.10's password: + +[root@server1 ~]# ll /test/local.txt +-rw-r--r-- 1 root root 0 3月 24 11:05 /test/local.txt +``` + +- 模式四,将远程文件拷贝到本地,当src包含冒号的时候就启动远程拷贝到本地 + +```bash +[root@server2 ~]# rsync root@192.168.88.10:/test/123 /backup/ +root@192.168.175.10's password: +[root@server2 ~]# ll /backup/123 +-rw-r--r-- 1 root root 0 3月 24 11:07 /backup/123 +``` + +- 模式五,从远程服务器拷贝到本地,当src使用两个冒号的时候启用这种方式,冒号后面跟的是服务端设置的模块 + +```bash +[root@server2 ~]# rsync -r root@192.168.88.10::test /backup/ +Password: +#这时的密码就不是主机root用户的密码而是rsync设置的密码了 +[root@server2 ~]# ls /backup/ +123 local.txt +``` + +- 模式六,从本地拷贝到远程服务器 + +```bash +[root@server2 ~]# touch 456 +[root@server2 ~]# rsync -r 456 root@192.168.88.10::test +Password: +[root@server1 ~]# ls /test/ +123 456 local.txt +``` + +# 6. 案例,定时备份 + +**客户端需求** + +1.客户端提前准备存放的备份的目录,目录规则如下:/backup/主机\_ip\_时间 + +2.客户端在本地打包备份(系统配置文件、应用配置等)例如/etc/passwd,拷贝至/backup/主机\_ip\_时间 + +3.客户端最后将备份的数据进行推送至备份服务器 + +4.客户端每天凌晨1点定时执行该脚本 + +5.客户端服务器本地保留最近7天的数据,避免浪费磁盘空间 + +**服务端需求** + +1.服务端部署rsync,用于接收客户端推送过来的备份数据 + +⒉.服务端需要每天校验客户端推送过来的数据是否完整 + +3.服务端需要每天校验的结果通知给管理员 + +4.服务端仅保留6个月的备份数据,其余的全部删除 + +------------------------------- + +客户端准备 + +- 创建目录 + +```bash +[root@server2 ~]# mkdir /backup +``` + +- 安装expect工具 + +```bash +[root@server2 ~]# yum install expect* -y +``` + +- 准备expect脚本 + +```bash +[root@server2 ~]# vim rsync.exp +#!/usr/bin/expect +#set: 进行赋值 +set mulu [lindex $argv 0] +#位置参数,0 表示第一个参数 +set timeout 10 +spawn rsync -avzr /backup/$mulu root@192.168.88.10::test +#spawn: 启动新的进程 +expect Password +#expect: 从进程接收字符串 +send "123456\n" +#send: 用于向进程发送字符串 +expect eof +``` + +- 准备备份脚本 + +```bash +[root@server2 ~]# vim beifen.sh +#!/bin/bash + +# 准备压缩文件的目录 +mulu=`ip a | grep global|awk -F'[ /]+' '{print $3}'`_`date +%F` +echo $mulu +mkdir -pv /backup/$mulu &> /dev/null + +# 打包待发送的数据 +tar zcf /backup/$mulu/conf.tar.gz /etc/passwd /etc/vimrc &> /dev/null +# 为了后面模拟一个月数据的变化 +touch /backup/$mulu + +# 发送数据 +#rsync -avzr /backup/$mulu root@192.168.175.10::test +expect rsync.exp $mulu + + +# 保留七天以内的数据 +find /backup -mtime +7 -delete +[root@server2 ~]# chmod +x beifen.sh +``` + +- 计划任务 + +```bash +[root@server2 ~]# cat /etc/crontab +...... +0 1 * * * root /root/beifen.sh + +``` + +服务端准备 + +- 安装rsync + +```bash +[root@server1 ~]# yum install rsync.x86_64 -y +``` + +- 修改配置文件 + +```bash +[root@server1 ~]# vim /etc/rsyncd.conf +[test] +path = /test +uid = root +gid = root +max connections = 2 +timeout = 300 +read only = false +auth users = root +secrets file = /etc/rsync.passwd +strict modes = yes +use chroot = yes +``` + +- 创建目录 + +```bash + [root@server1 ~]# mkdir /test -pv +``` + +- 准备密码文件 + +```bash +[root@server1 ~]# cat /etc/rsync.passwd +root:123456 +[root@server1 ~]# chmod 600 /etc/rsync.passwd +``` + +- 启动rsync + +```bash +[root@server1 ~]# systemctl start rsyncd.service +[root@server1 ~]# ss -tnl +State Recv-Q Send-Q Local Address:Port Peer Address:Port +LISTEN 0 128 *:22 *:* +LISTEN 0 100 127.0.0.1:25 *:* +LISTEN 0 5 *:873 *:* +LISTEN 0 128 :::22 :::* +LISTEN 0 100 ::1:25 :::* +LISTEN 0 5 :::873 :::* +``` + +- 验证服务端 + +```bash +[root@server2 ~]# rsync --list-only root@192.168.88.10:: +test +``` + + + +------------- + +- 模拟一个月的数据来验证结果 + +```bash +[root@server2 ~]# for i in {1..31};do date -s 2024/08/$i; /root/beifen.sh ; done +#修改时间,模拟一个月的时间 +[root@server2 ~]# ll /backup +总用量 0 +drwxr-xr-x 2 root root 25 8月 24 00:00 192.168.175.30_2022-08-24 +drwxr-xr-x 2 root root 25 8月 25 00:00 192.168.175.30_2022-08-25 +drwxr-xr-x 2 root root 25 8月 26 00:00 192.168.175.30_2022-08-26 +drwxr-xr-x 2 root root 25 8月 27 00:00 192.168.175.30_2022-08-27 +drwxr-xr-x 2 root root 25 8月 28 00:00 192.168.175.30_2022-08-28 +drwxr-xr-x 2 root root 25 8月 29 00:00 192.168.175.30_2022-08-29 +drwxr-xr-x 2 root root 25 8月 30 00:00 192.168.175.30_2022-08-30 +drwxr-xr-x 2 root root 25 8月 31 00:00 192.168.175.30_2022-08-31 +[root@server2 ~]# date +[root@server2 ~]# systemctl restart chronyd +#重置时间 +[root@server2 ~]# date + +[root@server1 ~]# ll /test +总用量 0 +drwxr-xr-x 2 root root 25 8月 1 00:00 192.168.175.30_2022-08-01 +drwxr-xr-x 2 root root 25 8月 2 00:00 192.168.175.30_2022-08-02 +drwxr-xr-x 2 root root 25 8月 3 00:00 192.168.175.30_2022-08-03 +drwxr-xr-x 2 root root 25 8月 4 00:00 192.168.175.30_2022-08-04 +drwxr-xr-x 2 root root 25 8月 5 00:00 192.168.175.30_2022-08-05 +drwxr-xr-x 2 root root 25 8月 7 00:00 192.168.175.30_2022-08-07 +drwxr-xr-x 2 root root 25 8月 8 00:00 192.168.175.30_2022-08-08 +drwxr-xr-x 2 root root 25 8月 9 00:00 192.168.175.30_2022-08-09 +drwxr-xr-x 2 root root 25 8月 10 00:00 192.168.175.30_2022-08-10 +drwxr-xr-x 2 root root 25 8月 11 00:00 192.168.175.30_2022-08-11 +drwxr-xr-x 2 root root 25 8月 12 00:00 192.168.175.30_2022-08-12 +drwxr-xr-x 2 root root 25 8月 20 2022 192.168.175.30_2022-08-20 +drwxr-xr-x 2 root root 25 8月 21 2022 192.168.175.30_2022-08-21 +drwxr-xr-x 2 root root 25 8月 22 2022 192.168.175.30_2022-08-22 +drwxr-xr-x 2 root root 25 8月 23 2022 192.168.175.30_2022-08-23 +drwxr-xr-x 2 root root 25 8月 24 2022 192.168.175.30_2022-08-24 +drwxr-xr-x 2 root root 25 8月 25 2022 192.168.175.30_2022-08-25 +drwxr-xr-x 2 root root 25 8月 27 2022 192.168.175.30_2022-08-27 +drwxr-xr-x 2 root root 25 8月 28 2022 192.168.175.30_2022-08-28 +drwxr-xr-x 2 root root 25 8月 29 2022 192.168.175.30_2022-08-29 +drwxr-xr-x 2 root root 25 8月 30 2022 192.168.175.30_2022-08-30 +drwxr-xr-x 2 root root 25 8月 31 2022 192.168.175.30_2022-08-31 +``` + diff --git a/04.自动化运维/04.Rsync/bVbpkxQ b/04.自动化运维/04.Rsync/bVbpkxQ new file mode 100644 index 0000000..3131375 Binary files /dev/null and b/04.自动化运维/04.Rsync/bVbpkxQ differ diff --git a/04.自动化运维/04.Rsync/bVbpkyQ b/04.自动化运维/04.Rsync/bVbpkyQ new file mode 100644 index 0000000..1ce869f Binary files /dev/null and b/04.自动化运维/04.Rsync/bVbpkyQ differ diff --git a/05.监控告警/01.Zabbix.md b/05.监控告警/01.Zabbix.md new file mode 100644 index 0000000..103da67 --- /dev/null +++ b/05.监控告警/01.Zabbix.md @@ -0,0 +1,716 @@ +# 1. Zabbix企业级监控方案 + +# 2. 常见监控软件介绍 + +## 2.1 Cacti + +- Cacti 是一套基于 PHP、MySQL、SNMP 及 RRD Tool 开发的监测图形分析工具,Cacti 是使用**轮询**的方式由主服务器向设备发送数据请求来获取设备上状态数据信息的,如果设备不断增多,这个**轮询的过程就非常的耗时**,轮询的结果就不能即时的反应设备的状态了。Cacti 监控关注的是对数据的**展示**,却不关注数据异常后的反馈。如果凌晨 3 点的时候设备的某个数据出现异常,除非监控人员在屏幕前发现这个异常变化,否则是没有任何报警机制能够让我们道出现了异常。 + +## 2.2 Nagios + +- Nagios 是一款**开源**的免费网络监控报警服务,能有效监控 Windows、Linux 和 Unix 的主机状态,交换机、路由器和防火墙等网络设置,打印机、网络投影、网络摄像等设备。在系统或服务状态异常时发出邮件或短信报警第一时间通知运维人员,在状态恢复后发出正常的邮件或短信通知。Nagios 有完善的插件功能,可以方便的根据应用服务扩展功能。 +- Nagios 已经可以支持由数万台服务器或上千台网络设备组成的云技术平台的监控,它可以充分发挥自动化运维技术特点在设备和人力资源减少成本。只是 Nagios 无法将多个相同应用集群的数据集合起来,也不能监控到集群中特殊节点的迁移和恢复。 + +## 2.3 Ganglia + +- Ganglia 是 UC Berkeley 发起的一个开源集群监视项目,设计用于测量数以千计的节点。Ganglia 的核心包含 gmond、gmetad 以及一个 Web 前端。 +- 主要是用来**监控系统性能**,如:CPU 、内存、硬盘利用率, I/O 负载、网络流量情况等,通过曲线很容易见到每个节点的工作状态,对合理调整、分配系统资源,提高系统整体 性能起到重要作用,目前是监控HADOOP 的官方推荐服务。 + +## 2.4 Zabbix + +- Zabbix 是一个基于 WEB 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。zabbix 能监视各种网络参数,保证服务器系统的安全运营;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 +- Zabbix 是由 Alexei Vladishev 创建,目前由 Zabbix SIA 在持续开发和支持。 +- Zabbix 是一个企业级的分布式开源监控方案。 +- Zabbix 是一款能够监控各种网络参数以及服务器健康性和完整性的软件。 +- Zabbix 使用灵活的通知机制,允许用户为几乎任何事件配置基于邮件的告警。这样可以快速反馈服务器的问题。基于已存储的数据,Zabbix提供了出色的报告和数据可视化功能。这些功能使得Zabbix成为容量规划的理想方案。 +- Zabbix 支持主动轮询和被动捕获。 +- Zabbix所有的报告、统计信息和配置参数都可以通过基于Web的前端页面进行访问。基于Web的前端页面可以确保您从任何方面评估您的网络状态和服务器的健康性。 +- Zabbix是免费的。Zabbix是根据GPL通用公共许可证第2版编写和发行的。这意味着它的源代码都是免费发行的,可供公众任意使用, [商业支持](http://www.zabbix.com/support.php) 由Zabbix公司提供。 + +## 2.5 监控区别 + +- nagios 图形不是特别好,也可以安装图形插件,但是也不怎么好看 +- nagios 一般情况下如果需要图形可以和 cacti 配合使用 +- cacti 的监控是轮询监控,效率低,图形相对 nagios 比较好看 +- zabbix 和 nagios 因为是并发监控,对 cpu 的要求更高 +- zabbix 在性能和功能上都强大很多 +- zabbix 的图形相当漂亮 +- 支持多种监控方式 zabbix-agent snmp 等等 +- 支持分布式监控,能监控的 agent 非常多 +- zabbix 有图形的 web 配置界面,配置简洁 +- zabbix 支持自动发现功能 + +# 3. Zabbix监控简介 + +## 3.1 zabbix优点 + +- 开源,无软件成本投入 +- Server 对设备性能要求低 +- 支持设备多,自带多种监控模板 +- 支持分布式集中管理,有自动发现功能,可以实现自动化监控 +- 开放式接口,扩展性强,插件编写容易 +- 当监控的 item 比较多服务器队列比较大时可以采用主动状态,被监控客户端主动 从server 端去下载需要监控的 item 然后取数据上传到 server 端。 这种方式对服务器的负载比较小。 +- Api 的支持,方便与其他系统结合 + +## 3.2 zabbix缺点 + +- 需在被监控主机上安装 agent,所有数据都存在数据库里, 产生的数据据很大,瓶颈主要在数据库。 +- 项目批量修改不方便 +- 社区虽然成熟,但是中文资料相对较少,服务支持有限; +- 入门容易,能实现基础的监控,但是深层次需求需要非常熟悉Zabbix并进行大量的二次定制开发难度较大 +- 系统级别报警设置相对比较多,如果不筛选的话报警邮件会很多;并且自定义的项目报警需要自己设置,过程比较繁琐; +- 缺少数据汇总功能,如无法查看一组服务器平均值,需进行二次开发; + +## 3.3 zabbix组件结构 + +- Zabbix_Server:整个监控体系中最核心的组件,它负责接收客户端发送的报告信息,所有配置、统计数据及操作 数据都由它组织。 +- 数据库存储:所有配置信息和Zabbix收集到的数据都被存储在数据库中。 +- Web界面:为了从任何地方和任何平台都可以轻松的访问Zabbix, 我们提供基于Web的Zabbix界面。该界面是 Zabbix Server的一部分,通常跟Zabbix Server运行在同一台物理机器上(!如果使用SQLite,Zabbix Web界面必 须要跟Zabbix Server运行在同一台物理机器上。) +- Zabbix_Proxy(可选):用于监控节点非常多的分布式环境中,它可以代理zabbix-server的功能,减轻zabbixserver的压力。 +- Zabbix_Agent:zabbix-agent为客户端软件,用于采集各监控项目的数据,并把采集的数据传输给zabbixproxy或zabbix-server。 + +## 3.4 zabbix监控方式 + +- 被动模式: + + - 被动检测:相对于agent而言;agent, **server向agent请求获取配置的各监控项相关的数据**,agent接收请求、获取数据并响应给server; + +- 主动模式 + + - 主动检测:相对于agent而言;agent(active),**agent向server请求与自己相关监控项配置**,主动地将server配置的监控项相关的数据发送给server; + - 主动监控能极大节约监控server 的资源。 + +## 3.5 Zabbix架构 + +image.png + +## 3.6 Zabbix常用术语 + +- 主机:一台你想监控的网络设备,用IP或域名表示 +- 主机组:主机的逻辑组;它包含主机和模板。一个主机组里的主机和模板之间并没有任何直接的关联。通常在给不同用户组的主机分配权限时候使用主机组。 +- 监控项:你想要接收的主机的特定数据,一个度量数据。 +- 触发器:一个被用于定义问题阈值和“评估”监控项接收到的数据的逻辑表达式 当接收到的数据高于阈值时,触发器从“OK”变成“Problem”状态。当接收到的数据低于阈值时,触发器保留/返回一个“OK”的状态。 + +# 4. Zabbix-server部署 + +## 4.1 安装Zabbix 服务端 + +1. 准备zabbix的yum源,这里通过安装zabbix-release软件包来自动获取zabbix的源。然后安装服务端相关组件 + + 官网地址:https://www.zabbix.com/ + +```bash +[root@server1 ~]# rpm -Uvh https://repo.zabbix.com/zabbix/6.0/rhel/9/x86_64/zabbix-release-latest-6.0.el9.noarch.rpm +[root@server1 ~]# yum install -y zabbix-server-mysql zabbix-web-mysql zabbix-apache-conf zabbix-sql-scripts zabbix-selinux-policy zabbix-agent + +# 安装中文插件 +[root@server1 ~]# yum install -y glibc-langpack-zh.x86_64 +``` + +2. 关闭防火墙和Selinux + +```bash +[root@server1 ~]# setenforce 0 +[root@server1 ~]# systemctl stop firewalld +``` + +## 4.2 准备数据库环境 + +1. 安装数据库,这里还是选择mariadb即可 + +```bash +[root@server1 ~]# yum install -y mariadb-server +# 启动mariadb数据库 +[root@server1 ~]# systemctl enable --now mariadb +# 初始化数据库并设置密码 +[root@server1 ~]# mysqladmin -uroot password '123456' +# 测试数据库连接是否正常 +[root@server1 ~]# mysql -uroot -p123456 -e "show databases;" ++--------------------+ +| Database | ++--------------------+ +| information_schema | +| mysql | +| performance_schema | +| test | ++--------------------+ +``` + +2. 创建zabbix所需数据库以及zabbix账户 + +```bash +[root@localhost ~]# mysql -uroot -p123456 +# 创建zabbix库 +MariaDB [(none)]> create database zabbix character set utf8mb4 collate utf8mb4_bin; +Query OK, 1 row affected (0.00 sec) +# 创建zabbix账户 +MariaDB [(none)]> grant all privileges on zabbix.* to zabbix@localhost identified by '123456'; +Query OK, 0 rows affected (0.00 sec) +# 刷新账户信息 +MariaDB [(none)]> flush privileges; +Query OK, 0 rows affected (0.00 sec) +MariaDB [(none)]> set global log_bin_trust_function_creators = 1; +``` + +## 4.3 修改服务端相关配置 + +1. 导入初始架构数据 + +```bash +[root@server1 ~]# zcat /usr/share/zabbix-sql-scripts/mysql/server.sql.gz | mysql --default-character-set=utf8mb4 -D zabbix -uzabbix -p123456 +# zabbix安装包中有一个SQL文件,这是zabbix架构的初始数据。 +[root@server1 ~]# mysql -uzabbix -p123456 -D zabbix -e "show tables" ++----------------------------+ +| Tables_in_zabbix | ++----------------------------+ +| acknowledges | +| actions | +| alerts | +| application_discovery | +| application_prototype | +| application_template | +| applications | +| auditlog | +| auditlog_details | +| autoreg_host | +...... +...... +...... +[root@server1 ~]# mysql -uroot -p123456 -e "set global log_bin_trust_function_creators = 0;" +``` + +2. 配置zabbix连接数据库 + +```bash +[root@server1 ~]# vim /etc/zabbix/zabbix_server.conf +DBPassword=123456 +``` + +3. 启动所有服务 + +```bash +# 安装zabbix的时候,会附带安装httpd来提供web服务,因为zabbix默认情况下是有一个web网站的 +[root@server1 ~]# systemctl start zabbix-server zabbix-agent httpd php-fpm +[root@localhost ~]# ss -nlt +State Recv-Q Send-Q Local Address:Port Peer Address:Port Process +LISTEN 0 128 0.0.0.0:22 0.0.0.0:* +LISTEN 0 4096 0.0.0.0:10050 0.0.0.0:* +LISTEN 0 4096 0.0.0.0:10051 0.0.0.0:* +LISTEN 0 511 *:80 *:* +LISTEN 0 128 [::]:22 [::]:* +LISTEN 0 80 *:3306 *:* +LISTEN 0 4096 [::]:10050 [::]:* +LISTEN 0 4096 [::]:10051 [::]:* +``` + +## 4.4 服务端初始化 + +完成上述配置后,可以在浏览器中输入`http://IP/zabbix`打开zabbix的web界面开始初始化 + +1. 进入初始化页面,可以选择中文 + +image-20250316143441157 + +2. 状态检测 + + 确保所有的php检测都是ok的状态 + +image-20250316143522811 + +3. 连接数据库 + + 数据库的信息按照图上的填入,其中密码是我们刚刚设置的zabbix用户的密码,端口号3306 + +image-20250316143612944 + +4. zabbix信息 + + 填写主机名以及选择正确的时区 + +image-20250316143728527 + +5. 完成安装 + + 看到如下界面,说明zabbix初始化成功 + +image-20250316143812080 + +6. 登录zabbix的web网站,默认用户名和密码如下 + + **username:**Admin + + **password:**zabbix + +image-20250316143838085 + +7. 进入Zabbix界面 + +image-20250316144001404 + +## 4.5 客户端配置 + +1. 在server2上安装zabbix-agent客户端 + +```bash +[root@server2 ~]# rpm -Uvh https://repo.zabbix.com/zabbix/6.0/rhel/9/x86_64/zabbix-release-latest-6.0.el9.noarch.rpm +[root@server2 ~]# yum install zabbix-agent -y +``` + +2. 修改客户端配置文件 + +```bash +[root@server2 ~]# vim /etc/zabbix/zabbix_agentd.conf +# 主要修改以下三个参数 +Server=192.168.88.10 +ServerActive=192.168.88.10 +Hostname=server_10 +``` + +3. 启动zabbix-agent + +```bash +[root@server2 ~]# setenforce 0 +[root@server2 ~]# systemctl stop firewalld +[root@server2 ~]# systemctl start zabbix-agent.service +``` + +# 5. 快速使用 + +## 5.1 主机监控 + +先简单的监控一个主机的状态和信息 + +1. 添加一个主机 + +image-20250316144631981 + +2. 填写主机信息 + +image-20250316144759012 + +3. 添加模板 + +zabbix内置了很多模板,比如监控Linux主机的模板,监控httpd的模板,监控MySQL的模板等等。我们可以直接使用这些模板,当然也可以自定义。 + +image-20250316145451714 + +4. 查看监控项 + +在监测中,点击最新数据,然后过滤出server_10主机,就可以看到各个监控项的具体数据 + +image-20250316145719393 + +查看具体数据 + +image-20250316145750925 + +### 5.1.1 手动添加主机监控项 + +模板中内置的很多的监控项,但是如果没有我们想要的监控项的话,我们可以手动添加。 + +1. 创建监控项 + +在配置中,选择对应的主机,然后创建监控项 + +![image-20250316145935907](01.Zabbix/image-20250316145935907.png) + +2. 创建一个监控项用于查看已挂载文件系统情况 + +image-20250316151257316 + +3. 查看监控项获取到的的监控结果 + +![image-20250316151353990](01.Zabbix/image-20250316151353990.png) + +## 5.2 自定义应用配置监控项 + +### 5.2.1 Nginx进程监控 + +在server2上安装nginx服务,然后编写监控项配置文件,来监控nginx的进程数量 + +#### 5.2.1.1 安装nginx + +```bash +[root@server2 ~]# yum install -y epel-release nginx +[root@server2 ~]# systemctl enable --now nginx +``` + +#### 5.2.1.2 配置监控项文件 + +```bash +[root@server2 ~]# vim /etc/zabbix/zabbix_agentd.d/userparameter_nginx.conf +UserParameter=nginx_process_num,ps aux | grep -c nginx + +# 重启zabbix-agent +[root@server2 ~]# systemctl restart zabbix-agent +``` + +#### 5.2.1.3 服务端验证 + +可以在服务端上安装一个zabbix工具`zabbix-get`,可以通过该工具在命令行中验证监控项的是否生效 + +```bash +[root@server1 ~]# yum install -y zabbix-get +[root@server1 ~]# zabbix_get -s 192.168.88.20 -k nginx_process_num +7 +``` + +#### 5.2.1.4 添加监控项 + +在web网站上使用刚刚编写的监控项 + +image-20250316152132163 + +查看刚刚添加的监控项键值的状态为已启用 + +image-20250316152222209 + +查看具体的检测到的数据,在菜单栏中点击监测->最新数据,然后过滤刚刚的监控项 + +image-20250316152330210 + +查看数据统计以及变化折线图 + +![image-20250316152417222](01.Zabbix/image-20250316152417222.png) + +#### 5.2.1.5 添加触发器 + +给该监控项添加触发器,让他能够出发警告通知 + +image-20250316152509112 + +创建一个新的触发器 + +![image-20250316152703035](01.Zabbix/image-20250316152703035.png) + +可以看到我们刚刚添加的触发器 + +image-20250316152744501 + +#### 5.2.1.6 手动宕机告警测试 + +在server2上手动关闭nginx后,在仪表盘中查看告警信息 + +```bash +[root@server2 ~]# systemctl stop nginx +``` + +查看仪表盘,可以看到告警在闪烁 + +image-20250316153251565 + +可以看到这里有一个告警信息,说明我们刚刚创建的触发器生效了。 + +这样的话,运维工程师就可以直接在这个网站上看到当前服务器组或者集群中各个机器和服务的状态。 不需要在一个一个登录上去查看了。 + +但是只是这样还是不够只能,稍后我们将继续配置触发告警以后,通过邮件或者叮叮企业微信等平台向工程师发送告警信息。 + +## 5.3 常见服务的监控项 + +### 5.3.1 Redis自定义监控项 + +```bash +vim /usr/local/zabbix/etc/zabbix_agentd.conf.d/redis.conf +UserParameter=Redis.Status,/usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 6379 ping |grep -c PONG +UserParameter=Redis_conn[*],/usr/local/redis/bin/redis-cli -h $1 -p $2 info | grep -w "connected_clients" | awk -F':' '{print $2}' +UserParameter=Redis_rss_mem[*],/usr/local/redis/bin/redis-cli -h $1 -p $2 info | grep -w "used_memory_rss" | awk -F':' '{print $2}' +UserParameter=Redis_lua_mem[*],/usr/local/redis/bin/redis-cli -h $1 -p $2 info | grep -w "used_memory_lua" | awk -F':' '{print $2}' +UserParameter=Redis_cpu_sys[*],/usr/local/redis/bin/redis-cli -h $1 -p $2 info | grep -w "used_cpu_sys" | awk -F':' '{print $2}' +UserParameter=Redis_cpu_user[*],/usr/local/redis/bin/redis-cli -h $1 -p $2 info | grep -w "used_cpu_user" | awk -F':' '{print $2}' +UserParameter=Redis_cpu_sys_cline[*],/usr/local/redis/bin/redis-cli -h $1 -p $2 info | grep -w "used_cpu_sys_children" | awk -F':' '{print $2}' +UserParameter=Redis_cpu_user_cline[*],/usr/local/redis/bin/redis-cli -h $1 -p $2 info | grep -w "used_cpu_user_children" | awk -F':' '{print $2}' +UserParameter=Redis_keys_num[*],/usr/local/redis/bin/redis-cli -h $1 -p $2 info | grep -w "$$1" | grep -w "keys" | grep db$3 | awk -F'=' '{print $2}' | awk -F',' '{print $1}' +UserParameter=Redis_loading[*],/usr/local/redis/bin/redis-cli -h $1 -p $2 info | grep loading | awk -F':' '{print $$2}' + +Redis.Status --检测Redis运行状态, 返回整数 +Redis_conn --检测Redis成功连接数,返回整数 +Redis_rss_mem --检测Redis系统分配内存,返回整数 +Redis_lua_mem --检测Redis引擎消耗内存,返回整数 +Redis_cpu_sys --检测Redis主程序核心CPU消耗率,返回整数 +Redis_cpu_user --检测Redis主程序用户CPU消耗率,返回整数 +Redis_cpu_sys_cline --检测Redis后台核心CPU消耗率,返回整数 +Redis_cpu_user_cline --检测Redis后台用户CPU消耗率,返回整数 +Redis_keys_num --检测库键值数,返回整数 +Redis_loding --检测Redis持久化文件状态,返回整数 +``` + +### 5.3.2 Nginx自定义监控项 + +```bash +vim /etc/nginx/conf.d/default.conf + location /nginx-status + { + stub_status on; + access_log off; + allow 127.0.0.1; + deny all; + } + + +vim /usr/local/zabbix/etc/zabbix_agentd.conf.d/nginx.conf +UserParameter=Nginx.active,/usr/bin/curl -s "http://127.0.0.1:80/nginx-status" | awk '/Active/ {print $NF}' +UserParameter=Nginx.read,/usr/bin/curl -s "http://127.0.0.1:80/nginx-status" | grep 'Reading' | cut -d" " -f2 +UserParameter=Nginx.wrie,/usr/bin/curl -s "http://127.0.0.1:80/nginx-status" | grep 'Writing' | cut -d" " -f4 +UserParameter=Nginx.wait,/usr/bin/curl -s "http://127.0.0.1:80/nginx-status" | grep 'Waiting' | cut -d" " -f6 +UserParameter=Nginx.accepted,/usr/bin/curl -s "http://127.0.0.1:80/nginx-status" | awk '/^[ \t]+[0-9]+[ \t]+[0-9]+[ \t]+[0-9]+/ {print $1}' +UserParameter=Nginx.handled,/usr/bin/curl -s "http://127.0.0.1:80/nginx-status" | awk '/^[ \t]+[0-9]+[ \t]+[0-9]+[ \t]+[0-9]+/ {print $2}' +UserParameter=Nginx.requests,/usr/bin/curl -s "http://127.0.0.1:80/nginx-status" | awk '/^[ \t]+[0-9]+[ \t]+[0-9]+[ \t]+[0-9]+/ {print $3}' +``` + +### 5.3.3 TCP协议自定义监控项 + +```bash +vim /usr/local/zabbix/share/zabbix/alertscripts/tcp_connection.sh +#!/bin/bash +function ESTAB { +/usr/sbin/ss -ant |awk '{++s[$1]} END {for(k in s) print k,s[k]}' | grep 'ESTAB' | awk '{print $2}' +} +function TIMEWAIT { +/usr/sbin/ss -ant | awk '{++s[$1]} END {for(k in s) print k,s[k]}' | grep 'TIME-WAIT' | awk '{print $2}' +} +function LISTEN { +/usr/sbin/ss -ant | awk '{++s[$1]} END {for(k in s) print k,s[k]}' | grep 'LISTEN' | awk '{print $2}' +} +$1 + +vim /usr/local/zabbix/etc/zabbix_agentd.conf.d/cattcp.conf +UserParameter=tcp[*],/usr/local/zabbix/share/zabbix/alertscripts/tcp_connection.sh $1 + +tcp[TIMEWAIT] --检测TCP的驻留数,返回整数 +tcp[ESTAB] --检测tcp的连接数、返回整数 +tcp[LISTEN] --检测TCP的监听数,返回整数 +``` + +### 5.3.4 系统监控自带监控项 + +```bash +agent.ping 检测客户端可达性、返回nothing表示不可达。1表示可达 +system.cpu.load --检测cpu负载。返回浮点数 +system.cpu.util -- 检测cpu使用率。返回浮点数 +vfs.dev.read -- 检测硬盘读取数据,返回是sps.ops.bps浮点类型,需要定义1024倍 +vfs.dev.write -- 检测硬盘写入数据。返回是sps.ops.bps浮点类型,需要定义1024倍 +net.if.out[br0] --检测网卡流速、流出方向,时间间隔为60S +net-if-in[br0] --检测网卡流速,流入方向(单位:字节) 时间间隔60S +proc.num[] 目前系统中的进程总数,时间间隔60s +proc.num[,,run] 目前正在运行的进程总数,时间间隔60S +###处理器信息 +通过zabbix_get 获取负载值 +合理的控制用户态、系统态、IO等待时间剋保证进程高效率的运行 +系统态运行时间较高说明进程进行系统调用的次数比较多,一般的程序如果系统态运行时间占用过高就需要优化程序,减少系统调用 +io等待时间过高则表明硬盘的io性能差,如果是读写文件比较频繁、读写效率要求比较高,可以考虑更换硬盘,或者使用多磁盘做raid的方案 +system.cpu.swtiches --cpu的进程上下文切换,单位sps,表示每秒采样次数,api中参数history需指定为3 +system.cpu.intr --cpu中断数量、api中参数history需指定为3 +system.cpu.load[percpu,avg1] --cpu每分钟的负载值,按照核数做平均值(Processor load (1 min average per core)),api中参数history需指定为0 +system.cpu.load[percpu,avg5] --cpu每5分钟的负载值,按照核数做平均值(Processor load (5 min average per core)),api中参数history需指定为0 +system.cpu.load[percpu,avg15] --cpu每5分钟的负载值,按照核数做平均值(Processor load (15 min average per core)),api中参数history需指定为0 +``` + +### 5.3.5 自定义系统监控项 + +```bash +###内存相关 +vim /usr/local/zabbix/etc/zabbix_agentd.conf.d/catcarm.conf +UserParameter=ram.info[*],/bin/cat /proc/meminfo |awk '/^$1:{print $2}' +ram.info[Cached] --检测内存的缓存使用量、返回整数,需要定义1024倍 +ram.info[MemFree] --检测内存的空余量,返回整数,需要定义1024倍 +ram.info[Buffers] --检测内存的使用量,返回整数,需要定义1024倍 + +####TCP相关的自定义项 +vim /usr/local/zabbix/share/zabbix/alertscripts/tcp_connection.sh +#!/bin/bash +function ESTAB { +/usr/sbin/ss -ant |awk '{++s[$1]} END {for(k in s) print k,s[k]}' | grep 'ESTAB' | awk '{print $2}' +} +function TIMEWAIT { +/usr/sbin/ss -ant | awk '{++s[$1]} END {for(k in s) print k,s[k]}' | grep 'TIME-WAIT' | awk '{print $2}' +} +function LISTEN { +/usr/sbin/ss -ant | awk '{++s[$1]} END {for(k in s) print k,s[k]}' | grep 'LISTEN' | awk '{print $2}' +} +$1 + +vim /usr/local/zabbix/etc/zabbix_agentd.conf.d/cattcp.conf +UserParameter=tcp[*],/usr/local/zabbix/share/zabbix/alertscripts/tcp_connection.sh $1 + +tcp[TIMEWAIT] --检测TCP的驻留数,返回整数 +tcp[ESTAB] --检测tcp的连接数、返回整数 +tcp[LISTEN] --检测TCP的监听数,返回整数 +``` + +# 6. Zabbix告警配置 + +## 6.1 Zabbix通过邮件告警 + +### 6.1.1 配置告警设置 + +1. 配置E-mail参数 + + 在菜单栏中选择管理->报警媒介类型 + +image-20250316153912958 + +按照图中配置参数,最后面的密码是在QQ邮箱中申请的**授权码** + +image-20250316154138064 + +2. 测试是否能够发信 + +![image-20250316154236124](01.Zabbix/image-20250316154236124.png) + +如果QQ邮箱测试失败的同学,可以尝试网易的163邮箱,亲测可以,需要注册邮箱号,开启smtp服务,配置如下 + +![image-20250316165313573](01.Zabbix/image-20250316165313573.png) + +3. 修改Admin用户的报警媒介 + +image-20250316155758866 + +4. 配置告警动作 + +这里定义如果触发告警,应该怎么处理。以及如果发送邮件,邮件的内容是什么样的.... + +image-20250316155845820 + +当然也可以自定义告警信息 + +image-20250316155928572 + +image-20240811191936758 + +### 6.1.2 邮件告警测试 + +手动停止server2上面的nginx服务以后,查看邮件 + +image-20240811192208427 + +可以看到,成功通过邮件发送告警信息 + +当问题恢复以后,也会发送邮件给我们 + +image-20240811192344941 + +## 6.2 Zabbix通过钉钉告警 + +### 6.2.1 创建钉钉群聊 + +先下载电脑版本的钉钉,然后创建一个群聊(创建群聊需要三个好友起步,大家可以相互之间添加好友)并且创建群聊 + +### 6.2.2 添加群机器人 + +添加自定义机器人,并且配置关键词 + +image-20240811205608526 + +保存好Webhook,并且设置安全关键词为:告警 + +image-20240811205636906 + +### 6.2.3 创建python脚本 + +由于zabbix无法直接向钉钉发送告警信息,所以这里需要借助python脚本来实现,让zabbix调用python脚本来向机器人接口发送信息 + +1. 安装requests模块 + +```bash +[root@server1 ~]# yum install -y python-requests +``` + +2. python脚本如下: + +```bash +[root@server1 alertscripts]# cd /usr/lib/zabbix/alertscripts +[root@server1 alertscripts]# vim zabbix_send_ding.py +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: 英格无敌牛老师 +import requests +import json +import sys +import os + +headers = {'Content-Type': 'application/json;charset=utf-8'} +api_url = "https://oapi.dingtalk.com/robot/send?access_token=8eab8f6f2d872a0e14376fd782b6aaf40bd57b1352f9cc8ed42c238baaec80e0" +def msg(text): + json_text= { + "msgtype": "text", + "at": { + "atMobiles": [ + "18888888888" + ], + "isAtAll": True + }, + "text": { + "content": text + } + } + response = requests.post(api_url,json.dumps(json_text),headers=headers).content + print(response) + +if __name__ == '__main__': +# 这里用发送关键字用来触发告警 + text = "告警" +# 使用sys.argv来接收一个参数,该参数是zabbix发送的消息 + # text = sys.argv[1] + msg(text) +``` + +3. 测试脚本 + +```bash +# 如果手动测试脚本的话,需要把text = sys.argv[1]先注释了 +[root@server1 alertscripts]# chmod a+x zabbix_send_ding.py +[root@server1 alertscripts]# ./zabbix_send_ding.py +{"errcode":0,"errmsg":"ok"} +``` + +### 6.2.4 配置zabbix钉钉告警 + +1. 添加告警媒介 + + 因为我们使用的python脚本只接收一个参数(内容),所以只需要添加一个参数{ALERT.MESSAGE}即可 + +image-20240811210327296 + +2. 添加处理动作 + +image-20240811210412566 + +![image-20240811210446030](01.Zabbix/image-20240811210446030.png) + +恢复操作 + +![image-20240811210609196](01.Zabbix/image-20240811210609196.png) + +告警消息内容如下: + +```bash +【zabbix告警】 +告警问题:{EVENT.NAME} +告警时间:{EVENT.DATE}-{EVENT.TIME} +告警主机:{HOST.NAME} +告 警 IP:{HOST.IP} +监控项目:{ITEM.NAME} +故障等级:{EVENT.SEVERITY} + + +【zabbix告警恢复】 +恢复时间:{EVENT.DATE}-{EVENT.TIME} +告警名称:{EVENT.NAME} +告警主机:{HOST.NAME} +告 警 IP:{HOST.IP} +告警等级:{EVENT.SEVERITY} + + +【zabbix告警更新】 +{USER.FULLNAME} {EVENT.UPDATE.ACTION} problem at {EVENT.UPDATE.DATE} {EVENT.UPDATE.TIME}. +{EVENT.UPDATE.MESSAGE} +``` + +3. 绑定用户,填写钉钉的手机号即可 + +![image-20240811210717780](01.Zabbix/image-20240811210717780.png) + +### 6.2.5 测试钉钉告警 + +image-20250215104654160 + diff --git a/05.监控告警/01.Zabbix/1604564765251-b49bb305-e64a-4d66-be84-e5326100d97c.png b/05.监控告警/01.Zabbix/1604564765251-b49bb305-e64a-4d66-be84-e5326100d97c.png new file mode 100644 index 0000000..b49c488 Binary files /dev/null and b/05.监控告警/01.Zabbix/1604564765251-b49bb305-e64a-4d66-be84-e5326100d97c.png differ diff --git a/05.监控告警/01.Zabbix/image-20240811191936758.png b/05.监控告警/01.Zabbix/image-20240811191936758.png new file mode 100644 index 0000000..6b25103 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20240811191936758.png differ diff --git a/05.监控告警/01.Zabbix/image-20240811192208427.png b/05.监控告警/01.Zabbix/image-20240811192208427.png new file mode 100644 index 0000000..7a3a9ae Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20240811192208427.png differ diff --git a/05.监控告警/01.Zabbix/image-20240811192344941.png b/05.监控告警/01.Zabbix/image-20240811192344941.png new file mode 100644 index 0000000..1a8da17 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20240811192344941.png differ diff --git a/05.监控告警/01.Zabbix/image-20240811205608526.png b/05.监控告警/01.Zabbix/image-20240811205608526.png new file mode 100644 index 0000000..b0de29c Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20240811205608526.png differ diff --git a/05.监控告警/01.Zabbix/image-20240811205636906.png b/05.监控告警/01.Zabbix/image-20240811205636906.png new file mode 100644 index 0000000..32aadb0 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20240811205636906.png differ diff --git a/05.监控告警/01.Zabbix/image-20240811210327296.png b/05.监控告警/01.Zabbix/image-20240811210327296.png new file mode 100644 index 0000000..2e15dd8 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20240811210327296.png differ diff --git a/05.监控告警/01.Zabbix/image-20240811210412566.png b/05.监控告警/01.Zabbix/image-20240811210412566.png new file mode 100644 index 0000000..d05ec5b Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20240811210412566.png differ diff --git a/05.监控告警/01.Zabbix/image-20240811210446030.png b/05.监控告警/01.Zabbix/image-20240811210446030.png new file mode 100644 index 0000000..3512f2f Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20240811210446030.png differ diff --git a/05.监控告警/01.Zabbix/image-20240811210609196.png b/05.监控告警/01.Zabbix/image-20240811210609196.png new file mode 100644 index 0000000..072ec6d Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20240811210609196.png differ diff --git a/05.监控告警/01.Zabbix/image-20240811210717780.png b/05.监控告警/01.Zabbix/image-20240811210717780.png new file mode 100644 index 0000000..785c83d Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20240811210717780.png differ diff --git a/05.监控告警/01.Zabbix/image-20250215104654160.png b/05.监控告警/01.Zabbix/image-20250215104654160.png new file mode 100644 index 0000000..6bb108f Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250215104654160.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316143441157.png b/05.监控告警/01.Zabbix/image-20250316143441157.png new file mode 100644 index 0000000..189e016 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316143441157.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316143522811.png b/05.监控告警/01.Zabbix/image-20250316143522811.png new file mode 100644 index 0000000..4ed58c5 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316143522811.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316143612944.png b/05.监控告警/01.Zabbix/image-20250316143612944.png new file mode 100644 index 0000000..d6d4511 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316143612944.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316143728527.png b/05.监控告警/01.Zabbix/image-20250316143728527.png new file mode 100644 index 0000000..f44ddcc Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316143728527.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316143812080.png b/05.监控告警/01.Zabbix/image-20250316143812080.png new file mode 100644 index 0000000..ab5aa11 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316143812080.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316143838085.png b/05.监控告警/01.Zabbix/image-20250316143838085.png new file mode 100644 index 0000000..87c4bb2 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316143838085.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316144001404.png b/05.监控告警/01.Zabbix/image-20250316144001404.png new file mode 100644 index 0000000..8816601 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316144001404.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316144631981.png b/05.监控告警/01.Zabbix/image-20250316144631981.png new file mode 100644 index 0000000..896fdbf Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316144631981.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316144759012.png b/05.监控告警/01.Zabbix/image-20250316144759012.png new file mode 100644 index 0000000..de1626f Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316144759012.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316145451714.png b/05.监控告警/01.Zabbix/image-20250316145451714.png new file mode 100644 index 0000000..481ff78 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316145451714.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316145719393.png b/05.监控告警/01.Zabbix/image-20250316145719393.png new file mode 100644 index 0000000..184eaed Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316145719393.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316145750925.png b/05.监控告警/01.Zabbix/image-20250316145750925.png new file mode 100644 index 0000000..ab2b30c Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316145750925.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316145935907.png b/05.监控告警/01.Zabbix/image-20250316145935907.png new file mode 100644 index 0000000..62c6d5d Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316145935907.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316151257316.png b/05.监控告警/01.Zabbix/image-20250316151257316.png new file mode 100644 index 0000000..b425369 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316151257316.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316151353990.png b/05.监控告警/01.Zabbix/image-20250316151353990.png new file mode 100644 index 0000000..36dc02a Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316151353990.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316152132163.png b/05.监控告警/01.Zabbix/image-20250316152132163.png new file mode 100644 index 0000000..9f9e273 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316152132163.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316152222209.png b/05.监控告警/01.Zabbix/image-20250316152222209.png new file mode 100644 index 0000000..aa27bbb Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316152222209.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316152330210.png b/05.监控告警/01.Zabbix/image-20250316152330210.png new file mode 100644 index 0000000..cbcf1b6 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316152330210.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316152417222.png b/05.监控告警/01.Zabbix/image-20250316152417222.png new file mode 100644 index 0000000..5857ca5 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316152417222.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316152509112.png b/05.监控告警/01.Zabbix/image-20250316152509112.png new file mode 100644 index 0000000..476f4fa Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316152509112.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316152703035.png b/05.监控告警/01.Zabbix/image-20250316152703035.png new file mode 100644 index 0000000..b045fa0 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316152703035.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316152744501.png b/05.监控告警/01.Zabbix/image-20250316152744501.png new file mode 100644 index 0000000..f874ece Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316152744501.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316153251565.png b/05.监控告警/01.Zabbix/image-20250316153251565.png new file mode 100644 index 0000000..64c26d1 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316153251565.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316153912958.png b/05.监控告警/01.Zabbix/image-20250316153912958.png new file mode 100644 index 0000000..a78f30b Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316153912958.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316154138064.png b/05.监控告警/01.Zabbix/image-20250316154138064.png new file mode 100644 index 0000000..112e86f Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316154138064.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316154236124.png b/05.监控告警/01.Zabbix/image-20250316154236124.png new file mode 100644 index 0000000..55c642e Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316154236124.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316155758866.png b/05.监控告警/01.Zabbix/image-20250316155758866.png new file mode 100644 index 0000000..1a12bef Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316155758866.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316155845820.png b/05.监控告警/01.Zabbix/image-20250316155845820.png new file mode 100644 index 0000000..79c8aa9 Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316155845820.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316155928572.png b/05.监控告警/01.Zabbix/image-20250316155928572.png new file mode 100644 index 0000000..cc287eb Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316155928572.png differ diff --git a/05.监控告警/01.Zabbix/image-20250316165313573.png b/05.监控告警/01.Zabbix/image-20250316165313573.png new file mode 100644 index 0000000..28c536d Binary files /dev/null and b/05.监控告警/01.Zabbix/image-20250316165313573.png differ diff --git a/05.监控告警/02.Prometheus.md b/05.监控告警/02.Prometheus.md new file mode 100644 index 0000000..ef9fe3a --- /dev/null +++ b/05.监控告警/02.Prometheus.md @@ -0,0 +1,1552 @@ +# 1. Prometheus监控系统 + +# 2. Prometheus简介 + +## 2.1 什么是Prometheus? + +Prometheus是由前 Google 工程师从 2012 年开始在 Soundcloud以开源软件的形式进行研发的系统监控和告警工具包,自此以后,许多公司和组织都采用了 Prometheus 作为监控告警工具。Prometheus 的开发者和用户社区非常活跃,它现在是一个独立的开源项目,可以独立于任何公司进行维护。为了证明这一点,Prometheus 于 2016 年 5 月加入CNCF基金会,成为继 Kubernetes 之后的第二个 CNCF 托管项目。 + +## 2.2 Prometheus的优势 + +Prometheus 的主要优势有: + +- 由指标名称和和键/值对标签标识的时间序列数据组成的多维数据模型 +- 强大的查询语言 PromQL +- 不依赖分布式存储;单个服务节点具有自治能力。 +- 时间序列数据是服务端通过 HTTP 协议主动拉取获得的。 +- 也可以通过中间网关来推送时间序列数据 +- 可以通过静态配置文件或服务发现来获取监控目标。 +- 支持多种类型的图表和仪表盘。 + +## 2.3 Prometheus的组件、架构 + +Prometheus 的整体架构以及生态系统组件如下图所示: + +![img](02.Prometheus/9Qt5yi.jpg) + +Prometheus Server 直接从监控目标中或者间接通过推送网关来拉取监控指标,它在本地存储所有抓取到的样本数据,并对此数据执行一系列规则,以汇总和记录现有数据的新时间序列或生成告警。可以通过 Grafana或者其他工具来实现监控数据的可视化。 + +- **Prometheus server**是Prometheus架构中的**核心组件**,基于go语言编写而成,无第三方依赖关系,可以独立部署在物理服务器上、云主机、Docker容器内。主要用于收集每个目标数据,并存储为时间序列数据,对外可提供数据查询支持和告警规则配置管理。 +- Prometheus服务器可以对监控目标进行静态配置管理或者动态配置管理,**它将监控采集到的数据按照时间序列存储在本地磁盘的时序数据库中**(当然也支持远程存储),自身对外提供了自定义的PromQL语言,可以对数据进行查询和分析 +- Client Library是用于检测应用程序代码的客户端库。在监控服务之前,需要向客户端库代码添加检测实现Prometheus中metric的类型。 +- **Exporter**(数据采集)用于**输出被监控组件信息的HTTP**接口统称为Exporter(导出器)。目前互联网公司常用的组件大部分都有Expoter供**直接使用**,比如Nginx、MySQL、linux系统信息等。 +- **Pushgateway**是指用于支持短期临时或批量计划任务工作的汇聚节点。主要用于短期的job,此类存在的job时间较短,可能在Prometheus来pull之前就自动消失了。所以针对这类job,设计成可以直接向Pushgateway推送metric,这样Prometheus服务器端便可以定时去Pushgateway拉去metric + - Pushgateway是prometheus的一个组件,prometheus server默认是通过exporter主动获取数据(默认采取pull拉取数据),pushgateway则是通过被动方式推送数据到prometheus server,用户可以写一些自定义的监控脚本把需要监控的数据发送给pushgateway, 然后pushgateway再把数据发送给Prometheus server + - 总结就是pushgateway是prometheus的一个组件,是通过被动的方式将数据上传至prometheus。这个可以解决不在一个网段的问题 +- **Alertmanager**主要用于处理Prometheus服务器端发送的alerts信息,对其去除重数据、分组并路由到正确的接收方式,发出**告警**,支持丰富的告警方式。 +- **Service Discovery:**动态发现待监控的target,从而完成监控配置的重要组件,在容器环境中尤为重要,该组件目前由Prometheus Server内建支持 + +参考博客:https://zhuanlan.zhihu.com/p/710612579 + +## 2.4 Prometheus适用于什么场景 + +Prometheus适用于记录**文本格式的时间序列**,它既适用于以机器为中心的监控,也适用于高度动态的面向服务的监控,在微服务的世界中,它对多维数据收集和查询的支持有特殊优势。Prometheus是专为提高系统可靠性而设计的,它可以在断电期间快速诊断问题,每个Prometheus Server都是相互独立的,不依赖于网络存储或者其他远程服务。当基础架构出现问题时,你可以通过Prometheus快速定位故障点,而且不会消耗大量的基础架构资源。 + +## 2.5 Prometheus不适合什么场景 + +Prometheus非常重视可靠性,即使在出现故障的情况下,你也可以随时统计有关系统的可用系统信息。如果你需要百分之百的准确度,例如按请求数量计费,那么Prometheus可能不太适合你,因为它收集的数据可能**不够详细完整精确**。 + +# 3. Prometheus部署 + +## 3.1 二进制部署 + +1. 下载prometheus的二进制包 + + 官网地址:https://prometheus.io/download/ + +```bash +[root@server1 ~]# wget https://github.com/prometheus/prometheus/releases/download/v2.47.0/prometheus-2.47.0.linux-amd64.tar.gz +``` + +2. 获取软件包的哈希值,与官网提供的软件包的哈希值进行对比,保证下载的Prometheus软件包的完整性 + +```bash +[root@server1 ~]# sha256sum prometheus-2.47.0.linux-amd64.tar.gz +277ad9f110ded8e326bc885848952941e839fa38dd3237e36415f0fa35a04424 prometheus-2.47.0.linux-amd64.tar.gz +``` + +3. 解压软件包到指定目录 + +```bash +[root@server1 ~]# mkdir /data +[root@server1 ~]# tar -zxvf prometheus-2.47.0.linux-amd64.tar.gz -C /data/ +[root@server1 ~]# cd /data/ +[root@server1 data]# chown -R root:root /data/prometheus-2.47.0.linux-amd64 +[root@server1 data]# ln -sv prometheus-2.47.0.linux-amd64 prometheus +"prometheus" -> "prometheus-2.47.0.linux-amd64" +``` + +### 3.1.1 前台启动 + +1. 启动Prometheus,会输出如下信息,此时当终端关闭或者按下ctrl + c服务会自动关闭 + +```bash +[root@server1 data]# cd /data/prometheus +[root@server1 prometheus]# ./prometheus +level=info ts=2021-02-28T06:03:36.885Z caller=main.go:366 msg="No time or size retention was set so using the default time retention" duration=15d +level=info ts=2021-02-28T06:03:36.885Z caller=main.go:404 msg="Starting Prometheus" version="(version=2.47.0, branch=HEAD, revision=a6be548dbc17780d562a39c0e4bd0bd4c00ad6e2)" +level=info ts=2021-02-28T06:03:36.885Z caller=main.go:409 build_context="(go=go1.15.8, user=root@615f028225c9, date=20210217-14:17:24)" +level=info ts=2021-02-28T06:03:36.885Z caller=main.go:410 host_details="(Linux 3.10.0-693.el7.x86_64 #1 SMP Tue Aug 22 21:09:27 UTC 2017 x86_64 server1 (none))" +level=info ts=2021-02-28T06:03:36.885Z caller=main.go:411 fd_limits="(soft=1024, hard=4096)" +level=info ts=2021-02-28T06:03:36.885Z caller=main.go:412 vm_limits="(soft=unlimited, hard=unlimited)" +level=info ts=2021-02-28T06:03:36.891Z caller=web.go:532 component=web msg="Start listening for connections" address=0.0.0.0:9090 +level=info ts=2021-02-28T06:03:36.895Z caller=main.go:779 msg="Starting TSDB ..." +level=info ts=2021-02-28T06:03:36.897Z caller=tls_config.go:191 component=web msg="TLS is disabled." http2=false +level=info ts=2021-02-28T06:03:36.930Z caller=head.go:668 component=tsdb msg="Replaying on-disk memory mappable chunks if any" +level=info ts=2021-02-28T06:03:36.930Z caller=head.go:682 component=tsdb msg="On-disk memory mappable chunks replay completed" duration=5.69µs +level=info ts=2021-02-28T06:03:36.930Z caller=head.go:688 component=tsdb msg="Replaying WAL, this may take a while" +level=info ts=2021-02-28T06:03:36.933Z caller=head.go:740 component=tsdb msg="WAL segment loaded" segment=0 maxSegment=0 +level=info ts=2021-02-28T06:03:36.933Z caller=head.go:745 component=tsdb msg="WAL replay completed" checkpoint_replay_duration=38.416µs wal_replay_duration=2.357106ms total_replay_duration=2.638813ms +level=info ts=2021-02-28T06:03:36.934Z caller=main.go:799 fs_type=XFS_SUPER_MAGIC +level=info ts=2021-02-28T06:03:36.934Z caller=main.go:802 msg="TSDB started" +level=info ts=2021-02-28T06:03:36.934Z caller=main.go:928 msg="Loading configuration file" filename=prometheus.yml +``` + +2. 新开一个窗口,检查端口号以及关闭防火墙和selinux + +```bash +[root@server1 prometheus]# systemctl stop firewalld +[root@server1 prometheus]# setenforce 0 +[root@server1 prometheus~]# ss -nlt +State Recv-Q Send-Q Local Address:Port Peer Address:Port +LISTEN 0 128 *:22 *:* +LISTEN 0 100 127.0.0.1:25 *:* +LISTEN 0 128 :::22 :::* +LISTEN 0 100 ::1:25 :::* +LISTEN 0 128 :::9090 :::* +``` + +### 3.1.2 检查配置文件 + +在Prometheus日常维护中,一定会对配置文件prometheus.yml进行再编辑操作,通常对Prometheus服务进行重新启动操作即可完成对配置文件的加载。当然也可以通过动态的热加载来更新prometheus.yml中的配置信息 +查看进程id,向进程发送SIHHUP信号 + +```bash +# kill -HUP pid +``` + +通过HTTP API发送post请求到/-/reload + +```bash +# curl -X POST http://localhost:9090/-/reload +``` + +- 检查配置文件的语法正确性 + +```bash +[root@server1 ~]# cd /data/prometheus +[root@server1 prometheus]# ls +console_libraries consoles data LICENSE NOTICE prometheus prometheus.yml promtool +[root@server1 prometheus]# ./promtool check config prometheus.yml +Checking prometheus.yml + SUCCESS: 0 rule files found +``` + +### 3.1.3 创建自启动脚本 + +```bash +[root@server1 ~]# vim /usr/lib/systemd/system/prometheus.service +[Unit] +Description=Prometheus Server +Documentation=https://prometheus.io/docs/introduction/overview/ +After=network.target + +[Service] +Type=simple +Restart=on-failure +ExecStart=/data/prometheus/prometheus \ + --config.file=/data/prometheus/prometheus.yml \ + --storage.tsdb.path=/data/prometheus/data \ + --web.listen-address=:9090 \ + --web.enable-lifecycle +ExecReload=/bin/kill -HUP $MAINPID + +[Install] +WantedBy=multi-user.target +``` + +systemd重载配置以及启动prometheus + +```bash +[root@server1 ~]# systemctl daemon-reload +[root@server1 ~]# systemctl start prometheus +[root@server1 prometheus]# ss -nlt +State Recv-Q Send-Q Local Address:Port Peer Address:Port +LISTEN 0 128 *:22 *:* +LISTEN 0 100 127.0.0.1:25 *:* +LISTEN 0 128 :::22 :::* +LISTEN 0 100 ::1:25 :::* +LISTEN 0 128 :::9090 :::* +``` + +### 3.1.4 浏览器访问测试 + +image-20240812215230909 + +# 4. Exporter + +## 4.1 简介 + +在Prometheus的核心组件中,Exporter是重要的组成部分,在实际中**监控样本数据的收集**都是由Exporter完成的,Prometheus服务器只需要定时从这些Exporter提供的HTTP服务获取数据即可。官方提供了多种常用的Exporter,比如用于对数据库监控的mysqld_exporter和redis_exporter等。 + +Exporter本质上是将收集的数据转化为对应的文本格式,并提供HTTP接口,供Prometheus定期采集数据。 + +## 4.2 Exporter类型 + +- 直接采集型 + - 这类Exporter直接内置了响应的应用程序,用于向Prometheus直接提供target数据支持。这样设计的好处是,可以更好地监控各自系统内部的运行状态,同时也适合更多自定义监控指标的项目实施。 +- 间接采集型 + - 原始监控目标并不直接支持Prometheus,需要我们使用Prometheus提供的客户端库编写该监控目标的监控采集数据,用户可以将该程序独立运行,取获取指定的各类监控数据值。例如,由于Linux操作系统自身并不能直接支持Prometheus,用户无法从操作系统层面上直接提供对Prometheus的支持,因此单独提供Node Exporter,还有数据库或网站HTTP应用类等Exporter。 + +## 4.3 文本数据格式 + +在Prometheus的监控环境中,所有返回监控样本数据的Exporter程序,均需要遵守Prometheus规范,即基于文本的数据格式,其特点是具有更好的跨平台和可读性。 + +- 可以使用浏览器,或者通过curl工具来获得采集数据 + +```bash +# 以HELP开头的行,表示metric的帮助与说明解释,可以包含当前监控指标名称和对应的说明信息 +# 以TYPE开始的行,表示定义metric的类型 +# 以非#开头的行即监控样本的数据,包括metric_name{label_name1,label_name2...} value [timestamp可选项] + +[root@server1 ~]# curl 192.168.88.10:9090/metrics +# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{quantile="0"} 9.4601e-05 +go_gc_duration_seconds{quantile="0.25"} 0.000141153 +go_gc_duration_seconds{quantile="0.5"} 0.000416738 +go_gc_duration_seconds{quantile="0.75"} 0.001050261 +go_gc_duration_seconds{quantile="1"} 0.008308442 +go_gc_duration_seconds_sum 0.014675204 +go_gc_duration_seconds_count 13 +# HELP go_goroutines Number of goroutines that currently exist. +# TYPE go_goroutines gauge +go_goroutines 32 +# HELP go_info Information about the Go environment. +# TYPE go_info gauge +go_info{version="go1.15.8"} 1 +# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. +# TYPE go_memstats_alloc_bytes gauge +go_memstats_alloc_bytes 1.6849432e+07 +# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed. +# TYPE go_memstats_alloc_bytes_total counter +go_memstats_alloc_bytes_total 6.016028e+07 +# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. +# TYPE go_memstats_buck_hash_sys_bytes gauge +go_memstats_buck_hash_sys_bytes 1.461736e+06 +# HELP go_memstats_frees_total Total number of frees. +# TYPE go_memstats_frees_total counter +go_memstats_frees_total 219435 +# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started. +# TYPE go_memstats_gc_cpu_fraction gauge +go_memstats_gc_cpu_fraction 3.012333999853177e-05 +# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. +# TYPE go_memstats_gc_sys_bytes gauge +go_memstats_gc_sys_bytes 5.719272e+06 +``` + +## 4.4 Linux主机监控 + +Prometheus社区很活跃,提供了非常多类型的Exporter。可以在官网中找到自己想要的Exporter并进行下载https://prometheus.io/download/ + +由于Linux操作系统自身并不支持Prometheus,所以Prometheus官方提供了go语言编写的Node Exporter来实现对Linux操作系统主机的监控数据采集。它提供了系统内部几乎所有的标准指标,如cpu、内存、磁盘空间、磁盘I/O、系统负载和网络带宽。另外它还提供了由内核公开的大量额外监控指标,从负载平均到主板温度等。 + +### 4.4.1 安装Exporter + +1. 下载node exporter的二进制包并解压 + +```bash +[root@server2 ~]# wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz +[root@server2 ~]# mkdir /data +[root@server2 ~]# tar -zxvf node_exporter-1.8.2.linux-amd64.tar.gz -C /data/ +node_exporter-1.8.2.linux-amd64/ +node_exporter-1.8.2.linux-amd64/NOTICE +node_exporter-1.8.2.linux-amd64/node_exporter +node_exporter-1.8.2.linux-amd64/LICENSE +[root@server2 ~]# cd /data/ +[root@server2 data]# chown -R root:root node_exporter-1.8.2.linux-amd64/ +[root@server2 data]# ln -sv node_exporter-1.8.2.linux-amd64 node_exporter +"node_exporter" -> "node_exporter-1.8.2.linux-amd64" +``` + +2. 启动node_exporter + +```bash +[root@server2 node_exporter]# ./node_exporter +``` + +### 4.4.2 关联Prometheus server + +当启动node_exporter开始工作时,node_exporter和Prometheus server还没有进行关联,二者各自独立没有关联。 + +可以在Prometheus server中,找到主机目录,找到主配置文件,使用其中的静态配置功能static_configs来采集node_exporter提供的数据 + +**server主配置文件介绍:** + +```bash +[root@server1 prometheus]# cat prometheus.yml +# my global config +global: + scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minut e. + evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. + # scrape_timeout is set to the global default (10s). + +# Alertmanager configuration +alerting: + alertmanagers: + - static_configs: + - targets: + # - alertmanager:9093 + +# Load rules once and periodically evaluate them according to the global 'evaluation_interval' . +rule_files: + # - "first_rules.yml" + # - "second_rules.yml" + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + # The job name is added as a label `job=` to any timeseries scraped from this conf ig. + - job_name: "prometheus" + + # metrics_path defaults to '/metrics' + # scheme defaults to 'http'. + + static_configs: + - targets: ["localhost:9090"] + + +================================================================================== +# 配置文件解释 +global: + scrape_interval:每次数据采集的时间间隔,默认为1分钟 + scrape_timeout:采集请求超时时间,默认为10秒 + evaluation_interval:执行rules的频率,默认为1分钟 +scrape_configs:主要用于配置被采集数据节点操作,每一个采集配置主要由以下几个参数 + job_name:全局唯一名称 + scrape_interval:默认等于global内设置的参数,设置后可以覆盖global中的值 + scrape_timeout:默认等于global内设置的参数 + metrics_path:从targets获取meitric的HTTP资源路径,默认是/metrics + honor_labels:Prometheus如何处理标签之间的冲突。若设置为True,则通过保留变迁来解决冲突;若设置为false,则通过重命名; + scheme:用于请求的协议方式,默认是http + params:数据采集访问时HTTP URL设定的参数 + relabel_configs:采集数据重置标签配置 + metric_relabel_configs:重置标签配置 + sample_limit:对每个被已知样本数量的每次采集进行限制,如果超过限制,该数据将被视为失败。默认值为0,表示无限制 +``` + +- 直接编辑主配置文件,添加job与node_exporter关联 + +```bash +scrape_configs: + # The job name is added as a label `job=` to any timeseries scraped from this config. + - job_name: "prometheus" + + # metrics_path defaults to '/metrics' + # scheme defaults to 'http'. + + static_configs: + - targets: ["localhost:9090"] + - job_name: "node_exporter" + static_configs: + - targets: ["192.168.88.20:9100"] + +``` + +- 检查配置文件并且重启服务 + +```bash +[root@server1 prometheus]# ./promtool check config prometheus.yml +Checking prometheus.yml + SUCCESS: prometheus.yml is valid prometheus config file syntax + +[root@server1 prometheus]# systemctl restart prometheus +[root@server1 prometheus]# ss -nlt +State Recv-Q Send-Q Local Address:Port Peer Address:Port +LISTEN 0 128 *:22 *:* +LISTEN 0 100 127.0.0.1:25 *:* +LISTEN 0 128 :::22 :::* +LISTEN 0 100 ::1:25 :::* +LISTEN 0 128 :::9090 :::* +``` + +### 4.4.3 查看Targets + +重启服务即可成功关联 + +image-20240812221003882 + +### 4.4.4 metricts数据采集 + +`node_exporter` 是 Prometheus 生态系统中的一个重要组件,用于收集主机级别的系统指标(如 CPU、内存、磁盘、网络等)。以下是 `node_exporter` 常见的监控项及其含义 + +#### 4.4.4.1 常见采集项 + +1. **CPU 相关指标** + +- `node_cpu_seconds_total`:CPU 在每个模式(user、system、idle 等)下的使用时间(秒)。 + - 模式包括:`user`(用户态)、`system`(内核态)、`idle`(空闲)、`iowait`(等待 I/O)等。 +- **`node_cpu_cores`**:CPU 核心数。 +- **`node_cpu_frequency_hertz`**:CPU 频率。 + +------ + +2. **内存相关指标** + +- **`node_memory_MemTotal_bytes`**:系统总内存大小。 +- **`node_memory_MemFree_bytes`**:空闲内存大小。 +- **`node_memory_Buffers_bytes`**:缓冲区使用的内存大小。 +- **`node_memory_Cached_bytes`**:缓存使用的内存大小。 +- **`node_memory_SwapTotal_bytes`**:交换分区总大小。 +- **`node_memory_SwapFree_bytes`**:空闲交换分区大小。 + +------ + +3. **磁盘相关指标** + +- **`node_disk_read_bytes_total`**:磁盘读取的总字节数。 +- **`node_disk_written_bytes_total`**:磁盘写入的总字节数。 +- **`node_disk_reads_completed_total`**:完成的磁盘读取操作数。 +- **`node_disk_writes_completed_total`**:完成的磁盘写入操作数。 +- **`node_disk_io_time_seconds_total`**:磁盘 I/O 操作的总时间。 +- **`node_filesystem_size_bytes`**:文件系统总大小。 +- **`node_filesystem_free_bytes`**:文件系统空闲大小。 + +------ + +4. **网络相关指标** + +- **`node_network_receive_bytes_total`**:网络接口接收的总字节数。 +- **`node_network_transmit_bytes_total`**:网络接口发送的总字节数。 +- **`node_network_receive_packets_total`**:网络接口接收的数据包总数。 +- **`node_network_transmit_packets_total`**:网络接口发送的数据包总数。 + +------ + +5. **系统负载相关指标** + +- **`node_load1`**:1 分钟内的系统平均负载。 +- **`node_load5`**:5 分钟内的系统平均负载。 +- **`node_load15`**:15 分钟内的系统平均负载。 + +------ + +6. **进程相关指标** + +- **`node_procs_running`**:当前正在运行的进程数。 +- **`node_procs_blocked`**:当前被阻塞的进程数。 + +------ + +7. **文件描述符相关指标** + +- **`node_filefd_allocated`**:已分配的文件描述符数量。 +- **`node_filefd_maximum`**:文件描述符的最大数量。 + +------ + +8. **时间相关指标** + +- **`node_time_seconds`**:当前系统时间(Unix 时间戳)。 +- **`node_boot_time_seconds`**:系统启动时间(Unix 时间戳)。 + +------ + +9. **其他指标** + +- **`node_uname_info`**:系统信息(如内核版本、操作系统等)。 +- **`node_os_info`**:操作系统信息。 + +#### 4.4.4.2 示例 + +一、CPU 在每个模式(user、system、idle 等)下的使用时间(秒) + +node_cpu_seconds_total + +image-20240812221145707 + +可以通过PromQL(后面会介绍这种查询语言)表达式进行查询,计算每核cpu每秒的空闲时间,然后对主机上的所有cpu求平均值 + +`avg without(cpu,mode) (rate(node_cpu_seconds_total {mode="idle"} [1m]))` + +image-20240812221244308 + +二、内存信息采集 + +当前主机内存查看 + +```bash +[root@server2 node_exporter]# free -b + total used free shared buff/cache available +Mem: 1907970048 112123904 1564102656 10014720 231743488 1609289728 +Swap: 2147479552 0 2147479552 +``` + +总内存:node_memory_MemTotal_bytes + +![image-20240812221435923](02.Prometheus/image-20240812221435923.png) + +空闲内存:node_memory_MemFree_bytes + +image-20240812221600846 + +三、磁盘信息采集 + +磁盘 I/O 操作的总时间:node_disk_io_time_seconds_total + +image-20240812221733204 + +四、文件系统采集 + +node_filesystem_size_bytes + +image-20240812221817805 + +五、网络信息采集 + +node_network_info + +image-20240812221939911 + +任意一个Exporter都会提供足够多的metric,我们在学习的时候也不需要关心具体有多少metric,每个metric具体意思(其实见名知义大概也可以猜到) + +## 4.5 MySQL监控 + +### 4.5.1 部署MySQL环境 + +```bash +[root@server2 ~]# yum install -y mariadb-server +# 启动mariadb数据库 +[root@server2 ~]# systemctl enable --now mariadb +# 初始化数据库并设置密码 +[root@server2 ~]# mysqladmin -uroot password '123456' +# 测试数据库连接是否正常 +[root@server2 ~]# mysql -uroot -p123456 -e "show databases;" ++--------------------+ +| Database | ++--------------------+ +| information_schema | +| mysql | +| performance_schema | +| test | ++--------------------+ + +# 创建exporter用户 +ariaDB [(none)]> grant all privileges on *.* to mysqld_exporter@'localhost' identified by '123456'; +Query OK, 0 rows affected (0.00 sec) +# 刷新账户信息 +MariaDB [(none)]> flush privileges; +Query OK, 0 rows affected (0.00 sec) +``` + +### 4.5.2 安装Exporter + +官网下载mysqld_exporter二进制包解压缩 + +```bash +[root@server2 ~]# wget https://github.com/prometheus/mysqld_exporter/releases/download/v0.15.1/mysqld_exporter-0.15.1.linux-amd64.tar.gz +[root@server2 ~]# tar -zxvf mysqld_exporter-0.15.1.linux-amd64.tar.gz -C /data/ +mysqld_exporter-0.15.1.linux-amd64/ +mysqld_exporter-0.15.1.linux-amd64/LICENSE +mysqld_exporter-0.15.1.linux-amd64/mysqld_exporter +mysqld_exporter-0.15.1.linux-amd64/NOTICE +[root@server2 ~]# cd /data/ +[root@server2 data]# chown -R root:root mysqld_exporter-0.15.1.linux-amd64/ +[root@server2 data]# ln -sv mysqld_exporter-0.15.1.linux-amd64/ mysqld_exporter +"mysqld_exporter" -> "mysqld_exporter-0.15.1.linux-amd64/" +``` + +### 4.5.3 配置Exporter + +```bash +[root@server2 mysqld_exporter]# pwd +/data/mysqld_exporter + +# 创建一个配置文件.mysqld_exporter.cnf +[root@server2 mysqld_exporter]# vim .mysqld_exporter.cnf +[client] +user=mysqld_exporter +password=123456 + +[root@server2 mysqld_exporter]# ./mysqld_exporter --config.my-cnf='.mysqld_exporter.cnf' & +[root@server2 mysqld_exporter]# ss -nlt +State Recv-Q Send-Q Local Address:Port Peer Address:Port +LISTEN 0 50 *:3306 *:* +LISTEN 0 128 *:22 *:* +LISTEN 0 100 127.0.0.1:25 *:* +LISTEN 0 128 :::9100 :::* +LISTEN 0 128 :::9104 :::* +LISTEN 0 128 :::22 :::* +LISTEN 0 100 ::1:25 :::* +``` + +### 4.5.4 关联Prometheus server + +```bash +[root@server1 prometheus]# vim prometheus.yml + - job_name: 'mysqld_exporter' + scrape_interval: 10s + static_configs: + - targets: [192.168.88.20:9104] + +[root@server1 prometheus]# ./promtool check config prometheus.yml +Checking prometheus.yml + SUCCESS: prometheus.yml is valid prometheus config file syntax + +[root@server1 prometheus]# systemctl restart prometheus +``` + +image-20240812223721673 + +### 4.5.5 metricts数据采集 + +MySQL数据库的性能状态监控内容非常多,但通常必不可少的内容包括查询吞吐量、查询执行性能、连接情况、缓冲池使用情况等。 + +#### 4.5.5.1 常见采集项 + +1. **MySQL状态信息**: + - `mysql_global_status_connections`: 当前连接数。 + - `mysql_global_status_threads_connected`: 当前连接的线程数。 + - `mysql_global_status_threads_running`: 当前正在运行的线程数。 + +---- + +2. **查询性能**: + +- `mysql_global_status_questions`: 服务器启动以来执行的查询总数。 +- `mysql_global_status_slow_queries`: 慢查询的数量。 + +---- + +3. **连接和线程**: + +- `mysql_global_status_max_used_connections`: 服务器启动以来同时使用的最大连接数。 +- `mysql_global_status_aborted_connects`: 失败的连接尝试次数。 + +---- + +4. **InnoDB存储引擎**: + +- `mysql_global_status_innodb_buffer_pool_read_requests`: InnoDB缓冲池的读请求次数。 +- `mysql_global_status_innodb_buffer_pool_reads`: 从磁盘读取的次数。 +- `mysql_global_status_innodb_buffer_pool_pages_free`: InnoDB缓冲池中空闲的页数。 +- `mysql_global_status_innodb_row_lock_waits`: InnoDB行锁等待的次数。 + +---- + +5. **网络流量**: + +- `mysql_global_status_bytes_received`: 从所有客户端接收的字节数。 +- `mysql_global_status_bytes_sent`: 发送到所有客户端的字节数。 + +---- + +6. **表缓存**: + +- `mysql_global_status_table_open_cache_hits`: 表缓存命中次数。 +- `mysql_global_status_table_open_cache_misses`: 表缓存未命中次数。 + +---- + +7. **复制状态**(如果启用了复制): + +- `mysql_slave_status_slave_io_running`: 从库I/O线程是否正在运行。 +- `mysql_slave_status_slave_sql_running`: 从库SQL线程是否正在运行。 +- `mysql_slave_status_seconds_behind_master`: 从库落后主库的秒数。 + +#### 4.5.5.2 示例 + +一、查询吞吐量,MySQL客户端应用程序发送的所有查询语句,该计数器都是递增的 + +mysql_global_status_questions + +image-20240812230423852 + +二、查询执行性能,每当查询时间超过预先设定的慢查询时间计数器都会递增 + +mysql_global_status_slow_queries + +image-20240812230457755 + +三、连接情况,查询MySQL所设置的最大连接数,防止连接数量过大导致服务器过载运行 + +mysql_global_variables_max_connections + +image-20240812230536434 + +四、查看当前连接数 + +mysql_global_status_threads_connected + +image-20240812230621725 + +五、缓存池使用情况 + +mysql_global_status_innodb_buffer_pool_bytes_data4 + +image-20240812230703411 + +Prometheus大多数情况下,显示的默认单位为字节,必要时候,可以自己进行单位换算 + +mysql_global_status_innodb_buffer_pool_bytes_data/1024/1024 + +存储单位通常以 **1024** 为基数进行换算(二进制方式),但在某些场景下也可能以 **1000** 为基数(十进制方式)。以下是常见的存储单位及其换算关系: + +| 单位 | 缩写 | 换算关系(二进制) | 换算关系(十进制) | +| :----- | :--- | :------------------------------- | :------------------------------- | +| 字节 | B | 1 B = 8 bit | 1 B = 8 bit | +| 千字节 | KB | 1 KB = 1024 B | 1 KB = 1000 B | +| 兆字节 | MB | 1 MB = 1024 KB = 1,048,576 B | 1 MB = 1000 KB = 1,000,000 B | +| 吉字节 | GB | 1 GB = 1024 MB = 1,073,741,824 B | 1 GB = 1000 MB = 1,000,000,000 B | +| 太字节 | TB | 1 TB = 1024 GB | 1 TB = 1000 GB | +| 拍字节 | PB | 1 PB = 1024 TB | 1 PB = 1000 TB | +| 艾字节 | EB | 1 EB = 1024 PB | 1 EB = 1000 PB | +| 泽字节 | ZB | 1 ZB = 1024 EB | 1 ZB = 1000 EB | +| 尧字节 | YB | 1 YB = 1024 ZB | 1 YB = 1000 ZB | + +# 5. 服务发现 + +Prometheus服务发现能够自动化检测分类,并且能够识别新目标和变更目标。也就是说,可以自动发现并监控目标或变更目标,动态进行数据采集和处理。 + +## 5.1 基于文件的服务发现 + +- 准备JSON格式的文件 + +```bash +[root@server1 ~]# cd /data/prometheus +[root@server1 prometheus]# mkdir targets +[root@server1 prometheus]# vim targets/dev_node.json +[{ + "targets": [ "192.168.88.20:9100","192.168.88.20:9104" ], + "labels": { + "env": "dev_webgame" + } +}] +------------------------------------------------------------------- +或者这里是准备yaml文件,那么下面相应的配置文件需要与yaml匹配 +vim targets/dev_node.yml +- targets: + - "192.168.88.20:9100" + - "192.168.88.20:9104" +``` + +- 修改配置文件 + +```bash +[root@server1 prometheus]# vim /data/prometheus/prometheus.yml + - job_name: 'node_service_discovery' + file_sd_configs: + - files: + - targets/*.yml + refresh_interval: 60s +``` + +- 重新启动服务 + +扩展:这是基于文件发现,还有基于consul基于dns的服务发现,这个自行扩展。 + +# 6. 相关概念 + +## 6.1 Jobs和Instances + +在Prometheus中,任何被采集的目标,即每一个暴露监控样本数据的HTTP服务都称为**一个实例instance**,**通常对应于单个进程**。而**具有相同采集目的实例集合称为作业job**。 + +## 6.2 数据模型 + +Prometheus所有采集的监控数据均以**指标的形式**保存在内置的时间序列数据库当中(TSDB):属于同一**指标名称**、同一**标签**集合的、有**时间戳**标记的数据流。除了存储的时间序列,Prometheus还可以根据查询请求产生临时的、衍生的时间序列作为返回结果。 + +### 6.2.1 指标名称和标签 + +每一条时间序列由指标名称(Metric Name)以及一组标签(键值对)唯一标识。其中指标的名称(Metric Name)可以反映被监控样本的含义(例如,http_request_total可以看出来表示当前系统接收到的http请求总量),指标名称只能由ASCII字符、数字、下划线以及冒号组成,同时必须匹配正则表达式`[a-zA-Z_:][a-zA-Z0-9_:]*`。 + +**注意** + +冒号用来表示用户自定义的记录规则,不能在 exporter 中或监控对象直接暴露的指标中使用冒号来定义指标名称。 + +通过使用标签,Prometheus开启了强大的多维数据模型:对于相同的指标名称,通过不同标签列表的集合,会形成特定的度量维度实例(例如,所有包含度量名称为 `/api/tracks` 的 http 请求,打上 `method=POST` 的标签,就会形成具体的 http 请求)。查询语言在这些指标和标签列表的基础上进行过滤和聚合,改变任何度量指标上的任何标签值(包括添加或删除指标),都会创建新的时间序列。 + +标签的名称只能由ASCII字符、数字、以及下划线组成并满足正则表达式`[a-zA-Z_][a-zA-Z0-9_]*`。其中以 `__` 作为前缀的标签,是系统保留的关键字,只能在系统内部使用。标签的值则可以包含任何 `Unicode` 编码的字符。 + +### 6.2.2 样本 + +在时间序列中的每一个点称为样本,样本由以下三部分组成: + +- 指标(metric):指标名称和描述当前样本特征的labelset; +- 时间戳:一个精确到时间毫秒的时间戳 +- 样本值:一个浮点型数据表示当前样本的值 + +### 6.2.3 表示方式 + +通过如下表示方式表示指定名称和指定标签集合的时间序列 + +``` +{