扩展收集器

在规划使用 OpenTelemetry Collector 进行可观察性流水线时,您应该考虑如何随着遥测数据的增加来扩展流水线。

以下几节将引导您通过规划阶段,讨论需要扩展的组件、如何确定何时需要扩展以及如何执行计划。

需要扩展的组件

虽然 OpenTelemetry Collector 在一个单独的二进制文件中处理所有的遥测信号类型,但事实上,每种类型可能具有不同的扩展需求,可能需要不同的扩展策略。首先,根据工作负载来确定哪种信号类型预计将占据负载的最大份额,以及预计收集器将接收哪些格式的数据。例如,扩展爬取集群与扩展日志接收器的方式差异很大。还要考虑工作负载的弹性:是否在一天中的特定时间有高峰,或者负载在所有 24 小时内是否相似?一旦收集这些信息,就会了解需要扩展什么。

例如,假设您有数百个要爬取的 Prometheus 端点,每分钟从 fluentd 实例接收一太字节的日志,并且一些应用程序指标和追踪以 OTLP 格式从最新的微服务到达。在这种情况下,您希望有一个可以逐个扩展每个信号的体系结构:扩展 Prometheus 接收器需要协调所有爬取器,以决定哪个爬取器应该处理哪个端点。相比之下,我们可以根据需要水平扩展无状态日志接收器。将指标和追踪的 OTLP 接收器放在第三个收集器群集中将使我们能够隔离故障并更快地进行迭代,而无需担心重启繁忙的流水线。鉴于 OTLP 接收器可以接收所有遥测类型,我们可以将应用程序指标和追踪保留在同一实例上,需要时进行水平扩展。

何时进行扩展

再次,我们应该了解工作负载来确定何时需要扩展或缩减,但是收集器产生的一些指标可以为您提供何时采取行动的重要线索。

memory_limiter 处理器属于流水线的一部分时,收集器可以给您提供一个有用的线索——指标 otelcol_processor_refused_spans 。该处理器允许您限制收集器使用的内存量。尽管收集器可能会使用比此处理器配置的最大量稍多一些的内存,但新数据最终会被 memory_limiter 拦截,此处理器会在这个指标中记录下来。对于所有其他遥测数据类型也存在相同的指标。如果数据被拒绝进入流水线的频率太高,您可能需要扩展收集器群集。一旦节点上的内存消耗明显低于该处理器设置的限制,您可以缩减扩展。

另一组值得关注的指标是与导出器的队列大小有关的指标:otelcol_exporter_queue_capacityotelcol_exporter_queue_size 。当等待工作者可用以发送数据时,收集器会在内存中排队数据。如果工作者不足或者后端太慢,数据会在队列中堆积。一旦队列达到容量上限(otelcol_exporter_queue_size > otelcol_exporter_queue_capacity),它就会拒绝数据(otelcol_exporter_enqueue_failed_spans)。添加更多工作者通常会使收集器导出更多数据,而这可能不是您想要的(请参见不需要扩展的情况)。

还值得熟悉您打算使用的组件,因为不同的组件可能会产生其他指标。例如,负载均衡导出器将记录有关导出操作的持续时间信息,并将其作为直方图 otelcol_loadbalancer_backend_latency 的一部分公开。您可以提取此信息以确定所有后端处理请求所花费的相似时间量:单个后端较慢可能表明 Collector 外部存在问题。

对于执行抓取操作的接收器(例如 Prometheus 接收器),当完成所有目标的抓取所需的时间接近抓取间隔时,应进行扩展或分片处理。当发生这种情况时,是时候添加更多爬取器了,通常是收集器的新实例。

不需要扩展

知道何时扩展和了解哪些迹象表明扩展操作不会带来任何好处可能一样重要。一个示例是当遥测数据库跟不上负载时:只有扩展数据库才能帮助,添加收集器到群集中不会有帮助。类似地,当收集器与后端之间的网络连接饱和时,添加更多收集器可能会产生有害的副作用。

再次,观察指标 otelcol_exporter_queue_sizeotelcol_exporter_queue_capacity 可以捕捉到这种情况。如果队列大小接近队列容量,表明导出数据的速度比接收数据的速度慢。您可以尝试增加队列大小,这将导致收集器消耗更多内存,但也会为后端提供一些空间,以便在不永久丢弃遥测数据的情况下进行呼吸。但是,如果您不断增加队列容量而队列大小保持以相同比例上升,这表明您可能需要查看 Collector 之外的部分。还要注意,在这里添加更多工作者是无济于事的:您只会给已经承受高负载的系统增加压力。

