# 高可用负载均衡

负载均衡集群

比较三种负载均衡

  • LVS包括三层结构:负载调度器,真实服务池,共享存储(保证请求的数据同步、一致)
  • LVS由两部分程序组成:工作在内核空间的ipvs(真正生效实现调度的代码),工作在用户看见的ipvsadm(为ipvs内核框架编写规则)

LVS、haproxy、nginx的区别

Nginx:
  + 工作在应用层,'1.9版本开始支持四层代理'。
  + 正则表达式强大。
  + 支持上万的高并发。
  + 应用层代理仅支持http、https、mail协议,应用面小。
  + 监控检查仅通过端口,无法使用url检查。
LVS(Linux Virtual Server):
  + 负载能力强,'工作在传输层',对内存、CPU消耗低,适用于大型系统。
  + 应用面广,几乎可以为所有应用提供负载均衡。
  + 支持多种负载均衡调度算法。
  + 不支持正则表达式,不能实现动静分离。
  + 如果网站架构庞大,LVS-DR的配置比较繁琐。
HAProxy:
  + 支持session、cookie功能。
  + 'HAProxy支持传输层(tcp),也支持应用层(http)'。
  + 支持多种负载均衡调度算法。
  + 相对于Nginx和LVS,HAProxy在处理大量连接时消耗的内存稍高一些。
  + HAProxy不直接支持HTTP加密,需要与其他工具配合使用。

比较
+ 工作层:
  + 四层:LVS、Nginx、HAProxy(mode tcp)
  + 七层:Nginx、HAProxy(mode http)    注意:七层负载均衡可以有效防止DDOS攻击
+ '负载均衡性能:LVS > HAProxy > Nginx'
  + 大规模集群:LVS
  + 中小型系统:HAProxy、Nginx
+ HAProxy和LVS的区别:
  + LVS'转发'客户端的请求,真实服务器服务日志(`/var/log/nginx/access.log`)看到的是'客户端的IP地址'。转发没有高并发瓶颈。
  + Nginx和HAProxy'代理'客户端请求服务,真实服务器服务日志看到的是'HAProxy服务器的IP地址'。代理由于要给客户端请求分配一个临时端口,因此有64k瓶颈(端口数量2的16次方65536个,64k)
  + HAProxy不需要VIP,真实服务器也就不需要修改内核参数。

nginx和LVS的对比

nginx与LVS的对比: 
	'nginx工作在网络的第7层,所以它可以针对http应用本身来做分流策略,比如针对域名、目录结构 等',相比之下lvs并不具备这样的功能,所以nginx单凭这点可以利用的场合就远多于lvs了;但nginx有用的这些功能使其可调整度要高于lvs,所以经常要去触碰,触碰多了,人为出现问题的几率也就会大。 
	nginx对网络的依赖较小,理论上只要ping得通,网页访问正常,nginx就能连得通,nginx同时还能区分内外网,如果是同时拥有内外网的节点,就相当于单机拥有了备份线路;lvs就比较依赖于网络环境,目前来看服务器在同一网段内并且lvs使用direct方式分流,效果较能得到保证。另外注 意,lvs需要向托管商至少申请多于一个ip来做visual ip。 
	nginx安装和配置比较简单,测试起来也很方便,因为它基本能把错误用日志打印出来。lvs的安装和配置、测试就要花比较长的时间,因为同上所述,'lvs对网络依赖性比较大',很多时候不能配置成功都是因为网络问题而不是配置问题,出了问题要解决也相应的会麻烦的多。
	nginx也同样能承受很高负载且稳定,但负载度和稳定度差lvs还有几个等级:nginx处理所有流量 所以受限于机器IO和配置;本身的bug也还是难以避免的;nginx没有现成的双机热备方案,所以 跑在单机上还是风险比较大,单机上的事情全都很难说。 
	nginx可以检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会 把返回错误的请求重新提交到另一个节点。目前lvs中ldirectd也能支持针对服务器内部的情况来监控,但lvs的原理使其不能重发请求。比如用户正在上传一个文件,而处理该上传的节点刚好在上传 过程中出现故障,nginx会把上传切到另一台服务器重新处理,而lvs就直接断掉了。
	
	两者配合使用:nginx用来做http的反向代理,能够upsteam实现http请求的多种方式的均衡转发。由于采用的是异步转发可以做到如果一个服务器请求失败,立即切换到其他服务器,直到请求成功或者最后一台服务器失败为止。这可以最大程度的提高系统的请求成功率。
	
	lvs采用的是同步请求转发的策略。这里说一下同步转发和异步转发的区别。同步转发是在lvs服务器接收到请求之后,立即redirect到一个后端服务器,由客户端直接和后端服务器建立连接。异步转发是nginx 在保持客户端连接的同时,发起一个相同内容的新请求到后端,等后端返回结果后,由nginx返回给客户端。进一步来说:'当做为负载均衡服务器的nginx和lvs处理相同的请求时,所有的请求和响应流量都会经过nginx;但是使用lvs时,仅请求流量经过lvs的网络,响应流量由后端服务器的网络返回'。也就是,'当作为后端的服务器规模庞大时,nginx的网络带宽就成了一个巨大的瓶颈'。 但是仅仅使用lvs作为负载均衡的话,一旦后端接受到请求的服务器出了问题,那么这次请求就失败了。 但是如果'在lvs的后端在添加一层nginx(多个),每个nginx后端再有几台应用服务器',那么结合两者的 优势,既能避免单nginx的流量集中瓶颈,又能避免单lvs时一锤子买卖的问题。
  • nginx与LVS的对比: nginx工作在网络的第7层,所以它可以针对http应用本身来做分流策略,比如针对域名、目录结构 等,相比之下lvs并不具备这样的功能,所以nginx单凭这点可以利用的场合就远多于lvs了;但 nginx有用的这些功能使其可调整度要高于lvs,所以经常要去触碰,由lvs的第2条优点来看,触碰 多了,人为出现问题的几率也就会大。 nginx对网络的依赖较小,理论上只要ping得通,网页访问正常,nginx就能连得通,nginx同时还 能区分内外网,如果是同时拥有内外网的节点,就相当于单机拥有了备份线路;lvs就比较依赖于网 络环境,目前来看服务器在同一网段内并且lvs使用direct方式分流,效果较能得到保证。另外注 意,lvs需要向托管商至少申请多于一个ip来做visual ip。 nginx安装和配置比较简单,测试起来也很方便,因为它基本能把错误用日志打印出来。lvs的安装 和配置、测试就要花比较长的时间,因为同上所述,lvs对网络依赖性比较大,很多时候不能配置成 功都是因为网络问题而不是配置问题,出了问题要解决也相应的会麻烦的多。 nginx也同样能承受很高负载且稳定,但负载度和稳定度差lvs还有几个等级:nginx处理所有流量 所以受限于机器IO和配置;本身的bug也还是难以避免的;nginx没有现成的双机热备方案,所以 跑在单机上还是风险比较大,单机上的事情全都很难说。 nginx可以检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会 把返回错误的请求重新提交到另一个节点。目前lvs中ldirectd也能支持针对服务器内部的情况来监 控,但lvs的原理使其不能重发请求。比如用户正在上传一个文件,而处理该上传的节点刚好在上传 过程中出现故障,nginx会把上传切到另一台服务器重新处理,而lvs就直接断掉了。
  • 两者配合使用: nginx用来做http的反向代理,能够upsteam实现http请求的多种方式的均衡转发。由于采用的是异步转发可以做到如果一个服务器请求失败,立即切换到其他服务器,直到请求成功或者最后一台服务器失败 为止。这可以最大程度的提高系统的请求成功率。 lvs采用的是同步请求转发的策略。这里说一下同步转发和异步转发的区别。同步转发是在lvs服务器接收 到请求之后,立即redirect到一个后端服务器,由客户端直接和后端服务器建立连接。异步转发是nginx 在保持客户端连接的同时,发起一个相同内容的新请求到后端,等后端返回结果后,由nginx返回给客户 端。 进一步来说:当做为负载均衡服务器的nginx和lvs处理相同的请求时,所有的请求和响应流量都会经过 nginx;但是使用lvs时,仅请求流量经过lvs的网络,响应流量由后端服务器的网络返回。 也就是,当作为后端的服务器规模庞大时,nginx的网络带宽就成了一个巨大的瓶颈。 但是仅仅使用lvs作为负载均衡的话,一旦后端接受到请求的服务器出了问题,那么这次请求就失败了。 但是如果在lvs的后端在添加一层nginx(多个),每个nginx后端再有几台应用服务器,那么结合两者的 优势,既能避免单nginx的流量集中瓶颈,又能避免单lvs时一锤子买卖的问题。

