简介:Prometheus作为开源监控与报警系统,是运维和DevOps工程师常用的时序数据监控方案。这份学习资料面向具备基础Linux操作能力的技术人员,系统讲解时间序列数据模型、PromQL查询语法、Prometheus架构组成,以及它与传统数据库和其他监控系统的适用场景对比。内容从环境准备入手,完整演示Prometheus服务安装、配置prometheus.yml,再通过node_exporter监控远程Linux主机,通过mysqld_exporter采集MySQL性能指标,并进一步集成Grafana绘制CPU负载、MySQL监控图表,最后使用Alertmanager配置报警规则和通知策略,形成一套可落地的监控全链路。资源以单个PDF形式提供,压缩包仅2.95MB,适合按章节循序渐进学习,目前已吸引633人学习下载。学完可独立完成监控平台部署、自定义指标定义、报警规则设置与仪表盘构建,达成实时监控和故障排查的目标。
1. Prometheus 从入门到精通:这套监控体系到底在解决什么问题
凌晨三点,某公司 A 同学被电话叫醒,线上接口超时率飙到 40%。打开监控看板,CPU 没满、内存没满、磁盘没满,满屏的绿色曲线,就是找不到问题在哪——排查到天亮,才发现是下游第三方 API 的 P99 延迟翻了一倍。这类“什么都正常,但业务不正常”的黑匣子时刻,就是监控体系的真正价值所在。Prometheus 是当前云原生场景下最被广泛接受的监控方案,它以拉取方式采集指标,把数据存成带标签的时间序列,再用 PromQL 这套查询语言,把运行状态变成可追溯的曲线和可触发的告警。这篇文章适合运维、后端开发,以及任何想用数据而不是直觉来定位故障的人。无论你是在容器环境还是传统虚拟机,读完你都能独立搭一套能用的监控体系。
2. 玩转 Prometheus 先吃透数据模型:从计数器到直方图的选型逻辑
Prometheus 的文档很多,但多数人第一步就卡在概念上:什么叫样本、什么叫标签,Counter 和 Gauge 有什么区别。不理解数据模型,后面全是靠瞎试。这一章把 Prometheus 最核心的几个机制讲透,后面的配置和查询才不会发虚。
2.1 拉取式采集:为什么 Prometheus 用 pull 而不是 push
传统监控系统多用 push 模式:客户端装 agent,agent 定时把数据推给服务端。推的模式看起来省事,但有几个根本问题:服务端无法判断“没有数据”是因为客户端挂了还是真的没数据;客户端数量多了以后服务端要分别承担流量峰值,扩容困难;数据在传输过程中没有统一的采样节奏,时间戳对齐很费劲。
Prometheus 选择了 pull 模式,服务端按 scrape_interval 定时到每个 target 的 /metrics 端口里抓取指标。这样做的直接好处是:抓取目标的状态是标准的——成功、失败、超时,都是服务端主动探测出来的;采集节奏由服务端统一控制;target 只要暴露一个 HTTP 端口就能被任何 Prometheus 抓取。这也是为什么云原生环境下它比老牌监控系统更顺手。
但 pull 模式对“生命周期很短的任务”有天然缺陷,一个批处理任务可能跑 30 秒就退出了,Prometheus 还没等到下一次抓取。常见的做法是这类短命任务额外推一个 Pushgateway,Pushgateway 是一个持有指标快照的缓存层,Prometheus 再从它那里拉。注意,Pushgateway 只适合“临时任务指标汇总”,不适合把业务所有指标都塞进去,否则它会变成一个故障点。
2.2 四种指标类型:Counter、Gauge、Histogram、Summary
接口暴露出来的每条指标都有类型声明。选错类型是新手最容易犯的错,比如用 Gauge 存累计请求数,那 rate 算出来会很难看。先记结论:
| 类型 | 特点 | 典型用法 |
|---|---|---|
| Counter | 只增不减 | 请求总数、错误总数、字节数 |
| Gauge | 可增可减 | 内存用量、连接数、温度 |
| Histogram | 累计桶计数 | 请求耗时分布,服务端可求分位数 |
| Summary | 客户端预计算分位数 | 旧系统兼容或需要精确分位时 |
Counter 之所以叫 Counter,是为了配合 rate():只看绝对值没有意义,绝对值除以时间才是每秒速率。Gauge 描述当前状态,直接读数,不要对它做 increase。Histogram 和 Summary 都用于“分布”类指标,区别在分位数在哪一侧计算——Histogram 在服务端算,Summary 在客户端算。推荐优先使用 Histogram,因为你可以在 PromQL 里随意调整分位值,Summary 则是一锤子买卖,必须提前在埋点里定死。
一个实际指标端点的样子,可以用 curl 看:
curl -s http://127.0.0.1:9100/metrics | \ grep -E "^node_cpu_seconds_total|^node_memory_MemAvailable_bytes" | head -n 5返回的每一行结构是:指标名、用花括号包围的标签、空格后的值。node_cpu_seconds_total 这个 Counter 后面带着 cpu 和 mode 两个标签,分别代表核序号和 CPU 工作模式,值表示该模式下累计的 CPU 秒数。看到 _total 后缀基本可以断定它是 Counter,这是社区约定。
2.3 标签是它的灵魂:job、instance 与自定义标签怎么命名
同一台机器上的 node_cpu_seconds_total 之所以有多条序列,就是因为标签值不同。Prometheus 的多维模型意味着:同一个指标 + 一组不同的标签值 = 一条独立的时间序列。所有查询都是在这个多维空间里做切片。
抓取配置里自带两个内建标签:job 是配置里的 job_name,instance 是 targets 的地址加端口。比如 job="node",instance="192.168.1.10:9100"。自定义标签用 static_configs 里的 labels 字段加:
scrape_configs: - job_name: "node" static_configs: - targets: ["192.168.1.10:9100"] labels: env: "prod" role: "web"这段配置的效果是:这个 target 抓回来的每个序列都会带 env="prod"、role="web" 这两个固定标签。标签命名的原则是:标签值的变化频率越低越好。env、zone、role 这种固定值是理想标签;Pod 名称、容器 IP、用户 ID 这种高频变化的字段如果进了标签,序列数量会爆发式增长,直接撑爆内存,这个问题在第 5 章单独展开。
2.4 本地存储机制:block、压缩与保留策略
Prometheus 不是通用时序数据库,它的本地存储是按 2 小时为一个 block 写的,后台再做 compaction 压缩。默认保留时间是 15 天,超过保留期的 block 会被删除。这个设计决定了它的定位:为近期排障和告警而生的短周期监控,不适合做跨月趋势分析和审计留存。
相关参数在启动里能看到:
./prometheus \ --storage.tsdb.path=/var/lib/prometheus \ --storage.tsdb.retention.time=30dstorage.tsdb.path 指定数据目录,storage.tsdb.retention.time 写成 30d 表示保留 30 天。我一般会在测试环境用 7d 省磁盘,生产环境按合规要求设 15d 或 30d。想长期留数年,就得把远程存储或者 Thanos 这类边车方案引入进来,让历史 block 上传到对象存储。这个复杂度不是入门阶段要考虑的,但你要知道边界在哪。
3. 本地跑通 Prometheus 的最小部署:从下载到第一个查询
概念到这儿差不多了,下面直接动手。这一章的最终目标:在你的 Linux 机器上跑起一个 Prometheus,抓取机器自身的指标,然后用 curl 验证一条真实查询。整个过程不需要 Docker、不需要容器,用最小依赖把链路打通。
3.1 二进制安装与 systemd 托管
常见做法是直接下载官方发布的二进制包,不用编译,不依赖额外运行时。到官方下载站的 release 页面拿当前最新稳定版的 linux-amd64 包,文件名形如 prometheus-2.x.x.linux-amd64.tar.gz。解压后目录里自带 prometheus 和 promtool 两个二进制:
tar xzf prometheus-2.x.x.linux-amd64.tar.gz cd prometheus-2.x.x.linux-amd64 ./prometheus --versionprometheus 是主服务,promtool 是命令行校验工具,配置写完之后拿它验证语法很有用。下载的版本号按你实际拿到的文件替换,下文其他地方同理。
把服务交给 systemd 管理,便于开机自启和崩溃拉起。先建运行用户和数据目录:
sudo useradd --no-create-home --shell /bin/false prometheus sudo mkdir -p /etc/prometheus /var/lib/prometheus sudo cp prometheus promtool /usr/local/bin/然后写 systemd 单元文件,配置文件路径指定到 /etc/prometheus/prometheus.yml:
[Unit] Description=Prometheus Wants=network-online.target After=network-online.target [Service] User=prometheus Group=prometheus Type=simple ExecStart=/usr/local/bin/prometheus \ --config.file=/etc/prometheus/prometheus.yml \ --storage.tsdb.path=/var/lib/prometheus \ --storage.tsdb.retention.time=30d \ --web.enable-lifecycle Restart=always [Install] WantedBy=multi-user.targetUser 和 Group 指定服务降权运行,避免 Prometheus 以 root 身份跑。storage.tsdb.path 是数据目录,务必保证 prometheus 用户对它可写。Restart=always 保证进程不因意外退出而失联。Type=simple 表示 ExecStart 启动的进程本身就是主进程。--web.enable-lifecycle 是为了后面能通过接口热加载配置,不加的话配置改动必须重启进程。
启动:
sudo systemctl daemon-reload sudo systemctl enable --now prometheus systemctl status prometheus看到 active (running) 说明服务起来了,浏览器访问服务器 9090 端口就能看到自带的 UI。
3.2 第一份 prometheus.yml:抓取间隔、规则加载与 storage 配置
Prometheus 的一切行为都从这份 YAML 开始。一个能工作的最小配置长这样:
global: scrape_interval: 15s evaluation_interval: 15s scrape_timeout: 10s rule_files: - /etc/prometheus/rules/*.yml scrape_configs: - job_name: "prometheus" static_configs: - targets: ["localhost:9090"]global 里的三个参数是全局默认值:scrape_interval 表示多久抓一次目标,evaluation_interval 表示多久评估一次告警规则,scrape_timeout 表示单次抓取允许的最大耗时。三者之间有个硬约束:scrape_timeout 必须小于 scrape_interval,如果抓一次要 8 秒、但 5 秒就再抓,请求会重叠,产生一堆超时误报。
rule_files 指向告警规则的存放路径,现在还没有规则文件,留空也会正常启动。scrape_configs 里定义的 job_name="prometheus" 是让 Prometheus 抓自己暴露的指标,targets 写 localhost:9090 就是它的 /metrics 地址。配置改完之后用 promtool 校验:
promtool check config /etc/prometheus/prometheus.yml输出 SUCCESS 说明语法通过。配置文件改动后需要重载才生效,不用重启进程,因为 3.1 里已经开了 lifecycle 开关:
curl -X POST http://127.0.0.1:9090/-/reload注意 9090 端口的默认监听地址是 0.0.0.0,生产环境一定用防火墙限制访问来源,或者在前面加一层带认证的反向代理,否则任何人都能查你的监控数据。
3.3 挂载 Node Exporter:监控一台普通 Linux 机器
Prometheus 只负责拉取,指标从哪来取决于被监控对象。对 Linux 服务器,社区标准做法是跑一个 node_exporter,它把 CPU、内存、磁盘、网络等系统指标暴露在 9100 端口。同样从官方下载站拿 node_exporter 的 linux-amd64 包:
tar xzf node_exporter-1.x.x.linux-amd64.tar.gz cd node_exporter-1.x.x.linux-amd64 sudo cp node_exporter /usr/local/bin/ node_exporter --web.listen-address=":9100" &同样可以把它写进 systemd 管理,此处先用后台方式验证。起来之后用 curl 看一眼指标端点:
curl http://127.0.0.1:9100/metrics | head -n 20能看到一堆 node_ 开头的指标就说明 exporter 工作正常。此时回到 Prometheus 配置文件,把 target 加进去:
scrape_configs: - job_name: "prometheus" static_configs: - targets: ["localhost:9090"] - job_name: "node" static_configs: - targets: ["192.168.1.10:9100"] labels: role: "web"重载之后,Web UI 里 Status -> Targets 页面会多出一个 node 任务,状态为 UP 表示抓取链路通畅。labels 里的 role 是自定义标签,加它的目的是在查询时能按业务角色切分。
3.4 用 curl 做第一次手动查询:看一眼 /api/v1/query
Web UI 里的 Graph 面板本质就是发 HTTP 请求到查询 API。直接 curl 能更清楚看到数据如何返回:
curl -G 'http://127.0.0.1:9090/api/v1/query' \ --data-urlencode 'query=up'返回的 JSON 会有 status、data.resultType 和 data.result。result 数组里每项代表一条序列,up 的值是 1 表示上一次抓取成功。Prometheus 内置的 up 指标是自检探活最常用的,每个 target 都有。再查一个真实指标:
curl -G 'http://127.0.0.1:9090/api/v1/query' \ --data-urlencode 'query=node_memory_MemAvailable_bytes'这时候返回结果里带着 instance 和 job 标签,值是该机当前可用内存字节数。查询 API 是 Prometheus 对外的数据接口,Grafana、自研平台、命令行都是通过它拿数据的,理解 curl 的行为等于理解了所有下游工具的调用方式。
4. PromQL 进阶:rate、histogram_quantile 与聚合的正确打开方式
PromQL 是 Prometheus 的查询语言,也是“玩转”这个词里含金量最高的部分。基础查询照葫芦画瓢不难,难在窗口、速率、分位、聚合这些概念之间存在大量边界条件。这一章把最常见也最容易出错的四个操作讲透。
4.1 四类选择器:匹配字符串的四个符号
一条完整的 PromQL 由指标名、标签选择器和可选的函数/运算组成。标签选择器有四种写法:
http_requests_total{job="api"} http_requests_total{job!="api"} http_requests_total{job=~"api|web"} http_requests_total{job!~"api|web"}第一行是精确匹配,第二行是不等于,第三行是正则匹配,第四行是不匹配正则。正则表达式用 RE2 语法,不是 PCRE,所以像 ?= 这类需要回溯的断言是不能用的。选择器写的标签越多,筛选条件越收敛。注意一个陷阱:PromQL 里 {job=""} 能匹配到没有该标签的序列,但 {job!=""} 不会排除没有该标签的序列——它只在有 job 标签的序列里过滤。两套语义不一致,写排除条件时习惯先确认目标序列确实带着这些标签。
4.2 速率计算的三条公式:rate、irate 和 increase
Counter 型指标本身是累计值,用它排查问题必须转成速率。三个函数是等价的入口,但行为差异很大:
rate(node_cpu_seconds_total{mode="user"}[5m]) irate(node_cpu_seconds_total{mode="user"}[5m]) increase(http_requests_total[5m])rate 计算窗口内每秒平均增速:取窗口第一个样本和最后一个样本,差值除以两者时间差。irate 只看窗口最后的两个样本,反映瞬时尖峰,曲线更敏感、也更毛糙。increase 返回窗口内的总增量,用于看“这 5 分钟涨了多少”这类问题。
窗口参数 [5m] 不是随便填的:窗口至少要是抓取间隔的两倍,否则窗口里可能只有一两个样本,rate 算出来要么是 0 要么是残值。我个人的习惯是:15s 抓一次就用 5m;看长期趋势用 rate,看突发尖峰用 irate,increase 只在要和总量对比时用。
4.3 直方图分位数:histogram_quantile 的参数与坑
耗时类指标是排查性能问题的关键,用的就是 Histogram。它把观测值按 bucket 累计,比如:
http_request_duration_seconds_bucket{le="0.1"} 3521 http_request_duration_seconds_bucket{le="0.5"} 5600 http_request_duration_seconds_bucket{le="1"} 7200 http_request_duration_seconds_bucket{le="+Inf"} 9800le 标签是“小于等于”的边界,数值是累计计数:耗时 0.1 秒以内的请求 3521 个,1 秒以内的 7200 个。求 P99 的标准写法是:
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))里面那层 rate 先把累计计数变成每秒增量,sum by (le) 按 le 标签聚合所有实例维度,外层 histogram_quantile 在 bucket 之间做线性插值。三个细节必须记住:第一,聚合时只能保留 le,其他维度都要用 by 规则聚合掉或过滤掉,不然插值会错;第二,bucket 的粒度决定分位数的精度,le 跨度越大,P99 的误差越大;第三,不要对 Histogram 用 increase,因为分位数需要的是速率分布,总量没有意义。
4.4 聚合与维度控制:CPU 使用率的正确写法
聚合函数控制着多序列如何合并。最常用的带上 by 保留维度:
sum(rate(node_cpu_seconds_total[5m])) by (instance) avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100第一行按 instance 分组计算整机 CPU 每秒使用速率,第二行算的是空闲率。要 CPU 使用率,取空闲率的补集:
100 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100这个公式看似绕,但它是社区里对 CPU 使用率最稳定的定义之一。不能直接对 user mode 求和,因为 mode 还有 system、iowait、steal 等好几种,只取 user 会漏掉一部分负载。写内存使用率也有类似的套路:
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)用空闲内存的补集,避免被 buffer/cache 干扰。聚合维度的选择和指标语义强相关,这部分的判断经验比语法本身更值钱,建议每个团队都沉淀一套自己的常用指标字典。
5. Prometheus 实战避坑:5 个让我熬夜的血泪教训
精确踩坑比顺畅教程更救命。这里列的是我在多个环境中真实遇到的问题,每条都按现象、原因、解决方案展开,你遇到同类问题可以直接照着走。
5.1 现象:targets 一直 UP,查询却没有最近数据
某个环境里,界面显示所有 target 都是 UP,但曲线时间轴越往前越近,最新数据停在几分钟前,再往后就没点了。看起来像“丢了数据”。
原因是虚拟机所在宿主机和监控服务器的时钟不同步,node_exporter 上报的样本时间戳比 Prometheus 本地时钟慢了几分钟。Prometheus 在写入时会对时间戳做合理性判断,过于陈旧的样本直接拒绝。
解决:给所有被监控节点统一部署 NTP 同步,配合 chrony 或 systemd-timesyncd 都可以。同步完成后重启 exporter,让新样本带正确的时间戳。这类问题排查时最容易迷惑的是“指标还在,但图形在漂”,先查两端 date 命令输出,10 秒就能确认。
5.2 现象:increase 在服务重启后数值明显失真
某服务每隔几天重启一次,increase(http_requests_total[5m]) 在重启时刻出现一个异常的尖峰或断崖,告警也跟着误报。
原因是 Counter 在进程重启时归零。increase 计算的是窗口内最后一个样本减第一个样本,如果重启发生在窗口中间,两侧的样本来自新旧两个进程周期,差值把正常增长和归零突变混在一起算出来,结果必然失真。
解决:不要在重启场景里直接用 increase 做告警阈值,先看 up 是否发生过翻转再决定要不要信这个增量。也可以改用来对比两个时间点的总量差值,或者把重启事件计入部署流程里的告警屏蔽窗口。一句话:rate 和 increase 的前提是 Counter 连续递增,进程一旦重启,这个前提就破了,你需要自己补条件。
5.3 现象:up == 1 但业务已经连续报错
Prometheus 自带的 up 指标表示“抓取到了指标”,不表示“服务健康”。很多团队的第一条告警就是 up == 0,结果某次接口全面 5xx,up 却依然全绿。
原因是 exporter 的探活停留在网络层面:node_exporter 能抓到数据,说明操作系统活着、9100 端口通,至于业务进程是不是在返回错误,up 根本不关心。它是基础设施探活,不是业务健康探活。
解决:业务健康要单独埋点。HTTP 服务用 blackbox_exporter 做真实探活,或者用业务自身暴露的指标,比如 http_requests_total{code="5xx"} 的速率超过阈值时告警。把 up 当“基础设施在线”看,把业务指标当“用户体感在线”看,两者组合才是完整的健康判断。
5.4 现象:Prometheus 内存一路攀升,频繁 OOM
某次上线新业务后,Prometheus 进程的 RSS 曲线肉眼可见地往上走,几天后 OOM 被杀,重启后继续攀升。
原因是标签基数爆炸。某个客户端埋点把请求里的请求 ID 或者用户 ID 写成了标签值,这会导致每来一个 ID 就新增一条时间序列。10 个 ID 没问题,1 万个 ID 内存就崩了。Prometheus 的时序是按标签组合存储的,组合数等于各标签值数量的笛卡尔积,增长是指数级的。
解决:先看当前序列总量:
prometheus_tsdb_head_series如果数值远超预期,去收集端把高基数标签去掉。已经在内存里的数据,用 metric_relabel_configs 在抓取时把标签丢弃:
scrape_configs: - job_name: "problem_app" metric_relabel_configs: - source_labels: [request_id] regex: ".+" action: labeldrop注意,relabel 只控制写入 Prometheus 的数据,源头埋点不改的话,流量早晚还会涨回来。更彻底的方案是把这类标签从指标设计阶段就删除,或者用日志系统去承载这类高基数维度。
5.5 现象:抓取超时,exporter 数据间歇性空白
某个 exporter 产生的指标特别多,15 秒的间隔里经常抓不完,目标一会 UP 一会 DOWN,曲线断断续续。
原因是 scrape_timeout 太短。注意 Prometheus 的超时逻辑:抓取开始后,如果超过 scrape_timeout 还没返回完,这次抓取会被标记失败,下一次间隔再试,所以图形上表现为“漏点”。默认 10s,对于大 exporter 来说确实不够。
解决:拆开两个参数——拉长抓取间隔、增大超时,但超时必须小于间隔:
scrape_configs: - job_name: "big_exporter" scrape_interval: 60s scrape_timeout: 30s也可以检查 exporter 自身是不是花了太多时间在计算指标上,例如 node_exporter 的某些 collector 可以按需关闭。参数调节本质是拿时效性换完整性,到底多长合适,要看你对数据粒度还是数据连续性的优先级更高。
6. 从查询到告警:Prometheus 的进阶用法与自检习惯
把数据查出来是一回事,让监控自动找你就是另一回事。最后这一章补上告警闭环,然后给你一份自检清单,把这套体系真正变成日常依赖的“值班伙伴”。
6.1 告警规则与 Alertmanager:两个组件各管一段
告警规则写在 Prometheus 的 rule_files 里,Prometheus 按 evaluation_interval 周期评估规则表达式,触发后把告警发给 Alertmanager。Alertmanager 负责去重、分组、路由和通知渠道。
先在 rules 目录建一个 node_alerts.yml:
groups: - name: node_alerts rules: - alert: NodeDown expr: up{job="node"} == 0 for: 2m labels: severity: critical annotations: summary: "instance {{ $labels.instance }} down"expr 是触发条件,for: 2m 表示条件持续 2 分钟才激活,避免瞬时抖动误报。labels 和 annotations 是告警的元数据和可读描述,Alertmanager 里会按 labels 做路由。
Alertmanager 的配置负责把告警发出去,一个最小配置长这样:
route: receiver: "default" receivers: - name: "default" webhook_configs: - url: "http://127.0.0.1:8080/alert"webhook 通道是为了对接企业内部的消息平台。生产环境一定要配置分组规则,把同服务多实例的告警合并成一条,否则大故障时告警轰炸是必然的。
6.2 长期可用的自检清单
监控系统自己也会生病,我习惯每周跑一遍下面的检查,成本不到十分钟:
| 检查项 | 方法 | 关注点 |
|---|---|---|
| 抓取失败率 | up == 0 的 target 数 | 不应持续存在 |
| 抓取耗时 | max(scrape_duration_seconds) | 是否逼近 scrape_timeout |
| 时间戳延迟 | timestamp(up) 与当前时间差 | 不应持续增长 |
| 序列总量 | prometheus_tsdb_head_series | 和上周对比是否翻倍 |
| 规则执行失败 | prometheus_rule_evaluation_failures_total | 必须为 0 |
| 磁盘使用 | tsdb 目录占用率 | 按 retention 估算增长 |
这套清单不解决所有问题,但能挡住大部分“监控没报警才知道监控挂了”的尴尬。从我自己的经验讲,每次出问题后回来反思,最后 80% 都落在指标设计不严谨或告警阈值没对齐上,Prometheus 本身反而很少是根源。把基础打牢,比追新工具值钱得多。希望帮到你。
本文还有配套的精品资源,点击获取