一、前言

1.1 为什么要监控

  • 监控可以及时有效的告知我们:服务发生了事件、服务器出现了故障、环境出现了异常

  • 出现问题后,利用监控数据追踪溯源,可以更高效的解决问题

  • 利用监控提高网站/服务的可用性

1.1.1 可用性

在软件系统的高可靠性(也称为可用性,英文描述为HA,High Available)里有个衡量其可靠性的标准——X个9,这个X是代表数字3~5。
X个9表示在软件系统1年时间的使用过程中,系统正常使用时间与总时间(1年)之比,通过下面的计算来感受下X个9在不同级别的可靠性差异。
    1个9:(1-90%)*365=36.5天,表示该软件系统在连续运行1年时间里最多可能的业务中断时间是36.5天
    2个9:(1-99%)*365=3.65天 , 表示该软件系统在连续运行1年时间里最多可能的业务中断时间是3.65天
    3个9:(1-99.9%)*365*24=8.76小时,表示该软件系统在连续运行1年时间里最多可能的业务中断时间是8.76小时。
    4个9:(1-99.99%)*365*24=0.876小时=52.6分钟,表示该软件系统在连续运行1年时间里最多可能的业务中断时间是52.6分钟。
    5个9:(1-99.999%)*365*24*60=5.26分钟,表示该软件系统在连续运行1年时间里最多可能的业务中断时间是5.26分钟。
    6个9:(1-99.9999%)*365*24*60*60=31秒, 示该软件系统在连续运行1年时间里最多可能的业务中断时间是31秒

1.2 监控什么目标

类型

目标

硬件监控

服务器IPMI、存储设备、SNMP网络设备、打印设备等

系统监控

CPU、内存、硬盘使用率、硬盘IO、系统负载、进程数等

服务监控

Mysql、oracle、Redis、Ceph、Hadoop、Apache、Nginx、tomcat、jvm···

性能监控

网站性能、服务器性能、数据库性能、存储性能

网络监控

物理端口、Web服务、带宽流量、端口速率、网络使用率、TCP连接数等

1.3 常见开源监控平台/工具

  • MRTG 流量监控

  • Nagios 综合监控

  • Zabbix 综合监控

  • Cacti 流量监控

  • Prometheus

二、Prometheus简介

2.1 Prometheus是什么

Prometheus是由SoundCloud开发的开源监控报警系统和TSDB(Time Series Database,时序列数据库)。受启发于Google的BorgMon监控系统,使用Go语言编写,从2012年开始由前Google工程师在Soundcloud以开源软件的形式进行研发,并且于2015年早期对外发布早期版本。2016年5月继Kubernetes之后成为第二个正式加入CNCF基金会的项目,同年6月正式发布1.0版本。2017年底发布了基于全新存储层的2.0版本,能更好地与容器平台、云平台配合。

有关 Prometheus 的详细信息,请参考官方介绍

2.2 Prometheus的特点

  • 由指标名称和键值对标签标识的时间序列数据组成的多维度数据模型

  • 灵活的查询语言(PromQL)

  • 不依赖分布式存储,单个服务节点具有自治能力

  • 通过基于HTTP的pull方式采集时序数据

  • 可以通过中间网关进行时序列数据推送(Pushgateway)

  • 通过服务发现或者静态配置来发现目标服务对象

  • 支持多种可视化图标和界面展示(Grafana,PromDash)

2.2 Prometheus的架构

Prometheus整体架构及生态系统组件如下:

2.3 Prometheus如何实现监控

在Prometheus的架构设计中,Prometheus Server并不直接服务监控特定的目标,其主要任务负责数据的收集,存储并且对外提供数据查询支持。因此为了能够监控到某些东西,如主机的CPU使用率,我们需要使用到Exporter。Prometheus周期性的从Exporter暴露的HTTP服务地址(通常是/metrics)拉取监控样本数据。

Exporter可以是一个独立运行的程序独立于监控目标以外,也可以是直接内置在监控目标中。只要能够向Prometheus提供标准格式的监控样本数据即可。