LVS NAT模式和DR模式的优缺点

DS:Director Server。指的是前端负载均衡器节点。
RS:Real Server。后端真实的工作服务器。
VIP:Virtual IP 向外部直接面向用户请求,作为用户请求的目标的IP地址。
DIP:Director Server IP,主要用于和内部主机通讯的IP地址。
RIP:Real Server IP,后端服务器的IP地址。
CIP:Client IP,访问客户端的IP地址。

LVS-NAT模式的原理
    (a). 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。此时报文的源IP为CIP,目标IP为VIP
    (b). PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链
    (c). IPVS比对数据包请求的服务是否为集群服务,若是,修改数据包的目标IP地址为后端服务器RIP, 然后将数据包发至POSTROUTING链。此时报文的源IP为CIP,目标IP为RIP
    (d). POSTROUTING链通过选路,将数据包发送给Real Server
    (e). Real Server比对发现目标为自己的IP,开始构建响应报文发回给Director Server。此时报文的源IP为RIP,目标IP为CIP
    (f). Director Server在响应客户端前,此时会将源IP地址修改为自己的VIP地址,然后响应给客户端。 此时报文的源IP为VIP,目标IP为CIP

LVS-NAT模型的特性
	RS应该使用私有地址,RS的网关必须指向DIP
	DIP和RIP必须在同一个网段内
	请求和响应报文都需要经过Director Server,高负载场景中,Director Server易成为性能瓶颈
	支持端口映射
	RS可以使用任意操作系统
	缺陷:对Director Server压力会比较大,请求和响应都需经过Director server

 LVS-DR模式原理
    (a) 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP
    (b) PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链
    (c) IPVS比对数据包请求的服务是否为集群服务,若是,将请求报文中的源MAC地址修改为DIP的MAC地址,将目标MAC地址修改RIP的MAC地址,然后将数据包发至POSTROUTING链。 此时的源IP和目的IP均未修改,仅修改了源MAC地址为DIP的MAC地址,目标MAC地址为RIP的MAC地址
    (d) 由于DS和RS在同一个网络中,所以是通过二层来传输。POSTROUTING链检查目标MAC地址为RIP的MAC地址,那么此时数据包将会发至Real Server。
    (e) RS发现请求报文的MAC地址是自己的MAC地址,就接收此报文。处理完成之后,将响应报文通过lo接口传送给eth0网卡然后向外发出。 此时的源IP地址为VIP,目标IP为CIP
    (f) 响应报文最终送达至客户端

LVS-DR模型的特性
    保证前端路由将目标地址为VIP报文统统发给Director Server,而不是RS
    RS可以使用私有地址;也可以是公网地址,如果使用公网地址,此时可以通过互联网对RIP进行直接访问
    RS跟Director Server必须在同一个物理网络中
    所有的请求报文经由Director Server,但响应报文必须不能经过Director Server
    不支持地址转换,也不支持端口映射
    RS可以是大多数常见的操作系统
    RS的网关绝不允许指向DIP(因为我们不允许他经过director)
    RS上的lo接口配置VIP的IP地址
    缺陷:RS和DS必须在同一机房中

NAT模式(基于网络层,IP)
DR模式(基于数据链路层,MAC)

NAT模式的优点:
    NAT模式可以隐藏后端服务器的真实IP地址,增加安全性。
    NAT模式可以进行负载均衡,分摊流量压力到多台后端服务器上。
    NAT模式可以通过配置不同的外部端口映射到不同的内部服务器,提高灵活性。
NAT模式的缺点:
    NAT模式需要额外的设备进行地址转换,增加了网络的复杂性和成本。
    NAT模式可能会成为性能瓶颈,降低网络传输的效率。
    NAT模式可能会导致单点故障,一旦NAT设备发生故障,整个网络服务将中断。

DR模式的优点:
    DR模式在数据转发时直接将数据包转发给后端服务器,减少了中间设备的负担,提高了系统性能,效率最高。
    DR模式可以实现无感知的服务器故障切换,提高了系统的稳定性和可用性。
    DR模式减少了单一设备的压力,提高了整个系统的吞吐量和处理能力。
DR模式的缺点:
    DR模式对后端服务器的网络环境要求较高,后端服务器需要配置正确的网络环境和路由表。
    DR模式无法隐藏后端服务器的真实IP地址,增加了网络安全风险。
    DR模式需要更多的管理和维护,配置和调试相对复杂。

LVS负载均衡

LVS(ipvsadm)的负载均衡调度算法-s的取值:
+ `rr`(轮询,Round-robin)
+ `wrr`(加权轮询,Weighted Round-robin)
+ `lc`(最少连接,Least Connection):将请求发送到当前具有最少活动连接数的服务器。
+ `wlc`(加权最少连接,Weighted Least Connection)
+ `dh`(目标地址散列,Destination Hashing):根据请求的目标IP地址的哈希值来确定将请求转发到哪个服务器。相同的目标IP地址将始终发送到同一个服务器。
+ `sh`(源地址散列,Source Hashing):根据请求的源IP地址的哈希值来确定将请求转发到哪个服务器。相同的源IP地址将始终发送到同一个服务器。
+ `wsh`(加权源地址散列,Weighted Source Hashing)
+ `sed`(最短预期延迟,Shortest Expected Delay):根据每个服务器的平均响应时间来选择最短预期延迟的服务器,以实现负载均衡。
+ `ssed`(源地址最短预期延迟,Source Shortest Expected Delay):类似于最短预期延迟,但根据请求的源IP地址来选择最短预期延迟的服务器。
+ `dst`(最小连接数,Least Connection on Destination IP)
# ipvsadm命令
+ `ipvsadm -A -t或u 虚拟服务器IP:端口号 -s 调度算法`:添加虚拟服务器并指定调度算法。
  `ipvsadm -E -t或u 虚拟服务器IP:端口号 -s 调度算法`:修改虚拟服务器调度算法。
  `ipvsadm -D -t或u 虚拟服务器IP:端口号`:删除虚拟服务器并指定调度算法。

+ `ipvsadm -a -t或u 虚拟服务器IP:端口号 -r 真实服务器IP:端口号 -w 权重 -m或-g`:添加真实服务器。RIP
  `ipvsadm -e -t或u 虚拟服务器IP:端口号 -r 真实服务器IP:端口号 -w 权重 -m或-g`:修改真实服务器。
  `ipvsadm -d -t或u 虚拟服务器IP:端口号 -r 真实服务器IP:端口号`:删除真实服务器。

+ `ipvsadm -Ln`:查看IPVS配置。

ipvsadm命令选项:
+ `-A`:添加虚拟服务器(Virtual Server,也叫负载均衡器)。
  `-E`:编辑虚拟服务器。
  `-D`:删除虚拟服务器。
  `-t`:指定虚拟服务器端口为tcp协议。
  `-u`:指定虚拟服务器端口为udp协议。
  `-s`(scheduler):指定调度算法。`rr`表示轮询,`wrr`表示加权轮询,`lc`表示最少连接,`wlc`表示加权最少连接。
+ `-a`:添加虚拟服务器后,向该虚拟服务器中加入真实服务器(Real Server,也叫真实服务器)。
  `-e`:修改真实服务器。
  `-d`:删除真实服务器。
+ `-r`:指定真实服务器。
  `-w`:设置真实服务器的权重。
  `-m`:指定工作模式为NAT。
  `-g`:指定工作模式为DR。
+ `-L`(list):查看列表。
  `-n`(numeric):以数字显示IP地址和端口号。

NAT模式举例:VIP和RIP在不同网段
ipvsadm -A -t VIP:80 -s 调度算法
ipvsadm -a -t VIP:80 -r RIP -w 权重 -m	

DR模式举例:VIP和RIP在同一个网段
ipvsadm -A -t VIP:80 -s 调度算法
ipvsadm -a -t VIP:80 -r RIP -w 权重 -g

nginx四层代理和七层代理

nginx负载均衡可用的调度算法
    轮询(默认)
    weight权重
    ip_hash
    fair(第三方插件)
    url_hash(第三方插件)

