Go 監控告警入門 Opentelemetry

灯火消逝的码头發表於2024-08-16

前言

Opentelemetry

分散式鏈路跟蹤( Distributed Tracing )的概念最早是由 Google 提出來的,發展至今技術已經比較成熟,也是有一些協議標準可以參考。目前在 Tracing 技術這塊比較有影響力的是兩大開源技術框架:Netflix 公司開源的 OpenTracing 和 Google 開源的 OpenCensus。兩大框架都擁有比較高的開發者群體。為形成統一的技術標準,兩大框架最終磨合成立了 OpenTelemetry 專案,簡稱 otel。otel 有鏈路追蹤和監控告警兩大塊,關於監控告警,可以檢視另一篇文章:Go 鏈路追蹤入門 Opentelemetry

Prometheus

Prometheus 源自 SoundCloud,擁有一整套開源系統監控和警報工具包,是支援 OpenTelemetry 的系統之一,是 CNCF 的第二個專案。

Grafana

Grafana 是一個開源的分析和視覺化平臺,它允許你查詢、視覺化和警報來自各種資料來源的資料。它提供了一個使用者友好的介面,用於建立和共享儀表板、圖表和警報。Grafana 支援廣泛的資料來源,其中就包括 Prometheus

Go 監控告警入門 Opentelemetry

基礎概念

這裡為了簡單入門,儘量簡單的介紹一些抽象概念,結合著程式碼理解,如果不能理解也沒關係,程式碼寫著寫著自然就明白了:

Meter Provider
用於介面化管理全域性的 Meter 建立,相當於全域性的監控指標管理工廠。

Meter
用於介面化建立並管理全域性的 Instrument,不同的 Meter 可以看做是不同的程式元件。

Instrument
用於管理不同元件下的各個不同型別的指標,例如 http.server.request.total

Measurements
對應指標上報的具體的 DataPoint 指標資料,是一系列的數值項。

Metric Reader
用於實現對指標的資料流讀取,內部定義了具體操作指標的資料結構。OpenTelemetry 官方社群提供了多種靈活的 Reader 實現,例如 PeridRader、ManualReader 等。

Metric Exporter
Exporter 用於暴露本地指標到對應的第三方廠商,例如:Promtheus、Zipkin 等。

指標型別

OpenTelemetry metrics 有許多不同指標型別,可以把它想象成類似於 int, float 這種的變數型別:

Counter:只增不減的指標,比如 http 請求總數,位元組大小;

Asynchronous Counter:非同步 Counter;

UpDownCounter:可增可減的指標,比如 http 活動連線數;

Asynchronous UpDownCounter:非同步 Counter;

Gauge:可增可減的指標,瞬時計量的值,比如 CPU 使用,它是非同步的;

Histogram:分組聚合指標,這個較為難以理解一些,可以移步此處檢視,當然,後文也會有一個詳細的例子來使用它。

實戰:採集指標

廢話了一堆,終於可以實戰了。我們先以 http 請求總數為例來走一遍整個採集指標流程。安裝擴充套件:

go get github.com/prometheus/client_golang
go get go.opentelemetry.io/otel/exporters/prometheus
go get go.opentelemetry.io/otel/metric
go get go.opentelemetry.io/otel/sdk/metric

開啟 main.go,編寫以下程式碼:

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"

	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.opentelemetry.io/otel/exporters/prometheus"
	api "go.opentelemetry.io/otel/metric"
	"go.opentelemetry.io/otel/sdk/metric"
)

const meterName = "oldme_prometheus_testing"

var (
	requestHelloCounter api.Int64Counter
)

func main() {
	ctx := context.Background()

	// 建立 prometheus 匯出器
	exporter, err := prometheus.New()
	if err != nil {
		log.Fatal(err)
	}

	// 建立 meter
	provider := metric.NewMeterProvider(metric.WithReader(exporter))
	meter := provider.Meter(meterName)

	// 建立 counter 指標型別
	requestHelloCounter, err = meter.Int64Counter("requests_hello_total")
	if err != nil {
		log.Fatal(err)
	}

	go serveMetrics()

	ctx, _ = signal.NotifyContext(ctx, os.Interrupt)
	<-ctx.Done()
}

func serveMetrics() {
	log.Printf("serving metrics at localhost:2223/metrics")
	http.Handle("/metrics", promhttp.Handler())

	http.Handle("/index", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 記錄 counter 指標
		requestHelloCounter.Add(r.Context(), 1)

		_, _ = w.Write([]byte("Hello, Otel!"))
	}))

	err := http.ListenAndServe(":2223", nil) //nolint:gosec // Ignoring G114: Use of net/http serve function that has no support for setting timeouts.
	if err != nil {
		fmt.Printf("error serving http: %v", err)
		return
	}
}

