歡迎光臨
每天分享高質量文章

理解監測指標,並使用 Python 去監測它們 | Linux 中國

透過學習這些關鍵的術語和概念來理解 Python 應用監測。
— Amit Saha


致謝
編譯自 | 
https://opensource.com/article/18/4/metrics-monitoring-and-python
 
 作者 | Amit Saha
 譯者 | qhwdw ?共計翻譯:146.5 篇 貢獻時間:309 天

透過學習這些關鍵的術語和概念來理解 Python 應用監測。

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

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

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

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

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

我們開始吧!

需要的軟體

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

為什麼要監測?

關於監測的主要原因是:

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

指標和指標型別

從我們的用途來看,一個指標就是在一個給定時間點上的某些數量的 測量 值。部落格文章的總點選次數、參與討論的總人數、在快取系統中資料沒有被找到的次數、你的網站上的已登入使用者數 —— 這些都是指標的例子。

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

計數器

以你的個人部落格為例。你釋出一篇文章後,過一段時間後,你希望去瞭解有多少點選量,這是一個只會增加的數字。這就是一個計數器counter指標。在你的部落格文章的生命週期中,它的值從 0 開始增加。用圖表來表示,一個計數器看起來應該像下麵的這樣:

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

計量器

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

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

一個計量器的值在某些時間視窗內通常有一個最大值ceiling

柱狀圖和計時器

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

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

Demo 1:計算和報告指標

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

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

  1. from flask import request

  2. import csv

  3. import time

  4. def start_timer():

  5.     request.start_time = time.time()

  6. def stop_timer(response):

  7.     # convert this into milliseconds for statsd

  8.     resp_time = (time.time() - request.start_time)*1000

  9.     with open('metrics.csv', 'a', newline='') as f:

  10.         csvwriter = csv.writer(f)

  11.         csvwriter.writerow([str(int(time.time())), str(resp_time)])

  12.     return response

  13. def setup_metrics(app):

  14.     app.before_request(start_timer)

  15.     app.after_request(stop_timer)

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

當我們在 demo1 目錄中執行 docker-compose up,它會啟動這個 web 應用程式,然後在一個客戶端容器中可以生成一些對 web 應用程式的請求。你將會看到建立了一個 src/metrics.csv 檔案,它有兩個欄位:timestamp 和 request_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 百分位值。

柱狀圖

實現細節。在統計學中,一個柱狀圖是一個將資料分組為  的圖表。我們來考慮一個人為的不同示例:閱讀你的部落格的人的年齡。如果你有一些這樣的資料,並想將它進行大致的分組,繪製成的柱狀圖將看起來像下麵的這樣:

Histogram graph

累積柱狀圖

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

Cumulative histogram

我們為什麼需要做統計?

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

Demo 2:在指標上增加特徵

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

◈ HTTP 端點
◈ HTTP 方法
◈ 執行它的主機/節點的識別符號

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

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

  1. node_ids = ['10.0.1.1', '10.1.3.4']

  2. def start_timer():

  3.     request.start_time = time.time()

  4. def stop_timer(response):

  5.     # convert this into milliseconds for statsd

  6.     resp_time = (time.time() - request.start_time)*1000

  7.     node_id = node_ids[random.choice(range(len(node_ids)))]

  8.     with open('metrics.csv', 'a', newline='') as f:

  9.         csvwriter = csv.writer(f)

  10.         csvwriter.writerow([

  11.             str(int(time.time())), 'webapp1', node_id,

  12.             request.endpoint, request.method, str(response.status_code),

  13.             str(resp_time)

  14.         ])

  15.     return response

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

用 pandas 分析指標

我們將使用 pandas[5] 去分析這個 CSV 檔案。執行 docker-compose up 將打印出一個 URL,我們將使用它來開啟一個 Jupyter[6] 會話。一旦我們上傳 Analysis.ipynb notebook 到會話中,我們就可以將 CSV 檔案讀入到一個 pandas 資料幀DataFrame中:

  1. import pandas as pd

  2. metrics = pd.read_csv('/data/metrics.csv', index_col=0)

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

因為每個特徵我們都要在資料幀中新增一個列,因此我們可以基於這些列進行分組和聚合:

  1. import numpy as np

  2. metrics.groupby(['node_id', 'http_status']).latency.aggregate(np.percentile, 99.999)

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

我應該監測什麼?

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

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

◈ CPU 使用
◈ 系統記憶體使用
◈ 檔案描述符使用
◈ 磁碟使用

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

