云原生二十篇|详解Prometheus

监控一直是我们在开发过程中老生常谈的话题,大多数开发认为监控是运维的工作,我们只需要关注业务逻辑,打印对应的日志,增加上报数据,或者是用一些轻量的开源监控即可,是这样么?

其实随着云原生的架构演进,微服务化越来越常见,同时底层框架越来越复杂,开发是越来越简单,但是监控却越来越复杂。

(1)往往业务逻辑就是由各个模块调用聚合组成,这个时候作为开发可能做的更多的监控各个模块中各种指标,增加监控的埋点,能在出现异常的情况下迅速定位模块的问题;

(2)传统的监控方式已经很难面对云上的大规模集群,和适配云上各种业务场景;

因此本文主要介绍云原生使用较多的指标曲线和监控工具 Prometheus ,包括架构,配置,PromQL,一些进阶知识和问题。

1、监控方法论

1.1 MDD思想

MDD(Monitor-Driven Development)是主张以指标驱动软件迭代的开发方式,它强调在软件开发过程中,监控和度量是至关重要的,MDD思想在监控中的应用主要体现在以下几个方面:

  • 监控需求的定义:在软件开发过程中,开发人员需要定义监控需求,明确需要监控的指标和阈值,这些需求应该与业务需求和用户需求紧密关联,以确保监控能够及时发现并解决问题;
  • 监控数据的采集和处理:在MDD中,开发人员需要采集和处理监控数据,以便及时发现异常情况,监控数据可以来自于系统日志、性能指标、用户行为等多个方面,开发人员需要通过数据分析和处理,找到问题的根本原因,并及时采取措施;
  • 监控系统的自动化:在MDD中,监控系统应该是自动化的,能够自动采集和处理监控数据,并及时发送警报,开发人员需要通过自动化工具和技术,实现监控系统的自动化,从而提高监控效率和准确性;
  • 监控结果的反馈和改进:在MDD中,开发人员需要不断反馈监控结果,及时发现和解决问题,并不断改进监控系统,监控结果的反馈可以来自于用户反馈、系统日志、性能指标等多个方面,开发人员需要对监控结果进行分析和处理,以便不断改进监控系统,提高软件质量和稳定性;

优点:

监控是软件开发的核心,开发人员通过监控系统的运行状态,就能及时发现并解决问题,从而提高软件的质量和稳定性,我个人觉得如果你是架构师,使用MDD开发方式能在后期系统维护上做到事半功倍的效果。

1.2 Google的黄金指标

SRE

读过《SRE Google运维解密》的书应该了解Google的四大黄金法则,其指标如下:

延迟 :(Latency)服务请求所需的耗时,例如HTTP延时分位值,当然这里需要区分成功请求和失败请求;
流量 :(Traffic)衡量服务容量需求,比如QPS,TPS等;
错误 :(Errors)请求失败QPS,用于衡量错误发生的情况,比如HTTP的5XX错误;
饱和度:(Saturation)衡量资源的使用情况,比如内存,CPU,I/O,磁盘使用情况等;

优点

通过监控以上指标能总体反应系统的健康状态,并结合时序数据能预测系统什么时候能达到瓶颈。

1.3 USE方法

USE方法(Utilization, Saturation, and Errors Method)是一种系统性能分析方法,用于评估计算机系统的瓶颈和资源利用率,这种方法是由Brendan Gregg提出的,它的基本思想是通过检查系统的资源利用率(Utilization)、饱和度(Saturation)和错误(Errors)来找出系统性能的问题。

使用率 :关注系统资源的使用情况,这里主要包括CPU,内存,网络,磁盘等;
饱和度 :资源使用排队的平均状态,和Google的饱和度有差别,以CPU为例指CPU的平均运行排队长度;
错误:错误数,和Google的错误是一样,衡量错误情况;

USE方法的主要目标是确定系统中存在的资源瓶颈,从而帮助优化系统性能,以下是使用USE方法的一般步骤:

  • 为每个资源定义利用率、饱和度和错误指标。这些资源可能包括CPU、内存、磁盘、网络等;
  • 收集和监控这些指标的数据;
  • 分析数据,找出存在问题的资源;
  • 优化问题资源,提高系统性;

