インターフェース監視用の Prometheus エクスポーターを開発する

インターフェース監視用の Prometheus エクスポーターを開発する

ブラックボックス監視については皆さんもよくご存知だと思います。ブラックボックスの監視には blackbox_exporter をよく使用します。 K8s でのブラック ボックス監視については、ここを参照してください。

成熟したツールがすでに利用可能であるのに、なぜ自分たちで開発しようとするのでしょうか?

勉強のためだと言ったけど、信じますか?

学習用なので、全体のロジックが複雑すぎる必要はありません。以下の機能を実装する必要があります。

  • 設定ファイルを通じて監視項目を追加できます
  • Prometheusの収集可能なメトリクスを吐き出す
  • TCPおよびHTTP検出をサポート
  • 検出頻度の設定をサポート

序文

始める前に、Prometheus と Prometheus Exporter について簡単に紹介しましょう。

Prometheus は CNCF のオープンソース監視ツールであり、近年最も人気のあるオープンソース プロジェクトの 1 つです。クラウドネイティブのシナリオでは、インジケーターの監視によく使用されます。

Prometheus は 4 種類のインジケーターをサポートしています。

  • カウンター: リクエスト数など、増加のみされ、減少しないメトリック。リクエストごとに、メトリックは 1 ずつ増加します。
  • ゲージ: CPU などの動的に変化するインジケーターで、その変動を確認できます。
  • ヒストグラム: データ サンプルの分布を示す指標。データをバケットに分割し、各バケット内のサンプルの合計数、平均値などの統計情報を計算します。
  • 概要: ヒストグラムと同様に、データ サンプルの分布を表すために使用されますが、サンプル サイズ、合計、平均、上位 4 位、下位 4 位などのより多くの統計情報も表示します。

実際の使用においては、システムの動作状態やパフォーマンス指標をより適切に観察するために、これらの指標を組み合わせて使用​​されることが多いです。

これらの指標はどこから来るのでしょうか?

Prometheus Exporter は、インジケーターを収集して公開するために使用されるツールです。通常、Prometheus Exporter はインジケーターを収集して公開し、その後 Prometheus がインジケーターを収集して保存します。 Grafana または Promethues UI を使用して、インジケーターを照会および表示できます。

Prometheus Exporter は主に 2 つの重要なコンポーネントで構成されています。

  • コレクター: アプリケーションや他のシステムからメトリックを収集し、Prometheus が認識して収集できるメトリックに変換します。
  • エクスポーター: コレクターからメトリック データを取得し、それを Prometheus が読み取り可能な形式に変換します。

では、Prometheus Exporter はどのようにして Prometheus がサポートする 4 種類のインジケーター (カウンター、ゲージ、ヒストグラム、サマリー) を生成するのでしょうか?

Prometheus は、次のようなさまざまな種類のインジケーターを宣言するために使用できるクライアント パッケージ github.com/prometheus/client_golang を提供します。

