Linux中國

理解監測指標,並使用 Python 去監測它們

當我第一次看到術語「 計數器 counter 」和「 計量器 gauge 」和使用顏色及標記著「平均數」和「大於 90%」的數字圖表時,我的反應之一是逃避。就像我看到它們一樣,我並不感興趣,因為我不理解它們是幹什麼的或如何去使用。因為我的工作不需要我去注意它們,它們被我完全無視。

這都是在兩年以前的事了。隨著我的職業發展,我希望去了解更多關於我們的網路應用程序的知識,而那個時候就是我開始去學習 監測指標 metrics 的時候。

我的理解監測的學習之旅共有三個階段(到目前為止),它們是:

  • 階段 1:什麼?(王顧左右)
  • 階段 2:沒有指標,我們真的是瞎撞。
  • 階段 3:出現不合理的指標我們該如何做?

我現在處於階段 2,我將分享到目前為止我學到了些什麼。我正在向階段 3 進發,在本文結束的位置我提供了一些我正在使用的學習資源。

我們開始吧!

需要的軟體

在文章中討論時用到的 demo 都可以在 我的 GitHub 倉庫 中找到。你需要安裝 docker 和 docker-compose 才能使用它們。

為什麼要監測

關於監測的主要原因是:

  • 理解 正常的 和 不正常的 系統和服務的特徵
  • 做容量規劃、彈性伸縮
  • 有助於排錯
  • 了解軟體/硬體改變的效果
  • 測量響應中的系統行為變化
  • 當系統出現意外行為時發出警報

指標和指標類型

從我們的用途來看,一個指標就是在一個給定時間點上的某些數量的 測量 值。博客文章的總點擊次數、參與討論的總人數、在緩存系統中數據沒有被找到的次數、你的網站上的已登錄用戶數 —— 這些都是指標的例子。

它們總體上可以分為三類:

計數器

以你的個人博客為例。你發布一篇文章後,過一段時間後,你希望去了解有多少點擊量,這是一個只會增加的數字。這就是一個 計數器 counter 指標。在你的博客文章的生命周期中,它的值從 0 開始增加。用圖表來表示,一個計數器看起來應該像下面的這樣:

![Counter metric](/data/attachment/album/201809/14/095111xnvttvuzoilv6n0z.png "Counter metric")

一個計數器指標總是在增加的。

計量器

如果你想去跟蹤你的博客每天或每周的點擊量,而不是基於時間的總點擊量。這種指標被稱為一個 計量器 gauge ,它的值可上可下。用圖表來表示,一個計量器看起來應該像下面的樣子:

![gauge metric](/data/attachment/album/201809/14/095111n1nknrr8zncnkkcq.png "gauge metric")

一個計量器指標可以增加或減少。

一個計量器的值在某些時間窗口內通常有一個 最大值 ceiling 和<ruby最小值floor。

柱狀圖和計時器

柱狀圖 histogram (在 Prometheus 中這麼叫它)或 計時器 timer (在 StatsD 中這麼叫它)是一個跟蹤 已採樣的觀測結果 的指標。不像一個計數器類或計量器類指標,柱狀圖指標的值並不是顯示為上或下的樣式。我知道這可能並沒有太多的意義,並且可能和一個計量器圖看上去沒有什麼不同。它們的不同之處在於,你期望使用柱狀圖數據來做什麼,而不是與一個計量器圖做比較。因此,監測系統需要知道那個指標是一個柱狀圖類型,它允許你去做哪些事情。

![Histogram metric](/data/attachment/album/201809/14/095112c46b9l913up3ahz6.png "Histogram metric")

一個柱狀圖指標可以增加或減少。

Demo 1:計算和報告指標

Demo 1 是使用 Flask 框架寫的一個基本的 web 應用程序。它演示了我們如何去 計算 和 報告 指標。

src 目錄中有 app.pysrc/helpers/middleware.py 應用程序,包含以下內容:

from flask import request
import csv
import time

def start_timer():
    request.start_time = time.time()

def stop_timer(response):
    # convert this into milliseconds for statsd
    resp_time = (time.time() - request.start_time)*1000
    with open(&apos;metrics.csv&apos;, &apos;a&apos;, newline=&apos;&apos;) as f:
        csvwriter = csv.writer(f)
        csvwriter.writerow([str(int(time.time())), str(resp_time)])

    return response

def setup_metrics(app):
    app.before_request(start_timer)
    app.after_request(stop_timer)

當在應用程序中調用 setup_metrics() 時,它配置在一個請求被處理之前調用 start_timer() 函數,然後在該請求處理之後、響應發送之前調用 stop_timer() 函數。在上面的函數中,我們寫了時間戳並用它來計算處理請求所花費的時間。

