使用 OpenTelemetry Collector 接收任何自定义指标

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

虽然 OpenTelemetry (OTel) 可以帮助您解决故障排除和处理“未知的未知数”,但它对于管理路由任务(例如监控系统指标,如磁盘使用情况、服务器可用性或 SSL 证书过期日期)也至关重要。这可以通过利用 OpenTelemetry Collector 可用的 90 多个接收器中的任何一个来实现,例如 Host Metrics Receiver 或 HTTP Check Receiver。

但是,如果现有接收器无法满足您的特定需求怎么办?假设您有一系列 shell 脚本可以提供自定义指标,并且您想将这些指标导出到 OpenTelemetry Collector。您可以编写自己的接收器,但这需要精通 Go。

在踏上这条道路之前,请仔细检查可用的接收器:其中一些接收器能够以不同的格式(如 CarbonStatsDInfluxDBPrometheus,甚至 SNMP)吸收指标,并将它们集成到 OpenTelemetry 生态系统中。通过对 shell 脚本进行少量修改,您就可以使用其中一个接收器来实现您的目标。例如,Carbon Receiver 凭借其简单的 纯文本协议,非常适合与 shell 脚本一起使用。它的协议非常简单

纯文本协议是 Carbon 支持的最简单的协议。发送的数据必须采用以下格式:<metric path> <metric value> <metric timestamp>

示例脚本:检查证书过期情况

考虑以下 shell 脚本,该脚本接受主机名作为参数,并使用 openssl s_client 来检索证书并计算证书过期前的剩余时间

#!/bin/bash
HOST=${1}
PORT=${2:-443}

now=$(date +%s)
notAfterString=$(echo q | openssl s_client -servername "${HOST}" "${HOST}:${PORT}" 2>/dev/null | openssl x509 -noout -enddate | awk -F"=" '{ print $2; }')
if [[ "$(uname)" == "Darwin" ]] ; then
  notAfter=$(date -j -f "%b %d %H:%M:%S %Y %Z" "${notAfterString}" +%s)
else
  notAfter=$(date -d "${notAfterString}" +%s)
fi

secondsLeft=$(($notAfter-$now))

echo ${secondsLeft}

您可以按如下方式测试此脚本

$ ./ssl_check.sh opentelemetry.io
4357523

使用 Carbon 的纯文本协议

要使此脚本使用 Carbon 的纯文本协议,您需要修改脚本的最后几行,以 Carbon 格式输出指标

#!/bin/bash
HOST=${1}
PORT=${2:-443}

now=$(date +%s)
str=$(echo q | openssl s_client -servername "${HOST}" "${HOST}:${PORT}" 2>/dev/null | openssl x509 -noout -enddate | awk -F"=" '{ print $2; }')
if [[ "$(uname)" == "Darwin" ]] ; then
  notAfter=$(date -j -f "%b %d %H:%M:%S %Y %Z" "${notAfterString}" +%s)
else
  notAfter=$(date -d "${notAfterString}" +%s)
fi

secondsLeft=$(($notAfter-$now))

metricPath="tls.server.not_after.time_left;unit=s"
echo "${metricPath} ${secondsLeft} ${now}"

通过这样做,脚本将输出 <metric path>tls.server.not_after.time_left;unit=s<metric value>${secondsLeft}<metric timestamp>${now}

这就是我们将指标发送到启用了 Carbon Receiver 的 OpenTelemetry Collector 所需的所有操作。

使用 OTel Collector 接收任何指标

要进行测试,请使用以下配置启动 OpenTelemetry Collector:

receivers:
  carbon:
    endpoint: localhost:8080
    transport: tcp
    parser:
      type: plaintext
      config:

exporters:
  debug:
    verbosity: detailed

service:
  pipelines:
    metrics:
      receivers: [carbon]
      exporters: [debug]

例如,如果您已将此文件保存为 collector-config.yml,请执行以下命令

$ ./otelcol --config collector-config.yml
2023-11-24T12:52:51.340+0100	info	service@v0.89.0/telemetry.go:85	Setting up own telemetry...
2023-11-24T12:52:51.341+0100	info	service@v0.89.0/telemetry.go:202	Serving Prometheus metrics	{"address": ":8888", "level": "Basic"}
2023-11-24T12:52:51.341+0100	info	exporter@v0.89.0/exporter.go:275	Development component. May change in the future.	{"kind": "exporter", "data_type": "metrics", "name": "debug"}
2023-11-24T12:52:51.341+0100	info	service@v0.89.0/service.go:143	Starting otelcol-any-metric...	{"Version": "1.0.0", "NumCPU": 10}
2023-11-24T12:52:51.341+0100	info	extensions/extensions.go:34	Starting extensions...
2023-11-24T12:52:51.342+0100	info	service@v0.89.0/service.go:169	Everything is ready. Begin running and processing data.

