NGINX 教程:如何使用 OpenTelemetry 跟踪来了解您的微服务

ul.无子弹 {
列表样式类型:无;
}

本文是帮助您将 2023 年 3 月的微服务概念付诸实践的四篇教程之一:开始交付微服务:

  • 如何部署和配置微服务
  • 如何安全地管理容器中的机密
  • 如何使用 GitHub Actions 自动化微服务金丝雀部署
  • 如何使用 OpenTelemetry Tracing 了解您的微服务(本文)

微服务架构具有许多好处,包括增加团队自主权以及提高扩展和部署的灵活性。不利的一面是,系统中的服务越多(微服务应用程序可能有数十个甚至数百个),维护系统整体运行情况的清晰画面就越困难。作为复杂软件系统的编写者和维护者,我们知道拥有清晰的图景至关重要。可观察性工具使我们有能力构建这种情况涉及众多服务和支持基础设施。

在本教程中,我们重点介绍微服务应用程序的一种非常重要的可观察性类型:跟踪。在开始之前,我们先定义一些讨论可观测性时常用的术语:

  • 可观察性 – 仅根据复杂系统(例如微服务应用)的外部输出(例如跟踪记录、日志和指标)的知识来了解复杂系统(例如微服务应用)的内部状态或状况的能力。
  • 监控 – 观察和检查对象在一段时间内的进度或状态的能力。例如,您可以监控高峰时段传入应用程序的流量,并使用该信息来适当扩展流量以做出响应。
  • 遥测 – 收集指标、跟踪和日志并将其从源点传输到另一个系统进行存储和分析的行为。还有数据本身。
  • 追踪/踪迹 –对请求或操作在分布式系统的所有节点中移动时的旅程的描述。
  • Span – 操作及其关联元数据跟踪中的记录。迹线由许多嵌套的跨度组成。
  • 事件记录/日志 – 带有元数据的带时间戳的文本记录。
  • 指标 – 在运行时捕获的测量结果。例如,应用程序在某个时间点使用的内存量。

我们可以使用所有这些概念来深入了解微服务的性能。跟踪是可观察性策略中特别有用的部分,因为跟踪提供了发出请求时多个(通常是松散耦合的)组件之间发生的情况的“全局”。这也是识别性能瓶颈的特别有效的方法。

本教程使用 OpenTelemetry (OTel) 的跟踪工具包,这是一个开源供应商中立的收集、处理和导出标准ng 遥测技术正在迅速流行。在OTel的概念中,跟踪将可能涉及多个服务的数据流分割成一系列按时间顺序排列的“块”,可以帮助您轻松理解:

  • “块”中发生的所有步骤
  • 所有这些步骤花了多长时间
  • 有关每个步骤的元数据

如果您不熟悉 OTel,请参阅什么是 OpenTelemetry?全面介绍该标准以及实施该标准的注意事项。

教程概述

本教程重点介绍使用 OTel 跟踪微服务应用的操作。在本教程的四个挑战中,您将学习如何通过系统跟踪请求并回答有关微服务的问题:

  • 设置基本 OTel 工具
  • 为所有服务设置 OTel 检测和跟踪可视化
  • 学习读取 OTel 痕迹
  • 根据跟踪读数优化检测

