企业实战(13)LVS负载均衡NAT(网络地址转换)模式实战详解(一)

2022-06-22 14:27:36 浏览数 (1)

什么是集群

  • 一组通过高速网络互连的计算组,并以单一系统的模式加以管理。
  • 将很多服务器集中起来一起,提供同一种服务,在客户端看起来就像是只有一个服务器。
  • 可以在付出较低成本的情况下获得在性能、可靠性、灵活性方面的相对较高的收益。
  • 任务调度是集群系统中的核心技术。

集群分类

  • 高性能计算集群HPC  -通过以集群开发的并行应用程序,解决 复杂的科学问题
  • 负载均衡(LB)集群  -客户端负载在计算机集群中尽可能平均分摊
  • 高可用(HA)集群  -避免单点故障,当一个系统发生故障时,可以快速迁移

负载均衡类型

- DNS 实现负载均衡

 DNS 实现负载均衡是最基础简单的方式。一个域名通过 DNS 解析到多个 IP,每个 IP 对应不同的服务器实例,这样就完成了流量的调度,虽然没有使用常规的负载均衡器,但也的确完成了简单负载均衡的功能。

- 硬件负载均衡

 硬件负载均衡是通过专门的硬件设备来实现负载均衡功能,类似于交换机、路由器,是一个负载均衡专用的网络设备。目前业界典型的硬件负载均衡设备有两款:F5 和 A10。这类设备性能强劲、功能强大,但价格非常昂贵,一般只有 “土豪” 公司才会使用此类设备,普通业务量级的公司一般负担不起,二是业务量没那么大,用这些设备也是浪费。

- 软件负载均衡

 软件负载均衡,可以在普通的服务器上运行负载均衡软件,实现负载均衡功能。目前常见的有 Nginx、HAproxy、LVS。  区别:   -Nginx :是 7 层负载均衡,支持 HTTP、E-mail 协议,貌似也支持 4 层负载均衡了。   -HAproxy :是 7 层负载均衡软件,支持 7 层规则的设置,性能也很不错。OpenStack 默认使用的负载均衡软件就是 HAproxy   -LVS :是纯 4 层的负载均衡,运行在内核态,性能是软件负载均衡中最高的,因为是在四层,所以也更通用一些。

负载均衡LVS简介

 LB集群的架构和原理很简单,就是当用户的请求过来时,会直接分发到Director Server上,然后它把用户的请求根据设置好的调度算法,智能均衡地分发到后端真正服务器(real server)上。为了避免不同机器上用户请求得到的数据不一样,需要用到了共享存储,这样保证所有用户请求的数据是一样的。

 LVS是 Linux Virtual Server 的简称,也就是Linux虚拟服务器。这是一个由章文嵩博士发起的一个开源项目, 现在 LVS 已经是 Linux 内核标准的一部分。使用 LVS 可以达到的技术目标是:通过 LVS 达到的负载均衡技术和 Linux 操作系统实现一个高性能高可用的 Linux 服务器集群,它具有良好的可靠性、可扩展性和可操作性。从而以低廉的成本实现最优的性能。LVS 是一个实现负载均衡集群的开源软件项目,LVS架构从逻辑上可分为调度层、Server集群层和共享存储。

LVS的基本工作原理

在这里插入图片描述  1.当用户向负载均衡调度器(Director Server)发起请求,调度器将请求发往至内核空间

 2.PREROUTING链首先会接收到用户请求,判断目标IP确定是本机IP,将数据包发往INPUT链

  3.IPVS是工作在INPUT链上的,当用户请求到达INPUT时,IPVS会将用户请求和自己已定义好的集群服务进行比对,如果用户请求的就是定义的集群服务,那么此时IPVS会强行修改数据包里的目标IP地址及端口,并将新的数据包发往POSTROUTING链

 4.POSTROUTING链接收数据包后发现目标IP地址刚好是自己的后端服务器,那么此时通过选路,将数据包最终发送给后端的服务器

LVS负载均衡调度算法

  • LVS目前实现了10种调度算法
  • 常用调度算法有4种  -`轮询` -将客户端请求平均分发到Real Server  -`加权轮询` -根据Real Server权重值进行轮询调度  -`最少连接` -选择连接数最少的服务器  -`加权最少连接 ` -根据Real Server权重值,选择连接数最少的服务器  -源地址散列   -根据请求的目标IP地址,作为散列键(Hash Key)从静态分配的散列表找出对应的服务器

LVS集群组成

- 前端:负载均衡层   -由一台或多台负载调度器构成

- 中间:服务器群组层   -由一组实际运行应用服务的服务器组成

- 底端:数据共享存储层   -提供共享存储空间的存储区域

相关术语

DS Director Server。指的是前端负载均衡器节点。

RS Real Server。后端真实的工作服务器。

CIP Client IP,表示的是客户端 IP 地址。