在 OpenTelemetry Collector 运行后,打开第二个 shell 并将您的指标传输到其中

./ssl_check.sh opentelemetry.io | nc 127.0.0.1 8080

Debug Exporter 将在控制台上为您显示指标

2023-11-24T12:54:51.369+0100	info	ResourceMetrics #0
Resource SchemaURL:
ScopeMetrics #0
ScopeMetrics SchemaURL:
InstrumentationScope
Metric #0
Descriptor:
     -> Name: tls.server.not_after.time_left
     -> Description:
     -> Unit:
     -> DataType: Gauge
NumberDataPoints #0
Data point attributes:
     -> unit: Str(s)
StartTimestamp: 1970-01-01 00:00:00 +0000 UTC
Timestamp: 2023-11-24 11:54:51 +0000 UTC
Value: 4356471
	{"kind": "exporter", "data_type": "metrics", "name": "debug"}

就是这样!您可以对任何其他报告自定义指标的 shell 脚本使用相同的技术。

使用 Transform Processor 进行精细调整

Carbon Receiver 使用 ; 作为分隔符来分割 <metric path>,以提取指标名称(第一个元素)和数据点属性(所有其他元素)。在我们的示例中,这意味着指标名称将是 tls.server.not_after.time_left,并且将具有数据点属性 unit: Str(s)

虽然这种方法很简单,但它不允许您设置任何其他详细信息,例如资源、指标描述或特别地,指标单位。

但是,Transform Processor 可以帮助您。通过将 OpenTelemetry Transformation Language (OTTL) 语句集成到您的 collector-config.yml 中,您可以将数据点属性 unit 转换为指标的单位

receivers:
  carbon:
    endpoint: localhost:8080
    transport: tcp
    parser:
      type: plaintext
      config:

exporters:
  debug:
    verbosity: detailed

processors:
  transform:
    metric_statements:
      - context: datapoint
        statements:
          - set(metric.unit, attributes["unit"])
          - delete_key(attributes, "unit")
service:
  pipelines:
    metrics:
      receivers: [carbon]
      processors: [transform]
      exporters: [debug]

再次运行 ssl_check.sh

./ssl_check.sh opentelemetry.io | nc 127.0.0.1 8080

现在,Debug Exporter 还将把单位包含到指标描述符中

2023-11-24T12:54:51.369+0100	info	ResourceMetrics #0
Resource SchemaURL:
ScopeMetrics #0
ScopeMetrics SchemaURL:
InstrumentationScope
Metric #0
Descriptor:
     -> Name: tls.server.not_after.time_left
     -> Description:
     -> Unit: s
     -> DataType: Gauge
NumberDataPoints #0
Data point attributes:
     -> unit: Str(s)
StartTimestamp: 1970-01-01 00:00:00 +0000 UTC
Timestamp: 2023-11-24 11:54:51 +0000 UTC
Value: 4356471
	{"kind": "exporter", "data_type": "metrics", "name": "debug"}

如果您希望将指标与您使用 OpenTelemetry 进行检测的服务关联起来,您可以首先将 service.nameservice.namespace 添加到您的 shell 脚本中作为数据点属性

metricName="tls.server.not_after.time_left;unit=s;service.name=otel-webserver;service.namespace=opentelemetry.io"
echo "${metricName} ${secondsLeft} ${now}"

接下来,添加另一个 OTTL 语句,从这些数据点属性创建资源

processors:
  transform:
    metric_statements:
      - context: datapoint
        statements:
          - set(metric.unit, attributes["unit"])
          - set(resource.attributes["service.name"], attributes["service.name"])
          - set(resource.attributes["service.namespace"],
            attributes["service.namespace"])
          - delete_key(attributes, "unit")
          - delete_key(attributes, "service.name")
          - delete_key(attributes, "service.namespace")

再次运行 ssl_check.sh

./ssl_check.sh opentelemetry.io | nc 127.0.0.1 8080

现在,Debug Exporter 还将包含具有 service.nameservice.namespace 属性的资源

