原文: Zero Configuration Service Mesh with On-Demand Cluster Discovery
在这篇文章中,我们将讨论 Netflix 采用服务网格的历史、动机,以及如何与 Kinvolk 和 Envoy 社区合作,在复杂的微服务环境中简化服务网格,即按需集群发现。
Netflix IPC 简史
在大型公司中,Netflix 很早就开始使用云计算了。从 2008 年开始迁移,到 2010 年,Netflix的流媒体就完全在AWS上运行了。今天,有大量工具(既有开源的,也有商业的)都是为云原生环境设计的。然而,在 2010 年,这些几乎都不存在(CNCF直到 2015 年才成立)!由于没有现成解决方案可用,不得不自己构建。
对于服务间的进程间通信(IPC,Inter-Process Communication),通常需要中间层负载均衡器提供的丰富功能集。此外还需要解决方案来解决在云中工作的现实: 高度动态的环境,节点不断启动关闭,服务需要快速响应更改并绕过故障。为了提高可用性,我们设计了可以单独失败并避免单点故障的系统组件。这些设计原则引导我们实现客户端负载均衡,而2012年圣诞节前夕的宕机事件进一步巩固了这一决定。在上云的最初几年里,我们为服务发现构建了Eureka,为IPC构建了Ribbon(内部称为 NIWS)。Eureka 解决了服务如何发现以及与哪些实例通信的问题,而 Ribbon 提供了负载均衡的客户端逻辑,以及许多其他弹性特性。这两项技术,以及许多其他弹性和混沌工具,产生了巨大的变化,显著提升了可靠性。
Eureka 和 Ribbon 提供了简单但功能强大的界面,使得采用很容易。为了使一个服务与另一个服务通信,需要知道两件事: 目标服务名,以及通信是否应该是安全的。Eureka 为此提供的抽象是用于非安全通信的虚拟 IP(VIP,Virtual IP)和用于安全通信的安全 VIP(SVIP,Secure VIP)。服务向 Eureka 发布一个 VIP 名称和端口(例如: myservice,端口 8080),或者一个 SVIP 名称和端口(例如: myservice-secure,端口 8443),或者同时发布两者。IPC 客户端针对 VIP 或 SVIP 实例化,Eureka 客户端代码通过从 Eureka 服务器获取 IP 和端口对来处理该 VIP 的转换。客户端还可以选择性启用 IPC 功能,如重试或断路,或定义一组合理的默认值。
在该体系架构中,服务到服务的通信不再经过负载均衡器,因此没有单点故障。缺点是,Eureka 作为主机注册 VIP 的真实来源,是一个新的故障点。但是,如果 Eureka 出现故障,服务可以继续相互通信,尽管随着时间推移,主机信息会随着 VIP 实例的启动、关闭而变得陈旧。但与完全无法通信相比,在停机期间以降级但可用的状态运行的能力仍然是个显著改进。
为什么需要服务网格?
过去的十年中,尽管不断变化的业务需求和不断发展的行业标准在许多方面增加了 IPC 生态系统的复杂性,但上述架构为我们提供了良好服务。首先,我们增加了不同 IPC 客户机的数量。内部 IPC 流量现在是纯 REST、GraphQL和gRPC的混合。其次,我们已经从纯 Java 环境转变为多语言环境,支持node.js、Python以及各种 OSS 和现成软件。第三,我们继续为 IPC 客户端添加更多功能: 自适应并发控制、断路、对冲和故障注入等功能已经成为工程师的标准工具,使我们的系统更加可靠。与十年前相比,我们现在支持更多特性、更多语言、更多客户端。在所有这些实现之间保持功能均等并确保以相同方式运行非常具有挑战性,我们想要一个单一的、所有功能经过良好测试的实现,这样就可以在一个地方进行更改和修复错误。
这就是服务网格的作用所在,我们可以将 IPC 功能集中在一个实现中,并使每种语言的客户端尽可能简单,只需要知道如何与本地代理通信。Envoy 非常适合作为代理,它是一个经过实战测试的 OSS 产品,在行业中大规模使用,具有许多关键弹性特性,当我们需要扩展其功能时,有很好的扩展点。通过中央控制平面配置代理的能力是一个杀手级功能,这允许我们动态配置客户端负载均衡,就好像中央负载均衡器一样,但避免了负载均衡器作为服务到服务请求路径中的故障单点。
向服务网格迁移
一旦决定迁移到服务网格,下个问题就变成了应该如何迁移?我们为迁移确定了一些约束条件。首先,希望保留现有界面,特定 VIP 名和安全的抽象可以很好的为我们服务,我们不想破坏向后兼容性。第二,希望自动化迁移,并使其尽可能无缝。这两个约束意味着我们需要在 Envoy 中支持 Discovery 抽象,以便 IPC 客户端可以继续在底层使用。幸运的是,Envoy 已经为此准备好了抽象。VIP 可以表示为 Envoy 集群,代理可以使用集群发现服务(CDS,Cluster Discovery Service)从控制平面获取。集群中的主机表示为 Envoy 端点,可以使用端点发现服务(EDS,Endpoint Discovery Service)来获取。
我们很快就遇到了无缝迁移的绊脚石,Envoy 要求将集群指定为代理配置的一部分。如果服务 A 需要与集群 B 和 C 通信,那么需要将集群 B 和 C 定义为 A 的代理配置的一部分。这在规模上具有挑战性,因为任何给定服务都可能与数十个集群通信,而每个应用的集群集都是不同的。此外,Netflix 一直在变化,不断有新业务上线,如直播、广告和游戏,架构也在不断发展。这意味着与服务通信的集群将随着时间的推移而变化。我们评估了许多不同方法来填充集群配置,假设有 Envoy 原语可供使用:
- 让服务所有者定义其服务需要与之通信的集群。这个选项看起来很简单,但在实践中,服务所有者并不总是知道,或者想知道,他们与哪些服务进行通信。服务通常导入由其他团队提供的库,这些库在底层与多个其他服务通信,或者与遥测和日志记录等其他运维服务通信。这意味着服务所有者需要知道这些辅助服务和库是如何在底层实现的,并在发生变化时调整配置。
- 基于服务调用图自动生成 Envoy 配置。这种方法对于已经存在的服务很简单,但是当上线新服务或添加一个新的上游通信集群时,这种方法就很有挑战性了。
- 将所有集群推送到每个应用。这个选项很简单,但很快就发现,将数百万个端点推送到每个代理并不可行。
考虑到我们的目标是无缝采用,这些选项中每一个的缺点都足够明显,因此我们探索了另一个选项: 如果可以在运行时按需获取集群信息,而不是预先定义,那会怎么样?当时,服务网格的工作仍处于起步阶段,只有少数工程师在从事这项工作。我们联系了Kinvolk,看看他们是否可以与我们和 Envoy 社区合作实现这个功能。这种协作的结果是按需集群发现(ODCDS,On-Demand Cluster Discovery)。有了这个特性,代理现在可以在第一次尝试连接集群时查找集群信息,而不用在配置中预定义所有集群。
有了这个功能,我们需要为代理提供集群信息以供查找。我们开发了一个实现 Envoy XDS 服务的服务网格控制平面。然后,需要从 Eureka 获取服务信息,以便返回到代理。我们将 Eureka VIP 和 SVIP 表示为独立的 Envoy 集群发现服务(CDS)集群(因此服务 myservice 可以有集群 myservice.vip 和 myservice.svip)。集群中单个主机表示为单独的端点发现服务(EDS)端点。这允许我们重用相同的 Eureka 抽象,并且像 Ribbon 这样的 IPC 客户端可以以最小的更改迁移到服务网格。随着控制平面和数据平面的变化,流程如下所示:
- 客户端请求发到 Envoy
- 根据 Host/:authority 头提取目标集群(这里使用的头是可配置的,这是我们的方法)。如果已经知道该集群,跳转到步骤 7
- 集群不存在,暂停当前请求
- 向控制平面集群发现服务(CDS)端点发出请求,控制平面根据服务配置和 Eureka 注册信息生成定制的 CDS 响应
- Envoy 收到响应,通过端点发现服务(EDS)触发端点拉取,EDS 根据 VIP 或 SVIP 的 Eureka 状态信息返回集群端点
- 继续处理客户端请求
- Envoy 正常处理请求,基于负载均衡算法选择一个端点并发出请求
整个流程在几毫秒内完成,但只有在对集群的第一个请求时触发。之后,Envoy 的行为就像在配置中定义了集群一样。关键是该系统让我们可以在不需要配置的情况下无缝的将服务迁移到服务网格,满足了之前定义的主要采用约束之一。我们提供的抽象仍然是 VIP 名称加安全,可以通过配置单个 IPC 客户端连接到本地代理而不是直接连接上游应用程序来迁移到服务网格。我们继续用 Eureka 作为 VIP 和实例状态的真实来源,让我们能够在迁移时为网格上应用提供异构环境的支持。还有一个额外的好处是可以通过只获取实际通信的集群数据来降低 Envoy 内存使用率。
按需获取数据有一个缺点,集群第一个请求的延迟会增加。我们遇到过这样的用例: 服务在第一次请求时需要非常低的访问延迟,增加几毫秒的额外时间会增加太多开销。对于这些用例,服务需要预先定义与之通信的集群,或者在发出第一个请求之前建立连接。我们还考虑了在代理启动时根据历史请求模式从控制平面预推送集群。总的来说,我们认为用一小部分服务受影响的代价降低系统杂性是合理的。
你好,我是俞凡,在 Motorola 做过研发,现在在 Mavenir 做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI 等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!