Knative 中的分布式追踪

博客文章在发布后不会更新。这篇文章已经发布一年多了,其内容可能已过时,部分链接可能无效。在依赖任何信息之前,请务必核实。

在本文中,您将了解 Knative 中的分布式追踪是如何工作的,我们将探讨 OpenTelemetry 项目如何让此环境下的追踪支持变得更容易。我们将深入了解 Knative 的内部机制,以理解它开箱即用地提供了哪些分布式追踪功能,以及系统的哪些部分需要额外的仪表化。

关于 Knative

Knative 是一个建立在 Kubernetes 之上的无服务器平台,它由一组 CustomResourceDefinitions (CRDs) 组成。该项目分为两个逻辑部分:

  • serving - 促进工作负载/服务的创建、部署和扩展
  • eventing - 促进工作负载之间的事件驱动通信,以实现松耦合的架构

在本文中,我们不会涵盖 Knative 的基础知识,请参考 Knative 文档来熟悉该项目。

Knative 数据流

在我们深入探讨追踪之前,让我们先看一下数据流示例。这将有助于我们理解 Knative 的架构以及需要对系统的哪些部分进行仪表化才能理解请求或事务的计时特性。在下面的图表中,有两个用户工作负载(第一个和第二个),以及一个标记为(1. HTTP)的入站请求,该请求会首先发送到用户工作负载一,然后作为云事件消息发送到工作负载二。

Knative data flow: incoming HTTP request goes through Knative service and queue-proxy sidecar container before it reaches a workload

这张图表中有两个重要事实:

  1. 所有流量都经过 queue-proxy sidecar
  2. 所有流量都经过 Knative 组件。图表中的 Knative 组件是抽象的。它可以是 Knative 激活器服务、Knative 事件代理、调度器等。

从遥测的角度来看,queue-proxy 的作用类似于 Istio 服务网格中的 istio-proxy。它是一个代理,会拦截流向工作负载的所有流量,并为流出或流入工作负载的任何通信发出遥测数据。

Knative 中的分布式追踪

Knative 项目附带了强大的分布式追踪集成。系统的主要部分已经进行了仪表化,并且系统会为流向用户工作负载的事务/请求创建追踪数据。

目前,Knative 内部使用 OpenCensus 仪表化库,以 Zipkin 格式导出数据。进程间上下文传播使用 Zipkin B3W3C Trace-Context 标准。Zipkin B3 传播格式很可能是出于历史原因,以便与使用旧技术仪表化的旧工作负载进行追踪上下文传播。最佳实践是使用 OpenTelemetry 项目原生支持的标准 W3C Trace-Context。

现在让我们来看一个包含两个工作负载(第一个和第二个)的追踪示例。工作流程与上一节的图类似:第一个服务接收 HTTP 调用,然后发送云事件到第二个服务。完整的演示源代码可以在 pavolloffay/knative-tracing 中找到。

Jaeger screenshot showing a Knative trace

该追踪显示了以下服务正在交互:activator、first workload、broker-ingress、imc-dispatcher、broker-filter、activator 和 second workload。有很多服务,对吗?两个工作负载的简单交互产生了一个显示许多 Knative 内部组件的追踪。从可观测性的角度来看,这很好,因为它可以显示基础设施中的问题,并额外显示与 Knative 请求处理相关的成本。

让我们简要地解释一下数据流。入站 HTTP 请求首先经过一个负责扩展工作负载的 activator 服务,然后其执行到达第一个工作负载。第一个工作负载发送一个云事件,该事件经过代理和调度器,最终到达第二个工作负载。

现在让我们仔细看看用户工作负载。第一个服务是一个 Golang 服务,带有一个 REST API 端点。该端点实现会创建一个云事件并将其发送到代理。让我们从可观测性的角度来看一些重要事实:

  • REST API 使用 OpenTelemetry 进行仪表化。这允许我们将从 Knative activator 服务开始的追踪与在工作负载中创建的 span 链接起来,并进一步链接到出站 span - 例如,到调用第二个服务的请求。
  • 工作负载使用仪表化的 Cloudevents 客户端/SDK - 与前一点类似,它允许我们在出站请求(在此场景中是到第二个服务)中继续追踪。

在我们的示例应用程序中,追踪上下文(traceIdspanIdsampled 标志)是如何传播的?追踪上下文通过 HTTP 头进行传播,既包括进入第一个服务的入站 HTTP 请求,也包括发送到第二个服务的云事件。追踪上下文不直接附加到事件的扩展/属性中。

