Bootstrap

第八周作业

1. 总结 哨兵机制实现原理,并搭建主从哨兵集群。

哨兵集群中的每个节点都会启动三个定时任务

  • 第一个定时任务: 每个sentinel节点每隔1s向所有的master、slaver、别的sentinel节点发送一个PING命令,作用:心跳检测
  • 第二个定时任务: 每个sentinel每隔2s都会向master的__sentinel__:hello这个channel中发送自己掌握的集群信息和自己的一些信息(比如host,ip,run id),这个是利用redis的pub/sub功能,每个sentinel节点都会订阅这个channel,也就是说,每个sentinel节点都可以知道别的sentinel节点掌握的集群信息,作用:信息交换,了解别的sentinel的信息和他们对于主节点的判断
  • 第三个定时任务: 每个sentinel节点每隔10s都会向master和slaver发送INFO命令,作用:发现最新的集群拓扑结构
    主观下线

      这个就是上面介绍的第一个定时任务做的事情,当sentinel节点向master发送一个PING命令,如果超过own-after-milliseconds(默认是30s,这个在sentinel的配置文件中可以自己配置)时间都没有收到有效回复,不好意思,我就认为你挂了,就是说为的主观下线(SDOWN),修改其flags状态为SRI_S_DOWN

  • 超过半数的哨兵认为此节点都down了,就会重新选举一个主出来,此故障迁移的过程会对redis的配置文件进行修改,重新指向新的主

哨兵集群:

先搭建主从集群(这里一主两从)

主:192.168.174.4

从1:192.168.174.5

从2:192.168.174.6

所有主从节点:

配置文件修改:

从节点:

echo "replicaof 192.168.174.4 6379" >> /etc/redis.conf

所有节点:

systemctl enable --now redis

主节点查看:

登录:redis-cli -a 123456

修改sentinel的配置文件:

[root@master ~]# cat /etc/redis-sentinel.conf |grep -Ev "^$|^#"
bind 0.0.0.0
port 26379
daemonize yes
pidfile "/var/run/redis-sentinel.pid"
logfile "/var/log/redis/sentinel.log"
dir "/var/lib/redis"
sentinel myid dbc3973f06f2c684fb6a5b67c81d8739a1b07ced
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 192.168.174.5 6379 2
sentinel auth-pass mymaster 123456
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
protected-mode no
supervised systemd
sentinel known-replica mymaster 192.168.174.4 6379
sentinel known-replica mymaster 192.168.174.6 6379
sentinel known-sentinel mymaster 192.168.174.5 26379 654c9a380be3b43692f4e62f79f43f605c283fda
sentinel known-sentinel mymaster 192.168.174.6 26379 dfc68c7f27d845d988298858ff29fc8f370daae1
sentinel current-epoch 1
[root@master ~]#

三个文件都保持相同,然后启动redis-sentinel:

然后killall redis-server

查看sentinel的日志,最终会主会切换到其他地方

配置文件也会自动修改


2. 总结redis cluster工作原理,并搭建集群实现扩缩容。

cluster模式解决了哨兵模式中单一主节点的性能瓶颈问题。无中心架构的cluster,cluster至少需要三个主节点,由hash槽的方式来分担业务,redis cluster有0-16383的槽,每一个key存储时先通过哈希计算和与16384取余得到一个数,这个数在谁的区间里就存给谁,并不是第一次就可以命中,第一次如果没有命中,会告诉你这个slot应该存哪。也符合集群能扩容的功能,不管扩不扩容,key计算出来的slot值是不变的,slot该归谁存又集群决定。

集群搭建:

环境:

版本redis5.0

192.168.174.4(M)

192.168.174.5(M)

192.168.174.6(M)

192.168.174.7(S)

192.168.174.8(S)

192.168.174.9(S)

所有节点:

dnf install redis -y

然后修改redis.conf文件

bind 0.0.0.0
masterauth 123456
requirepass 123456
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-require-full-coverage no

所有节点启动redis:

systemctl enable --now redis

第一个节点输入:

