Blog of Samperson

Prometheus 调研

2020-02-27

pre和record记录: media

特点

  • 多维时间序列数据模型——用度量名metric name键值对识别
  • 查询语言: PromQL
  • 不依赖分布式存储; 单服务器节点是自治的
  • 通过HTTP上的pull: 收集时间序列
  • 通过中间网关: 支持push时间序列
  • 通过service discovery(服务发现)或 static configuration(静态配置)发现目标
    配置方式包含多种,可以直接在写在yaml文件中,但如果配置较长也可以写入其他文件并启用文件发现(file_sd)功能让其自行侦听配置文件变化,甚至可以使用consul或者kubernetes这样的服务发现来动态更新配置以适应频繁的节点变更
  • 支持多种图形和仪表板

系统组件

  • server: 收集和存储时间序列数据
  • client libraries: 检测应用程序代码
  • push gateway:批量, 短期的监控数据的汇总节点, 主要用于业务数据汇报
  • exporters: 例如汇报机器数据的 node_exporter, 汇报 MongoDB 信息的 MongoDB exporter
  • alertmanager: 处理警报

基础架构

[1] 逻辑

  1. Prometheus server 定期从静态配置的 targets 或者服务发现的 targets 拉取数据, 直接通过短生命作业的push gateway, 从检测作业中获取度量
  2. 当新拉取的数据大于配置内存缓存区的时候, Prometheus 会将数据持久化到磁盘 / 云端
  3. Prometheus 可以配置规则, 然后定时查询数据, 当条件触发的时候, 会将 alert 推送到配置的 Alertmanager
  4. Alertmanager 收到警告的时候, 可以根据配置, 聚合、去重、降噪, 最后发送警告
  5. 可以使用 API, Prometheus Console 或者 Grafana 查询和聚合数据

[2] 适用: 记录纯数字时间序列(float64数据类型), 可以对多维数据收集和查询的支持
收集的数据可能不够详细和完整, 不能保证100%准确度, 原因: 数据是按一定时间采集的, 关注的更多是系统的运行瞬时状态以及趋势, 即使有少量数据没有采集也能容忍

Prometheus与InfluxDB

区别

InfluxDB Prometheus
事件记录: 使用LSM结构存储按时间分段的WAL 每条时间序列只追加文件
分布式存储集群, 存储和查询同时由多个节点处理——副本之间一致地查看数据 服务器彼此独立运行, 其核心功能仅依赖于本地存储

联系
数据模型将键值对作为标签, (InfluxDB中一级标签tag, 二级标签fields) 支持多维度量
使用基本相同的数据压缩算法
两者都有钩子, 允许进一步扩展 (如在统计工具中分析数据或执行自动化操作)

Prometheus联邦 (Federation)
[1] 分层联邦允许扩展到拥有数千万个数据中心和数百万个节点的环境中, 集群类似于一棵树, 更高级别的服务器从更多的从属服务器收集聚合的时间序列数据

[2] 在跨服务联邦中, 一个服务的服务器被配置为从另一个服务的服务器中收集选定的数据, 以启用对单个服务器中的两个数据集的警报和查询

系统功能

[1] 数据模型
metric_name{label_name=label_value, …}

例1
container_cpu_usage_seconds_total{container_name=”abc”,cpu=”total”,id=”123”,name=”def”} 4751.708280444
以上述指标为例, Promethues指标由如下基本部分组成:
指标名字 - metric name - container_cpu_usage_seconds_total
值 - sampling value(float64) - 4751.708280444
标签 - label name, value - container_name=”abc”…
时间戳 - timestamp (Prometheus维护)

例2
以 # HELP 开头表示 metric 帮助说明
以 # TYPE 开头表示定义 metric 类型, 包含 counter, gauge, histogram, summary 和 untyped

1
2
3
4
5
# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
...

[2] 度量类型
Counter: 单调递增计数器的累积度量, 其值只能在重新启动时递增或重置为零
Gauge: 可以任意上下移动的数值
Histogram: 直方图对观察结果(通常是请求持续时间或响应大小)进行采样, 并在可配置的存储桶中计数, 同时提供所有观测值的总和
Summary: 总结样本观察, 还提供观测值的总数和所有观测值的总和, 同时计算滑动时间窗口上的可配置分位数

Histogram 和 Summary: 都包含 basename_sum, basename_count, Histogram 需要通过 basename_bucket 计算 quantile, 而 Summary 直接存储了 quantile 的值
Best Practice

