Prometheus 和 OpenTelemetry - 更好地协同工作

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

Image of a Greek god holding a torch with the Prometheus logo, and OTel logo

PrometheusOpenTelemetry 这样的工具可以帮助我们监控复杂分布式系统的健康状况、性能和可用性。两者都是 Cloud Native Computing Foundation (CNCF) 旗下的开源项目——但它们各自在可观测性中扮演什么角色呢?

OpenTelemetry(简称 OTel)是一个供应商中立的开放标准,用于仪表化、生成、收集和导出遥测数据。Prometheus 是可观测性领域的重要组成部分,被各组织广泛用于监控和告警。

虽然 Prometheus 和 OTel 都可以发出指标,但关于它们之间的差异和相似之处还有很多需要介绍的内容,这超出了本文的范围。相反,我们想向您展示 OTel 如何支持 Prometheus,特别是在 Kubernetes 环境中。您将学到:

  • OTel Collector 的 Prometheus Receiver 如何用于摄取 Prometheus 指标。
  • 通过 OTel 原生选项(如 K8s cluster receiver 和 Kubelet stats receiver)进行 Prometheus 指标收集的替代方法。

我们还将深入探讨 OTel Operator 的 Target Allocator (TA),并学习:

  • 它如何用于 Prometheus 服务发现。
  • 它如何确保 Prometheus 目标的均匀分布。

OTel 和 Prometheus

由于 OTel 主要专注于可观测性的仪表化部分,因此它不提供用于存储遥测数据的后端;您必须将数据转发给后端供应商进行存储、告警和查询。

另一方面,Prometheus 除了提供仪表化客户端外,还提供了一个可用于存储指标的时间序列数据存储。您可以通过其 Web 用户界面查看图形和图表,设置告警,并查询您的数据。它还包含一种数据格式,称为 Prometheus 文本格式

Prometheus 的数据以维度时间序列的形式存储,这意味着数据具有属性(例如,标签或维度)和时间戳。

Prometheus服务器从配置文件中定义的目标收集 Prometheus 指标数据。目标是暴露指标供 Prometheus 服务器存储的端点。

Prometheus 在监控领域非常普及,许多工具都原生支持以 Prometheus 格式发出指标,包括 KubernetesHashiCorp 的 Nomad。对于那些不支持的,有许多供应商和社区构建的 Prometheus exporter 来聚合数据并导入 Prometheus。

虽然您可以使用 Prometheus 监控各种基础设施和应用程序指标,但它最受欢迎的用例之一是监控 Kubernetes。这是我们在本文中将重点关注的 Prometheus 监控方面。

使用 OpenTelemetry 的 Prometheus 指标

在本节中,您将了解几个 OTel Collector 组件,这些组件展示了 OTel 和 Prometheus 之间的互操作性。

首先,让我们快速回顾一下 Collector——它是一个 OTel 组件,可用于从多个源收集遥测数据并导出到多个目的地。Collector 还处理遥测数据处理,例如修改数据属性和清理个人身份信息。例如,您可以使用 Prometheus SDK 生成指标,使用 Collector 摄取它们,进行一些处理(如果需要),然后将其转发到您选择的后端。

Diagram showing the different components of the OTel Collector

the Prometheus receiver 允许您从任何公开 Prometheus 指标的软件收集指标。它作为 Prometheus 的直接替代品来抓取您的服务,并支持 `scrape_config` 中的完整集

如果您对 exemplars 感兴趣,即与指标事件关联 OTel 上下文的已记录值,您也可以使用 Prometheus receiver。请注意,exemplars 目前仅在 OpenMetrics 格式中可用。

关于此组件需要考虑的一点是,它正处于积极开发中;因此,它有几个限制,包括它是一个有状态的组件。此外,不建议在运行多个 Collector 副本没有 target allocator 的情况下使用此组件,因为在这种状态下:

  • Collector 无法自动扩展抓取
  • 如果副本使用相同的配置运行,则会多次抓取目标
  • 如果您想手动分片抓取,则需要为每个副本配置不同的抓取配置

要将指标从 OTel Collector 导出到 Prometheus,您有以下选项:Prometheus exporterPrometheus Remote Write exporter。您还可以使用 Collector 默认提供的 OTLP HTTP exporter,并使用 Prometheus 原生的 OTLP 端点。请注意,Prometheus 现在也原生支持 OTLP

