资讯动态

Prometheus从入门到精通:监控告警链路与实战避坑指南

发布时间:2026/10/18 19:35:13 来源:尧图企业网站定制
简介Prometheus监控系统学习指南面向具备一定Linux基础的系统管理员、DevOps工程师及开发人员系统讲解从部署到实战的完整路径。内容覆盖时间序列数据模型、PromQL查询语法、数据采集方式等核心概念并对比其与关系型数据库、NoSQL在时序处理上的差异。教程以实例演示和操作步骤贯穿详细展示Prometheus服务安装配置、通过node_exporter监控远程Linux主机、通过mysqld_exporter监控MySQL、与Grafana集成构建可视化仪表盘以及基于Alertmanager实现报警通知帮助读者掌握从指标定义到故障排查的全流程技能。资源为单份PDF文档包整体大小约2.95MB便于离线阅读与随时查阅。已有633人学习使用适合希望快速上手Prometheus并落地到真实运维场景的技术人员。1. Prometheus 从入门到精通从一个凌晨的告警漏报说起Prometheus 从入门到精通这条路我是从一次漏报开始意识到必须走通的。凌晨线上接口时延飙高业务一路追到监控台主面板曲线却是平的。查了一个小时才发现采集周期是 45 秒CPU 尖峰只持续 10 秒两层轮询刚好把故障漏掉了。Prometheus 是一套开源的时间序列监控与告警系统exporter 暴露指标Prometheus 按周期拉取并写入本地 TSDB你用 PromQL 把指标查出来再由规则引擎评估告警。它能解决的是“指标抓不回来、问题没人喊、查询越跑越慢”这三类问题。适合的人很明确运维、SRE、后端开发以及所有想真正看清系统现状的人。下面按从入门到精通的路径推进先打通抓取链路再玩透 PromQL然后跑通告警闭环最后排掉那些容易反复踩的坑。2. 先打通抓取链路从 exporter 到本地存储2.1 拉模型为什么是 Prometheus 的立身之本Prometheus 与很多监控系统的第一个区别是采集方式它默认用 pull而不是 push。每个被监控目标在/metrics路径上以文本格式暴露指标Prometheus 按scrape_interval去抓取。这个模型一开始让人不习惯但好处很明显。故障判定权在监控端不在被监控端。目标挂了Prometheus 拉取失败up指标变成 0这只取决于“能不能拉到”不依赖 agent 是否主动上报。排错时也简单手动 curl 一下目标地址就能确认是 exporter 没起来还是网络不通不用去猜采集链路里某段丢了数据。拉模型也有代价应用要么自己暴露/metrics要么在旁边跑一个 exporter。常见做法是每台机器装node_exporter数据库、中间件、黑盒探测都各有对应 exporter。我一般先保证所有核心节点都有 node_exporter然后再按业务加专用 exporter。exporter 和 Prometheus 不要求同机部署只要网络能访问端口就能跨主机抓取。这也是多机房监控的第一个基础把 Prometheus 放在能访问所有目标的网络位置目标列表写进配置就会自动纳管。在拉取入口上我们可以统一做超时、限流、标签改写这个入口就是scrape_configs。下面是最小可用配置。2.2 最简可复现的抓取配置prometheus.yml 这样写准备一个目录放入 Prometheus 和 node_exporter 的二进制然后写一份最小配置global: scrape_interval: 15s scrape_timeout: 10s evaluation_interval: 15s scrape_configs: - job_name: node static_configs: - targets: [localhost:9100]这段配置的意思是每 15 秒拉取一次jobnode目标地址是localhost:9100单次拉取最多等 10 秒。启动后打开 Prometheus 的/targets页面能看到这个目标up为 true 说明链路已经通了。scrape_interval决定数据密度。15 秒能反映大多数瞬时抖动业务敏感时我会调到 5 秒但这会增加 exporter 和网络的压力不建议全局低于 5 秒。scrape_timeout是一个容易被忽略的参数它必须小于scrape_interval否则上一次抓取还没结束下一次抓取就到了日志里会出现大量context deadline exceeded。接入目标前先手动验证一下curl -s http://localhost:9100/metrics | head -20如果看到node_cpu_seconds_total这类指标说明 exporter 正常再接入 Prometheus。否则不要急着查 Prometheus问题大概率在目标侧。static_configs适合一台实验机生产环境几十台机器时我会用目标文件和服务发现来维护节点列表。后面第 6 章会给file_sd的具体写法这里先把静态链路跑通。2.3 指标格式读懂counter、gauge、histogram 的选择与读法在/metrics返回的文本里最核心的三行长这样# HELP node_cpu_seconds_total Seconds the cpus spent in each mode. # TYPE node_cpu_seconds_total counter node_cpu_seconds_total{cpu0,modeidle} 123456.7HELP和TYPE是指标元信息真正被存储的是带 label 的样本行。四个基础类型里counter只增不减适合累计总量比如连接数总和、发包总字节gauge可增可减适合当前内存、负载、队列深度histogram用一组_bucket累积计数用于计算分位数配套还有_sum和_countsummary则是在客户端算好分位数直接暴露_quantile指标。我的选型习惯是速率类用 counter状态类用 gauge耗时分布用 histogram。别用 gauge 模拟 counter比如统计请求总量时不考虑重启归零后面用rate会算出奇怪的波形。类型错了PromQL 写起来很别扭。relabel可以删标签、改标签但不会改指标类型。类型定义在 exporter 或应用代码里Prometheus 只负责存。某个指标类型不对必须从源头改监控侧没有后悔药。另外每次抓取成功Prometheus 都会自动生成一个up指标值等于本次抓取结果。所以判断采集目标健康先看up{job...}。它是整个采集链路最直观的晴雨表。2.4 TSDB 本地存储数据写去哪保留期怎么给Prometheus 把抓到的样本写进自己的本地时序库 TSDB。默认数据目录是data/由--storage.tsdb.path指定。TSDB 按 2 小时一个 block 写入随后后台 compaction 合并旧 block。不需要把细节背下来但要理解几个现象。新写入的数据在 head block 中查询很新的事件时会读取这部分历史 block 经过压缩长期趋势查询会更快。prometheus_tsdb_head_series这个指标能看当前活跃序列数prometheus_tsdb_head_samples_appended_total能看写入速率排查性能问题时先看这两个。保留期参数我通常这样给./prometheus \ --config.fileprometheus.yml \ --storage.tsdb.path/data/prometheus \ --storage.tsdb.retention.time15d \ --storage.tsdb.retention.size50GBretention.time按时间保留retention.size按块维度控制上限。两个都设时先命中的生效。注意size是块级别的删除不是逐字节精确回收所以磁盘预留要留余量。如果业务产生了大量高基数 label50GB 可能装不了几天这个问题后面避坑章专门展开。本地 TSDB 不适合无限扩容。数据长期留存超过一两个月常见做法是 remote write 转发给外部长期存储。但 remote write 会引入额外故障点对大多数 15 天以内的监控本地 TSDB 加合理保留期完全够用。3. PromQL 实战从查指标到算分位数3.1 先把两个向量分清瞬时向量与区间向量刚上手 PromQL 最容易翻车的就是把方括号漏掉。node_load1返回的是“当前”瞬时值而node_load1[5m]返回的是最近 5 分钟的所有样本。规则很简单在面板里画一条曲线用的是瞬时向量比如node_load1。做速率、增量计算时必须给区间向量比如rate(node_cpu_seconds_total[5m])。一旦误写rate(node_cpu_seconds_total)PromQL 会直接报expected range vector这其实是善意的报错。一个反直觉点是avg_over_time(node_load1[5m])返回的仍然是“瞬时向量”只是每个时间点上取的是前 5 分钟平均值。它看起来像区间但在类型上不是。这个区分如果没建立后面写告警规则时容易把for语义搞混。提示方括号只属于选择器不代表最终返回类型。函数决定返回类型rate 返回瞬时向量原始值返回的也是瞬时向量。3.2 rate 与 irate算速率的两种姿势rate(v[5m])计算区间内的每秒平均增长率它正确处理了 counter 重启归零的问题。适合平滑毛刺也适合长时间段大盘。irate(v[5m])只取区间最后两个样本的差值并外推到每秒反应快能看到瞬时尖峰。但样本间隔不稳定时曲线会跳得比较厉害。算 CPU 使用率我通常这样写100 - avg by (instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100先取 idle 秒数算 5 分钟每秒平均空闲值按 instance 聚合平均再用 100 减去得到每个实例的 CPU 使用率。by (instance)是保每个实例一行没加的话会得到全局平均单台故障会被淹没。rate对 counter 重启的处理前提是区间内有足够样本。scrape_interval是 15 秒5 分钟才有 20 个样本区间太短只有两三个样本时首次抓取产生的计数器跳变会影响结果。经验是区间至少容纳 2 个抓取周期。网络流量方向用 counter 类型的node_network_receive_bytes_totalincrease(node_network_receive_bytes_total{deviceeth0}[1h])它表示过去 1 小时该网卡收到多少字节比直接看瞬时速率更符合“一个小时内收了多大流量”的提问方式。3.3 聚合与 topk把一堆机器合并成一条线多实例合并是日常高频操作。sum by (job)(rate(...))按 job 合并sum without (cpu, mode)去掉 cpu 和 mode 维度保留其他标签。两者效果接近without更适合“不想指定到底保留哪些标签只想删掉一两个”的场景。磁盘剩余比例的例子topk(5, max by (fs) (node_filesystem_avail_bytes / node_filesystem_size_bytes))这会返回可用剩余比例最小的 5 个文件系统。但要注意topk结果不适合直接写进告警规则。原因是规则评估每轮独立执行topk 选中的对象可能变化告警归属不稳定今天报 A 盘明天报 B 盘排障体验很差。要监控“某个盘快满了”更可靠的是对每个序列直接算阈值node_filesystem_avail_bytes / node_filesystem_size_bytes 0.1这样每台机器都有自己的告警判断不会被 topk 的轮换逻辑影响。3.4 histogram_quantile从 bucket 算出 P95/P99耗时类指标大多用 histogram 暴露PromQL 侧用histogram_quantile还原分位数histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le) )histogram 的每个 bucket 是累计计数。要算最近 5 分钟的分位数必须先用rate把 bucket 转成每秒速率再sum by (le)把多实例合并。le是分位数计算的横坐标必须保留。我这里踩过一个大坑直接对_bucket原始值做histogram_quantile得到的是进程启动以来的累计分布不是最近 5 分钟的分位。老慢请求一直在累积告警会持续误报。所以凡是写“最近一段时间”的分位数就必须包一层 rate。分位数只在 bucket 边界内插值是一种近似。如果 bucket 设计得太粗P99 可能和 P95 落在同一个档位这时需要回源头调整应用上报的 bucket 定义。平均时延不用动 bucket直接用_sum和_countrate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])3.5 写查询时顺手防住超时PromQL 写复杂后一个常见现象是查询大时间范围时转圈很久甚至超时报错。原因多半是查询一次性扫描了大量 series特别是没有加任何 label 过滤的查询。我的习惯是先加范围限制再写聚合。例如sum(rate(node_cpu_seconds_total{instance~10\\..*}[5m])) by (instance)用正则先圈定实例网段缩小数据量然后再算。这个习惯在实例上千后非常重要也是从“会写 PromQL”到“能在生产环境用”的分水岭。Prometheus 的查询参数--query.max-concurrency和--query.timeout可以限制资源占用但查询语句本身尽量自带过滤才是根本。4. 把告警闭环跑起来规则、Alertmanager 与抑制4.1 告警规则的语法与 for 的真实含义Prometheus 本体的告警规则写在独立规则文件里再在 prometheus.yml 中挂载rule_files: - rule.ymlrule.yml 内容groups: - name: node_rules rules: - alert: NodeHighCPU expr: 100 - avg(rate(node_cpu_seconds_total{modeidle}[5m])) * 100 80 for: 5m labels: severity: critical annotations: summary: {{ $labels.instance }} CPU 使用率过高 description: 当前值 {{ $value }}%expr返回结果为真的序列会触发这条告警。for: 5m表示该条件必须持续满足 5 分钟Prometheus 会先把告警标记为 pending连续满足后才变成 firing。这个字段是防告警风暴的第一道防线。没有for一次瞬时毛刺就会触发通知for设太长持续故障也要等几分钟才被人看到。我一般把瞬时类指标设为 1~5 分钟趋势类指标设为 10~15 分钟。labels用于告警分组和路由annotations是给人看的支持{{ $labels.xxx }}和{{ $value }}模板。写完规则先做静态校验promtool check rules rule.yml它会报 YAML 或 PromQL 语法错误。CI 里加一步promtool check config检查主配置是我比较推荐的习惯。4.2 Alertmanager 路由分组、等待和重复Prometheus 只负责产生告警真正发送、去重、分组的活由 Alertmanager 做。Prometheus 通过配置里的alertmanagers地址把告警推过去。Alertmanager 自己的配置route: group_by: [alertname, instance] group_wait: 30s group_interval: 5m repeat_interval: 4h receiver: ops-webhook receivers: - name: ops-webhook webhook_configs: - send_resolved: true url: http://xxx.example.com/hook/promgroup_by决定哪些告警合并成一个通知我常用alertname加instance同一实例同类型告警归到一组。group_wait是组内第一条告警到首次发送的等待时间用来吸收同时爆发的告警避免一瞬间收到几十条。group_interval是组内后续告警的发送间隔。repeat_interval是整个组重复提醒的最小间隔生产里我会调到 3~4 小时不然同一个告警每 5 分钟刷屏一次再好的消息通道也会被轰炸。如果需要按级别分流可以加 routesroutes: - match: severity: critical receiver: ops-phone把 critical 告警走独立通道warning 走 webhook。注意 route 是逐级向下匹配的父级 route 的 receiver 不生效必须落到叶子节点。4.3 抑制规则避免一个故障变成一批告警没有抑制时一台机器宕机你会同时收到 NodeDown、NodeHighCPU、磁盘不可达等一堆告警其实只源于同一个根因。抑制规则用一条高级别告警压住低级别告警inhibit_rules: - source_matchers: [severity\critical\] target_matchers: [severity\warning\] equal: [instance]含义是如果同一 instance 上已经有 critical 告警那么该 instance 上所有 warning 告警都会被抑制直到 critical 告警恢复。source_matchers是 Alertmanager 新版推荐的写法旧版本用source_labels。迁移到新写法时注意 label 值要用双引号包裹否则字符串可能会被解析错。equal里的instance是连接点必须两边都有这个 label 才生效。如果告警来源不同导致标签值不一致抑制规则会静默失效。所以设计告警 label 时尽量统一避免一套系统用instance另一套用host的情况。Alertmanager 的静默功能也很实用。发布窗口常用amtool silence add alertnameNodeHighCPU --duration1h它只按 label 匹配不支持匹配 annotation。用完记得到了时间要清理长期挂着的静默会把告警悄悄吞掉。4.4 告警链路排查的顺序告警“不响”最常见的三处断点第一Prometheus 规则状态。打开/alerts页面如果一直 pending 不进 firing先看for是否太长以及expr是否真的为真。第二Prometheus 到 Alertmanager 的连接。Prometheus 启动后可以查prometheus_notifications_alertmanagers_discovered指标确认发现到的 alertmanager 数量是否正确。数量为 0说明配置里的地址或服务发现没生效。第三Alertmanager 到接收端。打开 Alertmanager UI 的/alerts如果告警存在但没有发送看路由匹配对不对、receiver 是否为空。如果 receiver 是 webhook可以先临时改成测试地址触发一次真实告警再去看接收端是否收到 POST。5. 玩转 Prometheus 的避坑清单现象、原因与修复5.1 采集目标偶尔失败曲线上出现规则空洞现象图上看某个实例每隔几小时缺一个点时间序列不是连续曲线而是一段段虚线。日志里能找到context deadline exceeded或dial tcp timeout。原因最常见的是scrape_timeout和scrape_interval配置不合理。比如 interval 10 秒、timeout 10 秒任何一次网络抖动都可能导致超时后面的抓取又会因为上一次还没结束而被跳过。解决把 timeout 调到 interval 的 60%~70%。15 秒周期配 10 秒超时5 秒周期配 3 秒超时。如果个别 exporter 计算量大可以为该 job 单独设置更长的 timeout但必须同时调整 interval。还有一个隐蔽原因目标机器负载过高exporter 响应慢这是源头问题要优化 exporter 端而不是一味调大超时。5.2 告警一直 Pending 不 Firing现象/alerts页面里告警长时间停在 pending明明值已经超过阈值。原因for时间没到是一种情况另一种情况是evaluation_interval偏大规则评估次数太少导致for计数不连续。Prometheus 对 pending 的计数不是按真实时间均匀推进而是按每次 evaluation 的间隔估算如果 evaluation 是 30 秒一次for: 1m需要连续两次评估都命中实际可能要超过 1 分钟。解决把evaluation_interval调低到 15 秒并同步缩小for或者反过来让for至少是 evaluation_interval 的整数倍。还有一个小坑expr里用了topk每轮评估选中的对象不同告警在 pending/firing 之间反复横跳建议避免用 topk 写告警。5.3 重启后长时间没数据别急着删目录现象Prometheus 进程重启后面板上几分钟内没有新数据查询历史数据也是空的。原因TSDB 启动时要回放 WAL把上一次崩溃或重启前还没进入 block 的样本重新加载。这个阶段指标查询会出现暂时空洞尤其是数据量大时可能持续几分钟。解决先看日志里的WAL segment loaded记录等回放完成。千万不要在此时删data/目录“重置”一旦删除所有历史数据就真没了。如果每次重启回放都很慢说明 WAL 积压太多考虑缩短scrape_interval或检查 TSDB 写入性能。5.4 label 基数失控内存和磁盘撑不住现象Prometheus 内存持续上涨查询越来越慢prometheus_tsdb_head_series达到几十万甚至百万。一张表看过去全是高基数 label。原因有些 label 的取值接近无限。典型是把用户 ID、订单号、设备指纹写进指标 label。一个指标加一个 ID label每秒产生成千上万个新序列TSDB 很快爆炸。解决先在接入端控制 label。设计指标时label 只留枚举值可控的维度比如method、code、instance、job。如果某个 label 是业务主动上报的用 Prometheus 的 relabel 把它删掉metric_relabel_configs: - source_labels: [user_id] regex: . action: drop也可以用 labeldrop 直接删除metric_relabel_configs: - action: labeldrop regex: user_id|request_id必须承认当 label 基数已经写进了历史数据时删 label 并不能回收旧 block只能停掉增长等待保留期自然淘汰。所以这个坑最重要的是接入端把控。5.5 查询越来越慢但资源看起来还有余量现象同样的查询以前秒出现在转圈或者时报 503日志里出现query timeout。原因查询没有 label 过滤全量扫描所有序列或告警规则里写了超复杂的正则每次评估都要扫大量样本。解决把查询里不需要的维度去掉加 instance 或 job 过滤。Prometheus 的--query.max-concurrency控制并发上限--query.timeout控制单查询超时但调参只是治标。真正治本的是把高频聚合用记录规则落下来把复杂的 PromQL 提前算好查询时直接读结果。记录规则的具体做法在第 6 章给出。5.6 Alertmanager 没收到告警Prometheus 却已经触发现象Prometheus 的/alerts已经 firing但 Alertmanager UI 里没有任何告警。原因最常见是 Prometheus 配置里的alertmanagers服务发现没生效或者地址端口写错。也可能是 Alertmanager 收到了但被路由丢弃比如 receiver 为空或 match 条件不满足。解决先查prometheus_notifications_alertmanagers_discovered看 discovery 数量再查prometheus_notifications_sent_total看发送计数。如果发送计数涨了但 Alertmanager 没有多半是网络或地址问题如果发送计数没涨去看 Prometheus 到 Alertmanager 的握手配置。Alertmanager 侧再看/#/alerts和 receiver 日志确认路由匹配。6. 进阶服务发现、分片抓取与记录规则6.1 用 file_sd 动态维护目标列表节点数量超过几十台后static_configs 不好维护。我用的最省事方案是file_sd让 Prometheus 监听一批 YAML 文件的变化scrape_configs: - job_name: nodes file_sd_configs: - files: - /etc/prometheus/targets/*.yml refresh_interval: 1m目标文件内容- targets: - 10.0.0.1:9100 - 10.0.0.2:9100 labels: env: prod region: cn-east新增机器时只需要往里追加一行Prometheus 会在 1 分钟内感知。目标文件可以用配置管理工具批量分发也可以配合 CMDB 生成比手改主配置安全得多。6.2 分片抓取一台 Prometheus 扛不住时怎么办当实例数量很大一台 Prometheus 抓取压力过高常见做法是把目标按 hash 分片到多台 Prometheus各自抓取一部分。用 hashmod 加 keep 实现scrape_configs: - job_name: nodes static_configs: - targets: [10.0.0.1:9100, 10.0.0.2:9100] relabel_configs: - source_labels: [__address__] target_label: __tmp_hash modulus: 6 action: hashmod - source_labels: [__tmp_hash] regex: ^0$ action: keep这台只保留哈希结果为 0 的目标另一台把 regex 改成^1$以此类推6 台 Prometheus 各抓一份。这样每台的抓取目标和存储量都降下来。分片之后查询侧需要一个统一入口。常见做法是再加一层联邦 Prometheus配置如下scrape_configs: - job_name: federate metrics_path: /federate params: match[]: - {__name__~node_.*} static_configs: - targets: [prometheus-a:9090, prometheus-b:9090]联邦层只聚合需要的指标子集不建议把全部指标都拉上来否则分片省下的压力又会在联邦层回到原样。6.3 记录规则让复杂查询秒级返回最后我保留的进阶习惯不是换存储而是用记录规则把高频查询先算好。比如 CPU 使用率这个查询在多个面板和告警里反复出现我会写成groups: - name: recording_rules rules: - record: cpu:node_avg_usage expr: 100 - avg(rate(node_cpu_seconds_total{modeidle}[5m])) * 100面板和告警再查时直接用cpu:node_avg_usage即可不再触发大范围扫描。写记录规则时要考虑维度保留instance、job这类常用维度去掉 cpu、mode 这些不常用的。落到记录规则后每次评估只写一次结果比每个查询各自扫原始 series 便宜得多。每次改完配置我强制自己跑一遍promtool check config prometheus.yml和promtool check rules rule.yml再通过 HTTP 接口重载配置。这套习惯帮我避免了很多次凌晨的配置故障。Prometheus 玩到深处坑大多是采集链路、维度设计和查询写法这三件事引起的把这些根子打牢剩下的就是根据业务不断补充新的指标维度了。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