优点

通过USE方法是一种有效的系统性能分析方法,可以帮助我们找出系统中的资源瓶颈,并提供改进的方向。

1.3 RED方法

RED方法是Weave Cloud基于Google的四大黄金法则结合Prometheus和Kubernetes容器实践得出来的方法论,适应对于云原生应用和微服务框架的应用监控和度量。

Rate :每秒接收的请求数;
Errors :每秒失败的请求数; Duration:每个请求花费的时间;

2、时序数据库

2.1 什么是时序数据库?

时序数据是随时间不断产生的一系列数据,简单来说,就是带时间戳的数据,时序数据库 (Time Series Database,TSDB) 是优化用于摄取、处理和存储时间戳数据的数据库,对比其他SQL和NoSQL ,其中时序数据库是专门用于存储和处理时间序列数据的数据库,支持时序数据高效读写、高压缩存储、插值和聚合等功能。

2.2 有哪些时序数据库?

时序数据库的发展趋势,可以从DB-engines获得,具体时序数据库列表:db-engines.com/en/ranking/...

时序数据库

可以看到 InfluxDBPrometheus等都是比较常见时序数据库,其用于监控领域比较常见。

3、Prometheus架构

官方架构

上图是Prometheus的官方架构图,其包括如下几个部分:

  • Prometheus Server:Prometheus的核心模块,主要是采集和存储各个时序指标数据,并提供查询的功能,其中存储包括本地存储和远程存储
  • Pushgateway:通常Prometheus采用拉取模式,但是也提供推模式,可以推送数据到pushgateway来实现和拉取数据一样的效果,比如一些定时任务等临时作业
  • Exporter:Exporter是Prometheus的监控对象,主要是收集数据,比如golang程序中导出的 /metrics 数据,官方也提供很多类型的Exporter,比如CPU,内存等采集
  • Service Discovery:在云原生监控中,找到对应的Exporter不可能手动配置,所以Prometheus提供了服务发现机制来采集Exporter节点,支持DNS,Consule,文件和k8s API等方式
  • Dashboard:Prometheus提供的Web UI,虽然通常与Grafana组合使用,但是独立的Web UI可以方便快速查询和展示图标
  • Alertmanager:独立于Prometheus的告警组件,是独立的服务部署,Alertmanager主要接收Prometheus推送过来的告警,用于管理、整合和分发告警信息,除了这些还提供分组,抑制和静默等告警特性

4、Prometheus实践

这里为了方便读者使用和实验,提供一个 docker-compose 文件,通过 docker-compose up 可以拉起 prometheusalertmanager

bash 复制代码
version: '3'

services:
  prometheus:
    image: prom/prometheus:v2.30.3
    ports:
      - 9090:9090
    volumes:
      - ./prometheus:/etc/prometheus
      - prometheus-data:/prometheus
    command: --web.enable-lifecycle  --config.file=/etc/prometheus/prometheus.yml

  alertmanager:
    image: prom/alertmanager:v0.23.0
    restart: unless-stopped
    ports:
      - 9093:9093
    volumes:
      - ./alertmanager:/config
      - alertmanager-data:/data
    command: --config.file=/config/alertmanager.yml --log.level=debug 

volumes:
  prometheus-data:

  alertmanager-data:

打开浏览器访问:http://{你的IP}:9090 就可以打开页面,可以查询数据如下:

Prometheus

打开浏览器访问:http://{你的IP}:9093 可以看到 alertmanager 页面:

Alertmanager

5、PromQL语法

5.1 基础指标

