终端用户问答系列: 在 Lightstep 迁移到 OTel

由 Adriana Villela(Lightstep 自 ServiceNow)提供帮助。

在 2023 年第四次 OpenTelemetry(OTel)终端用户工作组的终端用户问答会话中,我们与 Jacob Aronoff 进行了交谈,他是Lightstep 自 ServiceNow的高级软件工程师,也是 OpenTelemetry Operator 维护者。如果您有兴趣了解供应商如何在内部使用 OTel,请继续阅读!

这个系列的采访是与一个团队进行的月度非正式讨论,该团队正在将 OpenTelemetry 用于生产环境。我们的目标是与社区分享他们在这方面所学到的知识,包括他们的成功和挑战,以便我们可以共同改进 OpenTelemetry。

概述

在本次会话中,Jacob 分享了以下内容:

  • 他如何从 OpenTracing 和 OpenCensus 迁移到 OpenTelemetry
  • TargetAllocator 是什么,以及他今天如何使用它
  • 为什么您可能不想将您的 Collector 部署为 sidecar

采访内容

背景故事

Jacob 在 Lightstep 自 ServiceNow 的 Telemetry Pipeline 团队已经工作了将近两年了。他在第一年专注于 OTel 迁移,包括内部迁移以及让他们的客户能够更容易地迁移。

他说:“加入团队的时候,我们的跟踪还在使用 OpenTracing,度量方面则是使用 OpenCensus 和一些自定义的 Statsd。这意味着我们必须在每个 Kubernetes Pod 上运行一个代理(在每个 Pod 上作为 sidecar 存在的代理,这意味着您必须运行另一个将从 Statsd 读取指标然后转发给 Lightstep 的应用程序)。”

这是在 OpenTelemetry 的度量版本候选者刚刚宣布的时候,他将其视为一个机会:“我们有一个内部的 OTel 团队,他们一直在研究它并希望能获得一些关于如何改进它的即时反馈,所以我决定为我们的环境进行迁移。”他说。

OpenCensus 度量迁移

由于之前有类似的迁移经验,他最初计划让它尽可能安全。由于他们在一个单体仓库中,他们本可以一次性完成所有迁移,但这可能会有风险,可能会存在一个 bug 被推送上去的问题。

Jacob 说:“这是我们用于告警的应用程序数据,用于了解我们在所有环境中的工作负载运行情况,因此不能关闭它,这将是灾难性的。对用户来说,他们想要知道如果他们迁移到 OTel,他们的告警功能是否还存在。你希望一种安全而简单的迁移方式。”

他的团队在 Kubernetes 中通过基于特性标记的方式实现了配置的一部分。他说:“它会禁用 sidecar 并启用一些代码,然后将 OTel 换成度量指标并将其转发到相应的位置。这就是路径。”。

然而,在此过程中,他在他们用于监控公共环境的环境中进行测试时,注意到了一些“相当大的性能问题”。他与 OTel 团队合作解决了其中一些问题,并发现其中一个阻碍因素是他们对度量指标属性的大量使用。

他说:“耐心地进入其中并找出使用这些属性的度量指标并消除它们是很麻烦的。我有一个理论,认为其中一个代码路径是问题所在。在这个路径中,我们正在从我们内部的标签实现转换为 OTel 标签,这涉及到很多其他逻辑,而且这是一种昂贵的操作,并且几乎在每次调用中都要进行。现在开始 OpenCensus 到 OTel 的另一次迁移再好不过了。”

他将此视为另一个机会:“在我们等待 OTel 在度量方面推出更高性能的代码和实现的同时,我们也可以测试另一个理论,即如果我们完全迁移到 OTel,我们将获得更多的性能益处。”因此,他们暂停了度量方面的工作,并开始迁移跟踪方面的工作。

OpenTracing 迁移

对于跟踪,Jacob 决定尝试“全面迁移的方法”。从 OpenTracing 到 OTel 的路径更为明确,有一些文档和示例可以参考。此外,他说:“它们是向后兼容的,您可以在正确设置传播器的情况下同时使用它们。”

