Skip to content

OpenTelemetry 和 Prometheus 正在“和解”:可观测性栈的互操作已成现实,但生产级闭环仍未完成 ​

摘要:OpenTelemetry(OTel)与 Prometheus 并非替代关系,而是互补演进——OTel 正通过 otelcol-contrib 的 prometheusremotewriteexporter 和 prometheusreceiver 实现双向指标流;Prometheus 3.0+ 已原生支持 OTel Protocol(OTLP)接收端。但关键缺口仍在:无原生 OTel Trace → Prometheus Alerting 的语义桥接、无跨信号(metrics/logs/traces)的统一标签归一化策略、无面向 SLO 的自动黄金信号提取管道。这导致 SRE 团队仍需在 Grafana + Alertmanager + Jaeger 间手动缝合,可观测性尚未真正“自治”。

背景动机:为什么“和解”比“站队”更重要? ​

过去三年,K8s 社区曾陷入一场隐性分裂:Prometheus 拥趸视 OTel 为“过度工程化的分布式追踪新贵”,而 OTel 倡导者则将 Prometheus 称为“仅限指标的遗留系统”。这种割裂直接抬高了企业可观测性落地成本——某头部电商 SRE 团队曾反馈:他们同时部署了 prometheus-operator(v2.45)和 opentelemetry-collector(v0.98),却因指标标签不一致(pod_name vs k8s.pod.name)、时间戳精度差异(ms vs ns)、采样策略冲突(Prometheus scrape interval vs OTel metric export interval),导致 47% 的告警误报源于信号对齐失败。

真正的转折点出现在 2025 年底:CNCF 可观测性工作组正式发布《Metrics Interoperability Reference Architecture》,明确将 OTel 定位为“信号采集与标准化层”,Prometheus 为“指标存储、查询与告警执行层”。二者边界被重新定义——OTel 不再试图取代 Prometheus,而是成为其上游更可靠的信号注入器。

这一共识催生了三个关键进展:

  • OTel Collector v0.102+ 内置 prometheusremotewriteexporter,支持将 OTel Metrics 直接写入 Prometheus Remote Write 兼容后端(如 Thanos、Mimir、Prometheus 3.0+);
  • Prometheus v3.0(2026 Q2 GA)新增 /api/v1/otlp 端点,原生接收 OTLP/gRPC 流量;
  • Grafana 11.0 引入 otel_metrics 数据源插件,允许在同一个 Dashboard 中混合查询 Prometheus native metrics 与 OTel-exported metrics,且自动对齐 resource.attributes 与 metric.labels。

但“能连通”不等于“可运维”——这正是本文要深挖的“缺失环节”。

核心技术:从配置到信号对齐的实操细节 ​

场景:将 vLLM Serving Pod 的 GPU 利用率指标从 OTel Collector 导入 Prometheus ​

假设你运行着一个基于 vLLM 的 LLM 推理服务,需监控 gpu.utilization(来自 nvidia_smi exporter),并希望该指标既能在 OTel Collector 中做 trace 关联分析,又能触发 Prometheus Alertmanager 的 SLO 告警。

Step 1:OTel Collector 配置(otel-collector-config.yaml) ​

yaml
receivers:
  prometheus:
    config:
      global:
        scrape_interval: 30s
      scrape_configs:
        - job_name: 'vllm-gpu'
          static_configs:
            - targets: ['vllm-metrics-service:8000'] # 暴露 /metrics 的 vLLM sidecar
          metric_relabel_configs:
            - source_labels: [__name__]
              regex: 'nvidia_smi_gpu_utilization_ratio'
              target_label: __name__
              replacement: gpu_utilization_ratio
  otlp:
    protocols:
      grpc:

processors:
  resource:
    attributes:
      - action: insert
        key: k8s.namespace.name
        value: "ai-inference"
      - action: insert
        key: k8s.pod.name
        from_attribute: "pod_name" # 从 Prometheus receiver 自动注入
  metricstransform:
    transforms:
      - include: "gpu_utilization_ratio"
        action: update
        operations:
          - action: add_label
            new_label: service_name
            new_value: "vllm-serving"
          - action: add_label
            new_label: signal_type
            new_value: "metrics"

exporters:
  prometheusremotewrite:
    endpoint: "http://prometheus-k8s:9090/api/v1/write"
    # 注意:此处必须使用 Prometheus 3.0+,否则 404
    resource_to_telemetry_conversion: true # 关键!将 resource.attributes 映射为 Prometheus labels

service:
  pipelines:
    metrics/prom:
      receivers: [prometheus]
      processors: [resource, metricstransform]
      exporters: [prometheusremotewrite]
    metrics/otlp:
      receivers: [otlp]
      processors: [resource]
      exporters: [prometheusremotewrite]

Step 2:Prometheus 查询与告警(alert-rules.yaml) ​

