现代网络负载平衡和代理介绍

一、网络负载均衡和代理是什么?

现代对网络铜元均衡和代理的定义涵盖了计算的各个方面,不单是网络。操作系统使用负载均衡在不同物理处理器上调度任务,K8S这样的容器编排引擎通过负载均衡在计算机集群上调度任务,网络负载均衡器在可用的后端之间调度网络任务。本文接下来只讨论网络负载均衡。

图1:网络负载均衡架构图

图1是网络负载均衡的高层架构图。若干客户端正在访问若干后端服务,它们中间是一个负载均衡器;从高层看,负载均衡器完成以下功能:

服务发现:系统中的哪些后端是可用的?它们的地址是多少(例如,负责均衡器如何和它们通信)?健康检查:当前哪些后端是健康的,可以接收请求?负载均衡:应该用什么算法将请求平衡地转发到健康的后端服务?

负载均衡使用得当可以给分布式系统带来很多好处:

命名抽象(Namingabstraction):客户端可以通过预设的机制访问LB,域名解析工作交给LB,这样每个客户端就无需知道每个后端(服务发现)。预设的机制包括内置库、众所周知的DNS/IP/port,接下来会详细讨论容错:通过健康检查和多种算法,LB可以将请求有效地路由到负载过高的后端。这意味着运维人员可以从容地修复异常的后端,而不用慌张成本和性能收益:分布式系统的网络很少是同构的(homogeneous)。系统很可能跨多个网络zone(可用区)和region(相隔较远的地理区域,这两者都是云计算术语,有严格定义,想进一步了解请自行搜索)。在每个zone内部,网络的利用率相对较低;zone之间,利用率经常达到上限。(这里利用率的衡量公式:网卡带宽/路由器之间带宽)。智能负载均衡可以最大限度地将请求流量保持在zone内部,既提高了性能(延迟降低),又减少了整体的系统成本(减少跨zone带宽及光纤成本)

1.1、负载均衡器vs代理

在业内讨论网络负载均衡器的时候,负载均衡器(loadbalancer)和代理(proxy)两个术语经常(大体上)无差别混用。本文中也将沿用这种惯例,认为二者整体上是对等的。(严格地从学术角度来说,不是所有代理都是负载均衡器,但绝大部分代理的核心功能都包括负载均衡。)

有人可能会说,当负载均衡内嵌到客户端库作为库的一部分的时候,负载均衡器并不是一个代理。对此我的意见是,这种区分只是给本已令人困惑的主题又增加了不必要的复杂性。本文后面会讨论负载均衡器拓扑的类型,其中将把嵌入式负载均衡器拓扑作为一种特殊的代理;应用通过内嵌的库进行代理转发,这个库提供的抽象和一个位于应用进程之外的负载均衡器是一样的。

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特殊场景:

两个gRPC/HTTP2客户端想连接到后端,因此它们通过L4LB建立连接L4LB为每个(从客户端)进来的连接建立一个出去的(到后端)的连接,因此最终由两个进来的连接和两个出去的连接客户端A的连接每分钟发送1个请求,而客户端B的连接每秒发送50个请求

在以上场景中,选中的处理客户端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流量考虑如下子层级:

可选的TLS(TransportLayerSecurity)层。网络领域的人们还在争论TLS到底属于OSI的哪一层。本文出于讨论目的将假设它属于L7物理HTTP协议(HTTP/1或者HTTP/2)逻辑HTTP协议(headers,bodydata,trailers)消息协议(gRPC,REST等等)

一个复杂的L7LB可能会提供与以上全部子层级相关的特性,而另一个L7LB可能会认为其中只有一部分才属于7层的功能,因此只提供这个子集的功能。也就是说,如果要比较负载均衡器的特性(features),L7的范围比L4的复杂的多。(当然,这里我们只涉及了HTTP;Redis、Kafka、MongoDB等等都是L7LB应用层协议的例子,它们都受益于7层负载均衡。)

2、负载均衡器特性

本节将简要总结负载均衡器提供的高层特性(highlevelfeatures)。但并不是所有负载均衡器都提供这里的所有特性。

2.1、服务发现

服务发现是负载均衡器判断它有哪些可用后端的过程。用到的方式差异很大,这里给出几个例子:

静态配置文件DNSZookeeper,Etcd,Consul等待Envoy的通用数据平面API(universaldataplaneAPI)

2.2、健康检查

健康检查是负载均衡器判断它的后端是否可以接收请求的过程。大致分为两类:

