[负载均衡]-LVS

Cluster

当一个单机系统的访问量迅速增长,系统开始承受不了巨大的访问量,性能开始下降。

为了提升性能,扩展系统,有两种方式:垂直扩展 Scale Up 和水平扩展 Scale Out。垂直扩展就是增强机器的各项指标各项性能;水平扩展就是增加机器,增加提高服务的设备。水平扩展的情况下就引入了请求调度分配的问题,即一个请求到来,要让这个请求打到哪台设备上进行处理。这就需要一个调度器来完成调度分配的任务,LVS 就是这样一个调度器

水平扩展下,为解决某个特定问题将多台计算机组合起来成为了单个系统,这样的系统就称为一个 集群。由于调度器的存在,用户请求访问集群时,实际上访问的是调度器的 IP 地址,然后再由调度器将请求分发给不同的后端实际服务器上。我们称调度器的 IP 为 VIP,虚拟 IP;后端实际服务器的 IP 称为 RIP,真实 IP

集群分类

根据集群的侧重点不同,可以分为 LB 负载均衡型,HA 高可用型,HPC 高性能型。其中高可用型主要是为了避免单点故障 SPOF 而设计的,其衡量标准为

A = M T B F / ( M T B F + M T T R ) A = MTBF/(MTBF + MTTR) A=MTBF/(MTBF+MTTR)

其中,MTBF 为 Mean Time Between Failure,平均无故障时间;MTTR 为 Mean Time To Restoration,平均恢复前时间,也就是平均故障时间。那么 A 表示的其实就是无故障时间占总时间的占比。对于具备调度器的集群来说,如果调度器只有一台机器,那么如果调度器宕机了,后端服务器也访问不了了,相当于整个集群也宕机了,所以对于调度器,需要配置高可用集群,来防止调度器的单点故障

LB Cluster 的实现

负载均衡的实现可以分为硬件负载均衡和软件负载均衡。硬件负载均衡需要使用具有负载均衡功能的设备,如很多大企业都在使用的 F5 交换机;由于硬件设备成本较高,可以选择软件负载均衡,相关的软件如 LVS,Nginx,haproxy,ats 等

基于工作的网络协议层次可分为传输层 L4 负载均衡以及应用层 L7 负载均衡

会话保持

在讨论负载均衡时经常会提到会话保持的问题:一个客户 IP 的请求第一次打到了一台服务器上,这台服务器上便产生了该客户 IP 的会话;但第二次相同 IP 的请求却有可能打到另一台服务器上,这样第二台服务器便需要产生本机上与该客户 IP 对应的会话,第一台服务器上对应的会话也派不上用场,相当于客户第一次请求的登录信息等都没了,会影响用户交互体验。对此可以引入会话保持的解决方案,确保一个客户 IP 的多个请求即使打到不同服务器上,服务器也能持有对应的会话。有三种实现方案:

Session Sticky

确保同一客户的访问会被调度器调度到同一后端服务器上。在调度器内部需要维护一个客户 IP 到后端服务器 IP 的映射表,当一个客户请求到来时根据其 IP 查找映射关系进行定向调度即可

这种方案的缺点在于,在使用 NAT 的情况下,多个客户 IP 经过 NAT 后调度器看到的是同一个 IP,即 NAT 路由器的 IP;这样就可能导致一个后端服务器被调度了太多客户 IP;还有就是需要调度器去 维护 IP 的映射表,需要一定的成本

还有一种实现是基于 cookie 进行调度转发而不是基于客户 IP

Session Replication

每台服务器会拥有全部的 session。每台服务器之间通过复制交换各自拥有的 session,这样不管客户请求打到了哪台服务器,都能拥有相应的 session

这种方案的问题也很明显,每台服务器都要保存所有的 session,session 数据非常 冗余,而且对于每台服务器都需要大量的存储资源

Session Server

第三种方案相比前面两种使用得更多,使用专门的 session 服务器来存储 session 数据,如 memcached,redis。

当服务器需要 session 数据时从 session server 取即可。那么 session server 也应该搭建高可用集群来防止 单点故障

HA Cluster 的实现

