首页 > 自动化运维 > > 现代负载均衡和代理简介
公司使用 AWS,最近线上出现一次故障和 AWS Network Load Balancer 相关,借机了解 Network Load Balancer 的工作原理,于是发现了 Envoy 的这篇文章,让我对负载均衡有了一个系统的了解,在征得作者同意后,翻译了一下(省略了一些认为不太重要的章节),希望也对你有所帮助。
原文地址:Introduction to modern network load balancing and proxying什么是负载均衡和代理?
Wikipedia 是这么定义的:
在计算世界中,负载均衡通过使用多个计算资源来提高负载的分布。计算资源包括计算机,计算集群,网络链接,CPU,或者硬盘等。负载均衡的目标是优化资源的使用,最大化吞吐量,最小化响应延时,同时避免任何单一资源超载。负载均衡通过冗余,使用多个而不是一个组件提高可靠和可用性。负载均衡器通常包括专门的软件或者硬件,比如一个多层的交换机或者一个 DNS 服务进程。
以上定义适用所有各种计算,不仅仅是网络。操作系统使用负载均衡在物理处理器之间调度任务;容器编排系统(如 Kubernetes)使用负载均衡在计算集群之间调度任务;网络负载均衡器使用负载均衡在不同的后端实例调度网络任务。这篇文章接下来的部分只讨论网络负载均衡。

Figure 1: Network load balancing overview
Figure 1 显示了一个高度概括的网络负载均衡。Client 从 Backend 请求资源。负载均衡器位于 Client 和 Backend 之间,主要执行以下关键任务:
在分布式系统中,正确使用负载均衡有以下好处:
Load Balancer v.s. Proxy 负载均衡器和 Proxy 对比
当我们说到网络 Load Balancer 的时候,Load Balancer 和 Proxy 这两个词基本上是可以互换的。这篇文章里面也把这两个术语认为是相同的。(准确的讲,不是所有的 Proxy 都是 Load Balancer,但是大部分的 Proxy 都把负载均衡作为基本功能)。
L4(connection/session) load balancing 四层(连接/会话)负载均衡
当今业界讨论负载均衡,解决方案通常分为两类:L4 和 L7。这两个分类是指 OSI 模型下的 4 层和 7 层。其实,用 OSI 模型来归类负载均衡器不是特别合适。因为 OSI 模型很难覆盖 Load Balancer 的复杂性,一个 L4 的 Load Balancer 多少还会包含一些其他 OSI 层的协议。比如,一个 4 层的 TCP Load Balancer 也支持 TLS 连接,那它又是一个 L7 的 Load Balancer 了吗?

Figure 2: TCP L4 termination load balancing
Figure 2 展示一个传统的 4 层 TCP Load Balancer。一个 Client 向 Load Balancer 发起了一个 TCP 连接。Load Balancer 直接响应了 Client 的 SYN 请求,然后选择一个 Backend 并发起了 TCP 连接。 这个图的细节不重要,接下来的章节会详细说明。
这一部分的关键是说一个 4 层的 Load Balancer 通常只是操作 4 层 TCP/UDP 的连接或者会话。因此,4 层的 Load Balancer 基本上是在倒腾字节流,而且还要保证同一个会话的字节流流向同一个 Backend,而且 Load Balancer 完全不会感知到字节流的内容,这些字节流可能是HTTP、Redis、MongoDB 或者其他任何应用层协议。
L7(application) load balancing 七层负载均衡
4 层 Load Balancer 简单且应用广泛。那 4 层 Load Balancer 有什么缺点会让我们再搞一个 7 层 Load Balancer 出来呢?我们以一个 L4 的具体例子来说明:
上面的场景中,被 L4 Load Balancer 选中处理 Client B 的请求的 Backend 的负载会是被选中处理 Client A 的请求的 Backend 的负载的 3000 倍!这就是一个大问题了,完全违背了负载均衡的意义。值得注意的是,对于任何多路复用,保持连接的协议这些问题都是存在的。(多路复用即用一条连接发送所有的应用请求,保持连接即即使当前没有活跃的请求也不会断开连接)出于效率的原因,当前所有协议几乎都是多路复用且保持连接的(创建连接的成本是很高的,尤其是当连接还需要 TLS 加密的时候)。所以,L4 的这方面的劣势就明显了,而 L7 可以解决这个问题。