主动:LB定时向后端发送ping消息(例如,向/healthcheck发送HTTP请求),以此测量后端健康状态被动:LB从数据流中检测健康状态。例如,L4LB可能会认为如果一个后端有三次连接错误,它就是不健康的;L7LB可能会认为如果后端有503错误码就是不健康的

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所示的中间代理拓扑应该是大家最熟悉的负载均衡方式。这一类型的方案包括:

硬件设备:Cisco、Juniper、F5等公司的产品云软件解决方案:Amazon的ALB和NLB,Google的CloudLoadBalancer纯软件方案:HAProxy、NGINX、Envoy等等

中间代理模式的优点是简单,用户一般只需要通过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代理实现负载均衡

客户端内嵌库拓扑的一个变种是sidecar拓扑,如图7所示。近年来这种拓扑非常流行,被称为服务网格(servicemesh)。sidecar代理模式背后的思想是:通过将流量导到其他进程的方式,牺牲一点(延迟)性能,实现客户端内嵌库模式的所有好处,而无任何语言绑定(languagelock-in)。写作本文时,最流行的sidecar代理有Envoy、NGINX、HAProxy、Linkerd。想了解sidercar模式负载均衡的更多信息,请查看我之前介绍Envoy的博客,以及servicemesh数据平面vs控制平面的博客。

3.5、不同拓扑类型的优缺点比较

中间代理拓扑是最简单的负载均衡方式,缺点是单点故障、扩展性问题、以及黑盒运维边缘代理拓扑和中间代理拓扑类似,但一些场景必须得用这种模式客户端内嵌库拓扑提供了最优的性能和扩展性,但必须为每种语言实现相应的库,并且升级非常痛苦sidecar代理拓扑性能不如客户端内嵌库好,但没有后者的那些缺点

总体上我认为在service-to-service通信中,sidecar(servicemesh)正在逐渐取代其他所有拓扑类型。另外,在流量进入servicemesh的地方,总是需要一个边缘代理拓扑负载均衡器。

4、当前L4负载均衡最新技术

4.1、L4负载均衡还有用吗?

我们前面已经解释了为什么L7负载均衡器对现代协议如此重要,接下来详细讨论L7LB的功能特性。这是否意味着L4LB没用了?不!虽然我认为在service-to-service通信中L7负载均衡最终会完全取代L4负载均衡,但L4负载均衡在边缘仍然是非常有用的,因为几乎所有的现代大型分布式架构都是在因特网流量接入处使用L4/L7两级负载均衡架构。在边缘L7负载均衡器之前部署L4负载均衡器的原因:

L7LB承担的更多工作是复杂的分析、变换、以及应用流量路由,他们处理原始流量的能力(按每秒处理的包数和字节数衡量)比经过优化的L4负载均衡器要差。这使得L4LB更适合处理特定类型的攻击,例如SYN泛洪、通用包(genericpacket)泛洪攻击等L7LB部署的更多更频繁,bug也比L4LB多。在L7之前加一层L4LB,可以在调整L7部署的时候,对其做健康检查和流量排除(drain),这比(单纯使用)现代L4LB要简单的多,后者通常使用BGP和ECMP(后面会介绍)。最后,因为L7功能更复杂,它们的bug也会比L4多,在前面有一层L4LB能及时将有问题的L7LB拉出

接下来的几节我将介绍中间/边缘代理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:

连接跟踪(connectiontracking):跟踪所有活动的TCP连接的状态的过程。这包括握手是否成功、是否收到FIN包、连接已经空闲多久、为当前连接选择哪个后端等NAT:利用连接跟踪的数据,在包经过负载均衡器时修改包的IP/port信息

使用连接跟踪和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类型,会在某些场景中替换前者使用呢?几点原因:

性能和资源消耗:passthroughLB不会terminateTCP连接,因此无需缓存任何TCP连接窗口。每个连接的状态数据非常小,通常可以通过哈希表直接查询。因此,passthroughLB的性能(packetspersecond,PPS,每秒处理的包数)要比terminatingLB高很多允许后端进行自主拥塞控制:TCP拥塞控制是一种避免发送太快导致超过网络带宽或缓冲区的机制。passthroughLB不terminateTCP连接,因此它不参与拥塞控制。这使得后端可以根据应用的类型自主决定采用哪种拥塞控制算法。而且,这种方式还使得验证拥塞控制的改动更容易(例如,最近的BBRrollout)是Directserverreturn(DSR)和L4LB集群化的基础:很多高级的L4负载均衡技术基于passthroughLB,例如DSR和一致性哈希集群(下面讨论)

4.4、DSR(直接服务器返回)

图10:L4Directserverreturn(DSR,直接服务器返回)