2.4 Prometheus 适用于什么场景

Prometheus 适用于记录文本格式的时间序列,它既适用于以机器为中心的监控,也适用于高度动态的面向服务架构的监控。在微服务的世界中,它对多维数据收集和查询的支持有特殊优势。Prometheus 是专为提高系统可靠性而设计的,它可以在断电期间快速诊断问题,每个 Prometheus Server 都是相互独立的,不依赖于网络存储或其他远程服务。当基础架构出现故障时,你可以通过 Prometheus 快速定位故障点,而且不会消耗大量的基础架构资源。

2.5 Prometheus 不适合什么场景

Prometheus 非常重视可靠性,即使在出现故障的情况下,你也可以随时查看有关系统的可用统计信息。如果你需要百分之百的准确度,例如按请求数量计费,那么 Prometheus 不太适合你,因为它收集的数据可能不够详细完整。这种情况下,你最好使用其他系统来收集和分析数据以进行计费,并使用 Prometheus 来监控系统的其余部分。

三、基本概念

本章节主要参考官方文档

3.1 数据模型

Prometheus 所有采集的监控数据均以指标(metric)的形式保存在内置的时间序列数据库当中(TSDB):属于同一指标名称,同一标签集合的、有时间戳标记的数据流。除了存储的时间序列,Prometheus 还可以根据查询请求产生临时的、衍生的时间序列作为返回结果。

3.1.1 指针名称和标签

每一条时间序列由指标名称(Metrics Name)以及一组标签(键值对)唯一标识。其中指标的名称(metric name)可以反映被监控样本的含义(例如,http_requests_total — 表示当前系统接收到的 HTTP 请求总量),指标名称只能由 ASCII 字符、数字、下划线以及冒号组成,同时必须匹配正则表达式 [a-zA-Z_:][a-zA-Z0-9_:]*

[info] 注意

冒号用来表示用户自定义的记录规则,不能在 exporter 中或监控对象直接暴露的指标中使用冒号来定义指标名称。

通过使用标签,Prometheus 开启了强大的多维数据模型:对于相同的指标名称,通过不同标签列表的集合,会形成特定的度量维度实例(例如:所有包含度量名称为 /api/tracks 的 http 请求,打上 method=POST 的标签,就会形成具体的 http 请求)。该查询语言在这些指标和标签列表的基础上进行过滤和聚合。改变任何度量指标上的任何标签值(包括添加或删除指标),都会创建新的时间序列。

标签的名称只能由 ASCII 字符、数字以及下划线组成并满足正则表达式 [a-zA-Z_][a-zA-Z0-9_]*。其中以 __ 作为前缀的标签,是系统保留的关键字,只能在系统内部使用。标签的值则可以包含任何 Unicode 编码的字符。

更多详细内容请参考 指标和标签命名最佳实践

3.1.2 样本

在时间序列中的每一个点称为一个样本(sample),样本由以下三部分组成:

  • 指标(metric):指标名称和描述当前样本特征的 labelsets;

  • 时间戳(timestamp):一个精确到毫秒的时间戳;

  • 样本值(value): 一个 folat64 的浮点型数据表示当前样本的值。

3.1.3 表达方式

通过如下表达方式表示指定指标名称和指定标签集合的时间序列:

<metric name>{<label name>=<label value>, ...}

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

api_http_requests_total{method="POST", handler="/messages"}

这与 OpenTSDB 中使用的标记法相同。

3.2 指针类型

Prometheus 的客户端库中提供了四种核心的指标类型。但这些类型只是在客户端库(客户端可以根据不同的数据类型调用不同的 API 接口)和在线协议中,实际在 Prometheus server 中并不对指标类型进行区分,而是简单地把这些指标统一视为无类型的时间序列。不过,将来我们会努力改变这一现状的。

3.2.1 Counter(计数器)

Counter 类型代表一种样本数据单调递增的指标,即只增不减,除非监控系统发生了重置。例如,你可以使用 counter 类型的指标来表示服务的请求数、已完成的任务数、错误发生的次数等。counter 主要有两个方法:

