Prometheus(下文称Prom) 是由 SoundCloud 开源监控告警解决方案,与Kubernetes同属CNCF,它已经成为炙手可热的Kubernetes生态圈中的核心监控系统,越来越多的项目(如Kubernetes和etcd等 )都加入了丰富的Prometheus原生支持,
Prom提供了通用的数据模型和便捷的数据采集、存储和查询接口,同时基于Go实现也大大降低了服务端的运维成本,可以借助一些优秀的图形化工具(如Grafana)可以实现友好的图形化和报警。
Prometheus 存储的是时序数据, 即按照相同时序(相同的名字和标签),以时间维度存储连续的数据的集合。
时序的名字由 ASCII 字符,数字,下划线,以及冒号组成,它必须满足正则表达式
[a-zA-Z_:][a-zA-Z0-9_:]*,
其名字应该具有语义化,一般表示一个可以度量的指标,例如: http_requests_total, 可以表示 http 请求的总数。
时序的标签可以使 Prometheus 的数据更加丰富,能够区分具体不同的实例,例如:
http_requests_total{method="POST"} 可以表示所有 http 中的 POST 请求。
标签名称由 ASCII 字符,数字,以及下划线组成, 其中 __ 开头属于 Prometheus 保留,标签的值可以是任何 Unicode 字符,支持中文。
Prometheus 时序格式与 OpenTSDB 相似:
{
其中包含时序名字以及时序的标签。
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 由 _bucket{le=""},
_bucket{le="+Inf"}, _sum,_count组成,
例如 Prometheus server中prometheus_local_storage_series_chunks_persisted,
表示 Prometheus 中每个时序需要存储的 chunks 数量,我们可以用它计算待持久化的数据的分位数。
Summary
**主要用于表示一段时间内数据采样结果(通常是请求持续时间或响
应大小),它直接存储了 quantile 数据,而不是根据统计区间计算出来的。**
Summary 和 Histogram 类似,由 {quantile="<φ>"},_sum,
_count 组成,
例如 Prometheus server 中 prometheus_target_interval_length_seconds。
Histogram vs Summary
都包含 _sum,_count
Histogram 需要通过 _bucket 计算 quantile,
而 Summary 直接存储了 quantile 的值。
Prometheus 中,将任意一个独立的数据源(target)称之为实例(instance)。包含相同类型的实例的集合称之为作业(job)。
Prometheus 在采集数据的同时,会自动在时序的基础上添加标签,作为数据源(target)的标识,以便区分:
job: The configured job name that the target belongs to.
作业:设定该作业名属于哪个数据源
instance: The : part of the target's URL that was scraped.
例如:这个域名端口号是该数据源的url被剪切出的一部分
如果其中任一标签已经在此前采集的数据中存在,那么将会根据 honor_labels 设置选项来决定新标签。
对每一个实例而言,Prometheus 按照以下时序来存储所采集的数据样本:
* up{job="", instance=""}: 1 表示该实例正常工作
* up{job="", instance=""}: 0 表示该实例故障
* scrape_duration_seconds{job="", instance=""} 表示拉取数据的时间间隔
* scrape_samples_scraped{job="", instance=""} 表示从该数据源获取的样本数
* scrape_samples_post_metric_relabeling{job="", instance=""}
表示采用重定义标签(relabeling)操作后仍然剩余的样本数
其中 up 时序可以有效应用于监控该实例是否正常工作。
PromQL (Prometheus Query Language) 是 Prometheus 自己开发的数据查询 DSL 语言,语言表现力非常丰富,内置函数很多,在日常数据可视化以及rule 告警中都会使用到它。
在查询语句中,字符串往往作为查询条件labels(标签)的值,和Golang 字符串语法一致,可以使用 “”, ”, 或者 “ 。也可以使用正数或浮点数,
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" 的数据
查询条件支持正则匹配,
Prometheus 查询语句中,支持常见的各种表达式操作符。例如:
Prometheus 内置不少函数,方便查询以及数据格式化,详情参考内置函数
经过与MySql对比发现,常用查询和统计方面,PromQL 比 MySQL 简单和丰富很多,而且查询性能也高不少。
在 Prometheus 中负责数据汇报的程序统一叫做 Exporter, 而不同的 Exporter 负责不同的业务。 它们具有统一命名格式,即
xx_exporter, 例如负责主机信息收集的 node_exporter。
Prometheus 社区已经提供了很多 exporter, 详情请参考这里
文本内容,如果以 # 开头通常表示注释。
内容如果不以 # 开头,表示采样数据。它通常紧挨着类型定义行,满足以下格式:
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 类型必需满足以下条件:
Node_Exporter主要用于 *NIX 系统监控, 用 Golang 编写。
在下载页面中下载相应的二进制安装包。下载并解压成功后,我们可以使用 ./node_exporter -h 查看运行选项,./node_exporter 运行 Node Exporter, 如果看到类似输出,表示启动成功。
INFO[0000] Starting node_exporter (version=0.14.0, branch=master, revision=840ba5dcc71a084a3bc63cb6063003c1f94435a6) source="node_exporter.go:140"
INFO[0000] Build context (go=go1.7.5, user=root@bb6d0678e7f3, date=20170321-12:13:32) source="node_exporter.go:141"
INFO[0000] No directory specified, see --collector.textfile.directory source="textfile.go:57"
INFO[0000] Enabled collectors: source="node_exporter.go:160"
.....
INFO[0000] Listening on :9100 source="node_exporter.go:186"
我们可以利用 Prometheus 的static_configs来拉取 node_exporter 的数据。
打开 prometheus.yml 文件, 在 scrape_configs 中添加如下配置:
- job_name: "node"
static_configs:
- targets: ["127.0.0.1:9100"]
重启加载配置,然后到 Prometheus Console 查询,你会看到 node_exporter 的数据。
收集到 node_exporter 的数据后,我们可以使用 PromQL 进行一些业务查询和监控,下面是一些比较常见的查询。
注意:以下查询均以单个节点作为例子,如果大家想查看所有节点,将 instance=”xxx” 去掉即可。
CPU 使用率
100 - (avg by (instance) (irate(node_cpu{instance="xxx", mode="idle"}[5m])) * 100)
CPU 各 mode 占比率
avg by (instance, mode) (irate(node_cpu{instance="xxx"}[5m])) * 100
机器平均负载
node_load1{instance="xxx"} // 1分钟负载
node_load5{instance="xxx"} // 5分钟负载
node_load15{instance="xxx"} // 15分钟负载
内存使用率
100 - ((node_memory_MemFree{instance="xxx"}+node_memory_Cached{instance="xxx"}+node_memory_Buffers{instance="xxx"})/node_memory_MemTotal) * 100
磁盘使用率
100 - node_filesystem_free{instance="xxx",fstype!~"rootfs|selinuxfs|autofs|rpc_pipefs|tmpfs|udev|none|devpts|sysfs|debugfs|fuse.*"} / node_filesystem_size{instance="xxx",fstype!~"rootfs|selinuxfs|autofs|rpc_pipefs|tmpfs|udev|none|devpts|sysfs|debugfs|fuse.*"} * 100
网络 IO
// 上行带宽
sum by (instance) (irate(node_network_receive_bytes{instance="xxx",device!~"bond.*?|lo"}[5m])/128)
// 下行带宽
sum by (instance) (irate(node_network_transmit_bytes{instance="xxx",device!~"bond.*?|lo"}[5m])/128)
网卡出/入包
// 入包量
sum by (instance) (rate(node_network_receive_bytes{instance="xxx",device!="lo"}[5m]))
// 出包量
sum by (instance) (rate(node_network_transmit_bytes{instance="xxx",device!="lo"}[5m]))
prometheus的配置文件中比较重要的是prometheus.yml,文件大致内容如下:
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
#Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# 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=` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']
该配置文件中比较重要的是以下几个部分:
scrape__configs:主要用于配置拉取数据节点,每一个拉取配置主要包含以下参数:
relabel_configs: 拉取数据重置标签配置
上述例子中可以配置多中抓取任务,因此是一个列表,这里我们只有一个任务,那就是抓取 promethues 本身的 metrics。配置里面最重要的是 static_configs.targets,表示要抓取任务的 HTTP 地址,默认会在 /metrics url 出进行抓取,比如这里就是 http://localhost:9090/。 这是 prometheus 本身提供的监控数据,可以在浏览器中直接查看。
告警配置
通常我们可以使用运行参数 -alertmanager.xxx 来配置 Alertmanager, 但是这样不够灵活,没有办法做到动态更新加载,以及动态定义告警属性。
所以 alerting 配置主要用来解决这个问题,它能够更好的管理 Alertmanager, 主要包含 2 个参数:
我们先来看下采集器Collector接口的实现
type Collector interface {
// 用于传递所有可能的指标的定义描述符
// 可以在程序运行期间添加新的描述,收集新的指标信息
// 重复的描述符将被忽略。两个不同的Collector不要设置相同的描述符
Describe(chan<- *Desc)
// Prometheus的注册器调用Collect执行实际的抓取参数的工作,
// 并将收集的数据传递到Channel中返回
// 收集的指标信息来自于Describe中传递,可以并发的执行抓取工作,但是必须要保证线程的安全。
Collect(chan<- Metric)
}
有很多数据类型实现了这个接口
type Gauge interface {
Metric
Collector
// Set将标尺设置为任意值。
Set(float64)
//Inc将测量值增加1。
Inc()
// dec将测量值减1
Dec()
// Add adds the given value to the Gauge. (The value can be
// negative, resulting in a decrease of the Gauge.)
Add(float64)
// Sub subtracts the given value from the Gauge. (The value can be
// negative, resulting in an increase of the Gauge.)
Sub(float64)
}
type Counter interface {
Metric
Collector
// Set is used to set the Counter to an arbitrary value. It is only used
// if you have to transfer a value from an external counter into this
// Prometheus metric. Do not use it for regular handling of a
// Prometheus counter (as it can be used to break the contract of
// monotonically increasing values).
//
// Deprecated: Use NewConstMetric to create a counter for an external
// value. A Counter should never be set.
Set(float64)
// Inc increments the counter by 1.
Inc()
// Add adds the given value to the counter. It panics if the value is <
// 0.
Add(float64)
}
type Histogram interface {
Metric
Collector
// Observe adds a single observation to the histogram.
Observe(float64)
}
type Summary interface {
Metric
Collector
// Observe adds a single observation to the summary.
Observe(float64)
}
…
..
.
..
…
详细过程参见collector源码解析