David Hope

使用 OpenTelemetry 手动检测 .NET 应用程序

在本博客中,我们将研究如何使用 OpenTelemetry 手动检测 .NET 应用程序。OpenTelemetry 提供了一组 API、库和代理,用于捕获应用程序的分布式跟踪和指标。您可以在 Elastic 中分析它们。

阅读时间 12 分钟
Manual instrumentation of .NET applications with OpenTelemetry

在快速发展的软件开发领域,尤其是在云原生领域,DevOps 和 SRE 团队正日益成为应用程序稳定性和增长的重要合作伙伴。

DevOps 工程师不断优化软件交付,而 SRE 团队则充当应用程序可靠性、可扩展性和顶级性能的管理者。挑战是什么?这些团队需要一个先进的可观测性解决方案,该解决方案应包含全栈洞察,使他们能够在潜在的中断演变为运营挑战之前快速管理、监控和纠正它们。

在我们现代化的分布式软件生态系统中,可观测性不仅仅是监控,它还需要无限的数据收集、精确的处理,以及将这些数据关联为可操作的洞察。然而,实现这种整体视图的道路上充满了障碍,从处理版本不兼容问题到与受限制的专有代码作斗争。

加入 OpenTelemetry (OTel),它为采用者带来以下好处

  • 借助 OTel 摆脱供应商的限制,让您摆脱供应商锁定并确保一流的可观测性。
  • 了解统一的日志、指标和跟踪如何协调一致,以提供完整的系统视图。
  • 通过更丰富、更完善的检测来改进您的应用程序监督。
  • 接受向后兼容性的好处,以保护您之前的检测投资。
  • 踏上 OpenTelemetry 之旅,上手难度低,简化了入职和可扩展性。
  • 依靠经过验证的、面向未来的标准,增强您对每项投资的信心。
  • 探索手动检测,从而实现定制的数据收集以满足您的独特需求。
  • 使用标准化的可观测性数据框架,确保跨层级的监控一致性。
  • 将开发与运营解耦,从而推动两者实现最高效率。

在本文中,我们将深入探讨如何使用 Docker 手动检测 .NET 应用程序的方法。

涵盖内容?

  • 手动检测 .NET 应用程序
  • 为 .NET 应用程序创建一个包含内置 OpenTelemetry 检测的 Docker 镜像
  • 安装和运行 OpenTelemetry .NET Profiler 以进行自动检测

先决条件

  • 对 Docker 和 .NET 的了解
  • Elastic Cloud
  • 安装在您的机器上的 Docker(我们建议使用 docker desktop)

查看示例源代码

完整的源代码(包括本博客中使用的 Dockerfile)可在 GitHub 上找到。存储库还包含未检测的相同应用程序。这使您可以比较每个文件并查看差异。

以下步骤将向您展示如何检测此应用程序并在命令行或 Docker 中运行它。如果您对更完整的 OTel 示例感兴趣,请查看此处的 docker-compose 文件,它将启动整个项目。

分步指南

本博客假设您有一个 Elastic Cloud 帐户 — 如果没有,请按照说明在 Elastic Cloud 上开始使用

步骤 1. 开始

在我们的演示中,我们将手动检测一个 .NET Core 应用程序 - Login。此应用程序模拟一个简单的用户登录服务。在此示例中,我们仅关注跟踪,因为如此处所述,OpenTelemetry 日志记录检测目前处于混合成熟度状态。

该应用程序具有以下文件

  1. Program.cs

  2. Startup.cs

  3. Telemetry.cs

  4. LoginController.cs

步骤 2. 检测应用程序

在涉及到 OpenTelemetry 时,.NET 生态系统呈现出一些独特的方面。虽然 OpenTelemetry 提供了自己的 API,但 .NET 利用其原生 System.Diagnostics API 来实现 OpenTelemetry 的跟踪 API。预先存在的构造(例如 ActivitySourceActivity)被适当地重新用于符合 OpenTelemetry。

也就是说,了解 OpenTelemetry API 及其术语对于 .NET 开发人员仍然至关重要。这对于完全掌控您的应用程序的检测至关重要,而且正如我们所见,它还扩展到理解 System.Diagnostics API 的元素。

对于那些可能倾向于使用原始 OpenTelemetry API 而不是 System.Diagnostics API 的人,也有一种方法。OpenTelemetry 提供了一个用于跟踪的 API shim,您可以使用它。它使开发人员可以切换到 OpenTelemetry API,您可以在 OpenTelemetry API Shim 文档中找到有关它的更多详细信息。