Figure 3: HTTP/2 L7 termination load balancing
Figure 3 展示了一个 L7 HTTP/2 的 Load Balancer。Client 向 Load Balancer 发起一条 HTTP/2 TCP 连接。Load Balancer 后续建立了两个 Backend 连接。当 Client 向 Load Balancer 发送了两条 HTTP/2 Stream 的时候,Stream 1 被转发给 Backend 1,Stream 2 被转发给 Backend 2。这样,即使有不同负载的多路复用的 Client 也可以被有效的均衡到 Backend 上。这就是为什么 L7 的负载均衡对于现代协议这么重要。(由于需要探测应用层的流量,L7 的负载均衡也放弃了大量的好处,以下会详述。)
L7 Load balancing and the OSI model
就像在 L4 Load balancing 章节中提到的一样,用 OSI 模型描述 Load Balancing 的特性也是问题多多。理由就是 L7 本身就包含了 Load Balancing 抽象的多个分立的层。比如,对于 HTTP 流量,考虑以下子层:
一个复杂的 L7 Load Balancer 可能会提供和任何一个子层都有关的特性。有些 Load Balancer 可能只会提供一个子集的特性。简而言之,从特性对比的视角来看,L7 Load Balancer 的场景分类是远比 L4 Load Balancer 复杂的多。
Load balancer features
这一节我们简要总结一下 Load Balancer 提供的高层特性。当然,并不是所有的 Load Balancer 都支持以下的特性。
服务发现是指 Load Balancer 如何发现可用的后端,方法有很多,比如:静态配置文件、DNS、Zookeeper/Etcd/Consul 等
Load Balancer 的拓扑类型
我们已经大体上说了什么是 Load Balancer,L4 & L7 Load Balancer 的区别,并总结了 Load Balancer 的特性。接下来我们看一下在分布式系统拓扑结构中,Load Balancer 是如何部署的。(下面谈到的这些拓扑结构对 L4 & L7 都是适用的)。
中间代理 Middle proxy

Figure 4: Middle proxy load balancing topology
这种拓扑结构应该是大多数读者比较熟悉的一种。这个分类包括硬件设备(厂商有Cisco、Juniper、F5);云软件方案,比如 AWS 的 ALB & NLB 或者 Google 的 Cloud Load Balancer;还有自主搭建的纯软件解决方案,比如 HAProxy, NGINX 和 Envoy. 中间代理的解决方案的好处是用户使用简单。用户只要通过 DNS 连接 Load Balancer 即可,其他不用关心。缺点就是这种方案的 Load Balancer (即使是一个集群)其实是一个故障单点而且也是规模的瓶颈。一个中间代理通常也是一个黑盒,运维起来困难。观察到的问题有可能出在 Client?也有可能是物理网络?或者中间代理?或者后端?很难确定。
边缘代理 Edge proxy

Figure 5: Edge proxy load balancing topology
边缘代理的拓扑其实上是中间代理模式的一个变种,区别是前者的 Client 访问 Load Balancer 是通过公网。在这种场景下,Load Balancer 通常必须提供额外的 gateway 特性,比如 TLS 中断,限速,认证和复杂的流量路由。它的优点和缺点跟中间代理模式的一样。通常不可避免的要为一个面向公网的大型分布式系统专门部署一个 Edge proxy。Client 通常是通过 DNS 访问系统,这对于服务的 owner 来说是不可控的。而且,出于安全考虑,必须有一个单一的 gateway 来让所有的公网流量进入系统。
嵌入 Client 的库 Embedded client library