Prometheus 定义了 4 种不同的指标类型:Counter(计数器)、Gauge(仪表盘)、Histogram(直方图)、Summary(摘要):

  • Counter(计数器):Counter 类型代表一种样本数据单调递增的指标,即只增不减,除非监控系统发生了重置。例如,你可以使用 counter 类型的指标来表示服务的请求数、已完成的任务数、错误发生的次数等
  • Gauge(仪表盘):Gauge 类型代表一种样本数据可以任意变化的指标,即可增可减。Gauge 通常用于像温度或者内存使用率这种指标数据,也可以表示能随时增加或减少的"总数",例如:当前并发请求的数量
  • Histogram(直方图):Histogram 在一段时间范围内对数据进行采样(通常是请求持续时间或响应大小等),并将其计入可配置的存储桶(bucket)中,后续可通过指定区间筛选样本,也可以统计样本总数,最后一般将数据展示为直方图
  • Summary(摘要):与 Histogram 类型类似,用于表示一段时间内的数据采样结果(通常是请求持续时间或响应大小等),但它直接存储了分位数(通过客户端计算,然后展示出来),而不是通过区间来计算

其中Prometheus的基础数据格式:

xml 复制代码
<metric name>{<label name>=<label value>, ...}

例如,指标名称为 http_requests_total,标签为 method="POST" 和 handler="/messages" 的时间序列可以表示为:

ini 复制代码
http_requests_total{method="POST", handler="/messages"}

5.1 基础PromQL

Prometheus通过指标名称(metrics name)以及对应的一组标签(labelset)唯一定义一条时间序列,指标名称反映了监控样本的基本标识,而label则在这个基本特征上为采集到的数据提供了多种特征维度,用户可以基于这些特征维度过滤,聚合,统计从而产生新的计算后的一条时间序列。

  • 查询某个指标数据,以http_requests_total为例:http_requests_total{}
  • 查询某个指标数据中的某一个label,如:http_requests_total{instance="localhost:9090"},查询 instancelocalhost:9090 的数据
  • 正则表达式(label=~regx),如:http_requests_total{environment=~"staging|testing|development",method!="GET"},查询 environment 匹配并且 method 不为 GET 方法的数据
  • 查询范围,通过区间向量表达式([]),如:http_requests_total{}[5m] 查询最近5分钟内的数据,这里时间范围可以用:s(秒),m(分钟),h(小时),d(天),w(周),y(年)
  • 时间位移查询,使用 offset,如:http_request_total{}[1d] offset 1d 查询昨天一天的区间内的样本数据
  • 聚合函数 sum,表示一段区间内获取数据总量,如:sum(http_request_total) 查询系统所有http请求的总量
  • 聚合函数 avg,表示一段区间内平均值,如:avg(node_cpu) by (mode) 按照mode计算主机CPU的平均使用时间

5.2 PromQL操作符

PromQL操作符包括数学运算符,逻辑运算符,布尔运算符等。

  • 数学运算,支持+(加法),-(减法),*(乘法),/(除法),%(求余),^(幂运算),如: http_requests_total / 60
  • 布尔运算,支持==(相等),!=(不相等),>(大于),<(小于),>=(大于等于),<=(小于等于),如:http_requests_total{} > 0
  • 操作符包括一些聚合操作,其语法如:<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]
  • without,用于移除计算结果中的标签,如:sum(http_requests_total) without (instance)http_requests_total 中的 instance 标签移除聚合
  • by,用于保留计算结果的标签,类似mysql的group,如:sum(http_requests_total) by (code,handler,job,method) 按照 code,handler,job,method 这几个维度聚合
  • count_values,每一个唯一的样本值出现的次数,类似mysql的group与count,如:count_values("count", http_requests_total),计算 count 值的个数
  • topkbottomk,用于对样本值进行排序,返回当前样本值前n位,或者后n位的时间序列,如:topk(5, http_requests_total),计算HTTP请求数前5位的时序样本数据
  • quantile 用于计算当前样本数据值的分布情况,如:quantile(0.5, http_requests_total)