Prometheus exporter 允许您以 Prometheus 格式发送数据,然后由 Prometheus 服务器抓取。它用于通过 Prometheus 抓取 HTTP 端点报告指标。您可以尝试此示例以了解更多信息。但是,抓取并不能真正扩展,因为所有指标都通过一次抓取发送。

为了解决扩展问题,您可以改用 Prometheus Remote Write exporter,它允许您从多个 Collector 实例推送数据,而不会出现任何问题。由于 Prometheus 也接受远程写入摄取,因此如果您正在生成 OTel 指标并希望将其发送到与 Prometheus 远程写入兼容的后端,也可以使用此 exporter。

请注意,Prometheus Server 中的 Prometheus Remote Write 目前不支持元数据,例如 Help 和 Type。有关更多信息,请查看问题 #13163 以及 问题 #12608。这将在 Prometheus Remote Write v2.0 中解决。

要了解有关这两个 exporter 架构的更多信息,请参阅使用 Prometheus Remote Write exporter

使用 Target Allocator

可扩展性是 Prometheus 面临的常见挑战;即在管理越来越多的监控目标和指标的同时,有效保持性能和资源分配的能力。一种有助于解决此问题的方法是根据标签或维度对工作负载进行分片,这意味着使用多个 Prometheus 实例根据特定参数处理您的指标。这有助于减轻单个实例的负担。但是,此方法需要考虑两件事。

第一,为了避免查询分片实例,您需要一个管理实例;这意味着您需要 N+1 个 Prometheus 实例,其中 +1 的内存等于 N,从而使您的内存请求翻倍。其次,Prometheus 分片要求每个实例都抓取目标,即使它最终会被丢弃。

需要注意的是,如果您可以拥有一个内存总量相当于单个实例内存之和的 Prometheus 实例,那么分片的益处就不大了,因为您可以直接使用更大的实例抓取所有内容。人们分片的一个常见原因是为了某种程度的容错能力。例如,如果一个 Prometheus 实例出现内存不足 (OOM) 的情况,那么您的整个告警管道就不会离线。

幸运的是,OTel Operator 的 Target Allocator (TA) 可以对此有所帮助。例如,它可以自动丢弃任何它知道不会被抓取的目标。TA 还会自动为您分片目标,而如果您使用 `hashmod` 进行分片,则需要根据您拥有的副本数量更新配置。TA 还允许您继续使用 PodMonitor 和 ServiceMonitor 等资源来持续收集有关 Kubernetes 基础设施的 Prometheus 指标。

Target Allocator 是 OTel Operator 的一部分。OTel Operator 是一个 Kubernetes Operator,它:

事实上,Operator 在 Kubernetes 中创建了两种新的自定义资源 (CR) 类型来支持此功能:OpenTelemetry Collector CRAutoinstrumentation CR

今天,我们将重点介绍 Target Allocator。TA 是 Operator 的 OTel Collector 管理功能的可选组件。

简而言之,Target Allocator 是一种用于解耦 Prometheus 的服务发现和指标收集功能的机制,它允许它们独立扩展。OTel Collector 在不需要安装 Prometheus 的情况下管理 Prometheus 指标。TA 管理 Collector 的 Prometheus Receiver 的配置。

Target Allocator 执行两项功能:

  • 将 Prometheus 目标均匀分布在 OTel Collector 池中
  • Prometheus 自定义资源的发现

让我们分别深入研究一下。

Prometheus 目标的均匀分布

Target Allocator 的第一项工作是发现要抓取的目标以及要分配目标的 OTel Collector。它通过以下方式实现:

  1. Target Allocator 查找所有要抓取的指标目标
  2. Target Allocator 查找所有可用的 Collector
  3. Target Allocator 确定哪些 Collector 抓取哪些指标
  4. Collectors 查询 Target Allocator 以了解要抓取的指标
  5. Collectors 抓取其分配的目标

这意味着是 OTel Collector——而不是 Prometheus Scraper——收集指标。

Target 是一个为 Prometheus 存储提供指标的端点。Scrape 是通过 HTTP 请求从目标实例收集指标、解析响应并将收集的样本摄入存储的操作。

sequenceDiagram
  participant Target Allocator
  participant Metrics Targets
  participant OTel Collectors
  Target Allocator ->>Metrics Targets: 1. Discover Metrics targets
  Target Allocator ->>OTel Collectors: 2. Discover available Collectors
  Target Allocator ->>Target Allocator: 3. Assign Metrics targets
  OTel Collectors ->>Target Allocator: 4. Query TA for Metrics endpoints scrape
  OTel Collectors ->>Metrics Targets: 5. Scrape Metrics target