另一个后端可能存在问题的迹象是 otelcol_exporter_send_failed_spans 指标的增加:这表明向后端发送数据失败。在出现这种情况时,扩展收集器很可能只会使情况更糟。

如何进行扩展

此时,我们已经知道了哪些部分的流水线需要进行扩展。关于扩展,我们有三种类型的组件:无状态组件、爬取器和有状态组件。

大多数收集器组件是无状态的。即使它们在内存中保存一些状态,这对于扩展目的来说并不重要。

爬取器(如 Prometheus 接收器)被配置为从外部位置获取遥测数据,然后逐个目标地进行爬取,并将数据放入流水线中。

像尾部采样处理器这样的组件不能轻松扩展,因为它们在内存中保存一些相关的状态以进行业务处理。在扩展之前,需要仔细考虑这些组件。

扩展无状态收集器

好消息是,大多数情况下,扩展收集器很容易,只需添加新的副本并使用现成的负载均衡器即可。当使用 gRPC 接收数据时,建议使用了解 gRPC 的负载均衡器。否则,客户端将始终连接到同一个支持的收集器。

您仍然应该考虑可靠性的同时拆分收集管道。例如,当您的工作负载在 Kubernetes 上运行时,您可能希望使用 DaemonSet,在与工作负载相同的物理节点上放置一个收集器以及一个远程中央收集器,负责在将数据发送到存储之前对数据进行预处理。当节点的数量较少且 Pod 的数量较多时,Sidecar 可能更合适,这样会获得更好的负载平衡,以供在 Collector 层之间的 gRPC 连接中使用,而无需使用专门的 gRPC 负载平衡器。使用 Sidecar 还有助于避免当一个 DaemonSet pod 失败时,将关键组件关掉影响到节点上的所有 pod。

Sidecar 模式是将一个容器添加到工作负载的 Pod 中。OpenTelemetry Operator 能够自动为您添加 Sidecar。为此,您需要一个 OpenTelemetry Collector CR,并在 PodSpec 或 Pod 中注释告知操作员注入一个 Sidecar:

---
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: sidecar-for-my-workload
spec:
  mode: sidecar
  config: |
    receivers:
      otlp:
        protocols:
          grpc:
    processors:

    exporters:
      # Note: Prior to v0.86.0 use the `logging` instead of `debug`.
      debug:

    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: []
          exporters: [debug]    
---
apiVersion: v1
kind: Pod
metadata:
  name: my-microservice
  annotations:
    sidecar.opentelemetry.io/inject: 'true'
spec:
  containers:
    - name: my-microservice
      image: my-org/my-microservice:v0.0.0
      ports:
        - containerPort: 8080
          protocol: TCP

如果您愿意跳过操作员并手动添加 Sidecar,则可以参考以下示例:

apiVersion: v1
kind: Pod
metadata:
  name: my-microservice
spec:
  containers:
    - name: my-microservice
      image: my-org/my-microservice:v0.0.0
      ports:
        - containerPort: 8080
          protocol: TCP
    - name: sidecar
      image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector:0.69.0
      ports:
        - containerPort: 8888
          name: metrics
          protocol: TCP
        - containerPort: 4317
          name: otlp-grpc
          protocol: TCP
      args:
        - --config=/conf/collector.yaml
      volumeMounts:
        - mountPath: /conf
          name: sidecar-conf
  volumes:
    - name: sidecar-conf
      configMap:
        name: sidecar-for-my-workload
        items:
          - key: collector.yaml
            path: collector.yaml

扩展爬取器

一些接收器正在主动获取遥测数据并将其放入流水线中,例如 hostmetricsprometheus 接收器。虽然通常不需要扩展主机度量,但是我们可能需要分片处理用于 Prometheus 接收器的数千个端点的爬取工作。我们不能简单地使用相同配置添加更多实例,因为每个收集器都会尝试爬取集群中的每个端点,导致出现更多问题,如无序的采样。

解决方案是通过收集器实例将端点进行分片,以便如果我们添加收集器的另一副本,每个副本都将在不同的端点集上操作。

