简介Prometheus是当前主流的开源监控系统这份学习资料面向系统管理员、DevOps工程师及软件开发者系统讲解从入门到精通的完整路径。内容包括时间序列数据模型、PromQL查询语言、系统架构等核心概念并比较其与其他监控方案的差异及适用场景同时细化服务器安装、节点导出器监控远程Linux主机、MySQL导出器监控数据库、可视化工具接入与仪表盘创建、告警规则配置等实操环节。资料以通俗讲解配合命令示例与操作步骤呈现适合具备基本Linux操作能力的技术人员结合真实环境动手实践。整个资源为单个PDF文件大小仅2.95MB内容精炼便于随时查阅。已有633人学习掌握后可用于搭建监控体系、自定义指标、设置告警规则与构建仪表盘实现实时监控与故障排查。1. 监控系统不是玄学这是一份能把 Prometheus 落地成工程的手记做运维的都知道线上告警最怕的不是服务挂了而是“不知道哪里出了问题”。某次大促前夜某公司的监控看板一片红CPU、内存、接口耗时全线报警可大家连“是谁在打接口、哪个服务在飙升”都说不清。那之后我把 Prometheus 完整搭了一遍从指标设计、抓取配置、查询语句到告警路由都梳理出了可复现的套路。Prometheus 是一个带自定义数据模型的监控与告警系统用抓取scrape方式拉取指标存入内置时序数据库再用 PromQL 查询和触发告警尤其适合容器环境和 Linux 服务器集群。这篇笔记写给要落地监控的开发者、运维和 DevOps 工程师跟着做就能把“监控”从玄学变成可查、可算、可告警的工程这也是这份资源里所有配置和脚本的由来。2. 数据模型指标的“长相”决定你能不能查得准Prometheus 和 Zabbix 这类传统监控最大的区别不在于存储和告警而在于数据模型。它把所有监控对象都抽象成“指标名 标签 时间戳 样本值”的四元组理解了这个模型后面写 PromQL 和配置告警规则时才不会翻车。2.1 指标名加标签等于一条独立时间序列一次抓取返回的原始数据大概是这样的node_cpu_seconds_total{cpu0,modeidle} 32872.3 node_cpu_seconds_total{cpu0,modesystem} 509.8 node_memory_Used_bytes{instance10.1.2.3:9100} 8161280000这里node_cpu_seconds_total是指标名{cpu0,modeidle}是标签集合后面的浮点数是样本值。标签每多一组取值就会多一条时间序列。比如一台 8 核机器每个核有 idle、system、user、iowait 等 8 种 mode光这一个指标就会产生 8×864 条序列。这就是“基数”的含义。我一般让团队先记住一句话查询是在选序列聚合是在合并序列。node_cpu_seconds_total{cpu0}选的是 cpu 为 0 的所有 mode 序列sum(node_cpu_seconds_total)是把所有核、所有 mode 的序列合并成一个值。心里有这个图景写 PromQL 就不容易凭空猜。2.2 四个指标类型Counter、Gauge、Histogram、Summary类型选错是新手最大的坑。四个类型解决四类问题类型语义典型场景后缀/字段Counter只增不减的计数值重启归零请求数、错误数、字节数_totalGauge可增可减的瞬时值CPU 使用率、在线人数、队列长度无Histogram分桶统计的分布延迟、响应大小_bucket、_sum、_countSummary客户端算好的分位数延迟分位数不推荐用于跨实例聚合无固定后缀业务代码里最常见的误用是把请求耗时定义成 Gauge然后每次请求覆盖成最新值。这样做只能看到“最后一次请求的耗时”完全丢了分布信息。正确的做法是用 Histogram或者直接接中间件暴露的指标。下面这段是 Python 业务服务常见的埋点写法from prometheus_client import Counter, Histogram # 请求总数Counter 必须带 _total 后缀 REQUEST_TOTAL Counter(http_requests_total, HTTP requests, [method, status]) # 请求耗时Histogram 会自动生成 _bucket/_sum/_count 三组序列 REQUEST_DURATION Histogram( http_request_duration_seconds, HTTP request latency, [method], buckets(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0), ) # 每次请求结束时打点 REQUEST_TOTAL.labels(methodGET, status200).inc() REQUEST_DURATION.labels(methodGET).observe(0.32)这里的buckets参数值得留意。默认桶是 0.005 到 10 秒对接口监控偏密对批处理任务又偏疏。我一般按业务指标的四分位预估来手动设桶原则是小于 10ms 的桶不设太细大于等于 5s 的桶尽量收紧否则histogram_quantile在尾部的估算会失真。2.3 标签设计把“可枚举的属性”放标签把“高频变化的值”放日志标签是 Prometheus 的灵魂也是最容易炸的地方。把userId、requestId、ip这种高基数信息放进标签每来一个用户就产生一条新序列几小时后 TSDB 的序列数量就会冲到百万级。它不会立刻报错但查询会越来越慢内存会持续上涨最后只能靠扩内存硬扛。可以放进标签的method、status、instance、job、env、region。这些取值的数量是有限的加上以后查询维度会非常方便。不能放进标签的用户 ID、订单 ID、请求路径如果路径里有 ID 动态段、Trace ID。另一个容易忽视的点是标签值里的单位。有人用mem_used8.16GB当数值存这会让 PromQL 里的所有数学运算直接失效。单位只出现在指标名或标签名里值永远是裸数字。3. 从部署到采集把配置写对数据才会“听话”Prometheus 是拉模式pull所以它的所有行为都由prometheus.yml里的抓取配置驱动。这一章从零写一份能用的配置再展开说抓取生命周期里几个容易看漏的参数。3.1 部署方式与最小可用配置二进制部署就是去官方下载站拿prometheus-version.linux-amd64.tar.gz解压后直接跑。实战中多数环境是容器方式docker run -d --name prometheus \ -p 9090:9090 \ -v /opt/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml \ -v /opt/prometheus/data:/prometheus \ --restartalways \ prom/prometheus:v2.53.0注意这里把数据目录/prometheus挂出来了不挂的话容器一删数据全没。配置目录要只读挂载避免容器内被篡改。对应这份最简配置global: scrape_interval: 15s # 每 15s 抓一次目标 evaluation_interval: 15s # 每 15s 计算一次告警规则 scrape_timeout: 10s # 抓取超时必须小于 scrape_interval rule_files: - /etc/prometheus/rules/*.yml scrape_configs: - job_name: prometheus static_configs: - targets: [localhost:9090]scrape_interval和scrape_timeout的关系是一个经典翻车点。如果scrape_timeout大于等于scrape_interval抓取会一直排队序列会出现时间戳断层rate 类函数就会报错或者给出离谱的值。官方虽然没强制但实际经验是 timeout 至少要留 5s 余量。3.2 抓取目标static_configs 背后的抓取会话是怎么发生的Prometheus 对每个 target 做的事情其实是一个完整的 HTTP 请求过程按metrics_path发 GET 请求带上params里的参数读取返回的文本解析成样本然后打上job和instance两个默认标签再进入存储。理解这个过程就能解释很多“数据明明有却不显示”的故障。一份接入了节点监控和业务监控的典型配置scrape_configs: - job_name: node metrics_path: /metrics static_configs: - targets: - 10.1.2.11:9100 - 10.1.2.12:9100 labels: env: prod - job_name: app-api metrics_path: /actuator/prometheus params: application: [api-server] static_configs: - targets: [10.2.1.10:8080] relabel_configs: - source_labels: [__address__] regex: (.*):.* replacement: ${1} target_label: ip最后那段relabel_configs是从__address__原始目标地址里提取 IP 部分放到ip标签里方便后面按网段聚合查询。注意__address__是双下划线开头Prometheus 里这种是内部标签不会直接出现在查询结果里只有job、instance是默认保留的。params参数很容易被忽略。部分应用的指标接口不是裸暴露的需要通过参数指定应用名或者区域比如/actuator/prometheus?applicationapi-server。这些参数会写进每次抓取请求的查询字符串。3.3 目标发现从静态文件到动态刷新机器超过 20 台后手动维护static_configs会让人想放弃。业界最常见的方案是file_sd_configs它让 Prometheus 定时扫描一个 JSON/YAML 文件文件内容变了就自动更新抓取目标。配合 CD 系统在发布时改写这个文件就能实现“实例上线即纳入监控下线自动移除”。scrape_configs: - job_name: node file_sd_configs: - files: - /etc/prometheus/targets/nodes.yml refresh_interval: 30s对应的nodes.yml- targets: - 10.1.2.11:9100 - 10.1.2.12:9100 labels: env: prod这份资源里我保留了从静态配置迁移到file_sd的一整套前后对比。如果你刚开始搭不要一上来就搞 Consul 或 Kubernetes 服务发现先跑通文件发现后面再演进排障成本最低。注意file_sd_configs只在文件内容发生变化时触发重新加载不会每分钟强制重读。所以更新文件的时候要写成“先写临时文件再 mv 覆盖”避免 Prometheus 读到半截内容。4. PromQL 实战从查一个数到出一页告警PromQL 是 Prometheus 的使用门槛但真正高频使用的函数不超过十个。这一章从选择器讲起逐步到聚合、Rate 和分位数最后落到一套能直接用的告警规则。4.1 瞬时向量与区间向量先搞清楚你查的是“一瞬”还是“一段”PromQL 里node_load1返回的是当前瞬时值叫瞬时向量node_load1[5m]返回的是过去 5 分钟每个抓取点上的值叫区间向量。rate、increase 这类函数只能接收区间向量很多新手把rate(node_load1[5m])写错成rate(node_load1)会直接报“expected range vector”错误。看几个实际表达式的差别# 当前的 CPU idle 总量瞬时 sum(node_cpu_seconds_total{modeidle}) # 过去 5 分钟 CPU idle 的平均每秒增量 rate(node_cpu_seconds_total{modeidle}[5m]) # 过去 1 小时 idle 的总增量秒 increase(node_cpu_seconds_total{modeidle}[1h])rate计算的是“每秒平均增量”increase计算的是“区间内的总增量”。从数学上看 increase 就等于 rate 乘以窗口秒数但实际带外插值时两者会有细微差异告警规则里尽量统一用rate不要混用。4.2 CPU、内存、延迟三个最常见的查询套路CPU 使用率的标准写法是 1 减去 idle 占比100 - (avg by (instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100)这里avg by (instance)是把单个实例所有核的 idle 每秒增量合并成一个均值再乘 100 变成百分比。内存使用率的套路是“以总量为分母”(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100接口延迟的核心是histogram_quantilehistogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))le标签是 Histogram 的桶边界sum by (le)是先把所有实例的桶计数相加再在这条合并的分布上求分位数。务必先执行sum by (le)再做histogram_quantile顺序反了的话每个实例单独算分位数再平均结果会失真。4.3 从一条查询变成一条告警规则Prometheus 的告警规则文件长这个样子groups: - name: node-alerts rules: - alert: HostCPUHigh expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100) 85 for: 10m labels: severity: critical annotations: summary: {{ $labels.instance }} CPU 使用率超过 85% description: 当前值 {{ $value | printf \%.2f\ }}% 已持续 10 分钟expr就是上一节验证过的那条查询for: 10m表示“阈值持续超过 10 分钟才触发”这是避免抖动误报的关键参数。$labels.instance是模板变量$value是当前表达式计算结果。我在资源里给了三套就绪的规则文件分别针对节点、容器和业务接口直接放进rule_files指定的目录就能生效。提示for参数越长预警越钝越短抖动弹窗越多。建议核心业务用 5m基础设施用 10m~15m避免半夜被无意义的抖动吵醒。5. 避坑指南5 个我反复踩过的 Prometheus 深坑技术文档会把正确路径讲得很清楚但真实世界全是边界情况。这五个坑是我在多个环境里一遍遍踩出来的每条都按“现象 → 原因 → 解决”的顺序写方便你直接对照排查。5.1 酥脆的 ratecounter 重启导致曲线“塌方”现象接口请求量监控每隔一段时间会出现一个急剧下跌的负值QPS 图看起来像被砍了一刀。原因Counter 是“只增不减”的语义但进程重启后计数器会从 0 开始。rate函数在计算递增时会做重置检测但它只会修正“恢复到 0”的情况如果进程在重启前后还有旧值残留会出现斜率突变为负值再跳回正值。解决排查顺序是先确认目标进程的启动时间再看抓取时间戳是否连续。如果重启必须接受建议在告警里对 rate 结果做下限裁剪例如max(rate(...), 0)。另外抓取周期不稳定也会放大这个效应把scrape_interval从 15s 调到 5s 会让重置修正平滑很多。5.2 类型不对一切白费Gauge 套 rate 得到一堆无意义的数现象有人把内存使用量接到rate(node_memory_MemTotal_bytes[5m])上出来的曲线在 0 附近摆动意图是“看内存增长速度”但它没有任何预警价值。原因Gauge 是标量瞬时值不是计数器。rate对 Gauge 计算的是“每秒变化量”内存总量恒定不变结果自然趋近于 0。更本质的问题是Gauge 本就该看绝对值而不是看增量。解决内存监控直接查node_memory_MemAvailable_bytes或用百分比表达式不要套 rate。只有 Counter 才能套 rate/increase。判断依据很简单查指标名是否带_total不带就是 Gauge别套。5.3 高基数标签查询变慢、内存涨都不是存储的锅现象加了几个新标签后Prometheus 的 RSS 内存从 2G 涨到 8G简单查询都要两三秒再往后tsdb status显示 Head 里的序列数到了几百万。原因把request_id、api_path这样的高基数标签放进了指标里序列数随流量增长且永不复用。TSDB 的倒排索引和 chunk 缓存全部膨胀。解决先用 API 确认基数来源。查/api/v1/status/tsdb看seriesCountByMetricName里哪个指标序列数最多。把高基数标签从指标标签移动到日志里如果暂时没法改代码就用relabel_configs在抓取端直接丢弃这个标签。丢弃只是止损根治要靠埋点规范。我在资源里放了一份标签规范 check-list接入新业务时照着过一遍。5.4 内存持续上涨垃圾回收和块压缩的“时间差”现象配置里明明写了storage.tsdb.retention.time: 15d但/prometheus数据目录还是涨到几十 G内存也没回落到预期。原因retention 管的是“落盘的块数据”而不直接管 WAL 和内存里的 head 数据。Head 块要攒满一定的 chunk 大小才会被压缩落盘WAL 默认要保留 5 小时期间如果读写负载大内存和磁盘占用会在几个时间点上同时达到峰值。解决明确“短期多吃长期稳定”的规律别用一条 retention 参数解决所有存储问题。建议给容器设置合理的内存上限并做持久化真要压缩空间用promtool tsdb compaction手动触发一次观察效果后再决定要不要调整storage.tsdb.min-block-duration。多数情况数据目录偏大是正常的重点是确认查询速度没劣化。5.5 Alertmanager 告警收到手软分组、抑制和路由没配对现象同一个服务 10 个实例同时 CPU 飙高收到 10 条独立告警等问题恢复又重复弹出恢复通知。有人直接关掉告警等于没有监控。原因alertmanager 的group_wait、group_interval、repeat_interval三个参数默认值对高密度告警不友好。10 条独立告警说明路由没有分组标签恢复通知重复是repeat_interval太短或没配置。解决给路由加上group_by: [alertname, job]把同一 job 的同类告警合并成一条设置group_wait: 30s让短时间内的同组告警先聚合再发送。repeat_interval根据告警紧急程度分别设 4hwarning和 1hcritical。我在资源的告警配置里给了完整路由树直接看注释就能改。6. 进阶技巧relabel 的“手术刀”和查询性能体检排查告警翻车时最值钱的不是搜索语法而是能快速缩小范围的两件工具relabel 配置和 Prometheus 自带的体检命令。高级用法里我最有心得的是relabel_configs的keep和labelmap它可以在数据进库之前就把不需要的序列截断在门外。relabel_configs: # 只保留 envprod 的目标 - source_labels: [__meta_consul_tags] regex: .*prod.* action: keep # 把 __meta_consul_service 映射成 app_name - source_labels: [__meta_consul_service] target_label: app_name replacement: $1action: keep是白名单机制正则不匹配的目标直接丢弃labelmap则做“搬家”把内部元标签变成可查询的普通标签。每次要新增服务发现时我都是先起一个 debug 实例抓一次/metrics看__meta_*标签长什么样再写 relabel 规则不要凭想象写正则。查询性能不对时先跑三个体检命令而不是直接重构指标promtool check config prometheus.yml promtool tsdb list /prometheus/data promtool tsdb analyze /prometheus/data | head -50check config能查出配置语义错误tsdb analyze会给出每个指标序列数、chunk 数量、标签占用一眼定位到“吃内存”的元凶。我习惯在告警响应里加一条凡是查询耗时超过 10s 的先跑tsdb analyze再决定是调整查询还是治理标签。从那以后每次有人报“Prometheus 变慢”我都会强制走一遍这三条命令大多数时候五分钟内就锁定了是某个业务指标把基数带崩了。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?