在从单片应用程序过渡到微服务体系结构的过程中,我们面临着新的问题。
在单片应用程序中,通常很容易确定在系统的哪一部分发生了错误。 问题很可能出在整体程序本身的代码或数据库中。 但是,当我们开始寻找微服务架构中的问题时,一切并不是那么明显。 您需要找到请求从头到尾的整个路径,然后从数百个微服务中选择它。 此外,它们中的许多还具有自己的存储库,其中也可能发生逻辑错误以及性能和容错问题。

很长一段时间以来,我一直在寻找一种工具来解决此类问题(我在Habré上写过: 1,2 ),但最终我制定了自己的开源解决方案。 在本文中,我将讨论服务网格方法的好处,并分享一种用于其实现的新工具。
分布式跟踪是解决分布式系统中错误的常见解决方案。 但是,如果系统尚未采用这种方法来收集有关网络交互的信息,或者更糟的是,在系统的一部分中它已经正常工作,而在一部分中却没有,因为它没有添加到旧服务中,那该怎么办? 为了确定问题的确切根本原因,您必须对系统中发生的事情有完整的了解。 了解主要业务关键路径中涉及哪些微服务特别重要。
在这里,服务网格方法可以为我们提供帮助,它将以比服务本身更低的水平处理所有用于收集网络信息的机器。 这种方法使我们能够拦截所有流量并进行动态分析。 而且,关于它的应用程序甚至都不应该知道任何东西。
服务网格方法
服务网格方法的主要思想是在网络上添加另一个基础结构层,这将使我们能够进行服务间交互。 大多数实现工作如下:将带有透明代理的附加sidecar容器添加到每个微服务,通过该容器传递所有传入和传出服务流量。 在这里,我们可以进行客户端平衡,应用安全策略,对请求数量进行限制并收集有关生产中服务交互的重要信息。

解决方案
这种方法已经有几种实现: Istio和linkerd2 。 它们提供了许多现成的功能。 但是与此同时,资源消耗很大。 此外,此类系统在其中运行的群集越大,维护新的基础结构将需要更多的资源。 在Avito中,我们运行具有数千个服务实例的kubernetes集群(并且它们的数量继续快速增长)。 在当前的实现中,每个服务实例Istio消耗约300Mb的RAM。 由于功能众多,透明平衡还会影响服务的总响应时间(最长10ms)。
结果,我们确切地查看了我们现在需要的功能,并决定我们开始实施此类解决方案的主要原因是能够透明地从整个系统收集跟踪信息的能力。 我们还希望控制服务的交互,并对服务之间传递的标头进行各种操作。
最后,我们做出了决定: Netramesh 。
Netramesh
Netramesh是一种轻量级服务网格解决方案,无论系统中有多少服务,它都具有无限的可扩展性。
新解决方案的主要目标是减少资源开销和提高性能。 在主要功能中,我们立即希望能够将跟踪范围透明地发送到Jaeger系统。
如今,大多数云解决方案都在Golang上实现。 当然,这是有原因的。 编写可与I / O异步工作并根据需要扩展至内核的Golang网络应用程序既方便又相当简单。 并且,这也很重要,其性能足以解决该问题。 因此,我们也选择了Golang。
性能表现
我们一直致力于实现最高性能。 对于在服务的每个实例旁边部署的解决方案,需要消耗少量的RAM和处理器时间。 而且,当然,答复的延迟也应该很小。
让我们看看结果如何。
内存
Netramesh在无流量的情况下消耗约10Mb的数据,最大消耗50Mb的数据,每个实例的负载高达10,000 RPS。
在具有数千个实例的群集中,Istio envoy代理始终消耗约300Mb的内存。 这不允许您将其扩展到整个群集。


使用Netramesh,我们的内存消耗减少了大约10倍。
中央处理器
在负载下,CPU使用率相对相等。 这取决于每单位时间对车辆的请求数。 高峰时每秒3000个请求的值:


还有一个重要点:Netramesh-没有控制平面且没有负载的解决方案不会消耗CPU时间。 使用Istio,Sidecar始终可以更新服务端点。 结果,我们可以看到没有负载的此类图片:

我们使用HTTP / 1在服务之间进行通信。 通过特使进行传播时,Istio的响应时间增加了5-10ms,对于准备在毫秒内响应的服务来说,这是很多。 使用Netramesh,这个时间减少到0.5-2ms。
可扩展性
每个代理花费的少量资源可以将其放置在每个服务旁边。 Netramesh是在没有控制平面组件的情况下故意创建的,以简单地保持每个侧车的亮度。 通常在服务网格解决方案中,控制平面将服务发现信息分发到每个边车。 随之而来的是有关超时,平衡设置的信息。 所有这些使您可以做很多有用的事情,但是不幸的是,它夸大了sidecar'y的大小。
服务发现