keepalived 是 解决 LVS 单点故障的一个很优秀的高可用方案

LVS

Linux Virtual Server,一种负载均衡调度器,已经集成在了 Linux 内核,发明者为 章文嵩。VS 负责调度,而 RS 负责真正地提供服务。

其工作原理为,VS 根据请求报文的目的 IP,目的协议 以及 目的端口 将其调度转发至某个 RS,其中需要使用调度算法来挑选转发的 RS

与 DNAT (目标地址转换) 相比,DNAT 用于从互联网中向企业内部发起的请求,公网的请求打到企业一个与公网连接的服务器,服务器再将公网 IP 的请求转发到企业内部私网中的服务器。它只能一对一地转发,不具备 调度 的功能

相关术语

  • VS,虚拟服务器,即 LVS。又称 DS ( D i r e c t o r   S e r v e r Director\ Server Director Server),Dispatcher,Load Balancer
  • RS,实际服务器,又称 upstream server (Nginx 中的说法),backend server (haproxy 中的说法)
  • CIP,客户 IP;VIP,VS 在外网中的 IP;DIP (Director IP),VS 在内网中的 IP;RIP,实际服务器的 IP

实现工具

  • ipvsadm:用户空间的命令行工具,LVS 的规则管理器,用于管理集群服务以及 RS,类似于 iptable。LVS 为内核级功能,而 ipvsadm 就是配置这个内核功能时所需的工具。ipvsadm 跟 LVS 的关系就类似于 iptable 跟 netfilter 的关系
  • ipvs:工作在内核空间 netfilter 的 INPUT 钩子上的框架,实现 LVS 的具体过程

LVS 集群类型

LVS-NAT

修改请求报文的目标 IP,相当于多目标的 DNAT。当 LVS 接受到请求报文时,将报文的 目的 IP 和 目的端口 改为要转发的 RS 的 IP 跟端口 然后进行转发。

RS 在发回响应报文时,目的 IP 跟端口为客户端的 IP 跟端口,源 IP 跟端口为 RS 自身的 IP 跟端口。那么响应报文必须按照请求报文的路径原路返回给客户端,中间经过 LVS。因为响应报文的源 IP 跟端口是 RS 的 IP 跟端口,但客户端在发出请求报文时目的 IP 跟端口是 LVS 的 IP 跟端口,它所期望收到的响应报文的源 IP 跟端口自然需要是 LVS 的 IP 跟端口,所以响应报文也必须经过 LVS,让 LVS 将源 IP 跟端口修改为它的信息。

那么,在客户端跟后端 RS 之间所有的报文都会经过 LVS,这使 LVS 的压力很大,易成为系统的性能瓶颈。

LVS 跟 RS 应在同一个子网内且使用私网 IP 通信,RS 网关为 LVS。当然它们之间引入路由器的话也不会对功能产生影响,但可能会延长链路的延迟以及减少带宽,所以一般没有必要使用路由器进行连接

[负载均衡]-LVS_第1张图片

LVS-DR

D i r e c t   R o u t i n g Direct\ Routing Direct Routing,直接路由,是 LVS 的默认模式,应用最广泛,LVS 通过 为请求报文重新封装一个 MAC 首部 进行转发。其中,源 MAC 是 DIP 所在的接口的 MAC,目的 MAC 是挑选出的 RS 的 RIP 所在接口的 MAC 地址;源 IP,源端口,目的 IP,目的端口 均保持不变。所以 DR 模式不支持端口映射,通信过程中 修改的只有 MAC 地址

那么,LVS 就必须知道 RS 的 MAC 地址。这可以通过 ARP 实现,不过要求 LVS 事先存储 RS 的 IP 地址。那么为了能通过 ARP 找到 RS 的 MAC 地址,VS 跟 RS 之间就只能通过交换机连接而不能通过路由器,RIP 跟 DIP 要在同一子网内

