David Hope

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

OpenTelemetry 为云原生软件提供了一个可观测性框架,使我们能够无缝地跟踪、监控和调试应用程序。在本文中,我们将探讨如何使用 OpenTelemetry 自动检测 .NET 应用程序。

阅读时长 13 分钟
Auto-instrumentation of .NET applications with OpenTelemetry

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

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

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

使用 OpenTelemetry (OTel),采用它的人员可获得以下好处:

  • 通过 OTel 摆脱供应商的限制,让您摆脱供应商锁定,并确保一流的可观测性。
  • 查看统一的日志、指标和跟踪的协调统一,从而提供完整的系统视图。
  • 通过更丰富和增强的检测来改进您的应用程序监督。
  • 利用向后兼容性的优势来保护您之前的检测投资。
  • 通过简单的学习曲线开始 OpenTelemetry 之旅,从而简化入门和可扩展性。
  • 依靠经过验证的、面向未来的标准来增强您对每一项投资的信心。
  • 探索手动检测,以便自定义数据收集,以满足您的独特需求。
  • 使用标准化的可观测性数据框架,确保跨层监控的一致性。
  • 将开发与运营分离,从而最大限度地提高两者的效率。

鉴于这种情况,OpenTelemetry 作为云原生软件的无与伦比的可观测性解决方案脱颖而出,无缝地实现跟踪、监控和调试。它的优势之一是能够自动检测应用程序,让开发人员可以轻松收集宝贵的遥测数据,而无需深入研究代码修改。

在本文中,我们将深入探讨使用 Docker 检测 .NET 应用程序的方法,从而将两者的优点结合在一起:强大的可观测性,而无需代码麻烦。

涵盖内容?

  • APM 如何使用 CLR 分析器功能与 .NET 配合使用
  • 为 .NET 应用程序创建带有内置 OpenTelemetry 检测的 Docker 映像
  • 安装并运行 OpenTelemetry .NET 分析器以进行自动检测

APM 如何使用 CLR 分析器功能与 .NET 配合使用

在我们深入了解细节之前,让我们先消除围绕 .NET 分析器和 CPU 分析器(如 Elastic® 的通用分析工具)的一些困惑 — 我们不想将这两者混淆,因为它们的目的非常不同。

在讨论分析工具时,尤其是在 .NET 的上下文中,在“.NET 分析器”和“CPU 分析器”之间遇到混淆是很常见的。虽然两者都用于诊断和优化应用程序,但它们的主要用途不同,并且在不同的级别上运行。让我们明确区分一下

.NET 分析器

  1. 范围:专门针对 .NET 应用程序。它旨在与 .NET 运行时(即公共语言运行时 (CLR))配合使用。

  2. 功能

  3. 用例

CPU 分析器

  1. 范围:比 .NET 分析器更通用。它可以分析任何应用程序,无论其语言或运行时如何,只要它在被分析的 CPU 上运行。

  2. 功能

  3. 用例

虽然 .NET 分析器和 CPU 分析器都有助于优化和诊断应用程序性能,但它们的方法和深度有所不同。.NET 分析器专门提供对 .NET 生态系统的深入了解,从而可以进行细粒度的分析和检测。相比之下,CPU 分析器提供更广泛的视图,专注于任何应用程序的 CPU 使用模式,而与其开发平台无关。

值得注意的是,要对 .NET 应用程序进行全面分析,您可能需要同时使用两者:.NET 分析器用于了解特定于 .NET 的代码级行为,而 CPU 分析器用于概述 CPU 资源利用率。

现在我们已经澄清了这一点,让我们专注于 .NET 分析器,我们将在本文中讨论它,用于自动检测 .NET 应用程序。首先,让我们熟悉一些与 .NET 分析器相关的基本概念和术语

  • CLR(公共语言运行时):CLR 是 .NET 框架的核心组件,充当 .NET 应用程序的执行引擎。它提供内存管理、异常处理和类型安全等关键服务。
  • 分析器 API:.NET 提供了一组用于分析应用程序的 API。这些 API 使工具和开发人员能够在运行时监控或操作 .NET 应用程序。
  • IL(中间语言):编译后,.NET 源代码转换为 IL,这是一种低级的、平台无关的表示形式。然后,此 IL 代码由 CLR 在应用程序执行期间即时 (JIT) 编译为机器代码。
  • JIT 编译:JIT 代表即时。在 .NET 中,CLR 在执行之前将 IL 编译为本机代码。

现在,让我们探索如何使用 CLR 分析器进行自动检测。

.NET 中的自动检测(与 Java 的字节码检测非常相似)围绕在运行时修改应用程序方法的行为,而无需更改实际的源代码。