Figure 6: Load balancing via embedded client library
为了避免 Middle Proxy 模式的单点故障问题,一些比较复杂的解决方案将 Load Balancer 以库的形式嵌入到服务中,如图6所示。在支持的功能上面,库与库之间的差别很大。在这个分类里面,功能丰富,比较知名的有 Finagle,Eureka/Ribbon/Hystrix,gRPC(主要是调用 Google 内部的 Stubby 系统)。基于库的解决方案主要的优势在于完全将 Load Balancer 的所有功能分布到每一个 Client 中,解决了之前说的单点和扩容的问题。但是这种方案的最大缺点就是需要针对每种使用到的开发语言都要实现一个库,而用不同的语言实现一个非常复杂的网络库,这个成本还是很高的。另外,还要考虑到库的升级,对于一个大型的服务架构来说,要升级一个库也是非常痛苦的,这通常会导致多个版本的库存在,这样维护起来会非常麻烦。
但是只要克服多语言开发和升级的问题,这种方案也是非常成功的。
跨斗代理 Sidecar proxy

Figure 7: Load balancing via sidecar proxy
Sidecar proxy 是 Embedded client library 方案的一个变种,如图 7 所示。最近几年这种拓扑方案以 『service mesh』的名字流行起来。Sidecar proxy 的思想就是通过另外一个不同进程来进行网络访问,这样虽然牺牲一点性能,但是却可以解决 Embedded client library 多语言实现的问题。截至撰写本文时,最流行的 sidecar proxy 模式的负载均衡器有 Envoy,Nginx,HAProxy,Linkerd。有关 sidecar proxy 方法更详细的描述请看我的这篇介绍 Envoy 的文章和 service mesh 数据面板和控制面板的对比文章。
不同拓扑类型的优势/劣势总结
总之,我认为对于服务之间的通信,sidecar proxy (service mesh)会逐渐取代其他的模式。进入 service mesh 的流量需要先通过 edge proxy.
当前 L4 负载均衡的当前水平
现在 L4 负载均衡还有意义吗?
我们之前的讲了 L7 的负载均衡对现代的协议多么多么好,接下来我们也会详细说一下,那 L4 的负载均衡就没有意义了吗?绝对不是!
虽然我认为在服务之间的通信,L7 负载均衡最终会替代 L4 的负载均衡,但是 L4 在边缘模式上还有很重要的意义,当今大型的分布式系统架构通常使用 L4/L7 两层负载均衡,服务之间使用 L7 负载均衡,在 L7 负载均衡前部署 L4 负载,这样的好处有:
以下我们将讨论 middle/edge proxy 模式下 L4 负载均衡的不同设计,这些设计通常不适用于 embedded client library 和 sidecar proxy 模式。
TCP/UDP termination load balancers

Figure 8: L4 termination load balancer
第一类 L4 负载均衡器就是仍然还在使用的中断式的负载均衡器,如图 8 所示。这个是与上文 L4 负载均衡器简介中介绍的那种是一样的。这种负载均衡器使用两个独立的连接:一个是 Client 和负载均衡器之间的连接,一个是负载均衡器与后端的连接。这种负载均衡器还在使用的原因有:
TCP/UDP passthrough load balancers

Figure 9: L4 passthrough load balancer
第二种类型的 L4 负载均衡器是透传型的,如图 9 所示。这种类型负载均衡器不会中断 TCP 连接,而是在经过链接跟踪(Connection tracking)和网络地址置换(NAT)之后,直接将每一条链接的包转发给一个选定的后端。我们首先来看一下什么是链接跟踪和网络地址转换:
通过使用 Connection tracking 和 NAT,Load balancer 可以把书大部分的原生 TCP 流量从 Client 透传到后端。举例,假如 Client 要和 1.2.3.4:80 通信,而选中的后端地址是 10.0.0.2:9000. Client 的 TCP 包先会到达 Load balancer 1.2.3.4:80,然后 Load balancer 修改目的 IP 和端口为 10.0.0.2:9000,同时也会把源 IP 修改为 Load balancer 自己的 IP。这样当后端响应TCP包时,也会先经过 Load balancer,再经过 Connection tracking 和 NAT 之后,包又透传给了 Client。
为什么它相对于中断的load balancer 复杂反而可以用来取代中断的 load balancer?
几个理由:
由于透传的 load balancer 不会中断 TCP 连接,所以它们不需要缓存任何 TCP 窗口,而保存每条连接的状态需要的存储是非常小的,使用一个高效的哈希表就可以满足需求了。因此,透传 load balancer 通常比中断的 load balancer 可以处理更大量的连接和流量。
TCP 拥塞控制可以控制互联网终端之间发送数据的量,避免耗尽可用带宽和缓冲。因为透传 load balancer 是不会中断 tcp 连接,所以它不参与拥塞控制。这意味着可以允许后端使用不同的拥塞控制算法。可以更容易的实验拥塞控制变化。(最近刚出的 BBR)
透传负载均衡需要更高级的 L4 负载均衡技术,比如 DSR 和支持分布式一致性哈希的集群化方案
Direct server return (DSR)