VIP Virtual IP,表示负载均衡对外提供访问的 IP 地址,一般负载均衡 IP 都会通过 Virtual IP 实现高可用。

RIP RealServer IP,表示负载均衡后端的真实服务器 IP 地址。

DIP Director IP,表示负载均衡与后端服务器通信的 IP 地址。

LVS工作模式

LVS/NAT:网络地址转换

 -通过网络地址转换实现的虚拟服务器

 -大并发访问时,调度器的性能成为瓶颈

-LVS/DR:直接路由

 -直接使用路由技术实现虚拟服务器

 -节点服务器需要配置VIP,注意MAC地址广播

-LVS/TUN:IP隧道

 -通过隧道方式实现虚拟服务器

LVS各工作模式原理

 更详细原理可参考博客:https://blog.csdn.net/liwei0526vip/article/details/103104483

1.网络地址转换(LVS-NAT)

在这里插入图片描述

代码语言:javascript复制
1.当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP 。

2.PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链

3.IPVS比对数据包请求的服务是否为集群服务,若是,修改数据包的目标IP地址为后端服务器IP,然后将数据包发至POSTROUTING链。 此时报文的源IP为CIP,目标IP为RIP 4.POSTROUTING链通过选路,将数据包发送给Real Server

5.Real Server比对发现目标为自己的IP,开始构建响应报文发回给Director Server。 此时报文的源IP为RIP,目标IP为CIP

6.Director Server在响应客户端前,此时会将源IP地址修改为自己的VIP地址,然后响应给客户端。 此时报文的源IP为VIP,目标IP为CIP

2.直接路由(LVS-DR)

在这里插入图片描述

代码语言:javascript复制
1.当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP 。

2.PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链

3.IPVS比对数据包请求的服务是否为集群服务,若是则将请求报文中的源MAC地址[CIP]修改为DIP的MAC地址,将目标MAC地址[VIP]修改RIP的MAC地址,然后将数据包发至POSTROUTING链[LVS]。 此时的源IP和目的IP均未修改,仅修改了源MAC地址为DIP的MAC地址,目标MAC地址为RIP的MAC地址

4.由于DS和RS在同一个网络中,所以是通过二层来传输。POSTROUTING链检查目标MAC地址为RIP的MAC地址[ARP广播],那么此时数据包将会发至Real Server。

5.RS发现请求报文的MAC地址是自己的MAC地址,就接收此报文。处理完成之后,将响应报文通过自己的lo接口传送给eth0网卡然后向外发出[ARP广播]。此时的源IP地址为VIP,目标IP为CIP.         

注意:
 如果没有给RS设置外网IP,RS将ARP广播查找CIP,内网没有就提交给网关,网关直接外网发送出去,会有可能提高网关压力

6.响应报文最终送达至客户端

特点: 多了一个Mac地址,作用是让真实服务器可以找到客户端,直接发送响应报文,并且整个过程的客户端IP(CIP)和负载均衡器的IP都没有改变,只是Mac地址变了,目的是让客户端知道,你发送请求的报文,和响应你报文的是一个人。

3.IP隧道(LVS-TUN)

在这里插入图片描述

代码语言:javascript复制
1.当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP 。

2.PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链

3.IPVS比对数据包请求的服务是否为集群服务,若是,在请求报文的首部再次封装一层IP报文,封装源IP为为DIP,目标IP为RIP。然后发至POSTROUTING链。 此时源IP为DIP,目标IP为RIP ④、POSTROUTING链根据最新封装的IP报文,将数据包发至RS(因为在外层封装多了一层IP首部,所以可以理解为此时通过隧道传输)。 此时源IP为DIP,目标IP为RIP

4.RS接收到报文后发现是自己的IP地址,就将报文接收下来,拆除掉最外层的IP后,会发现里面还有一层IP首部,而且目标是自己的lo接口VIP,那么此时RS开始处理此请求,处理完成之后,通过lo接口送给eth0网卡,然后向外传递。 此时的源IP地址为VIP,目标IP为CIP

5.响应报文最终送达至客户端

三种工作模式比较

在这里插入图片描述

ipvsadm命令选项

代码语言:javascript复制
  ipvsadm -A   创建虚拟服务器
  ipvsadm -E   修改虚拟服务器
  ipvsadm -D   删除虚拟服务器

  ipvsadm -t   设置集群地址(VIP,Virtual IP)
  ipvsadm -s   指定集群算法     ----> rr(轮询)、wrr(加权轮询)、lc(最少连接)、wlc(加权最少连接)、sh(ip_hash)

  ipvsadm -a   添加真实服务器
  ipvsadm -e   修改真实服务器
  ipvsadm -d   删除真实服务器

  ipvsadm -r   指定真实服务器的地址
  ipvsadm -w   为节点服务器设置权重,默认为1

  ipvsadm -C   清空所有
  ipvsadm -L   查看LVS规则表   ---> 一般跟n一起用,以数字形式输出(-Ln)

  ipvsadm -m   使用NAT模式
  ipvsadm -g   使用DR模式
  ipvsadm -i   使用TUN模式