以下是来自第一个服务的请求头日志输出:

2022/02/17 12:53:48 Request headers:
2022/02/17 12:53:48 	X-B3-Sampled: [1]
2022/02/17 12:53:48 	X-B3-Spanid: [af6c239eb7b39349]
2022/02/17 12:53:48 	X-B3-Traceid: [5f2c4775e0e36efc1d554a0b6c456cc1]
2022/02/17 12:53:48 	X-Forwarded-For: [10.244.0.12, 10.244.0.5]
2022/02/17 12:53:48 	Accept-Language: [en,fr;q=0.9,de;q=0.8,sk;q=0.7]
2022/02/17 12:53:48 	Cookie: [_ga=GA1.2.260863911.1644918876]
2022/02/17 12:53:48 	Accept: [text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9]
2022/02/17 12:53:48 	K-Proxy-Request: [activator]
2022/02/17 12:53:48 	Upgrade-Insecure-Requests: [1]
2022/02/17 12:53:48 	User-Agent: [Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36]
2022/02/17 12:53:48 	X-Request-Id: [ee2797b5-1ee9-408e-b1ff-d5e5431977e6]
2022/02/17 12:53:48 	Cache-Control: [max-age=0]
2022/02/17 12:53:48 	X-Forwarded-Proto: [http]
2022/02/17 12:53:48 	Traceparent: [00-5f2c4775e0e36efc1d554a0b6c456cc1-af6c239eb7b39349-01]
2022/02/17 12:53:48 	Accept-Encoding: [gzip, deflate]
2022/02/17 12:53:48 	Forwarded: [for=10.244.0.12;proto=http]
2022/02/17 12:53:48 Response headers:
2022/02/17 12:53:48 	Traceparent: [00-5f2c4775e0e36efc1d554a0b6c456cc1-1cf3f827eba96bf2-01]
2022/02/17 12:53:48

现在让我们看看第二个服务的日志,它公开了消费 Knative 事件的 API。在这种情况下,事件 API 只是一个 HTTP 端点,这是云事件实现的一个细节。

2022/02/17 13:39:36 Event received: Context Attributes,
  specversion: 1.0
  type: httpbody
  source: github/com/pavolloffay
  id: fad4139c-b3fb-48b2-b0f4-fee44addc5f1
  time: 2022-02-17T13:39:34.426355726Z
  datacontenttype: text/plain
Extensions,
  knativearrivaltime: 2022-02-17T13:39:34.491325425Z
Data,
  hello from first, traceid=5f2c4775e0e36efc1d554a0b6c456cc1

我们看到追踪上下文并未直接存在于事件对象中。但是,它已编码在入站传输消息 - HTTP 头中。

未来改进

在上一节中,提到 Knative serving 和 eventing 组件已使用 OpenCensus SDK 进行仪表化。仪表化将在未来更改为 OpenTelemetry,这在 knative/eventing/#3126knative/pkg#855 中有跟踪。SDK 的更改可能不会对用户产生直接影响,但是,它将使用户能够开始原生以 OpenTelemetry 格式 (OTLP) 报告数据。

另一个最近合并的更改是 Cloudevents 语义属性被添加到 OpenTelemetry 规范中。该文档标准化了与 CloudEvents 相关的属性。下面的截图来自尚未开始使用标准化属性名称的演示应用程序。

A screenshot from Jaeger that shows Knative attributes

配置

Knative 中的追踪可以轻松启用。请按照 官方文档进行分步指南。我们在这里简要描述一下该过程:

  1. 部署一个可以接收 Zipkin 格式追踪数据的追踪系统 - Zipkin、Jaeger 或 OpenTelemetry Collector
  2. Knative eventing 中启用追踪
  3. Knative serving 中启用追踪

起初,我建议使用 100% 的采样率配置来捕获集群中所有流量的追踪数据。这将有助于避免任何采样问题,请记住在迁移到生产环境时更改此配置。

结论

我们了解了 Knative 项目开箱即用地提供了哪些分布式追踪功能,以及哪些部分需要用户做更多工作。总的来说,Knative 会发出丰富的追踪数据,但是,一如既往,用户负责仪表化工作负载并确保追踪上下文从入站请求传播到出站请求或事件。这与在服务网格中实现分布式追踪的情况完全相同。

OpenTelemetry 可以帮助仪表化用户工作负载并正确传播追踪上下文。根据语言的不同,用户可以在代码中显式初始化仪表化库,或者甚至 动态地将 OpenTelemetry 自动仪表化注入到工作负载中

参考