通过将此类实践集成到您的 .NET 应用程序中,无论您使用的是 OpenTelemetry 的 API 还是 System.Diagnostics API,您都可以充分利用 OpenTelemetry 提供的强大功能。

在本博客中,我们将坚持默认方法并使用 System.Diagnostics API 规定的 Activity 约定。

要手动检测 .NET 应用程序,您需要在每个文件中进行更改。让我们逐一查看这些更改。

Program.cs

这是我们应用程序的入口点。在这里,我们使用默认配置创建一个 IHostBuilder 实例。请注意我们如何使用 Serilog 设置控制台记录器。

public static void Main(string[] args)
{
    Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateLogger();
    CreateHostBuilder(args).Build().Run();
}

Startup.cs

Startup.cs 文件中,我们使用 ConfigureServices 方法添加 OpenTelemetry 跟踪。

public void ConfigureServices(IServiceCollection services)
{
    services.AddOpenTelemetry().WithTracing(builder => builder.AddOtlpExporter()
        .AddSource("Login")
        .AddAspNetCoreInstrumentation()
        .AddOtlpExporter()
        .ConfigureResource(resource =>
            resource.AddService(
                serviceName: "Login"))
    );
    services.AddControllers();
}

WithTracing 方法启用 OpenTelemetry 中的跟踪。我们添加了 OTLP(OpenTelemetry 协议)导出器,它是一种通用的遥测数据传输协议。我们还添加了 AspNetCoreInstrumentation,它将自动从我们的应用程序收集跟踪。这是一个至关重要的步骤,OpenTelemetry 文档中没有提及。如果不添加此方法,我发现该检测对于登录应用程序不起作用。

Telemetry.cs

此文件包含我们的 ActivitySource 的定义。ActivitySource 表示遥测活动的来源。它以应用程序的服务名称命名,并且此名称可以来自配置文件、常量文件等。我们可以使用此 ActivitySource 来启动活动。

using System.Diagnostics;

public static class Telemetry
{
    //...

    // Name it after the service name for your app.
    // It can come from a config file, constants file, etc.
    public static readonly ActivitySource LoginActivitySource = new("Login");

    //...
}

在我们的例子中,我们创建了一个名为 LoginActivitySource。在我们的 LoginController.cs 中,我们使用此 LoginActivitySource 在我们开始操作时启动一个新活动。

using (Activity activity = Telemetry.LoginActivitySource.StartActivity("SomeWork"))
{
    // Perform operations here
}

这段代码启动了一个名为 SomeWork 的新活动,执行一些操作(在本例中,生成一个随机用户并使其登录),然后结束该活动。这些活动会被跟踪,并可以在以后进行分析,以了解操作的性能。

这个 ActivitySource 是 OpenTelemetry 手动检测的基础。它代表活动的来源,并提供一种启动和停止活动的方法。

LoginController.cs

LoginController.cs 文件中,我们正在跟踪 GET 和 POST 方法执行的操作。在开始操作之前,我们启动一个新的活动 SomeWork ,并在操作完成后将其释放。

using (Activity activity = Telemetry.LoginActivitySource.StartActivity("SomeWork"))
{
    var user = GenerateRandomUserResponse();
    Log.Information("User logged in: {UserName}", user);
    return user;
}

这将跟踪这些操作所花费的时间,并通过 OTLP 导出器将此数据发送到任何配置的遥测后端。

步骤 3. 基础镜像设置

现在我们已经创建并检测了应用程序源代码,是时候创建一个 Dockerfile 来构建和运行我们的 .NET 登录服务了。

从 Dockerfile 的基础层使用 .NET 运行时镜像开始

FROM ${ARCH}mcr.microsoft.com/dotnet/aspnet:7.0. AS base
WORKDIR /app
EXPOSE 8000

在这里,我们正在设置应用程序的运行时环境。

步骤 4. 构建 .NET 应用程序

Docker 的这个功能简直太棒了。在这里,我们编译 .NET 应用程序。我们将使用 SDK 镜像。在过去,我们通常在不同的平台上进行构建,然后将编译后的代码放入 Docker 容器中。这样,通过从头到尾使用 Docker,我们可以更加确信我们的构建将从开发人员的桌面复制到生产环境中。

FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-preview AS build
ARG TARGETPLATFORM