# 七层代理(内置模块upstream),可以代理http、https,写在配置文件的http{}中,可以监听本机80
例如
http {
	……省略一万字
	upstream webs {
        server 192.168.88.101  weight=1  max_fails=3  fail_timeout=5;	# ip地址:端口,省略端口默认80
        server 192.168.88.102  weight=2  max_fails=3  fail_timeout=5;
	}
	server {
        listen 100;
        location / {
            proxy_pass http://webs;
            root html;
            index index.html;
        }
	}
	……省略一万字
}

# 四层代理(需要安装模块stream),可以代理TCP、UDP,写在http{}外,stream{}中,不可以监听本机80
yum install -y nginx-mod-stream
stream {					# 四层代理写在stream{}中,七层代理写在http{}中
    upstream webs {
        server 192.168.88.101:80  weight=1 max_fails=3  fail_timeout=5;	# 四层代理不能省略端口
        server 192.168.88.102:80  weight=2 max_fails=3  fail_timeout=5;
    }
    server {
        listen 100;			# `listen 100;`为TCP协议,`listen 100 udp;`为UDP协议
        proxy_pass webs;
    }
    upstream ssh {
        server 192.168.88.101:22  weight=1 max_fails=3  fail_timeout=5;
        server 192.168.88.102:22  weight=2 max_fails=3  fail_timeout=5;
    }
    server {
        listen 123;
        proxy_pass ssh;
    }
}
http {
	……省略一万字
}

高可用负载均衡的搭建

主机清单

ansible控制端(control 192.168.88.240,192.168.99.240):
[root@control ~]# mkdir web && cd web
[root@control lb]# cat > ansible.cfg << EOF
[defaults]
inventory = hostlist
host_key_checking = no
EOF

[root@control lb]# cat > hostlist << EOF
[web1]
192.168.88.81
[web2]
192.168.88.82
[web3]
192.168.88.83
[web:children]
web1
web2
web3

[lvs1]
192.168.88.11 hostname=lvs1 state=MASTER priority=110
[lvs2]
192.168.88.12 hostname=lvs2 state=BACKUP priority=100
[lvs:children]
lvs1
lvs2

[haproxy1]
192.168.88.21 hostname=haproxy1 state=MASTER priority=110
[haproxy2]
192.168.88.22 hostname=haproxy2 state=BACKUP priority=100
[haproxy:children]
haproxy1
haproxy2

[nginx1]
192.168.88.31 hostname=nginx1 state=MASTER priority=110
[nginx2]
192.168.88.32 hostname=nginx2 state=BACKUP priority=100
[nginx:children]
nginx1
nginx2

[all:vars]
ansible_ssh_user=root
ansible_ssh_pass=a
EOF

[root@control lb]# ansible all -m ping

Web集群启用Web服务

# Web集群启用Web服务(nginx或httpd或tomcat)
[root@control lb]# echo 'Welcome to {{ansible_hostname}} on {{ansible_eth0.ipv4.address}}!!' > nginx_index.html
[root@control lb]# cat > web_install.yaml << EOF
---
- name: configure nginx
  hosts: web
  vars:
    - nginx_index: 'nginx_index.html'
  tasks:
    - name: remove nginx
      yum: 
        name: nginx
        state: absent
    - name: install nginx
      yum: 
        name: nginx
        state: latest
        update_cache: yes
    - name: start nginx and enable
      service:
        name: nginx.service
        state: started
        enabled: yes
    - name: template index.html
      template:
        src: '{{ nginx_index }}'
        dest: /usr/share/nginx/html/index.html
EOF
[root@control lb]# ansible-playbook web_install.yaml

# 注意:收集fact变量(ansible_hostname,ansible_eth0.ipv4.address等)会花费时间,可以在主机清单中设置变量,在剧本中跳过收集face变量(gather_facts: no)

# 验证
[root@control lb]# for i in {81..83};do curl 192.168.88.${i};done
    Welcome to web1 on 192.168.88.81!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web3 on 192.168.88.83!!

keepalived+LVS(DR模式)

  • 调度器(高可用)+ Web集群(负载均衡)
    • 目标:客户端访问调度器的VIP,可以访问到Web集群的Web服务。当主调度器故障时,会切换备用调度器。
    • 环境:调度器(两台),Web集群(三台),客户端:
      • Web集群、调度器(一张网卡)处于同一个网段
      • Web集群、调度器(一张网卡)、客户端可以相互ping通,简化情况下,处于同一网段。
    • DR模式配置步骤:Web集群部署Web服务——》Web集群的lo网卡配置VIP,并修改内核参数不响应对VIP的ARP请求——》调度器配置keepalived和IPVS(keepalived配置文件中可以配置IPVS)
  • keepalived可以实现本VRRP实例的高可用[,通过跟踪脚本实现服务的高可用]。
    keepalived可以提供VIP,配置IPVS规则,对后端服务器做健康检查以更新IPVS规则。
Web集群配置VIP和内核参数
# ipvsadm的DR模式需要Web集群配置VIP(注意网络为是32)和内核参数
[root@control lb]# cat > web_DR.yaml << EOF
---
- name: DR mode configure vip and sysctl
  hosts: web
  gather_facts: no
  vars:
    - VIP: '192.168.88.10'
    - sysctl_parameters:
      - { name: "net.ipv4.conf.all.arp_ignore", value: "1" }    # 所有网卡只响应对本网卡IP地址的arp请求
      - { name: "net.ipv4.conf.lo.arp_ignore", value: "1" }     # 网卡lo只响应对本网卡IP地址的arp请求
      - { name: "net.ipv4.conf.all.arp_announce", value: "2" }  # 所有网卡只使用本网卡IP地址作为发送arp请求的源IP地址
      - { name: "net.ipv4.conf.lo.arp_announce", value: "2" }   # 网卡lo只使用本网卡IP地址作为发送arp请求的源IP地址
  tasks:
    - name: install network-scripts,net-tools  # network-scripts提供ifup、ifdown命令用于激活lo:0,net-tools提供ifconfig命令用于查看lo:0
      yum: 
        name: network-scripts,net-tools
        state: present
        update_cache: yes
    - name: rm ifcfg-lo:0           # 为了配置lo:0可以重复执行,先删除该文件
      file:
        path: /etc/sysconfig/network-scripts/ifcfg-lo:0
        state: absent
    - name: add lo:0 set vip        # 配置本地环回虚拟网卡lo,第一个附加地址为lo:0
      copy:
        dest: /etc/sysconfig/network-scripts/ifcfg-lo:0
        content: |
          DEVICE=lo:0
          NAME=lo:0
          IPADDR={{ VIP }}
          PREFIX=32                 # 注意网络位,也可以使用`NETMASK=255.255.255.255`
          NETWORK={{ VIP }}
          BROADCAST={{ VIP }}
          ONBOOT=yes
        force: yes
        backup: no
      notify:                       # 只有本任务changed才会启用下列任务
          - activate lo:0
          - autoconnect lo:0
          - chmod +x
          - set sysctl
  handlers:
    - name: activate lo:0
      shell: ifup lo:0
      async: 60                      # 运行时间最长为60s
      poll: 0                        # 立即继续到下一个任务
    - name: autoconnect lo:0         # 以防万一手动启用
      lineinfile:
        path: /etc/rc.d/rc.local
        line: 'ifup lo:0'    
    - name: chmod +x
      shell: chmod +x /etc/rc.d/rc.local
      register: result
    - name: set sysctl
      sysctl:                        # 配置内核参数
        name: '{{ item.name }}'
        value: '{{ item.value }}'
        state: present
        reload: yes
      loop: '{{ sysctl_parameters }}'
      when: result.rc == 0
EOF
[root@control lb]# ansible-playbook web_DR.yaml

# 验证
[root@web1~3 ~]# ifconfig lo:0
    lo:0: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
            inet 192.168.88.10  netmask 255.255.255.255
            loop  txqueuelen 1000  (Local Loopback)
调度器配置keepalived和IPVS
keepalived配置文件
# keepalived配置文件,keepalived可以实现本VRRP实例的高可用;可以提供VIP;可以配置IPVS规则,对后端服务器做健康检查以更新IPVS规则
[root@control lb]# cat > keepalived_ipvsadm_DR.conf << EOF
global_defs { 
   notification_email { 
     acassen@firewall.loc 
     failover@firewall.loc 
     sysadmin@firewall.loc 
   } 
   notification_email_from Alexandre.Cassen@firewall.loc  
   smtp_server 127.0.0.1 
   smtp_connect_timeout 30 
   router_id {{hostname}}   # 本机id
} 

