Damien Mathieu

从 Elastic 的 Go APM 代理迁移到 OpenTelemetry Go SDK

随着 OpenTelemetry 快速成为行业标准,Elastic 也在快速采用它。在这篇文章中,我们将向您展示一种安全且简单的方法,将您的 Go 应用程序从我们的 APM 代理迁移到 OpenTelemetry。

7 分钟阅读
Migrating from Elastic’s Go APM agent to OpenTelemetry Go SDK

正如我们已经分享的那样,Elastic 致力于帮助 OpenTelemetry (OTel) 取得成功,这意味着在某些情况下,需要构建语言 SDK 的发行版。

Elastic 在战略上将 OTel 标准化用于可观测性和安全数据收集。此外,Elastic 致力于与 OTel 社区合作,成为可观测性生态系统的最佳数据收集基础设施。Elastic 正在深化与 OTel 的关系,这超越了最近对 Elastic Common Schema (ECS) 对 OpenTelemetry 的贡献OTel Java 代理中的 invokedynamic,以及 即将进行的分析代理捐赠

自 Elastic 7.14 版本起,Elastic 通过能够直接摄取基于 OpenTelemetry 协议 (OTLP) 的跟踪、指标和日志,从而原生支持 OTel。

Go SDK 与其他语言 SDK 有些不同,因为 Go 语言本身缺乏允许构建非 fork 发行版的动态性。

尽管如此,缺少发行版并不意味着您不应该使用 OTel 从具有 Elastic Stack 的 Go 应用程序收集数据。

Elastic 目前有一个 APM Go 代理,但我们建议切换到 OTel Go SDK。在这篇文章中,我们将介绍两种您可以进行迁移的方式

  • 通过替换应用程序代码中的所有遥测(“大爆炸式迁移”)并发布更改

  • 通过将迁移拆分为原子更改,以降低回归风险

大爆炸式迁移

从我们的 APM Go 代理迁移到 OTel SDK 的最简单方法可能是删除代理提供的所有遥测,并将其全部替换为新的遥测。

自动检测

您的大部分检测可能会自动提供,因为它属于您正在使用的框架或库的一部分。

例如,如果您使用 Elastic Go 代理,您可能正在使用我们的 net/http 自动检测模块,如下所示

import (
	"net/http"
	"go.elastic.co/apm/module/apmhttp/v2"
)


func handler(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func main() {
	http.ListenAndServe(
                  ":8080",
                  apmhttp.Wrap(http.HandlerFunc(handler)),
	)
}

使用 OpenTelemetry,您将改为使用 otelhttp 模块

import (
	"net/http"
	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)


func handler(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func main() {
	http.ListenAndServe(
                  ":8080",
                  otelhttp.NewHandler(http.HandlerFunc(handler), "http"),
	)
}

您应该对您从我们的代理使用的每个其他模块执行相同的更改。

手动检测

您的应用程序也可能具有手动检测,这包括通过调用 Elastic APM 代理 API 直接在应用程序代码中创建跟踪和 span。

您可以使用 Elastic 的 APM SDK 创建事务和 span,如下所示

import (
	"go.elastic.co/apm/v2"
)

func main() {
       // Create a transaction, and assign it to the context.
       tx :=  apm.DefaultTracer().StartTransaction("GET /", "request")
       defer tx.End()
       ctx = apm.ContextWithTransaction(ctx, tx)

       // Create a span
       span, ctx := apm.StartSpan(ctx, "span")
       defer span.End()
}

OpenTelemetry 对事务和 span 使用相同的 API——Elastic 认为的“事务”在 OTel 中只是没有父级的 span(“根 span”)。

因此,您的检测变为以下内容

import (
	"go.opentelemetry.io/otel/trace"
)

func main() {
	tracer := otel.Tracer("my library")

	// Create a root span.
	// It is assigned to the returned context automatically.
	ctx, span := tracer.Start(ctx, "GET /")
	defer span.End()

	// Create a child span (as the context has a parent).
	ctx, span := tracer.Start(ctx, "span")
	defer span.End()
}

通过大爆炸式迁移,您需要在发布到生产环境之前迁移所有内容。您无法将迁移拆分为更小的块。

对于小型应用程序或仅使用自动检测的应用程序,这种约束可能没问题。它可以让您快速验证迁移并继续前进。

但是,如果您正在处理复杂的服务集、大型应用程序或具有大量手动检测的应用程序,您可能希望能够在迁移期间多次发布代码,而不是一次全部发布。

原子迁移

原子迁移是指您可以逐步发布原子更改,并让您的应用程序正常运行。然后,您可以在最后,一旦您准备好这样做,才能够拔下最终的插头。

为了帮助进行原子迁移,我们提供了我们的 APM Go 代理和 OpenTelemetry 之间的桥梁

此桥梁允许您同时运行我们的代理和 OTel,并在同一进程中使用两个库进行检测,数据以相同的格式传输到相同的位置。

您可以使用我们的代理配置 OTel 桥梁,如下所示

import (
	"go.elastic.co/apm/v2"
	"go.elastic.co/apm/module/apmotel/v2"

	"go.opentelemetry.io/otel"
)

func main() {
	provider, err := apmotel.NewTracerProvider()
	if err != nil {
		log.Fatal(err)
	}
	otel.SetTracerProvider(provider)
}

设置此配置后,OTel 创建的每个 span 都将传输到 Elastic APM 代理。

使用此桥梁,您可以通过以下过程使迁移更加安全

  • 将桥梁添加到您的应用程序。

  • 将一项检测(自动或手动)从代理切换到 OpenTelemetry,就像您在上面的大爆炸式迁移中一样,但一次只切换一项。

  • 删除桥梁和我们的代理,并配置 OpenTelemetry 以通过其 SDK 传输数据。

这些步骤中的每一个都可以在您的应用程序中进行单次更改,并立即投入生产。

如果在迁移过程中出现任何问题,您应该能够立即看到并修复它,然后再继续。

使用 OTel 构建的可观测性优势

由于 OTel 正在快速成为行业标准,并且 Elastic 致力于使其变得更好,因此迁移到 OTel 对您的工程团队来说非常有益。

在 Go 中,无论您是通过大爆炸式迁移还是使用 Elastic 的 OTel 桥梁来完成此操作,这样做都将使您能够从全球社区维护的检测中受益,从而使您的可观测性更加有效,并更好地了解应用程序中正在发生的事情。

本帖子中描述的任何特性或功能的发布和时间安排均由 Elastic 自行决定。任何目前不可用的特性或功能可能不会按时或根本不交付。

分享这篇文章