APM
APM ( Application Performance Management ) 是应用性能管理的缩写,代表了服务的行为、可靠性、性能等的监控和管理。当应用发生故障,利用 APM 设施可以迅速检测并定位到产生故障的原因。
如今的后端服务大都是分布式、微服务化的,做好 APM 尤为重要。APM 包括许多方面工作,包括链路追踪(Tracing)、日志(Logging) 和 指标(Metrics)监控与报警 等,对应的开源基础设施也有很多,如 Jaeger/Zipkin (链路跟踪)、ELK (日志)、Prometheus/Zabbix/InfluxDB (指标监控系统)、Grafana (监控展示前端) 等。
今天来说说什么是指标监控,以及总结一下最近学习 Prometheus 的心得,梳理一下知识网络。
Metrics 指标和 Prometheus
我们通常在应用程序中产生日志,日志往往代表的是一个应用或请求生命周期中的一个事件,它是离散的数据。而指标往往代表着应用程序中可量化可聚合分析的数据,例如服务接收的请求量,请求量是一个不断增加的数据,我们可以根据它和时间的对应关系,计算出请求的增长率;再比如,服务的请求延迟,我们可以收集所有的延迟时长,统计出不同的分位数,以衡量服务的整体延迟表现;再比如消息队列系统的任务堆积情况、服务的内存占用、主机的 CPU 占用率等,这些是一个个上下浮动的数值,可以反应一些实时状况,并且通过历史时间的数据,反应过去一段时间的数据波动情况,以预测未来变化。
通过 Prometheus 我们就可以进行这些指标数据的统计、收集、查询/聚合分析、可视化展示、非正常指标的报警等工作。可以把 Prometheus 理解为一个指标收集器与时序数据库 (TSDB) 的集合。
Prometheus 架构
Prometheus 采用 PULL 的模式定期从应用程序中拉取指标数据。应用程序目标地址可以以静态配置的方式提供,也可以配置服务注册中心,动态的方式获取目标地址,见 文档。
组件
- Exporter 采集指标数据,为 Prometheus server 提供采集接口。Exporter 可以集成在业务代码中,也可以是一个单独的进程服务,来采集其他的服务指标。
- Pushgateway 是一个中间服务,短生命周期的 job 上传指标到 Pushgateway,server 从 Pushgateway 采集指标。
- TSDB 是 Prometheus server 内置的时序数据库,用来存储采集的指标数据,并且内置了一套用以查询数据的语法 PromQL。
- Alertmanager 是独立于 Prometheus server 的一个服务,用来处理报警。Prometheus server 只负责评估报警规则是否被触发,后续的报警通知等工作交由 Alertmanager 处理。
部署
本地使用 Docker Compose 进行部署。
version: "3"
volumes:
prometheus_config:
prometheus_data:
alertmanager_config:
grafana_config:
grafana_data:
services:
prometheus:
image: prom/prometheus:v2.20.1
container_name: prometheus
hostname: prometheus-host
ports:
- 9090:9090
command:
- --config.file=/etc/prometheus/prometheus.yml # 指定配置文件
- --web.enable-lifecycle # 运行通过 HTTP 接口重新加载配置 (POST localhost:9090/-/reload)
volumes:
- prometheus_config:/etc/prometheus # 配置
- prometheus_data:/prometheus # TSDB、WAL 等数据
depends_on:
- alertmanager
alertmanager:
image: prom/alertmanager:v0.21.0
container_name: alertmanager
hostname: alertmanager-host
ports:
- 9093:9093
volumes:
- alertmanager_config:/etc/alertmanager # 配置
# user: admin, psw: admin
grafana:
image: grafana/grafana:7.1.5
container_name: grafana
hostname: grafana-host
ports:
- 3000:3000
depends_on:
- prometheus
volumes:
- grafana_config:/etc/grafana # 配置
- grafana_data:/var/lib/grafana # DB、Plugins 等数据
样本、时间序列和指标类型
^
│ . . . . . . . . . . . . . . . . . . .
│ . . . . . . . . . . . . . . . . . . .
│ . . . . . . . . . . . . . . . . . . .
│ . . . . . . . . . . . . . . . . . . .
v
<------------------ 时间 ---------------->
样本
Prometheus TSDB 存储的基本单位是样本,可以理解为上面坐标中的一个点,横轴代表样本时间,数轴代表样本值。而样本本身又由一系列标签唯一标识,故样本由以下三部分组成:
- 指标:包含指标名 (指标名称其实也是一个标签) 和描述当前样本特征的标签集 ( labelsets )。
- 时间戳:一个精确到毫秒的时间戳。
- 样本值: 一个 float64 的浮点型数据表示当前样本的值。
时间序列
同一指标(指标名和标签集都相同的所有样本)在上面坐标中所构成的一条连续的线,成为一条时间序列。它代表了一个指标在时间范围内的数值波动。
指标类型
为了区分不同类型的指标,Prometheus 抽象出四种类型的指标。
- Counter 代表一个只增不减的计数器指标,例如,服务接收的请求总量。
- Gauge 代表一个可增可减的计量器指标,例如,服务内存占用。
上述两种指标都是连续的指标,基于原来的数值进行增减,而下面两种指标属于统计类指标。
- Histogram 意为直方图,它定义了不同的数值范围(桶),然后将收集到的数据纳入桶内进行计数统计,能够得到不同数值范围之间的比例。
- Summary 定义了不同的分位点,然后将收集到的数据进行大小排序,最终得到不同分位点对应的数值 (分位数)。
Prometheus server 同时也包含一个 metrics 接口,供自身进行指标采集。在部署之后可以通过 http://your-prometheus-host:9090/metrics 查看到不同指标类型的样本。
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 2.86e-05
go_gc_duration_seconds{quantile="0.25"} 0.0001114
go_gc_duration_seconds{quantile="0.5"} 0.0001812
go_gc_duration_seconds{quantile="0.75"} 0.0003278
go_gc_duration_seconds{quantile="1"} 0.0038698
go_gc_duration_seconds_sum 0.0531145
go_gc_duration_seconds_count 210
上面这部分是 Go 垃圾回收停顿时间的 summary 类型样本,从数据中可以得到如下信息:
- 总共进行了 210 次垃圾回收。
- 垃圾回收总耗时 0.0531145。
- 有一半次数的回收耗时是低于 0.0001812 的。
- ...
各种指标类型和样本含义不一一说明了,可以细读 文档。
PromQL
PromQL 是 Prometheus 的查询语言,可以通过 PromQL 查询单条或多条时间序列,也可以通过 PromQL 内置函数和操作符查询聚合后的数据。
可在 http://your-prometheus-host:9090/new/graph 中输入 PromQL 进行查询。
用法这里不再赘述,请参考 文档。
业务中集成 Exporter
我们模拟一个任务队列系统,使用 Go 来编写,用 Channel 来保存任务,利用生产者-消费者模型来进行任务的生产和消费。在生产者每次向队列 Push 任务时,收集队列的堆积的任务数,这是一个 Gauge 类型指标;在消费者处理任务时,统计完成的任务数,这是一个 Counter 指标,同时收集任务的完成时间以统计任务耗时的 Histogram 和 Summary。然后再暴露一个 HTTP 接口提供最新的指标样本数据。
代码在:https://github.com/xvrzhao/examples/tree/master/prometheus
假若该程序是跑在宿主机的,但是我们 Prometheus server 是跑在容器里的,server 要请求宿主机的端口,可以通过主机名 host.docker.internal
与宿主机通信。
可以修改容器里的配置文件 /etc/prometheus/prometheus.yml
,增加如下配置:
scrape_configs:
- job_name: 'task_queue'
static_configs:
- targets: ['host.docker.internal:9000']
修改配置后,需要让进程重新加载配置,可以执行如下两种方法:
$ curl -X POST ${Prometheus服务地址}/-/reload
# 或者
$ kill -s HUP ${Prometheus进程PID}
接着就可以在 Prometheus 的 WebUI 查看指标了:
http://your-prometheus-host:9090/new/graph?g0.expr=remain_tasks_in_queue&g0.tab=0&g0.stacked=0&g0.range_input=5m
报警
报警需要 Prometheus server 和 Alertmanager 配合完成,Prometheus server 只负责评估报警规则条件,如果符合条件则将报警发送给 Alertmanager。
Prometheus server 每次评估时,处于 firing 状态的警报都会发送给 Alertmanager,尽管同一条警报已经发送过一次。所以在 Alertmanager 要做好警报的分组、延迟合并、抑制和静默。关于这些内容,这篇文章讲解 得非常好,这里不再赘述。
对于上面的任务队列系统,我们可以这样配置:
/etc/prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets:
- "alertmanager:9093"
rule_files:
- "/etc/prometheus/rules.yml"
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'task_queue'
static_configs:
- targets: ['host.docker.internal:9000']
/etc/prometheus/rules.yml
groups:
- name: task_queue
rules:
- alert: TooManyRemainingTasks
expr: remain_tasks_in_queue > 30
for: 1m
labels:
severity: warning
annotations:
summary: "Too many remaining tasks."
description: "Current remaining tasks: {{ $value }}"
/etc/alertmanager/alertmanager.yml
global:
resolve_timeout: 5m
smtp_smarthost: 'smtp.qq.com:465'
smtp_from: 'example@qq.com'
smtp_auth_username: 'example@qq.com'
smtp_auth_password: 'secret'
smtp_require_tls: false
route:
group_by: ['instance']
group_wait: 1m
group_interval: 5m
repeat_interval: 1h
receiver: 'myemail'
receivers:
- name: 'myemail'
email_configs:
- to: xvrzhao@gmail.com
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。