當我們在 demo1 目錄中運行 docker-compose up,它會啟動這個 web 應用程序,然後在一個客戶端容器中可以生成一些對 web 應用程序的請求。你將會看到創建了一個 src/metrics.csv 文件,它有兩個欄位:timestamprequest_latency

通過查看這個文件,我們可以推斷出兩件事情:

  • 生成了很多數據
  • 沒有觀測到任何與指標相關的特徵

沒有觀測到與指標相關的特徵,我們就不能說這個指標與哪個 HTTP 端點有關聯,或這個指標是由哪個應用程序的節點所生成的。因此,我們需要使用合適的元數據去限定每個觀測指標。

《Statistics 101》

(LCTT 譯註:這是一本統計學入門教材的名字)

假如我們回到高中數學,我們應該回憶起一些統計術語,雖然不太確定,但應該包括平均數、中位數、百分位和柱狀圖。我們來簡要地回顧一下它們,不用去管它們的用法,就像是在上高中一樣。

平均數

平均數 mean ,即一系列數字的平均值,是將數字彙總然後除以列表的個數。3、2 和 10 的平均數是 (3+2+10)/3 = 5。

中位數

中位數 median 是另一種類型的平均,但它的計算方式不同;它是列表從小到大排序(反之亦然)後取列表的中間數字。以我們上面的列表中(2、3、10),中位數是 3。計算並不是非常直觀,它取決於列表中數字的個數。

百分位

百分位 percentile 是指那個百(千)分比數字低於我們給定的百分數的程度。在一些場景中,它是指這個測量值低於我們數據的百(千)分比數字的程度。比如,上面列表中 95% 是 9.29999。百分位的測量範圍是 0 到 100(不包括)。0% 是一組數字的最小分數。你可能會想到它的中位數是 50%,它的結果是 3。

一些監測系統將百分位稱為 upper_X,其中 X 就是百分位;upper 90 指的是在 90% 的位置的值。

分位數

「q-分位數」是將有 N 個數的集合等分為 qN 級。q 的取值範圍為 0 到 1(全部都包括)。當 q 取值為 0.5 時,值就是中位數。( 分位數 quantile )和百分位數的關係是,分位數值 q 等於 100 百分位值。

柱狀圖

<ruby柱狀圖histogram這個指標,我們前面學習過,它是監測系統中一個實現細節。在統計學中,一個柱狀圖是一個將數據分組為 桶 的圖表。我們來考慮一個人為的不同示例:閱讀你的博客的人的年齡。如果你有一些這樣的數據,並想將它進行大致的分組,繪製成的柱狀圖將看起來像下面的這樣:

![Histogram graph](/data/attachment/album/201809/14/095112antrjrmju6mo9cd6.png "Histogram graph")

累積柱狀圖

一個 累積柱狀圖 cumulative histogram 也是一個柱狀圖,它的每個桶的數包含前一個桶的數,因此命名為累積。將上面的數據集做成累積柱狀圖後,看起來應該是這樣的:

![Cumulative histogram](/data/attachment/album/201809/14/095113yqx6xph8xt6xxonp.png "Cumulative histogram")

我們為什麼需要做統計?

在上面的 Demo 1 中,我們注意到在我們報告指標時,這裡生成了許多數據。當我們將它們用於指標時我們需要做統計,因為它們實在是太多了。我們需要的是整體行為,我們沒法去處理單個值。我們預期展現出來的值的行為應該是代表我們觀察的系統的行為。

Demo 2:在指標上增加特徵

在我們上面的的 Demo 1 應用程序中,當我們計算和報告一個請求的延遲時,它指向了一個由一些特徵 唯一標識的特定請求。下面是其中一些:

  • HTTP 端點
  • HTTP 方法
  • 運行它的主機/節點的標識符

如果我們將這些特徵附加到要觀察的指標上,每個指標將有更多的內容。我們來解釋一下 Demo 2 中添加到我們的指標上的特徵。

在寫入指標時,src/helpers/middleware.py 文件將在 CSV 文件中寫入多個列:

node_ids = [&apos;10.0.1.1&apos;, &apos;10.1.3.4&apos;]

def start_timer():
    request.start_time = time.time()

def stop_timer(response):
    # convert this into milliseconds for statsd
    resp_time = (time.time() - request.start_time)*1000
    node_id = node_ids[random.choice(range(len(node_ids)))]
    with open(&apos;metrics.csv&apos;, &apos;a&apos;, newline=&apos;&apos;) as f:
        csvwriter = csv.writer(f)
        csvwriter.writerow([
            str(int(time.time())), &apos;webapp1&apos;, node_id,
            request.endpoint, request.method, str(response.status_code),
            str(resp_time)
        ])

    return response