//将counter值加1.
Inc()
// 将指定值加到counter值上,如果指定值<0 会panic.
Add(float64)

Counter 类型数据可以让用户方便的了解事件产生的速率的变化,在 PromQL 内置的相关操作函数可以提供相应的分析,比如以 HTTP 应用请求量来进行说明:

//通过rate()函数获取HTTP请求量的增长率
rate(http_requests_total[5m])
//查询当前系统中,访问量前10的HTTP地址
topk(10, http_requests_total)

不要将 counter 类型应用于样本数据非单调递增的指标,例如:当前运行的进程数量(应该用 Gauge 类型)。

不同语言关于 Counter 的客户端库使用文档:

3.2.2 Gauge(仪表盘)

Gauge 类型代表一种样本数据可以任意变化的指标,即可增可减。Gauge 通常用于像温度或者内存使用率这种指标数据,也可以表示能随时增加或减少的“总数”,例如:当前并发请求的数量。

对于 Gauge 类型的监控指标,通过 PromQL 内置函数 delta() 可以获取样本在一段时间内的变化情况,例如,计算 CPU 温度在两小时内的差异:

dalta(cpu_temp_celsius{host="zeus"}[2h])

你还可以通过PromQL 内置函数 predict_linear() 基于简单线性回归的方式,对样本数据的变化趋势做出预测。例如,基于 2 小时的样本数据,来预测主机可用磁盘空间在 4 个小时之后的剩余情况:

predict_linear(node_filesystem_free{job="node"}[2h], 4 * 3600) < 0

不同语言关于 Gauge 的客户端库使用文档:

3.2.3 Histogram (直方图)

在大多数情况下人们都倾向于使用某些量化指标的平均值,例如 CPU 的平均使用率、页面的平均响应时间。这种方式的问题很明显,以系统 API 调用的平均响应时间为例:如果大多数 API 请求都维持在 100ms 的响应时间范围内,而个别请求的响应时间需要 5s,那么就会导致某些 WEB 页面的响应时间落到中位数的情况,而这种现象被称为长尾问题

为了区分是平均的慢还是长尾的慢,最简单的方式就是按照请求延迟的范围进行分组。例如,统计延迟在 0~10ms 之间的请求数有多少而 10~20ms 之间的请求数又有多少。通过这种方式可以快速分析系统慢的原因。Histogram 和 Summary 都是为了能够解决这样问题的存在,通过 Histogram 和 Summary 类型的监控指标,我们可以快速了解监控样本的分布情况。

Histogram 在一段时间范围内对数据进行采样(通常是请求持续时间或响应大小等),并将其计入可配置的存储桶(bucket)中,后续可通过指定区间筛选样本,也可以统计样本总数,最后一般将数据展示为直方图。

Histogram 类型的样本会提供三种指标(假设指标名称为 <basename>):

  • 样本的值分布在 bucket 中的数量,命名为 <basename>_bucket{le="<上边界>"}。解释的更通俗易懂一点,这个值表示指标值小于等于上边界的所有样本数量。

      // 在总共2次请求当中。http 请求响应时间 <=0.005 秒 的请求次数为0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.005",} 0.0
      // 在总共2次请求当中。http 请求响应时间 <=0.01 秒 的请求次数为0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.01",} 0.0
      // 在总共2次请求当中。http 请求响应时间 <=0.025 秒 的请求次数为0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.025",} 0.0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.05",} 0.0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.075",} 0.0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.1",} 0.0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.25",} 0.0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.5",} 0.0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.75",} 0.0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="1.0",} 0.0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="2.5",} 0.0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="5.0",} 0.0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="7.5",} 2.0
      // 在总共2次请求当中。http 请求响应时间 <=10 秒 的请求次数为 2
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="10.0",} 2.0
      io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="+Inf",} 2.0
  • 所有样本值的大小总和,命名为 <basename>_sum

      // 实际含义: 发生的2次 http 请求总的响应时间为 13.107670803000001 秒
      io_namespace_http_requests_latency_seconds_histogram_sum{path="/",method="GET",code="200",} 13.107670803000001
  • 样本总数,命名为 <basename>_count。值和 <basename>_bucket{le="+Inf"} 相同。

      // 实际含义: 当前一共发生了 2 次 http 请求
      io_namespace_http_requests_latency_seconds_histogram_count{path="/",method="GET",code="200",} 2.0