Prometheus 自定义资源的发现

Target Allocator 的第二项工作是提供 Prometheus Operator CR 的发现,即 ServiceMonitor 和 PodMonitor

过去,所有 Prometheus 抓取配置都必须通过 Prometheus Receiver 完成。当启用 Target Allocator 的服务发现功能后,TA 通过从集群中部署的 PodMonitor 和 ServiceMonitor 创建 Prometheus Receiver 中的抓取配置来简化 Prometheus Receiver 的配置。

flowchart RL
  pm(PodMonitor)
  sm(ServiceMonitor)
  ta(Target Allocator)
  oc1(OTel Collector)
  oc2(OTel Collector)
  oc3(OTel Collector)
  ta --> pm
  ta --> sm
  oc1 --> ta
  oc2 --> ta
  oc3 --> ta
  sm ~~~|1\. Discover Prometheus Operator CRs| sm
  ta ~~~|2\. Add job to TA scrape configuration| ta
  oc3 ~~~|3\. Add job to OTel Collector scrape configuration| oc3

尽管为了使用 Target Allocator 进行 Prometheus CR 发现,不需要在 Kubernetes 集群中安装 Prometheus,但 TA 要求安装 ServiceMonitor 和 PodMonitor。这些 CR 与 Prometheus Operator 一起打包;但是,它们也可以独立安装。最简单的方法是获取单独的 PodMonitor YAMLServiceMonitor YAML 自定义资源定义 (CRD) 的副本。

OTel 支持 PodMonitor 和 ServiceMonitor Prometheus 资源,因为它们在 Kubernetes 基础设施监控中被广泛使用。因此,OTel Operator 开发人员希望将它们轻松添加到 OTel 生态系统中。

PodMonitor 和 ServiceMonitor 仅限于从 Pod 收集指标,无法抓取其他端点,例如 kubelet。在这种情况下,您仍然需要依赖 Collector 的 Prometheus Receiver 中的 Prometheus 抓取配置。

配置

以下是 OTel Collector CR 的 YAML 配置。请注意,此 Collector 运行在名为 `opentelemetry` 的命名空间中,但它也可以运行在您喜欢的任何命名空间中。

主要组件是:

apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: otelcol
  namespace: opentelemetry
spec:
  mode: statefulset
  targetAllocator:
    enabled: true
    serviceAccount: opentelemetry-targetallocator-sa
    prometheusCR:
      enabled: true
  config: |
    receivers:
      otlp:
        protocols:
          grpc:
          http:
      prometheus:
        config:
          scrape_configs:
          - job_name: 'otel-collector'
            scrape_interval: 30s
            static_configs:
            - targets: [ '0.0.0.0:8888' ]
        target_allocator:
          endpoint: http://otelcol-targetallocator
          interval: 30s
          collector_id: "${POD_NAME}"

要使用 Target Allocator,您需要将 `spec.targetallocator.enabled` 设置为 `true`。(请参阅关于支持模式的先前注释。)

接下来,您需要确保已部署 Collector 的 Prometheus Receiver 在 `spec` 的 Collector 配置部分中通过设置 `target_allocator.endpoint` 来感知 Target Allocator。

receivers:
  prometheus:
    config:
      scrape_configs:
        - job_name: 'otel-collector'
          scrape_interval: 30s
          static_configs:
            - targets: ['0.0.0.0:8888']
    target_allocator:
      endpoint: http://otelcol-targetallocator
      interval: 30s
      collector_id: '${POD_NAME}'

Prometheus receiver 配置指向的 Target Allocator 端点是 OTel Collector 的名称(在本例中为 `otelcol`)和 `-targetallocator` 后缀的组合。

要使用 Prometheus 服务发现功能,您需要通过将 `spec.targetallocator.prometheusCR.enabled` 设置为 `true` 来启用它。

最后,如果您想启用 Target Allocator 的 Prometheus CR 功能,您将需要定义自己的 ServiceMonitor 和 PodMonitor 实例。以下是一个示例 ServiceMonitor 定义,它表示查找一个标签为 `app: my-app` 的服务,其中有一个端口名为 `prom` 的端点,并每 15 秒抓取一次。

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: sm-example
  namespace: opentelemetry
  labels:
    app.kubernetes.io/name: py-prometheus-app
    release: prometheus