在正确设置传播器之后,他们确保所有他们的插件(现已开源)都能正常工作。虽然他们在托管环境中有几次回滚操作,但除了他错过的一个 bug 外,没有遇到其他重大问题。

他说:“我必须实现一个定制的采样器,这在 OTel 上比用 OpenTracing 简单十倍。我能够删除一千行代码和一些危险的 Hack,这是非常好的事情。”

如何开始迁移

Jacob 说:“我从我们团队拥有且流量很低但足够持续的一些小服务开始。您选择这样的服务的原因是,如果流量太低,例如每 10 分钟一个请求,您就必须担心采样率,并且可能没有足够的数据进行比较,数据对比是非常重要的。”

他从早期就编写了一个脚本,用于度量迁移,该脚本查询了所有度量的不同构建标签。如果新的构建标签的标准差相对于上一个版本大于 1,这可能会表示有问题的问题。

Jacob 提到:“我还需要检查迁移前后所有属性是否仍然存在,这也是需要考虑的。有时候,它们不在原有的位置,例如 Statsd 自动添加了他们不关心的一些属性,这些属性可以安全地忽略。”

对于跟踪,Jacob 说:“我选择了一个既包含仅在单个服务内部的跟踪,又包含涵盖多个带有不同类型仪表板的服务的跟踪的服务,例如从 Envoy 到 OTel 再到 OpenTracing。”

他解释说:“你想看到的是,迁移前后的跟踪具有相同的结构。因此,我又编写了另一个脚本,检查这些结构是否相对类似,并且它们都具有相同的属性。这就是跟踪迁移的目的 - 最重要的是属性保持不变。”

数据丢失的原因

Jacob 说:“为什么数据丢失这个故事变得非常复杂。”有时候,这很简单,只是“忘记在某个地方添加某些东西”,但其他时候,可能是一个上游库没有按照您对 OTel 的预期发出的数据。

他分享了一个关于迁移他们的 gRPC util 包的故事(现在位于 Go contrib 中),并发现了一个传播问题。

他分享道:“我试图理解出了什么问题。当我看代码时 - 它告诉你我当时进行这个迁移的早期有多早 - 那个应该作为一个传播器存在的地方,却只有一个’TODO’,它使我们的服务跟踪完全失效。”

他花了一些时间来解决这个问题,但他们转而等待其他事情的发展,来回等待,无休止的循环。最终,一旦解决了问题,他将其上游化,使其对社区可用。

他说:“大多数度量工作都对 OTel 度量产生了很大的性能提升,例如 OTel Go 度量工具。它还为 Statsd 的人提供了一些关于各种功能应该具有多少描述性的想法。因此,我们在迁移的早期大量使用了 Views。”

指标视图

Jacob 解释说:“Metrics View 是在 OTel 的 Meter Provider 内部运行的一种组件。”它有许多配置选项,例如删除属性,这是最常见的用例之一。“例如,您是在集中的 SRE 环境工作,不希望任何人在代码中添加用户 ID 属性,因为那是一个高基数的属性,而且会使您的度量成本爆炸。您可以创建一个 View 并将其添加到您的仪表板上,然后告诉视图不要记录它,不要允许它。”

还有更高级的用例,例如动态更改指标的时间性或聚合方式。时间性指的是度量是否包含先前的测量值(累积和增量),聚合方式指的是如何发送指标数据。

Jacob 说:“这对我们的直方图非常有用。当记录直方图时,有几种不同的类型 - DataDog 和 Statsd 直方图不是真正的直方图,因为它们记录的是聚合样本。它们会给您提供最小值、最大值、计数、平均值和 P95 等。问题在于,在分布式计算中,如果有多个应用程序报告了 P95,您无法从这些观察中获取真正的 P95。您需要有关原始数据的某些信息来重新计算它。您可以获取所有 P95 的平均值,但那不是很好的指标,它并不能告诉您很多。它没有很准确。如果你想在某些情况下报警并在晚上给某人发短信,你应该基于准确的度量值进行报警。”