因為這只是一個演示,在報告指標時,我們將隨意的報告一些隨機 IP 作為節點的 ID。當我們在 demo2 目錄下運行 docker-compose up 時,我們的結果將是一個有多個列的 CSV 文件。

用 pandas 分析指標

我們將使用 pandas 去分析這個 CSV 文件。運行 docker-compose up 將列印出一個 URL,我們將使用它來打開一個 Jupyter 會話。一旦我們上傳 Analysis.ipynb notebook 到會話中,我們就可以將 CSV 文件讀入到一個 pandas 數據幀 DataFrame 中:

import pandas as pd
metrics = pd.read_csv(&apos;/data/metrics.csv&apos;, index_col=0)

index_col 表明我們要指定時間戳作為索引。

因為每個特徵我們都要在數據幀中添加一個列,因此我們可以基於這些列進行分組和聚合:

import numpy as np
metrics.groupby([&apos;node_id&apos;, &apos;http_status&apos;]).latency.aggregate(np.percentile, 99.999)

更多內容請參考 Jupyter notebook 在數據上的分析示例。

我應該監測什麼?

一個軟體系統有許多的變數,這些變數的值在它的生命周期中不停地發生變化。軟體是運行在某種操作系統上的,而操作系統同時也在不停地變化。在我看來,當某些東西出錯時,你所擁有的數據越多越好。

我建議去監測的關鍵操作系統指標有:

  • CPU 使用
  • 系統內存使用
  • 文件描述符使用
  • 磁碟使用

還需要監測的其它關鍵指標根據你的軟體應用程序不同而不同。

網路應用程序

如果你的軟體是一個監聽客戶端請求和為它提供服務的網路應用程序,需要測量的關鍵指標還有:

  • 入站請求數(計數器)
  • 未處理的錯誤(計數器)
  • 請求延遲(柱狀圖/計時器)
  • 排隊時間,如果在你的應用程序中有隊列(柱狀圖/計時器)
  • 隊列大小,如果在你的應用程序中有隊列(計量器)
  • 工作進程/線程用量(計量器)

如果你的網路應用程序在一個客戶端請求的環境中向其它服務發送請求,那麼它應該有一個指標去記錄它與那個服務之間的通訊行為。需要監測的關鍵指標包括請求數、請求延遲、和響應狀態。

HTTP web 應用程序後端

HTTP 應用程序應該監測上面所列出的全部指標。除此之外,還應該按 HTTP 狀態代碼分組監測所有非 200 的 HTTP 狀態代碼的大致數據。如果你的 web 應用程序有用戶註冊和登錄功能,同時也應該為這個功能設置指標。

長時間運行的進程

長時間運行的進程如 Rabbit MQ 消費者或任務隊列的工作進程,雖然它們不是網路服務,它們以選取一個任務並處理它的工作模型來運行。因此,我們應該監測請求的進程數和這些進程的請求延遲。

不管是什麼類型的應用程序,都有指標與合適的元數據相關聯。

將監測集成到一個 Python 應用程序中

將監測集成到 Python 應用程序中需要涉及到兩個組件:

  • 更新你的應用程序去計算和報告指標
  • 配置一個監測基礎設施來容納應用程序的指標,並允許去查詢它們

下面是記錄和報告指標的基本思路:

def work():
    requests += 1
    # report counter
    start_time = time.time()

    # < do the work >

    # calculate and report latency
    work_latency = time.time() - start_time
    ...

考慮到上面的模式,我們經常使用修飾符、內容管理器、中間件(對於網路應用程序)所帶來的好處去計算和報告指標。在 Demo 1 和 Demo 2 中,我們在一個 Flask 應用程序中使用修飾符。

指標報告時的拉取和推送模型

大體來說,在一個 Python 應用程序中報告指標有兩種模式。在 拉取 模型中,監測系統在一個預定義的 HTTP 端點上「刮取」應用程序。在推送 模型中,應用程序發送數據到監測系統。

![Pull and push models](/data/attachment/album/201809/14/095113wherxv3ghhy1xo1y.png "Pull and push models")

工作在 拉取 模型中的監測系統的一個例子是 Prometheus。而 StatsD 是 推送 模型的一個例子。

集成 StatsD

將 StatsD 集成到一個 Python 應用程序中,我們將使用 StatsD Python 客戶端,然後更新我們的指標報告部分的代碼,調用合適的庫去推送數據到 StatsD 中。

首先,我們需要去創建一個客戶端實例:

statsd = statsd.StatsClient(host=&apos;statsd&apos;, port=8125, prefix=&apos;webapp1&apos;)

prefix 關鍵字參數將為通過這個客戶端報告的所有指標添加一個指定的前綴。