Figure 10: L4 Direct server return (DSR)
DSR load balancer 如图 10 所示。DSR 建立在前文提到的透传 load balancer 之上。 DSR 是一种优化:只有入口/请求流量经过 load balancer。出口和响应流量绕过 load balancer 直接回到 client。使用 DSR 最主要的原因是在大部分的负载情况下,响应的流量远远大于请求的流量(比如,HTTP 请求/响应模式)。假如 10% 流量是请求的,90% 的流量是响应的,如果一个 load balancer 使用 DSR 方案,那么它只需要 1/10 的能力就可以满足系统需求了。因为以往 load balancer 相当的昂贵,这样的优化对于系统成本和可靠性(少即是好)有非常大的意义。DSR load balancer 可以扩展透传 load balancer 的概念:
需要注意的是在透传 load balancer 和 DSR load balancer 的设计中,可以通过 load balancer 和后端设置连接跟踪,NAT,GRE等多种方式。但是这个话题不在我们这篇文章的讨论范围内。
Fault tolerance via high availability pairs 通过高可用性对来容错

Figure 11: L4 fault tolerance via HA pairs and connection tracking
到目前为止, 我们一直在独立的讨论 load balancer 的设计。透传和DSR load balancer 都需要一定量的连接跟踪和 load balancer 自己的状态。那如果 load balancer 挂了呢?如果一个单一的 load balancer 实例挂掉了,所有经过这个 load balancer 的连接都会断开。取决于应用的类型,但是通常这样会对应用的性能造成很大的影响。
以往,L4 load balancer 通常是硬件设备,可以从各大供应商购买(Cisco, Juniper,F5 等)。这些设备非常贵但是可以处理大量的流量。为了避免 load balancer 单点故障导致应用程序中断,load balancer 通常是以高可用对的方式来部署,如图 11 所示。一个经典的 HA load balancer 设置如下:
以上的配置很多高流量的互联网应用仍然在使用,但是,这种方式也有很多缺点:
Fault tolerance and scaling via clusters with distributed consistent hashing
通过支持分布式一致性哈希的集群实现容错和扩容

Figure 12: L4 fault tolerance and scaling via clustered load balancers and consistent hashing
最早从 2000 年中期开始,大型的互联网基础设施开始设计和部署新的大量并行 L4 负载均衡系统,如图 12 所示。这个系统的目标是:
工作原理如下:
让我们看一下以上的设计如何消除了高可用对的所有缺陷:
关于这个设计,经常被问到的问题是『为什么不让边缘路由器直接和后端通信?为什么我们还需要负载均衡器?』主要原因是考虑到DoS 缓解和后端操作的简便性。如果没有负载均衡器,每一个后端不得不参与到 BGP 中而且执行滚动部署的时候更困难。
所有的现代 L4 负载均衡器都在朝着这种设计(或者某种变化 )方向演进。最著名的两个例子是 Google 的 Maglev 和 Amazon 的NLB。目前没有任何开源负载均衡器实现了这种设计,但是,据我了解,有一家公司正在计划在2018年发布一个开源的此种负载均衡器。我非常期望这个发布,因为现代的 L4 的负载均衡器是网络领域的开源软件中一个关键缺失。
L7 负载均衡的当前水平

