Netramesh - 轻量级服务网格解决方案

当我们从单体应用程序转向微服务架构时,我们面临着新的挑战。

在单片应用程序中,通常很容易确定错误发生在系统的哪个部分。 最有可能的是,问题出在单体应用程序本身的代码中,或者出在数据库中。 但当我们开始寻找微服务架构中的问题时,一切就不再那么明显了。 我们需要找到请求从开始到结束的整个路径,并从数百个微服务中选择它。 而且,其中许多还拥有自己的存储设施,这也会导致逻辑错误,以及性能和容错方面的问题。

Netramesh - 轻量级服务网格解决方案

我长期以来一直在寻找一种可以帮助解决此类问题的工具(我在 Habré 上写过: 1, 2),但最终我做了自己的开源解决方案。 在本文中,我讨论了服务网格方法的好处,并分享了用于其实现的新工具。

分布式跟踪是分布式系统中查找错误问题的常见解决方案。 但是,如果这种收集网络交互信息的方法尚未在系统中实现,或者更糟糕的是,在系统的一部分中它已经正常工作,但在部分系统中却不能正常工作,因为它尚未添加到旧服务中,该怎么办? ? 为了确定问题的确切根本原因,有必要全面了解系统中发生的情况。 了解关键业务路径中涉及哪些微服务尤为重要。

在这里,服务网格方法可以为我们提供帮助,它将在低于服务本身运行的级别处理用于收集网络信息的所有机制。 这种方法使我们能够拦截所有流量并对其进行动态分析。 此外,应用程序甚至不需要了解任何相关信息。

服务网格方法

服务网格方法的主要思想是在网络上添加另一个基础设施层,这将允许我们通过服务间交互做任何事情。 大多数实现的工作原理如下:向每个微服务添加一个带有透明代理的附加 sidecar 容器,服务的所有传入和传出流量都通过该容器传递。 这正是我们可以进行客户端平衡、应用安全策略、对请求数量施加限制以及收集有关生产中服务交互的重要信息的地方。

Netramesh - 轻量级服务网格解决方案

解决方案

这种方法已经有多种实现: Istio и 链接器d2。 它们提供了许多开箱即用的功能。 但与此同时,资源的开销也很大。 此外,运行此类系统的集群越大,维护新基础设施所需的资源就越多。 在 Avito,我们运营的 kubernetes 集群包含数千个服务实例(并且它们的数量仍在快速增长)。 在当前的实现中,Istio 每个服务实例消耗约 300Mb 的 RAM。 由于存在大量的可能性,透明平衡也会影响服务的整体响应时间(最多10ms)。

因此,我们仔细研究了我们现在需要什么功能,并确定我们开始实施此类解决方案的主要原因是能够透明地从整个系统收集跟踪信息。 我们还希望控制服务的交互,并对服务之间传输的标头进行各种操作。

结果,我们做出了决定:  网网.

网网

网网 是一种轻量级服务网格解决方案,无论系统中的服务数量如何,都能够无限扩展。

新解决方案的主要目标是低资源开销和高性能。 在主要功能中,我们立即希望能够透明地将跟踪范围发送到我们的 Jaeger 系统。

如今,大多数云解决方案都是在 Golang 中实现的。 当然,这是有原因的。 在 Golang 中编写与 I/O 异步工作并根据需要跨内核扩展的网络应用程序既方便又简单。 而且,同样非常重要的是,性能足以解决这个问题。 这就是为什么我们也选择Golang。

Производительность

我们集中精力实现生产力最大化。 对于部署在每个服务实例旁边的解决方案,需要少量的 RAM 和 CPU 时间消耗。 当然,响应延迟也应该很小。

让我们看看我们得到了什么结果。

内存

Netramesh 在没有流量的情况下消耗约 10Mb,每个实例负载高达 50 RPS 时最大消耗 10000Mb。

在我们拥有数千个实例的集群中,Istio Envoy 代理始终消耗约 300Mb 的资源。 这不允许它扩展到整个集群。

Netramesh - 轻量级服务网格解决方案

Netramesh - 轻量级服务网格解决方案

使用 Netramesh,我们的内存消耗减少了约 10 倍。

中央处理器

负载下CPU使用率相对相等。 这取决于单位时间内向 sidecar 发出的请求数量。 峰值时每秒 3000 个请求的值:

Netramesh - 轻量级服务网格解决方案

Netramesh - 轻量级服务网格解决方案

还有更重要的一点:Netramesh——一种没有控制平面、没有负载的解决方案,不消耗CPU时间。 使用 Istio,sidecar 始终会更新服务端点。 结果,我们可以在没有负载的情况下看到这张图:

Netramesh - 轻量级服务网格解决方案

我们使用 HTTP/1 进行服务之间的通信。 通过 Envoy 代理时,Istio 的响应时间增加了 5-10 毫秒,这对于准备在毫秒内响应的服务来说是相当多的。 使用 Netramesh,这个时间已减少到 0.5-2 毫秒。

可扩展性

每个代理消耗的资源量很少,因此可以将其放置在每个服务旁边。 Netramesh 特意创建为没有控制平面组件,只是为了保持每个 sidecar 的轻量化。 通常在服务网格解决方案中,控制平面将服务发现信息分发到每个 sidecar。 随之而来的是有关超时和平衡设置的信息。 所有这些都允许您做很多有用的事情,但不幸的是,它使 sidecar 的尺寸变得臃肿。

服务发现

Netramesh - 轻量级服务网格解决方案

Netramesh 没有添加任何额外的服务发现机制。 所有流量均通过 netra sidecar 透明代理。

Netramesh 支持 HTTP/1 应用协议。 为了定义它,使用了可配置的端口列表。 通常,系统有多个端口用于进行 HTTP 通信。 例如我们使用80、8890、8080来进行服务与外部请求的交互,此时可以使用环境变量来设置它们 NETRA_HTTP_PORTS.

如果使用 Kubernetes 作为编排器及其 Service 实体机制来进行服务之间的集群内通信,那么该机制保持完全相同。 首先,微服务使用 kube-dns 获取服务 IP 地址并打开与其的新连接。 该连接首先与本地 netra-sidecar 建立,所有 TCP 数据包最初到达 netra。 接下来,netra-sidecar 与原始目的地建立连接。 节点上 pod IP 上的 NAT 与没有 netra 时完全相同。

分布式跟踪和上下文转发

Netramesh 提供发送有关 HTTP 交互的跟踪范围所需的功能。 Netra-sidecar 解析 HTTP 协议,测量请求延迟,并从 HTTP 标头中提取必要的信息。 最终,我们在一个 Jaeger 系统中获得了所有痕迹。 对于细粒度的配置,还可以使用官方库提供的环境变量 jaeger go 库.

Netramesh - 轻量级服务网格解决方案

Netramesh - 轻量级服务网格解决方案

但有一个问题。 在服务生成并发送特殊的 uber 标头之前,我们不会在系统中看到连接的跟踪跨度。 而这正是我们需要快速找到问题原因的。 Netramesh 再次提供了解决方案。 代理读取 HTTP 标头,如果它们不包含 uber 跟踪 ID,则生成一个。 Netramesh 还将有关传入和传出请求的信息存储在 sidecar 中,并通过使用必要的传出请求标头丰富它们来匹配它们。 您在服务中所需要做的就是仅发送一个标头 X-Request-Id,可以使用环境变量进行配置 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_VALUE 您可以设置为所有传出请求设置 X-Source 标头的值。

这使得这个有用的报头可以在整个网络中均匀分布。 然后您可以在服务中使用它并将其添加到日志和指标中。

流量路由和 Netramesh 内部结构

Netramesh 由两个主要组件组成。 第一个是 netra-init,设置网络规则来拦截流量。 他用 iptables 重定向规则 拦截 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 中收到大量跟踪信息后,我想获得系统中交互的完整图。 但是,如果您的系统负载很大并且每天累积数十亿个跟踪跨度,那么聚合它们就不是一件容易的任务。 有一个官方的方法可以做到这一点: Spark 依赖项。 然而,构建完整的图表需要几个小时,并且将迫使您从 Jaeger 下载过去 XNUMX 小时的整个数据集。

如果您使用 Elasticsearch 来存储跟踪范围,则可以使用 一个简单的 Golang 实用程序,它将使用 Elasticsearch 的特性和功能在几分钟内构建相同的图表。

Netramesh - 轻量级服务网格解决方案

如何使用Netramesh

Netra 可以轻松添加到运行任何编排器的任何服务中。 你可以看一个例子 这里.

目前,Netra 不具备自动将 sidecar 实现到服务的能力,但有实现计划。

Netramesh 的未来

主要目的 网网 是为了实现最小的资源成本和高性能,提供服务间通信的可观察和控制的基本能力。

未来,Netramesh将支持除HTTP之外的其他应用层协议。 L7路由将在不久的将来推出。

如果您遇到类似问题,请使用 Netramesh,并写信给我们提出问题和建议。

来源: habr.com

添加评论