站點可靠性工程SRE最佳實踐 -- 黃金監(jiān)控信號
黃金信號(Golden Signals)最初是谷歌在站點可靠性工程(SRE)實踐的背景下引入的,由谷歌軟件工程師Dave Rensin和Kevin Smathers在2016年O 'Reilly Velocity Conference上的一次演講中提出,其背后的想法是提供一組關鍵性能指標(KPI),用于測量和監(jiān)控復雜分布式系統的運行狀況。
引入黃金信號是為了幫助SRE團隊關注系統可靠性和性能方面真正重要的東西。黃金信號不依賴于難以解釋的大量指標和告警,而是提供一組簡單且易于理解的指標,用于快速評估系統健康狀況。
自從這一概念提出以來,黃金信號已在SRE社區(qū)中得到廣泛采用,并被認為是監(jiān)控和管理分布式系統運行狀況的最佳實踐。雖然最初黃金信號專注于延遲、流量、錯誤和飽和指標,但一些組織已經調整了這個概念,引入了特定于其系統和用例的附加指標。不過,通過一組KPI來度量和監(jiān)控系統健康的核心思想仍然是黃金信號概念的核心。
什么是黃金信號?
黃金信號是SRE用來衡量其系統健康狀況的一組四個關鍵指標,包括:
- 延遲(Latency) —— 延遲用來度量系統響應請求所需的時間,延遲高表明系統可能過載或遇到其他性能問題。
延遲
Prometheus查詢histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job="fastapi-app"}[5m])) by (le, method, endpoint))通過直方圖指標(histogram metric)度量來檢測FastAPI應用程序HTTP請求的P95延遲。
該查詢計算過去5分鐘內http_request_duration_seconds_bucket度量值(表示落入特定延遲桶的請求數量)的速率總和,并按延遲(le)、HTTP方法和端點分組。然后,histogram_quantile函數使用這些值計算每個HTTP方法和端點組合的P95延遲。
- 流量(Traffic) —— 流量衡量流經系統的數據或請求的數量,流量高表明系統可能正在處理大量請求,或者系統容量存在問題。
流量
Prometheus查詢rate(http_requests_total{job="fastapi-app"}[$__rate_interval])通過計數器指標(counter metric)度量FastAPI應用程序每秒HTTP請求的速率。
該查詢使用rate函數來計算http_requests_total計數器指標的每秒增長率,計算向FastAPI應用程序發(fā)出的HTTP請求總數。job="fastapi-app"標簽選擇器過濾度量數據,使其只包含來自FastAPI的數據。
$__rate_interval變量是模板變量,表示計算速率的持續(xù)時間,該變量值由用戶在Prometheus查詢界面中設置,用于確定計算速率的時間范圍。
例如,如果用戶將$__rate_interval設置為5m,查詢將計算過去5分鐘內HTTP請求的每秒速率。此查詢可用于監(jiān)控FastAPI應用程序的流量,并識別請求量隨時間變化的模式或異常情況。
- 錯誤(Errors) —— 錯誤度量系統中發(fā)生的錯誤數量,錯誤率高表明系統中可能存在bug或其他問題。
錯誤數
Prometheus查詢http_requests_total {endpoint="/generate_error", http_status="500"}檢索web應用程序的"/generate_error"端點的HTTP請求并且HTTP狀態(tài)碼為500(內部服務器錯誤)的數量。
該查詢使用http_requests_total計數器指標,計算向web應用程序發(fā)出的HTTP請求總數。查詢通過指定endpoint="/generate_error"標簽選擇器過濾度量數據,使其只包括對"/generate_error"端點的請求。此外,查詢通過指定http_status="500"標簽選擇器過濾數據,只包括HTTP狀態(tài)碼為500的請求。
通過運行這個查詢,可以深入了解web應用中錯誤發(fā)生率,以及哪些端點容易出錯。這些信息可以幫助識別和修復應用中的問題,提高可靠性,并確保為用戶提供更好的體驗。
- 飽和度(Saturation) —— 飽和度衡量系統的資源利用率,飽和度高表明系統資源(例如CPU或內存)可能正在被耗盡。
飽和度
Prometheus查詢clamp_max(active_requests{job="fastapi-app"} / 10, 1)用于計算活動請求與最大并發(fā)請求數的比率,并將該比率的值限制為不超過1。
該查詢使用active_requests度量(gauge)指標檢索FastAPI應用程序中的當前活動請求數。job="fastapi-app"標簽選擇器過濾度量數據,使其只包含來自FastAPI的數據。
然后,查詢將活動請求數除以10,表示系統可以處理的最大并發(fā)請求數。然后使用clamp_max函數將該比率的值限制為不超過1。這意味著,如果活動請求與最大并發(fā)請求數之比大于1,則查詢將返回值1。
通過這個查詢,可以監(jiān)控系統飽和情況,并確定系統何時因請求而過載。如果活動請求與最大并發(fā)請求數之比接近1,可能需要擴容系統以處理增加的請求。此查詢可以幫助我們確保系統在高負載下仍可保持可靠和高性能。
為什么黃金信號很重要?
因為黃金信號使SRE們可以清楚了解系統的運行情況,因此非常重要。通過測量和監(jiān)控這些關鍵指標,SRE可以快速識別問題,并在問題變得嚴重之前采取糾正措施,即使這么做增加了系統復雜性,也可以有助于確保系統的可靠性、可伸縮性和高性能。
如何使用黃金信號來提高系統可靠性?
黃金信號可以通過幾種方式來提高系統可靠性:
- 主動監(jiān)控(Proactive Monitoring) —— 通過持續(xù)監(jiān)控黃金信號,SRE可以在問題變得嚴重之前識別問題,從而能夠采取主動措施來防止停機或其他性能問題。
- 容量規(guī)劃(Capacity Planning) —— 黃金信號可用于識別系統何時達到其容量限制。通過監(jiān)控流量和飽和度指標,SRE可以做出明智決定,決定何時升級或擴容系統以滿足需求。
- 根因分析(Root Cause Analysis) —— 當系統出現問題時,SRE可以使用黃金信號來幫助確定問題的根本原因。通過查看延遲、流量、錯誤和飽和度指標,SRE可以深入了解出了什么問題,并采取措施防止將來發(fā)生類似問題。
了解如何在實踐中實現這些指標也很重要。實現黃金信號的一種方法是使用內置對其支持的監(jiān)控工具和庫,比如Prometheus。在下面代碼示例中,Python FastAPI應用程序通過Prometheus來實現黃金信號。
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應用程序中實現了黃金信號后,可能希望將其部署到Kubernetes集群中,以確??缮炜s性和高可用性。下面的Kubernetes清單文件可以用來部署FastAPI應用程序和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
總之,黃金信號是SRE工具箱中的關鍵工具。通過測量和監(jiān)控延遲、流量、錯誤和飽和度指標,即使面對日益增加的復雜性和需求,SRE也可以確保其系統保持可靠、可擴展和高性能。
完整代碼示例: 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