在我們的程式碼中,我們定義一個名字為 requests_hello_totalInt64Counter 指標型別,Int64Counter 代表這是一個只增不減的 int64 數值,用作記錄請求總數正好合適。執行我們的程式,如果不出錯的話,訪問 http://localhost:2223/index 可以看到 Hello, Otel!。並且我們訪問 http://localhost:2223/metrics 可以看到指標資料:

Go 監控告警入門 Opentelemetry

這裡資料還沒有進行視覺化,我們先把流程走通,多訪問幾次 http://localhost:2223/index 可以看到 requests_hello_total 會增加:

Go 監控告警入門 Opentelemetry

Histogram

接下來我們採集一下 Histogram 指標,統計在 0.1, 0.2, 0.5, 1, 2, 5 秒以內的 http 請求數,在 main.go 中加上相關程式碼,可以直接複製過去:

package main

import (
	"context"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/signal"
	"time"

	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.opentelemetry.io/otel/exporters/prometheus"
	api "go.opentelemetry.io/otel/metric"
	"go.opentelemetry.io/otel/sdk/metric"
)

const meterName = "oldme_prometheus_testing"

var (
	requestHelloCounter      api.Int64Counter
	requestDurationHistogram api.Float64Histogram
)

func main() {
	ctx := context.Background()

	// 建立 prometheus 匯出器
	exporter, err := prometheus.New()
	if err != nil {
		log.Fatal(err)
	}

	// 建立 meter
	provider := metric.NewMeterProvider(metric.WithReader(exporter))
	meter := provider.Meter(meterName)

	// 建立 counter 指標型別
	requestHelloCounter, err = meter.Int64Counter("requests_hello_total")
	if err != nil {
		log.Fatal(err)
	}

	// 建立 Histogram 指標型別
	requestDurationHistogram, err = meter.Float64Histogram(
		"request_hello_duration_seconds",
		api.WithDescription("記錄 Hello 請求的耗時統計"),
		api.WithExplicitBucketBoundaries(0.1, 0.2, 0.5, 1, 2, 5),
	)
	if err != nil {
		log.Fatal(err)
	}

	go serveMetrics()
	go goroutineMock()

	ctx, _ = signal.NotifyContext(ctx, os.Interrupt)
	<-ctx.Done()
}

func serveMetrics() {
	log.Printf("serving metrics at localhost:2223/metrics")
	http.Handle("/metrics", promhttp.Handler())

	http.Handle("/index", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 記錄 counter 指標
		requestHelloCounter.Add(r.Context(), 1)

		// 計算請求處理時間
		startTime := time.Now()
		// 模擬請求處理時間
		time.Sleep(time.Duration(rand.Intn(3)) * time.Second)
		defer func() {
			duration := time.Since(startTime).Seconds()
			requestDurationHistogram.Record(r.Context(), duration)
		}()

		_, _ = w.Write([]byte("Hello, Otel!"))
	}))

	err := http.ListenAndServe(":2223", nil) //nolint:gosec // Ignoring G114: Use of net/http serve function that has no support for setting timeouts.
	if err != nil {
		fmt.Printf("error serving http: %v", err)
		return
	}
}

// 隨機模擬若干個協程
func goroutineMock() {
	for {
		go func() {
			// 等待若干秒
			var s = time.Duration(rand.Intn(10))
			time.Sleep(s * time.Second)
		}()
		time.Sleep(1 * time.Millisecond)
	}
}

走到這裡,程式碼層面結束了,已經成功一半了,程式碼開源在 Github。之後我們就可以安裝 Prometheus 服務端和 Grafana 來進行資料視覺化。

安裝 Prometheus

Prometheus 有多種安裝方式,我這裡依舊採用 Docker 安裝,當然,你也可以使用其他方式安裝,具體安裝方式可以參考其他文章,後續 Grafana 同理,不在贅述,在 Prometheus.yml 中填寫 targets 我們的地址:

scrape_configs:
  - job_name: "prometheus"

    static_configs:
      - targets: ["localhost:2223"]

Prometheus 會自動去 {{target}}/metrics 中拉取我們的指標。之後在瀏覽器開啟 Promethues 的地址,例如我的是:http://localhost:9090,如果全部正常的話可以在 status:targets 中看見我們的指標:

Go 監控告警入門 Opentelemetry

Promethues 的首頁查詢 requests_hello_total 指標可以看到視覺化的圖表:

Go 監控告警入門 Opentelemetry

安裝 Grafana

我的 Grafana 安裝好了,登入進去後是這樣的(我更改後預設顏色):

Go 監控告警入門 Opentelemetry

Data source 中新增 Prometheus 伺服器,然後在 Dashboard 中新增我們想要監控的指標,即可看到更美觀的圖表:

Go 監控告警入門 OpentelemetryGo 監控告警入門 Opentelemetry

相關文章