LVS-NAT模式实战

NAT模式特性

代码语言:javascript复制
RS应该使用私有地址,RS的网关必须指向DIP(调度器IP)
DIP和RIP必须在同一个网段内,
请求和响应报文都需要经过Director Server,高负载场景中,Director Server易成为性能瓶颈
支持端口映射
RS可以使用任意操作系统
缺陷:对Director Server压力会比较大,请求和响应都需经过director server

环境介绍

客户端:192.168.4.132 (4网段充当外网)

负载均衡服务器: ens37:192.168.4.133 (4网段充当外网) ens33:192.168.2.130(test3)

后端服务器1:192.168.2.128(localhost)网关:192.168.2.130

后端服务器2:192.168.2.129(test2)网关:192.168.2.130

内核版本:3.10.0-862.el7.x86_64

系统版本:CentOS 7.5

注意:

 1.两台后端服务器必须配置网关地址,且网关地址都要指定为调度服务器的内网地址。(即:192.168.2.130)

2.LVS无论NAT及DR模式,均要求LVS server(调度器)和Real server在同一个网段内,NAT需要把LVS server(调度器)当作各个Real server的默认网关,

一、基础环境配置

1.两台后端服务器128/129分别安装Nginx

代码语言:javascript复制
[root@localhost ~]# wget http://nginx.org/download/nginx-1.16.1.tar.gz

[root@localhost ~]# yum -y install gcc pcre-devel openssl-devel

[root@localhost ~]# useradd -s /sbin/nologin nginx   //创建禁止登陆解释器的用户(为了安全)

[root@localhost ~]# id nginx
uid=1001(nginx) gid=1001(nginx) 组=1001(nginx)

[root@localhost ~]# tar -xf nginx-1.16.1.tar.gz

[root@localhost ~]# cd nginx-1.16.1

[root@localhost nginx-1.16.1]# ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module

         --prefix=/usr/local/nginx                   //指定安装路径
         --user=nginx                               //指定用户
         --group=nginx                              //指定组
         --with-http_ssl_module                    //安装ssl模块,开启其中的SSL加密功能(需要什么模块就安装什么模块)
  ......
  ......
  nginx modules path: "/usr/local/nginx/modules"
  nginx configuration prefix: "/usr/local/nginx/conf"
  nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
  nginx pid file: "/usr/local/nginx/logs/nginx.pid"
  nginx error log file: "/usr/local/nginx/logs/error.log"
  nginx http access log file: "/usr/local/nginx/logs/access.log"
  nginx http client request body temporary files: "client_body_temp"
  nginx http proxy temporary files: "proxy_temp"
  nginx http fastcgi temporary files: "fastcgi_temp"
  nginx http uwsgi temporary files: "uwsgi_temp"
  nginx http scgi temporary files: "scgi_temp"

[root@localhost nginx-1.16.1]#  make && make install    //编译并且安装
......
    '/usr/local/nginx/conf/scgi_params.default'
test -f '/usr/local/nginx/conf/nginx.conf' 
    || cp conf/nginx.conf '/usr/local/nginx/conf/nginx.conf'
cp conf/nginx.conf '/usr/local/nginx/conf/nginx.conf.default'
test -d '/usr/local/nginx/logs' 
    || mkdir -p '/usr/local/nginx/logs'
test -d '/usr/local/nginx/logs' 
    || mkdir -p '/usr/local/nginx/logs'
test -d '/usr/local/nginx/html' 
    || cp -R html '/usr/local/nginx'
test -d '/usr/local/nginx/logs' 
    || mkdir -p '/usr/local/nginx/logs'
make[1]: 离开目录“/root/nginx-1.16.1”

[root@localhost ~]# /usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.16.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module

[root@test2 ~]# /usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.16.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module

2.创建测试页面

代码语言:javascript复制
[root@localhost ~]# echo "I am 192.168.2.128" > /usr/local/nginx/html/index.html

[root@test2 ~]# echo "I am 192.168.2.129" > /usr/local/nginx/html/index.html

3.启动Nginx

代码语言:javascript复制
[root@localhost nginx-1.16.1]# /usr/local/nginx/sbin/nginx 

[root@localhost nginx-1.16.1]# netstat -antulp | grep :80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      6079/nginx: master

或者[root@localhost nginx-1.16.1]# netstat -antulp | grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      6079/nginx: master

3.关闭防火墙与selinux

 两台后端服务器都需要操作。

代码语言:javascript复制
[root@test2 ~]# systmctl stop firewalld

[root@test2 ~]# setenforce 0

