资讯详情

国产最强负载均衡器LVS(理论+实战)

长文预警,先看目录

image

不要因为技术而技术!技术服务于业务,必须是当前的技术不能满足业务的发展,才能产生新的技术来解决业务问题。为什么会产生负载平衡?这是因为业务需求。

中国人口14亿,移动网络用户7亿,固网用户4亿,保守估计独立用户为28.四亿,他们有消费能力。

假设你有一个idea,我一无是处,只有钱。当你的天使投资者给你第一笔投资时,你会做什么?毫无疑问,营销是谁?营销这2.假设2%的人(480万人)看到你的广告,20%的人(96W)有兴趣下载你的APP,光APP当然,96广告可以赚很多钱W人们使用你的服务,这必然会导致高并发,这表明高并发是真的!

高并发产生的日志有一个字段表示来源网站(例如,用户是通过百度点还是微博点),可以分析渠道流量,然后通过与订单表相关联,可以分析渠道流量转化率。这有什么用?这直接表明你下次应该去哪个渠道营销!这表明高并发性是有益的!

负载平衡是由高并发产生的!

LVS(Linux Virtual Server) Linux虚拟服务器

简写,意思是,它是一个虚拟的服务器集群系统。该项目于1998年5月由张文松博士建立,是中国最早的自由软件项目之一。

  • https://baike.baidu.com/item/LVS/17738

参考之前写的网络基础,很多基础内容需要先看这篇文章

学习手册

查看更多细节

  • LVS手册man ipvsadm
  • keepalived手册man keepalived
  • keepalived配置文件手册man keepalived.conf

由于负载均衡,后端必须不止一台服务器。如何调度?LVS提供以下调度算法

  1. rr(Round Robin): 轮询
  2. wrr(weight):加权
  3. sh(source hashing): 源地址散列
  4. dh(Destination Hashing): 散列目标地址

  1. lc(Least-Connection): 最少连接
  2. wlc(Weighted Least-Connection): 至少加权连接
  3. sed(Shortest Expected Delay): 最短期望延迟
  4. nq(never queue): 永不排队
  5. LBLC(Locality-Based Least Connection): 至少基于本地连接
  6. LBLCR(Locality-Based Least Connections withReplication): 至少基于本地带复制功能的连接

名词定义:

  • CIP: Client IP 客户端的IP
  • VIP: Virtual IP LVS实例IP,一般暴露在公共网络中的地址;直接向外部用户请求,作为用户请求的目标IP地址
  • DIP: Director IP,主要用于与内部主机通信IP地址
  • RIP: Real IP 真实的后端服务器IP
  • DS: Director Server 指前端负载均衡器节点
  • RS: Real Server 后端真实工作服务器

NAT(Net Address Transition)即网络地址转换模式

图片上传失败…(image-2138b5-1615470382448)]

图中的黄色数据包从客户端到LVS集群的,绿色数据包是从LVS集群到客户端的一般过程如下:

  1. 数据包CIP->VIP,如果您直接将数据包塞RS,RS会丢弃,所以LVS修改目标地址VIP为RIP(D-NAT),此时数据包变成了CIP->RIP发给RS
  2. RS处理完成后,发送数据包RIP->CIP,这个数据包直接塞到客户端,客户端不会收到,因为客户端是发送的VIP是的,但回来就变成了RIP如果给客户端,客户端也会丢弃,所以LVS修改源地址为VIP(S-NAT),此时数据包变成了VIP->CIP发给客户端

整个过程保证一个原则:如何回去,按照Socket四元组IP反向发回去!比如客户端和LVS而且是CIP->VIP,回去就是VIP->CIP,LVS和RS两者也是如此

  • RS使用私有地址,DIP和RIP必须在同一网段内,(因为最后给CIP将数据包扔回LVS)
  • 要求和响应报文都需要通过Director Server,在高负载场景中,LVS很容易成为性能瓶颈
  • 支持端口映射
  • RS可使用任何操作系统

  • 对LVS压力会更大,需要通过请求和响应LVS(一般来说,流量是不对称/倾斜的,请求小,响应大)
  • 不断转换网络地址,消耗计算能力

既然响应报文太大,有没有办法直接返回响应报文?CIP,不经过LVS?当然,这是DR模式

DR(Direct Router)即直接路由模式