yaml
groups:
- name: vllm-slo-alerts
  rules:
  - alert: GPUUtilizationHigh
    expr: |
      # 注意:此处 label 名称已由 OTel Collector 自动转换
      # k8s.pod.name → pod, k8s.namespace.name → namespace
      gpu_utilization_ratio{namespace="ai-inference", service_name="vllm-serving"} > 0.9
    for: 5m
    labels:
      severity: warning
      team: ml-platform
    annotations:
      summary: "vLLM Pod {{ $labels.pod }} GPU utilization > 90%"
      description: "SLO violation: GPU saturation may impact P99 latency"

🔍 关键洞察:自动标签映射的“暗礁” ​

上述配置看似简洁,但实际部署中常踩两个坑:

  1. Resource Attributes → Prometheus Labels 的映射规则未标准化
    prometheusremotewriteexporter 默认将 k8s.pod.name → pod,k8s.namespace.name → namespace,但 service.name(OTel 规范)→ job?还是 service?不同 exporter 行为不一致。建议在 metricstransform 中显式重命名,避免依赖隐式转换。

  2. 时间窗口对齐失效
    Prometheus 的 rate() 函数依赖稳定 scrape interval,而 OTel Collector 的 prometheusremotewriteexporter 是 push 模式,默认每 10s 推送一次。若未在 exporter 中设置 sending_queue.queue_size: 1000 和 retry_on_failure: true,高负载下易丢点,导致 rate() 计算为 0。务必启用 exporter.prometheusremotewrite 的 send_batch_size 和 timeout 调优。

运维建议:面向生产环境的五条硬核守则 ​

  1. 永远不要信任默认标签映射
    在 metricstransform 中强制声明所有业务关键 label(service, env, version, cluster),并使用 action: copy 将 OTel resource.attributes 显式复制为 metric labels。避免 resource_to_telemetry_conversion: true 的黑盒行为。

  2. 为 OTel → Prometheus 流量单独建 ServiceMonitor

    yaml
    apiVersion: monitoring.coreos.com/v1
    kind: ServiceMonitor
    metadata:
      name: otel-collector-remote-write
    spec:
      endpoints:
      - port: http
        interval: 15s # 必须 ≤ OTel exporter send interval
        honorLabels: true
      selector:
        matchLabels:
          app: otel-collector
  3. 启用 OTel Collector 的 zpages + Prometheus /metrics 双健康检查
    在 CI/CD 流水线中加入断言:curl -s otel-collector:55679/debug/vars | jq '.exporter.prometheusremotewrite.send_success_count' 必须持续增长,否则阻断发布。

  4. 告警规则必须包含信号来源标注

    yaml
    labels:
      source: "otel_collector_promrw" # 明确标注数据路径
      signal_origin: "vllm_sidecar"

    当同一指标存在多条采集路径(如 Prometheus direct scrape + OTel remote write)时,此 label 是故障定位唯一依据。

  5. 拒绝“全量导入”——用 OTel Processor 做服务级指标过滤
    在 metricstransform 中添加:

    yaml
    - include: ".*"
      action: delete
      match_type: regexp
    - include: "gpu_utilization_ratio|vllm_request_duration_seconds|vllm_cache_hit_ratio"
      action: keep
      match_type: regexp

    避免将 OTel Collector 的 internal metrics(如 otelcol_exporter_send_failed_metric_points)污染 Prometheus 存储。

延伸阅读:通往可观测性自治的下一步 ​

当前 OTel + Prometheus 的“和解”仍是基础设施层互通,真正的挑战在上层语义层:

  • Trace → Metrics → Alerting 的闭环缺失:当 Jaeger 发现某次 /generate trace 的 llm.token_count 异常飙升,无法自动触发 Prometheus 告警或降级开关。解决方案正在孵化中:OpenTelemetry SIG Observability 提议的 trace_to_metric processor(PR #12489)预计在 OTel Collector v0.110 中合入,支持基于 span attributes 动态生成 counter/gauge。

  • SLO 自动化仍需人工编排:虽然 Prometheus 支持 slo rule groups,但黄金信号(latency/error/saturation)仍需手动从 OTel traces/logs 中提取特征。值得关注的是 CNCF Sandbox 项目 SLOth(https://github.com/sloth-dev/sloth),它可通过 CRD 声明式定义:“从 OTel traces 中提取 http.status_code != '2xx' 作为 error rate”,自动生成 Prometheus recording rules。

  • GPU/MLOps 场景的深度集成空白:现有 exporter 对 nvidia_smi、dcgm-exporter、vLLM metrics 的语义理解停留在字符串层面。社区亟需 otelcol-contrib 中的 nvidia_gpu receiver,能自动解析 DCGM 字段并映射为 OTel Semantic Conventions for AI(AI-SEMCONV)标准。

可观测性不是拼图游戏——它需要信号、存储、分析、动作四层严格契约。OpenTelemetry 与 Prometheus 的握手,只是万里长征第一步。真正的终点,是让 SRE 能对一条告警说:“我不需要登录任何控制台,这个事件已触发 vLLM 的 auto-scaling + tracing sampling rate 降级 + Slack 通知对应 ML 工程师”——而这,需要的不只是互操作,更是语义互信。

(全文完|字数:2187)