最初,他们确实有一些人依赖于最小值、最大值、求和、计数的仪表,因此他们在 Metrics SDK 中使用 View 配置了自定义聚合或直方图,以发出分布式或 OTel 的指数直方图。“我们是双重发射的;这是可行的,因为它们是不同的度量指标名称,因此没有重叠。”

在完成迁移后,他们能够返回到使用最小值、最大值、求和和计数的任何仪表板或警报,并将其改为使用直方图。“由于我们在过去的几个星期、几个月中在公共环境中运行了 OTel 为度量指标,这是可以实现的。”Jacob 说:“这是关键的特性之一,因为它非常简单、方便。我们不需要引入任何其他组件,这非常棒。”

日志和跨度事件

当 Jacob 开始 OTel 迁移时,对于日志来说还为时过早。他说:“我们会改变的是如何收集这些日志,可能之前我们通过在 GKE 集群的每个节点上运行 fluentbit,然后将其发送到 GCP,我们在那里进行追踪。”他指出,他目前可能还不知道这方面是否有最新的变化。

他说:“我们很长一段时间都在内部使用跨度事件和日志进行很多事情。我很喜欢它们。”他对记录日志不太感兴趣,认为它们“麻烦且昂贵”。他建议用户在可能的情况下选择跟踪和跟踪日志,尽管对于本地开发,他确实喜欢日志记录,对于分布式开发,他喜欢跟踪。

在 Kubernetes 中的遥测收集

Kubernetes 现在具有原生发出 OTel 跟踪的功能,Jacob 对此很感兴趣,想知道从那里获得的跟踪是否足以用于使用 spanmetrics processor 对 Kubernetes 生成更好的度量指标。

注意: spanmetrics processor 已停用,应改为使用 spanmetrics connector

他说:“我非常关注基础架构度量,例如 Kubernetes 基础设施度量,我发现它们在当前形式下非常痛苦。”目前,他正在使用 Prometheus API 进行收集,这是观测社区中的普遍方式,因为 Kubernetes 已经原生支持这种方式。

“这是我们目前做的,我使用了一套名为“Target Allocator”的 OTel 组件来分发这些目标,这是一种获取所有数据的相当有效的方式。” Jacob 说。

“我们还使用守护进程集,它们在我们的集群中运行,除此之外,这也非常有效。令人沮丧的是 Prometheus,Prometheus 数据采集值可能是一个非常常见的问题,当您不得不同时担心度量基数时,它变得非常烦人。”

Target Allocator

Jacob 说:“Target Allocator 是 OTel Kubernetes Operator 的一部分,它可以做一些 Prometheus 不能做的事情,即在一组监视器之间动态分配目标。” Target Allocator 不需要运行 Prometheus 实例;但是,为了让 Target Allocator 选取到 Prometheus CRDs,需要存在 Prometheus CRDs。

来自文档

为了使 Allocator 可以选择它们,Prometheus CRDs 也必须存在。最好的方法是从 prometheus-operator 中获取它们:Releases。只需要部署 Allocator 监视的 CRs 的 CRDs。可以从 bundle.yaml 文件中挑出它们。

他继续解释说,虽然 Prometheus 有一些试验性的 sharding 功能,但您在查询时仍会有一个问题,因为 Prometheus 不仅仅是一个采集器,还是一个数据库。在这些 Prometheus 实例之间必须做一些协调,这可能会很昂贵,或者使用 Prometheus 缩放解决方案,例如 ThanosCortex – 但这将涉及到运行更多的组件,您将需要监控这些组件。

“在 OTel 中,我们附加了一个 Prometheus receiver 来获取所有这些数据,但因为我们希望比 Prometheus 更高效,因为我们不需要存储数据,我们有这个被称为 Target Allocator 的组件,它来自 Prometheus 执行服务发现。” Jacob 说:“它告诉我你给我需要采集的所有目标。然后 Target Allocator 会说:对于这些目标,将它们均匀地分配给正在运行的一组收集器。”