是的,确实是这样的。最近几年,L7 负载均衡器/代理的发展开始出现了复苏。这契合了分布式系统中对微服务架构的推进。从根本上说,固有故障的网络更难高效运维,尤其是当其被越来越多使用时。而且,自动扩容,容器调度等技术的出现意味着在静态文件中配置静态 IP 的日子已经不复存在。系统不仅更多的使用网络,而且越来越动态,需要负载均衡器有新的功能。在这个章节,我会简要的总结一下在现代 L7 负载均衡器中发展最快的领域。
协议支持
现代 L7 负载均衡器已经显式增加了对很多协议的支持。负载均衡器对应用的流量知道的越多,就可以在观测数据的输出,高级的负载均衡和路由等方面做更多复杂的事情。比如,Envoy 已经针对 HTTP/1,HTTP2,gRPC,Redis,MongoDB 和 DynamoDB 等协议实现了 L7 的协议解析和路由,接下来还要增加对更多协议的支持,包括 MySQL 和 Kafka。
动态配置
如上所述,分布式系统越来越多的动态特性需要在创建动态和实时响应的控制系统上进行投入。Istio 是一个控制系统的例子。关于这个话题的更多信息,请看我的另一篇博客 Service mesh data plane vs. control plane。
高级负载均衡
L7 的负载均衡器现在通常已经内置支持很多高级的负载均衡特性,比如超时,重传,限速,熔断,屏蔽,缓存,基于内容的路由等。
可观测性
如前文所述,分布式系统的动态性使系统变得很难调试。一个强健的协议相关的可视化输出通常是 L7 负载均衡器的所有功能中最重要的一个。输出数字统计,分布式跟踪和自定义的日志,这些基本上是任何一个 L7 负载均衡方案要支持的。
扩展性
现代 L7 负载均衡器的的用户通常希望能够容易的扩展,增加自定义的功能特性。通常负载均衡器通过提供插件功能来支持,也可以通过脚本来支持,比如 Lua。
容错
前文写了很多 L4 负载均衡器的容错性。那么 L7 负载均衡器的容错性怎么样呢?通常,我们认为 L7 负载均衡器是可扩展和无状态的。使用常用软件就可以让 L7 负载均衡器比较容易的水平扩容。而且,L7 负载均衡器的过程和状态跟踪要比 L4 的更复杂。尝试创建一个 L7 负载均衡器的 HA 对在技术上是可行的,但是这是一项重大任务。
总之,不管是 L4 还是 L7 负载均衡领域,业界都是在从 HA 对向通过一致性哈希实现水平扩容的系统转移。
其他
L7 负载均衡器正在以惊人的节奏演化。比如,了解一下 Envoy 提供的负载均衡技术,可以看一下 Envoy 的总体架构。
全局负载均衡和中心化的控制面板

Figure 13: Global load balancing
负载均衡的特性将会渐渐的把单个的负载均衡器作为一个常用设备。我认为,真正的创新和商业机会会集中在控制面板上。图 13 展示了一个 全局负载均衡系统 的示例。在这个例子中,一些事情发生了变化:
全局负载均衡器将逐渐的承担更复杂的事情,而这些事情是任何一个负载均衡的个体无法完成的。比如:
为了实例全局负载均衡,用于数据平面的负载均衡器必须具有复杂的动态配置的能力。更多信息,请看我的博客 Envoy’s universal data plane API 和 service mesh data plane vs. control plane。
从硬件到软件的演进
目前为止,这篇文章只是在讲 L4 负载均衡器高可用对的时候简单提到了硬件和软件。那在这个领域的业务趋势是什么呢?

这条推特确实有点搞笑夸张了,但是确实对当前的趋势做了总结:
总结和未来的负载均衡
总而言之,本文的关键要点是:
总之,我认为这是一个对于计算机网络最好的时代!大多数系统向开源和软件的转变正在将迭代速度提高几个数量级。此外,随着分布式系统通过『serverless』范例继续向动态化迈进,底层网络和负载均衡系统的复杂性将会相应的增加。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31537575/viewspace-3125803/ ,如需转载,请注明出处,否则将追究法律责任。