透徹理解 OpenTelemetry

OpenTelemetry 是由雲原生計算基金會(CNCF)託管的一個用於基礎設施性能檢測的開源可觀測性框架。該項目在幾乎所有主要的雲供應商(AWS、Google、Microsoft)以及可觀測性供應商(包括 Timescale)的貢獻下得到了極大的發展,以至於它成爲按活躍度和貢獻者排名第二的 CNCF 項目,僅次於 Kubernetes 本身。

OpenTelemetry 旨在爲所有類型的可觀測數據定義一個單一的標準(在 OpenTelemetry 的術語中被稱作 Signals 信號),包括監控指標、日誌和鏈路追蹤。通過一系列的工具、庫、API、SDK 和 exporters,OpenTelemetry 從根本上簡化了從服務中收集信號並將其發送到你所選用的後端服務的過程,爲更多的用戶和供應商打開了可觀測性的大門。

在設計 OpenTelemetry 指標標準時,有三個設計目標被採納:

OpenTelemetry 提供了一個採集器,可以用來重新聚合和重定向指標數據,經常被用來創建信號管道。由於 OpenTelemetry 不提供後端實現(它關注的是創建、收集和發送信號),數據將流向另一個或多個系統進行存儲並最終能夠被查詢。之所以 OpenTelemetry 有時會令人感到複雜,是因爲它可以用來模擬許多不同的信號實現。在這篇博文中,我們將聚焦於” 表面”,即開發者在 OpenTelemetry 中使用指標時可能遇到的要素。

OpenTelemetry 指標

OpenTelemetry 指標與 Prometheus 指標略有不同,它允許在採集過程中進行更加靈活的轉換,並支持許多導出類型,包括 Pull 和 Push 模式。由於這種靈活性,許多現有的指標系統都可以用 OpenTelemetry 建模,而不會損失語義或精確性,這使它成爲互操作性的完美指標系統。

OpenTelemetry 有三個模型:

在這篇文章中討論的指標都是事件模型的一部分,而轉換則是從事件模型到流模型的轉換的一部分。(作爲一個開發者,你其實不需要擔心這些模型,但有一個基本的瞭解會更有幫助)。

OpenTelemetry SDK 允許我們:

OpenTelemetry 指標的工作方式是使用全局的 MeterProvider 來創建一個 Meter,並將其與一個或多個 Instrument 相關聯,每一個 Instrument 都被用來創建一系列的 Measurements。這些測量值在 View 中被彙總爲一個指標。然後通過 Metric Reader 和 Metric Exporter 的組合來觀察和導出指標(可以是拉或推)。

圖示說明 OpenTelemetry 中 MeterProvider 的要素

Instrument 或 Measurements 是我們在應用中創建或觀察到的東西,而指標則表達了我們與可觀測性數據的消費者分享的該監測的當前聚合值。

OpenTelemetry 允許以多種方式將屬性(標籤)附加到指標上。最常見的有:

從 Measurements 到 Metrics

Measurements 的創建非常快,尤其是在同步的情況下,但這可能會迅速壓垮一個指標管道。爲了解決這個問題,Prometheus 使用了一個帶有抓取間隔的 Pull 機制,而 OpenTelemetry 通過給每個 Instrument 附加一個 Aggregation View 來解決收集路徑中的問題,然後將數據傳遞給觀察它們的 MetricReader 和輸出它們的 MetricExporter:

從測量到指標:OpenTelemetry 中指標的收集路徑圖

MetricReader 負責在 Instrument 沒有 View 的情況下附加默認的 View,也負責定義 MetricExporters,然後向其發送這些觀測值。MetricReader 還將改變指標的時間性,從默認的 Cumulative(新值累加到最後一個值上,這與我們在 Prometheus 中看到的相同)改爲 Delta(度量值是新舊值之差)。

