概诉
根据LVS四层负载均衡器所述,本节来详细搭建一下LVS环境, 这里我们只做NAT模型和DR模型。
LVS服务的搭建
ipvsadm
安装:
1 | sudo yum ipvsadm -y |
- ipvsadm 是LVS 管理集群服务的命令行工具,用法如下
1 | ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] [-M netmask] [-b sched-flags] |
- 管理集群服务:
1 | ipvsadm -A|E -t|u|f service-address [-s scheduler] |
- 管理集群服务上的RS:
1 | ipvsadm -a|e -t|u|f service-address -r server-address [-g|i|-r] [-w weight] |
- 查看
1 | ipvsadm -L|l [options] |
- 清空和保存:
1 | ipvsadm -C |
- 保存和重载:
1 | 保存: |
环境准备
环境概述
-
lvs-node-1: RS(CentOS7) sshd nginx
-
lvs-node-2: RS(CentOS7) sshd nginx
-
lvs-node-0: LVS(CentOS7) bind(name server) sshd ipvsadm
-
vmware funsion vmnet4 host-only 虚拟网路
-
nginx web server
-
DNS server
-
网络拓扑如下:
搭建vmnet4 虚拟网络
打开偏好设置
新建 vmnet4虚拟交换机
新建 vmnet4 子网为 172.16.100.0
LVS设置hostname 和 网卡信息
- 设置lvs-node-0 双网卡
虚拟机添加网卡,使得该网卡链接在虚拟vmnet4 设备之上
网卡信息如下:
vim /etc/sysconfig/network-scripts/ifcfg-ens33
编辑如下: 注意172.16.100.10 不要配置网关
1 | TYPE=Ethernet |
vim /etc/sysconfig/network-scripts/ifcfg-ens37
编辑如下:
1 | TYPE=Ethernet |
- 设置hostname
hostnamectl set-hostname lvs-node-0
- 关闭防火墙
systemctl stop firewalld
systemctl disable firwalld
- 安装dns服务(这里不做介绍,也可以做可以不做)
1 | 并设置 lvs-node-1.exmaple.com 为 172.16.100.11 |
- 设置完成重启网卡后网卡信息如下:
systemctl restart network
1 | root@lvs-node-0 ~]# ifconfig |
RS设置hostname 和 网卡信息
- 设置lvs-node-1 和 lvs-node-2 hostname
hostnamectl set-hostname {hostname}
- 设置lvs-node-1 和 lvs-node-2 网卡信息
vim /etc/sysconfig/network-scripts/ifcfg-ens33
编辑如下:DNS1设置刚才配置的
1 | TYPE=Ethernet |
安装nginx:
在 lvs-node-1 和lvs-node-2 上安装nginx
-
添加源:默认情况Centos7中无Nginx的源,最近发现Nginx官网提供了Centos的源地址。因此可以如下执行命令添加源:
sudo rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
-
安装Nginx
sudo yum install -y nginx
-
设置keepd-alive 为0(这一点很重要,关掉之后,我们才可以在浏览器看到负载的效果)
vim /etc/nginx/nginx.conf
1
2
3
4http {
// ...
keepalive_timeout 0; // 修改 keepalive_timeout 为0 关闭keepalive
} -
启动Nginx并设置开机自动运行
sudo systemctl start nginx.service
sudo systemctl enable nginx.service -
关闭防火墙
sudo systemctl stop firewalld
sudo systemctl disable firewalld -
设置nginx index 页面(在lvs-node-1 lvs-node-2上分别设置一下index.html)
vim /usr/share/nginx/html/index.html
替换hostname 和本地ip 到下面的{}设置
如下:
1
2
3
4
5
6
<html>
<body>
<h1>Welcome to nginx in {hostname}</h1>
</body>
</html> -
在lvs-node-0上验证服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19[root@lvs-node-0 ~]# curl http://172.16.100.11
<html>
<head>
</head>
<body>
<h1>Welcome to nginx in lvs-node-1</h1>
</body>
</html>
[root@lvs-node-0 ~]# curl http://172.16.100.12
<html>
<head>
</head>
<body>
<h1>Welcome to nginx in lvs-node-2</h1>
</body>
</html>
[root@lvs-node-0 ~]#- lvs-node-0 ip_forward 设置为转发
出于安全考虑,Linux系统默认是禁止数据包转发的。所谓转发即当主机拥有多于一块的网卡时,其中一块收到数据包,根据数据包的目的ip地址将包发往本机另一网卡,该网卡根据路由表继续发送数据包。这通常就是路由器所要实现的功能。
配置Linux系统的ip转发功能,首先保证硬件连通,然后打开系统的转发功能
/proc/sys/net/ipv4/ip_forward,该文件内容为0,表示禁止数据包转发,1表示允许,将其修改为1。可使用注意 proc 下的文件是内核运行后映射的内存参数, 不可使用vim 进行修改,要使用重定向才能修改生效
sudo echo “1” > /proc/sys/net/ipv4/ip_forward
修改内核参数文件
sudo echo “net.ipv4.ip_forward = 1” >> /etc/sysctl.conf
配置生效
sudo sysctl -p
LVS 四种模式的搭建
NAT模式
- NAT的特点LVS NAT模式
注意,当我们配置的NAT模式的时候,我们的网关要经过lvs服务器,所以上述的网关要执行lvs节点。
- 添加lvs-node-0 上执行命令
1 |
|
- 使用curl 实验负载均衡
1 | ➜ ~ curl http://192.168.100.6:80 |
总结:我们宿主机的数据包通过请求vip 到达了 lvs-node-0节点,阶段的lvs自动生效,通过将目的地址的ip转换,在通过ens33 接口转发转发出去,到达我们read server,当数据包回来的时候,real server 通过网关LVS机器在转发给我们的客户端。
DR模式
- DR的特点LVS DR模式
环境准备
环境准备参考如上配置,首先我们应该清空我们的规则
1 | [root@lvs-node-0 ~]# ipvsadm -C |
本次要解决的网络拓扑为不同子网之间的沟通问题,通过我们对DR模式的了解,我们知道,DR模式是通过修改mac地址达到负载的,这就要求我们需要将lvs的各个节点链接到同一个交换机上。
此时,我们的可选方案有:
1、 所有机器桥接链接Vmnet8 桥接在我们的物理网卡上,如下图:
-
一般网卡显示vmnet0,桥接模式就是将主机网卡与虚拟机虚拟的网卡利用虚拟网桥进行通信。在桥接的作用下,类似于把物理主机虚拟为一个交换机,所有桥接设置的虚拟机连接到这个交换机的一个接口上,物理主机也同样插在这个交换机当中,所以所有桥接下的网卡与网卡都是交换模式的,相互可以访问而不干扰。在桥接模式下,虚拟机ip地址需要与主机在同一个网段,如果需要联网,则网关与DNS需要与主机网卡一致。
-
虚拟网桥会转发主机网卡接收到的广播和组播信息,以及目标为虚拟交换机,网段的单播。所以,与虚拟交换机机连接,的虚拟网卡(如: eth0、 eth1等)接收到
了路由器发出的DHCP信息及路由更新。 -
桥接模式是通过虚拟网桥将主机上的网卡与虚拟交换机Vmnet0连接在一起, 虚拟机上的
虚拟网卡(并不是VMware Network Adapter VMnet1和VMware Network Adapter VMnet8)都连接在虚拟交换机Vmnet0上,所以桥接模式的虚拟机IP必须与主机在同一网段且子网掩码、网关与DNS也要与主机网卡-致。
我们这次以下面这种为搭建对象。
2、 所有机器链接Vmnet4, Vment4通过NAT连接外网。如下图:
-
当我们选用vmet4 可以连通外网的时候,vmnet虚拟交换机会虚拟出一个网关,这个网关链接在我们对外的网卡上,一般情况下,ip地址为vmnetip +1
-
可以将原来的删除vmnet8的桥接网络。
例如 vment4的子网为 172.16.100.0则 vment4虚拟适配器地址为 172.16.100.1
- 则会在一下地址出现 nat.conf 文件
1 | cd /Library/Preferences/VMware Fusion/vmnet4 |
里面有个配置为:gateway
- 并配置lvs-node-1 和 lvs-node-2 的网关为 172.16.100.2 然后重启我们的网卡。
重启虚拟网络设备
1 | sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli --stop |
配置 lvs-node-0 1 2 的网关 并配置相应的ip地址和DNS服务器
1 | # /etc/sysconfig/network-scripts/ifcfg-ens33 |
#重启网络服务
1 | sudo systemctl restart network |
配置lvs-node-1 和 lvs-node-2 内核参数
注意此服务要优先于 vip 的配置
arp_ignore和arp_announce介绍
arp_ignore和arp_announce参数都和ARP协议相关,主要用于控制系统返回arp响应和发送arp请求时的动作。
-
arp_ignore 参数的作用是控制系统在收到外部的arp请求时,是否要返回arp响应。
- 0:响应任意网卡上接收到的对本机IP地址的arp请求(包括环回网卡上的地址),而不管该目的IP是否在接收网卡上。
- 1:只响应目的IP地址为接收网卡上的本地地址的arp请求。
- 2:只响应目的IP地址为接收网卡上的本地地址的arp请求,并且arp请求的源IP必须和接收网卡同网段。
- 3:如果ARP请求数据包所请求的IP地址对应的本地地址其作用域(scope)为主机(host),则不回应ARP响应数据包,如果作用域为全局(global)或链路(link),则回应ARP响应数据包。
- 4~7:保留未使用
- 8:不回应所有的arp请求
sysctl.conf中包含all和eth/lo(具体网卡)的arp_ignore参数,取其中较大的值生效。
-
(1)当arp_ignore参数配置为0时,eth1网卡上收到目的IP为环回网卡IP的arp请求,但是eth1也会返回arp响应,把自己的mac地址告诉对端。 如下图
- (2)当arp_ignore参数配置为1时,eth1网卡上收到目的IP为环回网卡IP的arp请求,发现请求的IP不是自己网卡上的IP,不会回arp响应。
- arp_announce
作用是控制系统在对外发送arp请求时,如何选择arp请求数据包的源IP地址。
(比如系统准备通过网卡发送一个数据包a,这时数据包a的源IP和目的IP一般都是知道的,而根据目的IP查询路由表,发送网卡也是确定的,故源MAC地址也是知道的,这时就差确定目的MAC地址了。而想要获取目的IP对应的目的MAC地址,就需要发送arp请求。arp请求的目的IP自然就是想要获取其MAC地址的IP,而arp请求的源IP是什么呢? 可能第一反应会以为肯定是数据包a的源IP地址,但是这个也不是一定的,arp请求的源IP是可以选择的,控制这个地址如何选择就是arp_announce的作用)
arp_announce参数常用的取值有0,1,2。
- 0:允许使用任意网卡上的IP地址作为arp请求的源IP,通常就是使用数据包a的源IP。
- 1:尽量避免使用不属于该发送网卡子网的本地地址作为发送arp请求的源IP地址。
- 2:忽略IP数据包的源IP地址,选择该发送网卡上最合适的本地地址作为arp请求的源IP地址。
sysctl.conf中包含all和eth/lo(具体网卡)的arp_ignore参数,取其中较大的值生效。
- (1) 当arp_announce参数配置为0时,系统要发送的IP包源地址为eth1的地址,IP包目的地址根据路由表查询判断需要从eth2网卡发出,这时会先从eth2网卡发起一个arp请求,用于获取目的IP地址的MAC地址。该arp请求的源MAC自然是eth2网卡的MAC地址,但是源IP地址会选择eth1网卡的地址。
- (2)当arp_announce参数配置为2时,eth2网卡发起arp请求时,源IP地址会选择eth2网卡自身的IP地址。
配置方法有以下三种
- 修改/etc/sysctl.conf文件,然后sysctl -p刷新到内存。
1 | net.ipv4.conf.all.arp_ignore=1 |
- 使用sysctl -w直接写入内存:
1 | sysctl -w net.ipv4.conf.all.arp_ignore=1 |
- 修改/proc文件系统:
1 | echo "1">/proc/sys/net/ipv4/conf/all/arp_ignore |
配置 lvs-node-0 1 2 的 vip
1 | [root@lvs-node-0 ~]# ifconfig lo:0 172.16.100.200/32 broadcast 172.16.100.200 up |
- 增加路由条目,让host 为 192.168.100.6 的数据通过 lo:0 网卡出去
1 | route add -host 172.16.100.200 dev lo:0 |
- 查看arp 条目是否符合预期
- 添加LVS 配置
1 | ipvsadm -A -t 172.16.100.200:80 -s rr |
- 实验效果
1 | ➜ ~ curl http://172.16.100.200 |
FWM应用
FWM的应用场景 Session 保持的问题。
在电商行业,我们有一些东西是,需要保存到Seesion 当中的,当我们进行LVS调度的时候,需要进行Session的同步。业内对于Session 有很多已经很成熟的做法,
比如
- Session 复制
- Seesion 服务器
- 其中还有一种是连接定向。
链接定向
所谓的链接定向就是以某个特定的算法将一个请求定向到同一个RealServer上。这就是我们在负载均衡算法那个章节说的SH算法。
但是试想一下这样的情景, http是明文的,https是在http基础上增加了ssl的过程,但是https毕竟在性能损耗上多了个一个,因此,有些服务会存在http和https共存的现象,有些是可以通过http访问的,比如说一些静态资源,有一些服务必须要通过https进行,比如说登录注册。
比如,我们在浏览网站商品的时候使用http,但是登录的时候就会跳转成为https,这种意味着网站必须同时提供了80和443两种服务
-
链接定向的负载问题:
A客户端访问HTTP集群时,LVS会通过SH算法给A客户端分配一个real_server_1,以后A客户端再次访问V80端口时就一直是real_server_1给客户端A服务;
而B客户端访问的是443端口,通过SH算法依然可能将B客户端分配给real_server_1,因为SH算法是静态算法并不考虑服务器的负载,仅仅通过算法本身进行转发,这就可能导致在real_server_1的负载过重,而real_server_2可能会一直闲着,如何进行平衡一下呢?
-
Session 问题
除了平衡问题之外,还有一个问题,A客户端浏览商品和向购物车当中加商品时通过80端口集群被分发到real_server_1上。
但当A用户想登付款时,这时用户会通过HTTPS访问,结果由于是两个集群各自通过算法分发,把A用户又给分发到real_server_2上去了,real_server_1上并没有A用户的缓存记录,结果用户登录上之后发现购物车里面的商品全都不见了!!
可能你会说,不是通过SH算法吗?源地址哈希怎么还能把同一个用户不同请求调度到不同的real server上呢?
当A客户端访问的是80端口时保持连接没有问题,但是当A客户端用443端口访问时,这对于LVS的443端口集群来说这是一个新的用户请求,A用户之前通过80端口访问的连接记录在80集群的缓存上保存着呢?443端口集群没有呀!所以就会出现上述情况。
解决办法其实也好简单,我们只要把两个集群合并成为一个集群,然后统一进行调度不就行了吗?可是一个集群是80,另一个集群是443,如何合并呢?这个时候我们就要用到iptables当中mangle功能了。
FWM
FWM:FireWall Mark MARK target 可用于给特定的报文打标记 --set-mark value 其中:value 可为0xffff格式,表示十六进制数字 借助于防火墙标记来分类报文,而后基于标记定义集群服务;可将多个不同的应用使用同一个集群服务 进行调度,
- FWM 工作原理
lvs的ipvs函数位于net-filter架构当中的input函数上,我们在input函数的前面,也就是prerouting上将访问VIP的80端口和443端口的都打上一样的标记,就类似于交换机上的tag;
然后我们再通知ipvs函数,不让它根据报文当中的IP+端口的方式强行改道了,而是根据标记强行改道,只要报文上有这个标记的,统统改道到postrouting上,这样同一个用户无论访问的是80端口和还是443端口,都会统一通过一个SH算法调度到同一台R-SERVER上,并提高了缓存命中率,有效的解决了用户在将商品加入完购物车之后付款时商品丢失问题,而且因为是一个集群,所以也不会出现两个集群通过不同算法调度导致R-SERVER负载不平衡的问题。
- FWM命令
1 | iptables -t mangle -A PREROUTING -d ${vip} -p ${protocol} --dport ${port} -j MARK --set-mark 10 |
IPVS 持久链接
什么是持久链接
在LVS中,持久连接是为了用来保证当来自同一个client的请求时,director能够将这些请求调度定位到第一次选定的Real Server。
持久链接类型
- PPC(persistent port connections)
持久的端口连接,将来自于同一个客户端对同一个服务(端口)的请求,始终定向至此前选定的RS。
例如:来自同一个IP的用户第一次访问集群的80端口分配到Real Server1,433号端口分配到Real Server2。当之后这个用户继续访问80端口仍然分配到Real Server1,433号端口仍然分配到Real Server2。
PCC(persistent client connections)
将来自于同一个客户端的所有请求统统定向至此前选定的RS;也就是只要IP相同,分配的服务器始终相同。
例如,来自同一个IP的用户访问集群的80端口分配到Real Server1,然后用户访问433号端口仍然分配到Real Server1。但如需要SSH:22连接管理Director时,也被分配到Real Server就不好了,下面的PNMPP可以解决这个问题。
- PNMPP(Persistent Netfilter Marked Packet Persistence)
基于防火墙标记的持久性连接,这种防火墙标记仅在数据包在分发器上时有影响,数据包一旦离开Director,就不再被标记。
需要用到iptables的mangle表为数据包设置Mark标记,mangle表主要用于修改数据包的TOS(Type Of Service,服务类型)、TTL(Time To Live,生存周期)指以及为数据包设置Mark标记。
它可以将两个毫不相干的端口定义为一个集群服务,例如:合并http的80端口和https的443端口定义为同一个集群服务,而不会出现上面PCC据说的问题。
搭建持久链接及其防火墙表示
1 | # 设置防火墙标记 |
- PPC 和PCC 持久连接方法
#0表示所有端口,无论什么端口都会进行分发操作,我们只要请求的是这个地址,都会统统转发至realserver中 如果加上端口号 则表示PCC
1 |
|
参考
- https://blog.csdn.net/u010948569/article/details/80766597
- mac版VMWare的vmnet8默认网关和ip配置
- FireWall Mark + LVS
赞赏一下