Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。
在GitHub上我们每天要为数十亿HTTP,Git和SSH连接服务。为了获得最佳性能,我们在裸机硬件上运行。我们的负载均衡一直是历史上最复杂的组件之一。传统上,我们纵向扩展,运行一小组运行haproxy的超大型机器,并使用允许专用的10G链路故障转移的特定的硬件配置。最终,我们需要一个可扩展的解决方案,我们开始创建一个负载均衡器解决方案,该解决方案将在典型的数据中心配置中的商用硬件上运行。
在过去的一年,我们已经开发了新的负载均衡系统,称为GLB(GitHub Load Balancer)。今天,并在未来几周内,我们将共享这个设计并且释放它的组件作为开源软件。
吐故纳新
GitHub正在增长,在我们的体量下,垂直规模的负载均衡层已经无法满足需求了,需要一个新的方法。我们的原始设计是基于少数大型机器,每个机器都有专门的链接连接到我们的网络脊椎。这种设计将网络设备,负载均衡主机和负载均衡器配置绑定在一起,使得水平扩展变得困难。我们的目标是找到一个更好的办法。
我们首先确定了新系统的目标,我们可以从现有系统和现有技术的设计缺陷吸取经验和灵感。一段时间后,我们确定以下要求将产生一个成功的,在未来可维持的负载均衡层:
在商用硬件上运行
水平扩展
支持高可用性,避免了正常运行和故障转移过程中中断TCP连接
支持连接排水
在服务级别做负载均衡,每个负载均衡主机支持多个服务
可以像正常软件迭代并部署
每一层都可以测试,而不仅仅是集成测试
内置多个POPs和数据中心
适应典型的DDoS攻击,并拥有帮助缓解新的攻击的工具
设计
为了实现这些目标,我们需要重新思考IP地址和主机之间的关系,我们的负载均衡层的组成结构以及连接如何路由,控制和终止。
扩展IP
在一个典型的设置,您分配一个公网IP地址到一台物理机器。然后可以使用DNS在多个IP地址上分割流量,使您可以跨多个服务器分割流量。不幸的是,DNS条目缓存相当积极(往往忽略了TTL),我们的一些用户可能使用白名单或硬编码IP地址。此外,我们还提供一组特定的IP地址,为我们的Pages服务,客户可以直接使用他们的顶点域。而不是依靠增加额外的IP地址,以增加容量,并且当单个服务器发生故障时IP地址失效,我们需要一个解决方案,允许多个物理机器为一个IP地址提供服务。
路由器有一个叫做等价多路径路由(ECMP)的功能,其目的是通过多个等价的连接分割流量。ECMP通过散列传入分组的某些组件来工作,例如源和目的IP地址和端口。通过使用一致的散列,相同TCP流的后续分组将散列到相同的路径,避免了乱序分组并且保持会话的密切关系。
这对于跨越多个路径将数据包路由到同一物理目标服务器很有效。有趣的是,当您使用ECMP分割单个IP的流量到多个物理服务器上时,每个物理服务器都会终止TCP连接,但不共享任何状态,就像在一个负载均衡器一样。当这些服务器中的一个发生故障或被取消运行,并从ECMP服务器中删除时,会发生rehash事件。1 / N的连接将重新分配给其他服务器。由于这些服务器不共享这些被终止的连接状态。不幸的是,这些连接可能不是映射到发生故障的服务器的1 / N连接。此外,进行维护时删除服务器无法不中断1/N的活动连接。
L4/L7分离设计
其他项目使用的模式是将负载均衡器分为L4和L7层。在L4层,路由器使用ECMP,来分割流量,使用一致散列一组L4负载均衡器-通常使用软件,如IPVS/LVS。LVS保持连接状态,并且可选地将与多播的连接状态同步到其他L4节点,并将流量转发到运行诸如haproxy的软件的L7层。我们称L4层为“director”主机,因为它们直接引导流量,称L7层为“proxy”主机,因为它们代理到后端服务器的连接。
L4/L7分离的好处:proxy层节点现在可以通过正常地耗尽现有连接来从中移除,因为director节点上的连接状态将保持现有连接映射到他们现有的代理服务器,即使它们从新连接中被移除。此外,proxy层由于频繁的配置更改,升级和扩展,往往是需要更多的维护,这是我们的优势。
如果使用多播连接同步,则L4负载均衡节点能更优雅地处理故障,因为一旦连接已经同步到其他L4节点,连接将不再中断。如果没有连接同步,在提供director节点相同哈希连接方式,并具有相同的后端设置的情况想,director节点故障时连接也可能会成功地维持。在实践中,大多数这种分层设计只接受节点故障或节点维护下的连接中断。
不幸的是,director层使用LVS有一些显著的缺点。首先,多播不是我们想要支持的,所以我们将依赖于具有世界的相同视图的节点,并且具有一致的散列到后端节点。如果没有连接同步,某些事件,包括计划维护的节点,可能导致连接中断。连接中断是我们想要避免的,因为如果连接服务中中断,git不能重试或恢复。最后,director层需要连接状态的事实增加了DDoS缓解的额外复杂性,例如同步为了避免资源耗尽,现在需要在director节点上生成syncookies,尽管连接本身在proxy节点上被终止。
设计一个更好的director
我们早就在设计负载均衡系统时决定改进director层的通用模式。我们的目标是设计一个新的director层,它是无状态的,并允许director和proxy节点尽可能平滑地移除而无需中断用户。我们的用户所在的国家互联网链接可能很不理想,对我们来说重要的是,合理规模的存储库的克隆可以长期运行,在计划维护期间在合理的时间**内不会失败。
我们决定的设计,并且现如今在生产中使用的,是Rendezvous hashing的一个变体,支持常量时间查找。我们首先存储每个proxy主机并分配一个状态。这些状态处理我们设计目标连接消耗的方面,并且会在以后的文章中进一步讨论。然后,我们生成一个单一的固定大小的转发表,并使用Rendezvous散列的排序组件为每一行填充一组代理服务器。此表以及代理状态将发送到所有director服务器,并在代理记录发生变化时保持同步。这个保护proxy状态的表会发送给所有的director服务器以保持同步。当TCP数据包到达director,我们对源IP进行哈希以生成一致的索引到转发表中。然后,我们将数据包封装到另一个IP数据包(实际上是Foo-over-UDP)中,发往代理服务器的内部IP,并通过网络发送。代理服务器接收封装的分组,将其解封装,并且在本地处理原始分组。任何传出的数据包直接使用服务器返回,意味着到客户端的数据包直接来自处理服务器,完全绕过director层。
敬请关注
既然您可以处理和发送请求到这个博客文章中,我们希望您继续关注以后的文章,描述director深层的设计,改进haproxy热配置重载,以及我们如何设法迁移到新的系统而不引起任何人注意。
英文原文:http://githubengineering.com/introducing-glb/ 译者:flqzdzxx
本文转载于微信公众号: Python程序员(pythonbuluo),更多微信文章请扫描关注公众号:
|