vrrp_instance VI_1 {
    state {{state}}         # 主MASTER,备BACKUP
    interface {{interface}}  	
    virtual_router_id {{VRRP_id}}  # VRRP路由ID实例,每个VRRP实例是唯一的
    priority {{priority}}   # 优先级主高于备
    advert_int 1            # 心跳间隔1s
    authentication { 
        auth_type PASS      
        auth_pass 1111 
    }  
    # virtual_ipaddress:虚拟IP(VIP)
    virtual_ipaddress { 
        {{VIP}}
    } 
}
# 下面是keepalived配置IPVS的规则
# VIP: ipvsadm -A -t VIP:80 -s wrr
virtual_server {{VIP}} {{lvs_port}} {    
    delay_loop 6     				
    lb_algo wrr      				
    lb_kind DR                      # DR模式,VIP和RIP同网段
    persistence_timeout 0  		    # 会话保持的时间,0s方便测试
    protocol TCP     			
    # RIP: ipvsadm -a -t VIP -r RIP -w 权重 -g
    real_server {{RIP01}} {{lvs_port}} { 
        weight {{weight01}}         	 		
        TCP_CHECK {       			# 第一次,2秒超时,间隔3秒,第二次,2秒超时,间隔3秒,第三次,2秒超时
            connect_timeout 2       # 通过TCP协议对真实服务器做健康检查
            nb_get_retry 3          # 3次访问失败则认为真实服务器故障
            delay_before_retry 3    # 两次检查时间的间隔3秒
        }	
    }
    real_server {{RIP02}} {{lvs_port}} {
        weight {{weight02}}   
        TCP_CHECK {
            connect_timeout 2
            nb_get_retry 3
            delay_before_retry 3
        }
    }
    real_server {{RIP03}} {{lvs_port}} {
        weight {{weight03}}
        TCP_CHECK {
            connect_timeout 2
            nb_get_retry 3
            delay_before_retry 3
        }
    }
}
EOF
调度器配置keepalived和IPVS
# 调度器配置keepalived和IPVS
[root@control lb]# cat > lvs_keepalived_DR.yaml << EOF
--- 
- name: configure keepalived and ipvsadm
  hosts: lvs
  gather_facts: no
  vars:
    - keepalived_config: './keepalived_ipvsadm_DR.conf'
    - VRRP_id: '10'
    - interface: 'eth0'
    - VIP: '192.168.88.10'
    - lvs_port: '80'
    - RIP01: '192.168.88.81'
    - weight01: '1'
    - RIP02: '192.168.88.82'
    - weight02: '2'
    - RIP03: '192.168.88.83'
    - weight03: '1'
  tasks:
    - name: remove keepalived,ipvsadm
      yum:
        name: keepalived,ipvsadm
        state: absent
    - name: install keepadlived,ipvsadm
      yum:
        name: keepalived,ipvsadm
        state: present
    - name: template keepalived.conf
      template:
        src: '{{keepalived_config}}'
        dest: /etc/keepalived/keepalived.conf
        force: yes
        backup: no
        mode: 0644
    - name: start keepalived and enabled
      service:
        name: keepalived.service
        state: started
        enabled: yes
EOF
[root@control lb]# ansible-playbook lvs_keepalived_DR.yaml

# 验证
[root@lvs1 ~]# ip a s eth0
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:0c:29:c4:96:4b brd ff:ff:ff:ff:ff:ff
        inet 192.168.88.11/24 brd 192.168.88.255 scope global noprefixroute eth0
           valid_lft forever preferred_lft forever
        inet 192.168.88.10/32 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::79c3:82e6:9396:e1c1/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
[root@lvs1 ~]# 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.88.10:80 wrr
      -> 192.168.88.81:80             Route   1      0          0         
      -> 192.168.88.82:80             Route   2      0          0         
      -> 192.168.88.83:80             Route   1      0          0  

[root@lvs2 ~]# ip a s eth0
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:0c:29:78:7f:d6 brd ff:ff:ff:ff:ff:ff
        inet 192.168.88.12/24 brd 192.168.88.255 scope global noprefixroute eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::198b:22f4:8592:4a9a/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
[root@lvs2 ~]# 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.88.10:80 wrr
      -> 192.168.88.81:80             Route   1      0          0         
      -> 192.168.88.82:80             Route   2      0          0         
      -> 192.168.88.83:80             Route   1      0          0    

[root@control lb]# for i in $(seq 1 8);do curl 192.168.88.10;done
    Welcome to web3 on 192.168.88.83!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web1 on 192.168.88.81!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web3 on 192.168.88.83!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web1 on 192.168.88.81!!
    Welcome to web2 on 192.168.88.82!!
局限性
lvs(ipvsadm)的转发是同步的,如果客户端通过VIP访问lvs从而获取服务的这个过程中,后端的Web服务器故障,则VIP无法提供服务。
例如,ipvsadm将此次访问请求转发给web1,但web1故障,则此次访问请求无法获取服务。如果web1故障,ipvsadm通过健康检查将web1剔除出集群之后,客户端访问VIP,则ipvsadm不会将该访问请求转发给web1。因此,可以缩短健康检查的间隔,或者客户端刷新访问,或者在前面增加nginx反向代理ipvsadm。
还原Web集群
# Web集群清除ipvsadm的DR模式的设置,即清除VIP和内核参数的设置
[root@control lb]# cat > web_reset_DR.yaml << EOF
---
- name: reset web for ipvsadm DR
  hosts: web
  gather_facts: no
  vars:
    - sysctl_parameters:
      - { name: "net.ipv4.conf.all.arp_ignore", value: "0" }  
      - { name: "net.ipv4.conf.lo.arp_ignore", value: "0" }   
      - { name: "net.ipv4.conf.all.arp_announce", value: "0" }
      - { name: "net.ipv4.conf.lo.arp_announce", value: "0" } 
  tasks:
    - name: deactivate lo:0
      shell: 'ifdown lo:0'
      register: result
      ignore_errors: yes
    - name: disable lo:0
      lineinfile:
        path: /etc/rc.d/rc.local
        regexp: 'lo:0'
        state: absent 
    - name: rm lo:0
      file:
        path: /etc/sysconfig/network-scripts/ifcfg-lo:0
        state: absent
    - name: remove network-scripts
      yum: 
        name: network-scripts
        state: absent
      when: result.rc == 0
    - name: 
      sysctl:
        name: '{{ item.name }}'
        value: '{{ item.value }}'
        state: present
        reload: yes
      loop: '{{ sysctl_parameters }}'
EOF
[root@control lb]# ansible-playbook web_reset_DR.yaml 

keepalived+LVS(NAT模式)

  • 调度器(高可用)+ Web集群(负载均衡)
    • 目标:客户端访问调度器的VIP,可以访问到Web集群的Web服务。当主调度器故障时,会切换备用调度器。
    • 环境:调度器(两台),Web集群(三台),客户端:
      • Web集群、客户端处于不同网段,调度器和Web集群处于同一网段,且Web集群的网关是调度器。
      • 调度器(两张网卡)可以ping通客户端。简化情况下,调度器和客户端处于同一网段。
    • NAT模式配置步骤:Web集群(网关为VIP)部署Web服务——》调度器开启路由转发功能——》调度器配置keepalived和IPVS(keepalived配置文件中可以配置IPVS,此时客户端可以访问VIP来访问Web集群)
  • keepalived可以实现本VRRP实例的高可用[,通过跟踪脚本实现服务的高可用]。
    keepalived可以提供VIP,配置IPVS规则,对后端服务器做健康检查以更新IPVS规则。
主机清单
# 主机IP要求
调度器(lvs3、lvs4):
	eth0 192.168.88.13~14/24(VIP 192.168.88.10)
	eth1 192.168.99.13~14/24(VIP 192.168.99.10)
Web集群(web1、web2、web3):
	eth0 192.168.88.81~83/24 网关192.168.88.10
客户端(client):
	eth1 192.168.99.250/24

# 使用新的主机清单
ansible控制端(control 192.168.88.240,192.168.99.240):
[root@control ~]# mkdir web && cd web
[root@control lb]# cat > ansible.cfg << EOF
[defaults]
inventory = hostlist_nat
host_key_checking = no
EOF

