转载自:https://songjiayang.gitbooks.io/prometheus/content/pushgateway/why.html
Prometheus 实战
v0.1.0
在过去一年左右时间里,我们使用 Prometheus 完成了对几个机房的基础和业务监控,大大提高了服务质量以及 oncall 水平,在此特别感谢 Promethues 这样优秀的开源软件。
当初选择 Prometheus 并不是偶然,因为:
-
Prometheus 是按照 Google SRE 运维之道的理念构建的,具有实用性和前瞻性。
-
Prometheus 社区非常活跃,基本稳定在 1个月1个版本的迭代速度,从 2016 年 v1.01 开始接触使用以来,到目前发布的 v1.8.2 以及最新最新的 v2.1 ,你会发现 Prometheus 一直在进步、在优化。
-
Go 语言开发,性能不错,安装部署简单,多平台部署兼容性好。
-
丰富的数据收集客户端,官方提供了各种常用 exporter。
-
丰富强大的查询能力。
Prometheus 作为监控后起之秀,虽然还有做的不够好的地方,但是不妨碍我们使用和喜爱它。根据我们长期的使用经验来看,它足以满足大多数场景需求,只不过对于新东西,往往需要花费更多力气才能发挥它的最大能力而已。
本书主要根据个人过去一年多的使用经验总结而成,内容主要包括 Prometheus 基本知识、进阶、实战以及常见问题列表等方面,希望对大家有所帮助。
本开源书籍既适用于具备基础 Linux 知识的运维初学者,也可供渴望理解 Prometheus 原理和实现细节的高级用户参考,同时也希望书中给出的实践案例在实际部署监控中对大家有所帮助。
你准备好了吗?接下来就让我们一起开始这段神奇旅行吧!
一、什么是 Prometheus
Prometheus 是由 SoundCloud 开源监控告警解决方案,从 2012 年开始编写代码,再到 2015 年 github 上开源以来,已经吸引了 9k+ 关注,以及很多大公司的使用;2016 年 Prometheus 成为继 k8s 后,第二名 CNCF(Cloud Native Computing Foundation) 成员。
作为新一代开源解决方案,很多理念与 Google SRE 运维之道不谋而合。
主要功能
- 多维 数据模型(时序由 metric 名字和 k/v 的 labels 构成)。
- 灵活的查询语句(PromQL)。
- 无依赖存储,支持 local 和 remote 不同模型。
- 采用 http 协议,使用 pull 模式,拉取数据,简单易懂。
- 监控目标,可以采用服务发现或静态配置的方式。
- 支持多种统计数据模型,图形化友好。
核心组件
- Prometheus Server, 主要用于抓取数据和存储时序数据,另外还提供查询和 Alert Rule 配置管理。
- client libraries,用于对接 Prometheus Server, 可以查询和上报数据。
- push gateway ,用于批量,短期的监控数据的汇总节点,主要用于业务数据汇报等。
- 各种汇报数据的 exporters ,例如汇报机器数据的 node_exporter, 汇报 MongoDB 信息的 MongoDB exporter 等等。
- 用于告警通知管理的 alertmanager 。
基础架构
一图胜千言,先来张官方的架构图
从这个架构图,也可以看出 Prometheus 的主要模块包含, Server, Exporters, Pushgateway, PromQL, Alertmanager, WebUI 等。
它大致使用逻辑是这样:
- Prometheus server 定期从静态配置的 targets 或者服务发现的 targets 拉取数据。
- 当新拉取的数据大于配置内存缓存区的时候,Prometheus 会将数据持久化到磁盘(如果使用 remote storage 将持久化到云端)。
- Prometheus 可以配置 rules,然后定时查询数据,当条件触发的时候,会将 alert 推送到配置的 Alertmanager。
- Alertmanager 收到警告的时候,可以根据配置,聚合,去重,降噪,最后发送警告。
- 可以使用 API, Prometheus Console 或者 Grafana 查询和聚合数据。
注意
- Prometheus 的数据是基于时序的 float64 的值,如果你的数据值有更多类型,无法满足。
- Prometheus 不适合做审计计费,因为它的数据是按一定时间采集的,关注的更多是系统的运行瞬时状态以及趋势,即使有少量数据没有采集也能容忍,但是审计计费需要记录每个请求,并且数据长期存储,这个 Prometheus 无法满足,可能需要采用专门的审计系统。
二、为什么选择 Prometheus
在前言中,简单介绍了我们选择 Prometheus 的理由,以及使用后给我们带来的好处。
在这里主要和其他监控方案对比,方便大家更好的了解 Prometheus。
Prometheus vs Zabbix
- Zabbix 使用的是 C 和 PHP, Prometheus 使用 Golang, 整体而言 Prometheus 运行速度更快一点。
- Zabbix 属于传统主机监控,主要用于物理主机,交换机,网络等监控,Prometheus 不仅适用主机监控,还适用于 Cloud, SaaS, Openstack,Container 监控。
- Zabbix 在传统主机监控方面,有更丰富的插件。
- Zabbix 可以在 WebGui 中配置很多事情,但是 Prometheus 需要手动修改文件配置。
Prometheus vs Graphite
- Graphite 功能较少,它专注于两件事,存储时序数据, 可视化数据,其他功能需要安装相关插件,而 Prometheus 属于一站式,提供告警和趋势分析的常见功能,它提供更强的数据存储和查询能力。
- 在水平扩展方案以及数据存储周期上,Graphite 做的更好。
Prometheus vs InfluxDB
- InfluxDB 是一个开源的时序数据库,主要用于存储数据,如果想搭建监控告警系统, 需要依赖其他系统。
- InfluxDB 在存储水平扩展以及高可用方面做的更好, 毕竟核心是数据库。
Prometheus vs OpenTSDB
- OpenTSDB 是一个分布式时序数据库,它依赖 Hadoop 和 HBase,能存储更长久数据, 如果你系统已经运行了 Hadoop 和 HBase, 它是个不错的选择。
- 如果想搭建监控告警系统,OpenTSDB 需要依赖其他系统。
Prometheus vs Nagios
- Nagios 数据不支持自定义 Labels, 不支持查询,告警也不支持去噪,分组, 没有数据存储,如果想查询历史状态,需要安装插件。
- Nagios 是上世纪 90 年代的监控系统,比较适合小集群或静态系统的监控,显然 Nagios 太古老了,很多特性都没有,相比之下Prometheus 要优秀很多。
Prometheus vs Sensu
- Sensu 广义上讲是 Nagios 的升级版本,它解决了很多 Nagios 的问题,如果你对 Nagios 很熟悉,使用 Sensu 是个不错的选择。
- Sensu 依赖 RabbitMQ 和 Redis,数据存储上扩展性更好。
总结
- Prometheus 属于一站式监控告警平台,依赖少,功能齐全。
- Prometheus 支持对云或容器的监控,其他系统主要对主机监控。
- Prometheus 数据查询语句表现力更强大,内置更强大的统计函数。
- Prometheus 在数据存储扩展性以及持久性上没有 InfluxDB,OpenTSDB,Sensu 好。
三、安装Prometheus
本章将介绍 Prometheus 两种安装方式: 传统二进制包安装和 Docker 安装方式。
1、二进制包安装
我们可以到 Prometheus 二进制安装包下载页面,根据自己的操作系统选择下载对应的安装包。下面我们将以 ubuntu server 作为演示。
环境准备
- linux amd64 (ubuntu server)
- prometheus 1.6.2
下载 Prometheus Server
创建下载目录,以便安装过后清理掉
mkdir ~/Download
cd ~/Download
使用 wget 下载 Prometheus 的安装包
wget https://github.com/prometheus/prometheus/releases/download/v1.6.2/prometheus-1.6.2.linux-amd64.tar.gz
创建 Prometheus 目录,用于存放所有 Prometheus 相关的运行服务
mkdir ~/Prometheus
cd ~/Prometheus
使用 tar 解压缩 prometheus-1.6.2.linux-amd64.tar.gz
tar -xvzf ~/Download/prometheus-1.6.2.linux-amd64.tar.gz
cd prometheus-1.6.2.linux-amd64
当解压缩成功后,可以运行 version 检查运行环境是否正常
./prometheus version
如果你看到类似输出,表示你已安装成功:
prometheus, version 1.6.2 (branch: master, revision: xxxx)
build user: xxxx
build date: xxxx
go version: go1.8.1
启动 Prometheus Server
./prometheus
如果 prometheus 正常启动,你将看到如下信息:
INFO[0000] Starting prometheus (version=1.6.2, branch=master, revision=b38e977fd8cc2a0d13f47e7f0e17b82d1a908a9a) source=main.go:88
INFO[0000] Build context (go=go1.8.1, user=root@c99d9d650cf4, date=20170511-13:03:00) source=main.go:89
INFO[0000] Loading configuration file prometheus.yml source=main.go:251
INFO[0000] Loading series map and head chunks... source=storage.go:421
INFO[0000] 0 series loaded. source=storage.go:432
INFO[0000] Starting target manager... source=targetmanager.go:61
INFO[0000] Listening on :9090 source=web.go:259
通过启动日志,可以看到 Prometheus Server 默认端口是 9090。
当 Prometheus 启动后,你可以通过浏览器来访问 http://IP:9090
,将看到如下页面
在默认配置中,我们已经添加了 Prometheus Server 的监控,所以我们现在可以使用 PromQL
(Prometheus Query Language)来查看,比如:
总结
- 可以看出 Prometheus 二进制安装非常方便,没有依赖,自带查询 web 界面。
- 在生产环境中,我们可以将 Prometheus 添加到 init 配置里,或者使用 supervisord 作为服务自启动。
2、Docker 安装
首先确保你已安装了最新版本的 Docker, 如果没有安装请点击这里。
下面我将以 Mac 版本的 Docker 作为演示。
安装
Docker 镜像地址 Quay.io
执行命令安装:
$ docker run --name prometheus -d -p 127.0.0.1:9090:9090 quay.io/prometheus/prometheus
如果安装成功你可以访问 127.0.0.1:9090
查看到该页面:
Docker 管理 prometheus
运行 docker ps 查看所有服务:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e9ebc2435387 quay.io/prometheus/prometheus "/bin/prometheus -..." 26 minutes ago Up 26 minutes 127.0.0.1:9090->9090/tcp prometheus
运行 docker start prometheus
启动服务
运行 docker stats prometheus
查看 prometheus 状态
运行 docker stop prometheus
停止服务
四、Prometheus基础概念
本章将介绍 Prometheus 一些基础概念,包括:
- 数据模型
- 四种 Metric Type
- 作业与实例
1、数据模型
Prometheus 存储的是时序数据, 即按照相同时序(相同的名字和标签),以时间维度存储连续的数据的集合。
时序索引
时序(time series) 是由名字(Metric),以及一组 key/value 标签定义的,具有相同的名字以及标签属于相同时序。
时序的名字由 ASCII 字符,数字,下划线,以及冒号组成,它必须满足正则表达式 [a-zA-Z_:][a-zA-Z0-9_:]*
, 其名字应该具有语义化,一般表示一个可以度量的指标,例如: http_requests_total
, 可以表示 http 请求的总数。
时序的标签可以使 Prometheus 的数据更加丰富,能够区分具体不同的实例,例如 http_requests_total{method="POST"}
可以表示所有 http 中的 POST 请求。
标签名称由 ASCII 字符,数字,以及下划线组成, 其中 __
开头属于 Prometheus 保留,标签的值可以是任何 Unicode 字符,支持中文。
时序样本
按照某个时序以时间维度采集的数据,称之为样本,其值包含:
- 一个 float64 值
- 一个毫秒级的 unix 时间戳
格式
Prometheus 时序格式与 OpenTSDB 相似:
{
其中包含时序名字以及时序的标签。
2、时序 4 种类型
Prometheus 时序数据分为 Counter, Gauge, Histogram, Summary 四种类型。
Counter
Counter 表示收集的数据是按照某个趋势(增加/减少)一直变化的,我们往往用它记录服务请求总量、错误总数等。
例如 Prometheus server 中 http_requests_total
, 表示 Prometheus 处理的 http 请求总数,我们可以使用 delta
, 很容易得到任意区间数据的增量,这个会在 PromQL 一节中细讲。
Gauge
Gauge 表示搜集的数据是一个瞬时的值,与时间没有关系,可以任意变高变低,往往可以用来记录内存使用率、磁盘使用率等。
例如 Prometheus server 中 go_goroutines
, 表示 Prometheus 当前 goroutines 的数量。
Histogram
Histogram 由
,
,
,
组成,主要用于表示一段时间范围内对数据进行采样(通常是请求持续时间或响应大小),并能够对其指定区间以及总数进行统计,通常它采集的数据展示为直方图。
例如 Prometheus server 中 prometheus_local_storage_series_chunks_persisted
, 表示 Prometheus 中每个时序需要存储的 chunks 数量,我们可以用它计算待持久化的数据的分位数。
Summary
Summary 和 Histogram 类似,由
,
,
组成,主要用于表示一段时间内数据采样结果(通常是请求持续时间或响应大小),它直接存储了 quantile 数据,而不是根据统计区间计算出来的。
例如 Prometheus server 中 prometheus_target_interval_length_seconds
。
Histogram vs Summary
- 都包含
,_sum _count - Histogram 需要通过
计算 quantile, 而 Summary 直接存储了 quantile 的值。_bucket
3、作业和实例
Prometheus 中,将任意一个独立的数据源(target)称之为实例(instance)。包含相同类型的实例的集合称之为作业(job)。 如下是一个含有四个重复实例的作业:
- 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)的标识,以便区分:
job: The configured job name that the target belongs to.
instance: The : part of the target's URL that was scraped.
如果其中任一标签已经在此前采集的数据中存在,那么将会根据 honor_labels
设置选项来决定新标签。详见官网解释: scrape configuration documentation
对每一个实例而言,Prometheus 按照以下时序来存储所采集的数据样本:
up{job="", instance=""}: 1 表示该实例正常工作
up{job="", instance=""}: 0 表示该实例故障
scrape_duration_seconds{job="", instance=""} 表示拉取数据的时间间隔
scrape_samples_post_metric_relabeling{job="", instance=""} 表示采用重定义标签(relabeling)操作后仍然剩余的样本数
scrape_samples_scraped{job="", instance=""} 表示从该数据源获取的样本数
其中 up
时序可以有效应用于监控该实例是否正常工作。
五、PromQL
本章将介绍 PromQL 基本用法、示例,并将其与 SQL 进行比较。
1、PromQL 基本使用
PromQL (Prometheus Query Language) 是 Prometheus 自己开发的数据查询 DSL 语言,语言表现力非常丰富,内置函数很多,在日常数据可视化以及rule 告警中都会使用到它。
在页面 http://localhost:9090/graph
中,输入下面的查询语句,查看结果,例如:
http_requests_total{code="200"}
字符串和数字
字符串: 在查询语句中,字符串往往作为查询条件 labels 的值,和 Golang 字符串语法一致,可以使用 ""
, ''
, 或者 ``
, 格式如:
"this is a string"
'these are unescaped: \n \\ \t'
`these are not unescaped: \n ' " \t`
正数,浮点数: 表达式中可以使用正数或浮点数,例如:
3
-2.4
查询结果类型
PromQL 查询结果主要有 3 种类型:
- 瞬时数据 (Instant vector): 包含一组时序,每个时序只有一个点,例如:
http_requests_total
- 区间数据 (Range vector): 包含一组时序,每个时序有多个点,例如:
http_requests_total[5m]
- 纯量数据 (Scalar): 纯量只有一个数字,没有时序,例如:
count(http_requests_total)
查询条件
Prometheus 存储的是时序数据,而它的时序是由名字和一组标签构成的,其实名字也可以写出标签的形式,例如 http_requests_total
等价于 {name="http_requests_total"}。
一个简单的查询相当于是对各种标签的筛选,例如:
http_requests_total{code="200"} // 表示查询名字为 http_requests_total,code 为 "200" 的数据
查询条件支持正则匹配,例如:
http_requests_total{code!="200"} // 表示查询 code 不为 "200" 的数据
http_requests_total{code=~"2.."} // 表示查询 code 为 "2xx" 的数据
http_requests_total{code!~"2.."} // 表示查询 code 不为 "2xx" 的数据
操作符
Prometheus 查询语句中,支持常见的各种表达式操作符,例如
算术运算符:
支持的算术运算符有 +,-,*,/,%,^
, 例如 http_requests_total * 2
表示将 http_requests_total 所有数据 double 一倍。
比较运算符:
支持的比较运算符有 ==,!=,>,<,>=,<=
, 例如 http_requests_total > 100
表示 http_requests_total 结果中大于 100 的数据。
逻辑运算符:
支持的逻辑运算符有 and,or,unless
, 例如 http_requests_total == 5 or http_requests_total == 2
表示 http_requests_total 结果中等于 5 或者 2 的数据。
聚合运算符:
支持的聚合运算符有 sum,min,max,avg,stddev,stdvar,count,count_values,bottomk,topk,quantile,
, 例如 max(http_requests_total)
表示 http_requests_total 结果中最大的数据。
注意,和四则运算类型,Prometheus 的运算符也有优先级,它们遵从(^)> (*, /, %) > (+, -) > (==, !=, <=, <, >=, >) > (and, unless) > (or) 的原则。
内置函数
Prometheus 内置不少函数,方便查询以及数据格式化,例如将结果由浮点数转为整数的 floor 和 ceil,
floor(avg(http_requests_total{code="200"}))
ceil(avg(http_requests_total{code="200"}))
查看 http_requests_total 5分钟内,平均每秒数据
rate(http_requests_total[5m])
更多请参见详情。
2、与 SQL 对比
下面将以 Prometheus server 收集的 http_requests_total
时序数据为例子展开对比。
MySQL 数据准备
mysql>
# 创建数据库
create database prometheus_practice;
use prometheus_practice;
# 创建 http_requests_total 表
CREATE TABLE http_requests_total (
code VARCHAR(256),
handler VARCHAR(256),
instance VARCHAR(256),
job VARCHAR(256),
method VARCHAR(256),
created_at DOUBLE NOT NULL,
value DOUBLE NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE http_requests_total ADD INDEX created_at_index (created_at);
# 初始化数据
# time at 2017/5/22 14:45:27
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "query_range", "localhost:9090", "prometheus", "get", 1495435527, 3);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("400", "query_range", "localhost:9090", "prometheus", "get", 1495435527, 5);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "prometheus", "localhost:9090", "prometheus", "get", 1495435527, 6418);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "static", "localhost:9090", "prometheus", "get", 1495435527, 9);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("304", "static", "localhost:9090", "prometheus", "get", 1495435527, 19);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "query", "localhost:9090", "prometheus", "get", 1495435527, 87);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("400", "query", "localhost:9090", "prometheus", "get", 1495435527, 26);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "graph", "localhost:9090", "prometheus", "get", 1495435527, 7);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "label_values", "localhost:9090", "prometheus", "get", 1495435527, 7);
# time at 2017/5/22 14:48:27
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "query_range", "localhost:9090", "prometheus", "get", 1495435707, 3);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("400", "query_range", "localhost:9090", "prometheus", "get", 1495435707, 5);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "prometheus", "localhost:9090", "prometheus", "get", 1495435707, 6418);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "static", "localhost:9090", "prometheus", "get", 1495435707, 9);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("304", "static", "localhost:9090", "prometheus", "get", 1495435707, 19);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "query", "localhost:9090", "prometheus", "get", 1495435707, 87);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("400", "query", "localhost:9090", "prometheus", "get", 1495435707, 26);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "graph", "localhost:9090", "prometheus", "get", 1495435707, 7);
INSERT INTO http_requests_total (code, handler, instance, job, method, created_at, value) values ("200", "label_values", "localhost:9090", "prometheus", "get", 1495435707, 7);
数据初始完成后,通过查询可以看到如下数据:
mysql>
mysql> select * from http_requests_total;
+------+--------------+----------------+------------+--------+------------+-------+
| code | handler | instance | job | method | created_at | value |
+------+--------------+----------------+------------+--------+------------+-------+
| 200 | query_range | localhost:9090 | prometheus | get | 1495435527 | 3 |
| 400 | query_range | localhost:9090 | prometheus | get | 1495435527 | 5 |
| 200 | prometheus | localhost:9090 | prometheus | get | 1495435527 | 6418 |
| 200 | static | localhost:9090 | prometheus | get | 1495435527 | 9 |
| 304 | static | localhost:9090 | prometheus | get | 1495435527 | 19 |
| 200 | query | localhost:9090 | prometheus | get | 1495435527 | 87 |
| 400 | query | localhost:9090 | prometheus | get | 1495435527 | 26 |
| 200 | graph | localhost:9090 | prometheus | get | 1495435527 | 7 |
| 200 | label_values | localhost:9090 | prometheus | get | 1495435527 | 7 |
| 200 | query_range | localhost:9090 | prometheus | get | 1495435707 | 3 |
| 400 | query_range | localhost:9090 | prometheus | get | 1495435707 | 5 |
| 200 | prometheus | localhost:9090 | prometheus | get | 1495435707 | 6418 |
| 200 | static | localhost:9090 | prometheus | get | 1495435707 | 9 |
| 304 | static | localhost:9090 | prometheus | get | 1495435707 | 19 |
| 200 | query | localhost:9090 | prometheus | get | 1495435707 | 87 |
| 400 | query | localhost:9090 | prometheus | get | 1495435707 | 26 |
| 200 | graph | localhost:9090 | prometheus | get | 1495435707 | 7 |
| 200 | label_values | localhost:9090 | prometheus | get | 1495435707 | 7 |
+------+--------------+----------------+------------+--------+------------+-------+
18 rows in set (0.00 sec)
基本查询对比
假设当前时间为 2017/5/22 14:48:30
- 查询当前所有数据
// PromQL
http_requests_total
// MySQL
SELECT * from http_requests_total WHERE created_at BETWEEN 1495435700 AND 1495435710;
我们查询 MySQL 数据的时候,需要将当前时间向前推一定间隔,比如这里的 10s (Prometheus 数据抓取间隔),这样才能确保查询到数据,而 PromQL 自动帮我们实现了这个逻辑。
- 条件查询
// PromQL
http_requests_total{code="200", handler="query"}
// MySQL
SELECT * from http_requests_total WHERE code="200" AND handler="query" AND created_at BETWEEN 1495435700 AND 1495435710;
- 模糊查询: code 为 2xx 的数据
// PromQL
http_requests_total{code~="2xx"}
// MySQL
SELECT * from http_requests_total WHERE code LIKE "%2%" AND created_at BETWEEN 1495435700 AND 1495435710;
- 比较查询: value 大于 100 的数据
// PromQL
http_requests_total > 100
// MySQL
SELECT * from http_requests_total WHERE value > 100 AND created_at BETWEEN 1495435700 AND 1495435710;
- 范围区间查询: 过去 5 分钟数据
// PromQL
http_requests_total[5m]
// MySQL
SELECT * from http_requests_total WHERE created_at BETWEEN 1495435410 AND 1495435710;
聚合, 统计高级查询
- count 查询: 统计当前记录总数
// PromQL
count(http_requests_total)
// MySQL
SELECT COUNT(*) from http_requests_total WHERE created_at BETWEEN 1495435700 AND 1495435710;
- sum 查询: 统计当前数据总值
// PromQL
sum(http_requests_total)
// MySQL
SELECT SUM(value) from http_requests_total WHERE created_at BETWEEN 1495435700 AND 1495435710;
- avg 查询: 统计当前数据平均值
// PromQL
avg(http_requests_total)
// MySQL
SELECT AVG(value) from http_requests_total WHERE created_at BETWEEN 1495435700 AND 1495435710;
- top 查询: 查询最靠前的 3 个值
// PromQL
topk(3, http_requests_total)
// MySQL
SELECT * from http_requests_total WHERE created_at BETWEEN 1495435700 AND 1495435710 ORDER BY value DESC LIMIT 3;
- irate 查询,过去 5 分钟平均每秒数值
// PromQL
irate(http_requests_total[5m])
// MySQL
SELECT code, handler, instance, job, method, SUM(value)/300 AS value from http_requests_total WHERE created_at BETWEEN 1495435700 AND 1495435710 GROUP BY code, handler, instance, job, method;
总结
通过以上一些示例可以看出,在常用查询和统计方面,PromQL 比 MySQL 简单和丰富很多,而且查询性能也高不少。
六、数据可视化
收集到数据只是第一步,如果没有很好做到数据可视化,有时很难发现问题。
本章将介绍使用 Prometheus 自带的 web console 以及 grafana 来查询和展现数据。
Prometheus Web
Prometheus 自带了 Web Console, 安装成功后可以访问 http://localhost:9090/graph
页面,用它可以进行任何 PromQL 查询和调试工作,非常方便,例如:
通过上图你不难发现,Prometheus 自带的 Web 界面比较简单,因为它的目的是为了及时查询数据,方便 PromeQL 调试。
它并不是像常见的 Admin Dashboard,在一个页面尽可能展示多的数据,如果你有这方面的需求,不妨试试 Grafana。
Grafana 使用
Grafana 是一套开源的分析监视平台,支持 Graphite, InfluxDB, OpenTSDB, Prometheus, Elasticsearch, CloudWatch 等数据源,其 UI 非常漂亮且高度定制化。
这是 Prometheus web console 不具备的,在上一节中我已经说明了选择它的原因。
版本说明
- Mac version 4.3.2
安装和运行程序
这里我使用 brew 安装,命令为
brew update
brew install grafana
当安装成功后,你可以使用默认配置启动程序
grafana-server -homepath /usr/local/Cellar/grafana/4.3.2/share/grafana/
如果顺利,你将看到如下日志
INFO[06-11|15:20:14] Starting Grafana logger=main version=4.3.2 commit=unknown-dev compiled=2017-06-01T05:47:48+0800
INFO[06-11|15:20:14] Config loaded from logger=settings file=/usr/local/Cellar/grafana/4.3.2/share/grafana/conf/defaults.ini
INFO[06-11|15:20:14] Path Home logger=settings path=/usr/local/Cellar/grafana/4.3.2/share/grafana/
INFO[06-11|15:20:14] Path Data logger=settings path=/usr/local/Cellar/grafana/4.3.2/share/grafana/data
INFO[06-11|15:20:14] Path Logs logger=settings path=/usr/local/Cellar/grafana/4.3.2/share/grafana/data/log
INFO[06-11|15:20:14] Path Plugins logger=settings path=/usr/local/Cellar/grafana/4.3.2/share/grafana/data/plugins
INFO[06-11|15:20:14] Initializing DB logger=sqlstore dbtype=sqlite3
INFO[06-11|15:20:14] Starting DB migration logger=migrator
INFO[06-11|15:20:14] Executing migration logger=migrator id="copy data account to org"
INFO[06-11|15:20:14] Skipping migration condition not fulfilled logger=migrator id="copy data account to org"
INFO[06-11|15:20:14] Executing migration logger=migrator id="copy data account_user to org_user"
INFO[06-11|15:20:14] Skipping migration condition not fulfilled logger=migrator id="copy data account_user to org_user"
INFO[06-11|15:20:14] Starting plugin search logger=plugins
INFO[06-11|15:20:14] Initializing Alerting logger=alerting.engine
INFO[06-11|15:20:14] Initializing CleanUpService logger=cleanup
INFO[06-11|15:20:14] Initializing Stream Manager
INFO[06-11|15:20:14] Initializing HTTP Server logger=http.server address=0.0.0.0:3000 protocol=http subUrl= socket=
此时,你可以打开页面 http://localhost:3000
, 访问 Grafana 的 web 界面。
其他平台安装方案,请参考更多安装。
登录并设置 Prometheus 数据源
Grafana 本身支持 Prometheus 数据源,故不需要安装其他插件。
使用默认账号 admin/admin 登录 grafana
在 Dashboard 首页,点击添加数据源
配置 Prometheus 数据源
目前为止,Grafana 已经和 Prometheus 连上了,你将看到这样的 Dashboard
自定义监视画板
由顶部 Manage dashboard
-> Settings
进入管理页面
在管理页面中取消 Hide Controls
点击页面底部 + ADD ROW
按钮, 并选择 Graph
类型
点击 Panel Title
-> Edit
进入 Panel 编辑页面,并在 Metrics
中 的 Metric lookup
选择 go_goroutines
你也可以直接在管理界面中填写 Prometheus 的查询语句,以及修改查询的 step 数值。
当你修改了 Dashboard 后,记得点击顶部的 Save dashboard
按钮,或直接 CTRL+S
保存。
至此,我们自定义的 Panel 已添加完成
我们可以通过拖拽,拉升调节 panel 的位置和尺寸,我们调节的目的是尽量在一个屏幕显示更多信息。
总结
Grafana 是一款非常漂亮,强大的监视分析平台,本身支持了 Prometheus 数据源,所以在做数据和监视可视化的时候,Grafana + Prometheus 是个不错的选择。
配置
Prometheus 启动的时候,可以加载运行参数 -config.file
指定配置文件,默认为 prometheus.yml
。
在配置文件中我们可以指定 global, alerting, rule_files, scrape_configs, remote_write, remote_read 等属性。
其代码结构体定义为:
// Config is the top-level configuration for Prometheus's config files.
type Config struct {
GlobalConfig GlobalConfig `yaml:"global"`
AlertingConfig AlertingConfig `yaml:"alerting,omitempty"`
RuleFiles []string `yaml:"rule_files,omitempty"`
ScrapeConfigs []*ScrapeConfig `yaml:"scrape_configs,omitempty"`
RemoteWriteConfigs []*RemoteWriteConfig `yaml:"remote_write,omitempty"`
RemoteReadConfigs []*RemoteReadConfig `yaml:"remote_read,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
// original is the input from which the config was parsed.
original string
}
配置文件结构大概为:
global:
# How frequently to scrape targets by default.
[ scrape_interval: | default = 1m ]
# How long until a scrape request times out.
[ scrape_timeout: | default = 10s ]
# How frequently to evaluate rules.
[ evaluation_interval: | default = 1m ]
# The labels to add to any time series or alerts when communicating with
# external systems (federation, remote storage, Alertmanager).
external_labels:
[ : ... ]
# Rule files specifies a list of globs. Rules and alerts are read from
# all matching files.
rule_files:
[ - ... ]
# A list of scrape configurations.
scrape_configs:
[ - ... ]
# Alerting specifies settings related to the Alertmanager.
alerting:
alert_relabel_configs:
[ - ... ]
alertmanagers:
[ - ... ]
# Settings related to the experimental remote write feature.
remote_write:
[ - ... ]
# Settings related to the experimental remote read feature.
remote_read:
[ - ... ]
全局配置
global
属于全局的默认配置,它主要包含 4 个属性,
- scrape_interval: 拉取 targets 的默认时间间隔。
- scrape_timeout: 拉取一个 target 的超时时间。
- evaluation_interval: 执行 rules 的时间间隔。
- external_labels: 额外的属性,会添加到拉取的数据并存到数据库中。
其代码结构体定义为:
// GlobalConfig configures values that are used across other configuration
// objects.
type GlobalConfig struct {
// How frequently to scrape targets by default.
ScrapeInterval model.Duration `yaml:"scrape_interval,omitempty"`
// The default timeout when scraping targets.
ScrapeTimeout model.Duration `yaml:"scrape_timeout,omitempty"`
// How frequently to evaluate rules by default.
EvaluationInterval model.Duration `yaml:"evaluation_interval,omitempty"`
// The labels to add to any timeseries that this Prometheus instance scrapes.
ExternalLabels model.LabelSet `yaml:"external_labels,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
配置文件结构大概为:
global:
scrape_interval: 15s # By default, scrape targets every 15 seconds.
evaluation_interval: 15s # By default, scrape targets every 15 seconds.
scrape_timeout: 10s # is set to the global default (10s).
# Attach these labels to any time series or alerts when communicating with
# external systems (federation, remote storage, Alertmanager).
external_labels:
monitor: 'codelab-monitor'
告警配置
通常我们可以使用运行参数 -alertmanager.xxx
来配置 Alertmanager, 但是这样不够灵活,没有办法做到动态更新加载,以及动态定义告警属性。
所以 alerting
配置主要用来解决这个问题,它能够更好的管理 Alertmanager, 主要包含 2 个参数:
- alert_relabel_configs: 动态修改 alert 属性的规则配置。
- alertmanagers: 用于动态发现 Alertmanager 的配置。
其代码结构体定义为:
// AlertingConfig configures alerting and alertmanager related configs.
type AlertingConfig struct {
AlertRelabelConfigs []*RelabelConfig `yaml:"alert_relabel_configs,omitempty"`
AlertmanagerConfigs []*AlertmanagerConfig `yaml:"alertmanagers,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
配置文件结构大概为:
# Alerting specifies settings related to the Alertmanager.
alerting:
alert_relabel_configs:
[ - ... ]
alertmanagers:
[ - ... ]
其中 alertmanagers 为 alertmanager_config 数组,而 alertmanager_config 的代码结构体为,
// AlertmanagerConfig configures how Alertmanagers can be discovered and communicated with.
type AlertmanagerConfig struct {
// We cannot do proper Go type embedding below as the parser will then parse
// values arbitrarily into the overflow maps of further-down types.
ServiceDiscoveryConfig ServiceDiscoveryConfig `yaml:",inline"`
HTTPClientConfig HTTPClientConfig `yaml:",inline"`
// The URL scheme to use when talking to Alertmanagers.
Scheme string `yaml:"scheme,omitempty"`
// Path prefix to add in front of the push endpoint path.
PathPrefix string `yaml:"path_prefix,omitempty"`
// The timeout used when sending alerts.
Timeout time.Duration `yaml:"timeout,omitempty"`
// List of Alertmanager relabel configurations.
RelabelConfigs []*RelabelConfig `yaml:"relabel_configs,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
配置文件结构大概为:
# Per-target Alertmanager timeout when pushing alerts.
[ timeout: | default = 10s ]
# Prefix for the HTTP path alerts are pushed to.
[ path_prefix: | default = / ]
# Configures the protocol scheme used for requests.
[ scheme: | default = http ]
# Sets the `Authorization` header on every request with the
# configured username and password.
basic_auth:
[ username: ]
[ password: ]
# Sets the `Authorization` header on every request with
# the configured bearer token. It is mutually exclusive with `bearer_token_file`.
[ bearer_token: ]
# Sets the `Authorization` header on every request with the bearer token
# read from the configured file. It is mutually exclusive with `bearer_token`.
[ bearer_token_file: /path/to/bearer/token/file ]
# Configures the scrape request's TLS settings.
tls_config:
[ ]
# Optional proxy URL.
[ proxy_url: ]
# List of Azure service discovery configurations.
azure_sd_configs:
[ - ... ]
# List of Consul service discovery configurations.
consul_sd_configs:
[ - ... ]
# List of DNS service discovery configurations.
dns_sd_configs:
[ - ... ]
# List of EC2 service discovery configurations.
ec2_sd_configs:
[ - ... ]
# List of file service discovery configurations.
file_sd_configs:
[ - ... ]
# List of GCE service discovery configurations.
gce_sd_configs:
[ - ... ]
# List of Kubernetes service discovery configurations.
kubernetes_sd_configs:
[ - ... ]
# List of Marathon service discovery configurations.
marathon_sd_configs:
[ - ... ]
# List of AirBnB's Nerve service discovery configurations.
nerve_sd_configs:
[ - ... ]
# List of Zookeeper Serverset service discovery configurations.
serverset_sd_configs:
[ - ... ]
# List of Triton service discovery configurations.
triton_sd_configs:
[ - ... ]
# List of labeled statically configured Alertmanagers.
static_configs:
[ - ... ]
# List of Alertmanager relabel configurations.
relabel_configs:
[ - ... ]
规则配置
rule_files
主要用于配置 rules 文件,它支持多个文件以及文件目录。
其代码结构定义为:
RuleFiles []string `yaml:"rule_files,omitempty"`
配置文件结构大致为:
rule_files: - "rules/node.rules" - "rules2/*.rules"
数据拉取配置
scrape_configs 主要用于配置拉取数据节点,每一个拉取配置主要包含以下参数:
- job_name:任务名称
- honor_labels: 用于解决拉取数据标签有冲突,当设置为 true, 以拉取数据为准,否则以服务配置为准
- params:数据拉取访问时带的请求参数
- scrape_interval: 拉取时间间隔
- scrape_timeout: 拉取超时时间
- metrics_path: 拉取节点的 metric 路径
- scheme: 拉取数据访问协议
- sample_limit: 存储的数据标签个数限制,如果超过限制,该数据将被忽略,不入存储;默认值为0,表示没有限制
- relabel_configs: 拉取数据重置标签配置
- metric_relabel_configs:metric 重置标签配置
其代码结构体定义为:
// ScrapeConfig configures a scraping unit for Prometheus.
type ScrapeConfig struct {
// The job name to which the job label is set by default.
JobName string `yaml:"job_name"`
// Indicator whether the scraped metrics should remain unmodified.
HonorLabels bool `yaml:"honor_labels,omitempty"`
// A set of query parameters with which the target is scraped.
Params url.Values `yaml:"params,omitempty"`
// How frequently to scrape the targets of this scrape config.
ScrapeInterval model.Duration `yaml:"scrape_interval,omitempty"`
// The timeout for scraping targets of this config.
ScrapeTimeout model.Duration `yaml:"scrape_timeout,omitempty"`
// The HTTP resource path on which to fetch metrics from targets.
MetricsPath string `yaml:"metrics_path,omitempty"`
// The URL scheme with which to fetch metrics from targets.
Scheme string `yaml:"scheme,omitempty"`
// More than this many samples post metric-relabelling will cause the scrape to fail.
SampleLimit uint `yaml:"sample_limit,omitempty"`
// We cannot do proper Go type embedding below as the parser will then parse
// values arbitrarily into the overflow maps of further-down types.
ServiceDiscoveryConfig ServiceDiscoveryConfig `yaml:",inline"`
HTTPClientConfig HTTPClientConfig `yaml:",inline"`
// List of target relabel configurations.
RelabelConfigs []*RelabelConfig `yaml:"relabel_configs,omitempty"`
// List of metric relabel configurations.
MetricRelabelConfigs []*RelabelConfig `yaml:"metric_relabel_configs,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
以上配置定义中还包含 ServiceDiscoveryConfig,它的代码定义为:
// ServiceDiscoveryConfig configures lists of different service discovery mechanisms.
type ServiceDiscoveryConfig struct {
// List of labeled target groups for this job.
StaticConfigs []*TargetGroup `yaml:"static_configs,omitempty"`
// List of DNS service discovery configurations.
DNSSDConfigs []*DNSSDConfig `yaml:"dns_sd_configs,omitempty"`
// List of file service discovery configurations.
FileSDConfigs []*FileSDConfig `yaml:"file_sd_configs,omitempty"`
// List of Consul service discovery configurations.
ConsulSDConfigs []*ConsulSDConfig `yaml:"consul_sd_configs,omitempty"`
// List of Serverset service discovery configurations.
ServersetSDConfigs []*ServersetSDConfig `yaml:"serverset_sd_configs,omitempty"`
// NerveSDConfigs is a list of Nerve service discovery configurations.
NerveSDConfigs []*NerveSDConfig `yaml:"nerve_sd_configs,omitempty"`
// MarathonSDConfigs is a list of Marathon service discovery configurations.
MarathonSDConfigs []*MarathonSDConfig `yaml:"marathon_sd_configs,omitempty"`
// List of Kubernetes service discovery configurations.
KubernetesSDConfigs []*KubernetesSDConfig `yaml:"kubernetes_sd_configs,omitempty"`
// List of GCE service discovery configurations.
GCESDConfigs []*GCESDConfig `yaml:"gce_sd_configs,omitempty"`
// List of EC2 service discovery configurations.
EC2SDConfigs []*EC2SDConfig `yaml:"ec2_sd_configs,omitempty"`
// List of OpenStack service discovery configurations.
OpenstackSDConfigs []*OpenstackSDConfig `yaml:"openstack_sd_configs,omitempty"`
// List of Azure service discovery configurations.
AzureSDConfigs []*AzureSDConfig `yaml:"azure_sd_configs,omitempty"`
// List of Triton service discovery configurations.
TritonSDConfigs []*TritonSDConfig `yaml:"triton_sd_configs,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
ServiceDiscoveryConfig 主要用于 target 发现,大体分为两类,静态配置和动态发现。
所以,一份完整的 scrape_configs 配置大致为:
# The job name assigned to scraped metrics by default. job_name:
# How frequently to scrape targets from this job. [ scrape_interval: | default = ] # Per-scrape timeout when scraping this job. [ scrape_timeout: | default = ] # The HTTP resource path on which to fetch metrics from targets. [ metrics_path: | default = /metrics ] # honor_labels controls how Prometheus handles conflicts between labels that are # already present in scraped data and labels that Prometheus would attach # server-side ("job" and "instance" labels, manually configured target # labels, and labels generated by service discovery implementations). # # If honor_labels is set to "true", label conflicts are resolved by keeping label # values from the scraped data and ignoring the conflicting server-side labels. # # If honor_labels is set to "false", label conflicts are resolved by renaming # conflicting labels in the scraped data to "exported_ " (for # example "exported_instance", "exported_job") and then attaching server-side # labels. This is useful for use cases such as federation, where all labels # specified in the target should be preserved. # # Note that any globally configured "external_labels" are unaffected by this # setting. In communication with external systems, they are always applied only # when a time series does not have a given label yet and are ignored otherwise. [ honor_labels: | default = false ] # Configures the protocol scheme used for requests. [ scheme: | default = http ] # Optional HTTP URL parameters. params: [ : [ , ...] ] # Sets the `Authorization` header on every scrape request with the # configured username and password. basic_auth: [ username: ] [ password: ] # Sets the `Authorization` header on every scrape request with # the configured bearer token. It is mutually exclusive with `bearer_token_file`. [ bearer_token: ] # Sets the `Authorization` header on every scrape request with the bearer token # read from the configured file. It is mutually exclusive with `bearer_token`. [ bearer_token_file: /path/to/bearer/token/file ] # Configures the scrape request's TLS settings. tls_config: [ ] # Optional proxy URL. [ proxy_url: ] # List of Azure service discovery configurations. azure_sd_configs: [ - ... ] # List of Consul service discovery configurations. consul_sd_configs: [ - ... ] # List of DNS service discovery configurations. dns_sd_configs: [ - ... ] # List of EC2 service discovery configurations. ec2_sd_configs: [ - ... ] # List of OpenStack service discovery configurations. openstack_sd_configs: [ - ... ] # List of file service discovery configurations. file_sd_configs: [ - ... ] # List of GCE service discovery configurations. gce_sd_configs: [ - ... ] # List of Kubernetes service discovery configurations. kubernetes_sd_configs: [ - ... ] # List of Marathon service discovery configurations. marathon_sd_configs: [ - ... ] # List of AirBnB's Nerve service discovery configurations. nerve_sd_configs: [ - ... ] # List of Zookeeper Serverset service discovery configurations. serverset_sd_configs: [ - ... ] # List of Triton service discovery configurations. triton_sd_configs: [ - ... ] # List of labeled statically configured targets for this job. static_configs: [ - ... ] # List of target relabel configurations. relabel_configs: [ - ... ] # List of metric relabel configurations. metric_relabel_configs: [ - ... ] # Per-scrape limit on number of scraped samples that will be accepted. # If more than this number of samples are present after metric relabelling # the entire scrape will be treated as failed. 0 means no limit. [ sample_limit: | default = 0 ]
远程可写存储
remote_write
主要用于可写远程存储配置,主要包含以下参数:
- url: 访问地址
- remote_timeout: 请求超时时间
- write_relabel_configs: 标签重置配置, 拉取到的数据,经过重置处理后,发送给远程存储
其代码结构体为:
// RemoteWriteConfig is the configuration for writing to remote storage.
type RemoteWriteConfig struct {
URL *URL `yaml:"url,omitempty"`
RemoteTimeout model.Duration `yaml:"remote_timeout,omitempty"`
WriteRelabelConfigs []*RelabelConfig `yaml:"write_relabel_configs,omitempty"`
// We cannot do proper Go type embedding below as the parser will then parse
// values arbitrarily into the overflow maps of further-down types.
HTTPClientConfig HTTPClientConfig `yaml:",inline"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
一份完整的配置大致为:
# The URL of the endpoint to send samples to.
url:
# Timeout for requests to the remote write endpoint.
[ remote_timeout: | default = 30s ]
# List of remote write relabel configurations.
write_relabel_configs:
[ - ... ]
# Sets the `Authorization` header on every remote write request with the
# configured username and password.
basic_auth:
[ username: ]
[ password: ]
# Sets the `Authorization` header on every remote write request with
# the configured bearer token. It is mutually exclusive with `bearer_token_file`.
[ bearer_token: ]
# Sets the `Authorization` header on every remote write request with the bearer token
# read from the configured file. It is mutually exclusive with `bearer_token`.
[ bearer_token_file: /path/to/bearer/token/file ]
# Configures the remote write request's TLS settings.
tls_config:
[ ]
# Optional proxy URL.
[ proxy_url: ]
注意: remote_write 属于试验阶段,慎用,因为在以后的版本中可能发生改变。
远程可读存储
remote_read
主要用于可读远程存储配置,主要包含以下参数:
- url: 访问地址
- remote_timeout: 请求超时时间
其代码结构体为:
// RemoteReadConfig is the configuration for reading from remote storage.
type RemoteReadConfig struct {
URL *URL `yaml:"url,omitempty"`
RemoteTimeout model.Duration `yaml:"remote_timeout,omitempty"`
// We cannot do proper Go type embedding below as the parser will then parse
// values arbitrarily into the overflow maps of further-down types.
HTTPClientConfig HTTPClientConfig `yaml:",inline"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
一份完整的配置大致为:
# The URL of the endpoint to query from.
url:
# Timeout for requests to the remote read endpoint.
[ remote_timeout: | default = 30s ]
# Sets the `Authorization` header on every remote read request with the
# configured username and password.
basic_auth:
[ username: ]
[ password: ]
# Sets the `Authorization` header on every remote read request with
# the configured bearer token. It is mutually exclusive with `bearer_token_file`.
[ bearer_token: ]
# Sets the `Authorization` header on every remote read request with the bearer token
# read from the configured file. It is mutually exclusive with `bearer_token`.
[ bearer_token_file: /path/to/bearer/token/file ]
# Configures the remote read request's TLS settings.
tls_config:
[ ]
# Optional proxy URL.
[ proxy_url: ]
注意: remote_read 属于试验阶段,慎用,因为在以后的版本中可能发生改变。
服务发现
在 Prometheus 的配置中,一个最重要的概念就是数据源 target,而数据源的配置主要分为静态配置和动态发现, 大致为以下几类:
- static_configs: 静态服务发现
- dns_sd_configs: DNS 服务发现
- file_sd_configs: 文件服务发现
- consul_sd_configs: Consul 服务发现
- serverset_sd_configs: Serverset 服务发现
- nerve_sd_configs: Nerve 服务发现
- marathon_sd_configs: Marathon 服务发现
- kubernetes_sd_configs: Kubernetes 服务发现
- gce_sd_configs: GCE 服务发现
- ec2_sd_configs: EC2 服务发现
- openstack_sd_configs: OpenStack 服务发现
- azure_sd_configs: Azure 服务发现
- triton_sd_configs: Triton 服务发现
它们具体使用以及配置模板,请参考服务发现配置模板。
它们中最重要的,也是使用最广泛的应该是 static_configs
, 其实那些动态类型都可以看成是某些通用业务使用静态服务封装的结果。
配置样例
Prometheus 的配置参数比较多,但是个人使用较多的是 global, rules, scrap_configs, statstic_config, rebel_config 等。
我平时使用的配置文件大致为这样:
global: scrape_interval: 15s # By default, scrape targets every 15 seconds. evaluation_interval: 15s # By default, scrape targets every 15 seconds. rule_files: - "rules/node.rules" scrape_configs: - job_name: 'prometheus' scrape_interval: 5s static_configs: - targets: ['localhost:9090'] - job_name: 'node' scrape_interval: 8s static_configs: - targets: ['127.0.0.1:9100', '127.0.0.12:9100'] - job_name: 'mysqld' static_configs: - targets: ['127.0.0.1:9104'] - job_name: 'memcached' static_configs: - targets: ['127.0.0.1:9150']
Exporter
在 Prometheus 中负责数据汇报的程序统一叫做 Exporter, 而不同的 Exporter 负责不同的业务。 它们具有统一命名格式,即 xx_exporter, 例如负责主机信息收集的 node_exporter。
Prometheus 社区已经提供了很多 exporter, 详情请参考这里 。
文本格式
在讨论 Exporter 之前,有必要先介绍一下 Prometheus 文本数据格式,因为一个 Exporter 本质上就是将收集的数据,转化为对应的文本格式,并提供 http 请求。
Exporter 收集的数据转化的文本内容以行 (\n
) 为单位,空行将被忽略, 文本内容最后一行为空行。
注释
文本内容,如果以 #
开头通常表示注释。
- 以
# HELP
开头表示 metric 帮助说明。 - 以
# TYPE
开头表示定义 metric 类型,包含counter
,gauge
,histogram
,summary
, 和untyped
类型。 - 其他表示一般注释,供阅读使用,将被 Prometheus 忽略。
采样数据
内容如果不以 #
开头,表示采样数据。它通常紧挨着类型定义行,满足以下格式:
metric_name [
"{" label_name "=" `"` label_value `"` { "," label_name "=" `"` label_value `"` } [ "," ] "}"
] value [ timestamp ]
下面是一个完整的例子:
# HELP http_requests_total The total number of HTTP requests.
# TYPE http_requests_total counter
http_requests_total{method="post",code="200"} 1027 1395066363000
http_requests_total{method="post",code="400"} 3 1395066363000
# Escaping in label values:
msdos_file_access_time_seconds{path="C:\\DIR\\FILE.TXT",error="Cannot find file:\n\"FILE.TXT\""} 1.458255915e9
# Minimalistic line:
metric_without_timestamp_and_labels 12.47
# A weird metric from before the epoch:
something_weird{problem="division by zero"} +Inf -3982045
# A histogram, which has a pretty complex representation in the text format:
# HELP http_request_duration_seconds A histogram of the request duration.
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.05"} 24054
http_request_duration_seconds_bucket{le="0.1"} 33444
http_request_duration_seconds_bucket{le="0.2"} 100392
http_request_duration_seconds_bucket{le="0.5"} 129389
http_request_duration_seconds_bucket{le="1"} 133988
http_request_duration_seconds_bucket{le="+Inf"} 144320
http_request_duration_seconds_sum 53423
http_request_duration_seconds_count 144320
# Finally a summary, which has a complex representation, too:
# HELP rpc_duration_seconds A summary of the RPC duration in seconds.
# TYPE rpc_duration_seconds summary
rpc_duration_seconds{quantile="0.01"} 3102
rpc_duration_seconds{quantile="0.05"} 3272
rpc_duration_seconds{quantile="0.5"} 4773
rpc_duration_seconds{quantile="0.9"} 9001
rpc_duration_seconds{quantile="0.99"} 76656
rpc_duration_seconds_sum 1.7560473e+07
rpc_duration_seconds_count 2693
需要特别注意的是,假设采样数据 metric 叫做 x
, 如果 x
是 histogram
或 summary
类型必需满足以下条件:
- 采样数据的总和应表示为
x_sum
。 - 采样数据的总量应表示为
x_count
。 summary
类型的采样数据的 quantile 应表示为x{quantile="y"}
。histogram
类型的采样分区统计数据将表示为x_bucket{le="y"}
。histogram
类型的采样必须包含x_bucket{le="+Inf"}
, 它的值等于x_count
的值。summary
和historam
中quantile
和le
必需按从小到大顺序排列。
Sample Exporter
既然一个 exporter 就是将收集的数据转化为文本格式,并提供 http 请求即可,那很容自己实现一个。
一个简单的 exporter
下面我将用 golang
实现一个简单的 sample_exporter
, 其代码大致为:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, exportData) } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) } var exportData string = `# HELP sample_http_requests_total The total number of HTTP requests. # TYPE sample_http_requests_total counter sample_http_requests_total{method="post",code="200"} 1027 1395066363000 sample_http_requests_total{method="post",code="400"} 3 1395066363000 # Escaping in label values: sample_msdos_file_access_time_seconds{path="C:\\DIR\\FILE.TXT",error="Cannot find file:\n\"FILE.TXT\""} 1.458255915e9 # Minimalistic line: sample_metric_without_timestamp_and_labels 12.47 # A histogram, which has a pretty complex representation in the text format: # HELP sample_http_request_duration_seconds A histogram of the request duration. # TYPE sample_http_request_duration_seconds histogram sample_http_request_duration_seconds_bucket{le="0.05"} 24054 sample_http_request_duration_seconds_bucket{le="0.1"} 33444 sample_http_request_duration_seconds_bucket{le="0.2"} 100392 sample_http_request_duration_seconds_bucket{le="0.5"} 129389 sample_http_request_duration_seconds_bucket{le="1"} 133988 sample_http_request_duration_seconds_bucket{le="+Inf"} 144320 sample_http_request_duration_seconds_sum 53423 sample_http_request_duration_seconds_count 144320 # Finally a summary, which has a complex representation, too: # HELP sample_rpc_duration_seconds A summary of the RPC duration in seconds. # TYPE sample_rpc_duration_seconds summary sample_rpc_duration_seconds{quantile="0.01"} 3102 sample_rpc_duration_seconds{quantile="0.05"} 3272 sample_rpc_duration_seconds{quantile="0.5"} 4773 sample_rpc_duration_seconds{quantile="0.9"} 9001 sample_rpc_duration_seconds{quantile="0.99"} 76656 sample_rpc_duration_seconds_sum 1.7560473e+07 sample_rpc_duration_seconds_count 2693 `
当运行此程序,你访问 http://localhost:8080/metrics
, 将看到这样的页面:
与 Prometheus 集成
我们可以利用 Prometheus 的 static_configs 来收集 sample_exporter
的数据。
打开 prometheus.yml
文件, 在 scrape_configs
中添加如下配置:
- job_name: "sample"
static_configs:
- targets: ["127.0.0.1:8080"]
重启加载配置,然后到 Prometheus Console 查询,你会看到 simple_exporter
的数据。