理想上,每個服務都應該主動吐出 Metrics。但現實常常不是這樣:老舊系統難以改造、相容工具不存在、或是動程式碼的風險太高。Span Metrics 提供了一條退路——只要服務願意吐 Trace,就能反向產生與 Request 相關的 Metrics。

從 Trace 反推 Metrics#

Span Metrics Connector 是 OpenTelemetry Collector 的 Contrib 版本中提供的 Connector,會把 Trace 資料即時轉成 Metrics。注意這是 Contrib 版本獨有的元件,使用 Core 版本不會有它。

下面是一份最小可用設定,把同一份 Trace 同時送往 Tempo 與 Span Metrics Connector,最後由 Prometheus Exporter 揭露 Metrics:

receivers:
  otlp:
    protocols:
      grpc:
      http:

exporters:
  otlp:
    endpoint: tempo:4317
    tls:
      insecure: true
  prometheus:
    endpoint: "0.0.0.0:8889"

connectors:
  spanmetrics:

processors:
  batch:

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [spanmetrics, otlp]
    metrics/spanmetrics:
      receivers: [spanmetrics]
      exporters: [prometheus]

資料流程的重點在於 Connector 同時扮演兩種角色:

  • traces Pipeline 裡作為 Exporter,接收上游 Trace 並轉換出 Metrics。
  • metrics/spanmetrics Pipeline 裡作為 Receiver,把轉換出來的 Metrics 餵給 Prometheus Exporter。

最終 Collector 會在 8889 Port 對外揭露這些 Metrics,由 Prometheus 主動爬取。

預設輸出與自訂 Label#

預設情境下,Span Metrics Connector 會輸出四種 Metrics:

  • calls_total:Span 被觀察到的總次數。
  • duration_milliseconds_bucket:執行時間分布。
  • duration_milliseconds_sum:執行時間總和。
  • duration_milliseconds_count:累計次數。

預設帶的 Label 也固定:service_namespan_namespan_kindstatus_code,分別來自 Span 的 service.namenamespan.kindstatus.code

如果想拉更多維度(例如想分開觀察不同 HTTP route 的延遲),可以透過 dimensions 加入額外 Span Attribute:

connectors:
  spanmetrics:
    dimensions:
      - name: http.method
      - name: http.status_code
      - name: http.route

這些 Attribute 會被改寫成 Prometheus 慣例的命名(例如 http_methodhttp_status_codehttp_route)。

不過 Label 的維度組合會直接吃 Collector 記憶體,預設上限是 1000 種組合。系統流量大或 Attribute cardinality 高時,常常會撞到天花板,可以調大上限:

connectors:
  spanmetrics:
    dimensions_cache_size: 10000

值得提醒的是,「調大快取」只是延後問題;如果某個 Attribute 本身就是高基數(例如 user id),更好的做法是不要把它放進 dimensions

Jaeger Service Performance Monitoring#

Jaeger 從 1.43 起內建 Service Performance Monitoring(SPM),底層機制與上述完全一致:透過 OpenTelemetry Collector 的 Span Metrics Connector 把 Trace 轉成 Metrics,再由 Jaeger UI 直接呈現 RED Metrics(Request、Error、Duration)。

實務上常見搭配版本(Jaeger 1.50、OpenTelemetry Collector 0.86)需要調整 Jaeger Query 設定才能正確讀取 Prometheus:

  • 環境變數:METRICS_STORAGE_TYPE=prometheus
  • 啟動參數:
    • --prometheus.query.support-spanmetrics-connector=true
    • --prometheus.server-url=http://prometheus:9090
    • --prometheus.query.normalize-duration=true
    • --prometheus.query.normalize-calls=true

搭配 OpenTelemetry Collector 0.80 之後的版本時,Span Metrics Connector 改了 Metrics 名稱規則,導致 Jaeger SPM 抓不到資料。Jaeger 1.47 起在 PR「Support normalized metric names #4555」加入相容性支援,必須同時開啟 --prometheus.query.normalize-duration--prometheus.query.normalize-calls 兩個旗標,UI 才會顯示資料。

Lab:Basic 與 Jaeger SPM 兩條路線#

範例專案提供兩組 Docker Compose:

  • Basic:Collector 把 Trace 同時送 Tempo 與 spanmetrics,Prometheus 抓 Metrics,Grafana 透過 Provisioning 載入 OpenTelemetry APM Dashboard。
  • Jaeger SPM:把後端換成 Jaeger Collector + Jaeger Query + Cassandra,由 Jaeger UI 的 Monitor 頁籤呈現 RED Metrics。

啟動方式:

# Basic
docker-compose up -d

# Jaeger SPM
docker-compose -f docker-compose.jaeger.yaml up -d

兩條路線都用 k6 打流量:

k6 run --vus 1 --duration 300s k6-script.js

Jaeger SPM 啟動時要稍等 Cassandra 初始化完成,否則 Jaeger Collector 與 Jaeger Query 會啟動失敗,可用 docker-compose ps 觀察狀態。

小結#

Span Metrics 的價值在於「最低改動成本換取 Request 視角的 Metrics」。對於難以塞 Prometheus Client 的系統,只要能透過 OpenTelemetry Automatic Instrumentation 拿到 Trace(甚至更進一步用 Service Mesh 的 Sidecar 直接從網路流量抽 Trace),就能取得一組可觀測的 RED Metrics。

這個概念也呼應 Tempo 的 Metrics-generator:Trace 不只是排查單一請求的工具,它本身就蘊含了大量可被衍生的資料,往後可預期會出現更多以 Trace 為原料、產出新型態 Signal 的工具。

原文出處#

  • 原書/iThome:https://ithelp.ithome.com.tw/articles/10336986