数据包的流向仍然是黄色的,绿色是回来的;RS有一个见,对外隐藏VIP

  1. 数据包CIP->VIP发送给LVS
  2. LVS通过修改MAC地址直接把数据包扔给RS,由于RS也有VIP,所以接收数据包
  3. RS处理完成后,直接密封数据包VIP->CIP,回到客户端,不经过LVS

  • 确保前端路由以目标地址为准VIP统一发送报文LVS,而不是RS(因为RS的VIP是隐藏的)
  • RS如果使用,可以使用私可以使用公网地址,这个时候可以通过互联网RIP直接访问
  • RS跟LVS必须在同一个物理网络中(因为MAC地址欺骗/ARP欺骗就
  • 所有请求报文经理LVS,但必须响应报文LVS
  • 不支持地址转换或端口映射(因为这是/2层处理不涉及IP)
  • RS它可以是大多数常见的操作系统
  • RS不允许指向网关DIP(因为我们不允许他经过LVS)
  • RS上的lo接口配置VIP的IP地址(对外隐藏,对内可见)

参考【ARP】Linux内核参数之arp_ignore和arp_announce 内核参数(kernel parameter)定义了收到arp请求时的动作(arp_ignore)与主机通电时arp发起的动作(arp_announce),比如针对eth修改0网卡,文件路径如下:/proc/sys/net/ipv4/conf/eth0/arp_ignore

/proc/以下文件均为进程虚拟文件,Linux一切都是文件,内核运行时的变量arp_ignore映射成文件/proc/sys/net/ipv4/conf/eth0/arp_ignore,因此,修改文件是立即生效的!

定义接收到RP请求时的响应级别:

  • 0:本地配置的有相应地址,就给予响应;(只要有ARP请求,就把本机所有网卡的MAC地址都给出去,主动型,不管你是否需要都给你)
  • 1:在请求的目标(MAC)地址配置请求到达的接口上的时候,给予响应;(有ARP请求,并且ARP请求的IP是本机某块网卡的IP时,才响应,被动型,问哪个IP就给哪个IP,不全给);

定义将自己地址向外通告时的通告级别:

  • 0:允许使用上的IP地址作为arp请求的源IP,通常就是使用数据包的源IP;(发ARP请求时,本机上多块网卡的IP都可以作为源IP,可混用)
  • 1:使用不属于该发送网卡子网的本地地址作为发送arp请求的源IP地址;(发ARP请求时,尽量用自己的IP作为请求的源IP,注意尽量二字,万不得已还是可以借用别人的IP用一下,发生的情况可能是路由表的限制)
  • 2:忽略IP数据包的源IP地址,选择该发送网卡上的本地地址作为arp请求的源IP地址;(发ARP请求时,哪块网卡发的ARP请求,就用该网卡的IP地址,不能混用)

那么作为RS的VIP要达到对内隐藏,对外可见,需要怎么配置?

arp_ignore无疑应该配置成1,要隐藏,不问就不说

arp_announce应该配置成2,要隐藏,其他网卡发ARP请求时,肯定不能借用我的VIP,你这一借,不就把VIP暴露了么

RS和LVS必须在同一机房中,很显然,ARP欺骗条件要求LVS和DS要在同一个物理局域网内,那有没有不需要再同一个局域网内的?但是是有,那就是隧道模式

TUN(Tunnel)即隧道模式

所谓隧道,最简单的理解就是数据包套数据包,一个数据包背上另一个数据包,到了目的地再放下来,整个流程如下:

  1. 数据包CIP->VIP发送给LVS
  2. LVS在源数据包外面套一层形成[DIP->RIP[CIP->VIP]],通过DIP->RIP将数据包发送给RS(DIP和RIP可以是不同的物理网络)
  3. RS拆包接收到CIP->VIP的请求处理完成之后直接封数据包VIP->CIP,返回给客户端,不经过LVS

  • RIP、VIP、DIP全是公网地址
  • RS的网关不会也不可能指向DIP
  • 所有的请求报文经由LVS,但响应报文必须不能进过LVS
  • 不支持端口映射
  • RS的系统必须支持隧道

其实企业中最常用的是DR实现方式,而NAT配置上比较简单和方便,TUN模式则是综合了DR和NAT的优点

本次实验以最常用的DR模式作为目标

以下实验均基于CentOS7.5,docker 19.03.6,以下命令均在宿主机执行

为什么要在docker中实验?因为LVS涉及到的角色太多,需要很多台不同IP的服务器,而在docker中可以创建很多独立的容器来模拟,足够轻巧和方便,降低整个实验的成本!

  • 宿主机CentOS开启ipvs内核模块ipvs虽然是Linux内核模块,但是默认是没有开启的,需要手动开启,因为docker和宿主机是公用内核,所以宿主机开启了docker里面自动开启,否则docker内即使装上ipvsadm也会报错:Can’t initialize ipvs: Protocol not available开启ipvs模块modprobe ip_vs modprobe ip_vs_wrr查看是否开启成功:lsmod | grep ip_vs,回显如下说明开启成功ip_vs_wrr 12697 0 ip_vs 145458 2 ip_vs_wrr…

  • 准备docker容器node1到node4分别为LVS、RS1、RS2、LVS备用(为啥需要个备用?后面用来做主备高可用),注意要加上参数–privileged运行,因为我们的目的是把docker当虚拟机用,要在里面安装服务,并不是有现成的服务已经在虚拟机里面了,详情参考:搜索关键词docker使用centos的systemctldocker run -dit --name node1 --privileged centos /usr/sbin/init docker run -dit --name node2 --privileged centos /usr/sbin/initdocker run -dit --name node3 --privileged centos /usr/sbin/initdocker run -dit --name node4 --privileged centos /usr/sbin/init

  • 准备IPnode1IP=docker inspect -f '{ {range .NetworkSettings.Networks}}{ {.IPAddress}}{ {end}}' node1 node2IP=docker inspect -f '{ {range .NetworkSettings.Networks}}{ {.IPAddress}}{ {end}}' node2 node3IP=docker inspect -f '{ {range .NetworkSettings.Networks}}{ {.IPAddress}}{ {end}}' node3 node4IP=docker inspect -f '{ {range .NetworkSettings.Networks}}{ {.IPAddress}}{ {end}}' node4 nodeVIP=‘172.17.0.100’

  • 给所有容器添加IP环境变量,后面的命令就可以直接用上面提到的几个IP了,免得每次都要去定义docker exec -it node1 bash -c “echo node1IP=KaTeX parse error: Expected 'EOF', got '&' at position 22: …P >> ~/.bashrc &̲& echo node2IP=node2IP >> ~/.bashrc && echo node3IP=KaTeX parse error: Expected 'EOF', got '&' at position 22: …P >> ~/.bashrc &̲& echo node4IP=node4IP >> ~/.bashrc && echo nodeVIP=KaTeX parse error: Expected 'EOF', got '&' at position 23: … >> ~/.bashrc" &̲& \ docker exec…node1IP >> ~/.bashrc && echo node2IP=KaTeX parse error: Expected 'EOF', got '&' at position 22: …P >> ~/.bashrc &̲& echo node3IP=node3IP >> ~/.bashrc && echo node4IP=KaTeX parse error: Expected 'EOF', got '&' at position 22: …P >> ~/.bashrc &̲& echo nodeVIP=nodeVIP >> ~/.bashrc” && docker exec -it node3 bash -c “echo node1IP=KaTeX parse error: Expected 'EOF', got '&' at position 22: …P >> ~/.bashrc &̲& echo node2IP=node2IP >> ~/.bashrc && echo node3IP=KaTeX parse error: Expected 'EOF', got '&' at position 22: …P >> ~/.bashrc &̲& echo node4IP=node4IP >> ~/.bashrc && echo nodeVIP=KaTeX parse error: Expected 'EOF', got '&' at position 23: … >> ~/.bashrc" &̲& \ docker exec…node1IP >> ~/.bashrc && echo node2IP=KaTeX parse error: Expected 'EOF', got '&' at position 22: …P >> ~/.bashrc &̲& echo node3IP=node3IP >> ~/.bashrc && echo node4IP=KaTeX parse error: Expected 'EOF', got '&' at position 22: …P >> ~/.bashrc &̲& echo nodeVIP=nodeVIP >> ~/.bashrc”

  • 测试结束后销毁所有容器docker stop node1 node2 node3 node4 && docker rm node1 node2 node3 node4

参考

分别登陆node1(LVS)、node2(RS1)、node3(RS2)

进入node2和node3配置

# 安装ifconfig nginx
yum install -y  net-tools nginx 

# 修改内核参数(实现对内可见、对外隐藏)
echo 1 > /proc/sys/net/ipv4/conf/eth0/arp_ignore
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/eth0/arp_announce
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce

# 添加VIP(对内可见、对外隐藏) 注意掩码必须是255.255.255.255
# 因为VIP是绑在环回网卡上(回环网卡离内核更近,数据包优先匹配)的,如果掩码不是4个255,则数据包在返回的时候匹配上了回环网卡,匹配不上真实网关,数据包发送不出去
ifconfig lo:vip ${nodeVIP} netmask 255.255.255.255

# 启动nginx
systemctl start nginx

# 给不同RS添加各自IP用于区分 
echo `ifconfig $name | grep "inet.*broadcast.*" | cut -d' ' -f10` > /usr/share/nginx/html/index.html

进入node1配置

# 安装ifconfig ipvsadm命令
yum install -y net-tools ipvsadm

# 添加VIP 这里掩码是16还是24根据具体情况而定
ifconfig eth0:vip ${nodeVIP}/16

# 清空所有规则
ipvsadm -C

# -A添加流量进入规则
ipvsadm -A  -t ${nodeVIP}:80  -s rr

# 在上面的进入规则下面添加流量出去(分发给RS)的规则
ipvsadm -a  -t ${nodeVIP}:80  -r  ${node2IP} -g -w 1
ipvsadm -a  -t ${nodeVIP}:80  -r  ${node3IP} -g -w 1

查看规则ipvsadm -ln,输出如下,说明成功负载到了node2和node3

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.17.0.100:80 rr
  -> 172.17.0.4:80                Route   1      0          0         
  -> 172.17.0.5:80                Route   1      0          0      

在宿主机下面执行循环访问

while true; do curl ${nodeVIP}; sleep 1 ;done
  • node1中:netstat -natp看不到很多socket链接 ipvsadm -lnc查看偷窥小本本TCP 01:42 FIN_WAIT 172.17.0.1:55416 172.17.0.100:80 172.17.0.4:80 FIN_WAIT: 连接过,偷窥了所有的包 SYN_RECV: 基本上lvs都记录了,证明lvs没事,一定是后边网络层出问题
  • node2和node3中:netstat -natp看到很多socket链接

LVS可以实现高并发,但是也存在的问题,LVS挂了就不能响应请求,要实现高可用【HA(High Available)高可用】怎么办,很简单,一变多,一变多分为

  • 主备
  • 主从
  • 主主

此处肯定是用主备,主备健康监测又分为两种

  • 每个备轮询主是否挂了,挂了就顶上去,备发给主,主没挂就返回,数据包一去一回总共跑两次
  • 主主动给所有备广播健康包,当备一段时间没收到主发的健康包就认为主挂了,自己顶上去,这样数据包只用跑一次

很显然是第二种好,Keepalived也是采用的这种方式

进入node2和node3配置(和DR模式配置一样)

通过查看手册man keepalived.conf了解更多配置文件的细节

进入node1和node4配置

# 安装ifconfig ipvsadm keepalived命令
yum install -y net-tools ipvsadm keepalived

# 备份keepalived原配置文件
cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak

# 修改配置文件,本例而言如下,不需要的东西去掉
# 主机备机设置的选项不同,主要是state和priority不同
cat > /etc/keepalived/keepalived.conf <<EOF
vrrp_instance VI_1 {
    state MASTER  # 如果是备机的话使用 BACKUP
    interface eth0
    virtual_router_id 51
    priority 100  # 优先级,主机优先级肯定要比备机高
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        ${nodeVIP}/16 dev eth0 label eth0:vip # VIP的设置
    }
}