[root@test2 ~]# getenforce
Disabled

[root@test2 ~]# vim /etc/sysconfig/selinux     //永久关闭selinux
SELINUX=disabled

二、部署LVS-NAT模式调度器

1.确认调度器的路由转发功能是否开启(必须开启)

代码语言:javascript复制
[root@test3 ~]# cat /proc/sys/net/ipv4/ip_forward
1

若没有开启,则:
[root@test3 ~]# echo 1 > /proc/sys/net/ipv4/ip_forward

[root@test3 ~]# echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf  
     ////修改配置文件,设置路由转发永久规则

2.调度器再添加一张网卡

代码语言:javascript复制
[root@test3 ~]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.2.130  netmask 255.255.255.0  broadcast 192.168.2.255
        inet6 fe80::1e8a:7d4a:beb0:fd6c  prefixlen 64  scopeid 0x20<link>
        inet6 fe80::2c27:a02c:731a:2219  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:53:71:a2  txqueuelen 1000  (Ethernet)
        RX packets 66342  bytes 15217275 (14.5 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 13974  bytes 1254299 (1.1 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 48  bytes 3781 (3.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 48  bytes 3781 (3.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 

在这里插入图片描述

代码语言:javascript复制
[root@test3 ~]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.2.130  netmask 255.255.255.0  broadcast 192.168.2.255
        inet6 fe80::1e8a:7d4a:beb0:fd6c  prefixlen 64  scopeid 0x20<link>
        inet6 fe80::2c27:a02c:731a:2219  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:53:71:a2  txqueuelen 1000  (Ethernet)
        RX packets 66416  bytes 15223172 (14.5 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 13996  bytes 1256226 (1.1 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens37: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.4.133  netmask 255.255.255.0  broadcast 192.168.4.255
        inet6 fe80::7e5e:fea8:ba8e:ee18  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:53:71:ac  txqueuelen 1000  (Ethernet)
        RX packets 5018  bytes 385651 (376.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2524  bytes 269779 (263.4 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 48  bytes 3781 (3.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 48  bytes 3781 (3.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

3.两台后端服务器配置网关地址

代码语言:javascript复制
[root@localhost ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens37
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO="static"
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=ens37
UUID=2d899e46-1b9d-40d5-9fed-8a88cb181d79
DEVICE=ens37
ONBOOT=yes
IPADDR="192.168.2.128"
PREFIX="24"
GATEWAY="192.168.2.130"     //网关地址配置为调度器的内网地址
DNS1="8.8.8.8"

[root@test2 ~]#  cat /etc/sysconfig/network-scripts/ifcfg-ens33
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO="static"
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=ens33
UUID=2d899e46-1b9d-40d5-9fed-8a88cb181d65
DEVICE=ens33
ONBOOT=yes
IPADDR="192.168.2.129"
PREFIX="24"
GATEWAY="192.168.2.130"    //网关地址配置为调度器的内网地址
DNS1="8.8.8.8"

[root@localhost ~]# systemctl restart network

[root@test2 ~]# systemctl restart network

以下步骤在调度器上操作      ||

4.创建集群调度服务器

代码语言:javascript复制
[root@test3 ~]# yum -y install ipvsadm

[root@test3 ~]# ipvsadm -A -t 192.168.4.133:80 -s wrr  ////创建虚拟集群服务器并设置调度算法为加权轮询wrr

5.添加真实服务器组

代码语言:javascript复制
[root@test3 ~]# ipvsadm -a -t 192.168.4.133:80 -r 192.168.2.128 -w 1 -m

[root@test3 ~]# ipvsadm -a -t 192.168.4.133:80 -r 192.168.2.129 -w 2 -m

4.查看规则列表,并保存规则

代码语言:javascript复制
[root@test3 ~]# 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.4.133:80 wrr
  -> 192.168.2.128:80             Masq    1      0          0
  -> 192.168.2.129:80             Masq    2      0          0

[root@test3 ~]# ipvsadm-save -n > /etc/sysconfig/ipvsadm-config

三、客户端上测试

在这里插入图片描述

在这里插入图片描述  可以看到每当我们执行一次curl命令(相当于刷新一次网页),调度器都会根据权重值轮询到不同的后端真实服务器。

当我们依次停掉后端两台服务器后,调度器会顶替工作吗?

1.停掉后端129服务器nginx服务

代码语言:javascript复制
[root@test2 ~]# /usr/local/nginx/sbin/nginx -s stop

2.使用客户端访问,查看轮询结果

在这里插入图片描述 3.再停掉后端128服务器nginx服务器

代码语言:javascript复制
[root@localhost ~]# /usr/local/nginx/sbin/nginx -s stop

4.再使用客户端访问,查看轮询结果

在这里插入图片描述  可以看到负载均衡调度服务器并不会顶替工作。

0 人点赞