https://opensource.com/article/18/4/metrics-monitoring-and-python
作者 | Amit Saha
譯者 | qhwdw ?共計翻譯:146.5 篇 貢獻時間:309 天
透過學習這些關鍵的術語和概念來理解 Python 應用監測。
當我第一次看到術語“計數器”和“計量器”和使用顏色及標記著“平均數”和“大於 90%”的數字圖表時,我的反應之一是逃避。就像我看到它們一樣,我並不感興趣,因為我不理解它們是乾什麼的或如何去使用。因為我的工作不需要我去註意它們,它們被我完全無視。
這都是在兩年以前的事了。隨著我的職業發展,我希望去瞭解更多關於我們的網路應用程式的知識,而那個時候就是我開始去學習監測指標的時候。
我的理解監測的學習之旅共有三個階段(到目前為止),它們是:
我現在處於階段 2,我將分享到目前為止我學到了些什麼。我正在向階段 3 進發,在本文結束的位置我提供了一些我正在使用的學習資源。
我們開始吧!
需要的軟體
在文章中討論時用到的 demo 都可以在 我的 GitHub 倉庫[1] 中找到。你需要安裝 docker 和 docker-compose 才能使用它們。
為什麼要監測?
關於監測的主要原因是:
指標和指標型別
從我們的用途來看,一個指標就是在一個給定時間點上的某些數量的 測量 值。部落格文章的總點選次數、參與討論的總人數、在快取系統中資料沒有被找到的次數、你的網站上的已登入使用者數 —— 這些都是指標的例子。
它們總體上可以分為三類:
計數器
以你的個人部落格為例。你釋出一篇文章後,過一段時間後,你希望去瞭解有多少點選量,這是一個只會增加的數字。這就是一個計數器指標。在你的部落格文章的生命週期中,它的值從 0 開始增加。用圖表來表示,一個計數器看起來應該像下麵的這樣:
一個計數器指標總是在增加的。
計量器
如果你想去跟蹤你的部落格每天或每週的點選量,而不是基於時間的總點選量。這種指標被稱為一個計量器,它的值可上可下。用圖表來表示,一個計量器看起來應該像下麵的樣子:
一個計量器指標可以增加或減少。
一個計量器的值在某些時間視窗內通常有一個最大值和
柱狀圖和計時器
柱狀圖(在 Prometheus 中這麼叫它)或計時器(在 StatsD 中這麼叫它)是一個跟蹤 已取樣的觀測結果 的指標。不像一個計數器類或計量器類指標,柱狀圖指標的值並不是顯示為上或下的樣式。我知道這可能並沒有太多的意義,並且可能和一個計量器圖看上去沒有什麼不同。它們的不同之處在於,你期望使用柱狀圖資料來做什麼,而不是與一個計量器圖做比較。因此,監測系統需要知道那個指標是一個柱狀圖型別,它允許你去做哪些事情。
一個柱狀圖指標可以增加或減少。
Demo 1:計算和報告指標
Demo 1[2] 是使用 Flask[3] 框架寫的一個基本的 web 應用程式。它演示了我們如何去 計算和 報告 指標。
在 src
目錄中有 app.py
和 src/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('metrics.csv', 'a', newline='') 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
檔案,它有兩個欄位:timestamp
和 request_latency
。
透過檢視這個檔案,我們可以推斷出兩件事情:
沒有觀測到與指標相關的特徵,我們就不能說這個指標與哪個 HTTP 端點有關聯,或這個指標是由哪個應用程式的節點所生成的。因此,我們需要使用合適的元資料去限定每個觀測指標。
《Statistics 101》
(LCTT 譯註:這是一本統計學入門教材的名字)
假如我們回到高中數學,我們應該回憶起一些統計術語,雖然不太確定,但應該包括平均數、中位數、百分位和柱狀圖。我們來簡要地回顧一下它們,不用去管它們的用法,就像是在上高中一樣。
平均數
平均數,即一系列數字的平均值,是將數字彙總然後除以串列的個數。3、2 和 10 的平均數是 (3+2+10)/3 = 5。
中位數
中位數是另一種型別的平均,但它的計算方式不同;它是串列從小到大排序(反之亦然)後取串列的中間數字。以我們上面的串列中(2、3、10),中位數是 3。計算並不是非常直觀,它取決於串列中數字的個數。
百分位
百分位是指那個百(千)分比數字低於我們給定的百分數的程度。在一些場景中,它是指這個測量值低於我們資料的百(千)分比數字的程度。比如,上面串列中 95% 是 9.29999。百分位的測量範圍是 0 到 100(不包括)。0% 是一組數字的最小分數。你可能會想到它的中位數是 50%,它的結果是 3。
一些監測系統將百分位稱為 upper_X
,其中 X 就是百分位;upper 90
指的是在 90% 的位置的值。
分位數
“q-分位數”是將有 N 個數的集合等分為 qN
級。q
的取值範圍為 0 到 1(全部都包括)。當 q
取值為 0.5 時,值就是中位數。(分位數)和百分位數的關係是,分位數值 q
等於 100
百分位值。
柱狀圖
實現細節。在統計學中,一個柱狀圖是一個將資料分組為 桶 的圖表。我們來考慮一個人為的不同示例:閱讀你的部落格的人的年齡。如果你有一些這樣的資料,並想將它進行大致的分組,繪製成的柱狀圖將看起來像下麵的這樣:
Histogram graph
累積柱狀圖
一個累積柱狀圖也是一個柱狀圖,它的每個桶的數包含前一個桶的數,因此命名為累積。將上面的資料集做成累積柱狀圖後,看起來應該是這樣的:
Cumulative histogram
我們為什麼需要做統計?
在上面的 Demo 1 中,我們註意到在我們報告指標時,這裡生成了許多資料。當我們將它們用於指標時我們需要做統計,因為它們實在是太多了。我們需要的是整體行為,我們沒法去處理單個值。我們預期展現出來的值的行為應該是代表我們觀察的系統的行為。
Demo 2:在指標上增加特徵
在我們上面的的 Demo 1 應用程式中,當我們計算和報告一個請求的延遲時,它指向了一個由一些特徵 唯一標識的特定請求。下麵是其中一些:
如果我們將這些特徵附加到要觀察的指標上,每個指標將有更多的內容。我們來解釋一下 Demo 2[4] 中新增到我們的指標上的特徵。
在寫入指標時,src/helpers/middleware.py
檔案將在 CSV 檔案中寫入多個列:
node_ids = ['10.0.1.1', '10.1.3.4']
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('metrics.csv', 'a', newline='') as f:
csvwriter = csv.writer(f)
csvwriter.writerow([
str(int(time.time())), 'webapp1', node_id,
request.endpoint, request.method, str(response.status_code),
str(resp_time)
])
return response
因為這隻是一個演示,在報告指標時,我們將隨意的報告一些隨機 IP 作為節點的 ID。當我們在 demo2
目錄下執行 docker-compose up
時,我們的結果將是一個有多個列的 CSV 檔案。
用 pandas 分析指標
我們將使用 pandas[5] 去分析這個 CSV 檔案。執行 docker-compose up
將打印出一個 URL,我們將使用它來開啟一個 Jupyter[6] 會話。一旦我們上傳 Analysis.ipynb notebook
到會話中,我們就可以將 CSV 檔案讀入到一個 pandas 資料幀中:
import pandas as pd
metrics = pd.read_csv('/data/metrics.csv', index_col=0)
index_col
表明我們要指定時間戳作為索引。
因為每個特徵我們都要在資料幀中新增一個列,因此我們可以基於這些列進行分組和聚合:
import numpy as np
metrics.groupby(['node_id', 'http_status']).latency.aggregate(np.percentile, 99.999)
更多內容請參考 Jupyter notebook 在資料上的分析示例。
我應該監測什麼?
一個軟體系統有許多的變數,這些變數的值在它的生命週期中不停地發生變化。軟體是執行在某種作業系統上的,而作業系統同時也在不停地變化。在我看來,當某些東西出錯時,你所擁有的資料越多越好。
我建議去監測的關鍵作業系統指標有:
還需要監測的其它關鍵指標根據你的軟體應用程式不同而不同。
網路應用程式
如果你的軟體是一個監聽客戶端請求和為它提供服務的網路應用程式,需要測量的關鍵指標還有:
如果你的網路應用程式在一個客戶端請求的環境中向其它服務傳送請求,那麼它應該有一個指標去記錄它與那個服務之間的通訊行為。需要監測的關鍵指標包括請求數、請求延遲、和響應狀態。
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
工作在 拉取 模型中的監測系統的一個例子是 Prometheus[7]。而 StatsD[8] 是 推送 模型的一個例子。
整合 StatsD
將 StatsD 整合到一個 Python 應用程式中,我們將使用 StatsD Python 客戶端[9],然後更新我們的指標報告部分的程式碼,呼叫合適的庫去推送資料到 StatsD 中。
首先,我們需要去建立一個客戶端實體:
statsd = statsd.StatsClient(host='statsd', port=8125, prefix='webapp1')
prefix
關鍵字引數將為透過這個客戶端報告的所有指標新增一個指定的字首。
一旦我們有了客戶端,我們可以使用如下的程式碼為一個計時器報告值:
statsd.timing(key, resp_time)
增加計數器:
statsd.incr(key)
將指標關聯到元資料上,一個鍵的定義為:metadata1.metadata2.metric
,其中每個 metadataX 是一個可以進行聚合和分組的欄位。
這個演示應用程式 StatsD[10] 是將 statsd 與 Python Flask 應用程式整合的一個完整示例。
整合 Prometheus
要使用 Prometheus 監測系統,我們使用 Promethius Python 客戶端[11]。我們將首先去建立有關的指標類物件:
REQUEST_LATENCY = Histogram('request_latency_seconds', 'Request latency',
['app_name', 'endpoint']
)
在上面的陳述句中的第三個引數是與這個指標相關的識別符號。這些識別符號是由與單個指標值相關聯的元資料定義的。
去記錄一個特定的觀測指標:
REQUEST_LATENCY.labels('webapp', request.path).observe(resp_time)
下一步是在我們的應用程式中定義一個 Prometheus 能夠刮取的 HTTP 端點。這通常是一個被稱為 /metrics
的端點:
@app.route('/metrics')
def metrics():
return Response(prometheus_client.generate_latest(), mimetype=CONTENT_TYPE_LATEST)
這個演示應用程式 Prometheus[12] 是將 prometheus 與 Python Flask 應用程式整合的一個完整示例。
哪個更好:StatsD 還是 Prometheus?
本能地想到的下一個問題便是:我應該使用 StatsD 還是 Prometheus?關於這個主題我寫了幾篇文章,你可能發現它們對你很有幫助:
指標的使用方式
我們已經學習了一些關於為什麼要在我們的應用程式上配置監測的原因,而現在我們來更深入地研究其中的兩個用法:報警和自動擴充套件。
使用指標進行報警
指標的一個關鍵用途是建立警報。例如,假如過去的五分鐘,你的 HTTP 500 的數量持續增加,你可能希望給相關的人傳送一封電子郵件或頁面提示。對於配置警報做什麼取決於我們的監測設定。對於 Prometheus 我們可以使用 Alertmanager[16],而對於 StatsD,我們使用 Nagios[17]。
使用指標進行自動擴充套件
在一個雲基礎設施中,如果我們當前的基礎設施供應過量或供應不足,透過指標不僅可以讓我們知道,還可以幫我們實現一個自動伸縮的策略。例如,如果在過去的五分鐘裡,在我們伺服器上的工作行程使用率達到 90%,我們可以水平擴充套件。我們如何去擴充套件取決於雲基礎設施。AWS 的自動擴充套件,預設情況下,擴充套件策略是基於系統的 CPU 使用率、網路流量、以及其它因素。然而,讓基礎設施伸縮的應用程式指標,我們必鬚髮布 自定義的 CloudWatch 指標[18]。
在多服務架構中的應用程式監測
當我們超越一個單應用程式架構時,比如當客戶端的請求在響應被髮回之前,能夠觸發呼叫多個服務,就需要從我們的指標中獲取更多的資訊。我們需要一個統一的延遲檢視指標,這樣我們就能夠知道響應這個請求時每個服務花費了多少時間。這可以用 分散式跟蹤[19] 來實現。
你可以在我的部落格文章 《在你的 Python 應用程式中透過 Zipkin 引入分散式跟蹤[20]》 中看到在 Python 中進行分散式跟蹤的示例。
劃重點
總之,你需要記住以下幾點:
以上要點是假設你不去管理你的監測系統。如果管理你的監測系統是你的工作的一部分,那麼你還要考慮更多的問題!
其它資源
以下是我在我的監測學習過程中找到的一些非常有用的資源:
綜合的
StatsD/Graphite
Prometheus
避免犯錯(即第 3 階段的學習)
在我們學習監測的基本知識時,時刻註意不要犯錯誤是很重要的。以下是我偶然發現的一些很有見解的資源:
想學習更多內容,參與到 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中國 榮譽推出