由于 数据包从 LVS 到 RS 时目的 IP 保持不变,那么所有 RS 就必须也拥有一个 IP 地址为 VIP,这就会导致 LVS 跟 RS 之间出现 IP 冲突,为了确保前端路由器将目标 IP 为 VIP 的请求报文都发往 Director,有如下几种方式:

  1. 在前端网关路由器上静态绑定 VIP 和 DIrector 的 MAC 地址。这种方式需要配置路由器,一般用户无法或难以介入;而且 VIP 对应的 MAC 地址被写死了,如果 LVS 设备更换了,相关的配置也需要更换,不够灵活
  2. 在 RS 上修改内核参数限制 ARP 通告及应答级别:/proc/sys/net/ipv4/conf/all/arp_ignore/proc/sys/net/ipv4/conf/all/arp_announce。使 RS 不去响应 ARP 询问,也不发出 ARP 通告,以此避免 IP 冲突。这种方式更常用

RS 发回响应报文时,由于客户端的请求报文的目的 IP 为 VIP,RS 自然需要使用 VIP 作为响应报文的源 IP。然后目的 IP 为 CIP

所以,所有请求报文都会经过 LVS 再转发到 RS,但 所有响应报文都不会经过 LVS,直接从 RS 发往网关路由器然后发往 Client,这大大减少了 LVS 的压力

RS 的 RIP 可以使用私网地址也可以是公网地址,因为整个通信过程不涉及 RIP。但一般使用私网地址

LVS-TUN (tunnel)

不修改请求报文的 IP 首部 (源 IP 是 CIP,目的 IP 是 VIP),而 在原 IP 数据包之外再封装一个 IP 首部,源 IP 是 DIP,目的 IP 是 RIP,然后将报文发给所挑选出的目的 RS;RS 响应时直接响应给客户端 (源 IP 是 VIP,目的 IP 是 CIP)。不支持端口映射

DIP,VIP,RIP 都应该是公网地址,DIP,RIP 也可以是私网地址;RS 跟 VS 可以跨网段跨机房跨地域,此时就需要使用公网地址进行通信;而 RS 跟 VS 可以跨地域意味着 RS 之间也可以跨地域,实现异地容灾;RS 的网关一般不能指向 DIP,确保响应报文不经过 VS

RS 应支持隧道功能,因为 RS 接收到的报文具有两个 IP 首部,它应具备正常识别并去除 VS 额外增加的头部的功能

LVS-FULLNAT

该模式内核默认不支持。同时修改请求报文的源 IP 和目标 IP,源 IP 从 CIP 改为 DIP,目的 IP 从 VIP 改为 RIP

那么由于 VS 发往 RS 的报文的源 IP 改为了 DIP,所以 RS 的响应报文需要经过 VS 再由 VS 转发给 Client;FULLNAT 模式支持端口映射

ipvs scheduler

调度可以分为 静态 跟 动态 两类,其区别在于调度时是否考虑各个 RS 当前的负载状态

静态调度

静态调度即仅根据算法本身进行调度:

  • RR:round-robin,轮询

  • WRR:weighted RR,加权轮询,对每个 RS 给予一个权值

  • SH:Source Hashing,实现 session sticky,通过将源 IP 地址进行 hash 计算,把来自于同一 IP 地址的请求始终发往第一次被挑中的 RS,从而实现会话绑定

  • DH:Destination Hashing:目标地址哈希,将发往同一个目标地址的请求始终转发到第一次挑中的 RS,典型场景是正向代理缓存场景中的负载均衡,如宽带运营商。

    例如,假设用户的请求会发到一个正向代理服务器上,然后代理服务器再根据用户请求的服务提供商的 IP 地址去服务提供商的服务器获取资源,再返回给用户。而在代理服务器跟服务提供商的服务器之间还设置了缓存服务器,缓存服务器相对代理服务器来说就是 RS,例如 服务提供商 A 的资源缓存在缓存服务器 1 上,服务提供商 B 的资源缓存在缓存服务器 2 上…,那么为了提高缓存的命中率,提高用户访问资源的速度,代理服务器应该将请求服务提供商 A 的请求调度到缓存服务器 1 上,以此类推。根据请求的目标地址选择要调度的缓存服务器,从而提高缓存命中率

动态调度

