最近公司要求对所有的api服务进行系统性的监控,经过调研比较最后采用普罗米修斯(Prometheus)这款开源软件来做这件事,因此在网上经过学习,整理了这边普米的基础入门知识笔记。
一、什么是普罗米修斯(Prometheus)?
它是Go语言开发的一套开源的基于时序数据库的云原生监控告警系统,它和Kubernetes同来自于云原生计算基金会(CNCF)。它是借鉴Google监控系统BorgMon而于2015年实现的开源版本。整套系统大体框架为:
数据采集模块(exporter)、
数据服务端模块(Prometheus server)、
告警模块(Alertmanager)、
监控可视化模块(Prometheus web UI)。
二、特点
1. 数据结构为key和value的键值对;
2. 通过http的方式获取采集器的数据;
3. 专门为监控指标数据设计了高性能时序数据库TSDB;
4. 强大易用的查询语言PromQL以及丰富的聚合函数,可以完成非常复杂的查询与分析;
5. 开箱即用的各种服务发现机制(各种现成的开源Exporter组件实现,实现自定义的监控指标也非常简单),支持静态配置与自动发现监控端点;
6. 可以配置灵活的告警规则,支持告警收敛(分组、抑制、静默)、多级路由等等高级功能;
7. 有自己的可视化界面也可以可接入第三方Grafana可视化系统
三、工作流程
1.启动与配置加载
Prometheus server启动时,会读取prometheus.yml配置文件,该文件包含全局配置、告警设置、规则文件和监控目标定义。配置文件主要分为四个部分:
•global:定义全局参数如scrape_interval(默认15秒)和evaluation_interval(规则评估间隔)。
•alerting:配置与Alertmanager的通信。
•rule_files:指定告警和记录规则的路径。
•scrape_configs:定义监控目标(静态或动态发现)。
2.服务发现与目标抓取
•静态配置:通过static_configs直接指定目标地址(如targets: ['localhost:9090'])。
•动态发现:支持多种服务发现机制(如DNS、Kubernetes、Consul等),通过file_sd_configs或kubernetes_sd_configs等配置动态更新目标列表。
•抓取过程:根据scrape_interval定期从目标端的/metrics接口拉取指标数据(HTTP协议),超时时间由scrape_timeout控制(默认10秒)。
3.数据处理与存储
• 抓取的指标数据会存入内置的时序数据库(TSDB),并通过remote_write支持远程存储。
• 根据evaluation_interval定期执行告警规则,触发告警时会推送至Alertmanager。
4.扩展与集成
• 可通过exporter(如node_exporter)扩展监控能力,exporter运行在被监控端并暴露指标接口。
• 结合Grafana可视化数据,或通过API对接其他系统。
四、架构图:
Prometheus Server: 用数据的采集和存储,PromQL查询,报警配置。
Push gateway:用于批量,短期的监控数据的汇报总节点。
Exporters: 各种汇报数据的exporter,例如汇报机器数据的node_exporter,汇报MondogDB信息的 MongoDB_exporter 等等。
Alertmanager: 用于高级通知管理。
Prometheus通过PromQL、API、Console和其他可视化组件如Grafana、Promdash展示数据,也可以使用自身的Prometheus UI,不过用的最多的是Grafana。
五、数据结构和指标5.1 数据结构
Prometheus以时间序列的方式采集的监控数据(即样本),并保存在内置时间序列数据库(TSDB)中。
样本,是时间序列中某一个时间点的序列值。包括以下三部分:
• 指标(metric):指标名称和描述当前样本特征的 labelsets
• 时间戳(timestamp):一个精确到毫秒的时间戳
• 样本值(value):一个 folat64 的浮点型数据表示当前样本的值。
表现形式为:
<metric name>{<label name>=<label value>,<labe2 name>=<labe2 value>, ...},
例如:
request_count_total{appid="标识1",application="标识2"}
prometheus的指标共有四种类型:Counter、Gauge、Histogram和Summary。
5.2 指标1. Counter(计数器)
用于描述某个指标的单调累计状态,因此它的值只能上升,重启归0。Counter的实际值通常用处不大。一般是用来计算两个时间戳之间的delta或者随时间变化的速率。
例如:利用Counter的一个典型用例是记录API调用次数:
# HELP http_requests_total Total number of http api requests
# TYPE http_requests_total counter
http_requests_total{appid="xxx",application="yyyy",api="api01"} 4633433
上面的内容解释:
指标名称为http_requests_total,有三个标签(appid,application、api),Counter的值为4633433。即自从上次服务启动或Counter重置以来,api01的API已经被调用了4633433次。
可视化:
与PromQL的rate函数一起使用,可计算API每秒收到的请求数:
rate(http_requests_total{appid="xxx",application="yyyy",api="api01"}[5m]) # 计算过去5分钟内每秒的平均请求数
使用increate函数(delta),计算一段时间内的绝对值变化:
increase(http_requests_total{appid="xxx",application="yyyy",api="api01"}[5m]) # 计算5分钟内的请求总数2. Gauge(仪表盘)
用于描述某个指标当前的状态,如CPU和内存等使用指标,或者队列的大小都是Gauge。
例如:利用Gauge统计机器的内存使用情况
# HELP node_memory_used_bytes Total memory used in the node in bytes
# TYPE node_memory_used_bytes gauge
node_memory_used_bytes{appid="xxx",application="yyyy",hostname="host1.domain.com"} 943348382
上面的内容解释:
表示节点host1.domain.com使用的内存约为900 MB。
可视化:
Gauge使用rate和delta函数没有意义,但可以计算特定时间序列的平均数、最大值、最小值或百分比。在Prometheus中,这些函数的名称是avg_over_time、max_over_time、min_over_time和quantile_over_time。如要计算过去10分钟内在host1.domain.com上使用的平均内存,可以这样做:
avg_over_time(node_memory_used_bytes{hostname="host1.domain.com"}[10m])3.Histogram(直方图)
Histogram指标对于表示测量的分布很有用。它们经常被用来测量请求持续时间或响应大小。Histogram将整个测量范围划分为一组区间,称为桶,并计算每个桶中有多少测量值。
一个直方图指标包括几个项目:
• 一个包含测量次数的Counter。指标名称使用_count后缀。
• 一个包含所有测量值之和的Counter。指标名称使用_sum后缀。
• 直方图桶被暴露为一系列的Counter,使用指标名称的后缀_bucket和表示桶的上限的le标签。
Prometheus中的桶是包含桶的边界的,包括所有数值小于或等于le的数据点。
例如,测量运行在host1.domain.com实例上的api01端点实例的响应时间。
# HELP http_request_duration_seconds Api requests response time in seconds
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_sum{appid="xxx",application="yyyy",api="api01", instance="host1.domain.com"} 8953.332
http_request_duration_seconds_count{appid="xxx",application="yyyy",api="api01",api="add_product" instance="host1.domain.com"} 27892
http_request_duration_seconds_bucket{appid="xxx",application="yyyy",api="api01",api="add_product" instance="host1.domain.com" le="0"}
http_request_duration_seconds_bucket{appid="xxx",application="yyyy",api="api01",api="add_product", instance="host1.domain.com", le="0.01"} 0
.....
上面的内容解释:
统计包括sum、counter和12个桶。sum和counter可以用来计算一个测量值随时间变化的平均值。
可视化:
在PromQL中,计算过去5分钟的平均请求响应时间:
rate(http_request_duration_seconds_sum{appid="xxx",application="yyyy",api="api01", instance="host1.domain.com"}[5m]) / rate(http_request_duration_seconds_count{appid="xxx",application="yyyy",api="api01", instance="host1.domain.com"}[5m])
计算各时间序列的平均数,如计算出所有API和实例在过去5分钟内的平均请求响应时间:
sum(rate(http_request_duration_seconds_sum[5m])) / sum(rate(http_request_duration_seconds_count[5m]))
计算单个时间序列以及多个时间序列的百分位,如计算在host1.domain.com上运行的api01响应时间的第99百分位数:
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket{appid="xxx",application="yyyy",api="api01", instance="host1.domain.com"}[5m]))
进行汇总,如返回所有API和实例的响应时间的第99个百分点:
histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))
Histograms有三个主要的缺点:
• Histograms必须是预定义的,如果桶没有被很好地定义,可能无法计算出需要的百分比,或者会消耗不必要的资源。例如,如果有一个总是需要超过一秒钟的API,那么拥有上限(le标签)小于一秒钟的桶将是无用的,只会消耗监控后端服务器的计算和存储资源。另一方面,如果99.9%的API请求耗时少于50毫秒,那么拥有一个上限为100毫秒的初始桶将无法让你准确测量API的性能。
• Histograms提供的是近似的百分位数,而不是精确的百分位数。
• 由于百分位数需要在服务器端计算,当有大量数据需要处理时,它们的计算成本会非常高。减轻这种情况的一个方法是使用录制规则来预先计算所需的百分位数。
4. Summary(摘要)
和histogram类似,summary指标对于测量请求持续时间和响应大小很有用。一个Summary指标包括以下指标:
• 一个包含总测量次数的Counter。指标名使用_count后缀。
• 一个包含所有测量值之和的Counter。指标名称使用_sum后缀。可以选择使用带有分位数标签的指标名称,来暴露一些测量值的分位数指标。
例如,测量在host1.domain.com上运行的api01端点实例的响应时间的Summary指标可以表示为:
# HELP http_request_duration_seconds Api requests response time in seconds
# TYPE http_request_duration_seconds summary
http_request_duration_seconds_sum{appid="xxx",application="yyyy",api="api01", , instance="host1.domain.com"} 8953.332
http_request_duration_seconds_count{appid="xxx",application="yyyy",api="api01", instance="host1.domain.com"} 27892
http_request_duration_seconds{appid="xxx",application="yyyy",api="api01", instance="host1.domain.com" quantile="0"}
http_request_duration_seconds{appid="xxx",application="yyyy",api="api01", instance="host1.domain.com" quantile="0.5"} 0.232227334
http_request_duration_secondsappid="xxx",application="yyyy",api="api01", instance="host1.domain.com" quantile="0.90"} 0.821139321
http_request_duration_seconds{appid="xxx",application="yyyy",api="api01", instance="host1.domain.com" quantile="0.95"} 1.528948804
http_request_duration_seconds{appid="xxx",application="yyyy",api="api01", instance="host1.domain.com" quantile="0.99"} 2.829188272
http_request_duration_secondsappid="xxx",application="yyyy",api="api01", instance="host1.domain.com" quantile="1"} 34.283829292
上面的内容解释:
上面这个例子包括总和和计数以及五个分位数。分位数0相当于最小值,分位数1相当于最大值。
像Histogram一样,Summary指标包括总和和计数,可用于计算随时间的平均值以及不同时间序列的平均值。Summary提供了比Histogram更精确的百分位计算结果,但这些百分位有三个主要缺点:
• 客户端计算百分位是很昂贵的,但并非所有的Prometheus客户端库都支持汇总指标中的量值,例如,Python SDK就不支持。
• 要查询的量值必须由客户端预先定义。只有那些已经提供了指标的量值才能通过查询返回。没有办法在查询时计算其他百分位。增加一个新的百分位指标需要修改代码,该指标才可以被使用。
• 不可能把多个Summary指标进行聚合计算。
Histogram与Summary对比分析核心区别
六、代码引入方式1. Flask集成Prometheus
1. 方式一:通过插件prometheus-flask-exporter
pip install prometheus-flask-exporter
快速使用:
from flask import Flask
from prometheus_flask_exporter import PrometheusMetrics
app = Flask(__name__)
metrics = PrometheusMetrics(app) # 自动暴露 `/metrics` 端点
@app.route('/')
defhello():
return"Hello, Prometheus!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
"""
启动项目后,访问 http://127.0.0.1:5000/metrics 即可获取默认的Flask监控指标(默认监控:请求耗时、请求总数、异常计数等基础HTTP指标)
"""
过滤掉默认的http监控
@app.route('/skip')
@metrics.do_not_track()
def skip():
"""通过do_not_track()来排除某个接口插件默认的监控指标"""
pass
自定义指标并且过滤掉插件自带的监控
@app.route('/status/<int:status>')
@metrics.do_not_track()
@metrics.summary('requests_by_status', 'Request latencies by status',
labels={'status': lambda r: r.status_code}) # 自定义记录按状态码分组的请求延迟分布,标签status从响应对象动态获取状态码
@metrics.histogram('requests_by_status_and_path', 'Request latencies by status and path',
labels={'status': lambda r: r.status_code,
'path': lambda: request.path}) # 自定义记录按状态码和路径分组的请求延迟直方图,标签path从请求对象动态获取路径
def echo_status(status):
"""通过do_not_track不记录默认的http指标,采用自己自定义的summary,histogram配置的指标"""
return 'Status: %s' % status, status
2. 方式二:通过prometheus_client
pip install prometheus_client
2.1 为所有接口配置通用指标配置文件monitoring_util.py:
import os
import shutil
import platform
from prometheus_client import Counter
from prometheus_client import Histogram
from prometheus_client import multiprocess
from prometheus_client import CollectorRegistry
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 临时数据保存目录-在上云时需同步配置临时卷,将此目录从容器中挂载出来以便所有进程能操作该目录
prometheus_dir = os.path.join(BASE_DIR, "prometheus_data") if platform.system() == 'Windows'else"/tmp/prometheus_data"
# 多进程必须配置
os.environ["PROMETHEUS_MULTIPROC_DIR"] = prometheus_dir
# 启动前清理残留数据
if os.path.exists(prometheus_dir):
shutil.rmtree(prometheus_dir)
os.makedirs(prometheus_dir, mode=0o777, exist_ok=True)
prometheus_registry = CollectorRegistry()
# 定义 直方图:统计接口耗时-毫秒(通过接口编码method_code区分)
api_request_duration = Histogram(name='http_request_duration_seconds',
documentation='请求耗时',
labelnames=['method_code', 'response_code', 'path', 'appid', 'application'],
# 普罗米修斯默认以秒为单位
buckets=[0.15, 0.25, 0.3, 1, 2, 3],
registry=prometheus_registry)
# 定义 计数器:统计接口调用掉及响应状态码(通过接口编码method_code区分)
api_request_count = Counter('http_request_total_count', '请求总量',
labelnames=['method_code', 'response_code', 'path', 'appid', 'application'],
registry=prometheus_registry)
defcleanup_worker():
"""Worker 退出时调用"""
multiprocess.mark_process_dead(os.getpid())
2.2 通过中间件装饰器before_request和after_request获取请求与响应数据
from flask import Flask
from flask import request
from Utils.monitoring_util import api_request_count
from Utils.monitoring_util import api_request_duration
from Utils.monitoring_util import prometheus_registry
from prometheus_client import generate_latest
from prometheus_client import multiprocess
app = Flask(__name__)
@app.before_request
deftrack_request_start():
request.method_code = "Unknown"
request.start_time = time.time()
request.res_code = "500"
@app.after_request
deftrack_request_end(response):
tracking_excludes = {
"/prometheus/metrics", # Prometheus
"/static/", # 静态文件
"/favicon.ico",
"/health_check"
}
ifany(request.path.startswith(exclude) for exclude in tracking_excludes):
return response
duration = time.time() - request.start_time
labels = {
"path": request.path,
"method_code": request.method_code,
"response_code": request.res_code,
"appid": "xxxx",
"application": PLATFORM_CODE.lower()
}
api_request_duration.labels(**labels).observe(duration)
api_request_count.labels(**labels).inc()
return response
@app.route('/')
defhello():
return"Hello, Prometheus!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
2.3 通过访问http://127.0.0.1:5000/prometheus/metrics获取各个api的指标
关键特性对比
领取专属 10元无门槛券
私享最新 技术干货