5.3 PromQL聚合操作

  • sum(求和):sum(http_requests_total),整个应用的HTTP请求总量
  • min(最小值):min(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance),返回 CPU 使用率最低的实例的值
  • max(最大值):max(http_request_duration_seconds) by (instance),返回 HTTP 请求响应时间最长的实例的值
  • avg(平均值):avg(rate(node_cpu_seconds_total{mode="user"}[5m])) without (cpu),返回所有节点的 CPU 使用率平均值
  • stddev(标准差):stddev(http_request_duration_seconds),返回 HTTP 请求响应时间的标准差
  • stdvar(标准方差):stdvar(http_request_duration_seconds),返回 HTTP 请求响应时间的方差
  • count(计数):count(node_cpu_seconds_total) without (cpu, mode),返回所有节点的数量

5.4 PromQL内置函数

由于Prometheus的内置函数太多了,这里列举几个常用的:

  • ceil():将 v 中所有元素的样本值向上四舍五入到最接近的整数,如:node_load5{instance="192.168.1.75:9100"} # 结果为 2.79
  • rate():可以直接计算区间向量 v 在时间窗口内平均增长速率,它会在单调性发生变化时(如由于采样目标重启引起的计数器复位)自动中断,如:rate(http_requests_total[5m]) 返回区间向量中每个时间序列过去 5 分钟内 HTTP 请求数的每秒增长率
  • label_join():可以将时间序列 v 中多个标签 src_label 的值,通过 separator 作为连接符写入到一个新的标签 dst_label 中
  • label_replace():为了能够让客户端的图标更具有可读性,可以通过 label_replace 函数为时间序列添加额外的标签

其他更多的文档可以参考:官方文档或者 https://prometheus.fuckcloudnative.io/di-san-zhang-prometheus/di-4-jie-cha-xun/functions

6、Alertmanager告警

6.1 告警架构

Alertmanager

Alertmanager 通常是接收Prometheus的数据,然后经过去重,分组,最后调用到对应的webhook,通知告警接收人。

6.2 Prometheus与Alertmanager如何关联

通常Prometheus告警流程如图:

Alertmanager

在告警中Prometheus做什么?

  • Prometheus需要先定义告警规则,其中规则样例如下:
bash 复制代码
groups:
- name: example           # 分组名称
  rules:
  - alert: alertname  # 告警规则1的名称
    expr: job:request_latency_seconds:mean5m{job="xxx"} > 0.5   # 基于PromQL表达式告警触发条件,用于计算是否有时间序列满足该条件
    for: 10m          # 评估等待时间,可选参数,用于表示只有当触发条件持续一段时间后才发送告警,在等待期间新产生告警的状态为pending
    labels:           # 自定义标签,允许用户指定要附加到告警上的一组附加标签
      severity: page
    annotations:      # 用于指定一组附加信息,比如用于描述告警详细信息的文字等
      summary: High request latency
      description: description info

也可以定义一些批量的文件,然后再 prometheus.yml 设置 rule_files,比如:

javascript 复制代码
rule_files:
  - /etc/prometheus/rules/*.rules
  • 定义告警周期(也就是计算周期):
csharp 复制代码
global:
  [ evaluation_interval: <duration> | default = 1m ]

在告警中Alertmanager做什么?

  • 定义配置文件,如下:
csharp 复制代码
global:
  resolve_timeout: 5m

route:          # 所有的告警信息都会从配置中的顶级路由(route)进入路由树,根据路由规则将告警信息发送给相应的接收器
  group_by: ['alertname'] # 分组参数
  group_wait: 10s         # 最初发送通知之前等待缓冲同一组告警的时间
  group_interval: 10s     # 发送添加到已有的告警通知组之前需要等待的时间
  repeat_interval: 1h     # 每隔repeat_interval收到一条告警信息
  receiver: 'web.hook' # 定义发给哪一个receiver
receivers:      # 定义一组接收器,比如可以按照角色(比如系统运维,数据库管理员)来划分多个接收器。接收器可以关联邮件,Slack以及其它方式接收告警信息
- name: 'web.hook'
  webhook_configs:
  - url: 'http://127.0.0.1:5001/'
inhibit_rules:   # 设置抑制规则可以减少垃圾告警的产生
  - target_match:
      severity: 'warning'
    equal: ['alertname', 'dev', 'instance']
  • 将Alertmanager的地址配置到 prometheus.yml 中:
yaml 复制代码
alerting:
  alertmanagers:
    - static_configs:
        - targets: ['localhost:9093']

7、Prometheus进阶知识

7.1 存储原理

Prometheus 1.x版本的TSDB(V2存储引擎)基于LevelDB,并且使用了和Facebook Gorilla一样的压缩算法,能够将16个字节的数据点压缩到平均1.37个字节。Prometheus 2.x版本引入了全新的V3存储引擎,提供了更高的写入和查询性能,Prometheus按2小时一个block进行存储,每个block由一个目录组成,目录里包含:

  • 一个或者多个chunk文件(保存timeseries数据)、一个metadata文件、一个index文件(通过metric name和labels查找timeseries数据在chunk文件的位置)
  • 最新写入的数据保存在内存block中,达到2小时后写入磁盘,为了防止程序崩溃导致数据丢失,实现了WAL(write-ahead-log)机制,启动时会以写入日志(WAL)的方式来实现重播,从而恢复数据
  • 删除数据时,删除条目会记录在独立的tombstone文件中,而不是立即从chunk文件删除
  • 2小时的block会在后台压缩成更大的block,数据压缩合并成更高level的block文件后删除低level的block文件,这个和leveldb、rocksdb等LSM树的思路一致

以上设计和Gorilla的设计高度相似,所以Prometheus几乎就是等于一个缓存TSDB,它本地存储的特点决定了它不能用于long-term数据存储,只能用于短期窗口的timeseries数据保存和查询,并且不具有高可用性,其中以下是我在线上部署Prometheus的数据目录:

存储

Prometheus如果只是存储在单机上,在面临大规模集群情况下肯定磁盘不够,所以提供了远程存储能力,可以在 prometheus.yml 配置:

makefile 复制代码
remote_write:
  - url: "http://localhost:9201/write"

remote_read:
  - url: "http://localhost:9201/read"

7.2 Prometheus高可用架构

Prometheus官方的高可用有几种方案:

  • HA:即两套 Prometheus 采集完全一样的数据,外边挂负载均衡
  • HA + 远程存储:除了基础的多副本 Prometheus,还通过 Remote write 写入到远程存储,解决存储持久化问题
  • 联邦集群:即 Federation,按照功能进行分区,不同的 Shard 采集不同的数据,由 Global 节点来统一存放,解决监控数据规模的问题

HA方案

HA

多个Prometheus同时采集数据,数据会在各个机器上存储一份,如果查询发现某台机器挂了,可以通过LVS/nginx自动切换,但是存储缺点是数据不同步,无法横向扩容。

HA + 远程存储

HA存储

多个Prometheus同时采集数据,数据会存储到远端存储上,如果查询发现某台机器挂了,可以通过LVS/nginx自动切换,只要远端存储数据不挂或者容量不大情况,这种方案架构简单,建议采用。

联邦集群

联邦集群

联邦集群主要是针对大规模集群,Prometheus计算存储等分离方式,某几个Prometheus管理集群A数据,某几个Prometheus管理集群B数据...,然后通过多层逐步缩小集群节点,最后汇总到某个Prometheus查询大盘数据等。

7.3 Alertmanager高可用架构

解决了Prometheus的单节点问题,而Alertmanager同样面临这种情况,而Alertmanager也提供官方的解决方案:

Alertmanager高可用架构

Prometheus 可以发送告警信息到多个 Alertmanager 节点上,但是 Alertmanager 收到以后没法收敛,于是 Alertmanager 引入了Gossip机制,Gossip机制为多个 Alertmanager 之间提供了信息传递的机制,确保及时在多个Alertmanager 分别接收到相同告警信息的情况下,也只有一个告警通知被发送给 Receiver。

7.4 Thanos架构

为什么需要Thanos?

随着集群规模越来越大,监控数据的种类和数量也越来越多:如Master/Node 机器监控、进程监控、4 大核心组件的性能监控,POD 资源监控、kube-stats-metrics、K8S events监控、插件监控等等。

除了解决上面的高可用问题,更多希望基于 Prometheus 构建全局视图,主要需求有:

  • 长期存储:集群很大的情况下,一般 Prometheus 一天可能产生几十G到几百G的数据不等,如果一个月就是T级别以上的数据
  • 无限拓展:集群节点在大厂到数万到数十万比较正常,而且其中服务的上报随着服务增多,这个量级也是逐步增大

解决上述问题开源社区有一些解决方案,比如:Cortex/Thanos/Victoria/StackDriver等,但是Thanos相对的优势是侵入性比较小,可以快速与Prometheus结合使用,其官方架构如下:

Thanos

限于篇幅,有兴趣了解Thanos架构和源码的,可以去 github(github.com/thanos-io/t...

8、Prometheus使用过程中的问题汇总

8.1 大内存问题

随着规模变大,prometheus需要的cpu和内存都会升高,内存一般先达到瓶颈,这个时候要么加内存,要么集群分片减少单机指标,解决方案:

  • sample 数量超过了 200 万,就不要单实例了,做下分片,然后通过 victoriametrics,thanos,trickster等方案合并数据
  • 评估 metric 和 label 占用较多的指标,去掉没用的指标
  • 查询时尽量避免大范围查询,注意时间范围和 step 的比例,慎用 group
  • 如果需要关联查询,先想想能不能通过 relabel 的方式给原始数据多加个 label

8.2 慢查询问题

评估 prometheus 的整体响应时间,可以用这个默认指标:

prometheus_engine_query_duration_seconds{}

对于 prometheus 查询返回比较慢解决方案:

  • 如果比较复杂且耗时的sql,可以使用record rule(预聚合)减少指标数量,并使查询效率更高
  • 范围查询时,大的时间范围,step 值却很小,导致查询到的数量量过大,减少查询范围
  • 通过联邦架构,将不需要的instance的数据聚合
  • 去出高基数label,比如用户的userid或者来源IP等,这种可以TSDB Status中查到,命令 ./tsdb analyze ../data/prometheus/

8.3 找到最大的 metric 或 job

有时候需要分析 metric 的数量,确定哪些 metric 中的 label 太多,可以通过 topk 查看:

ini 复制代码
topk(10, count by (__name__)({__name__=~".+"}))

参考

(1)yunlzheng.gitbook.io/prometheus-...

(2)github.com/prometheus-...

(3)github.com/thanos-io/t...

(4)yasongxu.gitbook.io/container-m...

相关推荐
黄俊懿1 分钟前
【深入理解SpringCloud微服务】手写实现各种限流算法——固定时间窗、滑动时间窗、令牌桶算法、漏桶算法
java·后端·算法·spring cloud·微服务·架构
飞酱不会电脑17 分钟前
云计算第四阶段 CLOUD2周目 01-03
云原生·容器·kubernetes
2401_8574396928 分钟前
“衣依”服装销售平台:Spring Boot技术应用与优化
spring boot·后端·mfc
Jerry.ZZZ1 小时前
系统设计,如何设计一个秒杀功能
后端
程序那点事儿2 小时前
k8s 之安装busybox
云原生·容器·kubernetes
九圣残炎2 小时前
【springboot】简易模块化开发项目整合Redis
spring boot·redis·后端
是芽芽哩!3 小时前
【Kubernetes】常见面试题汇总(五十八)
云原生·容器·kubernetes
.生产的驴3 小时前
Electron Vue框架环境搭建 Vue3环境搭建
java·前端·vue.js·spring boot·后端·electron·ecmascript
爱学的小涛3 小时前
【NIO基础】基于 NIO 中的组件实现对文件的操作(文件编程),FileChannel 详解
java·开发语言·笔记·后端·nio
爱学的小涛3 小时前
【NIO基础】NIO(非阻塞 I/O)和 IO(传统 I/O)的区别,以及 NIO 的三大组件详解
java·开发语言·笔记·后端·nio