[info] 注意

bucket 可以理解为是对数据指标值域的一个划分,划分的依据应该基于数据值的分布。注意后面的采样点是包含前面的采样点的,假设 xxx_bucket{...,le="0.01"} 的值为 10,而 xxx_bucket{...,le="0.05"} 的值为 30,那么意味着这 30 个采样点中,有 10 个是小于 10 ms 的,其余 20 个采样点的响应时间是介于 10 ms 和 50 ms 之间的。

可以通过 histogram_quantile() 函数来计算 Histogram 类型样本的分位数。分位数可能不太好理解,你可以理解为分割数据的点。我举个例子,假设样本的 9 分位数(quantile=0.9)的值为 x,即表示小于 x 的采样值的数量占总体采样值的 90%。Histogram 还可以用来计算应用性能指标值(Apdex score)。

不同语言关于 Histogram 的客户端库使用文档:

3.2.4 Summary (摘要)

与 Histogram 类型类似,用于表示一段时间内的数据采样结果(通常是请求持续时间或响应大小等),但它直接存储了分位数(通过客户端计算,然后展示出来),而不是通过区间来计算。

Summary 类型的样本也会提供三种指标(假设指标名称为 ):

  • 样本值的分位数分布情况,命名为 <basename>{quantile="<φ>"}

      // 含义:这 12 次 http 请求中有 50% 的请求响应时间是 3.052404983s
      io_namespace_http_requests_latency_seconds_summary{path="/",method="GET",code="200",quantile="0.5",} 3.052404983
      // 含义:这 12 次 http 请求中有 90% 的请求响应时间是 8.003261666s
      io_namespace_http_requests_latency_seconds_summary{path="/",method="GET",code="200",quantile="0.9",} 8.003261666
  • 所有样本值的大小总和,命名为 <basename>_sum

      // 含义:这12次 http 请求的总响应时间为 51.029495508s
      io_namespace_http_requests_latency_seconds_summary_sum{path="/",method="GET",code="200",} 51.029495508
  • 样本总数,命名为 <basename>_count

      // 含义:当前一共发生了 12 次 http 请求
      io_namespace_http_requests_latency_seconds_summary_count{path="/",method="GET",code="200",} 12.0

现在可以总结一下 Histogram 与 Summary 的异同:

  • 它们都包含了 <basename>_sum<basename>_count 指标

  • Histogram 需要通过 <basename>_bucket 来计算分位数,而 Summary 则直接存储了分位数的值。

关于 Summary 与 Histogram 的详细用法,请参考 histograms and summaries

不同语言关于 Summary 的客户端库使用文档:

四、安装部署

Prometheus基于Golang编写,编译后的软件包,不依赖于任何的第三方依赖。用户只需要下载对应平台的二进制包,解压并且添加基本的配置即可正常启动Prometheus Server。

4.1 二进制安装

从官网下载Prometheus,执行以下命令

export VERSION={选择版本号}
export OS={选择平台}
curl -LO https://github.com/prometheus/prometheus/releases/download/v$VERSION/prometheus-$VERSION.$OS.tar.gz
tar -zxvf promtheus-${VERSION}.${OS}.tar.gz
cd promtheus-${VERSION}.${OS}

解压后当前目录会包含默认的Prometheus配置文件prometheus.yml

global:
  scrape_interval:     15s # By default, scrape targets every 15 seconds.
​
  # Attach these labels to any time series or alerts when communicating with
  # external systems (federation, remote storage, Alertmanager).
  external_labels:
    monitor: 'codelab-monitor'
​
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'
​
    # Override the global default and scrape targets from this job every 5 seconds.
    scrape_interval: 5s
​
    static_configs:
      - targets: ['localhost:9090']

