站點(diǎn)可靠性工程SRE最佳實(shí)踐 -- 黃金監(jiān)控信號(hào)
黃金信號(hào)(Golden Signals)最初是谷歌在站點(diǎn)可靠性工程(SRE)實(shí)踐的背景下引入的,由谷歌軟件工程師Dave Rensin和Kevin Smathers在2016年O 'Reilly Velocity Conference上的一次演講中提出,其背后的想法是提供一組關(guān)鍵性能指標(biāo)(KPI),用于測(cè)量和監(jiān)控復(fù)雜分布式系統(tǒng)的運(yùn)行狀況。
引入黃金信號(hào)是為了幫助SRE團(tuán)隊(duì)關(guān)注系統(tǒng)可靠性和性能方面真正重要的東西。黃金信號(hào)不依賴(lài)于難以解釋的大量指標(biāo)和告警,而是提供一組簡(jiǎn)單且易于理解的指標(biāo),用于快速評(píng)估系統(tǒng)健康狀況。
自從這一概念提出以來(lái),黃金信號(hào)已在SRE社區(qū)中得到廣泛采用,并被認(rèn)為是監(jiān)控和管理分布式系統(tǒng)運(yùn)行狀況的最佳實(shí)踐。雖然最初黃金信號(hào)專(zhuān)注于延遲、流量、錯(cuò)誤和飽和指標(biāo),但一些組織已經(jīng)調(diào)整了這個(gè)概念,引入了特定于其系統(tǒng)和用例的附加指標(biāo)。不過(guò),通過(guò)一組KPI來(lái)度量和監(jiān)控系統(tǒng)健康的核心思想仍然是黃金信號(hào)概念的核心。
什么是黃金信號(hào)?
黃金信號(hào)是SRE用來(lái)衡量其系統(tǒng)健康狀況的一組四個(gè)關(guān)鍵指標(biāo),包括:
- 延遲(Latency) —— 延遲用來(lái)度量系統(tǒng)響應(yīng)請(qǐng)求所需的時(shí)間,延遲高表明系統(tǒng)可能過(guò)載或遇到其他性能問(wèn)題。
延遲
Prometheus查詢(xún)histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job="fastapi-app"}[5m])) by (le, method, endpoint))通過(guò)直方圖指標(biāo)(histogram metric)度量來(lái)檢測(cè)FastAPI應(yīng)用程序HTTP請(qǐng)求的P95延遲。
該查詢(xún)計(jì)算過(guò)去5分鐘內(nèi)http_request_duration_seconds_bucket度量值(表示落入特定延遲桶的請(qǐng)求數(shù)量)的速率總和,并按延遲(le)、HTTP方法和端點(diǎn)分組。然后,histogram_quantile函數(shù)使用這些值計(jì)算每個(gè)HTTP方法和端點(diǎn)組合的P95延遲。
- 流量(Traffic) —— 流量衡量流經(jīng)系統(tǒng)的數(shù)據(jù)或請(qǐng)求的數(shù)量,流量高表明系統(tǒng)可能正在處理大量請(qǐng)求,或者系統(tǒng)容量存在問(wèn)題。
流量
Prometheus查詢(xún)r(jià)ate(http_requests_total{job="fastapi-app"}[$__rate_interval])通過(guò)計(jì)數(shù)器指標(biāo)(counter metric)度量FastAPI應(yīng)用程序每秒HTTP請(qǐng)求的速率。
該查詢(xún)使用rate函數(shù)來(lái)計(jì)算http_requests_total計(jì)數(shù)器指標(biāo)的每秒增長(zhǎng)率,計(jì)算向FastAPI應(yīng)用程序發(fā)出的HTTP請(qǐng)求總數(shù)。job="fastapi-app"標(biāo)簽選擇器過(guò)濾度量數(shù)據(jù),使其只包含來(lái)自FastAPI的數(shù)據(jù)。
$__rate_interval變量是模板變量,表示計(jì)算速率的持續(xù)時(shí)間,該變量值由用戶(hù)在Prometheus查詢(xún)界面中設(shè)置,用于確定計(jì)算速率的時(shí)間范圍。
例如,如果用戶(hù)將$__rate_interval設(shè)置為5m,查詢(xún)將計(jì)算過(guò)去5分鐘內(nèi)HTTP請(qǐng)求的每秒速率。此查詢(xún)可用于監(jiān)控FastAPI應(yīng)用程序的流量,并識(shí)別請(qǐng)求量隨時(shí)間變化的模式或異常情況。
- 錯(cuò)誤(Errors) —— 錯(cuò)誤度量系統(tǒng)中發(fā)生的錯(cuò)誤數(shù)量,錯(cuò)誤率高表明系統(tǒng)中可能存在bug或其他問(wèn)題。
錯(cuò)誤數(shù)
Prometheus查詢(xún)http_requests_total {endpoint="/generate_error", http_status="500"}檢索web應(yīng)用程序的"/generate_error"端點(diǎn)的HTTP請(qǐng)求并且HTTP狀態(tài)碼為500(內(nèi)部服務(wù)器錯(cuò)誤)的數(shù)量。
該查詢(xún)使用http_requests_total計(jì)數(shù)器指標(biāo),計(jì)算向web應(yīng)用程序發(fā)出的HTTP請(qǐng)求總數(shù)。查詢(xún)通過(guò)指定endpoint="/generate_error"標(biāo)簽選擇器過(guò)濾度量數(shù)據(jù),使其只包括對(duì)"/generate_error"端點(diǎn)的請(qǐng)求。此外,查詢(xún)通過(guò)指定http_status="500"標(biāo)簽選擇器過(guò)濾數(shù)據(jù),只包括HTTP狀態(tài)碼為500的請(qǐng)求。
通過(guò)運(yùn)行這個(gè)查詢(xún),可以深入了解web應(yīng)用中錯(cuò)誤發(fā)生率,以及哪些端點(diǎn)容易出錯(cuò)。這些信息可以幫助識(shí)別和修復(fù)應(yīng)用中的問(wèn)題,提高可靠性,并確保為用戶(hù)提供更好的體驗(yàn)。
- 飽和度(Saturation) —— 飽和度衡量系統(tǒng)的資源利用率,飽和度高表明系統(tǒng)資源(例如CPU或內(nèi)存)可能正在被耗盡。
飽和度
Prometheus查詢(xún)clamp_max(active_requests{job="fastapi-app"} / 10, 1)用于計(jì)算活動(dòng)請(qǐng)求與最大并發(fā)請(qǐng)求數(shù)的比率,并將該比率的值限制為不超過(guò)1。
該查詢(xún)使用active_requests度量(gauge)指標(biāo)檢索FastAPI應(yīng)用程序中的當(dāng)前活動(dòng)請(qǐng)求數(shù)。job="fastapi-app"標(biāo)簽選擇器過(guò)濾度量數(shù)據(jù),使其只包含來(lái)自FastAPI的數(shù)據(jù)。
然后,查詢(xún)將活動(dòng)請(qǐng)求數(shù)除以10,表示系統(tǒng)可以處理的最大并發(fā)請(qǐng)求數(shù)。然后使用clamp_max函數(shù)將該比率的值限制為不超過(guò)1。這意味著,如果活動(dòng)請(qǐng)求與最大并發(fā)請(qǐng)求數(shù)之比大于1,則查詢(xún)將返回值1。
通過(guò)這個(gè)查詢(xún),可以監(jiān)控系統(tǒng)飽和情況,并確定系統(tǒng)何時(shí)因請(qǐng)求而過(guò)載。如果活動(dòng)請(qǐng)求與最大并發(fā)請(qǐng)求數(shù)之比接近1,可能需要擴(kuò)容系統(tǒng)以處理增加的請(qǐng)求。此查詢(xún)可以幫助我們確保系統(tǒng)在高負(fù)載下仍可保持可靠和高性能。
為什么黃金信號(hào)很重要?
因?yàn)辄S金信號(hào)使SRE們可以清楚了解系統(tǒng)的運(yùn)行情況,因此非常重要。通過(guò)測(cè)量和監(jiān)控這些關(guān)鍵指標(biāo),SRE可以快速識(shí)別問(wèn)題,并在問(wèn)題變得嚴(yán)重之前采取糾正措施,即使這么做增加了系統(tǒng)復(fù)雜性,也可以有助于確保系統(tǒng)的可靠性、可伸縮性和高性能。
如何使用黃金信號(hào)來(lái)提高系統(tǒng)可靠性?
黃金信號(hào)可以通過(guò)幾種方式來(lái)提高系統(tǒng)可靠性:
- 主動(dòng)監(jiān)控(Proactive Monitoring) —— 通過(guò)持續(xù)監(jiān)控黃金信號(hào),SRE可以在問(wèn)題變得嚴(yán)重之前識(shí)別問(wèn)題,從而能夠采取主動(dòng)措施來(lái)防止停機(jī)或其他性能問(wèn)題。
- 容量規(guī)劃(Capacity Planning) —— 黃金信號(hào)可用于識(shí)別系統(tǒng)何時(shí)達(dá)到其容量限制。通過(guò)監(jiān)控流量和飽和度指標(biāo),SRE可以做出明智決定,決定何時(shí)升級(jí)或擴(kuò)容系統(tǒng)以滿(mǎn)足需求。
- 根因分析(Root Cause Analysis) —— 當(dāng)系統(tǒng)出現(xiàn)問(wèn)題時(shí),SRE可以使用黃金信號(hào)來(lái)幫助確定問(wèn)題的根本原因。通過(guò)查看延遲、流量、錯(cuò)誤和飽和度指標(biāo),SRE可以深入了解出了什么問(wèn)題,并采取措施防止將來(lái)發(fā)生類(lèi)似問(wèn)題。
了解如何在實(shí)踐中實(shí)現(xiàn)這些指標(biāo)也很重要。實(shí)現(xiàn)黃金信號(hào)的一種方法是使用內(nèi)置對(duì)其支持的監(jiān)控工具和庫(kù),比如Prometheus。在下面代碼示例中,Python FastAPI應(yīng)用程序通過(guò)Prometheus來(lái)實(shí)現(xiàn)黃金信號(hào)。
from fastapi import FastAPI, Request, HTTPException, Response
from prometheus_client import Counter, Gauge, Histogram, generate_latest, CONTENT_TYPE_LATEST
from starlette.responses import StreamingResponse
import time
app = FastAPI()
# Define Prometheus metrics
http_requests_total = Counter(
"http_requests_total",
"Total number of HTTP requests",
["method", "endpoint", "http_status"]
)
http_request_duration_seconds = Histogram(
"http_request_duration_seconds",
"HTTP request duration in seconds",
["method", "endpoint"]
)
http_request_size_bytes = Histogram(
"http_request_size_bytes",
"HTTP request size in bytes",
["method", "endpoint"]
)
http_response_size_bytes = Histogram(
"http_response_size_bytes",
"HTTP response size in bytes",
["method", "endpoint"]
)
active_requests = Gauge(
"active_requests",
"Number of active requests"
)
error_counter = Counter(
"error_counter",
"Total number of HTTP errors",
["method", "endpoint", "http_status"]
)
@app.middleware("http")
async def record_request_start_time(request: Request, call_next):
request.state.start_time = time.time()
response = await call_next(request)
return response
@app.middleware("http")
async def record_request_end_time(request: Request, call_next):
response = await call_next(request)
latency = time.time() - request.state.start_time
http_request_duration_seconds.labels(
request.method, request.url.path
).observe(latency)
http_request_size_bytes.labels(
request.method, request.url.path
).observe(request.headers.get("Content-Length", 0))
if isinstance(response, StreamingResponse):
response_size = 0
else:
response_size = len(response.content)
http_response_size_bytes.labels(
request.method, request.url.path
).observe(response_size)
http_requests_total.labels(
request.method, request.url.path, response.status_code
).inc()
return response
@app.middleware("http")
async def increment_counter(request: Request, call_next):
active_requests.inc()
response = await call_next(request)
active_requests.dec()
return response
@app.middleware("http")
async def log_saturation(request: Request, call_next):
max_concurrent_requests = 10 # set the maximum number of concurrent requests
saturation_ratio = active_requests._value._value / max_concurrent_requests
print(f"Saturation: {saturation_ratio}")
return await call_next(request)
@app.middleware("http")
async def increment_error_counter(request: Request, call_next):
try:
response = await call_next(request)
return response
except HTTPException as e:
error_counter.labels(
request.method, request.url.path, e.status_code
).inc()
print(f"Incremented error counter for {request.method} {request.url.path} {e.status_code}")
raise e
@app.get("/")
async def root():
return {"message": "Hello, World!"}
@app.get("/generate_traffic")
async def generate_traffic():
for i in range(100):
response = await root()
print(response)
return {"message": "Generated traffic successfully."}
@app.get("/generate_error")
async def generate_error():
raise HTTPException(status_code=500, detail="Generated an error.")
@app.get("/metrics")
async def metrics():
return Response(cnotallow=generate_latest(), media_type=CONTENT_TYPE_LATEST)
requirements.txt:
anyio==3.6.2
click==8.1.3
fastapi==0.92.0
h11==0.14.0
idna==3.4
prometheus-client==0.16.0
pydantic==1.10.5
sniffio==1.3.0
starlette==0.25.0
typing_extensinotallow==4.5.0
uvicorn==0.20.0
在K8S上部署
使用Prometheus在FastAPI應(yīng)用程序中實(shí)現(xiàn)了黃金信號(hào)后,可能希望將其部署到Kubernetes集群中,以確??缮炜s性和高可用性。下面的Kubernetes清單文件可以用來(lái)部署FastAPI應(yīng)用程序和Grafana儀表板:
fastapi-app.yaml
# @format
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-app
spec:
selector:
matchLabels:
app: fastapi-app
replicas: 2
template:
metadata:
labels:
app: fastapi-app
annotations:
prometheus.io/scrape: "true"
prometheus.io/path: "/"
prometheus.io/port: "80"
spec:
containers:
- name: fastapi-app
image: rtiwariops/fastapi-app:v1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: fastapi-app
spec:
selector:
app: fastapi-app
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
grafana.yaml
# @format
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana
spec:
selector:
matchLabels:
app: grafana
replicas: 1
template:
metadata:
labels:
app: grafana
spec:
containers:
- name: grafana
image: grafana/grafana:latest
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: grafana
spec:
selector:
app: grafana
ports:
- name: http
protocol: TCP
port: 3000
targetPort: 3000
prometheus.yaml
apiVersion: v1
kind: Service
metadata:
name: prometheus
spec:
selector:
app: prometheus
ports:
- name: web
port: 9090
targetPort: 9090
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
spec:
selector:
matchLabels:
app: prometheus
replicas: 1
template:
metadata:
labels:
app: prometheus
spec:
containers:
- name: prometheus
image: prom/prometheus:v2.28.1
ports:
- name: web
containerPort: 9090
command:
- "/bin/prometheus"
args:
- "--config.file=/etc/prometheus/prometheus.yml"
volumeMounts:
- name: config-volume
mountPath: /etc/prometheus
volumes:
- name: config-volume
configMap:
name: prometheus-config
總之,黃金信號(hào)是SRE工具箱中的關(guān)鍵工具。通過(guò)測(cè)量和監(jiān)控延遲、流量、錯(cuò)誤和飽和度指標(biāo),即使面對(duì)日益增加的復(fù)雜性和需求,SRE也可以確保其系統(tǒng)保持可靠、可擴(kuò)展和高性能。
完整代碼示例: https://github.com/PolyCloudNative/Golden-Rule-Demo
[1]Four Golden Signals Of Monitoring: Site Reliability Engineering (SRE) Metrics: https://umeey.medium.com/four-golden-signals-of-monitoring-site-reliability-engineering-sre-metrics-64031dbe268