Prometheus 支持通过 HTTP 接收 OTLP(即 "OpenTelemetry Protocol")数据。
启用 OTLP 接收器
默认情况下,OTLP 接收器是禁用的,类似于 Remote Write 接收器。这是因为 Prometheus 可以在没有任何认证的情况下工作,因此除非明确配置,否则接受传入流量是不安全的。
要启用接收器,你需要开启 CLI 标志 --web.enable-otlp-receiver。这将使 Prometheus 在 HTTP /api/v1/otlp/v1/metrics 路径上接收 OTLP 指标。
$ prometheus --web.enable-otlp-receiver
向 Prometheus 服务器发送 OpenTelemetry 指标
通常你需要告诉 OTLP 指标流量的来源关于 Prometheus 端点,以及应该使用 HTTP 模式的 OTLP(gRPC 通常是默认模式)。
OpenTelemetry SDK 和插桩库通常可以通过标准环境变量进行配置。以下是将 OpenTelemetry 指标发送到本地 Prometheus 服务器所需的 OpenTelemetry 变量:
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:9090/api/v1/otlp/v1/metrics
关闭 traces 和 logs:
export OTEL_TRACES_EXPORTER=none
export OTEL_LOGS_EXPORTER=none
OpenTelemetry 指标的默认推送间隔为 60 秒。以下设置将推送间隔改为 15 秒:
export OTEL_METRIC_EXPORT_INTERVAL=15000
如果你的插桩库没有开箱即用地提供 service.name 和 service.instance.id,强烈建议进行设置。
export OTEL_SERVICE_NAME="my-example-service"
export OTEL_RESOURCE_ATTRIBUTES="service.instance.id=$(uuidgen)"
以上假设你的系统上有 uuidgen 命令可用。确保 service.instance.id 对每个实例是唯一的,并且每当资源属性发生变化时都会生成新的 service.instance.id。推荐的做法是在每次实例启动时生成一个新的 UUID。
配置 Prometheus
本节介绍 Prometheus 服务器的各种推荐配置方面,以启用和调优你的 OpenTelemetry 流程。
参见我们将在下面章节中使用的示例 Prometheus 配置文件。
启用乱序写入
有多种原因可能需要启用乱序写入。
例如,OpenTelemetry collector 鼓励批量处理,你可能有多个 collector 副本向 Prometheus 发送数据。因为没有机制对这些样本进行排序,它们可能会出现乱序。
要启用乱序写入,你需要在 Prometheus 配置文件中添加以下内容:
storage:
tsdb:
out_of_order_time_window: 30m
30 分钟的乱序窗口对大多数情况已经足够,但可以根据你的需要调整此值。
提升资源属性
基于经验和与社区的交流,我们发现在所有常见的资源属性中,有一些值得附加到你所有的 OTLP 指标上。
默认情况下,Prometheus 不会提升任何属性。如果你想提升其中一些,可以在 Prometheus 配置文件的此部分进行设置。以下代码片段分享了推荐提升的最佳实践属性集:
otlp:
# Recommended attributes to be promoted to labels.
promote_resource_attributes:
- service.instance.id
- service.name
- service.namespace
- service.version
- cloud.availability_zone
- cloud.region
- container.name
- deployment.environment
- deployment.environment.name
- k8s.cluster.name
- k8s.container.name
- k8s.cronjob.name
- k8s.daemonset.name
- k8s.deployment.name
- k8s.job.name
- k8s.namespace.name
- k8s.pod.name
- k8s.replicaset.name
- k8s.statefulset.name
在查询时包含资源属性
默认情况下,除了 service.instance.id、service.namespace 和 service.name 之外,所有 OTel 资源属性都被转换为特殊 target_info 指标上的标签。
这意味着,对于你未提升的 OTel 资源属性,你仍然可以通过与 target_info 进行连接(join)在查询中包含相应的标签。
为此,我们建议通过以下标志启用实验性的 PromQL 函数 info 以便于连接:
--enable-feature=promql-experimental-functions
这种查询的一个示例如下:
info(rate(http_server_request_duration_seconds_count[2m]), {k8s_cluster_name=~".+"})
或者,也可以通过原始 join 查询来实现同样的效果:
rate(http_server_request_duration_seconds_count[2m])
* on (job, instance) group_left (k8s_cluster_name)
target_info
以上两个查询的作用是,将 rate(http_server_request_duration_seconds_count[2m]) 产生的时间序列用共享相同 job 和 instance 标签的 target_info 序列中的 k8s_cluster_name 标签进行增强。
换句话说,job 和 instance 标签在 http_server_request_duration_seconds_count 和 target_info 之间是共享的,类似于 SQL 的外键。
另一方面,k8s_cluster_name 标签对应 OTel 资源属性 k8s.cluster.name(Prometheus 将点转换为下划线,除非另有配置)。
但请注意,info 函数通常比原始 join 查询性能更好,因为它只选择具有匹配 job 和 instance 标签的 target_info 序列。
更重要的是,info 函数解决了 join 查询方法中一个古老且相当深奥的问题。
当被连接标签以外的其他标签(即所谓的标识标签)的值发生变化(即流转)时,除非旧的 target_info 版本被标记为过时,否则在 PromQL 回溯时间窗口内(默认 5 分钟),旧版本和新版本的 target_info 之间会存在重叠。
在此期间,针对 target_info 的 join 查询将因为存在两个匹配的不同 target_info 时间序列而失败。
幸运的是,info 函数不会受到这个问题的影响,因为它总是选择具有最新样本的时间序列!
那么,target_info 指标和 OTel 资源属性之间的关系是什么?
当 Prometheus 处理 OTLP 写入请求时,如果包含的资源包括 service.instance.id 和/或 service.name 属性,Prometheus 会为每个(OTel)资源生成 info 指标 target_info。
它在每个 target_info 序列上添加 instance 标签(值为 service.instance.id 资源属性的值)和 job 标签(值为 service.name 资源属性的值)。
如果存在 service.namespace 资源属性,它会作为前缀添加到 job 标签值中(即 <service.namespace>/<service.name>)。
默认情况下,service.name、service.namespace 和 service.instance.id 本身不会添加到 target_info,因为它们已转换为 job 和 instance。
但是,可以启用以下配置参数,在转换为 job 和 instance 的基础上,将它们直接添加到 target_info(如果 otlp.translation_strategy 是 UnderscoreEscapingWithSuffixes,则经过规范化将点替换为下划线)。
otlp:
keep_identifying_resource_attributes: true
其余的资源属性也作为标签添加到 target_info 序列中,如果 otlp.translation_strategy 是 UnderscoreEscapingWithSuffixes,名称会转换为 Prometheus 格式(例如点转换为下划线)。
如果资源同时缺少 service.instance.id 和 service.name 属性,则不会生成相应的 target_info 序列。
对于资源的每个 OTel 指标,Prometheus 将其转换为相应的 Prometheus 时间序列,并(如果生成了 target_info)为其添加正确的 instance 和 job 标签。
UTF-8
从 3.x 版本开始,Prometheus 支持指标名称和标签的 UTF-8,因此可以省略 OpenTelemetry 的 Prometheus normalization translator 包。请注意,当 Prometheus 通过内容协商宣告它允许 UTF-8 字符时,并不要求指标名称必须包含以前不支持的字符。OTLP 指标可能会根据端点的配置以几种不同的方式进行转换。因此,虽然 UTF-8 在 Prometheus 存储和 UI 中默认启用,但你需要为 OTLP 指标接收器设置 translation_strategy,默认设置为旧的规范化方式 UnderscoreEscapingWithSuffixes。
有四种可能的转换策略,其中两种需要在 Prometheus 中启用 UTF-8 支持:
UnderscoreEscapingWithSuffixes,默认策略。完全转义指标名称以兼容经典的 Prometheus 指标命名规范,并包括附加类型和单位后缀。UnderscoreEscapingWithoutSuffixes。类似于 UnderscoreEscapingWithSuffixes 完全转义指标名称,但不附加类型和单位后缀。从多个角度来看,此模式并不理想,用户应该意识到缺少后缀可能导致指标名称冲突,只有在仔细测试后才应启用此模式。一些组织偏好这种 OTel 对称性和有限字符支持之间的平衡。NoUTF8EscapingWithSuffixes将禁用将特殊字符更改为_,允许原生使用 OpenTelemetry 指标格式,特别是与语义约定配合使用。请注意,特殊后缀如单位和计数器的_total仍会附加,以防止具有相同名称但不同类型或单位的多个指标之间可能的冲突。此模式需要启用 UTF-8。NoTranslation。此策略绕过所有指标和标签名称的转换,原样传递。此模式需要启用 UTF-8。请注意,没有后缀时,如果具有相同名称但不同类型或单位的多个指标可能会发生冲突。
otlp:
# Ingest OTLP data keeping UTF-8 characters in metric/label names.
translation_strategy: NoTranslation
Delta 时间性
OpenTelemetry 规范说明 Delta 时间性和 Cumulative 时间性都受支持。虽然 Delta 时间性在 statsd 和 graphite 等系统中很常见,但 Cumulative 时间性是 Prometheus 的默认方式。
目前,Prometheus 内嵌了来自 OpenTelemetry-Collector-contrib 的 delta to cumulative 处理器,能够接收 delta 数据并将其转换为等效的累积表示形式,然后存储在 Prometheus 的 TSDB 中。
此功能是实验性的,因此请使用功能标志 otlp-deltatocumulative 启用 Prometheus 来使用它。
团队仍在致力于开发更高效的 OTLP delta 处理方式。