[3] 作业和实例
实例(instance): 可以收集的端点, 通常对应一个进程
作业(job): 具有相同目的的实例集合 (如为了可伸缩性或可靠性而复制的进程)
如下是一个含有四个重复实例的作业:

1
2
3
4
5
job: api-server
instance 1: 1.2.3.4:5670
instance 2: 1.2.3.4:5671
instance 3: 5.6.7.8:5670
instance 4: 5.6.7.8:5671

Prometheus 在采集数据的同时, 会自动在时序的基础上添加标签, 作为数据源(target)的标识, 以便区分
如果其中任一标签已经在此前采集的数据中存在, 那么将会根据 honor_labels 设置选项来决定新标签

[4] 对于每个实例抓取, server将样本(sample)存储在以下时间序列中:
样本是时间序列中某个时间点的单个值, 每个样本由float64值和毫秒精度时间戳组成

标签 说明
up 1 表示该实例正常工作, 0 表示该实例故障
scrape_duration_seconds 表示拉取数据的时间间隔
scrape_samples_post_metric_relabeling 表示采用重定义标签(relabeling)操作后仍然剩余的样本数
scrape_samples_scraped 表示从该数据源获取的样本数
scrape_series_added 抓取中新序列的近似数目

配置项

[1] 配置项
scrape_interval: 拉取 targets 的默认时间间隔
scrape_timeout: 拉取一个 target 的超时时间
evaluation_interval: 执行 rules 的时间间隔
external_labels: 额外的属性, 会添加到拉取的数据并存到数据库中

[2] 录制规则 Recording Rules: 允许预计算经常需要或计算开销较大的表达式,并将其结果保存为一组新的时间序列 (适用于仪表板, 每次刷新时都需要重复查询同一表达式)

[3] 警告规则 Alerting Rules: 定义警报条件, 并向外部服务发送有关触发警报的通知。每当警报表达式在给定的时间点产生一个或多个向量元素时, 对于这些元素的标签集, 警报计为活跃
规则文件可以在运行时通过向Prometheus进程发送SIGHUP来重新加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
groups:
- name: example
rules:
- alert: HighRequestLatency
expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
for: 10m
// 使Prometheus在[第一次遇到新的表达式输出向量元素]和[将警报计为该元素的触发]之间等待一定的时间
// 在每次评估期间检查警报是否持续激活10分钟,然后再触发警报
labels:
// 允许指定要附加到警报的一组附加标签, 任何现有的冲突标签都将被覆盖
severity: page
annotations:
// 指定一组信息标签用于存储更长的附加信息, 如警报说明或runbook链接
summary: High request latency

[4] 运行时检查警报: 对于挂起和触发警报, 普罗米修斯还存储形式为alerts{alert name=”alert name”,alertstate=”pending | firning”, additional alert labels}的合成时间序列
Alertmanager: 在简单警报定义的基础上添加摘要、通知速率限制、静音和警报依赖项, 负责发送正确的通知

[5] 警报规则的测试

查询

[1] 如果查询需要对大量数据进行操作, 绘制可能会超时或使服务器或浏览器过载。因此在构建对未知数据的查询时, 先在Prometheus的expression browser的表格视图中开始构建查询, 直到结果集看起来合理为止 (最多是数百个时间序列)
如果表达式仍然需要很长的时间来绘制即席图表, 通过录制规则对其进行预录制

[2] 支持的函数
absent() / absent_over_time(): 如果传递给它的向量有任何元素, 则返回空向量; 如果传递给它的向量没有元素. 则返回1元素向量。当给定度量名称和标签组合不存在时间序列时, 用于发出警报

delta()计算范围向量中每个时间序列元素的第一个值和最后一个值之间的差。delta被外推(?extrapolated)以覆盖范围向量选择器中指定的整个时间范围, 因此即使样本值都是整数, 也可能得到非整数结果

holt_winters(): 根据范围为时间序列生成平滑值

[3] PromQL与 SQL 对比

1
2
3
4
5
6
http_requests_total{code="200", handler="query"}
http_requests_total{code~="2xx"}
http_requests_total > 100

count(http_requests_total)
irate(http_requests_total[5m]) // 过去 5 分钟平均每秒数值

存储

