下图为DR模型的通讯过程,图中的IP不要被扑结构中的IP迷惑,图里只是为了说明DR的通讯原理,应用到本例中的拓扑上其工做原理不变。html
服务器 | IP地址 | 角色 |
---|---|---|
Srv01 | 172.16.42.100 VIP: 172.16.42.111 |
LVS+Keepalive |
Srv02 | 172.16.42.101 VIP: 192.168.100.1 |
LVS+Keepalive |
Srv03 | 172.16.42.102 VIP: 172.16.42.111 |
Nginx |
Srv04 | 172.16.42.103 VIP: 172.16.42.111 |
Nginx |
另外,我这4台主机都是2个网卡, 其中有ens33是172.16.42.0/24这个网络,ens34是192.168.100.0/24网络,这个网络在本例中没有用,请忽略。算法
因为LVS服务器和后端服务器的网卡上都配置了VIP,那么当客户端联系VIP的时候确定是和LVS服务器的VIP进行通讯,而后由LVS服务器基于规则进行调度,咱们知道2层通讯是基于MAC地址的,那么首次通讯时客户端可能并不知道LVS服务器的MAC地址,那么就须要进行ARP广播来解析出VIP所在的服务器的MAC地址,那么显然对客户端进行ARP应答的只能是LVS服务器不能是后端服务器,因此咱们就要在后端上修改内核参数来禁止ARP应答和宣告。那么有2个内核参数表示这两个设置:后端
arp_ignore:表示接收到ARP广播时的响应级别,默认值为0bash
0,默认值,表示响应全部,只要对方查询的IP配置在我本身这台主机上且不管ARP请求从哪一个网卡进来,该主机都会响应服务器
1,收到该ARP请求的网卡IP与ARP请求的IP一致,该主机才响应网络
arp_announce:定义将本身的地址向外通告的级别,默认是0负载均衡
0,表示将本机全部的MAC地址都向外通告curl
1,多网卡主机且都配置了IP地址,那么该主机接入到网络时,不管哪一个网卡接入到网络,该网卡都会向外宣告本身全部的MAC地址,因此1表示若是IP不在这个接口上,就避免向外通告,可是不保证必定不会下外通告。jsp
2,仅向网卡IP直连的网络进行通告tcp
为何会有这些级别呢?由于主机能够有多个网卡,每一个网卡都对应一个网段,默认状况下这个多网卡主机只要接入网络它就会把本身所在的全部网络地址都向外进行通告。
因此对于后端服务器,也就是本例子中的Nginx,咱们应该在lo上配置子接口,且设置arp_ignore为1,arp_announce为2。
后端服务器的全部操做都是同样,我这里就演示一台。首先要保证2台后端服务器都安装了Nginx,我用Nginx只是为了后端提供一个Web服务而已,你使用Tomcat也是同样的。
经过这个命令查看当前服务器设置sysctl -a | egrep "arp_ignore|arp_announce"
能够看到这里每一个网卡都有这2个参数还有一个All也有,那应该配置在哪里呢?all表示全局,理论上来讲在all上配置就能够,可是为了保险咱们在ens33上也配置。
记住arp_announce配置为2;apr_ignore配置为1。
sysctl -w net.ipv4.conf.all.arp_announce=2 sysctl -w net.ipv4.conf.ens33.arp_announce=2
我使用sysctl -w来修改只是临时生效,重启就没有了,为了永久有效请修改
/etc/sysctl.conf
文件。
如今在说觉得什么arp_announce配置为2,我这个主机有2个网卡,每一个网卡所链接的是不一样网段。我要使用的是ens33上面这个172.16.42.0/24这个网段,且VIP也是这个网段,当我把ens33的apr_announce配置为2 的时候,这就意味着当这个ens33接入网络时它不会对外宣告ens34的网络设置,也不会对外宣告lo的网络设置(由于咱们要在lo上配置一个子接口,该接口的IP就是VIP),下面配置apr_ignore
sysctl -w net.ipv4.conf.all.arp_ignore=1 sysctl -w net.ipv4.conf.ens33.arp_ignore=1
下面设置lo的子接口,这里为何要32位呢?由于要把广播地址设置为本身,这样它的广播就不会广播到其余地方。
ifconfig lo:0 172.16.42.111 netmask 255.255.255.255 broadcast 172.16.42.111 up
下面咱们在Srv01上ping一下这个地址,发现没有人应答,因此ping不通。
接下来添加一条路由,其目的是因为后端服务器是直接应答客户端请求的,因此就须要确保应答是其源IP必定是VIP(由于客户端请求的就是VIP),可是当LVS修改完数据包发送给后端服务器时,使用的是后端服务器的真实IP地址进行通讯的,而网络通讯是请求入栈是哪一个接口,响应出栈还走哪一个接口,这就势必致使后端服务器会使用本身的真实IP而不是VIP对客户端响应,这确定是不对的,因此咱们要经过这一条路由设置让ens33网卡收到数据包后转发给lo:0这个子接口,这样响应出栈的时候就会通过lo:0,这样也就会把响应报文的源IP设置为VIP了。
route add -host 172.16.42.111 dev lo:0
这条命令的含义是若是目标地址是172.16.42.111就要送到lo:0,这样就保证了入栈通过lo:0,那么出栈天然会通过。若是你不理解,那么就要好好看看最上面的图,二层通讯是使用mac地址,而此时后端服务器收到这个数据包的时候,IP报文中源IP是客户端的IP,而目标IP则是VIP,只是数据链路层报文mac地址信息被LVS替换了。
以后启动后端服务的Nginx服务,两台后端服务器上都作上面的修改。
之间经过yum安装便可yum install -y keepalived
。我这里使用的是阿里云的源,它默认就在里面,以下图:
在2个节点都安装。
文件 | 说明 |
---|---|
/usr/sbin/keepalived | 二进制程序 |
/etc/keepalived/keepalived.conf | 配置文件 |
/usr/lib/systemd/system/keepalived.service | 服务文件 |
# 全局配置 global_defs { # 邮件通知信息 notification_email { # 定义收件人 acassen@firewall.loc } # 定义发件人 notification_email_from Alexandre.Cassen@firewall.loc # SMTP服务器地址 smtp_server 192.168.200.1 smtp_connect_timeout 30 # 路由器标识,通常不用改,也能够写成每一个主机本身的主机名 router_id LVS_DEVEL # VRRP的ipv4和ipv6的广播地址,配置了VIP的网卡向这个地址广播来宣告本身的配置信息,下面是默认值 vrrp_mcast_group4 224.0.0.18 vrrp_mcast_group6 ff02::12 } # 定义用于实例执行的脚本内容,好比能够在线下降优先级,用于强制切换 vrrp_script SCRIPT_NAME { } # 一个vrrp_instance就是定义一个虚拟路由器的,实例名称 vrrp_instance VI_1 { # 定义初始状态,能够是MASTER或者BACKUP state MASTER # 工做接口,通告选举使用哪一个接口进行 interface ens33 # 虚拟路由ID,若是是一组虚拟路由就定义一个ID,若是是多组就要定义多个,并且这个虚拟 # ID仍是虚拟MAC最后一段地址的信息,取值范围0-255 virtual_router_id 51 # 使用哪一个虚拟MAC地址 use_vmac XX:XX:XX:XX:XX # 监控本机上的哪一个网卡,网卡一旦故障则须要把VIP转移出去 track_interface { eth0 ens33 } # 若是你上面定义了MASTER,这里的优先级就须要定义的比其余的高 priority 100 # 通告频率,单位为秒 advert_int 1 # 通讯认证机制,这里是明文认证还有一种是加密认证 authentication { auth_type PASS auth_pass 1111 } # 设置虚拟VIP地址,通常就设置一个,在LVS中这个就是为LVS主机设置VIP的,这样你就不用本身手动设置了 virtual_ipaddress { # IP/掩码 dev 配置在哪一个网卡 192.168.200.16/24 dev eth1 # IP/掩码 dev 配置在哪一个网卡的哪一个别名上 192.168.200.17/24 dev label eth1:1 } # 虚拟路由,在须要的状况下能够设置lvs主机 数据包在哪一个网卡进来从哪一个网卡出去 virtual_routes { 192.168.110.0/24 dev eth2 } # 工做模式,nopreempt表示工做在非抢占模式,默认是抢占模式 preempt nopreempt|preempt # 若是是抢占默认则能够设置等多久再抢占,默认5分钟 preempt delay 300 # 追踪脚本,一般用于去执行上面的vrrp_script定义的脚本内容 track_script { } # 三个指令,若是主机状态变成Master|Backup|Fault以后会去执行的通知脚本,脚本要本身写 notify_master "" notify_backup "" notify_fault "" } # 定义LVS集群服务,能够是IP+PORT;也能够是fwmark 数字,也就是防火墙规则 # 因此经过这里就能够看出来keepalive天生就是为ipvs而设计的 virtual_server 10.10.10.2 1358 { delay_loop 6 # 算法 lb_algo rr|wrr|lc|wlc|lblc|sh|dh # LVS的模式 lb_kind NAT|DR|TUN # 子网掩码,这个掩码是VIP的掩码 net_mask 255.255.255.0 # 持久链接超时时间 persistence_timeout 50 # 定义协议 protocol TCP # 若是后端应用服务器都不可用,就会定向到那个服务器上 sorry_server 192.168.200.200 1358 # 后端应用服务器 IP PORT real_server 192.168.200.2 1358 { # 权重 weight 1 # 应用服务器UP或者DOWN,就执行那个脚本 notify_up "这里写的是路径,若是脚本后有参数,总体路径+参数引发来" notify_down "/PATH/SCRIPTS.sh 参数" # MSIC_CHECK|SMTP_CHEKC|TCP_CHECK|SSL_GET|HTTP_GET这些都是 # 针对应用服务器作健康检查的方法 MISC_CHECK {} # 用于检查SMTP服务器的 SMTP_CHEKC {} # 若是应用服务器不是WEB服务器,就用TCP_CHECK检查 TCP_CHECK { # 向哪个端口检查,若是不指定默认使用上面定义的端口 connect_port <PORT> # 向哪个IP检测,若是不指定默认使用上面定义的IP地址 bindto <IP> # 链接超时时间 connect_timeout 3 } # 若是对方是HTTPS服务器就用SSL_GET方法去检查,里面配置的内容和HTTP_GET同样 SSL_GET {} # 使用HTTP_GET方法去检查 HTTP_GET { # 检测URL url { # 具体检测哪个URL path /testurl/test.jsp # 检测内容的哈希值,也就是网页的md5值 digest 640205b7b0fc66c1ea91c463fac6334d # 除了检测哈希值还能够检测状态码,好比HTTP的200 表示正常,两种方法二选一便可 status_code 200 } url { path /testurl2/test.jsp digest 640205b7b0fc66c1ea91c463fac6334d } url { path /testurl3/test.jsp digest 640205b7b0fc66c1ea91c463fac6334d } # 向哪个端口检查,若是不指定默认使用上面定义的端口 connect_port <PORT> # 向哪个IP检测,若是不指定默认使用上面定义的IP地址 bindto <IP> # 链接超时时间 connect_timeout 3 # 尝试次数 nb_get_retry 3 # 每次尝试之间间隔几秒 delay_before_retry 3 } } real_server 192.168.200.3 1358 { weight 1 HTTP_GET { url { path /testurl/test.jsp digest 640205b7b0fc66c1ea91c463fac6334c } url { path /testurl2/test.jsp digest 640205b7b0fc66c1ea91c463fac6334c } connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } }
Srv01上的keepalived.conf
global_defs { notification_email { acassen@firewall.loc } notification_email_from Alexandre.Cassen@firewall.loc smtp_server 127.0.0.1 smtp_connect_timeout 30 router_id srv01 } vrrp_instance VI_1 { state MASTER interface ens33 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 172.16.42.111/24 brd 172.16.42.111 dev ens33 label ens33:0 } preempt delay 60 }
Srv02上的keepalived.conf,惟一不一样的就是state、priority以及router_id。
global_defs { notification_email { acassen@firewall.loc } notification_email_from Alexandre.Cassen@firewall.loc smtp_server 127.0.0.1 smtp_connect_timeout 30 router_id srv02 } vrrp_instance VI_1 { state BACKUP interface ens33 virtual_router_id 51 priority 90 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 172.16.42.111/24 brd 172.16.42.111 dev ens33 label ens33:0 } preempt delay 60 }
启动2个节点,启动后会自动配置ens33:0这个子接口的虚拟IP
在主节点上你经过systemctl status keepalived
看不到它究竟是什么角色,不过在BACKUP节点上你能够看到,可是你在主节点日志中cat /var/log/message
里能够看到Srv01进入到MASTER状态,以下图:
查看Srv02的状态
那么你经过中止Srv01上的keepalived服务就看到MASTER会被转移到Srv02上。
使用该命令查看VRRP通告tcpdum -i ens33 -nn host 224.0.0.18
,你在2台主机都会看到相同的信息。
Srv01使用真实物理IP对该地址进行发送通告,那么Srv02也会收到,若是Srv01宕机,那么Srv02就会使用本身的物理IP向该地址发送通告,因为Srv01已经宕机那么此时Srv02的优先级就是最高的,因此Srv02就变成了MASTER。
在keepalived.conf文件中增长下面的内容,2台服务器增长的内容一致,因此这里就写一份。
virtual_server 172.16.42.111 80 { delay_loop 6 lb_algo rr lb_kind DR nat_mask 255.255.255.0 persistence_timeout 0 protocol TCP sorry_server 192.168.200.200 1358 # 后端应用服务器 IP PORT real_server 172.16.42.102 80 { weight 1 # 应用服务器UP或者DOWN,就执行那个脚本 notify_up "/usr/local/notify.sh 172.16.42.102 up" notify_down "/usr/local/notify.sh 172.16.42.102 down" HTTP_GET { # 检测URL url { path /index.html # 除了检测哈希值还能够检测状态码,好比HTTP的200 表示正常,两种方法二选一便可 status_code 200 } connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } real_server 172.16.42.103 80 { weight 1 # 应用服务器UP或者DOWN,就执行那个脚本 notify_up "/usr/local/notify.sh 172.16.42.103 up" notify_down "/usr/local/notify.sh 172.16.42.103 down" HTTP_GET { # 检测URL url { path /index.html # 除了检测哈希值还能够检测状态码,好比HTTP的200 表示正常,两种方法二选一便可 status_code 200 } connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } }
这里的notify_up|down脚本我写的很简单就是为了使用一下这个功能,内容以下:
#!/bin/bash if [ $2 == "up" ]; then echo "Real server ${1} is UP" > /tmp/notify.txt elif [ $2 == "down" ]; then echo "Real server ${1} is DOWN" > /tmp/notify.txt fi
重启Keepalived服务以后你就能够经过ipvsadm -Ln
查看ipvs规则了,这些规则在2台服务器上都会有,以下图:
测试访问
使用下面的命令快速访问for i in {1..20}; do curl http://172.16.42.111/ | grep "Srv0" --color ; done
能够看到2台服务器交替,由于咱们使用的rr调度算法。
连续访问VIP,而后中止Srv01上面的keepalived服务,这就意味着Srv01也就是失去了VIP,而后观察请求状况以及是否触发以前设定的脚本。
查看Srv02上面的日志
为了提供一个Nginx或者某种后端服务器的负载均衡功能,那么咱们须要一个LVS来作调度,可是一台LVS存在单点故障,为了解决LVS单点故障咱们使用Keepalived来组建一个LVS的高可用集群。