4、指标采集与Prometheus:从架构到告警,一套完整的可观测体系
大家好,我是你们的运维老司机。今天咱们聊聊监控体系里最核心的一环——Prometheus。说实话,我入行那会儿,监控还是Zabbix和Nagios的天下,后来Prometheus一出来,整个生态都变了。它不只是一个工具,更像是一套哲学:拉模式采集、多维数据模型、强大的PromQL查询语言……嗯,今天我就把这些年踩过的坑、总结的经验,一次性讲透。
4.1 Prometheus架构与核心概念
先看一张图,这是我手绘的Prometheus核心架构,你品,你细品。
这张图我画了好几个版本,最终定稿成这样。你看,Prometheus Server是绝对的核心,它通过HTTP pull的方式从各种Exporter拉取指标。为什么是拉而不是推?我个人觉得,拉模式让监控系统掌握了主动权——你想什么时候采集、采集哪些目标,都由Server说了算。这在微服务架构里特别重要,你想想看,如果每个服务都主动推数据,万一某个服务挂了,数据就丢了。而拉模式,Server发现目标挂了,还能记录一条"目标不可达"的告警。
4.2 Exporter开发与使用
Exporter说白了就是一个HTTP服务,暴露/metrics端点,返回Prometheus能识别的文本格式数据。我最早接触Exporter是在监控MySQL的时候,用官方的mysqld_exporter,部署起来很简单:
# 下载并启动MySQL Exporter
wget https://github.com/prometheus/mysqld_exporter/releases/download/v0.14.0/mysqld_exporter-0.14.0.linux-amd64.tar.gz
tar xzf mysqld_exporter-0.14.0.linux-amd64.tar.gz
cd mysqld_exporter-0.14.0.linux-amd64
# 配置数据库连接信息
export DATA_SOURCE_NAME='user:password@tcp(localhost:3306)/'
# 启动
./mysqld_exporter --web.listen-address=:9104
启动后访问http://localhost:9104/metrics,你会看到类似这样的输出:
# HELP mysql_global_status_connections MySQL global status connections
# TYPE mysql_global_status_connections gauge
mysql_global_status_connections 1234
# HELP mysql_global_status_threads_connected MySQL global status threads connected
# TYPE mysql_global_status_threads_connected gauge
mysql_global_status_threads_connected 45
嗯,这里要注意,Exporter的指标命名是有规范的。我见过不少团队自己写Exporter,命名随意得很,什么my_app_cpu_usage、my_app_mem……这其实不好。Prometheus官方推荐用namespace_subsystem_metricname的格式,比如mysql_global_status_connections。这样在PromQL里查询时,可以方便地用正则匹配。
4.2.1 自定义Exporter示例
下面是一个用Go写的简单Exporter,监控一个假想的"订单处理延迟"指标:
package main
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
orderLatency = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "order_processing_latency_seconds",
Help: "Current order processing latency in seconds",
})
)
func init() {
prometheus.MustRegister(orderLatency)
}
func main() {
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", nil)
}
这段代码虽然简单,但已经是一个完整的Exporter了。你只需要在业务逻辑里更新orderLatency的值,Prometheus就能拉取到。
4.3 PromQL查询语言入门
PromQL是Prometheus的灵魂。说实话,刚开始学PromQL的时候,我觉得它有点反人类——没有SQL那种直观的SELECT FROM WHERE。但用久了你会发现,它其实是为时序数据量身定做的。
先看几个最常用的查询:
| 查询目的 | PromQL表达式 | 说明 |
|---|---|---|
| 查看当前CPU使用率 | 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) |
rate函数计算每秒增长率,5m是时间窗口 |
| 查看内存使用量 | node_memory_MemTotal_bytes - node_memory_MemFree_bytes - node_memory_Buffers_bytes - node_memory_Cached_bytes |
直接做减法,单位是字节 |
| 查看过去1小时的平均延迟 | avg_over_time(http_request_duration_seconds[1h]) |
avg_over_time是聚合函数 |
| 查看错误率超过5%的服务 | rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05 |
用正则匹配5xx状态码 |
你可能会问,为什么CPU使用率要这么复杂?直接看node_cpu_seconds_total不行吗?嗯,这里有个关键点——node_cpu_seconds_total是一个Counter类型的指标,它只会不断增加。要得到"每秒使用率",必须用rate()函数计算变化率。这是PromQL里最容易犯错的地方,我刚开始也搞混过。
4.4 告警规则配置(Alertmanager)
告警是监控的最终目的。Prometheus的告警分为两步:先在Prometheus Server里定义告警规则,再由Alertmanager负责发送通知。
告警规则的写法是这样的:
groups:
- name: example-alerts
rules:
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: critical
annotations:
summary: "Instance {{ $labels.instance }} CPU usage is high"
description: "CPU usage is {{ $value }}% for more than 5 minutes"
这里有几个关键字段:
- expr:PromQL表达式,当结果为true时触发告警
- for:持续时间,比如持续5分钟才告警,避免抖动
- labels:告警标签,可以用来区分严重级别
- annotations:告警描述,会发送到通知渠道
Alertmanager的配置稍微复杂一些,它负责路由、分组、去重和静默。我一般这样配置:
route:
receiver: 'default-receiver'
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- match:
severity: critical
receiver: 'critical-receiver'
receivers:
- name: 'default-receiver'
webhook_configs:
- url: 'http://webhook.example.com/alert'
- name: 'critical-receiver'
webhook_configs:
- url: 'http://webhook.example.com/critical-alert'
repeat_interval设得太短,比如30分钟。结果某个晚上一个告警重复发送了十几次,值班同事差点把我拉黑。建议至少设4小时以上,除非是P0级别的告警。另外,group_interval也不要太短,否则告警风暴会让你怀疑人生。
最后说一句,告警规则不是越多越好。我见过有些团队配置了上百条告警规则,结果每天告警几百条,最后大家都麻木了。我的建议是:先配最核心的5-10条,比如CPU、内存、磁盘、关键业务接口的延迟和错误率。等稳定了再逐步增加。告警是手段,不是目的——让系统稳定运行才是。
好了,这一章的内容就到这里。Prometheus的生态很庞大,今天我们只讲了最核心的部分。下一章我们会深入Grafana的可视化配置,到时候见。