DSRLB如图10所示,它基于passthroughLB,对后者的改进之处是:只允许进来的流量/请求(ingress/request)经过LB,而出去的流量/响应(egress/response)直接从服务器返回到客户端。

设计DSR的主要原因是:在一些场景中,响应的流量要远远大于请求的流量(例如典型的HTTPrequest/response模式)。假设请求占10%的流量,响应占90%,使用DSR技术,只需1/10的带宽就可以满足系统需求。因为早期的负载均衡器非常昂贵,这种类型的优化可以极大地节省成本,还提高了负载均衡器的可靠性(流量越低肯定越好)。DSR在如下方面扩展了passthroughLB:

LB仍然做一部分连接跟踪工作。因为响应不再经过LB,LB无法知道TCP连接的完整状态。但是,它仍然可以根据客户端的包以及多种类型的idletimeout,(strongly)推测连接的状态与NAT不同,负载均衡器通常使用GRE(GenericRoutingEncapsulation)将IP包封装发送到后端。后端收到后进行解封装,就可以拿到原始的IP包,里面有客户端的IP和port信息。因此后端可以直接将应答包发给客户端,而需要经过LBDSR非常的重要一点是:后端参与负载均衡过程。后端需要配置正确的GRE隧道,视网络设置的底层细节,GRE可能还需要自己的连接跟踪和NAT

注意,不管是在passthrough还是DSR设计中,负载均衡器和后端之间的连接跟踪、NAT、GRE等等都有多种设置方式。但不幸的是这个话题超出本文的讨论范围。

4.5、通过HApair实现容错

图11:通过HApair和连接跟踪实现L4容错

到目前为止,我们讨论的都是单个L4LB。passthrough和DSR都需要LB保存一些连接跟踪的状态。假如LB挂了呢?如果一个LB实例挂了,那所有经过这个LB的连接都会受到影响。视应用的不同,这可能会对应用性能产生很大影响。

历史上,L4负载均衡器是从一些厂商(Cisco、Juniper、F5等等)购买的硬件设备,这些设备非常昂贵,可以处理大量的网络流量。为了避免单个负载均衡器挂掉导致应用不可用,负载均衡器通常都是以高可用对(highavailabilitypair)方式部署的,如图11所示。典型的HA负载均衡器设置包括:

一对HA边缘路由器提供若干虚拟IP(virtualIP,VIP),并通过BGP(BorderGatewayProtocol)协议通告VIP。主(primary)边缘路由器的BGP权重比备(backup)边缘路由器的高,在正常情况下处理所有流量。(BGP是一个非常复杂的协议,出于本文讨论目的,可以认为BGP就是一种对外宣告哪个网络设备配置了哪个IP的机制,每个设备有一个表示处理网络流量的权重)类似地,primaryL4LB向边缘路由器宣告它的权重比backupLB大,因此正常情况下它处理所有流量primaryLB交叉连接(cross-connected)到backupLB,共享所有的连接跟踪状态。因此,假如primaryLB挂了,backupLB可以马上接管所有活动连接两个边缘路由器和两个负载均衡器都是交叉连接的。这意味着,如果一个边缘路由器或一个负载均衡器挂了,或者由于某种原因之前声明的BGP权重收回了(withdraw),backup马上可以接受所有流量

以上就是许多大流量因特网应用今天仍然在使用的架构。然而,以上架构也有很大的不足:

VIP需要做容量规划,并正确sharding给两个负载均衡器实例。如果一个VIP(的连接数?)增长超过了单个HApair的容量,那这个VIP需要分裂成多个VIP资源利用率很低,平稳状态下50%的容量是空闲的。考虑到有史以来硬件负载均衡器都是非常昂贵的,这意味着大量的资金没有得到有效利用现代分布式系统设计追求比active/backup更高的容错(faulttolerance)性。例如,理想情况下,一个系统有多个实例同时挂掉仍能继续运行。而HALBpair的主备实例同时挂掉时,服务就彻底挂了供应商提供的专有大型硬件设备非常昂贵,导致用户被锁死到厂商(vendorlock-in,即买了某个厂商的设备后,后期只能继续买这个厂商的设备或服务)。通常期望的是,可以用基于通用服务器的、水平扩展性良好的纯软件方案代替这些硬件设备

4.6、基于集群和一致性哈希的容错和可扩展

图12:基于负载均衡器集群和一致性哈希实现L4容错和可扩展

前一节介绍了通过HApair实现L4LB的容错,以及这种设计固有的问题。从2000s初期到中期,大型因特性基础设施(公司)开始设计和部署全新的大规模并行L4负载均衡系统,如图12所示。这些系统的设计目标是:

避免HApair设计的所有缺点从厂商的商业硬件方案,迁移到基于标准服务器和网卡的通用软件方案