[root@control lb]# cat > hostlist_nat << EOF
[web1]
192.168.88.81
[web2]
192.168.88.82
[web3]
192.168.88.83
[web:children]
web1
web2
web3

[lvs3]
192.168.88.13 hostname=lvs3 state=MASTER priority=110
[lvs4]
192.168.88.14 hostname=lvs4 state=BACKUP priority=100
[lvs:children]
lvs3
lvs4

[all:vars]
ansible_ssh_user=root
ansible_ssh_pass=a
EOF

[root@control lb]# ansible all -m ping

# Web集群依旧开启Web服务,但是修改网关为VIP
[root@control lb]# for i in {81..83};do curl 192.168.88.${i};done
    Welcome to web1 on 192.168.88.81!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web3 on 192.168.88.83!!
调度器开启路由转发功能
# 调度器开启路由转发功能
[root@control lb]# cat > lvs_ipforward.yaml << EOF
---
- name: NAT mode configure ip_forward
  hosts: lvs
  gather_facts: no
  tasks:
    - name: set net.ipv4.ip_forward=1
      sysctl:
        name: net.ipv4.ip_forward
        value: '1'
        state: present
        reload: yes
EOF
[root@control lb]# ansible-playbook lvs_ipforward.yaml
调度器配置keepalived和IPVS
keepalived配置文件
# keepalived配置文件,keepalived可以实现本VRRP实例的高可用;可以提供VIP;可以配置IPVS规则,对后端服务器做健康检查以更新IPVS规则
[root@control lb]# cat > keepalived_ipvsadm_NAT.conf << EOF
global_defs { 
   notification_email { 
     acassen@firewall.loc 
     failover@firewall.loc 
     sysadmin@firewall.loc 
   } 
   notification_email_from Alexandre.Cassen@firewall.loc  
   smtp_server 127.0.0.1 
   smtp_connect_timeout 30 
   router_id {{hostname}}   # 本机id
} 

vrrp_instance VI_1 {
    state {{state}}         # 主MASTER,备BACKUP
    interface {{interface01}}  	
    virtual_router_id {{VRRP_id01}}  # VRRP路由ID实例,每个VRRP实例是唯一的
    priority {{priority}}   # 优先级主高于备
    advert_int 1            # 心跳间隔1s
    authentication { 
        auth_type PASS      
        auth_pass 1111 
    }  
    # virtual_ipaddress:虚拟IP(VIP)
    virtual_ipaddress { 
        {{VIP01}}           # VIP01是Web集群的网关
    } 
}
vrrp_instance VI_2 {
    state {{state}}         # 主MASTER,备BACKUP
    interface {{interface02}}  	
    virtual_router_id {{VRRP_id02}}  # VRRP路由ID实例,每个VRRP实例是唯一的
    priority {{priority}}   # 优先级主高于备
    advert_int 1            # 心跳间隔1s
    authentication { 
        auth_type PASS      
        auth_pass 1111 
    }  
    # virtual_ipaddress:虚拟IP(VIP)
    virtual_ipaddress { 
        {{VIP02}}           # VIP02是Web集群的VIP
    } 
}
# 下面是keepalived配置IPVS的规则
# VIP: ipvsadm -A -t VIP:80 -s wrr
virtual_server {{VIP02}} {{lvs_port}} {
    delay_loop 6     				
    lb_algo wrr      				
    lb_kind nat                     # NAT模式,VIP和RIP不同网段
    persistence_timeout 0  		    # 会话保持的时间,0s方便测试
    protocol TCP     			
    # RIP: ipvsadm -a -t VIP -r RIP -w 权重 -m
    real_server {{RIP01}} {{lvs_port}} { 
        weight {{weight01}}         	 		
        TCP_CHECK {       			# 第一次,2秒超时,间隔3秒,第二次,2秒超时,间隔3秒,第三次,2秒超时
            connect_timeout 2       # 通过TCP协议对真实服务器做健康检查
            nb_get_retry 3          # 3次访问失败则认为真实服务器故障
            delay_before_retry 3    # 两次检查时间的间隔3秒
        }	
    }
    real_server {{RIP02}} {{lvs_port}} {
        weight {{weight02}}   
        TCP_CHECK {
            connect_timeout 2
            nb_get_retry 3
            delay_before_retry 3
        }
    }
    real_server {{RIP03}} {{lvs_port}} {
        weight {{weight03}}
        TCP_CHECK {
            connect_timeout 2
            nb_get_retry 3
            delay_before_retry 3
        }
    }
}
EOF
调度器配置keepalived和IPVS
# 调度器配置keepalived和IPVS
[root@control lb]# cat > lvs_keepalived_NAT.yaml << EOF
--- 
- name: configure keepalived and ipvsadm
  hosts: lvs
  gather_facts: no
  vars:
    - keepalived_config: './keepalived_ipvsadm_NAT.conf'
    - VRRP_id01: '88'
    - interface01: 'eth0'
    - VIP01: '192.168.88.10'
    - VRRP_id02: '99'
    - interface02: 'eth1'
    - VIP02: '192.168.99.10'
    - lvs_port: '80'
    - RIP01: '192.168.88.81'
    - weight01: '1'
    - RIP02: '192.168.88.82'
    - weight02: '2'
    - RIP03: '192.168.88.83'
    - weight03: '1'
  tasks:
    - name: remove keepalived,ipvsadm
      yum:
        name: keepalived,ipvsadm
        state: absent
    - name: install keepadlived,ipvsadm
      yum:
        name: keepalived,ipvsadm
        state: present
    - name: template keepalived.conf
      template:
        src: '{{keepalived_config}}'
        dest: /etc/keepalived/keepalived.conf
        force: yes
        backup: no
        mode: 0644
    - name: start keepalived and enabled
      service:
        name: keepalived.service
        state: started
        enabled: yes
EOF
[root@control lb]# ansible-playbook lvs_keepalived_NAT.yaml

# 验证
[root@lvs3 ~]# ip a s
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host 
           valid_lft forever preferred_lft forever
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:0c:29:5c:7a:0b brd ff:ff:ff:ff:ff:ff
        inet 192.168.88.13/24 brd 192.168.88.255 scope global noprefixroute eth0
           valid_lft forever preferred_lft forever
        inet 192.168.88.10/32 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::6f22:6fb:a3de:bd2b/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
    3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:0c:29:5c:7a:15 brd ff:ff:ff:ff:ff:ff
        inet 192.168.99.13/24 brd 192.168.99.255 scope global noprefixroute eth1
           valid_lft forever preferred_lft forever
        inet 192.168.99.10/32 scope global eth1
           valid_lft forever preferred_lft forever
        inet6 fe80::4aca:dfb4:bb47:e7d/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
[root@lvs3 ~]# 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.99.10:80 wrr
      -> 192.168.88.81:80             Masq    1      0          0         
      -> 192.168.88.82:80             Masq    2      0          0         
      -> 192.168.88.83:80             Masq    1      0          0   

[root@lvs4 ~]# ip a s
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host 
           valid_lft forever preferred_lft forever
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:0c:29:2c:99:11 brd ff:ff:ff:ff:ff:ff
        inet 192.168.88.14/24 brd 192.168.88.255 scope global noprefixroute eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::267:f548:bb9a:7b7a/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
    3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:0c:29:2c:99:1b brd ff:ff:ff:ff:ff:ff
        inet 192.168.99.14/24 brd 192.168.99.255 scope global noprefixroute eth1
           valid_lft forever preferred_lft forever
        inet6 fe80::bee7:4d11:d800:4083/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
[root@lvs4 ~]# 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.99.10:80 wrr
      -> 192.168.88.81:80             Masq    1      0          0         
      -> 192.168.88.82:80             Masq    2      0          0         
      -> 192.168.88.83:80             Masq    1      0          0  

[root@client ~]# for i in $(seq 1 8);do curl 192.168.99.10;done
    Welcome to web1 on 192.168.88.81!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web3 on 192.168.88.83!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web1 on 192.168.88.81!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web3 on 192.168.88.83!!
    Welcome to web2 on 192.168.88.82!!