一旦我們有了客戶端,我們可以使用如下的代碼為一個計時器報告值:

statsd.timing(key, resp_time)

增加計數器:

statsd.incr(key)

將指標關聯到元數據上,一個鍵的定義為:metadata1.metadata2.metric,其中每個 metadataX 是一個可以進行聚合和分組的欄位。

這個演示應用程序 StatsD 是將 statsd 與 Python Flask 應用程序集成的一個完整示例。

集成 Prometheus

要使用 Prometheus 監測系統,我們使用 Promethius Python 客戶端。我們將首先去創建有關的指標類對象:

REQUEST_LATENCY = Histogram(&apos;request_latency_seconds&apos;, &apos;Request latency&apos;,
    [&apos;app_name&apos;, &apos;endpoint&apos;]
)

在上面的語句中的第三個參數是與這個指標相關的標識符。這些標識符是由與單個指標值相關聯的元數據定義的。

去記錄一個特定的觀測指標:

REQUEST_LATENCY.labels(&apos;webapp&apos;, request.path).observe(resp_time)

下一步是在我們的應用程序中定義一個 Prometheus 能夠刮取的 HTTP 端點。這通常是一個被稱為 /metrics 的端點:

@app.route(&apos;/metrics&apos;)
def metrics():
    return Response(prometheus_client.generate_latest(), mimetype=CONTENT_TYPE_LATEST)

這個演示應用程序 Prometheus 是將 prometheus 與 Python Flask 應用程序集成的一個完整示例。

哪個更好:StatsD 還是 Prometheus?

本能地想到的下一個問題便是:我應該使用 StatsD 還是 Prometheus?關於這個主題我寫了幾篇文章,你可能發現它們對你很有幫助:

指標的使用方式

我們已經學習了一些關於為什麼要在我們的應用程序上配置監測的原因,而現在我們來更深入地研究其中的兩個用法:報警和自動擴展。

使用指標進行報警

指標的一個關鍵用途是創建警報。例如,假如過去的五分鐘,你的 HTTP 500 的數量持續增加,你可能希望給相關的人發送一封電子郵件或頁面提示。對於配置警報做什麼取決於我們的監測設置。對於 Prometheus 我們可以使用 Alertmanager,而對於 StatsD,我們使用 Nagios

使用指標進行自動擴展

在一個雲基礎設施中,如果我們當前的基礎設施供應過量或供應不足,通過指標不僅可以讓我們知道,還可以幫我們實現一個自動伸縮的策略。例如,如果在過去的五分鐘里,在我們伺服器上的工作進程使用率達到 90%,我們可以水平擴展。我們如何去擴展取決於雲基礎設施。AWS 的自動擴展,預設情況下,擴展策略是基於系統的 CPU 使用率、網路流量、以及其它因素。然而,讓基礎設施伸縮的應用程序指標,我們必須發布 自定義的 CloudWatch 指標

在多服務架構中的應用程序監測

當我們超越一個單應用程序架構時,比如當客戶端的請求在響應被發回之前,能夠觸發調用多個服務,就需要從我們的指標中獲取更多的信息。我們需要一個統一的延遲視圖指標,這樣我們就能夠知道響應這個請求時每個服務花費了多少時間。這可以用 分散式跟蹤 來實現。

你可以在我的博客文章 《在你的 Python 應用程序中通過 Zipkin 引入分散式跟蹤》 中看到在 Python 中進行分散式跟蹤的示例。

劃重點

總之,你需要記住以下幾點:

  • 理解你的監測系統中指標類型的含義
  • 知道監測系統需要的你的數據的測量單位
  • 監測你的應用程序中的大多數關鍵組件
  • 監測你的應用程序在它的大多數關鍵階段的行為

以上要點是假設你不去管理你的監測系統。如果管理你的監測系統是你的工作的一部分,那麼你還要考慮更多的問題!

其它資源

以下是我在我的監測學習過程中找到的一些非常有用的資源:

綜合的

StatsD/Graphite

Prometheus

避免犯錯(即第 3 階段的學習)

在我們學習監測的基本知識時,時刻注意不要犯錯誤是很重要的。以下是我偶然發現的一些很有見解的資源:

想學習更多內容,參與到 PyCon Cleveland 2018 上的 Amit Saha 的討論,Counter, gauge, upper 90—Oh my!

關於作者

Amit Saha — 我是一名對基礎設施、監測、和工具感興趣的軟體工程師。我是「用 Python 做數學」的作者和創始人,以及 Fedora Scientific Spin 維護者。

關於我的更多信息

via: https://opensource.com/article/18/4/metrics-monitoring-and-python

作者: Amit Saha 選題者: lujun9972 譯者: qhwdw 校對: wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國