spec:
  selector:
    matchLabels:
      app: my-app
  namespaceSelector:
    matchNames:
      - opentelemetry
  endpoints:
    - port: prom
      interval: 15s

相应的 `Service` 定义,它只是一个标准的 Kubernetes Service 定义,如下所示:

apiVersion: v1
kind: Service
metadata:
  name: py-prometheus-app
  namespace: opentelemetry
  labels:
    app: my-app
    app.kubernetes.io/name: py-prometheus-app
spec:
  selector:
    app: my-app
    app.kubernetes.io/name: py-prometheus-app
  ports:
    - name: prom
      port: 8080

因为 `Service` 有一个名为 `app: my-app` 的标签和一个名为 `prom` 的端口,所以它会被 ServiceMonitor 拾取。

您可以为希望监控的每个服务创建单独的 ServiceMonitor,也可以创建一个单独的 ServiceMonitor 来包含所有服务。PodMonitor 的情况也一样。

在 Target Allocator 开始抓取之前,您需要设置 Kubernetes 基于角色的访问控制 (RBAC)。这意味着您需要有一个 ServiceAccount 和相应的集群角色,以便 Target Allocator 能够访问所有必需的资源来提取指标。

您可以创建自己的 ServiceAccount,并在 OTel Collector CR 中将其引用为 `spec.targetAllocator.serviceAccount`。然后,您需要为该 ServiceAccount 配置 ClusterRoleClusterRoleBinding

如果您省略 ServiceAccount 配置,Target Allocator 将自动为您创建一个 ServiceAccount。ServiceAccount 的默认名称是 Collector 名称和 `-collector` 后缀的组合。默认情况下,此 ServiceAccount 没有定义的策略,因此您需要创建自己的 ClusterRoleClusterRoleBinding

以下是从 OTel Target Allocator readme 中提取的 RBAC 配置示例。它包括 ServiceAccount、ClusterRole 和 ClusterRoleBinding 配置。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: opentelemetry-targetallocator-sa
  namespace: opentelemetry
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: opentelemetry-targetallocator-role
rules:
  - apiGroups:
      - monitoring.coreos.com
    resources:
      - servicemonitors
      - podmonitors
    verbs:
      - '*'
  - apiGroups: ['']
    resources:
      - namespaces
    verbs: ['get', 'list', 'watch']
  - apiGroups: ['']
    resources:
      - nodes
      - nodes/metrics
      - services
      - endpoints
      - pods
    verbs: ['get', 'list', 'watch']
  - apiGroups: ['']
    resources:
      - configmaps
    verbs: ['get']
  - apiGroups:
      - discovery.k8s.io
    resources:
      - endpointslices
    verbs: ['get', 'list', 'watch']
  - apiGroups:
      - networking.k8s.io
    resources:
      - ingresses
    verbs: ['get', 'list', 'watch']
  - nonResourceURLs: ['/metrics']
    verbs: ['get']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: opentelemetry-targetallocator-rb
subjects:
  - kind: ServiceAccount
    name: opentelemetry-targetallocator-sa
    namespace: opentelemetry
roleRef:
  kind: ClusterRole
  name: opentelemetry-targetallocator-role
  apiGroup: rbac.authorization.k8s.io

稍微放大一下之前的 ClusterRole,以下规则将提供 Target Allocator 查询所有其所需目标所需的最少访问权限,这些目标基于任何 Prometheus 配置。

- apiGroups: ['']
  resources:
    - nodes
    - nodes/metrics
    - services
    - endpoints
    - pods
  verbs: ['get', 'list', 'watch']
- apiGroups: ['']
  resources:
    - configmaps
  verbs: ['get']
- apiGroups:
    - discovery.k8s.io
  resources:
    - endpointslices
  verbs: ['get', 'list', 'watch']
- apiGroups:
    - networking.k8s.io
  resources:
    - ingresses
  verbs: ['get', 'list', 'watch']
- nonResourceURLs: ['/metrics']
  verbs: ['get']

如果您在 OpenTelemetryCollector CR 中启用了 `prometheusCR`(将 `spec.targetAllocator.prometheusCR.enabled` 设置为 `true`),您还需要定义以下角色。这些角色授予 Target Allocator 访问 PodMonitor 和 ServiceMonitor CR 的权限。它还授予 PodMonitor 和 ServiceMonitor 的命名空间访问权限。

