基于 OpenTelemetry Collector 写入指标到阿里云 Prometheus

该方案将部署在 ACK 容器集群,且已经进行 OpenTelemetry SDK 埋点的应用,通过阿里云可观测监控 Prometheus 版实现指标全面监控。统一采集 OTel 原生指标、业务自定义埋点指标和 Trace Span 转化的指标,实现应用全面监控。将 Trace 数据转换为指标,可以有效降低数据摄取量。

OpenTelemetry 概述

OpenTelemetry(简称 OTel)是一个开源的可观测性框架,提供统一的 API、SDK 和工具,用于生成、收集和导出分布式系统的遥测数据(包括指标(Metrics)追踪(Traces)和日志(Logs))。其核心目标是解决不同工具和系统之间的可观测性数据碎片化问题。

OpenTelemetry 的指标(Metrics)是量化系统行为的结构化数据,用于监控系统性能和健康状态。指标来源取决于不同语言的 SDK 或 Agent 埋点。以 Java 应用为例,通常指标包括:JVM 标准指标、用户自埋点指标和 Trace 数据转换的指标。

OpenTelemetry 的指标设计与 Prometheus 兼容,通过 OpenTelemetry Collector 可以将指标转换为 Prometheus 格式,从而无缝对接阿里云可观测监控 Prometheus 版

OpenTelemetry Collector 的作用

OpenTelemetry Collector 是一个可扩展的数据处理管道,负责从数据源(如应用、服务)收集遥测数据,并将其转换为目标系统(如 Prometheus)所需的格式。

数据收集

应用通过 OpenTelemetry SDK 生成指标数据,通过 OTLP(OpenTelemetry Protocol) 或其他协议(如 Prometheus 原生协议)发送到 Collector。

数据转换

在社区 Collector 扩展中,您可以使用以下两种 Exporter 将 OTel 指标转换为 Prometheus 格式。

  • Prometheus Exporter 将 OpenTelemetry 指标转换为 Prometheus 格式,并提供端点供 Prometheus Agent 来抓取数据。

    1. 指标名称映射:将 OpenTelemetry 指标名称转换为 Prometheus 兼容的格式。

    2. 标签处理:保留或重命名标签以符合 Prometheus 的命名规则。

    3. 数据类型转换

      • gauge → Prometheus gauge

      • sum → Prometheus countergauge(根据 Monotonic 属性)

      • histogram → Prometheus histogram(通过 bucketsum 子指标)

      参考配置如下,此配置意味着在 1234端口上提供指标抓取 Endpoint。

      exporters:
        prometheus:
          endpoint: "0.0.0.0:1234"
          namespace: "acs"
          const_labels:
            label1: value1
          send_timestamps: true
          metric_expiration: 5m
          enable_open_metrics: true
          add_metric_suffixes: false
          resource_to_telemetry_conversion:
            enabled: true
  • Prometheus Remote Write Exporter 将 OpenTelemetry 指标转换为 Prometheus 格式,并直接通过 RemoteWrite 协议写入目标 Prometheus 服务。

    与 Prometheus Exporter 一样,该 Exporter 同样会进行数据格式的转换。参考配置如下:

    exporters:
      prometheusremotewrite:
        endpoint: http://<Prometheus Endpoint>/api/v1/write
        namespace: "acs"
        resource_to_telemetry_conversion:
          enabled: true
        timeout: 10s   
        headers:
          Prometheus-Remote-Write-Version: "0.1.0"
        external_labels:
          data-mode: metrics

在 ACK 集群中部署的应用最佳实践

一、阿里云 Prometheus 服务准备

  • 集群已开启 Prometheus 监控

    如果您的 ACK 集群已经开启 Prometheus 监控,那么 Prometheus 实例已经存在。您可以登录云监控控制台,在Prometheus 监控 > 实例列表页面,查询与 ACK 容器集群名称一致的 Prometheus 实例。image

  • 集群未开启 Prometheus 监控

    您可以登录容器服务管理控制台,单击目标集群,然后在组件管理页面安装 ack-arms-prometheus 组件,安装后会自动开启集群的 Prometheus 监控。58

二、Collector 部署(SideCar 模式)