以下是逐步分解

  1. 附加分析器:启动 .NET 应用程序时,您必须指定加载分析器。CLR 通过读取环境变量来检查是否存在分析器。如果找到,CLR 将在执行任何用户代码之前初始化分析器。

  2. 使用 Profiler API 监控事件: Profiler API 允许性能分析器监控各种事件。例如,可以跟踪方法 JIT 编译事件。当一个方法即将进行 JIT 编译时,性能分析器会收到通知。

  3. 操作 IL 代码: 在收到 JIT 编译通知后,性能分析器可以操作该方法的 IL 代码。使用 Profiler API,性能分析器可以插入、删除或替换 IL 指令。这类似于 Java 代理修改字节码的方式。例如,如果您想测量方法的执行时间,您可以修改 IL 以在方法的开头和结尾分别插入调用来启动和停止计时器。

  4. 执行转换后的代码: 一旦 IL 被修改,JIT 编译器会将其转换为机器代码。然后应用程序将执行此机器代码,其中包括性能分析器所做的添加。

  5. 收集和报告数据: 添加的插桩可以收集各种数据,例如方法执行时间或调用计数。然后可以将此数据传递给应用程序性能管理 (APM) 工具,该工具可以根据数据提供见解、可视化和警报。

本质上,使用 CLR Profiler 进行自动插桩就是在运行时修改 .NET 方法的行为。这对于监控、诊断和微调 .NET 应用程序的性能非常宝贵,而不会干扰应用程序的实际源代码。

先决条件

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

查看示例源代码

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

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

分步指南

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

步骤 1. 基础镜像设置

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

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

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

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

Docker 的这个功能真是太棒了。在这里,我们使用 SDK 镜像编译我们的 .NET 应用程序。在过去,我们过去是在不同的平台上构建,然后将编译后的代码放入 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 代码已正确还原和编译。

步骤 3. 发布应用程序

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

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

步骤 4. 准备最终镜像

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

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

步骤 5. 安装 OpenTelemetry

我们将安装依赖项并下载 OpenTelemetry 自动插桩脚本

RUN apt-get update && apt-get install -y zip curl
RUN mkdir /otel
RUN curl -L -o /otel/otel-dotnet-install.sh https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/download/v0.7.0/otel-dotnet-auto-install.sh
RUN chmod +x /otel/otel-dotnet-install.sh

步骤 6. 配置 OpenTelemetry

指定 OpenTelemetry 应该驻留的位置并执行安装脚本。请注意,需要 ENV OTEL_DOTNET_AUTO_HOME,因为脚本会查找它

ENV OTEL_DOTNET_AUTO_HOME=/otel
RUN /bin/bash /otel/otel-dotnet-install.sh

步骤 7. 其他配置

确保自动插桩和平台检测脚本是可执行的,并运行平台检测脚本。

COPY platform-detection.sh /otel/
RUN chmod +x /otel/instrument.sh
RUN chmod +x /otel/platform-detection.sh && /otel/platform-detection.sh

此平台检测脚本将检查 Docker 构建是否适用于 ARM64,并实施一种解决方法,使 OpenTelemetry 插桩可以在 MacOS 上运行。如果您碰巧在 MacOS M1 或 M2 处理器上本地运行,您会感谢这个脚本。

步骤 8. 入口点设置

最后,设置 Docker 镜像的入口点,以获取 OpenTelemetry 插桩的来源,该插桩设置引导 .NET Profiler 所需的环境变量,然后我们启动我们的 .NET 应用程序

ENTRYPOINT ["/bin/bash", "-c", "source /otel/instrument.sh && dotnet login.dll"]

步骤 9. 使用环境变量运行 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-auto" \
       -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 关注的吞吐量和延迟关键指标。

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

并查看特定的事务

这里显然有一个异常值,其中一个事务花费了超过 200 毫秒。这很可能是由于 .NET CLR 正在预热。单击 日志,我们看到日志也被带过来。OTel Agent 会自动引入日志,并将其与跟踪相关联。

总结

使用此 Dockerfile,您已将简单的 .NET 应用程序转换为使用 OpenTelemetry 自动插桩的应用程序。这将极大地帮助您了解应用程序性能、跟踪错误并深入了解用户如何与您的软件交互。

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

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

  • 如何使用 OpenTelemetry 自动插桩 .NET。
  • 通过在 Docker 文件中使用标准命令,可以高效地完成自动插桩,而无需在多个位置添加代码,从而实现可管理性。
  • 通过使用 OpenTelemetry 及其对多种语言的支持,DevOps 和 SRE 团队可以轻松地自动插桩他们的应用程序,立即了解整个应用程序堆栈的运行状况,并缩短平均修复时间 (MTTR)。

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

开发人员资源

通用配置和用例资源

还没有 Elastic Cloud 帐户吗?注册 Elastic Cloud 并试用我上面讨论的自动插桩功能。我很乐意收到您关于使用 Elastic 深入了解您的应用程序堆栈的体验的反馈。

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