完整的配置选项说明,请查看官方配置文档

4.1.1 My Prometheus.yml

# 全局设置
global:
  # 设置抓取间隔,默认为1分钟
  scrape_interval: 15s 
​
  # 设置抓取超时时间,默认为10秒  
  scrape_timeout: 10s
​
  # 设置规则刷新间隔,默认为1分钟
  evaluation_interval: 15s 
​
# 告警配置
# 一般使用Alertmanager
alerting:
 alertmanagers:
 - static_configs:
    # 设置alertmanager ip和端口
   - targets: ["localhost:9093"]
 
# 规则配置
# 配置会根据'evaluation_interval'设定刷新
rule_files:
   # Rule配置文件路径
   - "./first_rules.yml"
   - "/user/prometheus/second_rules.yml"
​
# 抓取配置
scrape_configs:
    # 指定抓取任务名称,job=<job_name>标签会被加入到指针序列中
  - job_name: 'prometheus'
​
    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.
​
    static_configs:
    # 静态配置抓取目标,这里配置抓取prometheus本身
    - targets: ['localhost:9090']
​
  - job_name: 'node_exporter'
    static_configs:
    # 多个target的第二种写法
      - targets:
        - 172.16.2.30:9100
        - 172.16.2.31:9100
        - 172.16.2.32:9100
        # 使用labels可以添加自定义标签,这里添加了{location="china"}
        labels:
          location: 'china'
​
# 下面来点高级玩法
  - job_name: 'blackbox_http_2xx'
    # 指定抓取目标的metrics路径,blackbox_exporter默认路径/probe
    metrics_path: /probe
    params:
    # 指定需要使用的module,module可在对应exporter的yml文件中定义,具体参见exporter文档
      module: [http_2xx]
    static_configs:
      - targets:
        - https://www.baidu.com/
        - https://www.google.com/
        labels:
          group: 'http_status'
    # 使用relabel_config重定义标签
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 127.0.0.1:9115
​

4.2 Dcoker安装

Docker用户直接使用prometheus镜像即可启动服务:

docker run -p 9090:9090 -v /etc/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus

4.3 启动Prometheus

创建配置文件后,进入prometheus工作目录,执行命令启动:

# Start Prometheus.
# By default, Prometheus stores its database in ./data (flag --storage.tsdb.path).
./prometheus --config.file=prometheus.yml
  • 我的启动参数

nohup ./prometheus --query.max-samples=120000000 --config.file=/prometheus.yml --storage.tsdb.path=/data/ --storage.tsdb.retention.time=7d &
# --query.max-samples:设置最大查询样本量
# --storage.tsdb.path:设置数据存储路径
# --storage.tsdb.retention.time:数据保存时长

启动完成后,可以通过http://localhost:9090访问Prometheus的UI界面:

4.4 发现服务

五、查询表达式

Prometheus 提供了一种功能表达式语言 PromQL,允许用户实时选择和汇聚时间序列数据。表达式的结果可以在浏览器中显示为图形,也可以显示为表格数据,或者由外部系统通过 HTTP API 调用。

详细参考PromQL

六、Grafana数据可视化

6.1 部署配置

6.2 监控模块

6.3 Dashboard

七、Alertmanager告警管理

7.1 部署配置

7.2 告警规则

7.3 集成邮箱&微信告警

八、Pushgateway

九、自定义Exporter

9.1 Exporter的工作机制

9.2 构建Exporter

十、实战案例:ESXi宿主机RAID监控

参考资料

Install & Configuration

https://yunlzheng.gitbook.io/prometheus-book/

PromQL

https://prometheus.fuckcloudnative.io/di-san-zhang-prometheus/di-4-jie-cha-xun/basics

Others

https://www.dell.com/support/article/zh-cn/sln292232/extracting-the-raid-controller-logs-via-megacli?lang=en

https://github.com/prometheus/pushgateway/blob/master/README.md

https://github.com/prometheus/node_exporter

https://github.com/pryorda/vmware_exporter

https://github.com/coreos/prometheus-operator/blob/master/Documentation/high-availability.md