开箱即用~ 神器 Prometheus 如何深入浅出?
来源:本文转自公众号腾讯云开发者
链接:https://mp.weixin.qq.com/s?__biz=MzI2NDU4OTExOQ==&mid=2247537945&idx=1&sn=d51d07540f96441cd96ee708480c4646&scene=21&token=1175690808&lang=zh_CN#wechat_redirect
一、简介
二、整体生态
(二)指标抓取
Pull 模型:监控服务主动拉取被监控服务的指标。
Push模型:被监控服务主动将指标推送到监控服务,可能需要对指标做协议适配,必须得符合监控服务要求的指标格式。
对于 Prometheus 中的指标抓取,采用的是 Pull 模型,默认是一分钟去拉取一次指标,通过 Prometheus.yaml 配置文件中的
scrape_interval
配置项配置,Prometheus 对外都是用的 Pull 模型,一个是 Pull Exporter 的暴露的指标,一个是 Pull PushGateway 暴露的指标。(三)指标存储和查询
(四)监控告警
三、工作原理
(一)服务注册
scrape_configs
配置下:scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
- job_name: “node_export_consul”
metrics_path: /node_metrics
scheme: http
consul_sd_configs:
- server: localhost:8500
services:
- node_exporter
node_exporter
,在这个服务下有一个exporter实例:localhost:9600。注意:如果是动态注册,最好加上这两配置,静态注册指标拉取的路径会默认的帮我们指定为 metrics_path:/metrics
,所以如果暴露的指标抓取路径不同或者是动态的服务注册,最好加上这两个配置。不然会报错“INVALID“ is not a valid start token,演示下,百度了一下,这里可能是数据格式不统一导致。
metrics_path: /node_metrics
scheme: http
<azure_sd_config>
<consul_sd_config>
<digitalocean_sd_config>
<docker_sd_config>
<dockerswarm_sd_config>
<dns_sd_config>
<ec2_sd_config>
<openstack_sd_config>
<file_sd_config>
<gce_sd_config>
<hetzner_sd_config>
<http_sd_config>
<kubernetes_sd_config>
<kuma_sd_config>
<lightsail_sd_config>
<linode_sd_config>
<marathon_sd_config>
<nerve_sd_config>
<serverset_sd_config>
<triton_sd_config>
<eureka_sd_config>
<scaleway_sd_config>
<static_config>
(二)配置更新
--web.enable-lifecycle
prometheus --config.file=/usr/local/etc/prometheus.yml --web.enable-lifecycle
curl -v --request POST 'http://localhost:9090/-/reload'
if o.EnableLifecycle {
router.Post("/-/quit", h.quit)
router.Put("/-/quit", h.quit)
router.Post("/-/reload", h.reload) // reload配置
router.Put("/-/reload", h.reload)
}
h.reload
这个handler
方法实现:这个handler
就是往一个channle中发送一个信号:func (h *Handler) reload(w http.ResponseWriter, r *http.Request) {
rc := make(chan error)
h.reloadCh <- rc // 发送一个信号到channe了中
if err := <-rc; err != nil {
http.Error(w, fmt.Sprintf("failed to reload config: %s", err), http.StatusInternalServerError)
}
}
在 main 函数中会去监听这个 channel,只要有监听到信号,就会做配置的 reload,重新将新配置加载到内存中 case rc := <-webHandler.Reload():
if err := reloadConfig(cfg.configFile, cfg.enableExpandExternalLabels, cfg.tsdb.EnableExemplarStorage, logger, noStepSubqueryInterval, reloaders...); err != nil {
level.Error(logger).Log("msg", "Error reloading config", "err", err)
rc <- err
} else {
rc <- nil
}
(三)指标抓取和存储
global:
scrape_interval: 15s
四、Metric指标
(一)数据模型
指标名和指标标签集合:metric_name{ 时间戳:描述当前时间序列的时间,单位:毫秒。 样本值:当前监控指标的具体数值,比如 http_request_total
的值就是请求数是多少。
# HELP // HELP:这里描述的指标的信息,表示这个是一个什么指标,统计什么的
# TYPE // TYPE:这个指标是什么类型的
<metric name>{<label name>=<label value>, ...} value // 指标的具体格式,<指标名>{标签集合} 指标值
(二)指标模型
histogram_quantile
在 Prometheus 服务端计算求出。(三)指标导出
github.com/prometheus/client_golang/prometheus/promhttp
package main
import (
“net/http”
“github.com/prometheus/client_golang/prometheus/promhttp”
)
func main() {
http.Handle(“/metrics”, promhttp.Handler())
http.ListenAndServe(“:8080”, nil)
}
package main
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
// 1.定义指标(类型,名字,帮助信息)
myCounter := prometheus.NewCounter(prometheus.CounterOpts{
Name: "my_counter_total",
Help: "自定义counter",
})
// 2.注册指标
prometheus.MustRegister(myCounter)
// 3.设置指标值
myCounter.Add(23)
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", nil)
}
package main
import (
"fmt"
"net/http"
"github.com/prometheus/client_golang/prometheus"
)
var (
MyCounter prometheus.Counter
)
// init 注册指标
func init() {
// 1.定义指标(类型,名字,帮助信息)
MyCounter = prometheus.NewCounter(prometheus.CounterOpts{
Name: "my_counter_total",
Help: "自定义counter",
})
// 2.注册指标
prometheus.MustRegister(MyCounter)
}
// Sayhello
func Sayhello(w http.ResponseWriter, r *http.Request) {
// 接口请求量递增
MyCounter.Inc()
fmt.Fprintf(w, "Hello Wrold!")
}
package main
import (
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/counter",Sayhello)
http.ListenAndServe(":8080", nil)
}
一开始启动时,指标 counter 是 0
var (
MyCounter prometheus.Counter
MyGauge prometheus.Gauge
MyHistogram prometheus.Histogram
MySummary prometheus.Summary
)
// init 注册指标
func init() {
// 1.定义指标(类型,名字,帮助信息)
MyCounter = prometheus.NewCounter(prometheus.CounterOpts{
Name: "my_counter_total",
Help: "自定义counter",
})
// 定义gauge类型指标
MyGauge = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "my_gauge_num",
Help: "自定义gauge",
})
// 定义histogram
MyHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "my_histogram_bucket",
Help: "自定义histogram",
Buckets: []float64{0.1,0.2,0.3,0.4,0.5}, // 需要指定桶
})
// 定义Summary
MySummary = prometheus.NewSummary(prometheus.SummaryOpts{
Name: "my_summary_bucket",
Help: "自定义summary",
// 这部分可以算好后在set
Objectives: map[float64]float64{
0.5: 0.05,
0.9: 0.01,
0.99: 0.001,
},
})
// 2.注册指标
prometheus.MustRegister(MyCounter)
prometheus.MustRegister(MyGauge)
prometheus.MustRegister(MyHistogram)
prometheus.MustRegister(MySummary)
}
MyCounter *prometheus.CounterVec
// 1.定义指标(类型,名字,帮助信息)
MyCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: “my_counter_total”,
Help: “自定义counter”,
},
// 标签集合
[]string{“label1”,”label2”},
)
// 带标签的set指标值
MyCounter.With(prometheus.Labels{“label1”:”1”,”label2”:”2”}).Inc()
五、PromQL
字符串:只作为某些内置函数的参数出现; 标量:单一的数字值,可以是函数参数,也可以是函数的返回结果; 瞬时向量:某一时刻的时序数据; 区间向量:某一时间区间内的时序数据集合。
(一)瞬时查询
go_gc_duration_seconds_count
go_gc_duration_seconds_count{instance="127.0.0.1:9600"}
go_gc_duration_seconds_count{instance=~"localhost.*"}
(二)范围查询
go_gc_duration_seconds_count{}[5m]
d:天,h:小时,m:分钟,ms:毫秒,s:秒,w:周,y:年
go_gc_duration_seconds_count{}[5m] offset 1d
(三)内置函数
rate函数=时间区间前后两个点的差 / 时间范围
时间区间内最后两个样本点的差 / 最后两个样本点的时间差
rate(demo_api_request_duration_seconds_count{job="demo", method="GET", status="200"}[5m])
sum(rate(demo_api_request_duration_seconds_count{job="demo", method="GET", status="200"}[5m])) by(path)
sum(rate(demo_api_request_duration_seconds_count{job="demo", method="GET", status="200"}[5m])) without(path)
可以通过 histogram_quantile
函数做数据统计:可以用来统计百分位数:第一个参数是百分位,第二个histogram指标,这样计算出来的就是中位数,即P50
histogram_quantile(0.5,go_gc_pauses_seconds_total_bucket)
分享之前和同事一起发现的坑:
MyHistogram.Observe(0.3)
MyHistogram.Observe(0.4)
MyHistogram.Observe(0.5)
MyHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "my_histogram_bucket",
Help: "自定义histogram",
Buckets: []float64{0,2.5,5,7.5,10}, // 需要指定桶
})
histogram_quantile
函数计算下:计算结果是1.25,其实已经不对了。histogram_quantile(0.5,my_histogram_bucket_bucket)
histogram_quantile(0.99,my_histogram_bucket_bucket)
// 定义histogram
MyHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "my_histogram_bucket",
Help: "自定义histogram",
Buckets: []float64{0.1,0.2,0.3,0.4,0.5}, // 需要指定桶
})
MyHistogram.Observe(0.1)
MyHistogram.Observe(0.3)
MyHistogram.Observe(0.4)
六、Grafana可视化
七、监控告警
Http_srv
这个服务挂了,Prometheus采集不到指标,并且持续时间1分钟,就会触发告警groups:
- name: simulator-alert-rule
rules:
- alert: HttpSimulatorDown
expr: sum(up{job="http_srv"}) == 0
for: 1m
labels:
severity: critical
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets: ['localhost:9093']
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
- "alert_rules.yml"
#- "first_rules.yml"
global:
smtp_smarthost: 'smtp.qq.com:465'
smtp_from: '[email protected]'
smtp_auth_username: '[email protected]'
smtp_auth_password: 'xxxx'
smtp_require_tls: false
route:
group_interval: 1m
repeat_interval: 1m
receiver: 'mail-receiver'
# group_by //采用哪个标签作为分组
# group_wait //分组等待的时间,收到报警不是立马发送出去,而是等待一段时间,看看同一组中是否有其他报警,如果有一并发送
# group_interval //告警时间间隔
# repeat_interval //重复告警时间间隔,可以减少发送告警的频率
# receiver //接收者是谁
# routes //子路由配置
receivers:
- name: 'mail-receiver'
email_configs:
- to: '[email protected]'
END
官方站点:www.linuxprobe.com
Linux命令大全:www.linuxcool.com
刘遄老师QQ:5604215
Linux技术交流群:2636170
(新群,火热加群中……)
想要学习Linux系统的读者可以点击"阅读原文"按钮来了解书籍《Linux就该这么学》,同时也非常适合专业的运维人员阅读,成为辅助您工作的高价值工具书!
微信扫码关注该文公众号作者
戳这里提交新闻线索和高质量文章给我们。
来源: qq
点击查看作者最近其他文章