每個 Instrument 都與一個 MetricReader 相關聯,如果沒有爲一個 Instrument 定義其他 View,它就負責附加默認 View。此外,它還定義了時間性:可能從默認的 Cumulative(當前值加到以前的值,如 Prometheus 中使用的)切換到 Delta(報告當前觀測值和最後觀測值之間的差異,減少計算速率時客戶端的開銷)。

MetricReader 和 MetricExporter 的組合決定了數據如何被髮送到下游。一個非常流行的方法是使用 PeriodicExportingMetricReader 和 OTLPMetricExporter,每隔一段時間(默認爲 60 秒)對指標進行採樣,並將其發送到 OpenTelemetry 採集器(它將使用另一個 Exporter)進行進一步處理。許多其他的導出器可用於各種語言。

一些流行的導出器有:

在 Python 中,初始化 OpenTelemetry 指標並附加一個默認的 MetricReader 和 MetricExporter(發送指標到本地 OpenTelemetry 採集器),代碼看起來像這樣:

from opentelemetry._metrics import get_meter_provider, set_meter_provider
from opentelemetry.exporter.otlp.proto.grpc._metric_exporter import (
    OTLPMetricExporter,
)
from opentelemetry.sdk._metrics import MeterProvider
from opentelemetry.sdk._metrics.export import PeriodicExportingMetricReader

exporter = OTLPMetricExporter(insecure=True)
reader = PeriodicExportingMetricReader(exporter)
provider = MeterProvider(metric_readers=[reader])
set_meter_provider(provider)

監測併發送 Measurements

OpenTelemetry 提供了六種類型的 Instruments,我們可以用它們來捕獲測量結果。它們可以被分爲兩大類:同步和異步。每個 Instrument 都可以發送測量值,每個 Instrument 都可以與屬性相關聯。同步 Instrument 以類似於 Prometheus 指標的方式在應用程序代碼中實現,通過在應用程序中插入代碼,每次執行時都會更新一個值。它們可以與當前的 Context 上下文相關聯(這有助於描述當前的應用狀態)。

異步 Instrument 則會註冊一個回調函數,只在觀察時發送數值。例如,可以註冊一個異步 Instrument,每 10 秒報告一個傳感器的值。這些 Instrument 不能與當前的 Context 相關聯,因爲它們位於主程序的外部,而不是在主程序運行時被調用,它們根據觀察者的要求觀察信號數據。在某些方面,它們類似於 Prometheus 通過 Exporter 對一個未被檢測的應用程序進行監控。

所有 Instrument 的創建都有一個名稱、一個描述和一個測量單位(必須遵循 Instrument 單位規則)。異步 Instrument 還指定了回調函數,該函數被調用以觀察測量結果。開發人員可以使用的 OpenTelemetry Instrument 類型,如下表所示。(令人困惑的是,語言呈現給用戶的建議名稱與測量名稱不一樣,用 Observable 來代替 Asynchronous。即 observable_counter。)

下表概述了 OpenTelemetry 中 Instrument 類型的名稱和特點。

現在,讓我們來講一下每一種 Instrument 類型。

Counter / Asynchronous Counter

Counter 可譯爲” 計數器”,則 Asynchronous Counter 譯爲” 異步計數器”

Counter 是一個同步 Instrument,它總是在增加,即它是單調的,只接受非負的值。不出所料,它與 Prometheus 的 Counter 是相同的。Counter 通過接收增量或 Delta 差值來工作。

當使用計數器時,語言 SDK 中會有一個 add 的操作,必須提供一個非負數來增加計數器的值。同時可以附加一組可選的屬性。這些屬性類似於 Prometheus 的標籤。

異步計數器的不同之處在於通過回調而不是 add 函數來操作。當觀察 Instrument 時,回調函數會被執行,並將傳回一個或多個測量值,表示爲絕對值(不是 delta 值)。一旦這些值被傳遞,它們將在內部通過計算得到 delta 值。

例如,你可以實現一個異步計數器,報告應用程序自啓動以來所消耗的 CPU 時間。這一信息將在回調中從操作系統中提取並返回。可以一次性返回幾個值,比如每個 CPU 或線程都有一個值。這些測量值總是被期望能夠通過一種有意義的方式進行跨屬性求和(在這種情況下,我們可以得到系統使用的總 CPU 時間)。