2023-11-24T14:49:03.806+0100	info	ResourceMetrics #0
Resource SchemaURL:
Resource attributes:
     -> service.name: Str(otel-webserver)
     -> service.namespace: Str(opentelemetry.io)
ScopeMetrics #0
ScopeMetrics SchemaURL:
InstrumentationScope
Metric #0
Descriptor:
     -> Name: tls.server.not_after.time_left
     -> Description:
     -> Unit: s
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 1970-01-01 00:00:00 +0000 UTC
Timestamp: 2023-11-24 13:49:03 +0000 UTC
Value: 4349619
	{"kind": "exporter", "data_type": "metrics", "name": "debug"}

Transform Processor 和 OTTL 提供了广泛的功能。从中了解更多

这样,您就可以使用 OpenTelemetry Collector 接收任何自定义指标了!

奖励:使用 OTLP!

虽然使用 Carbon Receiver 和 Transform Processor 是一种可靠的收集自定义指标的方法,但使用外部格式将指标导入 OpenTelemetry 可能显得有些不寻常,尤其是当 OpenTelemetry Protocol (OTLP) 提供了您所需的一切时。

作为 Carbon Receiver 的替代方案,您还可以使用 OTLP JSON 发送自定义指标

#!/bin/bash
URL=${1}
PORT=${2:-443}

now=$(date +%s)
notAfterString=$(echo q | openssl s_client -servername "${URL}" "${URL}:${PORT}" 2>/dev/null | openssl x509 -noout -enddate | awk -F"=" '{ print $2; }')
if [[ "$(uname)" == "Darwin" ]] ; then
  notAfter=$(date -j -f "%b %d %H:%M:%S %Y %Z" "${notAfterString}" +%s)
else
  notAfter=$(date -d "${notAfterString}" +%s)
fi

secondsLeft=$(($notAfter-$now))

data="
{
    \"resourceMetrics\": [
      {
        \"resource\": {
          \"attributes\": [
            {
              \"key\": \"service.name\",
              \"value\": {
                \"stringValue\": \"${URL}\"
              }
            }
          ]
        },
        \"scopeMetrics\": [
          {
            \"metrics\": [
              {
                \"name\": \"tls.server.not_after.time_left\",
                \"unit\": \"s\",
                \"description\": \"\",
                \"gauge\": {
                  \"dataPoints\": [
                    {
                      \"asInt\": ${secondsLeft},
                      \"timeUnixNano\": ${now}000000000
                    }
                  ]
                }
              }
            ]
          }
        ]
      }
    ]
  }
"
curl -X POST -H "Content-Type: application/json" -d "${data}" -i localhost:4318/v1/metrics

在您的 collectors-config 中启用 OTLP Receiver

receivers:
  otlp:
    protocols:
      http:
      grpc:
exporters:
  debug:
    verbosity: detailed
service:
  pipelines:
    metrics:
      receivers: [otlp]
      exporters: [debug]

执行您的更新后的 ssl_check.sh

./ssl_check.sh opentelemetry.io

这次,您的指标将显示正确的单位设置,并且资源将按 JSON 中的定义进行报告

2023-11-24T15:28:51.212+0100	info	ResourceMetrics #0
Resource SchemaURL:
Resource attributes:
     -> service.name: Str(opentelemetry.io)
ScopeMetrics #0
ScopeMetrics SchemaURL:
InstrumentationScope
Metric #0
Descriptor:
     -> Name: tls.server.not_after.time_left
     -> Description:
     -> Unit: s
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 1970-01-01 00:00:00 +0000 UTC
Timestamp: 2023-11-24 14:28:51 +0000 UTC
Value: 4347231
	{"kind": "exporter", "data_type": "metrics", "name": "debug"}

在 shell 脚本中处理 JSON 并不理想,正如本示例清楚表明的那样!虽然有改进的方法,但最终您可能会发现使用 Python 或 Node.js 等语言,或将指标(支持 gauge)集成到您喜欢的 OTel CLI 工具中效率更高!

摘要

在本篇文章中,您学习了如何使用像 Carbon Receiver 这样的“通用”接收器将任何指标馈送到 OpenTelemetry Collector。当现有接收器不满足您的需求并且您不想用 Go 编写自己的接收器时,请使用此方法。

您学会了如何直接使用 OTLP 和 curl 将指标发送到 OpenTelemetry Collector。当您无法修改 OpenTelemetry Collector 的管道时,请使用此方法。随着通过 OTLP 导出指标的命令行工具的可用,它也将成为“通用”接收器的有效替代方案。