redis-cli -a 123456 --cluster create 192.168.174.4:6379 192.168.174.5:6379 192.168.174.6:6379 192.168.174.7:6379 192.168.174.8:6379 192.168.174.9:6379 --cluster-replicas 1

先查看主从状态:

这里看相当于有三个主从

再查看cluster的状态:

缩容:

输入下命令调整slots的分配,正常均分,考虑到redis服务器可能性能有差异,可以给性能高的多分配一点。

redis-cli -a 123456 --cluster reshard 192.168.174.4:6379

通过此命令先将192.168.174.6的5461个slot一半一半迁到172.4与174.5上

保证要删除的174.6上没有slot:

删除空的174.6节点和174.8节点,都是用此命令,用不同的node id:

最终结果:

扩容:

在缩容的基础上,再次将174.6,174.8两个节点添加回去

174.6与174.8:

killall redis-server

rm -f /var/lib/redis/*

systemctl restart redis

然后在集群中的一个节点执行(把6与8节点添加回去):

可见添加回去默认是主节点,还需要将其中新加的一个变成slave

登录174.8,将其设成slave:

指向174.6

再次查看集群状态:

将slot分一部分给新加进去的主:

redis-cli -a 123456 --cluster reshard 192.168.174.4:6379


3. 总结 LVS的NAT和DR模型工作原理,并完成DR模型实战。

NAT:

client请求资源,当报文达到director时,源和目标IP是CIP-VIP,IPVS会强行修改目标地址为RIP,将报文从INPUT发向POSTROUTING,源和目标IP修改为CIP-RIP,RS验证目标地址是本地地址,则接受报文并处理请求
RS响应请求,由于报文源IP是CIP,所以相应报文的目标IP是CIP;将响应报文发送给网关director后,director会通过SNAT将源IP修改为VIP,相当于中间人攻击

DR:

1) 客户端 发送请求到 Director Server,请求的数据报文(源 IP 是 CIP,目标 IP 是 VIP) 到达内核空间。
(2) Director Server 和 Real Server 在同一个网络中,数据通过二层数据链路层来传输。
(3) 内核空间判断数据包的目标 IP 是本机 VIP,此时 IPVS 比对数据包请求的服务是否是集群服务,是集群服务就重新封装数据包。修改源MAC 地址为 Director Server 的 MAC 地址,修改目标 MAC 地址为 Real Server 的 MAC 地址,源IP 地址与目标 IP 地址没有改 变,然后将数据包发送给 Real Server。(目的IP是配置在回环口上的VIP,目的MAC是RIP-MAC,物理网卡的MAC
(4) 到达 Real Server 的请求报文的 MAC 地址是自身的 MAC 地址,就接收此报文。数 据包重新封装报文(源 IP 地址为VIP,目标 IP 为 CIP),将响应报文通过 lo 接口传送给物理 网卡然后向外发出。
(5) Real Server 直接将响应报文传送到客户端。

实战:

客户端1:虚拟机,配置仅主机模式的网卡

路由器:创建一台linux虚拟机,双网卡,一个网卡NAT模式,一个网卡仅主机模式,开启ip forward转发模式

交换机:vmware在nat模式下,自带交换机,即所有nat的网卡可以看作接在了同一个虚拟交换机上,无需配置

LVS,RS1,RS2安装上图进行配置,网关需要修改成此路由器下接口的ip,不是vmware默认的网关

客户端配置:

路由器配置:

配置ip:仅主机网卡:192.168.188.3

NAT网卡:192.168.174.11

开启转发(在开启前后用客户端ping LVS看一下):

RS1、RS2配置:

安装并启动httpd服务

先修改内核参数,避免ip冲突
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce

 RS1,RS2配置回环口的vip:

ifconfig lo:1 192.168.174.3/32

LVS配置:

NAT网卡:192.168.174.12

回环口配置vip:

ifconfig lo:1 192.168.174.3/32

下载ipvsadm包,并配置策略

参数解析:

-A        add virtual service

-t        TCP

-s       调度算法,rr就是轮询

 -a        add real server

  -r       server-address    server-address is host (and port)

-g         gatewaying (direct routing) (default)

在客户端进行测试:

4. 总结 http协议的通信过程详解

1.在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更深层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80,tcp的80端口连上后才能进行后面的http通信

2.一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令,如GET。

3.客户机向服务器发出请求后,服务器会客户机回送应答

4.一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码Connection:keep-alive。TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽


5. 总结  网络IO模型和nginx架构

阻塞性IO(blocking IO):

用户线程通过系统调用read发起IO操作,由用户空间转到内核空间,数据包到内核后,再拷贝到用户空间完成read操作,在等待内核的过程中,线程被阻塞,不能干其他事

优点:程序简单

缺点:每一个连接都需要一个进程/线程单独处理,如1000个并发连接,会导致线程来回切换开销大

非阻塞性IO(nonblocking IO):

用户发起IO请求时立即返回,需要不断的进行请求,直到读到数据为止,发起请求有一个时间间隔。

IO多路复用型(IO mutiplexing):

一个线程可以同时(交替)监控和处理多个文件描述符对应的各自的IO,用户进程调用select,整个进程会被bolck,内核会监视所有select负责的socket,任何一个socket数据准备好了,select就返回,此时用户进程在调用read操作,从内核拷贝到用户进程中

信号驱动式IO(signal-driven IO):

进程不用等待,不用轮询,而是数据报准备好了后,发送信号通知进程,过来进行系统调用取数据,取数据的过程也是阻塞的。和阻塞性IO比,省去了无数据报准备好到数据报准备好的这段时间

异步IO(asyncchronous IO):

发起请求后无需等待,无需轮询,与信号驱动式的区别式,信号驱动是告诉你准备好了才让你发起请求,异步IO是你请求的时候不管准备与否,直接返回,准备好之后会把数据返回给你,整个过程非阻塞

nginx架构:

后台进程包含一个master进程和多个worker进程

master进程主要用来管理worker进程,worker进程异常停止会自动重启worker

worker进程:竞争处理网络请求,将请求送到各个模块进行处理,worker数一般为cpu核数


6. 完成nginx编译安装脚本

#!/bin/bash
create_nginx_user(){
id nginx &> /dev/null
if [ $? -eq 0 ];then
echo "nginx用户已创建"
else
echo "nginx用户创建中"
useradd -s /sbin/nologin -r nginx
fi
}
mkdir -p /apps/nginx &> /dev/null
nginx_DIR="/apps/nginx"
OS_cpu_core_num=$(lscpu|grep '^CPU(s)'|awk '{printf $2}')

# install depend
nginx_install(){
dnf -y install make gcc-c++ libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel perl-ExtUtils-Embed
cd /opt
if [ -e /opt/nginx-1.24.0.tar.gz ];then
tar zxvf nginx-1.24.0.tar.gz
cd /opt/nginx-1.24.0
else
echo "nginx安装包未找到,请检查opt下是否存放安装包"
exit
fi
./configure --prefix=/apps/nginx/ --user=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
make -j $OS_cpu_core_num && make install
if [ $? -eq 0 ];then
echo "nginx安装完成"
else
echo "nginx安装失败"
exit
fi
}

create_nginx_user
nginx_install


7. 总结nginx核心配置,并实现nginx多虚拟主机

配置:


user  nginx nginx; #启动nginx的用户与组
worker_processes  4;### 一般设成cpu核数
错误日志记录配置:语法 error_log  logs/error.log [debug|info|notice|warn|error|crit|alert|emerg]
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
 error_log /apps/nginx/logs/error.log error;

#pid        logs/nginx.pid;  #pid文件路径
pid         /apps/nginx/logs/nginx.pid

events {
    worker_connections  1024; ##单个进程的最大并发数
    use epoll;使用epoll事件驱动
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #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  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;
##每一个server对应一个虚拟主机

    server {
        listen       80;
        server_name  localhost; 虚拟主机名

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html; 指定URL特性
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

多虚拟主机:

这里基于端口不同来做:

nginx.conf文件:

修改html文件:

然后reload下nginx

测试:

端口号不同,web也不同,相当于可以当多个web服务器来使


8. 总结nginx日志格式定制

配置文件(只能在http块来配置,不能在server块):

然后reload下

打开网页进行测试:

;