Netramesh不为服务发现添加任何其他机制。 通过netra边车透明地代理所有流量。
Netramesh支持HTTP / 1应用程序协议。 可配置的端口列表用于确定端口。 通常,系统上有多个通过HTTP通信的端口。 例如,我们使用NETRA_HTTP_PORTS
与服务和外部请求进行交互,在这种情况下,可以使用NETRA_HTTP_PORTS
环境NETRA_HTTP_PORTS
进行设置。
如果您使用Kubernetes作为管弦乐队及其服务实体在服务之间进行集群内交互的机制,则该机制将保持完全相同。 首先,微服务使用kube-dns获得服务IP地址,并为其打开新连接。 首先使用本地netra-sidecar建立此连接,所有TCP数据包最初都到达netra。 接下来,netra-sidecar建立到原始目的地的连接。 节点上Pod IP上的NAT与没有netra时完全相同。
分布式跟踪和上下文滚动
Netramesh提供了发送有关HTTP交互的跟踪范围所需的功能。 Netra-sidecar解析HTTP协议,测量请求延迟,从HTTP标头中检索必要的信息。 最终,我们在一个Jaeger系统中获得了所有痕迹。 为了进行微调,您还可以使用官方jaeger go库提供的环境变量。


但是有一个问题。 在服务生成并转发特殊的uber标头之前,我们将不会在系统中看到连接的跟踪范围。 这就是我们需要快速找到问题原因的原因。 Netramesh在这里又有了解决方案。 代理读取HTTP标头,如果没有超级跟踪ID,则会生成它。 Netramesh还将有关传入和传出请求的信息存储在Sidecar中,并通过丰富传出请求的必要标头来进行比较。 服务中需要做的就是仅抛出一个X-Request-Id
标头,可以使用NETRA_HTTP_REQUEST_ID_HEADER_NAME
环境NETRA_HTTP_REQUEST_ID_HEADER_NAME
进行配置。 要在Netramesh中控制上下文的大小,可以设置以下环境变量: NETRA_TRACING_CONTEXT_EXPIRATION_MILLISECONDS
(将存储上下文的时间)和NETRA_TRACING_CONTEXT_CLEANUP_INTERVAL
(上下文清除的周期性)。
也可以通过用特殊的会话标记标记多个路径来组合系统中的多个路径。 Netra允许您设置HTTP_HEADER_TAG_MAP
以将HTTP标头转换为适当的跟踪范围标记。 这对于测试尤其有用。 通过功能测试后,您可以查看通过相应的会话密钥进行过滤对系统的哪个部分产生了影响。
确定请求源
要确定请求来自何处,可以使用该功能自动添加带有源的标头。 使用NETRA_HTTP_X_SOURCE_HEADER_NAME
环境NETRA_HTTP_X_SOURCE_HEADER_NAME
可以指定将自动设置的标题的名称。 使用NETRA_HTTP_X_SOURCE_VALUE
可以设置将为所有传出请求设置X-Source标头的值。
这使您可以在整个网络中统一分配此有用的标头。 然后,您已经可以在服务中使用它,并将其添加到日志和指标中。
Netramesh通信和内部路由
Netramesh由两个主要组件组成。 第一个是netra-init,它设置用于拦截流量的网络规则。 它使用iptables重定向规则来拦截Sidecar上的全部或部分流量,Sidecar是Netramesh的第二个主要组件。 您可以配置要为传入和传出TCP会话拦截的端口: INBOUND_INTERCEPT_PORTS, OUTBOUND_INTERCEPT_PORTS
。
该工具还具有一个有趣的功能-概率路由。 如果专门使用Netramesh收集跟踪范围,则在生产环境中,您可以使用变量NETRA_INBOUND_PROBABILITY
和NETRA_OUTBOUND_PROBABILITY
(从0到1)来节省资源并启用概率路由。 默认值为1(将拦截所有流量)。
成功拦截后,netra sidecar接受新连接并使用SO_ORIGINAL_DST
套接字选项获取原始目的地。 然后,Netra打开到原始IP地址的新连接,并在双方之间建立双向TCP通信,侦听所有通过的流量。 如果端口定义为HTTP,则Netra将尝试解析并路由它。 如果HTTP解析不成功,Netra将回退到TCP并透明地代理字节。
建立依赖图
在Jaeger中收到大量跟踪信息之后,我想获得系统中交互的完整图。 但是,如果您的系统已满载,并且每天累积数十亿个跟踪范围,那么使它们聚合就不是一件容易的事。 有一种官方的方法可以做到: 火花依赖 。 但是,在过去的24小时内,要构建完整的图形并强制从Jaeger下载整个数据集将花费数小时。
如果使用Elasticsearch存储跟踪范围,则可以在Golang上使用一个简单的实用程序,该实用程序将利用Elasticsearch的功能在几分钟内构建相同的图形。

如何使用Netramesh
Netra可以简单地添加到运行任何协调器的任何服务中。 您可以在此处查看示例。
目前,Netra无法自动将Sidecar部署到服务,但是有实施计划。
未来网
Netramesh的主要目标是实现最低的资源成本和高性能,为可观察性和服务间交互的控制提供主要机会。
将来,Netramesh将获得对除HTTP以外的应用程序级协议的支持。 在不久的将来,将有可能进行L7路由。
如果您遇到类似的问题并给我们写问题和建议,请使用Netramesh。