这些挑战说明了我们在首次设置跟踪时推荐的流程。步骤是:

  • 了解系统以及您正在检测的特定操作。
  • 确定您需要从正在运行的系统中了解哪些信息。
  • “天真”地检测系统(即使用默认配置,而不尝试删除不需要的信息或收集自定义数据点)并评估检测是否可以帮助您回答问题。
  • 调整报告的信息,以便您能够更快地回答这些问题。
  • 注意:本教程的目的是说明有关遥测的一些核心概念,而不是展示在生产中部署微服务的正确方法。虽然它使用真正的“微服务”架构,但有一些重要的注意事项:

    • 本教程不使用容器编排框架,例如 Kubernetes 或游牧民族。这样您就可以了解微服务概念,而不必陷入某个框架的细节中。此处介绍的模式可移植到运行这些框架之一的系统。
    • 这些服务经过优化是为了易于理解,而不是为了软件工程的严谨性。重点是查看服务在系统中的角色及其通信模式,而不是代码的细节。有关详细信息,请参阅各个服务的自述文件。

    教程架构和遥测目标

    架构和用户流程

    此图说明了微服务和本教程中使用的其他元素之间的整体架构和数据流。

    这两个微服务是:

    • 消息服务 – 具有消息存储功能的简单聊天 API
    • 通知服务 – 触发事件来提醒我们的监听器读者,根据他们的喜好

    三项支持基础设施是:

    • NGINX 开源 – 消息服务和整个系统的入口点
    • RabbitMQ – 一种流行的开源消息代理,支持服务异步通信
    • Jaeger – 一种开源端到端分布式跟踪系统,用于从生成遥测数据的系统组件中收集遥测数据并将其可视化

    暂时不考虑 OTel,我们可以集中关注正在跟踪的事件序列:当用户发送新的聊天消息并且收件人收到通知时会发生什么。

    流程如下:

  • 用户向消息服务发送消息。 NGINX 反向代理拦截消息并将其转发到消息服务的多个实例之一。
  • 消息服务将新消息写入其数据库。
  • 消息服务在 RabbitMQ 消息队列上生成一个名为 chat_queue 的事件,以指示消息已发送。该活动是通用的,没有具体目标。
  • 同时:

    • 4a。消息服务向发送者返回一个响应,报告消息已成功发送。
    • 4b。通知程序服务注意到 chat_queue 上的新事件并使用它。
  • 通知程序服务会检查其数据库以了解新消息收件人的通知首选项。
  • 通知程序服务使用收件人的首选方法发送一个或多个通知(在本教程中,选择的方法是短信和电子邮件)。
  • 遥测目标

    在设置遥测仪器时,最好从一组比“发送所有内容并希望获得见解”更明确的仪器目标开始。我们为本教程设定了三个关键遥测目标:

  • 了解d 请求在新消息流中经历的所有步骤
  • 确信流程在正常情况下会在五秒内端到端执行
  • 查看通知程序服务需要多长时间才能开始处理由信使服务分派的事件(过度延迟可能意味着通知程序服务在从事件队列读取数据时遇到问题并且事件正在备份)
  • 请注意,这些目标与系统的技术操作和用户体验都相关。

    教程先决条件和设置

    先决条件

    要在您自己的环境中完成本教程,您需要:

    • Linux/Unix 兼容环境

      注意:本教程中涉及跟踪 NGINX 的活动不适用于基于 ARM 的处理器,因为 NGINX 的 OpenTelemetry 模块不兼容。 (这包括 Linux aarch64 架构和带有 M1 或 M2 芯片的 Apple 机器。)涉及信使和通知程序服务的服务适用于所有架构。

    • 基本熟悉 Linux 命令行、JavaScript 和 bash(但提供并解释了所有代码和命令,因此即使知识有限,您仍然可以取得成功)
    • Docker 和 Docker Compose
    • Node.js 19.x 或更高版本

      • 我们测试了 19.x 版本,但预计较新版本的 Node.js 也能正常工作。
      • 有关安装 Node,js 的详细信息,请参阅消息服务存储库中的自述文件。您还可以安装 asdf 以获得与教程中使用的完全相同的 Node.js 版本。
    • curl(已安装在大多数系统上)
    • 架构和用户流程中列出的技术:信使和通知程序(您将在下一部分中下载它们)、NGINX 开源、Jaeger 和 RabbitMQ。

    注意:本教程使用 JavaScript SDK,因为消息服务和通知服务是用 Node.js 编写的。是您还可以设置 OTel 自动检测功能(也称为自动检测),以便您可以了解 OTel 提供的信息类型。本教程解释了您需要了解的有关 OTel Node.js SDK 的所有信息,但有关更多详细信息,请参阅 OTel 文档。

    设置

  • 启动终端会话。
  • 在您的主目录中,创建 microservices-march 目录并将本教程的 GitHub 存储库克隆到其中。 (您还可以使用不同的目录名称并相应地调整说明。)

    注意:在整个教程中,省略了 Linux 命令行上的提示符,以便更轻松地将命令复制并粘贴到终端中。波形符 (~) 代表您的主目录。

    mkdir ~/微服务-march
    cd ~/微服务-march
    git克隆 https://github.com/microservices-march/messenger –branch mm23-metrics-start
    git clone https://github.com/microservices-march/notifier –branch mm23-metrics-开始
    git克隆 https://github.com/microservices-march/platform –branch mm23-metrics-start

  • 挑战 1:设置基本 OTel 仪器

    在此挑战中,您启动消息服务并配置 OTel 自动检测以将遥测数据发送到控制台。

    启动信使服务

  • 更改为平台存储库并启动 Docker Compose:

    cd ~/微服务-march/平台
    docker compose up -d –build

    这将启动 RabbitMQ 和 Jaeger,这将在后续挑战中使用。

    • -d 标志指示 Docker Compose 在容器启动时与容器分离(否则容器将保持连接到您的终端)。
    • –build 标志指示 Docker Compose 在启动时重建所有映像。这可确保您正在运行的映像在任何潜在的文件更改时保持更新。
  • 切换到m中的app目录essenger 存储库并安装 Node.js(如果您愿意,可以替换为不同的方法):

    cd ~/microservices-march/messenger/app
    asdf安装

  • 安装依赖项:

    npm 安装

  • 为消息服务启动 PostgreSQL 数据库:

    docker 组成-d

  • 创建数据库架构和表并插入一些种子数据:

    npm 运行刷新数据库

  • 配置发送到控制台的 OTel 自动检测

    借助 OTel 自动检测,您无需修改​​ Messenger 代码库中的任何内容即可设置跟踪。所有跟踪配置不是导入到应用程序代码本身,而是在运行时导入到 Node.js 进程的脚本中定义。

    在这里,您可以使用最基本的跟踪目标(控制台)来配置消息服务的自动检测。在挑战 2 中,您将更改配置以将跟踪发送到 Jaeg呃作为外部收集器。

  • 仍然在 Messenger 存储库的 app 目录中工作,安装核心 OTel Node.js 包:

    npm install @opentelemetry/sdk-node@0.36.0 \
    @opentelemetry/auto-instrumentations-node@0.36.4

    这些库提供以下功能:

    • @opentelemetry/sdk-node – OTel 数据的生成和导出
    • @opentelemetry/auto-instrumentations-node – 自动设置所有最常见 Node.js 工具的默认配置

    注意:OTel 的一个怪癖是它的 JavaScript SDK 被分成非常非常小的部分。因此,您将为本教程中的基本示例安装更多软件包。要了解您可能需要哪些软件包来完成本教程中涵盖的任务之外的检测任务,请仔细阅读(非常好的)OTel 入门指南并浏览 OTel GitHub 存储库。

  • 新建一个文件called Tracing.mjs 包含 OTel 跟踪的设置和配置代码:

    触摸追踪.mjs

  • 在您喜欢的文本编辑器中,打开tracing.mjs 并添加以下代码:

    //1
    从“@opentelemetry/sdk-node”导入opentelemetry;
    从“@opentelemetry/auto-instrumentations-node”导入{ getNodeAutoInstrumentations };

    //2
    const sdk = 新 opentelemetry.NodeSDK({
    traceExporter:新的 opentelemetry.tracing.ConsoleSpanExporter(),
    仪器:[getNodeAutoInstrumentations()],
    });

    //3
    sdk.start();

    该代码执行以下操作:

  • 从 OTel SDK 导入所需的函数和对象。
  • 创建 NodeSDK 的新实例并将其配置为:

    • 将 Span 发送到控制台 (ConsoleSpanExporter)。
    • 使用自动仪器作为基础仪器集。该检测加载所有最常见的自动检测库。在教程中相关的是:

      • @opentelemetry/instrumentation-pg 用于 Postgres 数据库库 (pg)
      • @opentelemetry/instrumentation-express 用于 Node.js Express 框架
      • @opentelemetry/instrumentation-amqplib 用于 RabbitMQ 库 (amqplib)
  • 启动 SDK。
  • 启动 Messenger 服务,导入您在第 3 步中创建的自动检测脚本。

    节点 –import ./tracing.mjs 索引.mjs

    过了一会儿,控制台(您的终端)中开始出现大量与跟踪相关的输出:


    {
    跟踪 ID: ‘9c1801593a9d3b773e5cbd314a8ea89c’,
    父 ID:未定义,
    跟踪状态:未定义,
    name: ‘fs statSync’,
    id: ‘2ddf082c1d609fbe’,
    种类:0,
    时间戳:1676076410782000,
    持续时间:3,
    属性: {},
    状态:{代码:0},
    事件:[],
    链接:[]
    }

  • 注意:让终端会话保持打开状态,以便在挑战 2 中重复使用。

    挑战 2:为所有服务设置 OTel 检测和跟踪可视化冰

    您可以使用许多工具来查看和分析跟踪,但本教程使用 Jaeger。 Jaeger 是一个简单的开源端到端分布式跟踪框架,具有内置的基于 Web 的用户界面,用于查看跨度和其他跟踪数据。平台存储库中提供的基础设施包括 Jaeger(您在挑战 1 的第 1 步中启动了它),因此您可以专注于分析数据,而不是处理复杂的工具。

    可以通过浏览器中的 http://localhost:16686 端点访问 Jaeger,但如果您现在访问该端点,则无法看到有关您系统的任何内容。那是因为您当前收集的痕迹正在发送到控制台!要在 Jaeger 中查看跟踪数据,您需要使用 OpenTelemetry 协议 (OTLP) 格式导出跟踪。

    在本次挑战中,您将通过配置检测来检测核心用户流:

    • 消息服务,切换跟踪目标从控制台到 Jaeger
    • 通知程序服务
    • NGINX

    配置发送到外部收集器的 OTel 自动检测

    提醒一下,使用 OTel 自动检测意味着您无需修改​​ Messenger 代码库中的任何内容即可设置跟踪。相反,所有跟踪配置都位于在运行时导入 Node.js 进程的脚本中。在这里,您将消息服务生成的跟踪的目标从控制台更改为外部收集器(本教程中为 Jaeger)。

  • 仍然在挑战 1 中的同一终端中工作,并在 Messenger 存储库的应用目录中安装 OTLP 导出器 Node.js 软件包:

    npm install @opentelemetry/exporter-trace-otlp-http@0.36.0

    @opentelemetry/exporter-trace-otlp-http 库通过 HTTP 以 OTLP 格式导出跟踪信息。将遥测数据发送到 OTel 外部收集器时使用。

  • 打开tracing.mjs(您在挑战 1 中创建和编辑的)并进行以下更改:

    • 将此行添加到文件顶部的导入语句集中:

      从“@opentelemetry/exporter-trace-otlp-http”导入{ OTLPTraceExporter };

    • 将您提供给 OTel SDK 的“导出器”从挑战 1 中使用的控制台导出器更改为可以通过 HTTP 将 OTLP 数据发送到与 OTLP 兼容的收集器的导出器。替换:

      traceExporter:new opentelemetry.tracing.ConsoleSpanExporter(),

      与:

      跟踪导出器:新的 OTLPTraceExporter({ headers: {} }),

    注意:为了简单起见,本教程假设收集器位于默认位置,http://localhost:4318/v1/traces。在实际系统中,明确设置位置是个好主意。

  • 按 Ctrl+c 停止您在配置发送到控制台的 OTel 自动检测的步骤 4 中在此终端中启动的信使服务。然后重新启动即可使用新的第 2 步中配置的导出器:

    ^c
    节点 –import ./tracing.mjs 索引.mjs

  • 启动第二个单独的终端会话。 (后续说明将其称为客户端终端和步骤 1 和 3 中使用的原始终端 – 信使终端。)等待大约十秒钟,然后向信使服务发送运行状况检查请求(如果满足以下条件,您可以运行此几次)你想看到多条痕迹):

    卷曲 -X GET http://localhost:4000/health

    在发送请求之前等待十秒有助于使您的跟踪更容易找到,因为它是在服务启动时自动检测生成的许多跟踪之后出现的。

  • 在浏览器中,通过 http://localhost:16686 访问 Jaeger UI 并验证 OTLP 导出器是否按预期工作。单击标题栏中的搜索,然后从服务字段的下拉菜单中选择名称以unknown_service 开头的服务。单击“查找痕迹”按钮上:

  • 单击窗口右侧的迹线可显示其中的跨度列表。每个跨度描述了作为跟踪的一部分运行的操作,有时涉及多个服务。屏幕截图中的 jsonParser 范围显示了运行消息服务请求处理代码的 jsonParser 部分所花费的时间。

  • 如步骤 5 中所述,OTel SDK 导出的服务名称 (unknown_service) 没有意义。要解决此问题,请在信使终端中按 Ctrl+c 停止信使服务。然后再安装几个 Node.js 包:

    ^c
    npm install @opentelemetry/semantic-conventions@1.10.0 \
    @opentelemetry/resources@1.10.0

    这两个库提供以下功能:

    • @opentelemetry/semantic-conventions – 定义 OTel 规范中定义的跟踪的标准属性。
    • @opentelemetry/resources – 定义代表生成 OTel 数据的源(在本教程中为消息服务)的对象(资源)。
  • 在文本编辑器中打开tracing.mjs并进行以下更改:

    • 将这些行添加到文件顶部的导入语句集中:

      从“@opentelemetry/resources”导入{资源};
      从“@opentelemetry/semantic-conventions”导入{SemanticResourceAttributes};

    • 通过在最后一个导入语句后添加以下行,在 OTel 规范中的正确键下创建一个名为 Messenger 的资源:

      常量资源 = 新资源({
      [SemanticResourceAttributes.SERVICE_NAME]:“信使”,
      });

    • 通过在黑色线条之间添加橙色突出显示的线条,将资源对象传递给 NodeSDK 构造函数:

      const sdk = 新 opentelemetry.NodeSDK({
      资源,
      跟踪导出器:新的 OTLPTraceExporter({ headers: {} }),
      仪器:[getNodeAutoInstrumentations()],
      });

  • 重新启动消息服务:

    节点 –import ./tracing.mjs 索引.mjs

  • 等待大约十秒钟,然后在客户端(您在步骤 4 中打开)中向服务器发送另一个运行状况检查请求(如果您想查看多个跟踪,可以运行该命令几次):

    卷曲 -X GET http://localhost:4000/health

    注意:让客户端保持开放状态以便在下一部分中重复使用,并让消息终端保持开放状态以便在挑战 3 中重复使用。

  • 确认浏览器中的 Jaeger UI 中出现名为Messenger的新服务(这可能需要几秒钟,并且您可能需要刷新 Jaeger UI):

  • 从“服务”下拉菜单中选择“Messenger”,然后单击“查找跟踪”按钮以查看源自该 Messenger 服务的所有最新跟踪(屏幕截图显示 20 条中最新的 2 条):

  • 单击迹线可显示其中的跨度。每个跨度都被正确标记为 originat来自信使服务:

  • 配置通知程序服务的 OTel 自动检测

    现在启动并配置通知程序服务的自动检测,运行与前两节中针对消息服务的命令基本相同的命令。

  • 打开一个新的终端会话(在后续步骤中称为通知程序终端)。切换到通知程序存储库中的应用程序目录并安装 Node.js(如果您愿意,可以替换为不同的方法):

    cd ~/microservices-march/notifier/app
    asdf安装

  • 安装依赖项:

    npm 安装

  • 为通知程序服务启动 PostgreSQL 数据库:

    docker 组成-d

  • 创建数据库架构和表并插入一些种子数据:

    npm 运行刷新数据库

  • 安装 OTel Node.js 软件包(有关软件包功能的说明,请参阅配置 OTel Auto-Instrumen 中的步骤 1 和 3发送到控制台):

    npm install @opentelemetry/auto-instrumentations-node@0.36.4 \
    @opentelemetry/exporter-trace-otlp-http@0.36.0 \
    @opentelemetry/resources@1.10.0 \
    @opentelemetry/sdk-node@0.36.0 \
    @opentelemetry/semantic-conventions@1.10.0

  • 创建一个名为tracing.mjs的新文件:

    触摸追踪.mjs

  • 在您首选的文本编辑器中,打开 Tracing.mjs 并添加以下脚本以启动并运行 OTel SDK:

    从“@opentelemetry/sdk-node”导入opentelemetry;
    从“@opentelemetry/auto-instrumentations-node”导入{ getNodeAutoInstrumentations };
    从“@opentelemetry/exporter-trace-otlp-http”导入{ OTLPTraceExporter };
    从“@opentelemetry/resources”导入{资源};
    从“@opentelemetry/semantic-conventions”导入{SemanticResourceAttributes};

    常量资源 = 新资源({
    [SemanticResourceAttributes.SERVICE_NAME]:“通知者”,
    });

    const sdk = 新 opentelemetry.NodeSDK({
    资源,
    跟踪导出器:新的 OTLPTraceExporter({ 标题: {} }),
    仪器:[getNodeAutoInstrumentations()],
    });

    sdk.start();

    注意:此脚本与信使服务中的脚本完全相同,只是 SemanticResourceAttributes.SERVICE_NAME 字段中的值为 notifier。

  • 使用 OTel 自动检测启动通知程序服务:

    节点 –import ./tracing.mjs 索引.mjs

  • 等待大约十秒钟,然后在客户端向通知服务发送健康检查请求。此服务正在侦听端口 5000,以防止与侦听端口 4000 的信使服务发生冲突:

    卷曲 http://localhost:5000/health

    注意:让客户端和通知者终端保持开放状态,以便在挑战 3 中重复使用。

  • 确认名为 notifier 的新服务出现在浏览器的 Jaeger UI 中:

  • 配置NGINX的OTel Instrumentation

    对于 NGINX,您可以手动设置跟踪,而不是使用g OTel 自动检测方法。目前,使用 OTel 检测 NGINX 的最常见方法是使用用 C 语言编写的模块。第三方模块是 NGINX 生态系统的重要组成部分,但需要一些工作来设置。本教程为您完成设置。有关背景信息,请参阅我们博客上的为 NGINX 和 NGINX Plus 编译第三方动态模块。

  • 启动一个新的终端会话(NGINX 终端),将目录更改为 Messenger 存储库的根目录,并创建一个名为 load-balancer 的新目录,以及名为 Dockerfile、nginx.conf 和 opentelemetry_module.conf 的新文件:

    cd ~/microservices-march/messenger/
    mkdir 负载均衡器
    cd 负载均衡器
    触摸 Dockerfile
    触摸 nginx.conf
    触摸 opentelemetry_module.conf

  • 在您喜欢的文本编辑器中,打开 Dockerfile 添加以下内容(注释解释了每一行的作用,但您可以在不了解全部内容的情况下构建和运行 Docker 容器):

    来自 –platform=amd64 nginx:1.23.1

    # 将 nginx.conf 文件替换为我们自己的
    复制 nginx.conf /etc/nginx/nginx.conf

    # 定义NGINX OTel模块的版本
    ARG OPENTELEMETRY_CPP_VERSION=1.0.3

    # 定义编译和运行NGINX时使用的共享库的搜索路径
    ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/opentelemetry-webserver-sdk/sdk_lib/lib

    # 1. 下载最新版本的 Consul 模板和 OTel C++ Web 服务器模块 otel-webserver-module
    添加 https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/download/webserver%2Fv${OPENTELEMETRY_CPP_VERSION}/opentelemetry-webserver-sdk-x64-linux.tgz /tmp

    运行 apt-get update \
    && apt-get install -y –no-install-recommends mud-init unzip \
    # 2. 提取模块文件
    && tar xvfz /tmp/opentelemetry-webserver-sdk-x64-linux.tgz -C /opt \
    && rm -rf /tmp/opentelemetry-webserver-sdk-x64-linux.tgz \
    # 3. 在主 NGINX 配置文件顶部安装并添加“load_module”指令&& /opt/opentelemetry-webserver-sdk/install.sh \
    && echo “load_module /opt/opentelemetry-webserver-sdk/WebServerModule/Nginx/1.23.1/ngx_http_opentelemetry_module.so;\n$(cat /etc/nginx/nginx.conf)” > /etc/nginx/nginx.conf

    # 4. 复制NGINX OTel模块的配置文件
    复制 opentelemetry_module.conf /etc/nginx/conf.d/opentelemetry_module.conf

    曝光8085

    停止信号退出

  • 打开 nginx.conf 并添加以下内容:

    事件{}

    http{
    包括/etc/nginx/conf.d/opentelemetry_module.conf;

    上游信使{
    服务器本地主机:4000;
    }

    服务器 {
    听8085;

    地点 / {
    proxy_pass http://messenger;
    }
    }
    }

    这个极其基本的 NGINX 配置文件告诉 NGINX:

    • 设置一个名为“messenger”的上游组,代表信使服务实例组
    • 监听端口 8085 上的 HTTP 请求
    • 转发以 / 开头的路径的所有传入请求(即所有传入请求)到上游的信使

    注意:这非常接近 NGINX 在生产环境中作为反向代理和负载均衡器的实际配置。唯一的主要区别是上游块中服务器指令的参数通常是域名或 IP 地址,而不是 localhost。

  • 打开 opentelemetry_module.conf 并添加以下内容:

    NginxModuleEnabled ON;
    NginxModuleOtelSpanExporter otlp;
    NginxModuleOtelExporterEndpoint 本地主机:4317;
    NginxModuleServiceName 信使-lb;
    NginxModuleServiceNamespace 微服务三月演示架构;
    NginxModuleServiceInstanceId 演示实例Id;
    NginxModuleResolveBackends 开启;
    NginxModuleTraceAsError 开启;

  • 构建包含 NGINX 以及 NGINX OTel 模块的 Docker 镜像:

    docker build -t Messenger-lb 。

  • 启动 NGINX 反向代理和负载均衡器的 Docker 容器:

    docker run –rm –name messenger-lb -p 8085:8085 –network =“主机”messenger-lb

  • 在客户端,通过NGINX反向代理和负载均衡器向Messenger服务发送健康检查请求(发送该请求之前无需等待):

    卷曲 http://localhost:8085/health

    注意:让 NGINX 和客户端终端保持开放状态,以便在挑战 3 中重复使用。

  • 在浏览器中,确认 Jaeger UI 中列出了新的 messenger-lb 服务以及您之前启动的服务。您可能需要在浏览器中重新加载 Jaeger UI。

  • 挑战 3:学习读取 OTel 痕迹

    在架构和用户流程中,我们概述了用户流程的各个阶段,但回顾一下:

  • 用户通过向另一个用户发送消息来开始对话。
  • NGINX 反向代理拦截消息并将其转发到消息服务。
  • 消息服务将消息写入其数据库,然后通过它调度一个事件唉,RabbitMQ。
  • 通知程序服务使用该事件,查找接收者(第二个用户)的通知首选项,并通过首选方法向接收者发送通知。
  • 实施遥测的目标是:

  • 了解请求完成新消息流所经历的所有步骤。
  • 确信流程在正常情况下会在五秒内执行端到端。
  • 查看通知程序服务需要多长时间才能开始处理消息服务分派的事件。
  • 在本次挑战中,您将学习如何评估 OTel 仪器生成的迹线是否满足上述目标。首先,您运行系统并创建一些跟踪。然后,您可以检查消息流的跟踪以及由 NGINX、消息服务和通知程序服务生成的消息流部分。

    创建跟踪数据

    在客户端,设置对话并在两个用户之间发送几条消息:

    卷曲-X POST \
    -H“内容类型:application/json”\
    -d ‘{“participant_ids”: [1, 2]}’ \
    ‘http://localhost:8085/conversations’

    卷曲-X POST \
    -H“用户 ID:1”\
    -H“内容类型:application/json”\
    -d ‘{“content”: “这是第一条消息”}’ \
    ‘http://localhost:8085/conversations/1/messages’

    卷曲-X POST \
    -H“用户 ID:2”\
    -H“内容类型:application/json”\
    -d ‘{“content”: “这是第二条消息”}’ \
    ‘http://localhost:8085/conversations/1/messages’

    如下所示的输出由通知程序服务生成并显示在通知程序终端中:

    收到 new_message: {“type”:”new_message”,”channel_id”:1,”user_id”:1,”index”:1,”participant_ids”:[1,2]}
    通过短信发送新消息通知至12027621401

    收到 new_message: {“type”:”new_message”,”channel_id”:1,”user_id”:2,”index”:2,”participant_ids”:[1,2]}

    通过 ema 发送新消息通知发送至 the_hotstepper@kamo.ze

    通过短信发送新消息通知至19147379938

    准备好读取痕迹

    在浏览器中打开 Jaeger UI,从“服务”下拉菜单中选择“messenger-lb”,然后单击“查找跟踪”按钮。将出现一个跟踪列表,从流程的最开始处开始。单击任意跟踪可显示其详细信息,如以下屏幕截图所示:

    点击并探索一下。然后,在继续之前,请考虑跟踪中的信息如何支持挑战 3 简介中列出的检测目标。相关问题包括:

    • 哪些信息有助于实现目标?
    • 缺少什么信息?
    • 哪些信息不相关?

    检查跟踪的 NGINX (messenger-lb) 部分

    目标 1:查看请求在新消息流中经历的所有步骤

    从 NGINX 跨度开始,其中父范围内有 11 个子范围。由于当前的 NGINX 配置非常简单,子跨度并不是很有趣,只是显示 NGINX 请求处理生命周期中每个步骤所花费的时间。然而,父跨度(第一个跨度)包含一些有趣的见解:

    • 在“标签”下,您可以看到以下属性:

      • http.method 字段 – POST(在 REST 术语中,这意味着创建)
      • http.status_code 字段 – 201(表示创建成功)
      • http.target 字段 –conversations/1/messages(消息端点)

      这三条信息结合在一起表示:“POST 请求已发送至 /conversations/1/messages,响应为 201(已成功创建)”。这对应于架构和用户流程中的步骤 1 和 4a)。

    • 在 Process 下,webengine.name 字段显示这是 NGINX p请求的一部分。

    此外,由于Messenger和Notifier的跨度嵌套在messenger-lbconversations/1跨度内(如准备阅读跟踪中的屏幕截图所示),因此您可以知道请求通过以下方式发送到信使服务: NGINX 反向代理命中了流程中的所有预期组件。

    此信息满足目标,因为您可以看到 NGINX 反向代理是流程的一部分。

    目标 2:验证流程是否在五秒内执行

    在标记为 messenger-lb 的 Span 列表中,查看最近的 Span(位于列表底部),以了解请求的 NGINX 部分花费了多长时间。在屏幕截图中,时间跨度从 589 微秒 (μs) 开始,持续 24 微秒,这意味着完整的反向代理操作仅花费 613 微秒 – 大约 0.6 毫秒 (ms)。 (当您运行 t 时,确切的值当然会有所不同自己教程。)

    在这样的设置中,大多数情况下,这些值仅相对于其他测量有用,并且它们在系统之间有所不同。但在这种情况下,该操作的长度显然不会有接近五秒的危险。

    此信息满足目标,因为您可以看到 NGINX 操作花费的时间不超过五秒。如果流程中有一个非常慢的操作,那么它一定是稍后发生的。

    目标 3:查看通知程序服务读取消息服务发送的事件需要多长时间

    NGINX 反向代理层不包含任何相关信息,因此您可以继续讨论信使跨度。

    检查跟踪的信使部分

    目标 1:查看请求在新消息流中经历的所有步骤

    跟踪的信使服务部分包含另外 11 个 sp回答。同样,大多数子跨度都涉及 Express 框架在处理请求时使用的基本步骤,并且不是很有趣。然而,父跨度(第一个跨度)再次包含一些有趣的见解:

    在“标签”下,您可以看到以下属性:

    • http.method 字段 – POST(同样,在 REST 术语中,这意味着创建)
    • http.route 字段 – /conversations/:conversationId/messages(消息路由)
    • http.target 字段 – /conversations/1/messages(消息端点)

    此信息满足目标,因为它向我们表明消息服务是流的一部分,并且端点命中是新的消息端点。

    目标 2:验证流程是否在五秒内执行

    如下面的屏幕截图所示,跟踪的信使部分从 1.28 毫秒开始,到 36.28 毫秒结束,总时间为 35 ms。大部分时间都花在解析 JSON(中间件 – jsonParser)上,并在更大程度上连接到数据库(pg-pool.connect 和 tcp.connect)。

    考虑到在写入消息的过程中还进行了多个 SQL 查询,这是有道理的。这反过来表明您可能希望增强自动检测配置以捕获这些查询的时间。 (本教程没有展示这种额外的工具,但在挑战 4 中,您可以手动创建跨度,这些跨度又可用于包装数据库查询。)

    此信息满足目标,因为它表明信使操作不会花费接近五秒的时间。如果流程中有一个非常慢的操作,那么它一定是稍后发生的。

    目标 3:查看通知程序服务读取消息服务发送的事件需要多长时间

    与 NGINX 跨度一样,信使跨度不包含此信息,以便您可以移至通知程序范围。

    检查跟踪的通知程序部分

    目标 1:查看请求在新消息流中经历的所有步骤

    跟踪的通知程序部分仅包含两个范围:

    • chat_queue 进程范围 – 确认通知程序服务处理了 chat_queue 消息队列中的事件
    • pg-pool.connect 范围 – 显示处理事件后通知程序服务与其数据库建立了某种连接

    从这些跨度中获得的信息仅部分实现了理解每个步骤的目标。您可以看到通知程序服务已达到消耗队列中事件的程度,但您不知道是否:

    • 此服务发送的消息通知对应于消息服务调度的事件
    • 相关消息通知阳离子已正确发送至消息收件人

    这表明您需要执行以下操作才能完全理解通知程序服务流程:

    • 手动检测显示正在发送的通知的范围
    • 确保消息服务分派的事件与通知程序服务使用的事件之间存在以跟踪 ID 形式存在的显式联系

    目标 2:验证流程是否在五秒内执行

    查看通知程序服务跨度的总体时间,您会发现该请求在流程的通知程序部分花费了 30.77 毫秒。但是,由于不存在表示整个流程“结束”(向收件人发送通知)的跨度,因此您无法确定该流程部分的总时间或操作的总体完成时间。

    目标 3:查看 n 需要多长时间otifier 服务读取消息服务发送的事件

    但是,您可以看到通知程序服务的 chat_queue 进程跨度在 6.12 毫秒开始,比消息服务的 chat_queue 发送跨度在 4.12 毫秒开始后 2 毫秒。

    此目标已实现,因为您知道通知程序在消息服务发送事件后 2 毫秒消耗了该事件。与目标 2 不同,实现此目标不需要您知道事件是否已完全处理或需要多长时间。

    结论

    根据我们对当前 OTel 自动仪表产生的痕迹的分析,很明显:

    • 其中许多跨度在当前形式下没有用处:

      • NGINX 正在生成与功能相关的跨度(例如授权检查和文件服务),这些跨度与您关心的角色(反向代理)无关。然而,此时NGINX 的 OTel 检测不允许您省略不相关的跨度,因此无能为力。
      • 在 Node.js 服务(消息传递服务和通知程序服务)的范围中,有些范围似乎与目​​标相关:JSON 解析、请求处理程序和所有数据库操作的范围。一些中间件跨度(例如expressInit和corsMiddleware)似乎不相关,可以删除。
    • 以下内容缺少关键跨度:

      • 通知程序服务发送的通知
      • 从消息服务发送的 RabbitMQ 事件与通知服务处理的事件之间有清晰的映射

    这意味着基本工具实现了最后一个目标:

    • 查看通知程序服务需要多长时间才能开始处理消息服务分派的事件

    但是,没有足够的信息来实现前两个目标:

    • 了解所有步骤ps 在新消息流期间发生请求
    • 确信该流程在正常情况下在不到五秒的时间内端到端执行

    挑战 4:根据跟踪读数优化检测

    在此挑战中,您将根据在挑战 3 中进行的跟踪分析来优化 OTel 检测。这包括删除不必要的跨度、创建新的自定义跨度以及确认通知程序服务所使用的事件是生成的事件通过信使服务。

    删除不必要的跨度

  • 在您首选的文本编辑器中,打开 Messenger 存储库的应用程序目录中的tracing.mjs 文件,并在顶部导入语句列表的末尾添加以下内容:

    const IGNORED_EXPRESS_SPANS = 新集([
    “中间件-expressInit”,
    “中间件 – corsMiddleware”,
    ]);

    这定义了一组跨度名称,源自跨度显示列表在 Jaeger UI 的以下屏幕截图中,从跟踪中省略,因为它们没有为此流程提供有用的信息。您可能认为屏幕截图中列出的其他跨度也不需要,并将它们添加到 IGNORED_EXPRESS_SPANS 列表中。

  • 通过更改以橙色突出显示的行,将过滤器添加到自动检测配置中,以忽略不需要的跨度:

    const sdk = 新 opentelemetry.NodeSDK({
    资源,
    跟踪导出器:新的 OTLPTraceExporter({ headers: {} }),
    仪器:[getNodeAutoInstrumentations()],
    });

    对此:

    const sdk = 新 opentelemetry.NodeSDK({
    资源,
    跟踪导出器:新的 OTLPTraceExporter({ headers: {} }),
    仪器仪表:[
    获取NodeAutoInstrumentations({
    “@opentelemetry/instrumentation-express”:{
    忽略层:[
    (姓名)=> {
    返回 IGNORED_EXPRESS_SPANS.has(name);
    },
    ],
    },
    }),
    ],
    });

    getNodeAutoInstrumentations 函数引用步骤 1 中定义的跨度集,将其从 @opentelemetry/instrumentation-express 生成的跟踪中过滤掉。换句话说,对于属于 IGNORED_EXPRESS_SPANS 的跨度,return 语句解析为 true,并且ignoreLayers 语句从跟踪中删除该跨度。

  • 在信使终端中,按 Ctrl+c 停止信使服务。然后重新启动它:

    ^c
    节点 –import ./tracing.mjs 索引.mjs

  • 等待大约十秒,然后在客户端发送一条新消息:

    卷曲-X POST \
    -H“用户 ID:2”\
    -H“内容类型:application/json”\
    -d ‘{“content”: “这是第二条消息”}’ \
    ‘http://localhost:8085/conversations/1/messages’

  • 重新检查 Jaeger UI 中的信使范围。两个中间件跨度,expressInit 和 corsMiddleware,不再出现(您可以将其与挑战 3 中检查跟踪的信使部分的目标 2 的屏幕截图进行比较。

  • 设置自定义跨度

    在本节中,您将第一次接触应用程序代码。自动检测可以生成大量信息,无需更改应用程序,但只有通过检测特定的业务逻辑片段才能获得一些见解。

    对于您正在检测的新消息流,一个示例是跟踪向消息收件人发送通知的情况。

  • 打开通知程序存储库的应用程序目录中的index.mjs。该文件包含服务的所有业务逻辑。在文件顶部的导入语句列表末尾添加以下行:

    从“@opentelemetry/api”导入{trace};

  • 替换此代码(位于文件中的第 91 行左右):

    for (让 pref 偏好) {
    控制台.日志(
    `通过 ${pref.address_type} 向 ${pref.address} 发送新消息通知`
    );
    }

    与:

    const 跟踪器 =trace.getTracer(“通知器”); // 1
    跟踪器.startActiveSpan( // 2
    “通知.send_all”,
    {
    属性: {
    user_id: 消息.user_id,
    },
    },
    (父跨度)=> {
    for (让 pref 偏好) {
    跟踪器.startActiveSpan( // 3
    “通知.发送”,
    {
    属性:{ // 4
    notification_type: pref.address_type,
    user_id: pref.user_id,
    },
    },
    (跨度)=> {
    控制台.日志(
    `通过 ${pref.address_type} 向 ${pref.address} 发送新消息通知`
    );
    跨度结束(); // 5
    }
    );
    }
    父跨度.end(); // 6
    }
    );

    新代码执行以下操作:

  • 获取跟踪器,它是用于与 OTel 跟踪交互的全局对象。
  • 启动一个名为 notification.send_all 的新父级范围,并设置 user_id 属性来标识消息的发送者。
  • 进入一个循环,其中枚举收件人的通知首选项并调用新的子范围ed notification.send 在 notification.send_all 下创建。每个通知都会生成一个新的范围。
  • 为子范围设置更多属性:

    • notification_type – 短信或电子邮件之一
    • user_id – 接收通知的用户 ID
  • 依次关闭每个子 notification.send 范围。
  • 关闭父 notification.send_all 范围。
  • 拥有父范围可以保证即使没有发现用户的通知首选项,每个“发送通知”操作也会得到报告。

  • 在通知程序终端中,按 Ctrl+c 停止通知程序服务。然后重新启动它:

    ^c
    节点 –import ./tracing.mjs 索引.mjs

  • 等待大约十秒,然后在客户端发送一条新消息:

    卷曲-X POST \
    -H“用户 ID:2”\
    -H“内容类型:application/json”\
    -d ‘{“content”: “这是第二条消息”}’ \
    ‘http://localhost:8085/conversations/1/messages’

  • 重新检查 Jaeger UI 中的通知程序范围。您会看到父范围和两个子范围,每个范围都有一个“发送通知”操作:

  • 您现在可以完全实现第一个和第二个目标,因为您可以看到请求在新消息流中经历的所有步骤。每个跨度的计时暴露了这些步骤之间的任何滞后。

    确认消息发送者和通知者正在处理同一事件

    为了全面了解流程,您还需要做一件事。通知程序服务正在处理的事件实际上是由信使服务调度的事件吗?

    您无需进行任何显式更改即可连接两条迹线 – 但您也不想盲目相信自动检测的魔力。

    考虑到这一点,添加一些快速调试代码来验证 NGINX 服务中启动的跟踪确实与消耗的跟踪相同(具有相同的跟踪 ID)由通知程序服务 d。

  • 打开 Messenger 存储库的 app 目录中的 index.mjs 文件并进行以下更改:

    • 在顶部导入语句列表的末尾添加以下行:

      从“@opentelemetry/api”导入{trace};

    • 在现有的黑色行下方添加橙色突出显示的行:

      异步函数 createMessageInConversation(req, res) {
      const 跟踪器 =trace.getActiveSpan();
      console.log(“TRACE_ID:”,tracer.spanContext().traceId);

      新行从Messenger中处理新消息创建的函数内部打印出TRACE_ID。

  • 打开通知程序存储库的应用程序目录中的index.mjs文件,并将以橙色突出显示的行添加到现有黑色行的下方:

    导出异步函数handleMessageConsume(通道,消息,处理程序){
    console.log(“RABBIT_MQ_MESSAGE: “, msg);

    新行打印AMQP事件接收的完整内容由通知服务提供。

  • 通过在信使和通知程序终端中运行以下命令来停止并重新启动信使和通知程序服务:

    ^c
    节点 –import ./tracing.mjs 索引.mjs

  • 等待大约十秒,然后在客户端再次发送消息:

    卷曲-X POST \
    -H“用户 ID:2”\
    -H“内容类型:application/json”\
    -d ‘{“content”: “这是第二条消息”}’ \
    ‘http://localhost:8085/conversations/1/messages’

  • 查看信使和通知程序服务的日志。 Messenger 服务日志包含这样一行,它报告消息的跟踪 ID(运行教程时实际 ID 会有所不同):

    跟踪 ID:29377a9b546c50be629c8e64409bbfb5

  • 同样,通知程序服务日志会在输出中报告跟踪 ID,如下所示:

    _span上下文:{
    跟踪 ID: ‘29377a9b546c50be629c8e64409bbfb5’,
    spanId: ‘a94e9462a39e6dbf’,
    跟踪标志:1,
    跟踪状态:未定义
    },

  • T跟踪 ID 在控制台中匹配,但作为最后一步,您可以将它们与 Jaeger UI 中的跟踪 ID 进行比较。在相关跟踪 ID 端点(您的端点会有所不同,但在本例中为 http://localhost:16686/trace/29377a9b546c50be629c8e64409bbfb5)打开 UI 以查看整个跟踪。 Jaeger 跟踪证实:

    • 消息服务中的 AMQP 自动检测会在分派事件时将此跟踪 ID 作为元数据的一部分添加。
    • 通知程序服务中的 AMQP 自动检测需要元数据并适当设置跟踪上下文。
  • 注意:在实际的生产系统中,一旦确认流程按预期工作,您将删除在本部分中添加的代码。

    资源清理

    您在本教程中创建了一些容器和图像!使用这些说明来删除它们。

    • 要删除任何正在运行的 Docker 容器:

      泊坞窗rm $(docker 停止信使-lb)

    • 要删除平台服务以及信使和通知程序数据库服务:

      cd ~/microservices-march/platform && docker compose down
      cd ~/microservices-march/notifier && docker compose down
      cd ~/microservices-march/messenger && docker compose down

    后续步骤

    恭喜您,您已完成本教程!

    • 您跨 NGINX 反向代理和两个 Node.js 服务设置 OTel 检测。
    • 您以批判的眼光查看了 OTel 自动仪表提供的数据,并添加了一些缺失的遥测数据,以实现 OTel 实验室的目标:
      • 您无需直接更改任何应用程序代码,即可通过消息传递系统合理地了解特定请求的流程。
      • 您确认该流程在正常情况下在不到五秒的时间内端到端执行。

    然而,您仅仅触及了表面理想的跟踪配置可能是什么样子!在生产环境中,您可能希望为每个数据库查询添加自定义跨度等内容,并在所有跨度上添加描述运行时详细信息(例如每个服务的容器 ID)的附加元数据。您还可以实施其他两种类型的 OTel 数据(指标和日志记录),以全面了解系统的运行状况。

    要继续您的微服务教育,请查看 2023 年 3 月的微服务。在第 4 单元:通过可观测性管理微服务的混乱和复杂性中,您将了解可观测性数据的三个主要类别、基础设施和应用对齐的重要性以及方法开始分析深度数据。


    评论

    发表回复

    您的电子邮箱地址不会被公开。 必填项已用 * 标注