由于指标统计计算要求同一个 Pod 实例的 Metric 或 Trace 到达同一个 Collector,采用 Gateway 模式部署需要处理负载均衡,相对复杂。因此建议您直接采用 SideCar 模式部署 Collector。

使用 Prometheus Exporter 模式

说明

该方式的好处是用户不需要处理 Prometheus 的写入链路鉴权相关事项,同时可以调整采集配置来调整指标采集时间间隔。

部署架构图
image
部署配置参考
# Kubernetes Deployment 示例
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    metadata:
      labels:
        # Pod 上打入特定标签,通常以应用信息来命名,方便后续配置指标采集
        observability: opentelemetry-collector
    spec:
      volumes:
      - name: otel-config-volume
        configMap:
          # 该配置通过下述的 Collector 配置参考创建
          name: otel-config
      containers:
        - name: app
          image: your-app:latest
          env:
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: http://localhost:4317
        - name: otel-collector
          # 可直接使用我们提供的 Collector 镜像(包含了Prometheus相关扩展插件)
          # 根据区域信息,替换镜像名称中的regionId
          image: registry-<regionId>.ack.aliyuncs.com/acs/otel-collector:v0.128.0-7436f91	
          args: ["--config=/etc/otel/config/otel-config.yaml"]
          ports:
            - containerPort: 1234  # Prometheus 端点
              name: metrics
          volumeMounts:
          - name: otel-config-volume
            mountPath: /etc/otel/config
Collector 配置参考
说明

请根据应用请求量配置 Collector 的资源限制(CPU/Memory),使其可以正常处理所有数据。

apiVersion: v1
kind: ConfigMap
metadata:
  name: otel-config
  namespace: <app-namespace>
data:
  otel-config.yaml: |
    extensions:
      zpages:
        endpoint: localhost:55679

    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318
    
    processors:
      batch:
      memory_limiter:
        # 75% of maximum memory up to 2G
        limit_mib: 1536
        # 25% of limit up to 2G
        spike_limit_mib: 512
        check_interval: 5s
      resource:
        attributes:
          - key: process.runtime.description
            action: delete
          - key: process.command_args
            action: delete
          - key: telemetry.distro.version
            action: delete
          - key: telemetry.sdk.name
            action: delete
          - key: telemetry.sdk.version
            action: delete
          - key: service.instance.id
            action: delete
          - key: process.runtime.name
            action: delete
          - key: process.runtime.description
            action: delete
          - key: process.pid
            action: delete
          - key: process.executable.path
            action: delete
          - key: process.command.args
            action: delete
          - key: os.description
            action: delete
          - key: instance
            action: delete
          - key: container.id
            action: delete
    
    connectors:
      spanmetrics:
        histogram:
          explicit:
            buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]
        dimensions: 
          - name: http.method
            default: "GET"
          - name: http.response.status_code
          - name: http.route
            # 自定义 Attribute
          - name: user.id
        metrics_flush_interval: 15s
        exclude_dimensions:
        metrics_expiration: 3m
        events:
          enabled: true
          dimensions: 
          - name: default
            default: "GET"
    
    
    exporters:
      debug:
        verbosity: detailed
      prometheus:
        endpoint: "0.0.0.0:1234"
        namespace: "acs"
        const_labels:
          label1: value1
        send_timestamps: true
        metric_expiration: 5m
        enable_open_metrics: true
        add_metric_suffixes: false
        resource_to_telemetry_conversion:
          enabled: true
            
    
    service:
      pipelines:
        logs:
          receivers: [otlp]
          exporters: [debug]
        traces:
          receivers: [otlp]
          processors: [resource]
          exporters: [spanmetrics]
        metrics:
          receivers: [otlp]
          processors: [memory_limiter, batch]
          exporters: [prometheus]
        metrics/2:
          receivers: [spanmetrics]
          exporters: [prometheus]
    
      extensions: [zpages]

该配置的含义是处理写入的 Metrics 和 Traces 数据,通过 resource类型的 processors丢弃通常不关心的环境 Attributes,防止指标数据量过大。通过 spanmetrics将 Span 关键统计数据转换为指标。

配置采集任务
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: opentelemetry-collector-podmonitor
  namespace: default
  annotations:
    arms.prometheus.io/discovery: "true"