一种方法是为每个收集器使用一个配置文件,这样每个收集器只会发现与该收集器相关的端点。例如,每个收集器可以负责一个 Kubernetes 命名空间或工作负载上的特定标签。

扩展 Prometheus 接收器的另一种方法是使用目标分配器(Target Allocator):它是一个额外的二进制文件,可以作为 OpenTelemetry Operator 的一部分部署,并将给定配置的 Prometheus 抓取目标分布到收集器群集上。您可以使用类似以下的自定义资源(CR)来使用 Target Allocator:

apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: collector-with-ta
spec:
  mode: statefulset
  targetAllocator:
    enabled: true
  config: |
    receivers:
      prometheus:
        config:
          scrape_configs:
          - job_name: 'otel-collector'
            scrape_interval: 10s
            static_configs:
            - targets: [ '0.0.0.0:8888' ]

    exporters:
      # Note: Prior to v0.86.0 use the `logging` instead of `debug`.
      debug:

    service:
      pipelines:
        traces:
          receivers: [prometheus]
          processors: []
          exporters: [debug]    

调解之后,OpenTelemetry Operator 将将收集器的配置转换为以下配置:

exporters:
   # Note: Prior to v0.86.0 use the `logging` instead of `debug`.
   debug: null
 receivers:
   prometheus:
     config:
       global:
         scrape_interval: 1m
         scrape_timeout: 10s
         evaluation_interval: 1m
       scrape_configs:
       - job_name: otel-collector
         honor_timestamps: true
         scrape_interval: 10s
         scrape_timeout: 10s
         metrics_path: /metrics
         scheme: http
         follow_redirects: true
         http_sd_configs:
         - follow_redirects: false
           url: http://collector-with-ta-targetallocator:80/jobs/otel-collector/targets?collector_id=$POD_NAME
service:
   pipelines:
     traces:
       exporters:
       - debug
       processors: []
       receivers:
       - prometheus

请注意,操作员添加了一个 global 部分和一个 新的 http_sd_configsotel-collector 抓取配置,指向它创建的一个 Target Allocator 实例。因此,要扩展收集器,只需更改 CR 的“replicas”属性,Target Allocator 将相应地分发负载,并为每个收集器实例(Pod)提供自定义的 http_sd_config

扩展有状态收集器

某些组件可能在内存中保存数据,在扩展时会产生不同的结果。尾部采样处理器就是这种情况,它在内存中会为一段时间保存跨度,并仅在跨度被视为完整时才对采样决策进行评估。通过添加更多收集器的方式来扩展收集器群集意味着不同的收集器将接收到关于给定跨度的数据,导致每个收集器在评估该跨度是否应采样时得出不同的答案。这种行为导致跨度缺失,误导了该事务中发生的情况。

当使用跨度到度量的处理器生成服务指标时,会发生类似情况。当不同的收集器接收到与同一服务相关的数据时,基于服务名称的聚合将不准确。

为了解决这个问题,您可以在承担尾部采样或跨度到指标处理的角色的收集器之前部署一层包含负载均衡导出器的收集器。负载均衡导出器将一致性地对跟踪 ID 或服务名称进行哈希,并确定哪个后端收集器应接收该跨度。您可以将负载均衡导出器配置为使用给定 DNS A 记录的主机列表,例如 Kubernetes headless 服务。当支持该服务的部署进行扩展或缩减时,负载均衡导出器最终会看到更新后的主机列表。或者,您可以指定要由负载均衡导出器使用的固定主机列表。您可以通过增加副本数量来扩展配置了负载均衡导出器的收集器层。请注意,每个收集器可能在不同的时间运行 DNS 查询,短暂地导致了几个时刻的集群视图的不同。我们建议缩短间隔值,以便在高度弹性的环境下仅仅在短时间内在集群视图中存在差异。

下面是一个使用 DNS A 记录(observability 命名空间上的 Kubernetes 服务 otelcol)作为后端信息输入的示例配置:

receivers:
  otlp:
    protocols:
      grpc:

processors:

exporters:
  loadbalancing:
    protocol:
      otlp:
    resolver:
      dns:
        hostname: otelcol.observability.svc.cluster.local

service:
  pipelines:
    traces:
      receivers:
        - otlp
      processors: []
      exporters:
        - loadbalancing
最后修改 December 10, 2023: translate (a4350d6e)