这种L4LB设计最合适的名称是基于集群化和一致性哈希的容错和可扩展(faulttoleranceandscalingviaclusteringanddistributedconsistenthashing)。它的工作原理如下:

N个边缘路由器以相同的BGP权重通告所有AnycastVIP。通过ECMP(Equal-cost,Multi-pathrouting)保证每个flow的所有包都会到达同一个边缘路由器。一个flow通常是4元组:源IP/port和目的IP/port。简单来说,ECMP是一种通过一致性哈希将包分发到一组权重相同的网络设备的方式。虽然边缘路由器通常并不关心每个包要发往哪里,但一般都是希望同一flow的所有包都以相同路径经过各个设备,因为这可以避免乱序代理的性能下降N个L4LB以相同的BGP权重向所有的边缘路由器通告所有的VIP。仍然使用ECMP,边缘路由器会为相同flow的包选择相同的LB每个L4LB实例会做部分连接跟踪(partialconnectiontracking)工作,然后使用一致性哈希为每个flow选择一个后端。通过GRE封装将包从LB发送到后端然后使用DSR将应答包从后端直接发送到边缘路由器,最后到客户端L4LB用到的一致性哈希算法是一个热门的研究领域。需要在平衡负载、最小化延迟、最小化后端变化带来的扰动、最小化内存开销等等之间做取舍。关于这一话题的完整讨论超出了本篇的范围

我们来看看以上的设计是如何避免了HApair的不足的:

边缘路由器和负载均衡器实例可以按需添加。每一层都用到了ECMP,当新实例加入的时候,ECMP能最大程度地减少受影响的flow数量在预留足够的突发量(burstmargin)和容错的前提下,系统的资源利用率想达到多高就可以到多高边缘路由器和负载均衡器都可以基于通用硬件搭建,成本只是传统硬件LB的很小一部分(后面有更多信息)

很多读者可能会问:“为什么不让边缘路由器通过ECMP直接和后端通信?为什么我们还需要这一层负载均衡器?”这样做主要的原因是防止DoS攻击,以及方便后端的运维。没有这一层负载均衡,后端就得直接参与BGP,当对后端集群进行滚动(rolling)部署时受影响程度会大很多。

所有现代L4负载均衡系统都在朝着这种设计(或其变种)演进。其中最有名的两个分别是来自Google的Maglev和来自Amazon的NetworkLoadBalancer。基于这种设计的开源方案目前还没有,但据我所知,有一家公司准备在2018年开源他们的产品。对此我非常兴奋,因为现代L4LB是网络领域的开源产品中仍然缺失的重要部分。

5、当前L7负载均衡最新技术

过去几年见证了L7负载均衡器/代理的一阵复兴(resurgence)浪潮,这紧跟了分布式系统微服务化的发展趋势。本质上,当使用更加频繁时,天生有损的网络(inherentlyfaultynetwork)越来越难以有效运维。而且,自动扩缩容(autoscaling)、容器调度器等技术的崛起,意味着通过静态文件配置静态IP的方式早就过时了。系统不仅使用网络更加频繁,而且使用的方式越来越动态,需要负载均衡器提供更多的功能。本节我将简要现代L7负载均衡器发展最快的几个领域。

5.1、协议支持

现代L7负载均衡器正在显示地添加对更多协议的支持。负载均衡器对应用层协议了解的越多,就可以处理越多更复杂的事情,包括观测输出、高级负载均衡和路由等等。例如,在写作本文时,Envoy显式支持如下L7协议的解析和路由:HTTP/1、HTTP/2、gRPC、Redis、MongoDB、DynamoDB。未来可能会添加包括MySQL和Kafka在内的更多协议。

5.2、动态配置

如前面描述的,分布式系统越来越动态的本质需要同时在两方面做投资:动态和响应式控制。Istio即使这种系统的一个例子。

5.3、高级负载均衡

L7LB现在一般都内置高级负载均衡的特性,例如超时、重试、限速、熔断(circuitbreaking)、流量镜像(shadowing)、缓存、基于内容的路由等等。

5.4、可观测性

前面在介绍通用负载均衡器特性时讲到,随着部署的系统越来越动态,debug也越来越困难。健壮的协议特定的(protocolspecific)可观测性输出可能是现代L7LB提供的最重要的特性。输出数值统计、分布式跟踪以及自定义日志等功能现在几乎是L7负载均衡解决方案的标配。

5.5、可扩展性

现代L7LB的用户常常希望能够轻松地对它扩展以添加自定义的功能。这可以通过编写可插拔的过滤器,然后加载到负载均衡器实现。一些负载均衡器还支持脚本编程,典型的是通过Lua。

