站点可靠性工程 SRE 最佳实践 -- 黄金监控信号

本文涉及的产品
可观测可视化 Grafana 版,10个用户账号 1个月
可观测监控 Prometheus 版,每月50GB免费额度
简介: 站点可靠性工程 SRE 最佳实践 -- 黄金监控信号

延迟、流量、错误率、饱和度这四大黄金信号是 SRE 的最佳实践,可用来帮助 SRE 团队快速评估系统状态,在异常情况下及时介入,保证系统始终工作在健康状态。原文: Four Golden Signals Of Monitoring: Site Reliability Engineering (SRE) Metrics


黄金信号(Golden Signals)最初是谷歌在站点可靠性工程(SRE)实践的背景下引入的,由谷歌软件工程师 Dave Rensin 和 Kevin Smathers 在 2016 年 O 'Reilly Velocity Conference 上的一次演讲中提出,其背后的想法是提供一组关键性能指标(KPI),用于测量和监控复杂分布式系统的运行状况。


引入黄金信号是为了帮助 SRE 团队关注系统可靠性和性能方面真正重要的东西。黄金信号不依赖于难以解释的大量指标和告警,而是提供一组简单且易于理解的指标,用于快速评估系统健康状况。


自从这一概念提出以来,黄金信号已在 SRE 社区中得到广泛采用,并被认为是监控和管理分布式系统运行状况的最佳实践。虽然最初黄金信号专注于延迟、流量、错误和饱和指标,但一些组织已经调整了这个概念,引入了特定于其系统和用例的附加指标。不过,通过一组 KPI 来度量和监控系统健康的核心思想仍然是黄金信号概念的核心。

什么是黄金信号?