網路應用程式

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

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

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

HTTP web 應用程式後端

HTTP 應用程式應該監測上面所列出的全部指標。除此之外,還應該按 HTTP 狀態程式碼分組監測所有非 200 的 HTTP 狀態程式碼的大致資料。如果你的 web 應用程式有使用者註冊和登入功能,同時也應該為這個功能設定指標。

長時間執行的行程

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

不管是什麼型別的應用程式,都有指標與合適的元資料相關聯。

將監測整合到一個 Python 應用程式中

將監測整合到 Python 應用程式中需要涉及到兩個元件:

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

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

  1. def work():

  2.     requests += 1

  3.     # report counter

  4.     start_time = time.time()

  5.    

  6.     # < do the work >

  7.     # calculate and report latency

  8.     work_latency = time.time() - start_time

  9.     ...

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

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

大體來說,在一個 Python 應用程式中報告指標有兩種樣式。在 拉取 模型中,監測系統在一個預定義的 HTTP 端點上“刮取”應用程式。在推送 模型中,應用程式傳送資料到監測系統。

Pull and push models

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

整合 StatsD

將 StatsD 整合到一個 Python 應用程式中,我們將使用 StatsD Python 客戶端[9],然後更新我們的指標報告部分的程式碼,呼叫合適的庫去推送資料到 StatsD 中。

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

  1. statsd = statsd.StatsClient(host='statsd', port=8125, prefix='webapp1')

prefix 關鍵字引數將為透過這個客戶端報告的所有指標新增一個指定的字首。

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

  1. statsd.timing(key, resp_time)

增加計數器:

  1. statsd.incr(key)

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

這個演示應用程式 StatsD[10] 是將 statsd 與 Python Flask 應用程式整合的一個完整示例。

整合 Prometheus

要使用 Prometheus 監測系統,我們使用 Promethius Python 客戶端[11]。我們將首先去建立有關的指標類物件:

  1. REQUEST_LATENCY = Histogram('request_latency_seconds', 'Request latency',

  2.     ['app_name', 'endpoint']

  3. )

在上面的陳述句中的第三個引數是與這個指標相關的識別符號。這些識別符號是由與單個指標值相關聯的元資料定義的。

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

  1. REQUEST_LATENCY.labels('webapp', request.path).observe(resp_time)

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

  1. @app.route('/metrics')

  2. def metrics():

  3.     return Response(prometheus_client.generate_latest(), mimetype=CONTENT_TYPE_LATEST)

這個演示應用程式 Prometheus[12] 是將 prometheus 與 Python Flask 應用程式整合的一個完整示例。

哪個更好:StatsD 還是 Prometheus?

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

◈ 使用 Prometheus 監測多行程 Python 應用的方式[13]
◈ 使用 Prometheus 監測你的同步 Python 應用[14]
◈ 使用 Prometheus 監測你的非同步 Python 應用[15]

指標的使用方式

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

使用指標進行報警

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

使用指標進行自動擴充套件

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

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

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

你可以在我的部落格文章 《在你的 Python 應用程式中透過 Zipkin 引入分散式跟蹤[20]》 中看到在 Python 中進行分散式跟蹤的示例。

劃重點

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

◈ 理解你的監測系統中指標型別的含義
◈ 知道監測系統需要的你的資料的測量單位
◈ 監測你的應用程式中的大多數關鍵元件
◈ 監測你的應用程式在它的大多數關鍵階段的行為

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

其它資源

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

綜合的

◈ 監測分散式系統[21]
◈ 觀測和監測最佳實踐[22]
◈ 誰想使用秒?[23]

StatsD/Graphite

◈ StatsD 指標型別[24]

Prometheus

◈ Prometheus 指標型別[25]
◈ Prometheus 計量器如何工作?[26]
◈ 為什麼用 Prometheus 累積柱形圖?[27]
◈ 在 Python 中監測批次作業[28]
◈ Prometheus:監測 SoundCloud[29]

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

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

◈ 如何不測量延遲[30]
◈ Prometheus 柱形圖:悲傷的故事[31]
◈ 為什麼平均值很討厭,而百分位很棒[32]
◈ 對延遲的認知錯誤[33]
◈ 誰動了我的 99% 延遲?[34]
◈ 日誌、指標和圖形[35]
◈ HdrHistogram:一個更好的延遲捕獲方式[36]

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

關於作者

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

關於我的更多資訊[39]


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

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

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

贊(0)

分享創造快樂

© 2024 知識星球   網站地圖