(1)カウンタータイプの場合
import ( "net/http" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) func main() { // 创建一个Counter指标counterMetric := prometheus.NewCounter(prometheus.CounterOpts{ Name: "example_counter", // 指标名称Help: "An example counter metric.", // 指标帮助信息}) // 注册指标prometheus.MustRegister(counterMetric) // 增加指标值counterMetric.Inc() // 创建一个HTTP处理器来暴露指标http.Handle("/metrics", promhttp.Handler()) // 启动Web服务器http.ListenAndServe(":8080", nil) }
(2)グラウジ型の場合
import ( "net/http" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) func main() { // 创建一个Gauge指标guageMetric := prometheus.NewGauge(prometheus.GaugeOpts{ Name: "example_gauge", // 指标名称Help: "An example gauge metric.", // 指标帮助信息}) // 注册指标prometheus.MustRegister(guageMetric) // 设置指标值guageMetric.Set(100) // 创建一个HTTP处理器来暴露指标http.Handle("/metrics", promhttp.Handler()) // 启动Web服务器http.ListenAndServe(":8080", nil) }
(3)ヒストグラムタイプの場合
import ( "math/rand" "net/http" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) func main() { // 创建一个Histogram指标histogramMetric := prometheus.NewHistogram(prometheus.HistogramOpts{ Name: "example_histogram", // 指标名称Help: "An example histogram metric.", // 指标帮助信息Buckets: prometheus.LinearBuckets(0, 10, 10), // 设置桶宽度}) // 注册指标prometheus.MustRegister(histogramMetric) // 定期更新指标值go func() { for { time.Sleep(time.Second) histogramMetric.Observe(rand.Float64() * 100) } }() // 创建一个HTTP处理器来暴露指标http.Handle("/metrics", promhttp.Handler()) // 启动Web服务器http.ListenAndServe(":8080", nil) }
(4)要約タイプの場合
import ( "math/rand" "net/http" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) func main() { // 创建一个Summary指标summaryMetric := prometheus.NewSummary(prometheus.SummaryOpts{ Name: "example_summary", // 指标名称Help: "An example summary metric.", // 指标帮助信息Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, // 设置分位数和偏差}) // 注册指标prometheus.MustRegister(summaryMetric) // 定期更新指标值go func() { for { time.Sleep(time.Second) summaryMetric.Observe(rand.Float64() * 100) } }() // 创建一个HTTP处理器来暴露指标http.Handle("/metrics", promhttp.Handler()) // 启动Web服务器http.ListenAndServe(":8080", nil) }

上記の例はすべて、インジケーターを作成するときにインジケーターの説明を直接宣言します。最初に説明を宣言してからインジケーターを作成することもできます。次に例を示します。

 import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "net/http") // 1. 定义一个结构体,用于存放描述信息type Exporter struct { summaryDesc *prometheus.Desc } // 2. 定义一个Collector接口,用于存放两个必备函数,Describe和Collect type Collector interface { Describe(chan<- *prometheus.Desc) Collect(chan<- prometheus.Metric) } // 3. 定义两个必备函数Describe和Collect func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { // 将描述信息放入队列ch <- e.summaryDesc } func (e *Exporter) Collect(ch chan<- prometheus.Metric) { // 采集业务指标数据ch <- prometheus.MustNewConstSummary( e.summaryDesc, // 将指标数据与自定义描述信息绑定4711, 403.34, // 是该指标数据的值,这里表示该Summary 指标的计数值和总和值。 map[float64]float64{0.5: 42.3, 0.9: 323.3}, // 是一个map,其中包含了Summary 指标的quantile 值及其对应的值。例如,0.5 表示50% 的样本值处于这个值以下,0.9 表示90% 的样本值处于这个值以下"200", "get", // 是指标的标签值,用于标识和区分指标实例的特征。这些标签值与在NewExporter 中创建的prometheus.NewDesc 函数的第三个参数相对应。 ) } // 4. 定义一个实例化函数,用于生成prometheus数据func NewExporter() *Exporter { return &Exporter{ summaryDesc: prometheus.NewDesc( "example_summary", // 指标名"An example summary metric.", // 帮助信息[]string{"code", "method"}, // 变量标签名,值是可变的prometheus.Labels{"owner": "joker"}, // 常量标签,固定的), } } func main() { // 实例化exporter exporter := NewExporter() // 注册指标prometheus.MustRegister(exporter) // 创建一个HTTP处理器来暴露指标http.Handle("/metrics", promhttp.Handler()) // 启动Web服务器http.ListenAndServe(":8080", nil) }

上記の紹介を通じて、Prometheus エクスポーターを作成する方法についての予備的な理解が得られましたか?それは次のステップに分けられます:

  • 説明情報を格納するためのエクスポーター構造を定義する
  • コレクターインターフェースの実装
  • エクスポーターをインスタンス化する
  • 登録インジケーター
  • 露出指標

今すぐ始める

基本的な知識を得た後、独自のエクスポーターの開発を開始しました。

実装する必要がある機能を確認しましょう。

  • 設定ファイルを通じて監視項目を追加できます
  • Prometheusの収集可能なメトリクスを吐き出す
  • TCPおよびHTTP検出をサポート
  • 検出頻度の設定をサポート

(1) コレクション オブジェクトは構成ファイルを通じて読み込まれるため、まず構成ファイルの形式を決定します。次のような形式になることを期待します。

 - url: "http://www.baidu.com" name: "百度测试" protocol: "http" check_interval: 2s - url: "localhost:2222" name: "本地接口2222检测" protocol: "tcp"

check_intervalは検出頻度です。設定されていない場合、デフォルトは 1 秒です。

設定ファイルの内容を解析する必要があるため、まず次のように設定ファイルの構造を定義する必要があります。

 // InterfaceConfig 定义接口配置结构type InterfaceConfig struct { Name string `yaml:"name"` URL string `yaml:"url"` Protocol string `yaml:"protocol"` CheckInterval time.Duration `yaml:"check_interval,omitempty"` }

次に、config.yaml ファイルに保存されている yaml 形式の構成ファイルを使用します。つまり、config.yaml ファイルを解析してから解析する必要があります。

 // loadConfig 从配置文件加载接口配置func loadConfig(configFile string) ([]InterfaceConfig, error) { config := []InterfaceConfig{} // 从文件加载配置data, err := ioutil.ReadFile(configFile) if err != nil { return nil, err } // 解析配置文件err = yaml.Unmarshal(data, &config) if err != nil { return nil, err } // 设置默认的检测时间间隔为1s for i := range config { if config[i].CheckInterval == 0 { config[i].CheckInterval = time.Second } } return config, nil }

監視オブジェクトは複数存在する可能性があるため、複数のオブジェクトを保存するには []InterfaceConfig{} を使用します。

(2)インターフェース検出のためのコレクターインターフェースを定義し、Promethuesコレクターインターフェースを実装する

type HealthCollector struct { interfaceConfigs []InterfaceConfig healthStatus *prometheus.Desc }

構成ファイルもここに配置され、HealthCollector の初期化時に読み込まれる予定です。

 // NewHealthCollector 创建HealthCollector实例func NewHealthCollector(configFile string) (*HealthCollector, error) { // 从配置文件加载接口配置config, err := loadConfig(configFile) if err != nil { return nil, err } // 初始化HealthCollector collector := &HealthCollector{ interfaceConfigs: config, healthStatus: prometheus.NewDesc( "interface_health_status", "Health status of the interfaces", []string{"name", "url", "protocol"}, nil, ), } return collector, nil }

ここでは、[]string{"name", "url", "protocol"} 動的タグが定義されており、PromQL を使用してインジケーターを照会し、監視アラートを作成できるようにしています。

(3)Prometheus CollectorインターフェースのDescribeメソッドとCollectメソッドを実装する

// Describe 实现Prometheus Collector接口的Describe方法func (c *HealthCollector) Describe(ch chan<- *prometheus.Desc) { ch <- c.healthStatus } // Collect 实现Prometheus Collector接口的Collect方法func (c *HealthCollector) Collect(ch chan<- prometheus.Metric) { var wg sync.WaitGroup for _, iface := range c.interfaceConfigs { wg.Add(1) go func(iface InterfaceConfig) { defer wg.Done() // 检测接口健康状态healthy := c.checkInterfaceHealth(iface) // 创建Prometheus指标var metricValue float64 if healthy { metricValue = 1 } else { metricValue = 0 } ch <- prometheus.MustNewConstMetric( c.healthStatus, prometheus.GaugeValue, metricValue, iface.Name, iface.URL, iface.Protocol, ) }(iface) } wg.Wait() }

Collect メソッドでは、checkInterfaceHealth を使用して検出オブジェクトの監視ステータスを取得し、対応する Prometheus のインジケーターを作成します。ここで、1 は生存状態、0 は異常状態です。

(4)httpおよびtcp検出方法の実装

// checkInterfaceHealth 检测接口健康状态func (c *HealthCollector) checkInterfaceHealth(iface InterfaceConfig) bool { switch iface.Protocol { case "http": return c.checkHTTPInterfaceHealth(iface) case "tcp": return c.checkTCPInterfaceHealth(iface) default: return false } } // checkHTTPInterfaceHealth 检测HTTP接口健康状态func (c *HealthCollector) checkHTTPInterfaceHealth(iface InterfaceConfig) bool { client := &http.Client{ Timeout: 5 * time.Second, } resp, err := client.Get(iface.URL) if err != nil { return false } defer resp.Body.Close() return resp.StatusCode == http.StatusOK } // checkTCPInterfaceHealth 检测TCP接口健康状态func (c *HealthCollector) checkTCPInterfaceHealth(iface InterfaceConfig) bool { conn, err := net.DialTimeout("tcp", iface.URL, 5*time.Second) if err != nil { return false } defer conn.Close() return true }

http と tcp の検出方法は比較的大まかです。 http の場合は、ステータス コードを確認するために 1 回だけリクエストし、tcp の場合は、接続を確立できるかどうかを確認するだけです。

(5)メインメソッドを作成し、開発を完了する。

 func main() { // 解析命令行参数configFile := flag.String("config", "", "Path to the config file") flag.Parse() if *configFile == "" { // 默认使用当前目录下的config.yaml *configFile = "config.yaml" } // 加载配置文件collector, err := NewHealthCollector(*configFile) if err != nil { fmt.Println("Failed to create collector:", err) return } // 注册HealthCollector prometheus.MustRegister(collector) // 启动HTTP服务,暴露Prometheus指标http.Handle("/metrics", promhttp.Handler()) err = http.ListenAndServe(":2112", nil) if err != nil { fmt.Println("Failed to start HTTP server:", err) os.Exit(1) } }

ここでコマンドラインパラメータの解析が追加され、--config で構成ファイルを指定できます。指定されていない場合は、デフォルトで config.yaml が使用されます。

ここで開発は完了です。前項の開発手順では厳密には書かれていませんが、全体的な状況は同様です。

アプリケーションの展開

開発した製品がオンライン化されなければ、何もしていないのと同じであり、KPI は 0 です。リーダーは作業プロセスを気にせず、結果だけを見ています。だから、それが良いか悪いかに関係なく、まずやるべきことはそれを実行させることです。

(1)Dockerfileを書く。もちろん、アプリケーションを実行するにはコンテナを使用する必要があります。

 FROM golang:1.19 AS build-env ENV GOPROXY https://goproxy.cn ADD . /go/src/app WORKDIR /go/src/app RUN go mod tidy RUN GOOS=linux GOARCH=386 go build -v -o /go/src/app/go-interface-health-check FROM alpine COPY --from=build-env /go/src/app/go-interface-health-check /usr/local/bin/go-interface-health-check COPY --from=build-env /go/src/app/config.yaml /opt/ WORKDIR /opt EXPOSE 2112 CMD [ "go-interface-health-check","--config=/opt/config.yaml" ]

(2)docker-compose設定ファイルを作成します。ここでは、docker-compose がデプロイメントに直接使用され、K8s の yaml よりもシンプルで高速です。

 version: '3.8' services: haproxy: image: go-interface-health-check:v0.3 container_name: interface-health-check network_mode: host restart: unless-stopped command: [ "go-interface-health-check","--config=/opt/config.yaml" ] volumes: - /u01/interface-health-check:/opt - /etc/localtime:/etc/localtime:ro user: root logging: driver: json-file options: max-size: 20m max-file: 100

docker-compose up -d を使用してコンテナを実行した後、curl http://127.0.0.1:2112/metrics を使用してメトリックを表示できます。

コレクション展示

ここでは Prometheus の構築については説明しません。不明な場合は、こちらに移動してください。

Prometheus でクロール インジケーターの構成を構成します。

 scrape_configs: - job_name: 'interface-health-check' static_configs: - targets: ['127.0.0.1:2112']

Prometheus を設定してリロードした後、キャプチャしたターゲットが生きているかどうかを確認できます。

最後に、簡単に表示できるように、次のような Grafana パネルを作成できます。

もちろん、必要に応じてアラーム ルールを作成することもできます。 interface_health_status==0 の場合、インターフェースが異常であることを意味します。

やっと

以上でPrometheusエクスポーターの開発は完了です。上記の例は比較的単純で大まかなものであり、実際の状況に応じて調整できます。

数日前、私は馮唐の次の言葉を目にしました。「社会の底辺にいるほど、人間関係をうまく扱う能力は低下します。地位が上がれば上がるほど、人々は毎日物事を研究していると思うかもしれませんが、実際には人々を研究しているだけなのです。

この文章をどう理解しますか?

リンク

[1] https://www.yuque.com/coolops/kubernetes/dff1cg。
[2] https://www.yuque.com/coolops/kubernetes/wd2vts。
[3] https://github.com/prometheus/client_golang/blob/main/prometheus/examples_test.go。
[4] https://www.cnblogs.com/0x00000/p/17557743.html。

<<:  クラウド コンピューティングとエッジ コンピューティングの違いは何ですか?

>>:  ハイブリッド クラウド シナリオにおける BGP 冗長パス障害 - イベント レビュー

推薦する

ramhost-5周年記念、50%オフ、3つのデータセンター

2009 年 1 月 19 日、一人の技術愛好家が独自にシステムを開発し、低価格の VPS サービス...

メールシステムの管理: 開発者とウェブサイト所有者が知っておくべき、注意すべきこと

1. 電子メールはどれくらい重要ですか?中国で最初の電子メールは、1987 年 9 月 20 日に「...

百度が9月以降に新規サイトを登録するための隠れたルールと対応方法

百度が新しいサイトを開設する問題は、よく話題になる。さまざまな声があちこちで聞かれ、さまざまな記事が...

リバースホスト-VPSプロモーション概要 128M 年間支払いはわずか7ドル

Reversehostsは2011年9月に設立されました。OpenVZベースのVPSを提供する企業で...

王華:インターネットはなぜトラフィックを生成するべきなのか、そしてどのようにトラフィックを生成するのか

トラフィックとユーザーの生成は一度きりのことではなく、モバイルで持続的かつ長期的な運用が必要です。な...

クラウドで優れた統合を実現する方法

統合プロジェクトは、以前はバックエンドの IT 運用でした。これらは重要ではありますが、通常は組織の...

ライセンス獲得競争は徐々に沈静化し、サードパーティ決済会社は人員不足を懸念している。

サードパーティ決済事業者間のライセンス獲得競争は徐々に沈静化してきているようだ。ライセンス取得の有無...

tripodcloud: クリスマス + 新年、素晴らしい CN2 GIA VPS、3 つのネットワークへの直接接続

Tripodcloudは、クリスマスと新年を前に特別にいくつかの新しいサーバー(E5-2690v2、...

WeChatの創始者、張小龍の「テンセント特区」:過去と未来

趙南張小龍を有名にしたのはWeChatが初めてではなかった。国内のソフトウェア業界はすでに彼の足跡を...

クラウド コンピューティングが 2020 年に政府の IT イノベーションを推進する理由

2020 年には、さらに多くの政府機関がクラウド コンピューティングの可能性を最大限に活用するでしょ...

3大事業者が共同で躍進:中国のパブリッククラウド市場が急成長

世界を見渡すと、かつては通信事業者がパブリッククラウド市場への参入を急いでいた時期もありました。しか...

中国式 SEO 公式: SEO = なんてこった

SEO に携わるほとんどの人は、古典的な SEO 公式 (SEO=∫Clock=∫C1+L2+K3+...