动态算法主要根据每个 RS 当前的 负载状态 以及调度算法进行调度,负载较小的 RS 将被调度

  • LCLeast Connections,负载计算公式为

    O v e r h e a d = a c t i v e c o n n s ∗ 256 + i n a c t i v e c o n n s Overhead = activeconns * 256 + inactiveconns Overhead=activeconns256+inactiveconns

    inactiveconns 非活动连接指的是连接已经建立但没有数据通信活动,这种计算方式相当于认为一个活动连接所占用的资源相当于 256 个非活动连接所占用的资源。这种计算方法只考虑服务器连接数的问题,没有考虑服务器本身性能,有可能一台服务器虽然连接数很多,但其实它的性能很好,可以接受更多的连接

  • WLCWeighted LC,默认的调度算法,相比 LC 而言多了权重的计算,考虑了服务器本身的性能。

    O v e r h e a d = ( a c t i v e c o n n s ∗ 256 + i n a c t i v e c o n n s ) / w e i g h t Overhead = (activeconns * 256 + inactiveconns) / weight Overhead=(activeconns256+inactiveconns)/weight

    这种方法的问题是:当集群刚开始运行,每台服务器的连接数都为 0,那么 Overhead 也都为 0,此时随机选择一台服务器进行调度,就有可能选到性能没有那么好的服务器作为第一台被调度的服务器,而更好的做法应该是能让性能最好的服务器被选中

  • SEDShortest Expectation Delay,负载计算公式为

    O v e r h e a d = ( a c t i v e c o n n s + 1 ) ∗ 256 / w e i g h t Overhead = (activeconns + 1) * 256 / weight Overhead=(activeconns+1)256/weight

    这样在集群刚启动时就能做到权重高的优先调度。这种方法问题在于,如果一台权重高的服务器与其它服务器相差太大,那么一开始的一系列请求就都会被调度到该服务器上,而其它服务器一个请求都接受不到

  • NQNever Queue,第一轮的请求均匀分配,后续再使用 SED 进行调度,这样就不会导致一开始所有请求都调度到某台权重很高的服务器上,其它服务器也能接受到请求

  • LBLCLocality-Based LC,动态的 DH 算法,根据负载状态实现正向代理,根据缓存服务器的负载状态调度到不同缓存服务器上

  • LBLCRLBLC with Replication,带复制功能的 LBLC,将缓存内容从负载重的服务器复制到负载轻的服务器,正向代理调度时就可以调度到其它具有相同缓存数据的服务器上

ipvsadm命令

  1. 增加,修改集群服务:ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]]
  2. 删除集群服务:ipvsadm -D -t|u|f service-address
    t 表示调度 TCP 协议的端口,u 表示 UDP 协议;f 表示防火墙
    service-address 表示服务地址,形式为 VIP:PORT
    所以,LVS 的调度是基于 协议,IP 跟 端口的,发送到 VS 的请求有一个条件与 LVS 定义的规则不同都不会被调度给后端的 RS 而是直接给 VS 自身处理
    -s 用于指定调度算法
  3. 增加,修改集群上的 RS:ipvsadm -a|e -t|u|f service-address -r server-address [-g|i|m] [-w weight]
  4. 删除 RS:ipvsadm -d -t|u|f service-address -r server-address
    -r 后面指定 RS 的地址: RIP[:PORT],port 可以省略,如果省略则不做端口映射
    -w,指定权重
  5. LVS 类型:-g,表示 gateway,DR 模型,默认;-i,tun 模型;-m,表示 masquerade,NAT 模型
  6. ipvsadm -Ln 查看定义的集群服务
    选项:-n,以数字形式输出地址和端口号;–exact,显示精确值;-c,显示当前连接;–stats,显示统计信息;–rate,输出速率的信息
  7. ipvsadm -C 清空定义的所有内容
  8. ipvsadm -Z [-t|u|f service-address] 清空计数器,如各个服务器上连接的统计个数
  9. ipvs 的规则存在 /proc/net/ip_vs 中;ipvs 的连接存在 /proc/net/ip_vs_conn 中
  10. 规则保存:ipvsadm -Sn > 文件
    重载:ipvsadm -R

你可能感兴趣的:(负载均衡,lvs,服务器)