黄金信号是 SRE 用来衡量其系统健康状况的一组四个关键指标,包括:


  1. 延迟(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 延迟。


  1. 流量(Traffic) —— 流量衡量流经系统的数据或请求的数量,流量高表明系统可能正在处理大量请求,或者系统容量存在问题。


流量


Prometheus 查询rate(http_requests_total{job="fastapi-app"}[$__rate_interval])通过计数器指标(counter metric)度量 FastAPI 应用程序每秒 HTTP 请求的速率。


该查询使用rate函数来计算http_requests_total计数器指标的每秒增长率,计算向 FastAPI 应用程序发出的 HTTP 请求总数。job="fastapi-app"标签选择器过滤度量数据,使其只包含来自 FastAPI 的数据。


$__rate_interval变量是模板变量,表示计算速率的持续时间,该变量值由用户在 Prometheus 查询界面中设置,用于确定计算速率的时间范围。


例如,如果用户将$__rate_interval设置为5m,查询将计算过去 5 分钟内 HTTP 请求的每秒速率。此查询可用于监控 FastAPI 应用程序的流量,并识别请求量随时间变化的模式或异常情况。


  1. 错误(Errors) —— 错误度量系统中发生的错误数量,错误率高表明系统中可能存在 bug 或其他问题。


错误数


Prometheus 查询http_requests_total {endpoint="/generate_error", http_status="500"}检索 web 应用程序的"/generate_error"端点的 HTTP 请求并且 HTTP 状态码为 500(内部服务器错误)的数量。


该查询使用http_requests_total计数器指标,计算向 web 应用程序发出的 HTTP 请求总数。查询通过指定endpoint="/generate_error"标签选择器过滤度量数据,使其只包括对"/generate_error"端点的请求。此外,查询通过指定http_status="500"标签选择器过滤数据,只包括 HTTP 状态码为 500 的请求。


通过运行这个查询,可以深入了解 web 应用中错误发生率,以及哪些端点容易出错。这些信息可以帮助识别和修复应用中的问题,提高可靠性,并确保为用户提供更好的体验。


  1. 饱和度(Saturation) —— 饱和度衡量系统的资源利用率,饱和度高表明系统资源(例如 CPU 或内存)可能正在被耗尽。


饱和度


Prometheus 查询clamp_max(active_requests{job="fastapi-app"} / 10, 1)用于计算活动请求与最大并发请求数的比率,并将该比率的值限制为不超过 1。


该查询使用active_requests度量(gauge)指标检索 FastAPI 应用程序中的当前活动请求数。job="fastapi-app"标签选择器过滤度量数据,使其只包含来自 FastAPI 的数据。


然后,查询将活动请求数除以 10,表示系统可以处理的最大并发请求数。然后使用clamp_max函数将该比率的值限制为不超过 1。这意味着,如果活动请求与最大并发请求数之比大于 1,则查询将返回值 1。


通过这个查询,可以监控系统饱和情况,并确定系统何时因请求而过载。如果活动请求与最大并发请求数之比接近 1,可能需要扩容系统以处理增加的请求。此查询可以帮助我们确保系统在高负载下仍可保持可靠和高性能。

为什么黄金信号很重要?

因为黄金信号使 SRE 们可以清楚了解系统的运行情况,因此非常重要。通过测量和监控这些关键指标,SRE 可以快速识别问题,并在问题变得严重之前采取纠正措施,即使这么做增加了系统复杂性,也可以有助于确保系统的可靠性、可伸缩性和高性能。

如何使用黄金信号来提高系统可靠性?

黄金信号可以通过几种方式来提高系统可靠性:


  1. 主动监控(Proactive Monitoring) —— 通过持续监控黄金信号,SRE 可以在问题变得严重之前识别问题,从而能够采取主动措施来防止停机或其他性能问题。
  2. 容量规划(Capacity Planning) —— 黄金信号可用于识别系统何时达到其容量限制。通过监控流量和饱和度指标,SRE 可以做出明智决定,决定何时升级或扩容系统以满足需求。
  3. 根因分析(Root Cause Analysis) —— 当系统出现问题时,SRE 可以使用黄金信号来帮助确定问题的根本原因。通过查看延迟、流量、错误和饱和度指标,SRE 可以深入了解出了什么问题,并采取措施防止将来发生类似问题。


了解如何在实践中实现这些指标也很重要。实现黄金信号的一种方法是使用内置对其支持的监控工具和库,比如 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(content=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_extensions==4.5.0
uvicorn==0.20.0

复制代码

在 K8S 上部署

使用 Prometheus 在 FastAPI 应用程序中实现了黄金信号后,可能希望将其部署到 Kubernetes 集群中,以确保可伸缩性和高可用性。下面的 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 工具箱中的关键工具。通过测量和监控延迟、流量、错误和饱和度指标,即使面对日益增加的复杂性和需求,SRE 也可以确保其系统保持可靠、可扩展和高性能。


完整代码示例: https://github.com/PolyCloudNative/Golden-Rule-Demo




你好,我是俞凡,在 Motorola 做过研发,现在在 Mavenir 做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI 等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。

微信公众号:DeepNoMind

相关实践学习
通过可观测可视化Grafana版进行数据可视化展示与分析
使用可观测可视化Grafana版进行数据可视化展示与分析。
目录
相关文章
|
3月前
|
数据采集 监控 测试技术
大型IM稳定性监测实践:手Q客户端性能防劣化系统的建设之路
本文以iOS端为例,详细分享了手 Q 客户端性能防劣化系统从0到1的构建之路,相信对业界和IM开发者们都有较高的借鉴意义。
129 2
|
3月前
|
测试技术 持续交付 开发者
持续部署的内涵和实施路径问题之质量内建对持续部署有何重要性
持续部署的内涵和实施路径问题之质量内建对持续部署有何重要性
|
3月前
|
监控 Kubernetes 持续交付
持续部署的内涵和实施路径问题之确保持续部署的准确性和可预期性的问题如何解决
持续部署的内涵和实施路径问题之确保持续部署的准确性和可预期性的问题如何解决
|
3月前
|
物联网 测试技术 持续交付
持续部署的内涵和实施路径问题之持续部署过程中需要控制过程成本并保持高效的问题如何解决
持续部署的内涵和实施路径问题之持续部署过程中需要控制过程成本并保持高效的问题如何解决
|
3月前
|
测试技术 编译器 持续交付
持续部署的内涵和实施路径问题之集成尽早进行每次集成很小的问题如何解决
持续部署的内涵和实施路径问题之集成尽早进行每次集成很小的问题如何解决
|
6月前
|
应用服务中间件 中间件 缓存
Web应急响应0基础讲解国赛信安管理与评估二阶段
Web应急响应0基础讲解国赛信安管理与评估二阶段
Web应急响应0基础讲解国赛信安管理与评估二阶段
|
6月前
|
消息中间件 Cloud Native Java
项目环境稳定性指标建设之路
这篇文章讨论了项目环境在集团研发中的重要性,它是一个灵活的平台工具,用于支持联调测试和不同阶段的环境隔离。早期的项目环境管理存在任务重复运行、单机处理瓶颈和任务猝死等问题。为了解决这些问题,文章介绍了通过引入领域驱动设计(DDD)来重构流程引擎,创建了统一的异常处理和任务执行接口,增强了异常处理能力,并通过分布式分片任务、工厂模式和责任链模式实现了任务的分布式运行。此外,还使用分布式锁解决了多机忙等和任务重复执行的问题,提高了任务执行效率。优化后,环境创建成功率提升至99%以上,创建时间降低至100秒以下,系统异常率低于1%,并且能够应对更高的并发量。
|
机器人
量化交易丨交易所系统开发策略规则/逻辑方案/成熟技术/开发案例/源码部署
  “量化交易”有两层含义,一种是从狭义上的讲法,中指量化交易的内容,将交易的条件转变为程序的意思,自动下单。二是从广义上讲,是指系统交易的方法,一个整合交易的系统,按照一系列的交易条件,智能化的辅助决策系统体系,把丰富的从业经验与交易条件相符合,交易过程管理好风险控制。
|
新零售 搜索推荐 数据挖掘
短剧CPS系统开发规则详细/案例项目/成熟技术/源码方案
开发新零售是指利用科技和创新的方法,开发和构建与新零售概念相符的电子商务系统、应用和平台等,以满足消费者的个性化需求并提供全渠道的零售体验。
|
API Python
  SleeFi睡眠链游开发部署规则【分析】
最近出来的边睡边赚(Sleep to Earn),其实是类似gamefi概念的变体,Sleep to Earn让玩家用户透过睡觉作为玩游戏的方式,或完成游戏内任务的替代方式来赚钱。