keepalived+haproxy

  • 调度器(高可用)+ Web集群(负载均衡)
    • 目标:客户端访问调度器,可以访问到Web集群的Web服务。当主调度器故障时,会切换备用调度器。
    • 环境:调度器(两台),Web集群(三台),客户端:
      • Web集群、调度器、客户端可以相互ping通,简化情况下,处于同一网段。
    • 配置步骤:Web集群部署Web服务——》调度器配置haproxy开启负载均衡功能(此时客户端可以访问调度器来访问Web集群)——》调度器配置keepalived实现高可用,编写脚本让keepalived跟踪haproxy服务,实现haproxy的高可用(此时haproxy服务故障会切换主服务器)
  • keepalived可以实现本VRRP实例的高可用,通过跟踪脚本实现服务的高可用。
haproxy配置文件
HAProxy配置文件
工作模式mode的取值:
+ `http`:只适用于http,即Web服务。HAProxy对于http相关服务做了特别优化。
+ `tcp`:适用于各种tcp端口的服务,也适用于Web服务(`http`性能更佳)。
+ `health`:仅做健康检查,很少使用

负载均衡调度算法balance的取值:
+ `roundrobin`(轮询):将请求依次发送到每个服务器,以实现请求的均衡分布。
+ `leastconn`(最小连接数):将请求发送到当前具有最少活动连接数的服务器,以实现连接的均衡负载。
+ `static-rr`(静态轮询):类似轮询,但具有固定的服务器顺序。每个新连接将按顺序分配给下一个服务器。
+ `source`(源地址哈希):根据源IP地址的哈希值来确定将请求转发到哪个服务器。相同的源IP地址将始终发送到同一个服务器。
+ `uri`(URI哈希):根据请求的URI的哈希值来确定将请求转发到哪个服务器。相同的URI将始终发送到同一个服务器。
+ `url_param`(URL参数哈希):根据请求URL中的参数值的哈希值来确定将请求转发到哪个服务器。相同的URL参数将始终发送到同一个服务器。
+ `hdr(name)`(HTTP头部哈希):根据指定的HTTP请求头部的哈希值来确定将请求转发到哪个服务器。相同的HTTP头部值将始终发送到同一个服务器。
+ `rdp-cookie(name)`(RDP Cookie哈希):根据指定的RDP Cookie的哈希值来确定将请求转发到哪个服务器。相同的RDP Cookie将始终发送到同一个服务器。
# haproxy配置文件
[root@control lb]# cat > haproxy.cfg << EOF
#---------------------------------------------------------------------
# Example configuration for a possible web application.  See the
# full configuration options online.
#
#   https://www.haproxy.org/download/1.8/doc/configuration.txt
#
#---------------------------------------------------------------------

#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    # to have these messages end up in /var/log/haproxy.log you will
    # need to:
    #
    # 1) configure syslog to accept network log events.  This is done
    #    by adding the '-r' option to the SYSLOGD_OPTIONS in
    #    /etc/sysconfig/syslog
    #
    # 2) configure local2 events to go to the /var/log/haproxy.log
    #   file. A line like the following can be added to
    #   /etc/sysconfig/syslog
    #
    #    local2.*                       /var/log/haproxy.log
    #
    log         127.0.0.1 local2

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid	
    maxconn     4000                    # 最大连接数
    user        haproxy
    group       haproxy
    daemon

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats

    # utilize system-wide crypto-policies
    ssl-default-bind-ciphers PROFILE=SYSTEM
    ssl-default-server-ciphers PROFILE=SYSTEM

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
    mode                    http        # 三种模式:http、tcp、health
    log                     global		
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

#---------------------------------------------------------------------
# 64行以下的内容:frontend配置是和前端客户端的交互;backend配置是和后端服务器的交互。可以合并使用listen监听。全部删除后增加监听项
listen myweb                            # 自定义监听项myweb
    bind 0.0.0.0:{{web_port}}           # bind绑定监听的IP地址和端口,0.0.0.0本机任意IP地址,80端口
    balance roundrobin                  # balance负载均衡调度算法,轮询roundrobin
    server web1 {{RIP01}}:{{RIP_port}} weight {{weight01}} check inter 1000 rise 2 fall 3
    server web2 {{RIP02}}:{{RIP_port}} weight {{weight02}} check inter 1000 rise 2 fall 3
    server web3 {{RIP03}}:{{RIP_port}} weight {{weight03}} check inter 1000 rise 2 fall 3
# server指定后端服务器,主机id自定义不重复。check监控检查、inter(interval)检查间隔1000毫秒,即每1秒检查一次连接。如果连续2次成功,认为服务器健康,如果连续3次失败,认为服务器故障。
listen show                             # 自定义监听项show。通过关键字stats启用HAProxy的统计功能
    bind 0.0.0.0:{{stats_port}}
    stats refresh 15s                   # 设置统计页面自动刷新时间,15秒。默认为10秒
    stats uri /show                     # 设置统计页面的访问地址,/stats。默认为"/haproxy?stats"
    stats auth admin:admin              # 设置统计页面的用户认证,用户名:密码。默认为无
EOF
调度器配置haproxy
# 调度器配置haproxy
[root@control lb]# cat > haproxy_install.yaml << EOF
---
- name: configue haproxy
  hosts: haproxy
  gather_facts: no
  vars:
    - haproxy_config: './haproxy.cfg'
    - web_port: '80'
    - RIP_port: '80'
    - RIP01: '192.168.88.81'
    - weight01: '1'
    - RIP02: '192.168.88.82'
    - weight02: '2'
    - RIP03: '192.168.88.83'
    - weight03: '1'
    - stats_port: '1080'
  tasks:
    - name: remove haproxy
      yum: 
        name: haproxy
        state: absent
    - name: install haproxy
      yum:
        name: haproxy
        state: latest
        update_cache: yes
    - name: template haproxy.cfg
      template:
        src: '{{haproxy_config}}'
        dest: /etc/haproxy/haproxy.cfg
        force: yes
        backup: no
        mode: '0644'
    - name: start haproxy and enabled
      service:
        name: haproxy.service
        state: started
        enabled: yes
EOF
[root@control lb]# ansible-playbook haproxy_install.yaml 

# 测试
[root@control lb]# for i in {1..4};do curl 192.168.88.21;done
    Welcome to web2 on 192.168.88.82!!
    Welcome to web1 on 192.168.88.81!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web3 on 192.168.88.83!!
[root@control lb]# for i in {1..4};do curl 192.168.88.22;done
    Welcome to web2 on 192.168.88.82!!
    Welcome to web1 on 192.168.88.81!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web3 on 192.168.88.83!!
keepalived配置文件
# keepalived高可用VIP,脚本检查haproxy工作状态
1. keepalived配置文件
[root@control lb]# cat > keepalived_haproxy.conf << EOF
global_defs { 
   notification_email { 
     acassen@firewall.loc 
     failover@firewall.loc 
     sysadmin@firewall.loc 
   }
   notification_email_from Alexandre.Cassen@firewall.loc  
   smtp_server 127.0.0.1 
   smtp_connect_timeout 30 
   router_id {{hostname}}   # 本机id
} 
vrrp_script check_haproxy {
    script "/etc/keepalived/check_haproxy.sh"
    interval 1              # 脚本的检查间隔1s
}
vrrp_instance VI_1 { 
    state {{state}} 		# 主MASTER,备BACKUP
    interface {{interface}}
    virtual_router_id {{VRRP_id}} 	# VRRP路由ID实例,每个VRRP实例是唯一的
    priority {{priority}}   # 优先级主高于备
    advert_int 1    		# 心跳间隔1s
    authentication {
        auth_type PASS
        auth_pass 1111
    }  
    # virtual_ipaddress:虚拟IP(VIP)
    virtual_ipaddress { 
        {{VIP}}
    }
    track_script {
        check_haproxy
    }
}
EOF

2. keepalived检查haproxy的脚本
[root@control lb]# cat > check_haproxy.sh << EOF
#!/bin/bash
ss -ntulp | grep :1080 > /dev/null
if [[ \$? == 0 ]]
then
    exit
else
    for i in \$(seq 1 2)
    do
        systemctl restart haproxy.service > /dev/null
        ss -ntulp | grep :1080 > /dev/null
        if [[ \$? == 0 ]]
        then 
            break
        fi
    done
    ss -ntulp | grep :1080 > /dev/null && exit || exit 1
