eBay如何以及为何转向OpenTelemetry

博客文章在发布后不会更新。这篇文章已经发布一年多了,其内容可能已过时,部分链接可能无效。在依赖任何信息之前,请务必核实。

eBay 做出关键性转变,转向 OpenTelemetry,以更好地与可观测性行业标准保持一致。

header

简介

可观测性为任何组织提供了洞察力。可观测性的主要好处在于通过有效呈现关键工作流程中可能影响客户体验的持续性问题,从而防止收入损失。可观测性领域不断变化,OpenTelemetry 世界的最新发展迫使我们重新思考策略,以转向使用它。eBay 的可观测性平台 Sherlock.io 为开发人员和站点可靠性工程师 (SRE) 提供了强大的云原生产品集,用于观测驱动 eBay 生态系统的各种应用程序。Sherlock.io 支持可观测性的三个支柱——指标、日志和追踪。该平台的指标存储是一个集群化和分片化的 Prometheus 存储引擎实现。我们使用 Metricbeat 代理每分钟抓取大约 150 万个 Prometheus 端点,并将其摄取到指标存储中。这些端点加上记录规则,每秒摄取大约 4000 万个样本。摄取的样本导致 Prometheus 中存储了 30 亿个活动序列。因此,eBay 的可观测性平台在非同寻常的大规模下运行,这带来了新的挑战。

作为可观测性平台提供商,eBay 是最早使用代理抓取指标端点和跟踪日志文件的公司之一。正如我们在之前的博客文章中所讨论的,我们严重依赖 Elastic Beats 产品来接收平台信号。Beats 是一个轻量级的数据传输器,用于传输指标和日志等操作数据。从 2016 年到 2020 年的五年间,我们在所有 Kubernetes 集群上运行了 Filebeat 和 Metricbeat 作为 DaemonSets。DaemonSets 允许用户在 Kubernetes 集群的每个节点上部署给定的工作负载。然而,在内部黑客周进行的一次实验提供了一些令人惊讶的结论,并促使我们重新考虑使用 DaemonSets。在本文中,我们将讨论我们遇到的一些问题,特别是指标抓取方面的问题,以及我们如何演进自己的解决方案。我们还将详细讨论我们如何随着许可和开源景观的发展而调整,以及我们打算如何与 OpenTelemetry 这项倡议保持一致。

指标检测

eBay 的指标检测在很大程度上已标准化为 Prometheus 端点。各种应用程序的端点暴露是由于各种检测实践,包括(但不限于)

  • 官方 Prometheus 客户端(包括 Java、Go、Python 等)
  • Micrometer
  • 具有 Prometheus 导出器的 OTel SDK
  • 请求时发出 Prometheus 端点的自定义代码

eBay 平台工程组提供的框架内置了检测客户端,并暴露了各种指标端点,这些端点代表服务器端、客户端和数据库客户端指标。根据应用程序的性质,可以暴露需要抓取的 Prometheus 端点。应用程序所有者还可以暴露自己的端点来检测其业务 KPI。

自动发现

驱动 eBay 生态系统的绝大多数应用程序都运行在 eBay 的内部 Kubernetes 提供商 Tess 上。eBay 运行着数百个由 Tess 提供支持的 Kubernetes 集群,一个应用程序可以在任何数量和组合的这些集群上运行。应用程序所有者可以选择在其应用程序指标中添加从框架级别检测中免费提供的指标。我们的代理需要确切地知道当前运行的 Kubernetes Pod 暴露了哪些端点。为了向代理提供此信息,我们帮助丰富了 Beats 平台,以执行一项称为“基于提示的自动发现”的任务。自动发现是 Beats 的一个概念,它允许 Kubernetes API 服务器等动态源向代理提供信息,例如

  • 需要抓取的端点是什么?
  • 端点类型是什么——Dropwizard、Prometheus、Foobar,还是其他什么?
  • 多久抓取一次?
  • 代理是否需要了解任何其他额外信息,例如 SSL 证书?

随着日益复杂的发现模式的需求,我们与 Beats 开源社区合作,增强了自动发现的能力以满足我们的特定需求。我们贡献的一些功能包括

  • 发现多组配置:传统的基于注解的抓取非常有限,因为它只允许用户为抓取管理器提供简单的配置。考虑到每个端点都可以有动态需求,例如不同的处理和抓取间隔,我们增强了自动发现以接受多组配置。
  • 从命名空间注解发现目标:宣布抓取目标的规定方法要求在 Pod spec 中添加注解。但是,将其添加到那里意味着更改将导致 Pod 重启。如果更改旨在影响框架中检测到的并且在所有部署的应用程序中都可用的指标,那么这是不可取的。为了避免重启所有 Pod,我们添加了对自动发现的支持,使其能够另外检查命名空间级别的注解。

这些功能使 Beats 自动发现成为识别部署在 Kubernetes 集群上的目标的更通用、功能更丰富的发现机制之一。

通过 DaemonSets 进行指标抓取的限制