# 虚拟主机设置
virtual_server ${nodeVIP} 80 {
    delay_loop 6
    lb_algo rr  # 轮询
    lb_kind DR  # DR模式
    # 持久化时间超时,避免三次握手打散在不同的RS上,根据具体情况设置,本例实验所以设置为0
    persistence_timeout 0 
    protocol TCP

    # RS1
    real_server ${node2IP} 80 {
        weight 1
        HTTP_GET {
            url {
              path /
              status_code 200  # 健康检查状态码为200
            }
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }

    # RS2
    real_server ${node3IP} 80 {
        weight 1
        HTTP_GET {
            url {
              path /
              status_code 200
            }
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }
}
EOF

# 启动keepalived
systemctl start keepalived

查看VIP是否生效ifconfig,查看ipvs规则是否生效ipvsadm -ln

正常的情况应该是node1 VIP生效,node4没有生效,等node1挂掉之后自己顶上去!

  • node1挂掉,检测node4是否生效(node4生效)
  1. node1上执行systemctl stop keepalived
  2. 在宿主机查看服务是否还能访问curl ${nodeVIP},发现能访问,说明node4顶上去了!
  3. 在node4上执行ifocnfig查看vip网卡是否生效,发现生效!
  4. 启动node1:systemctl start keepalived
  5. 继续访问,发现能访问,并且在node4上的vip网卡自动失效!
  • node2挂掉,检测请求是否还继续打在node2上
  1. 在node2上执行systemctl stop nginx
  2. 在宿主机查看服务是否还能访问curl ${nodeVIP},发现能访问,且没有访问到node2上
  3. 启动node2:systemctl start nginx
  4. 继续访问多次,发现访问到了node2

keepalived在主机挂掉之后备机自动顶上去,在主机恢复之后备机自动下线,并且还具有对后端服务器的健康检查

标签: 国产1928403740连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台