一、 络负载均衡和代理是什么?
图1: 络负载均衡架构图
图1是 络负载均衡的高层架构图。若干客户端正在访问若干后端服务,它们中间是一个负载均衡器;从高层看,负载均衡器完成以下功能:
负载均衡使用得当可以给分布式系统带来很多好处:
1.1、负载均衡器vs代理
1.2、L4(会话/连接层)负载均衡
现在,当业内讨论负载均衡的时候,所有解决方案通常分为两类:L4和L7。这两者分别对应OSI模型的4层和7层。不过我认为使用这个术语相当不幸,在后面讨论L7负载均衡的时候会明显看到这一点。OSI模型是一个很差的对负载均衡解决方案复杂度的近似,这些解决方案包含4层协议,例如TCP和UDP,但经常又包括一些OSI其他协议层的内容。比如,如果一个L4TCPLB同时支持TLStermination,那它现在是不是一个L7LB?
图2:TCPL4termination负载均衡
图2是传统的L4TCP负载均衡器。这种情况下,客户端建立一个TCP连接到LB。LB终止(terminate)这个连接(例如,立即应答SYN包),选择一个后端,然后建立一个新的TCP连接到后端(例如,发送一个新的SYN包)。不要太在意图中的细节,我们在后面章节会专门讨论L4负载均衡。
本节想说明的是,典型情况下,L4负载均衡器只工作在
L4TCP/UDPconnection/session。因此,LB在双向来回转发字节,保证属于同一session的字节永远落到同一后端。L4LB不感知其转发字节所属应用的任何细节。这些字节可能是HTTP、Redis、MongoDB,或者任何其他应用层协议。
1.3、L7(应用层)负载均衡
L4负载均衡很简单,应用范围也很广。那么,相比于L7(应用层)负载均衡,L4有哪些缺点?设想如下L4特殊场景:
在以上场景中,选中的处理客户端A请求的后端比选中的处理客户端B请求的后端,负载要相差3000x倍。这个问题非常严重,与负载均衡的目的背道而驰。而且要注意,对任何multiplexing,kept-alive(多路复用,保活)协议,都存在这个问题。(Multiplexing表示通过单个L4连接发送并发应用的请求,kept-alive表示当没有主动的请求时也不要关闭连接)。出于性能考虑(创建连接的开销是非常大的,尤其是连接是使用TLS加密的时候),所有现代协议都在演进以支持multiplexing和kept-alive,因此L4LB的阻抗不匹配问题(impedancemismatch)随时间越来越彰显。这个问题被L7LB解决了。
图3:HTTP/2L7负载均衡
图3是一个L7HTTP/2负载均衡器。这种情况下,客户端与LB只建立一个HTTP/2TCP连接。LB接下来和两个后端建立连接。当客户端向LB发送两个HTTP/2流(streams)时,stream1会被发送到后端1,而stream2会被发送到后端2。因此,即使不同客户端的请求数量差异巨大,这些请求也可以被高效地、平衡地分发到后端。这就是L7LB对现代协议如此重要的原因。L7负载均衡具备检测应用层流量的能力,这带来了大量额外的好处,我们后面会更详细看到。
1.4、L7负载均衡和OSI7层模型
前面讨论L4负载均衡时我说过,使用OSI模型描述负载均衡特性是有问题的。原因是,对于L7,至少按照OSI模型的描述,它本身就包括了负载均衡抽象的多个独立层级(discretelayers),例如,对于HTTP流量考虑如下子层级:
一个复杂的L7LB可能会提供与以上全部子层级相关的特性,而另一个L7LB可能会认为其中只有一部分才属于7层的功能,因此只提供这个子集的功能。也就是说,如果要比较负载均衡器的特性(features),L7的范围比L4的复杂的多。(当然,这里我们只涉及了HTTP;Redis、Kafka、MongoDB等等都是L7LB应用层协议的例子,它们都受益于7层负载均衡。)
2、负载均衡器特性
本节将简要总结负载均衡器提供的高层特性(highlevelfeatures)。但并不是所有负载均衡器都提供这里的所有特性。
2.1、服务发现
服务发现是负载均衡器判断它有哪些可用后端的过程。用到的方式差异很大,这里给出几个例子:
2.2、健康检查
健康检查是负载均衡器判断它的后端是否可以接收请求的过程。大致分为两类:
2.3、负载均衡
LB必须保证负载是均衡的。给定一组健康的后端,如何选择哪个后端来处理一个连接或一个请求呢?负载均衡算法是一个相对活跃的研究领域,从简单的随机选择、RoundRobin,到更复杂的考虑各种延迟和后端负载状态的算法。最流行的负载均衡算法之一是幂次最少请求(powerof2leastrequest)负载均衡。
2.4、StickySession(黏性会话)
对于一些特定应用,保证属于同一session的请求落到同一后端非常重要。这可能需要考虑缓存、结构复杂的临时状态等问题。session的定义也并不相同,可能会包括HTTPcookies、客户端连接特性(properties),或者其他一些属性。一些L7LB支持stickysession。但这里我要说明的是,sessionstickiness本质上是脆弱的(处理/保持session的后端会挂掉),因此如果设计的系统依赖这个特性,那要额外小心。
2.5、TLSTermination
关于TLS以及它在边缘服务(edgeserving)和安全的service-to-service通信中扮演的角色,值得单独写一篇文章,因此这里不详细展开。许多L7LB会做大量的TLS处理工作,包括termination、证书验证和绑定(verificationandpinning)、使用SNI提供证书服务等等。
2.6、可观测性(observability)
我在技术分享中喜欢说:“可观测性、可观测性、可观测性。” 络在本质上是不可靠的,LB通常需要导出统计、跟踪和日志信息,以帮助运维判断出了什么问题并修复它。负载均衡器输出的可观测性数据差异很大。最高级的负载均衡器提供丰富的输出,包括数值统计、分布式跟踪以及自定义日志。需要指出的是,丰富的可观测数据并不是没有代价的,负载均衡器需要做一些额外的工作才能产生这些数据。但是,这些数据带来的收益要远远大于为产生它们而增加的那点性能损失。
2.7、安全和DoS防御
至少(尤其)在边缘部署拓扑(下面会看到)情况下,负载均衡器通常需要实现很多安全特性,包括限速、鉴权和DoS防御(例如,给IP地址打标签及分配标识符、tarpitting等等)。
2.8、配置和控制平面
负载均衡器要可以配置。在大型部署场景中,这可能是一项很大的工作。一般地,将配置负载均衡器的系统称为“控制平面”,其实现方式各异。
2.9、其他更多特性
本节对负载均衡器提供的功能做了一个非常浅的介绍。更多内容我们会在下面讨论L7LB的时候看到。
3、负载均衡器的拓扑类型
前面我们已经覆盖了负载均衡器的高层概览,L4和L7负载均衡器的区别,以及负载均衡器的功能特性等内容,接下来介绍它的分布式部署拓扑(下面介绍的每种拓扑都适用于L4和L7负载均衡器)。
3.1、中间代理(middleproxy)
图4:中间代理负载均衡拓扑
图4所示的中间代理拓扑应该是大家最熟悉的负载均衡方式。这一类型的方案包括:
中间代理模式的优点是简单,用户一般只需要通过DNS连接到LB,其他的事情就不用关心了。缺点是,这种模式下负载均衡器(即使已经做了集群)是单点的(singlepointoffailure),而且横向扩展有瓶颈。
中间代理很多情况下都是一个黑盒子,给运维带来很多困难。例如发生故障的时候,很难判断问题是出在客户端,中间代理,还是后端。
3.2、边缘代理(edgeproxy)
图5:边缘代理负载均衡拓扑
图5所示的边缘代理拓扑其实只是中间代理拓扑的一个变种,这种情况下负载均衡器是可以从因特 直接访问的。这种场景下,负载均衡器通常还要提供额外的“API 关”功能,例如TLStermination、限速、鉴权,以及复杂的流量路由等等。
中间代理拓扑的优缺点对边缘代理也是适用的。需要说明的是,对于面向因特 的分布式系统,部署边缘代理通常是无法避免的。客户端一般通过DNS访问系统,而它使用什么 络库,服务方是控制不了的(下文会看到的客户端内嵌库或sidecar代理拓扑在此不适用)。另外,从安全的角度考虑,所有来自因特 的流量都通过唯一的 关进入系统是比较好的。
3.3、客户端内嵌库(embeddedclientlibrary)
图6:客户端内嵌库实现负载均衡
为了解决中间代理拓扑固有的单点和扩展问题,出现了一些更复杂的方案,例如将负载均衡器已函数库的形式内嵌到客户端,如图6所示。这些库支持的特性差异非常大,最知名的库包括Finagle、Eureka/Ribbon/Hystrix、gRPC(大致基于一个Google内部系统Stubby)。
这种拓扑的最大优点是:将LB的全部功能下放到每个客户端,从而完全避免了单点和扩展问题。缺点是:必须为公司使用的每种语言实现相应的库。分布式架构正在变得越来越“polyglot”(multilingual,多语言化)。在这种情况下,为多种语言实现一个复杂的 络库是非常难的(prohibitive)。最后,对大型服务架构,进行客户端升级也是一件极其痛苦的事情,最终很可能导致生产集群中同时运行多个版本的客户端,增加运维和认知(cognitive)负担。
虽然如此,但是那些在能够限制语言数量增加(proliferation)而且能够解决客户端升级痛苦的公司,这种拓扑还是取得了成功的。
3.4、sidecar代理
图7:sidecar代理实现负载均衡
3.5、不同拓扑类型的优缺点比较
总体上我认为在service-to-service通信中,sidecar(servicemesh)正在逐渐取代其他所有拓扑类型。另外,在流量进入servicemesh的地方,总是需要一个边缘代理拓扑负载均衡器。
4、当前L4负载均衡最新技术
4.1、L4负载均衡还有用吗?
我们前面已经解释了为什么L7负载均衡器对现代协议如此重要,接下来详细讨论L7LB的功能特性。这是否意味着L4LB没用了?不!虽然我认为在service-to-service通信中L7负载均衡最终会完全取代L4负载均衡,但L4负载均衡在边缘仍然是非常有用的,因为几乎所有的现代大型分布式架构都是在因特 流量接入处使用L4/L7两级负载均衡架构。在边缘L7负载均衡器之前部署L4负载均衡器的原因:
接下来的几节我将介绍中间/边缘代理L4LB的几种不同设计。这些设计通常不适用于客户端内嵌库和sidecar代理拓扑模式。
4.2、TCP/UDPtermination负载均衡
图8:TCPL4termination负载均衡
第一种现在仍在用的L4LB是terminationLB,如图8所示。这和我们最开始介绍L4负载均衡器时看到的图是一样的(图2)。这种模式中,会使用两个独立的TCP连接:一个用于客户端和负载均衡器之间,一个用于负载均衡器和后端之间。
L4负载均衡器仍然在用有两个原因:
- 他们实现相对简单
- 连接terminate的地方离客户端越近,客户端的性能(延迟)越好。特别地,如果在一个有丢包的 络(lossynetwork,例如蜂窝 )中将terminationLB部署的离客户端很近,重传可能就会更快的发生(retransmitsarelikelytohappenfasterpriortothedatabeingmovedtoreliablefibertransiten-routetoitsultimatelocation)。换句话说,这种负载均衡方式可能会用于入 点(POP,PointofPresence)的rawTCPconnectiontermination
4.3、TCP/UDPpassthrough负载均衡
图9:TCPpassthrough负载均衡
第二种L4负载均衡是passthrough,如图9所示。在这种类型中,TCP连接不会被负载均衡器terminate,而是在建立连接跟踪和 络地址转换(NAT)之后直接转发给选中的后端。我们首先来定义连接跟踪和NAT:
使用连接跟踪和NAT技术,负载均衡器可以将大部分rawTCP流量从客户端转发到后端。例如,我们假设客户端正在和负载均衡器1.2.3.4:80通信,选中的后端是10.0.0.2:9000。当客户端的TCP包到达负载均衡器时,负载均衡器会将包的目的IP/port(从1.2.3.4:80)换成10.0.0.2:9000,以及将源IP/port换成负载均衡器自己的IP/port。当应答包回来的时候,负载均衡器再做相反的转换。
为什么这种比terminatingLB更复杂的LB类型,会在某些场景中替换前者使用呢?几点原因:
4.4、DSR(直接服务器返回)
图10:L4Directserverreturn(DSR,直接服务器返回)
DSRLB如图10所示,它基于passthroughLB,对后者的改进之处是:只允许进来的流量/请求(ingress/request)经过LB,而出去的流量/响应(egress/response)直接从服务器返回到客户端。
设计DSR的主要原因是:在一些场景中,响应的流量要远远大于请求的流量(例如典型的HTTPrequest/response模式)。假设请求占10%的流量,响应占90%,使用DSR技术,只需1/10的带宽就可以满足系统需求。因为早期的负载均衡器非常昂贵,这种类型的优化可以极大地节省成本,还提高了负载均衡器的可靠性(流量越低肯定越好)。DSR在如下方面扩展了passthroughLB:
4.5、通过HApair实现容错
图11:通过HApair和连接跟踪实现L4容错
到目前为止,我们讨论的都是单个L4LB。passthrough和DSR都需要LB保存一些连接跟踪的状态。假如LB挂了呢?如果一个LB实例挂了,那所有经过这个LB的连接都会受到影响。视应用的不同,这可能会对应用性能产生很大影响。
历史上,L4负载均衡器是从一些厂商(Cisco、Juniper、F5等等)购买的硬件设备,这些设备非常昂贵,可以处理大量的 络流量。为了避免单个负载均衡器挂掉导致应用不可用,负载均衡器通常都是以高可用对(highavailabilitypair)方式部署的,如图11所示。典型的HA负载均衡器设置包括:
以上就是许多大流量因特 应用今天仍然在使用的架构。然而,以上架构也有很大的不足:
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!