我们首次尝试大规模运行 Metricbeat 是在每个 Kubernetes 集群上将其作为 DaemonSets 运行。每个 Pod 都分配了一个 CPU 和 1GB 内存来处理该节点上暴露的所有指标。当 Metricbeat 启动时,它会向 API 服务器请求该集群的所有命名空间以及在其上运行的节点上部署的 Pod。有了这些信息,它会为每个 Pod 通过汇总 Pod 和 Pod 命名空间上的注解来创建配置。我们观察到的一些结果包括

  • 资源碎片化:鉴于我们在 N 个节点集群上运行 N 个 Beat,如果单个 Beat 管道需要 50MB 的引导成本,我们实际上会浪费 50*N MB 的资源。这相当于 3000 个节点的 Kubernetes 集群上的 150GB!
  • 轮询大型端点时出现 OOM 问题:我们曾见过客户暴露多达 150,000 个条目的端点。一些巨大的端点,如“kube-state-metrics”,包含三百万个条目,每次轮询产生 600MB 的数据。当这些用例落在节点上时,抓取变得不可靠。

下图描绘了任何 Beats 实例(如 Metricbeat、Filebeat 和 Auditbeat)在部署为 DaemonSet 时如何与 Sherlock.io 平台进行交互

daemonset

迁移到集群本地抓取

在处理一个不相关的项目时,我们采用了将 Metricbeat 作为单个实例运行以处理集群中所有目标的捷径。当我们观察运行 Metricbeat 的总 CPU 和内存使用量时,数字令人惊叹。在部署过程中,我们看到了以下情况

  • Kubernetes 节点数量:2851
  • CPU 使用率:29 核
  • 内存使用量:57GB
  • 摄取速率:每秒 238K 个样本
  • 每个节点监控的端点:4
  • 监控的每个节点的平均内存使用量:20MB
  • 监控的每个节点的平均 CPU 使用量:0.01 核

在 DaemonSet 模式下监控类似数量端点的单个 Metricbeat 实例消耗约 200MB(10 倍),约 0.6 核(60 倍)。在整个集群中,这将累积到 570GB 和约 1700 个 CPU。通过迁移到集群本地实例,总节省成本约为 90%。

这迫使我们重新思考处理抓取的方法。运行一个针对整个集群的单个实例意味着当该实例进行升级或发生故障时,100% 的抓取将在该时间点中断。为了减轻故障,我们将 Metricbeat 部署为具有 N 个副本的 StatefulSet。整个 Pod 列表根据 Metricbeat 实例的数量分片 N 倍,每个实例监控其分配的分片

xxHash(pod uid) % statefulset_size == instance number

每个实例都会对 API 服务器进行全面扫描,但会忽略除其自身应监控的内容之外的所有内容。这种模式对 Metricbeat 非常有效,因为它主要抓取 Prometheus 端点,并且此活动可以在 Tess 节点之外进行。一个大型的 3000 节点 Kubernetes 集群有 30 多个实例,具有更高的 CPU 和内存数量,使其能够抓取比节点上的守护进程大得多的端点。如果一个 Metricbeat 实例重新启动,抓取只会中断由该实例单独监控的端点,故障百分比降低到总实例数的 1/N。

新的部署模式可以可视化如下

clusterlocal

解耦自动发现

虽然迁移到集群本地抓取使我们能够比使用 DaemonSets 时获得更高的可扩展性,但该模型仍有改进空间。出现了一系列新问题,尤其是在具有更高 Pod 密度的更大集群上。鉴于每个 Metricbeat 实例都必须扫描所有 Pod 并选择它需要监控的 Pod,具体取决于集群中存在的 Pod 数量,初始扫描可能需要很长时间。在滚动更新期间,这变得非常成问题,因为新的 Metricbeat Pod 在恢复正常后需要长达 10 分钟才能恢复抓取。根据实例数量,此技术还会因 Metricbeat 请求的各种资源的 WATCH 数量而对 API 服务器造成不必要的压力。

经过进一步评估,我们决定将自动发现移出代理,而是移到一个独立的控制循环中。该控制循环将

  • 实现与 Beats 自动发现逻辑类似的解析器;
  • 发现所有可以执行抓取工作的代理;
  • 选择其中一个代理;
  • 并将配置传递给选定的代理以监控目标。

该控制循环将做出重要决策,例如在代理崩溃、代理分配过剩和其他故障场景下重新分配工作负载。鉴于解析注解的逻辑已与代理解耦,只要存在 Beats 公开的功能与新代理之间的映射,就可以轻松地为任何代理生成配置。

OpenTelemetry 的出现

2019 年,Open Tracing 和 Open Census 社区同意合并,共同推出 OpenTelemetry。OpenTelemetry 倡议的目标是提供与供应商无关的 API、SDK 和工具,用于摄取、转换和发送数据到任何可观测性后端。投资于这样一项倡议似乎与我们在 eBay 使用开源的方式很契合,因为我们选择了 Kubernetes,它也提供了与供应商无关的 API 来管理云上的容器。2021 年,我们开始尝试分布式追踪,以了解它对我们的开发人员可能有多大用处。