spec:
  selector:
    matchLabels:
      observability: opentelemetry-collector
  podMetricsEndpoints:
  - port: metrics
    interval: 15s
    scheme: http
    path: /metrics

使用 Prometheus RemoteWrite Exporter 模式

说明
  • 该方式适用于数据量较大,采集不稳定场景,由 Collector 直接写入到 Prometheus 实例。

  • 配置相对复杂,需要自行处理数据写入链路配置。

部署架构图
image
写入数据链路准备

由于该方式是由 Collector 直接写入数据到 Prometheus 实例,因此首先需要获取 Prometheus 的 Endpoint 和认证信息。

  • Endpoint 获取

    您可以登录通过云监控控制台,在Prometheus 监控 > 实例列表页面,查找集群对应的 Prometheus 实例(通常Prometheus 实例 ID 与 ACK 集群 ID 一致,实例名称与容器集群名称一致)。单击目标实例名称,在设置页面,查找 Remote Write 地址,获取其中的内网地址待用。57

  • 认证信息获取

    您有以下两种方案可以选择:

    • V2 版本 Prometheus 实例支持配置免密策略,针对集群当前 VPC 内网支持免密写入。

    • 分配指标数据写入子账号,并分配 AliyunPrometheusMetricWriteAccess 系统角色权限,获取该子账号的 AK/SK 分别作为写入的账号和密码。

部署配置参考

Collector 部署配置与使用 Prometheus Exporter 模式一致,请直接参考上述Prometheus Exporter 部署配置

Collector 配置参考
apiVersion: v1
kind: ConfigMap
metadata:
  name: otel-config
  namespace: <app-namespace>
data:
  otel-config.yaml: |
    extensions:
    zpages:
      endpoint: localhost:55679

    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318
    
    processors:
      batch:
      memory_limiter:
        # 75% of maximum memory up to 2G
        limit_mib: 1536
        # 25% of limit up to 2G
        spike_limit_mib: 512
        check_interval: 5s
      resource:
        attributes:
          - key: process.runtime.description
            action: delete
          - key: process.command_args
            action: delete
          - key: telemetry.distro.version
            action: delete
          - key: telemetry.sdk.name
            action: delete
          - key: telemetry.sdk.version
            action: delete
          - key: service.instance.id
            action: delete
          - key: process.runtime.name
            action: delete
          - key: process.runtime.description
            action: delete
          - key: process.pid
            action: delete
          - key: process.executable.path
            action: delete
          - key: process.command.args
            action: delete
          - key: os.description
            action: delete
          - key: instance
            action: delete
          - key: container.id
            action: delete
    
    connectors:
      spanmetrics:
        histogram:
          explicit:
            buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]
        dimensions: 
          - name: http.method
            default: "GET"
          - name: http.response.status_code
          - name: http.route
            # 自定义 Attribute
          - name: user.id
        metrics_flush_interval: 15s
        exclude_dimensions:
        metrics_expiration: 3m
        events:
          enabled: true
          dimensions: 
          - name: default
            default: "GET"
    
    
    exporters:
      debug:
        verbosity: detailed
      prometheusremotewrite:
        # 这里替换为 Prometheus RemoteWrite 内网地址
        endpoint: http://<Endpoint>/api/v3/write
        namespace: "acs"
        resource_to_telemetry_conversion:
          enabled: true
        timeout: 10s   
        headers:
          Prometheus-Remote-Write-Version: "0.1.0"
          # 如果未开启免密,该配置是必需的
          Authorization: Basic <base64-encoded-username-password>
        external_labels:
          data-mode: metrics
            
    
    service:
      pipelines:
        logs:
          receivers: [otlp]
          exporters: [debug]
        traces:
          receivers: [otlp]
          processors: [resource]
          exporters: [spanmetrics]
        metrics:
          receivers: [otlp]
          processors: [memory_limiter, batch]
          exporters: [prometheusremotewrite]
        metrics/2:
          receivers: [spanmetrics]
          exporters: [prometheusremotewrite]
    
      extensions: [zpages]
  • 使用上文中获取到的地址替换配置中的 Prometheus RemoteWrite Endpoint 地址。

  • base64-encoded-username-password可以通过以下方式获取:

    echo -n 'AK:SK' | base64

三、验证用例