这就是该组件的主要功能,它还有助于作业发现。如果您正在使用 Prometheus 服务监视器(Prometheus operator 的一部分),这是在您的集群中运行 Prometheus 的一种流行方式,“Target Allocator 也可以获取这些服务监视器,并更新监视器和采集配置。”

Jacob 的团队不运行任何 Prometheus 实例 - 他们只运行 Collector,并将 Prometheus receiver 的数据发送到 Lightstep。“这很好。”他说。

他的团队过去运行过 Prometheus sidecar,作为 Prometheus 安装的一部分运行。然后,它会在与 Prometheus 实例相同的 Pod 中运行,并读取 Prometheus 用于持久性和批处理的写入日志。但是,如果您的 Prometheus 实例很吵,效率会很低。 Jacob 说:“Collector 是运行它的最佳方式。”

Collector 设置

Jacob 的团队在 Lightstep 运行了许多不同类型的 Collector。“我们运行度量指标的 Collector,跟踪的 Collector,内部的 Collector,外部的 Collector - 运行着许多不同的 Collector,一直都是如此。”他分享。

“一切都在飞速变化中。” 他们经常改变东西来运行实验,因为为客户和最终用户创建功能的最佳方式是确保它们在公司内部运行良好。

“我们正在单一路径中前进,在不同的环境中可能运行两个 Collector,它们可能运行两个不同的图像和两个不同的版本。这变得非常深奥和令人困惑,如果您试图谈论它,会让人感到非常困惑。” 他说:“然后,如果您将 Collector A 发送到环境 B 上的 Collector B,Collector B 本身也会发出关于自己的遥测数据,这将被 Collector C 收集,形成一个链。”

简而言之,您需要确保 Collector 实际上正在工作。“这是我们在调试这些问题时遇到的困扰之处。当问题出现时,您必须考虑问题实际发生在哪里 - 是在我们收集数据的方式中发生的问题,还是在我们发出数据的方式中发生的问题,还是在数据生成源头的问题?可能是几个问题中的一个。”

OTel上的Kubernetes模式

OTel Operator在Kubernetes中支持OTel Collector的四种部署模式:

您应该使用哪种模式取决于您需要做什么,例如如何以确保可靠性运行应用程序。

“边车(Sidecar)是我们最少使用的,如果我要打赌的话,整个行业中它可能也是最少使用的。”Jacob说道。“它们很昂贵。如果您不是非常需要它们,那么就不应该使用它们。”边车的一个示例是Istio,“将它作为边车运行是非常合理的,因为它会代理流量,并且会连接到您的容器网络以改变其工作方式。”

如果您将所有服务的Collector都作为边车,您将面临成本问题,并且功能受到一定限制。他表示,“如果您正在进行Kubernetes API调用或属性增强,那将变得非常昂贵。”他分享了一个例子:“…如果您有10,000个pod上的边车[使用 k8sattributesprocessor],那就是向K8s API进行了10,000次API调用。那是非常昂贵的。”

另一方面,如果您在StatefulSet上部署了五个pod,“那就不是那么昂贵了。”当您以StatefulSet模式运行时,您会得到一定数量的副本,每个副本都有可预测的名称-这是“当您需要一致的ID时非常有价值的事情。”

由于有了一致的ID,您可以在Target Allocator中做一些额外的工作,这也是为什么它是必需的。StatefulSet保证的另一件事是所谓的原地部署(in-place deployment),这也适用于DaemonSets;在创建新pod之前,您需要将pod关闭。

“在部署中,通常进行1个上线、1个下线,或者所谓的滚动部署。”Jacob说道。如果您在使用Target Allocator时这样做,您可能会得到更不可靠的收集结果。这是因为当新的副本出现时,您必须重新分配所有目标,因为您放置在hash环上的目标已发生改变,需要重新计算所有已分配的哈希。

而对于StatefulSets,这是不必要的,因为您会得到一个一致的ID范围。“因此,当您进行1个下线、1个上线操作时,它会保持相同的目标。所以就好像为其保留了一个位置-您不需要重新计算哈希环,”他解释道。