当时,我们查看 OpenTelemetry Collector 的代码库,发现其某些功能具有巨大潜力,包括指标、日志和追踪的定义类型,以及使用 Prometheus 抓取管理器从 OpenMetrics 端点收集指标。我们选择 OpenTelemetry Collector 和 OpenTelemetry SDK 来采用分布式追踪。因此,我们理应随之确定如何将指标和日志收集迁移到 OpenTelemetry Collector。这将是一项艰巨的任务,因为我们需要弥合所有功能差距,与新的开源社区建立关系,并在不停机的情况下更换大规模的指标收集基础设施。2022 年初,我们开始着手将指标抓取迁移到 OpenTelemetry Collector 的艰巨任务。

迁移

鉴于我们已将发现逻辑与代理解耦,实际迁移仅意味着我们需要生成 OpenTelemetry Collector 可以理解的配置。这些配置必须在每次启动新 Pod 时推送,并在 Pod 终止时清理。但是,OpenTelemetry Collector 有一个关键的不足:它无法动态重新加载配置。OpenTelemetry Collector 具有“管道”的概念,用于定义指标如何接收、处理和导出。为了实现管道的动态重新加载,我们创建了一个“filereloadreceiver”,它可以监视一个目录,该目录包含描述“部分管道”的文件,这些文件插入到 collector 的整体管道中。每个需要指标抓取的 Pod 都有一个由自动发现控制器生成并推送到 collector 的部分管道。在此过程中,另一项复杂的任务是创建一张映射表,用于将我们在 Beats 平台中依赖的每个功能与 OpenTelemetry Collector 对应起来。例如,Beats 中的字段将转换为在 OpenTelemetry 中使用属性处理器。有了这些映射和 filereloadreceiver,我们能够生成新的 OpenTelemetry Collector 配置,如下所示。

config

如上所示,我们能够保持 Pod/命名空间注解的最终用户合同不变,并在后台简单地替换代理。这大大简化了推出新代理的实际任务。最后的障碍涉及 Elastic Beats、OpenTelemetry 之间以及有时甚至 Prometheus 抓取管理器之间的语义不匹配。我们在最终替换生产环境中的所有 Metricbeat 之前,在这里花费了数月时间。我们看到的一些差异,并帮助修补了 OpenTelemetry Collector 项目中的问题,包括

这些问题很难发现,有时只有在我们尝试升级 Kubernetes 集群以使用 OpenTelemetry Collector 时才会暴露出来。一旦遇到此类问题,回滚是唯一的选择,我们被迫重新开始。一个部分解决方案是编写一个比较脚本,该脚本可以同时使用 Metricbeat 和 OpenTelemetry Collector 抓取一个端点,将它们摄取到指标存储中,并比较指标名称和标签,以确保抓取工作与它们相匹配。这大大增强了我们前进的信心。

有时前进意味着放弃对某些功能的支持。我们确实就这样做了,放弃了对 Dropwizard 指标的支持,并让用户迁移了。除了语义差异之外,我们还积极致力于添加我们认为对项目至关重要的功能,例如支持 Exemplars

经过数月的辛勤工作和社区的支持,我们很高兴地宣布,我们已完全停用 Metricbeat,并用 OpenTelemetry Collector 取代了它。目前我们正忙于为 Filebeat 做同样的事情,早期的性能基准测试非常有希望。到目前为止,我们已为该项目做出了 10 多项贡献,但这只是一个富有成效的合作的开始。

结论

在过去的五年里,我们 eBay 遇到了几次需求高峰,迫使我们重新思考传统的观念。我们最初使用了 DaemonSets,并发现它在规模化部署时成本过高且不可靠。我们迁移到集群本地模型,将代理成本降低了约 90%,但我们在 API 服务器和代理本身的工作量上存在冗余。我们解耦了发现过程,将其移到一个执行调度工作的控制循环中,并将代理变成了无状态进程,可以接受抓取目标。考虑到 OpenTelemetry 不断成熟,我们转向使用 OpenTelemetry Collector 来处理指标,并积极致力于为日志做同样的事情。我们将继续从大规模运行代理中学习,并根据需要进行调整。我们将继续与 OpenTelemetry 社区合作,因为它继续为可观测性生态系统内的标准化铺平道路。现在我们正在使用 OpenTelemetry,我们可以为开发人员提供行业认可的开放标准,将遥测数据发送到 Sherlock.io。随着社区不断提供对剖析等新功能的 D 支持,我们将把它们整合到平台中,以造福我们的开发人员社区。

致谢

许多思想领袖的参与使得这些优化/方向性改变成为可能

我们非常感谢过去和现在的 Elastic Beats 社区以及 OpenTelemetry 社区,感谢他们在我们努力为 eBay 开发人员社区构建世界一流的可观测性产品时给予的支持和合作。

Elastic 社区

OpenTelemetry Collector 社区

本文的一个版本最初发布在 eBay 技术博客上。