WORKDIR /src
COPY ["login.csproj", "./"]
RUN dotnet restore "./login.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "login.csproj" -c Release -o /app/build

本节确保我们的 .NET 代码被正确地还原和编译。

步骤 5. 发布应用程序

构建完成后,我们将发布该应用程序

FROM build AS publish
RUN dotnet publish "login.csproj" -c Release -o /app/publish

步骤 6. 准备最终镜像

现在,让我们设置最终的运行时镜像

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .

步骤 7. 入口点设置

最后,设置 Docker 镜像的入口点,以同时获取 OpenTelemetry 检测,这将设置引导 .NET 分析器所需的环境变量,然后我们启动我们的 .NET 应用程序

ENTRYPOINT ["/bin/bash", "-c", "dotnet login.dll"]

步骤 8. 使用环境变量运行 Docker 镜像

要构建和运行 Docker 镜像,您通常需要遵循以下步骤

构建 Docker 镜像

首先,您需要从您的 Dockerfile 构建 Docker 镜像。假设 Dockerfile 位于当前目录中,并且您想将您的镜像命名/标记为 dotnet-login-otel-image。

docker build -t dotnet-login-otel-image .

运行 Docker 镜像

构建镜像后,您将使用指定的环境变量运行它。为此,可以使用 docker run 命令,并为每个环境变量使用 -e 标志。

docker run \
       -e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer ${ELASTIC_APM_SECRET_TOKEN}" \
       -e OTEL_EXPORTER_OTLP_ENDPOINT="${ELASTIC_APM_SERVER_URL}" \
       -e OTEL_METRICS_EXPORTER="otlp" \
       -e OTEL_RESOURCE_ATTRIBUTES="service.version=1.0,deployment.environment=production" \
       -e OTEL_SERVICE_NAME="dotnet-login-otel-manual" \
       -e OTEL_TRACES_EXPORTER="otlp" \
       dotnet-login-otel-image

请确保

${ELASTIC_APM_SECRET_TOKEN}
${ELASTIC_APM_SERVER_URL}
已在您的 shell 环境中设置,请将其替换为云中显示的实际值,如下所示。

获取 Elastic Cloud 变量
您可以从 Kibana 的以下路径复制端点和令牌

/app/home#/tutorial/apm
.

如果您有多个环境变量,也可以使用带 docker run --env-file 的环境文件,以使命令不那么冗长。

一旦您启动并运行了此操作,您就可以 ping 您检测服务的端点(在我们的例子中,这是 /login),您应该在 Elastic APM 中看到该应用程序,如下所示

它将首先跟踪吞吐量和延迟等对 SRE 来说至关重要的指标。

深入研究,我们可以看到所有事务的概述。

并查看特定的事务,包括我们在上面的代码中创建的“SomeWork”活动/span

这里显然有一个异常值,其中一个事务花费了超过 20 毫秒。这可能是由于 CLR 预热造成的。

总结

通过对这里的代码进行检测以及使用 Dockerfile 引导应用程序,您已将简单的 .NET 应用程序转换为使用 OpenTelemetry 进行检测的应用程序。这将极大地帮助您了解应用程序性能、跟踪错误并深入了解用户如何与您的软件进行交互。

请记住,可观测性是现代应用程序开发的关键方面,尤其是在分布式系统中。借助 OpenTelemetry 等工具,理解复杂的系统变得稍微容易一些。

在本博客中,我们讨论了以下内容

  • 如何使用 OpenTelemetry 手动检测 .NET。
  • 在 Docker 文件中使用标准命令,构建并启动了我们检测的应用程序。
  • 通过 OpenTelemetry 及其对多种语言的支持,DevOps 和 SRE 团队可以轻松地检测他们的应用程序,从而立即深入了解整个应用程序堆栈的运行状况,并减少平均修复时间 (MTTR)。

由于 Elastic 可以支持多种数据摄取方法,无论是使用开源 OpenTelemetry 的自动检测还是使用其原生 APM 代理的手动检测,您都可以通过首先关注一些应用程序,然后在以后以最适合您业务需求的方式在您的应用程序中使用 OpenTelemetry 来计划迁移到 OTel。

开发者资源

通用配置和用例资源

还没有 Elastic Cloud 帐户?注册 Elastic Cloud 并试用我在上面讨论的检测功能。我很有兴趣了解您在使用 Elastic 深入了解您的应用程序堆栈方面的体验反馈。

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