5.6、容错

前面介绍了很多L4LB容错的内容。那么L7LB的容错又如何呢?通常来说,我们认为L7LB是易消耗的和无状态的(expendableandstateless)。基于通用软件使得L7负载均衡器可以轻松地实现水平扩展。进一步,L7LB的处理过程和状态跟踪比L4LB要复杂的多。搭建一个L7LBHApair技术上是可行的,但代价相当大。

总体来说,不管是在L4还是在L7负载均衡领域,业界都在从HApair架构转向基于一致性哈希的水平可扩展架构。

5.7、其他

L7负载均衡器正在以蹒跚的步伐演进。以Envoy作为例子,读者可以查看它的架构综述。

6、全局负载均衡和集中式控制平面

图13:全局负载均衡

未来的负载均衡会越来越将单个负载均衡器看做通用设备(commoditydevice)。我个人觉得,真正的创新和商业机会全部都会在控制平面。图13展示了全局负载均衡系统的一个例子。这个例子包含如下内容:

每个sidecar同时和位于三个zone的后端通信图上可以看到,90%的流量到了zoneC,而zoneA和B各只有5%sidecar和后端都定期向全局负载均衡器汇报状态。这使得全局负载均衡器可以基于延迟、代价、负载、当前失败率等参数做出决策全局负载均衡器定期配置每个sidecar的路由信息

全局负载均衡器可以做越来越复杂、单个负载均衡器无法完成的事情。例如:

自动检测和路由zonalfailure(可用区级别失败)应用全局安全和路由策略使用机器学习和神经网络技术检测和缓解流量异常,包括DDoS攻击提供集中式UI和可视化平台,方便工程师理解和运维整个分布式系统

为了实现分布式负载均衡,作为数据平面使用的负载均衡器必须具有支持复杂的动态配置的能力。这一话题的更多信息请参考我之前关于Envoy’suniversaldataplaneAPI以及servicemeshdataplanevs.controlplane的博客。

7、从硬件进化到软件

到目前为止本文只是对硬件和软件做了简要对比,大部分内容是在介绍传统L4LBHApair的时候。那么,这一领域当前的趋势是什么呢?

上面这条tweet是一个很幽默的夸张,但确实很好地总结了当前的趋势:

从历史来说,路由器和负载均衡器都是厂商提供的专有硬件,非常昂贵。越来越多的专有L3/L4网络设备被通用服务器、通用网卡,以及基于IPVS、DPDK、fd.io等框架的特殊软件方案代替。一台现代数据中心的价格$5K以下机器,基于DPDK开发用户态应用程序在Linux发小包,很容易就可以用满80Gbps的网卡带宽。同时,人们正在将价格便宜的、ECMP路由聚合带宽能力惊人的基础路由器/交换机ASICs组装成通用路由器。NGINX、HAProxy以及Envoy这样的功能复杂的L7负载均衡器正在快速迭代,并不断侵蚀原来硬件厂商例如F5的地盘。因此,L7LB也在非常有气势地朝着通用软件方案迈进。同时,工业界几个主要云厂商主导的以IaaS、CaaS、FaaA为整体演进的趋势,意味着将来只有很少一部分工程师需要了解物理的网络是如何工作的(这些就是“黑科技”)。

8、总结及展望

最后总结,本文的核心内容:

负载均衡器是现代分布式系统的一个核心组件有两类通用负载均衡器:L4和L7L4和L7负载均衡器在现代架构中都有很重要的应用场景L4负载均衡器正在朝着基于分布式一致性哈希的水平可扩展架构演进L7负载均衡器近年来投入的资源非常大,源于最近火热的动态微服务架构的需求全局负载均衡,以及控制平面和数据平面的分离是负载均衡的未来,将来大部分创新和商业机会也都会在这两个方向对于网络解决方案,工业界正在大步迈向通用开源硬件和软件解决方案。我相信传统负载均衡厂商,比如F5,会是最先被开源软件和云厂商干掉的。传统路由器/交换机厂商,例如Arista/Cumulus等,由于on-premisesdeployments(本地部署)的需求,我认为存在时间会更长一些,但最终会被云厂商和他们的自研物理网络干掉

总体来说,我认为这是计算机网络的一个令人振奋的时代。朝着开源和软件方向的转变使得大部分系统的迭代速度有了数量级(ordersofmagnitude)的提高。而且,随着分布系统基于serverless设计,继续朝着动态化的目标长征,底层网络和负载均衡系统的复杂性也会成比例的(commensurately)增加。

原文:Introduction to modern network load balancing and proxying