fi
EOF
调度器配置keepalived
[root@control lb]# cat > haproxy_keepalived.yaml << EOF
---
- name: configure keepalived
  hosts: haproxy
  gather_facts: no
  vars:
    - keepalived_config: './keepalived_haproxy.conf'
    - check_haproxy: './check_haproxy.sh'
    - VRRP_id: '20'
    - interface: 'eth0'
    - VIP: '192.168.88.20'
  tasks:
    - name: remove keepalived
      yum:
        name: keepalived
        state: absent
    - name: install keepalived
      yum: 
        name: keepalived
        state: latest
        update_cache: yes
    - name: copy check_haproxy.sh
      copy:
        src: '{{check_haproxy}}'
        dest: /etc/keepalived/check_haproxy.sh
        force: yes
        backup: no
        mode: '0755'
    - name: template keepalived.conf
      template:
        src: '{{keepalived_config}}'
        dest: /etc/keepalived/keepalived.conf
        force: yes
        backup: no
        mode: '0644'
    - name: start keepalived and enabled
      service:
        name: keepalived.service
        state: started
        enabled: yes
EOF
[root@control lb]# ansible-playbook haproxy_keepalived.yaml 

# 验证
[root@haproxy1 ~]# ip a s eth0
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:0c:29:28:2a:f7 brd ff:ff:ff:ff:ff:ff
        inet 192.168.88.21/24 brd 192.168.88.255 scope global noprefixroute eth0
           valid_lft forever preferred_lft forever
        inet 192.168.88.20/32 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::267:f548:bb9a:7b7a/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
[root@haproxy2 ~]# ip a s eth0
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:0c:29:7c:b5:83 brd ff:ff:ff:ff:ff:ff
        inet 192.168.88.22/24 brd 192.168.88.255 scope global noprefixroute eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::79c3:82e6:9396:e1c1/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
[root@control lb]# for i in $(seq 1 8);do curl 192.168.88.20;done
    Welcome to web2 on 192.168.88.82!!
    Welcome to web1 on 192.168.88.81!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web3 on 192.168.88.83!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web1 on 192.168.88.81!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web3 on 192.168.88.83!!
若主调度器haproxy1的haproxy故障,则VIP会切换到备用调度器haproxy2成为主调度器,haproxy1的haproxy恢复后,VIP重新切换到haproxy1,实现调度器的高可用,不影响Web集群的负载均衡。

keepalived+nginx

  • 调度器(高可用)+ Web集群(负载均衡)
    • 目标:客户端访问调度器,可以访问到Web集群的Web服务。当主调度器故障时,会切换备用调度器。
    • 环境:调度器(两台),Web集群(三台),客户端:
      • Web集群、调度器、客户端可以相互ping通,简化情况下,处于同一网段。
    • 配置步骤:Web集群部署Web服务——》调度器配置nginx开启负载均衡功能(此时客户端可以访问调度器来访问Web集群)——》调度器配置keepalived实现高可用,编写脚本让keepalived跟踪nginx服务,实现nginx的高可用(此时nginx服务故障会切换主服务器)
  • keepalived可以实现本VRRP实例的高可用,通过跟踪脚本实现服务的高可用。
nginx和keepalived的配置文件
# nginx监听80反向代理,keepalived高可用VIP,脚本检查nginx工作状态
1. nginx配置文件
[root@control lb]# cat > nginx.conf << EOF
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}
# 增加四层负载均衡,为两台Master apiserver组件提供负载均衡。其它内容不变
stream {

    log_format  main  '\$remote_addr \$upstream_addr - [\$time_local] \$status \$upstream_bytes_sent';

    access_log  /var/log/nginx/web-access.log  main;

    upstream myweb {
            server {{RIP01}}:{{RIP_port}} weight={{weight01}} max_fails=3 fail_timeout=30s;  
            server {{RIP02}}:{{RIP_port}} weight={{weight02}} max_fails=3 fail_timeout=30s;
            server {{RIP03}}:{{RIP_port}} weight={{weight03}} max_fails=3 fail_timeout=30s;
    }
    
    server {
       listen {{web_port}}; 	# 由于使用四层代理,监听端口不能使用80,否则会冲突。七层代理则可以使用80
       proxy_pass myweb;
    }
}

http {
    log_format  main  '\$remote_addr - \$remote_user [\$time_local] "\$request" '
                      '\$status \$body_bytes_sent "\$http_referer" '
                      '"\$http_user_agent" "\$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    server {
        listen       80 default_server;
        server_name  _;

        location / {
        }
    }
}
EOF

2. keepalived配置文件,使用变量
[root@control lb]# cat > keepalived_nginx.conf << EOF
global_defs { 
   notification_email { 
     acassen@firewall.loc 
     failover@firewall.loc 
     sysadmin@firewall.loc 
   } 
   notification_email_from Alexandre.Cassen@firewall.loc  
   smtp_server 127.0.0.1 
   smtp_connect_timeout 30 
   router_id {{hostname}}           # 本机id
} 
# vrrp_script:指定检查nginx工作状态脚本(根据nginx状态判断是否故障转移)
vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"
}

vrrp_instance VI_1 { 
    state {{state}}                 # 主MASTER,备BACKUP
    interface {{interface}}  	
    virtual_router_id {{VRRP_id}}   # VRRP路由ID实例,每个VRRP实例是唯一的
    priority {{priority}}           # 优先级主高于备
    advert_int 1    		
    authentication { 
        auth_type PASS      
        auth_pass 1111 
    }  
    # virtual_ipaddress:虚拟IP(VIP)
    virtual_ipaddress { 
        {{VIP}}
    } 
    track_script {
        check_nginx
    } 
}
EOF

3. keepalived检查nginx工作状态的脚本
[root@control lb]# cat > check_nginx.sh << EOF
#!/bin/bash
#1、判断Nginx是否存活
counter=\$(ps -ef |grep nginx | grep sbin | egrep -cv "grep" )
if [ \$counter -eq 0 ]; then
    #2、如果不存活则尝试启动Nginx
    service nginx start
    sleep 2
    #3、等待2秒后再次获取一次Nginx状态
    counter=\$(ps -ef |grep nginx | grep sbin | egrep -cv "grep" )
    #4、再次进行判断,如Nginx还不存活则停止Keepalived,让地址进行漂移
    if [ \$counter -eq 0 ]; then
    	service keepalived stop
    fi
fi
EOF
调度器配置nginx和keepalived
[root@control lb]# cat > nginx_keepalived.yaml << EOF
---
- name: configure nginx and keepalived
  hosts: nginx
  gather_facts: no
  vars:
    - nginx_conf: './nginx.conf'
    - keepalived_conf: './keepalived_nginx.conf'
    - check_nginx_sh: './check_nginx.sh'
    - web_port: '10080'
    - RIP_port: '80'
    - RIP01: '192.168.88.81'
    - weight01: '1'
    - RIP02: '192.168.88.82'
    - weight02: '2' 
    - RIP03: '192.168.88.83'
    - weight03: '1' 
    - VRRP_id: '30'
    - interface: 'eth0'
    - VIP: '192.168.88.30'
  tasks:
    - name: remove nginx,keepalived     # 卸载nginx,keepalived
      yum: 
        name: nginx,keepalived
        state: absent
    - name: install nginx,keepalived    # 安装nginx,keepalived
      yum:
        name: nginx,keepalived
        update_cache: yes
    - name: config nginx                # 配置nginx
      template:
        src: '{{nginx_conf}}'
        dest: /etc/nginx/nginx.conf
        force: yes
        backup: no
        owner: nginx
        group: nginx
        mode: '0755'
    - name: config keepalived           # 配置keepalived
      template:
        src: '{{keepalived_conf}}'
        dest: /etc/keepalived/keepalived.conf
        force: yes
        backup: no
        mode: '0644'                    # 注意:keepalived.conf不能有x权限,否则无法启服务,报错:Fail to start LVS and VRRP High Availability Monitor
    - name: copy check_nginx.sh         # 拷贝keepalived检查nginx的脚本
      copy: 
        src: '{{check_nginx_sh}}'
        dest: /etc/keepalived/check_nginx.sh
        force: yes
        backup: no
        mode: '0755'                    # 注意:检查脚本check_nginx.sh需要有x权限
    - name: start nginx and keepalived  # 启服务
      service:
        name: '{{ item }}'
        state: started
        enabled: yes
      loop:
        - nginx.service
        - keepalived.service
EOF
[root@control lb]# ansible-playbook nginx_keepalived.yaml