他指出,这实际上只对指标用例有用,其中您正在收集Prometheus数据。他指出,对于其他任何情况,他们可能会将其作为Deployment运行,因为该模式提供了大部分所需功能。Collector通常是无状态的,因此它们不需要保存任何内容,并且作为结果,部署更加精简。“您只需运行和部署,大家都很满意。”他说道。“我们大多数的收集器都是这样运行的,只是作为一个Deployment。”

对于每个节点收集数据,DaemonSets非常有用。“这使您能够收集在每个节点上运行的kubelet数据,还允许您收集在每个节点上运行的节点导出器(node exporter),它是另一个大多数人运行的Prometheus DaemonSet。”他解释道。

在进行扩展时,DaemonSets非常有用,因为它们保证在与其选择器匹配的每个节点上都有运行的pod。“如果您有一个超过800个节点的集群,通过运行许多小的收集器来收集这些小规模的指标会更可靠,而不是运行几个较大的StatefulSet pod,因为这样可以降低影响范围,”他说道。

“如果一个pod出现故障,您只会丢失一小部分数据,但请记住,在所有这些唯一值方面,这是大量的内存。因此,如果您正在进行StatefulSet,收集所有这些节点的数据,那就需要大量的目标、大量的内存,它很容易停下来,您可能会丢失更多的数据。”

如果Collector出现故障,它会很快恢复,因为它通常是无状态的,这意味着“通常中断很短,”Jacob说道。但是,如果您过度饱和,中断“可能会更加不稳定,可能会快速升起又快速下降。”因此,建议使用水平pod自动扩展器(HPA)。

从度量衡的角度来看,这对于追踪工作负载也非常有用。由于是基于推送的,它们要容易得多进行扩展,并且可以分发目标和负载均衡。

“基于拉取的方式是Prometheus如此广泛使用的原因之一…因为它使得本地开发变得非常容易,您只需要抓取本地的端点,这就是大多数后端开发的方式。”他说道。“您可以访问端点A,然后访问您的指标端点。再次访问端点A,然后再次访问指标端点,然后检查一下,这是一个简单的开发循环。它还意味着您不必向网络外部发送数据,所以如果您在发送数据时有非常严格的代理要求,那么本地开发就容易得多。这就是为什么OTel现在有一个非常好的Prometheus导出器,因此它可以同时进行两种操作。”

集中式OTel Collector网关

正在进行中的一个项目是集中式网关,它是Jacob之前提到的Collector链的一部分。这个项目的重点在于Arrow。Lightstep已经在改进“通过使用Apache Arrow来提高OTel数据的处理速度和进入成本,Arrow是一个基于列的数据表示项目,”Jacob解释道。

他们目前正在进行一些实现验证以研究其性能,并确认一切都按预期工作。

保持遥测数据的最新状态

Jacob指出,保持遥测数据的最新状态非常重要,因为库的作者和维护者总是在努力开发新的性能功能并改进软件。

“这也使得迁移变得容易。尝试从某个早期版本迁移到最新版本时,您可能会错过很多破坏性的变化,并且您必须小心。”他说道。

他建议使用Dependabot,他们在OTel中使用它。OTel包以步调一致的方式更新,这意味着您必须一次更新“相当数量的软件包,但它会为您完成所有工作,这很好。”然而,您应该对所有依赖项都这样做,因为“在行业中,CVE漏洞修复不断发生。如果您不始终保持漏洞修复的最新状态,您会面临安全攻击风险,这是您不希望的。我的建议是‘采取行动。’”

其他资源

总结

OpenTelemetry重视社区,没有了我们的贡献者、维护者和用户,我们就不会走到今天。我们重视用户反馈-请分享您的经验,帮助我们改进OpenTelemetry。

以下是与我们联系的方式:

记得在Mastodon上关注OpenTelemetry](https://fosstodon.org/@opentelemetry)和Twitter,并使用**#OpenTelemetry**标签分享您的故事!