- apiGroups:
    - monitoring.coreos.com
  resources:
    - servicemonitors
    - podmonitors
  verbs:
    - '*'
- apiGroups: ['']
  resources:
    - namespaces
  verbs: ['get', 'list', 'watch']

Kubernetes 的其他 OTel 组件

本节介绍了您可以用来捕获 Kubernetes 指标的其他 OTel Collector 组件。

接收数据

处理数据

  • Kubernetes Attributes Processor:添加 Kubernetes 上下文,从而
  • 使您能够将应用程序遥测数据与 Kubernetes 遥测数据相关联。
  • – 被认为是使用 OpenTelemetry 监控 Kubernetes 最重要的组件之一。
  • 使用 OpenTelemetry

您还可以使用 Kubernetes attributes processor,通过您添加到 Pod 和命名空间的 Kubernetes 标签和注释为跟踪、指标和日志设置自定义资源属性。

还有一些 Collector 组件可用于监控 Kubernetes,包括特定于 Kubernetes 的组件以及通用的处理器,例如 batch、memory limiter 和 resource 处理器。要了解更多信息,请参阅Kubernetes 的重要组件

在配置了 Collector 配置文件中的组件后,您需要在管道部分中启用它们。数据管道使您能够收集处理并将数据从任何源路由到一个或多个目的地

优点和缺点

以下是我们在本文中介绍的设置的优缺点。

优点

  • 不必维护 Prometheus 作为数据存储,这意味着需要维护的基础设施总体上减少了——特别是如果您选择了一个全能的可观测性后端来摄取 OTel 数据(跟踪、指标、日志)。
  • 虽然您仍然需要维护 ServiceMonitor 和 PodMonitor,但这比保持 Prometheus Operator 更新要容易得多。
  • 允许您获得完整的 OTel 解决方案,同时仍然获取您的 Prometheus 指标。
  • OTel 可以提供跟踪和日志,除了指标,还可以关联这些信号,从而增强 Kubernetes 环境的可观测性。
  • OTel 提供了方便的工具,例如 Target Allocator 和 OTel Collector 组件,为配置和部署选项提供了灵活性。

缺点

  • 采用和管理新的可观测性工具对于不熟悉 OTel 概念、组件和工作流程的用户来说,学习曲线很陡峭。
  • PromQL(Prometheus 的强大查询语言)的用户仍然可以使用它,如果他们将指标发送到 Prometheus 兼容的后端。
  • OTel 本身包含许多活动部件,并且在可扩展性和采用方面带来了自身的挑战。
  • OTel 的成熟度和稳定性各不相同;Prometheus 拥有成熟的生态系统。
  • 维护 OTel 组件需要额外的计算和人力资源。
  • 管理和维护 Prometheus 和 OTel 组件会增加监控基础设施的运维复杂性。

结论

Prometheus 维护者也一直在从 Prometheus 端进一步开发两个项目之间的互操作性,以便更容易地将其用作 OTLP 指标的后端。例如,Prometheus 现在可以接受 OTLP,很快,您就可以使用 Prometheus exporters 来导出 OTLP。因此,如果您有一个使用 Prometheus SDK 仪表化的服务,您将能够推送 OTLP 并利用丰富的 Prometheus exporter 生态系统供 OTel 用户使用。维护者还在致力于添加对增量时态的支持。此组件将增量样本聚合到其各自的累积对应项。有关 Prometheus 对 OTel 的承诺的更多信息,请参阅我们对 OpenTelemetry 的承诺

无论您决定如何使用 OTel 来收集 Prometheus 指标,最终适合您组织的方案取决于您的业务需求。利用前面讨论的 OTel 组件,您可以将所有指标转换为 Prometheus 格式,或者将 Prometheus 指标转换为 OTLP。尽管 Prometheus 本身并非为长期数据存储而设计,并且在扩展性方面存在挑战,但存在诸如 MimirThanosCortex 等开源项目,可以帮助解决这些问题。

无论您是否选择在组织中实施这些解决方案,了解还有其他选项可以帮助您通过 OTel 和 Prometheus 实现卓越的可观察性,这一点还是很好的。

本文的一个版本 最初发布 在 New Relic 博客上。

最后修改于 2025 年 5 月 9 日:修复流程图语法 (#6454) (9bbe5e67)