# 验证
[root@nginx1 ~]# ip a s eth0
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:0c:29:24:d2:f8 brd ff:ff:ff:ff:ff:ff
        inet 192.168.88.31/24 brd 192.168.88.255 scope global noprefixroute eth0
           valid_lft forever preferred_lft forever
        inet 192.168.88.30/32 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::ca3a:810f:bbd:ce74/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
[root@nginx2 ~]# ip a s eth0
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:0c:29:96:4a:8e brd ff:ff:ff:ff:ff:ff
        inet 192.168.88.32/24 brd 192.168.88.255 scope global noprefixroute eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::6f22:6fb:a3de:bd2b/64 scope link noprefixroute 
           valid_lft forever preferred_lft forever
[root@control lb]# for i in $(seq 1 8);do curl 192.168.88.30:10080;done
    Welcome to web2 on 192.168.88.82!!
    Welcome to web1 on 192.168.88.81!!
    Welcome to web3 on 192.168.88.83!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web2 on 192.168.88.82!!
    Welcome to web1 on 192.168.88.81!!
    Welcome to web3 on 192.168.88.83!!
    Welcome to web2 on 192.168.88.82!!

问题

脑裂

什么是脑裂?
    在高可用系统(keepalived)中,当联系2个节点的“心跳线”(VRRP实例)断开时,本来为一整体、动作协调的HA系统,就分裂成为2个独立的个体。
    由于相互失去了联系,都以为是对方出了故障。两个节点上的HA软件像“裂脑人”一样,争抢“共享资源”、争起“应用服务”,就会发生严重后果。共享资源被瓜分、两边“服务”都起不来了;或者两边“服务”都起来了,但同时读写“共享存储”,导致数据损坏。

脑裂的原因:
    1.网线导致的网络故障
    2.交换机故障导致的网络故障
    3.网卡故障导致的网络故障
    4.网卡驱动故障导致的网络故障
    5.IP配置及冲突等导致的网络故障
    6.防火墙或iptables等拦截了心跳消息的传输
    7.其他服务配置不当等原因,如心跳方式不同,心跳广插冲突、软件Bug等。

解决脑裂问题:
	1.保持网络通信正常
	2.防火墙或iptables等不拦截心跳消息
	3.其它检测方式保证节点间的心跳检测

web服务

nginx、apache、tomcat


nginx采用的是epoll模型和kqueue网络模型,而apache采用的是select模型
    epoll:在Linux系统上使用,由于采用了红黑树的数据结构,具有较高的性能,支持水平触发和边缘触发。
    kqueue:在BSD和MacOS系统上使用,也支持水平触发和边缘触发,性能较epoll略低一些。
    select:在多个操作系统上都通用,性能相对较差,适用于连接数较少的情况。

> 假设你在大学读书,住的宿舍楼有很多间房间,你的朋友要来找你。
    select版宿管大妈就会带着你的朋友挨个房间去找,直到找到你为止。
    而epoll版宿管大妈会先记下每位同学的房间号,
    你的朋友来时,只需告诉你的朋友你住在哪个房间即可,不用亲自带着你的朋友满大楼找人。
    如果来了10000个人,都要找自己住这栋楼的同学时,select版和epoll版宿管大妈,谁的效率更高,不言自明。
    同理,在高并发服务器中,轮询I/O是最耗时间的操作之一,select和epoll的性能谁的性能更高,同样十分明了。
	select 采用的是轮询的方式来处理请求,轮询的次数越多,耗时也就越多。

Nginx
    轻量级,采用 C 进行编写,同样的 web 服务,会占用更少的内存及资源
    抗并发,nginx 以 epoll and kqueue 作为开发模型,处理请求是'异步非阻塞'的,负载能力比apache 高很多,而 apache 则是'阻塞型'的。在高并发下 nginx 能保持低资源低消耗高性能 ,而apache 在 PHP 处理慢或者前端压力很大的情况下,很容易出现进程数飙升,从而拒绝服务的现象。
    nginx 处理静态文件好,静态处理性能比 apache 高三倍以上
    nginx 的设计高度模块化,编写模块相对简单
    nginx 配置简洁,正则配置让很多事情变得简单,而且改完配置能使用 -t 测试配置有没有问题,apache 配置复杂 ,重启的时候发现配置出错了,会很崩溃
    nginx 作为负载均衡服务器,支持 7 层负载均衡,'七层负载可以有效的防止ddos攻击'
    nginx本身就是一个反向代理服务器,也可以作为邮件代理服务器来使用
Apache
    apache 的 rewrite 比 nginx 强大,在 rewrite 频繁的情况下,用 apache
    apache 发展到现在,模块超多,基本想到的都可以找到
    apache 更为成熟,少 bug ,nginx 的 bug 相对较多
    apache 对 PHP 支持比较简单,nginx 需要配合其他后端用
    apache 在处理动态请求有优势,nginx 在这方面是鸡肋,'一般动态请求要 apache' 去做,'nginx 适合静态和反向'。
    apache 仍然是目前的主流,拥有丰富的特性,成熟的技术和开发社区
    两者最核心的区别在于 apache 是同步多进程模型,一个连接对应一个进程,而 nginx 是异步的,多个连接(万级别)可以对应一个进程。
    需要'稳定用apache',需要'高性能用nginx'

Tomcat作为web的优缺点?
    缺点:tomcat 只能用做java服务器,处理静态请求的能力不如nginx和apache。,高并发能力有限
    优点:动态解析容器,处理动态请求,是编译JSP/Servlet的容器,轻量级

cgi和fastcgi


CGI (Common Gateway Interface) 是一种协议,用于建立Web服务器和外部程序之间的通信,以动态生成Web页面。它的工作方式是web服务器每次接收请求都创建一个新的进程来处理,并在处理完请求后将结果发送给客户端并退出。

FastCGI 是对CGI 的改进和扩展,它web服务启动时就开启了若干个持续的进程,在处理多个请求也不会立即退出,避免了CGI 中频繁创建和退出进程的开销,从而提高了性能。

因此,主要的区别在于CGI 每次请求都创建一个新的进程,而FastCGI 可以在持续的进程中处理多个请求,提高了性能。

正向代理和反向代理

正向代理,其实是"代理服务器"代理了"客户端",去和"目标服务器"进行交互。
正向代理的用途:
    提高访问速度
    隐藏客户真实IP

反向代理:
反向代理是 在服务端的,不需要访问用户关心。用户访问服务器A, A服务器是代理服务器,将用户服务再转发到服务器B.这就是反向代理
反向代理的作用:
    1.缓存,将服务器的响应缓存在自己的内存中,减少服务器的压力。
    2.负载均衡,将用户请求分配给多个服务器。
    3.访问控制

如何在Nginx中设置缓存时间和大小:
    # 在Nginx配置文件中添加以下配置来定义一个名为"my_cache"的缓存路径和设置缓存大小为100MB:
    http {
        proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=100m inactive=1d;
    }
    # 在具体的location中使用proxy_cache指令来开启缓存并设置缓存的有效时间为1小时:
    server {
        location / {
            proxy_pass http://backend/;
            proxy_cache my_cache;
            proxy_cache_valid 200 304 1h;
        }
    }
在上述示例中,我们定义了名为"my_cache"的缓存路径,缓存大小为100MB,有效时间为1小时。根据实际需求和服务器性能,可以根据情况调整缓存路径、缓存大小、以及缓存的有效时间参数。
在示例中,proxy_cache_valid 200 304 1h;中的200和304表示了响应状态码。具体解释如下:
200:表示成功响应,即服务器成功返回请求的数据。
304:表示无需返回内容,客户端可以使用缓存的响应。
因此,在这个示例中,我们设置了对于返回状态码为200(成功响应)和304(无需返回内容)的请求,缓存的有效时间为1小时。这样可以有效地控制缓存的过期时间,以提高性能并降低服务器的负载。

Squid、Varinsh、Nginx 有什么区别?
    三者都实现缓存服务器的作用
    Nginx本来是反向代理/web服务器,用了插件可以做做这个副业(缓存服务器)。但本身支持的特性不是很多,只能缓存静态文件
    varinsh 和squid是专业的cache服务,而nginx这些需要使用第三方模块
    varinsh本身在技术上的优势要高于squid,它采用了可视化页面缓存技术。
    要做 cache 服务的话,我们肯定是要选择专业的 cache 服务,优先选择Squid 或者 Varnish

  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值