請注意,由於 Python SDK 還不穩定,我們需要導入_metrics,而不是本文代碼示例的 metrics。未來可能還會有一些破壞性的變更。隨着項目的進展,我們會保持更新。目前的例子是用 OpenTelemetry Python SDK v1.11.1 編寫的。

在 Python 中,創建和使用計數器和異步計數器的例子看起來是這樣的:

from opentelemetry._metrics import get_meter_provider
from opentelemetry._metrics.observation import Observation

meter = get_meter_provider().get_meter("otel-demo")

# Counter
counter = meter.create_counter("counter")

# This adds 100 to the total (a delta value)
counter.add(100,{"app""timescale"})

# Callback function for Async counter
def observable_counter_func() -> [Observation]:
    # This reports that the current value is 10, which will be
    # converted to a delta internally
    return [Observation(10, {"app""timescale"}]

# Async Counter
observable_counter = meter.create_observable_counter(
    "observable_counter"[observable_counter_func]
)

UpDownCounter / Asynchronous UpDownCounter

UpDownCounter 是一個類似於 Counter 的同步 Instrument,但它允許傳遞負的 delta 值(它可以不是單調的)。Counter 適合表示已經提交的作業數量,而 UpDownCounter 則適合表示當前正在處理的作業數量(它可以向上和向下移動)。需要注意的是,這與 Prometheus 中的 Gauge 的用法不一樣,因爲我們記錄的是變化,而不是絕對值。

一個 UpDownCounter 提供了一個 add 操作,與 Counter 操作相同:與之相反的是它接受負的數據值。由屬性數據關聯的值被期望是可以求和的。

不出所料,異步 UpDownCounter 提供了一個回調接口,返回一個或多個測量值,將每個測量值表達爲一個絕對值,該值將在內部被計算爲 delta 值。

OpenTelemetry 規範指出,當被返回的值很容易被觀察到時,不應該使用同步的 UpDownCounter。在這種情況下,應該使用一個異步 UpDownCounter 來代替。

在 Python 中,創建和使用 UpDownCounter 和 Asynchronous UpDownCounter 的例子看起來是這樣的:

from opentelemetry._metrics import get_meter_provider
from opentelemetry._metrics.observation import Observation

meter = get_meter_provider().get_meter("otel-demo")

# UpDownCounter
up_down_counter = meter.create_up_down_counter("up_down_counter")

# This adds 100, then removes 10 from the total (both delta values)
up_down_counter.add(100,{"app""timescale"})
up_down_counter.add(-10,{"app""timescale"})

# Callback function for Async counter
def observable_up_down_counter_func() -> [Observation]:
    # This reports that the current value is 10, which will be
    # converted to a delta internally
    return [Observation(10, {"app""timescale"})]

# Async UpDownCounter, note the observable prefix
observable_up_down_counter = meter.create_observable_up_down_counter(
    "observable_up_down_counter"[observable_up_down_counter_func]
)

Histogram

Histogram 可譯爲直方圖。

Histogram 是一種同步 Instrument,它允許記錄在統計上相互關聯的多個數值。當你不想孤立地分析數據點,而是想通過跟蹤落在每個預定義桶中的值的數量以及最小值和最大值(如果有進行配置)來生成關於其分佈的統計信息時,你會選擇 Histogram。

直方圖只有一個單一的方法被暴露出來:record。記錄需要一個非負的觀察值,並可以附加一組可選的屬性。

當你查看 HTTP 響應時間時,你可能會選擇直方圖:知道每個請求發生時的確切響應時間可能並不那麼有用(而且更適合鏈路追蹤數據,它將暴露每個請求的開始和結束時間),但從服務層面來看,能夠報告中位響應時間和超過 95 分位數的 HTTP 請求數量可能更有意思。

需要注意的是,記錄測量結果並不能創建 Histogram;默認的 Aggregation(Explicit Bucket Histogram Aggregation)可以。當使用 Histogram Instrument 時,重要的是確保桶也被配置了。默認值是 [0, 5, 10, 25, 50, 75, 100, 250, 500, 1000],而這並不總是理想的。你可以在下面幾段看到一些創建 View 的例子。

在 Python 中,一個創建和使用直方圖的例子是這樣的:

from opentelemetry._metrics import get_meter_provider
from opentelemetry._metrics.observation import Observation

meter = get_meter_provider().get_meter("otel-demo")

# Histogram
histogram = meter.create_histogram("histogram")

# This records a value of 100
histogram.record(100,{"app""timescale"})

Asynchronous Gauge

異步 Gauge 在 OpenTelemetry API 中的獨特之處在於兩個方面:它沒有同步的實現,並且被設計用來表示那些相加沒有意義的值,即使它們共享屬性數據。

這方面的一個例子是一個房子的各個房間的溫度。這是常見的數據,但是把它作爲一個總值來報告沒有任何意義–你可能像求一個平均值或最大值,但絕不是一個總和。這是與 Prometheus 不同的方法,Prometheus 將這些類型的要求編碼到指標的命名規則中。在 OpenTelemetry 中,如果你使用一個 Asynchronous Gauge,你不能像使用其他指標類型那樣對其進行彙總。

與所有的異步 Instrument 一樣,在創建異步 Instrument 時,需要傳遞一個回調,它可以返回一個或多個(在這種情況下是完全離散的)測量值。

在 Python 中,創建和使用 Asynchronous Gauge 的例子是這樣的:

from opentelemetry._metrics import get_meter_provider
from opentelemetry._metrics.observation import Observation

meter = get_meter_provider().get_meter("otel-demo")

# Callback function for Async gauge
def observable_gauge_func() -> [Observation]:
    # This reports that the current value is 10
    return [Observation(10, {"app""timescale"})]

# Async Gauge, note the observable prefix
observable_gauge = meter.create_observable_gauge(
    "observable_gauge"[observable_gauge_func]
)

—__4 

Views 和 Aggregations

OpenTelemetry 中的 View 定義了一個 Aggregation 操作,它接受一系列的 Measurements,並在該時間點上將其表達爲一個單一的指標值。隨着更多的 Measurements 被創建,指標的值會被持續更新。如果沒有爲一個 Instrument 創建 View,那麼就會根據 Instrument 類型選擇一個默認的 Aggregation。自定義 View 可以通過 Meter 名稱、Instrument 名稱、Instrument 類型或通配符來定位。

在 OpenTelemetry 中,有三種 Aggregation 類型:

下表定義了每種 OpenTelemetry Instrument 類型的默認 Aggregation:

這段 Python 代碼使用 ConsoleMetricExporter 寫入控制檯,也改變了所有柱狀圖 Instrument 的桶狀結構:

from opentelemetry._metrics import get_meter_provider, set_meter_provider
from opentelemetry.sdk._metrics import MeterProvider
from opentelemetry.sdk._metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk._metrics.export import ConsoleMetricExporter
from opentelemetry.sdk._metrics.aggregation import ExplicitBucketHistogramAggregation

exporter = ConsoleMetricExporter()
reader = PeriodicExportingMetricReader(exporter)
provider = MeterProvider(
    metric_readers=[reader],
    views=[View(
        instrument_name="*”,
        aggregation=ExplicitBucketHistogramAggregation(
            (1,20,50,100,1000)
    ))],
)
set_meter_provider(provider)

總結

在指標系列博文的第二部分,我們討論了 OpenTelemetry 標準,重點是它的六種 Instrument 類型:counters、asynchronous counters、UpDownCounters、asynchronous UpDownCounters、histograms 和 asynchronous gauges。

在該系列的最後一篇博文中,我們將介紹該模型與 Prometheus 的比較,解釋其中的差異,並分享我們關於選型的建議。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/-BRlyuMczl01P6uVh5h9Ng