Prometheus2.0 从入门到放弃

96
上弦月Tt
2017.12.15 09:34* 字数 3278

Prometheus 是什么?

Prometheus是一套开源的监控&报警&时间序列数据库的组合,起始是由SoundCloud公司开发的。随着发展,越来越多公司和组织接受采用Prometheus,社区也十分活跃,他们便将它独立成开源项目,并且有公司来运作。google SRE的书内也曾提到跟他们BorgMon监控系统相似的实现是Prometheus。现在最常见的Kubernetes容器管理系统中,通常会搭配Prometheus进行监控。

Prometheus 的优点

  • 非常少的外部依赖,安装使用超简单
  • 已经有非常多的系统集成 例如:docker HAProxy Nginx JMX等等
  • 服务自动化发现
  • 直接集成到代码
  • 设计思想是按照分布式、微服务架构来实现的

Prometheus 的特性

  • 自定义多维度的数据模型
  • 非常高效的存储 平均一个采样数据占 ~3.5 bytes左右,320万的时间序列,每30秒采样,保持60天,消耗磁盘大概228G。
  • 强大的查询语句
  • 轻松实现数据可视化

架构图

prometheus 架构图

组件介绍

Prometheus生态系统由多个组件组成。其中许多组件都是可选的

Promethus server
  • 必须安装,
  • 本质是一个时序数据库
  • 主要负责数据pull、存储、分析
Push Gateway
  • 非必选项
  • 支持临时性Job主动推送指标的中间网关
exporters
  • 部署在客户端的agent,如 node_exporte, mysql_exporter等
alertmanager
  • 用来进行报警,Promethus server 经过分析, 把出发的警报发送给 alertmanager 组件,alertmanager 组件通过自身的规则,来发送通知,(邮件,或者webhook)

接下来就是实战啦

本章内容
mkdir /opt/monitor/ -p
cd /opt/monitor/
wget https://github.com/x82423990/prometheus/archive/v2.0.0.tar.gz
tar xf v2.0.0.tar.gz
cd prometheus-2.0.linux-amd64
# 运行
# ./prometheus &

通过启动日志,可以看到 Prometheus Server 默认端口是 9090。

当 Prometheus 启动后,你可以通过浏览器来访问

http://IP:9090,将看到如下页面

在默认配置中,我们已经添加了 Prometheus Server 的监控,所以我们现在可以使用PromQL(Prometheus Query Language)来查看,比如

Prometheus Query Language

so , 既然他是一个数据库, 我们来简单的了解他的数据结构及他的数据模型

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 相似:

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

其中包含时序名字以及时序的标签。

作业和实例

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 <host>:<port> part of the target's URL that was scraped.

如果其中任一标签已经在此前采集的数据中存在,那么将会根据 honor_labels 设置选项来决定新标签。详见官网解释: scrape configuration documentation

对每一个实例而言,prometheus 按照以下时序来存储所采集的数据样本:

up{job="<job-name>", instance="<instance-id>"}: 1 表示该实例正常工作
up{job="<job-name>", instance="<instance-id>"}: 0 表示该实例故障

scrape_duration_seconds{job="<job-name>", instance="<instance-id>"} 表示拉取数据的时间间隔

scrape_samples_post_metric_relabeling{job="<job-name>", instance="<instance-id>"} 表示采用重定义标签(relabeling)操作后仍然剩余的样本数

scrape_samples_scraped{job="<job-name>", instance="<instance-id>"}  表示从该数据源获取的样本数

其中 up 时序可以有效应用于监控该实例是否正常工作。


时序 4 种类型

Prometheus 时序数据分为 Counter, Gauge, Histogram, Summary 四种类型。

Counter

Counter 表示收集的数据是按照某个趋势(增加/减少)一直变化的,我们往往用它记录服务请求总量,错误总数等。

例如 Prometheus server 中 http_requests_total, 表示 Prometheus 处理的 http 请求总数,可以使用 delta, 很容易得到任意区间数据的增量。

Gauge

Gauge 表示搜集的数据是一个瞬时的,与时间没有关系,可以任意变高变低,往往可以用来记录内存使用率、磁盘使用率等。

例如 Prometheus server 中 go_goroutines, 表示 Prometheus 当前 goroutines 的数量。

Histogram

Histogram 由 <basename>_bucket{le="<upper inclusive bound>"}<basename>_bucket{le="+Inf"}, <basename>_sum<basename>_count 组成,主要用于表示一段时间范围内对数据进行采样,(通常是请求持续时间或响应大小),并能够对其指定区间以及总数进行统计,通常我们用它计算分位数的直方图。

例如 Prometheus server 中 prometheus_local_storage_series_chunks_persisted, 表示 Prometheus 中每个时序需要存储的 chunks 数量,我们可以用它计算待持久化的数据的分位数。

Summary

Summary 和 Histogram 类似,由 <basename>{quantile="<φ>"}<basename>_sum<basename>_count 组成,主要用于表示一段时间内数据采样结果,(通常是请求持续时间或响应大小),它直接存储了 quantile 数据,而不是根据统计区间计算出来的。

例如 Prometheus server 中 prometheus_target_interval_length_seconds

Histogram vs Summary

  • 都包含 <basename>_sum<basename>_count
  • Histogram 需要通过 <basename>_bucket 计算 quantile, 而 Summary 直接存储了 quantile 的值。

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])

更多请参见详情


与 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

Prometheus 自带了 Web Console, 安装成功后可以访问 http://localhost:9090/graph 页面,用它可以进行任何 PromQL 查询和调试工作,非常方便,例如:

prometheus web console
prometheus web graph

通过上图你不难发现,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

grafana-login

在 Dashboard 首页,点击添加数据源

grafana-datasource

配置 Prometheus 数据源

grafana-prometheus-data-source

目前为止,Grafana 已经和 Prometheus 连上了,你将看到这样的 Dashboard

grafana-default-dashbord

自定义监视画板

由顶部 Manage dashboard -> Settings 进入管理页面

rafana-into-manage-dashboard

在管理页面中取消 Hide Controls

grafana-hide-controls

点击页面底部 + ADD ROW 按钮, 并选择 Graph 类型

grafana-add-graph

点击 Panel Title -> Edit 进入 Panel 编辑页面,并在 Metrics
Metric lookup 选择 go_goroutines

grafana-edit-panel

你也可以直接在管理界面中填写 Prometheus 的查询语句,以及修改查询的 step 数值。

当你修改了 Dashboard 后,记得点击顶部的 Save dashboard 按钮,或直接 CTRL+S 保存。

至此,我们自定义的 Panel 已添加完成

grafana-added-panel

我们可以通过拖拽,拉升调节 panel 的位置和尺寸,我们调节的目的是尽量在一个屏幕显示更多信息。

总结

Grafana 是一款非常漂亮,强大的监视分析平台,本身支持了 Prometheus 数据源,所以在做数据和监视可视化的时候,Grafana + Prometheus 是个不错的选择。


全局配置

global 属于全局的默认配置,它主要包含 4 个属性,

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

配置文件结构大概为:

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'
Prometheus
Web note ad 1