[1] 摄取的样本被分成两个小时的区块, 每个块包含一个目录, 其中包含一个或多个块文件, 这些文件包含该时间窗口的所有时间序列sample, 元数据文件索引文件(这些文件将度量名称和标签索引到块文件中的时间序列)
通过API删除序列时, 删除记录存储在单独的墓碑文件
WAL文件以128MB段存储在WAL目录中, 文件包含尚未压缩的原始数据

[2] 最初两个小时的块最终被压缩成背景中较长的块, 压实将产生更大的块体, 保留时间可达min{10%或31天}
过期块清理按后台计划进行, 删除过期的块可能需要两个小时。过期的块必须完全过期, 然后才能清除它们

[3] remote writeremote read 功能允许透明地发送和接收样本, 主要用于长期储存
读写协议都使用基于HTTP的snappy压缩协议缓冲区编码
Remote Write: Prometheus发送规定格式的数据给Adapter, Adapter转义后发送给数据库
Remote Read: Prometheus规定Adapter需要解析四种运算符 “=”,”!=”,”=~”,”!~”, 用户在界面上查询时, Prometheus发送查询命令给Adapter, Adapter转义后发送给数据库
使用remote read实现HA

可视化

Grafana控制台模板

系统生态集成

文件服务发现, 远程端点和存储, Alertmanager Webhook接收器, 管理: 集成工作

Client libraries

[1] JVM Client 注册度量:

1
2
static final Counter requests = Counter.build()
.name("my_library_requests_total").help("Total requests.").labelNames("path").register();

[2] Java client包括垃圾收集、内存池、类加载和线程计数的收集器, 可以单独添加, 也可以使用DefaultExports注册:DefaultExports.initialize();

[3] 度量通常通过HTTP公开, 由Prometheus服务器读取。客户端库中包含HTTPServer、Servlet、SpringBoot和Vert.x集成

Exporters

[1] 有许多库和服务器可帮助将第三方系统中的现有度量导出为 Prometheus 度量
在 Prometheus 中负责数据汇报的程序统一叫做 Exporter, 不同的 Exporter 负责不同的业务, 如负责主机信息收集的 node_exporter
Exporter 本质: 将收集的数据转化为对应的文本格式, 并提供 http 请求

[2] exporter集成

Pushgateway

[1] 使用Pushgateway的原因:

  • Prometheus 采用 pull 模式,可能由于不在一个子网或者防火墙原因,导致 Prometheus 无法直接拉取各个 target 数据。
  • 在监控业务数据的时候,需要将不同数据汇总, 由 Prometheus 统一收集。

[2] 弊端

  • 将多个节点数据汇总到 pushgateway, pushgateway 成为单一故障点和潜在的瓶颈
  • Prometheus 拉取状态 up 只针对 pushgateway, 无法做到对每个节点有效 (失去通过up的自动实例健康监测)
  • Pushgateway 可以持久化推送给它的所有监控数据, 即使监控已经下线, prometheus 还会拉取到旧的监控数据, 需要手动清理 pushgateway 不要的数据
    原因: Pushgateway作为度量缓存的生命周期从根本上与向其推送度量的流程的生命周期分离

[3] 使用场景: 捕获服务级别批处理作业的结果——此类作业的度量不应包含机器或实例标签, 以将特定机器或实例的生命周期与推送度量分离

AlertManager

[1] 在 Prometheus 中告警分为两部分:

  1. Prometheus 服务根据所设置的告警规则将告警信息发送给 Alertmanager
  2. Alertmanager 对收到的告警信息进行处理, 策略路由告警通知:

Grouping分组: 将类似性质的警报分类为单个通知, 警报分组、分组通知的计时以及这些通知的接收者由配置文件中的路由树配置
Inhibition抑制: 当某些其他警报已经触发时, 抑制某些警报的通知
Silence静音: 在给定时间内将警报静音, 将检查传入警报是否与活动静默的所有相等或正则表达式匹配项匹配
客户行为: 对其客户端的行为有特殊要求。这些只与Prometheus不用于发送警报的高级用例相关。
高可用性: Alertmanager 支持配置以创建高可用性群集, 不要在Prometheus和它的Alertmanagers之间进行负载平衡, 而是将Prometheus指向一个所有Alertmanagers列表

[2] 配置项

[3] 架构

参考资料

[1] https://prometheus.io/docs/introduction/overview/
[2] https://www.jianshu.com/p/5df4d23db8de
[3] https://songjiayang.gitbooks.io/prometheus/content/
[4] https://github.com/prometheus/client_java
[5] https://www.jianshu.com/p/413fd42ae660s