半夜手机震动,我爬起来看了一眼,一台Nginx服务器的带宽被打满,CPU长时间跑在90%以上,而这一切我都是第二天早上到公司才发现的。那种被动挨打的局面,是我决定系统性搭建一套监控平台的直接原因。当时把市面上的方案都翻了一遍,最后选择了Prometheus(普罗米修斯)这套开源组合拳,一直用到现在。这篇文章我就以自己实际部署和使用的经历为主线,把Prometheus从选型、部署、采集、展示到告警的完整链路,以及这中间踩过的坑,全部整理出来。无论你是刚接触监控的新手,还是正在考虑迁移旧监控系统的运维,都可以直接照着做。
1. 为什么选Prometheus:监控方案选型背后的逻辑
1.1 它解决的是我过去最头疼的问题
在决定引入Prometheus之前,我的监控工作基本靠三样东西:Shell脚本定时跑、crontab发邮件、以及一台半死不活的Zabbix。脚本方案的问题很明显——没有历史趋势,告警全靠邮件,查个历史数据得翻日志,出了问题排查效率极低。Zabbix虽然功能全面,但它的数据模型偏传统,安装维护重,画图能力也比较原始,每次想加一个新指标都要在Web界面里点半天。更关键的是,Zabbix这种“中心化定期轮询”的模式,在现代容器化和微服务架构下越来越吃力——服务实例是动态起停的,IP不断变化,你根本没法用一个固定的主机清单去描述整个系统。
Prometheus的诞生背景是SoundCloud为了解决微服务监控而设计的,它天生就是为动态环境准备的。它的核心思路不是“脚本去采集数据推上来”,而是“Prometheus主动去找目标拉数据”,这个拉取模型的优势在后面会详细讲。第一眼看到它的表达式查询语言PromQL时,我就知道这是我需要的工具——它让指标查询变成了一种灵活的计算语言,而不是在Web界面里找现成的图表。
1.2 拉取模型:Prometheus最核心的设计思路
Prometheus采用“拉”而不是“推”的方式获取监控数据。每个被监控的目标(Target)会暴露一个HTTP端点,比如http://192.168.1.10:9100/metrics,Prometheus服务端定期从这个端点获取当前时刻的指标快照,存入内置的时序数据库TSDB。
这个模型带来的好处非常实在:
- 不需要在被监控机器上安装复杂的Agent服务,只要能提供一个HTTP接口就行,很多Exporter就是一个轻量的单文件程序。
- Prometheus主动发起请求,目标是否存活、指标是否正常返回,Prometheus自己一清二楚。有一个内置指标
up,等于1表示采集成功,等于0表示失败,这是一切健康告警的基础。 - 数据采集的主动权完全在监控端,想调整采集频率,改一个配置就行,不用去每台机器上改Agent。
当然,这种模型也有短板。比如目标处于防火墙后面,Prometheus无法直接访问时就采不到数据,这时候需要Pushgateway临时顶一下。还有就是采集频率不能太高,通常10到15秒一次,太频繁会对被监控服务产生额外压力。但这个短板在实际使用中完全可以接受。
1.3 整套系统的主角:Prometheus、Exporter、Grafana、Alertmanager
很多人提到Prometheus,会误以为它就是那个二进制程序。实际上在生产环境里,一套完整的Prometheus监控体系由多个组件协作完成。我用一张分工表来帮助理解:
| 组件 | 职责 | 类比 |
|---|---|---|
| Prometheus Server | 采集指标、存储时序数据、执行PromQL查询、评估告警规则 | 仓库管理员+账本记录员 |
| Exporter | 把各类系统数据转换成Prometheus可抓取的metrics格式 | 翻译官 |
| Grafana | 把时序数据转换成可视化图表和大盘 | 报表设计师 |
| Alertmanager | 接收告警并负责去重、分组、路由、通知 | 值班调度员 |
Exporter是整个生态的关键。你以为Prometheus本身知道怎么采集CPU或内存?不是,它只认统一的metrics文本格式。具体到采集什么,全靠Exporter来翻译。比如Node Exporter负责把Linux服务器的CPU、内存、磁盘、网络指标翻译成Prometheus能懂的格式,SNMP Exporter负责把网络交换机、路由器通过SNMP协议暴露的信息翻译成Prometheus格式。这套“翻译官”机制让Prometheus几乎可以监控任何东西——只要有人写了对应的Exporter。
2. 从零部署一套Prometheus+Grafana监控平台
2.1 镜像下载:先把基础镜像准备到位
部署Prometheus最常见的方式是用Docker,特别是用Docker Compose编排,几行配置就能把Prometheus、Grafana、Node Exporter、Alertmanager全部拉起来,不污染宿主机环境,迁移也方便。
需要拉的镜像主要有这么几个。Prometheus官方镜像名是prom/prometheus,Grafana是grafana/grafana,Node Exporter是prom/node-exporter,Alertmanager是prom/alertmanager。有洁癖的同学还可以拉一个prom/snmp-exporter用于后续监控网络设备。
docker pull prom/prometheus:v2.53.0 docker pull grafana/grafana:11.1.0 docker pull prom/node-exporter:v1.8.2 docker pull prom/alertmanager:v0.27.0 docker pull prom/snmp-exporter:v0.26.0关于镜像版本,我建议这样定版本而不用latest。监控系统对稳定性要求高,latest可能在某个时间点悄悄变化,导致行为差异。我自己就遇到过同事用latest起了一个Grafana容器,结果由于新版本改了数据源配置的默认要求,面板全部连不上数据源,排查了半天才意识到是版本差异。另外,如果所在网络环境下Docker Hub拉取很慢,可以考虑配置镜像加速器,这个基础操作就不赘述了。
2.2 Docker Compose编排:一条命令拉起整套监控栈
我习惯把整套监控服务放在一个目录下统一管理,目录结构如下:
monitoring/ ├── docker-compose.yml ├── prometheus/ │ ├── prometheus.yml │ └── rules/ │ └── node-rules.yml └── grafana/ └── datasources/ └── datasource.yml这是我的docker-compose.yml核心内容:
version: "3.8" services: prometheus: image: prom/prometheus:v2.53.0 container_name: prometheus volumes: - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - ./prometheus/rules:/etc/prometheus/rules:ro - prom_data:/prometheus command: - "--config.file=/etc/prometheus/prometheus.yml" - "--storage.tsdb.path=/prometheus" - "--storage.tsdb.retention.time=30d" ports: - "9090:9090" restart: always networks: - monitoring grafana: image: grafana/grafana:11.1.0 container_name: grafana volumes: - grafana_data:/var/lib/grafana ports: - "3000:3000" environment: - GF_SECURITY_ADMIN_PASSWORD=admin123 restart: always networks: - monitoring node-exporter: image: prom/node-exporter:v1.8.2 container_name: node-exporter command: - "--path.rootfs=/host" volumes: - /:/host:ro,rslave ports: - "9100:9100" restart: always networks: - monitoring alertmanager: image: prom/alertmanager:v0.27.0 container_name: alertmanager volumes: - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro ports: - "9093:9093" restart: always networks: - monitoring networks: monitoring: driver: bridge volumes: prom_data: grafana_data:这里有几个细节需要特别注意。node-exporter挂载了宿主机的根目录到容器的/host路径,并设置--path.rootfs=/host,这样Node Exporter才能读到宿主机真正的文件系统数据,否则它看到的只是容器内部的文件系统,磁盘指标就会不准确。这是很多人第一次部署Node Exporter时最容易忽略的点。
另外Prometheus的数据目录必须用Volume持久化,否则容器重建后历史数据全部丢失,监控系统最怕的就是数据断层。我设置了--storage.tsdb.retention.time=30d,也就是数据保留30天,可以根据自己的磁盘容量调整。
2.3 prometheus.yml配置解读:搞懂scrape_configs
Prometheus的主配置文件是prometheus.yml,这是整个监控平台的核心配置。以下是一个最基础的版本:
global: scrape_interval: 15s # 全局采集间隔 evaluation_interval: 15s # 告警规则评估间隔 scrape_configs: - job_name: "prometheus" static_configs: - targets: ["localhost:9090"] - job_name: "node-exporter" static_configs: - targets: - "192.168.1.101:9100" - "192.168.1.102:9100"scrape_interval和evaluation_interval是两个容易混淆的参数。前者控制Prometheus多久去拉取一次指标,后者控制Prometheus每隔多久检查一次告警规则是否满足触发条件。两者不需要完全一样,但通常保持相同。如果采集间隔是15秒,告警评估也设在15秒,那么理论上一个告警最多延迟15秒被发现,这个响应速度对大部分场景足够。
scrape_configs是一个数组,每个job_name代表一类采集任务。static_configs是最简单的目标配置方式,直接把目标地址列表写死。对于服务器资源监控,在机器数量少的时候完全够用。机器数量多了之后,才需要考虑引入Consul或Kubernetes的服务发现机制,让Prometheus自动发现新加入的目标,这个后续可以单独讲。
2.4 启动与验证:从Prometheus自带UI确认采集正常
配置完成后,在monitoring目录下执行:
docker compose up -d启动后依次检查三个界面:
- Prometheus Web界面:
http://服务器IP:9090,这是Prometheus自带的控制台,虽然简陋,但很实用。 - Grafana界面:
http://服务器IP:3000,默认账号admin,密码就是上面环境变量里设置的。 - Alertmanager界面:
http://服务器IP:9093,用于查看告警接收和分组情况。
在Prometheus界面的Status -> Targets页面,可以看到所有配置的采集目标,绿色UP表示采集正常,红色DOWN表示无法访问。这个页面是排查采集问题的第一步。在Graph页面,可以输入PromQL表达式进行查询。输入up然后点Execute,会返回每条采集链路的状态,up == 1表示这个目标当前是健康的。验证到这里,基础监控平台就通了。
3. 把服务器资源盯起来:Node Exporter实战
3.1 Node Exporter部署要点
Node Exporter是Prometheus生态里使用率最高的Exporter,专门负责采集Linux服务器的基础资源指标。部署方式在上面Compose里已经体现,单独部署时一条命令即可:
docker run -d --name node-exporter \ --network host \ --restart always \ -v /:/host:ro,rslave \ prom/node-exporter:v1.8.2 \ --path.rootfs=/host注意我在这里用了--network host模式,这样Node Exporter可以直接绑定宿主机的网络端口9100,在容器里被访问时不会经过NAT转换,指标采集更稳定。也可以像前面Compose里一样用端口映射,两种方式都行,但如果服务器上有其他服务占了9100端口,就要改端口并同步修改Prometheus的targets配置。
启动后,在浏览器访问http://目标IP:9100/metrics,你会看到大量形如node_cpu_seconds_total{cpu="0",mode="idle"} 56324.7的文本。这就是Prometheus采集的原始指标,每行一个指标名加一组标签加一个值,格式非常标准。理解这个格式是学习PromQL的基础——指标名决定了“是什么”,标签决定了“是哪一个”。
3.2 CPU、内存、磁盘、网络四类核心指标的查询方法
Node Exporter提供了海量指标,但日常监控中真正高频使用的是这四类。我把最常用的PromQL查询语句整理出来,你可以直接拿到Grafana里建面板,也可以在Prometheus的Graph页面先验证:
CPU使用率(百分比)
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)这条语句的逻辑是:先计算过去5分钟内CPU空闲时间的速率,算出空闲比例,再用100减去空闲比例得到使用率。为什么要用rate()而不是直接取原始值?因为node_cpu_seconds_total是一个不断累加的计数器,比如CPU运行了100秒,它的idle计数可能是60秒,必须通过rate()计算单位时间内的增量才能得到真实的使用率,这个思想贯穿整个PromQL查询。
内存使用率(百分比)
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100内存指标不用rate(),直接拿当前可用内存和总内存做比值就行。注意这里用的是MemAvailable而不是MemFree,因为MemFree只是真正空闲的物理内存,而MemAvailable还考虑了可回收的缓存内存,更接近“可用”的真实含义。这也是经验之谈,用MemFree算出来的使用率往往会偏高,误导你做出扩容决策。
磁盘使用率(百分比)
(1 - (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay|squashfs"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay|squashfs"})) * 100这里加了一个fstype过滤,排除掉tmpfs、overlay这类虚拟文件系统,否则你会看到一堆容器层的虚假磁盘分区,干扰判断。生产环境建议加上这个过滤条件。
网络流量(速率)
rate(node_network_receive_bytes_total[5m]) * 8node_network_receive_bytes_total是累计接收字节数,取5分钟速率后再乘以8,得到每秒比特数,也就是我们常说的带宽。除以1024再除以1024可以换算成Mbps。
这些指标查询表达式,我强烈建议你先在Prometheus自带的Graph页面逐条执行一遍,确认数值符合预期后再去Grafana里建面板。直接在Grafana里调试查询,还要同时处理数据源切换、时间范围等问题,效率会低很多。
3.3 在Grafana里快速出图:不要从零画面板
自己从零开始在Grafana里拖拽面板、配置查询、调整图例,不是不行,但效率太低。Grafana社区有大量现成的Dashboard模板,对于Node Exporter,我建议直接导入ID为1860的官方模板,这是最经典的Node Exporter Full模板,CPU、内存、磁盘、网络、系统负载等指标一应俱全。
导入流程很简单:Grafana登录后,在左侧菜单点击Dashboards -> New -> Import,在Import via dashboard ID输入框里填1860,点击Load,然后选择Prometheus数据源,最后点Import即可。整个过程不到一分钟,你就拥有一套专业级服务器监控大盘。
当然,导入模板不代表什么都不用管。第一次导入后,我建议花15分钟逐个面板检查一遍数据是否正常,因为模板里有些面板使用了特定的标签或过滤条件,如果与你环境的命名不一致,可能显示不出数据。最常见的坑是instance标签包含了IP和端口(如192.168.1.101:9100),模板里的变量可能直接用了$instance传参,这时需要检查Dashboard顶部的变量是否正确识别了instance和job。
3.4 Grafana数据源配置中一个容易踩的坑
在Grafana中新建Prometheus数据源时,URL填写http://localhost:9090是很多新手第一次必踩的坑。因为在Docker Compose里,Grafana是一个独立容器,它访问localhost只会访问到它自己容器内部的网络,根本访问不到Prometheus。正确的写法是使用Docker Compose网络内的服务名:http://prometheus:9090。
这是一个非常典型的基础问题,但它引出了一个重要习惯:在容器化部署环境中,服务间通信要用容器名,而不是localhost或宿主机的IP。服务名是Docker内部DNS自动解析的,只要两个服务在同一个网络里,就一定能互通。
4. 网络设备也能纳管:SNMP Exporter监控交换机
4.1 没有Agent,怎么监控交换机
服务器上可以装Exporter,但交换机、路由器这种网络设备可没法装任何Agent。它们是封闭的系统,唯一通用的管理协议是SNMP(简单网络管理协议)。Prometheus官方提供了SNMP Exporter,它的任务就是把SNMP协议返回的OID数据转换成Prometheus metrics格式。
我印象最深的是一次给一台核心交换机做监控的经过。交换机型号比较老,厂商的网管平台早已停止维护,Web界面也打不开,但SNMP还正常工作。我用SNMP Exporter顺利采集到了端口流量、端口状态、CPU利用率、内存利用率,还根据端口状态做了一个掉线告警。那次的成功让我意识到,SNMP Exporter几乎是网络设备监控的唯一普适方案。
4.2 SNMP Exporter的配置流程:generator、snmp.yml与module
老版本SNMP Exporter使用静态的snmp.yml文件,里面预先定义好了各种OID的映射关系。新版本的推荐做法是使用generator根据MIB文件生成snmp.yml。不过如果只是监控常见的交换机CPU、内存、接口流量,直接使用官方预编译的snmp.yml已经足够。
首先创建snmp-exporter的配置文件,核心是启用哪些协议版本和模块:
auths: public_v2: community: public security_level: noAuthNoPriv version: 2 auth_v3: username: monitor security_level: authPriv password: "your-auth-password" auth_protocol: SHA priv_protocol: AES priv_password: "your-priv-password" version: 3如果不涉及SNMP v3认证,直接把public_v2配置好就行,社区字符串默认一般是public,实际生产环境建议改掉。在Prometheus主配置中增加一个job:
- job_name: "snmp-switch" static_configs: - targets: - "192.168.1.200" - "192.168.1.201" metrics_path: /snmp params: auth: [public_v2] module: [if_mib] relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: 192.168.1.100:9116这段配置逻辑是:Prometheus采集的目标地址是交换机IP,但它实际发起HTTP请求的地址是SNMP Exporter所在的主机端口9116,真正的交换机IP通过__param_target参数传给SNMP Exporter。这个重打标签(relabel)机制是Prometheus最灵活也最难理解的部分,但掌握之后,几乎任何非常规采集需求都能通过它实现。这里简单理解就行:左侧是最终展示的标签,右侧是实际请求要替换的字段。
4.3 在Grafana中查看交换机指标
SNMP Exporter启动后,可以通过Grafana社区模板快速建一个网络设备大盘,常用模板ID是10361。导入后在变量里选择对应的数据源,就能看到交换机各端口的进出流量、状态、丢包率等信息。交换机接口流量是以ifHCInOctets这种计数器形式存在的,在PromQL里同样需要用rate()去计算速率:
rate(snmp_if_octets_in{ifAlias!=""}[5m]) * 8这里需要说明一下,实际指标名会带有ifIndex、ifName等标签,这些标签可以帮助你在面板中按端口或别名过滤,定位某个具体接口的流量状况。
5. Prometheus如何从OTel Collector收取数据
5.1 OTel Collector到底是什么
OpenTelemetry是当前可观测性领域最热门的开源标准,它统一了Metrics、Logs、Traces三类遥测数据的产生和传输方式。OTel Collector是这个体系里的数据采集和转发组件,负责接收各种格式的遥测数据,经过处理后再导出到不同的后端系统。
这就产生了一个常见需求:业务已经接入了OpenTelemetry,数据进了OTel Collector,但监控大盘还是Prometheus+Grafana,怎么让Prometheus拿到OTel Collector里的数据?我把实际验证过的两种方式都讲清楚。
5.2 方式一:Prometheus主动拉取Collector暴露的metrics端点
OTel Collector的原生配置中自带一个prometheusexporter组件。它会把Collector内部已经统一好的指标,在本地暴露成一个标准的/metricsHTTP端点。Prometheus只需要把它当成一个普通的Target进行采集即可。
OTel Collector配置示例:
exporters: prometheus: endpoint: "0.0.0.0:8889" namespace: "otel" service: pipelines: metrics: receivers: [otlp] processors: [] exporters: [prometheus]Prometheus配置中新增:
- job_name: "otel-collector" static_configs: - targets: ["otel-collector-host:8889"]这种方式的好处是架构简单,复用Prometheus成熟的拉取模型,采集状态一目了然。但要注意,prometheusexporter输出的是OTel指标转换后的Prometheus格式,指标名称、标签在转换过程中可能发生变化。比如OTel的counter类型指标会用_total后缀,histogram会拆成_bucket、_sum、_count。你需要对这些命名规则有心理预期,否则在Grafana里写查询时会找不到预期的指标名。
5.3 方式二:通过Remote Write推给Prometheus生态
另一种思路是让OTel Collector主动把数据推出来。OTel Collector有prometheusremotewriteexporter,它可以将指标数据编码成Prometheus远端写入协议的格式,推送到支持Remote Write接收的后端。
不过这里要重点提醒:原生的Prometheus Server本身不提供Remote Write接收API,它只有Remote Read和Remote Write的发送端能力。要想接收远端数据,需要在Prometheus前面挂一层兼容组件,比如Thanos Receive、Cortex、Mimir,或者Grafana Cloud这类托管服务。也就是说,如果只是装了一个裸Prometheus,这个方式需要再引进一套新的组件,复杂度会上升不少。
OTel Collector的remote write配置示例:
exporters: prometheusremotewrite: endpoint: "http://mimir:9009/api/v1/push"这种方式适合已经搭建了Thanos、Mimir这类长期存储和集群方案的场景,数据推送后可以跨实例查询、长期存储。如果只是单机Prometheus+Grafana,我建议优先采用方式一,少引入一个组件就少一份运维负担。
5.4 实际项目中的选择经验
我在一个业务监控项目中同时遇到过这两种需求。线上服务通过OTel SDK上报业务指标,数据进入OTel Collector后,我用了方式一,让Prometheus直接拉取Collector的/metrics端点,简单可靠,告警和目标状态都能复用现有的Prometheus体系。另一个项目的需求是数据需要长期保存并做多集群汇总,我才引入Thanos并采用方式二,把Collector的数据推给Thanos Receive。
如果你判断不了该用哪种,我的建议是默认选择方式一。理由很直接:Prometheus的拉取模型使得采集失败、Target离线这些问题能被及时发现,而推送模型下,数据可能悄悄丢了却无人察觉。监控系统本身要先保证确定性,其次再考虑扩展性。
6. 告警规则配置详解:让系统自己发现异常
6.1 告警规则的结构拆解
光有可视化大盘是不够的,监控系统的最终目标是能够主动发现问题并通知到人。Prometheus的告警体系由两部分组成:Prometheus Server负责根据规则文件评估告警条件,Alertmanager负责把触发的告警发出去。规则文件结构如下:
groups: - name: node-alerts rules: - alert: HighCPUUsage expr: | 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85 for: 5m labels: severity: warning annotations: summary: "{{ $labels.instance }} 的CPU使用率超过85%" description: "实例 {{ $labels.instance }} 的CPU使用率已持续5分钟超过85%,当前值 {{ $value | humanizePercentage }}"告警规则中几个关键字段的含义:
alert:告警名称,必须唯一标识这条规则。expr:触发条件表达式,满足条件时产生告警。for:持续时间,表达式持续满足该时长后才真正触发告警。这是防止抖动误报的核心参数。labels:附加标签,可以按严重级别、所属团队分类。annotations:告警内容描述,支持用模板语言动态填充实例名和当前值,告警消息里能直接看到是谁出了问题。
6.2 直接抄作业的几条常用告警规则
我整理了一份Linux服务器监控的基础告警规则集,涵盖最常见的几个风险场景,你可以直接复制到规则文件中使用,再根据实际环境调整阈值。
groups: - name: server-alerts rules: - alert: InstanceDown expr: up == 0 for: 1m labels: severity: critical annotations: summary: "实例 {{ $labels.instance }} 失联" description: "Prometheus无法从 {{ $labels.instance }} 采集到任何指标,已持续1分钟。" - alert: HighCPUUsage expr: | 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85 for: 5m labels: severity: warning annotations: summary: "CPU使用率过高" description: "实例 {{ $labels.instance }} 的CPU使用率持续超过85%,当前值 {{ $value }}%" - alert: HighMemoryUsage expr: | (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90 for: 5m labels: severity: warning annotations: summary: "内存使用率过高" description: "实例 {{ $labels.instance }} 的内存使用率持续超过90%,当前值 {{ $value }}%" - alert: DiskUsageHigh expr: | (1 - (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay|squashfs"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay|squashfs"})) * 100 > 85 for: 5m labels: severity: warning annotations: summary: "磁盘使用率过高" description: "分区 {{ $labels.mountpoint }} 的使用率超过85%,当前值 {{ $value }}%"这几条规则是我自己在生产环境里一直在用的,阈值都是根据实际经验调整过的。CPU和内存设85%、90%,可以保持合理的预警提前量;实例失联告警的for设为1分钟,避免网络抖动误报,又不会让问题拖太久才被发现。
6.3 Alertmanager:把告警发到钉钉或企业微信
规则触发了只是第一步,重要的是把告警推送到人的手机上。Alertmanager负责这最后一公里,它支持Email、企业微信webhook、钉钉webhook、Slack等多种通知渠道。我这边最常用的是通过webhook转发到钉钉群,配置示例如下:
route: group_by: ["alertname"] group_wait: 30s group_interval: 5m repeat_interval: 4h receiver: "webhook-dingtalk" receivers: - name: "webhook-dingtalk" webhook_configs: - url: "http://your-webhook-service:8080/dingtalk" send_resolved: true这个配置里有几个时间参数值得细说:
group_wait:同一组告警首次发送前等待的时间,用于缓冲,把短时间内同一组的多条告警合并成一条发送。group_interval:同一组告警中新增一条告警后,下一次发送的间隔。repeat_interval:同一告警重复通知的间隔,避免同一个问题每隔几分钟就轰炸一次。设为4小时比较合理。
实际生产环境我还会在钉钉群里加一个通知机器人,把告警内容转成Markdown格式。这一步需要自己写一个简单的webhook转发服务,把Alertmanager的JSON告警内容解析后重新包装成钉钉的markdown消息格式。这里不展开写完整代码,但思路很通用,网上也有大量现成实现。
6.4for字段为什么要慎重设置
for字段的作用是等待表达式持续成立一段时间,再触发告警。它的好处是防止瞬时抖动引起的误报,但设置不合理也会带来问题。
举个例子,磁盘使用率告警我一开始设的是for: 5m,结果有一次磁盘满了,系统在5分钟内迅速写满,告警还没触发,服务就已经挂了。后来我把磁盘告警的for改成了1分钟,其他CPU、内存类概率性抖动的指标继续保留5分钟。所以我的经验是:for的取值要根据指标的波动特性和问题的危害程度来定。CPU、内存这类瞬时波动大的指标,可以设置较长;而磁盘使用率、实例失联这类一旦发生就可能是严重故障的指标,要尽量短。
另外还有一点,for的时间长度必须大于scrape_interval至少两三个采集周期。如果采集间隔是15秒,for设置为10秒,那么Prometheus可能只采到一次数据就触发了告警,抖动导致误报的概率就很大。
7. 常见问题与排查技巧实录
7.1 Target显示DOWN
这是部署期最常遇到的问题。我的排查顺序是:先确认目标IP端口在网络层面能通,在Prometheus服务器上执行telnet <目标IP> <端口>;然后检查目标服务的metrics端点是否能访问,直接浏览器打开http://<目标IP>:<端口>/metrics,看看是否有内容返回;最后检查Prometheus配置中的目标地址是否写错,特别是端口号,常见的错误是把node_exporter的9100写成了9101或者9090(9090是Prometheus自己的端口)。
还有一个隐蔽问题:目标机器防火墙没有放行对应端口。在云环境或企业网内,安全组规则和本地防火墙是双重关卡,即使Exporter运行正常,外部也无法访问。这种情况在Prometheus的Targets页面看到的数据是DOWN (connection refused)或context deadline exceeded,后者通常表示网络不通或防火墙静默丢包。
7.2 数据有指标但Grafana面板没数据
很多人在Grafana导入了模板,面板却一直显示No data。首先检查Grafana数据源是不是选的Prometheus,且URL正确。在数据源设置里点击Save & Test,如果提示HTTP 200说明连接成功。然后去Explore页面手动执行一条简单的PromQL,比如up,如果Explore里都没数据,那就是数据源或Prometheus采集的问题;如果Explore里有数据但面板没数据,问题基本出在Dashboard模板的变量配置上。
变量配置是Grafana最常见的数据黑洞。导入的模板通常会定义$node、$job这类变量,如果这些变量的查询结果为空,面板就什么都不会显示。处理方法是在Dashboard的Settings -> Variables里检查变量定义,确保查询条件匹配你的实际指标标签。比如变量定义是label_values(node_uname_info, nodename),而你环境里没有这些指标,就要换一种标签来源,通常改成label_values(up, instance)更通用。
7.3 告警规则格式错误导致告警不生效
Prometheus规则文件是YAML格式,缩进错误、字段名拼错都会导致整个规则组无法加载。在Prometheus界面的Status -> Rules页面,如果没有任何规则显示或规则组下面报错,说明配置有问题。最简单的排查方式是加载成功后查看Prometheus容器日志:
docker logs prometheus --tail 100 | grep -i alert我在实际部署中遇到过一次比较隐蔽的错误:规则文件里某个告警的expr写错了一个引号,Prometheus启动时只报警告,但整个group被跳过,所有规则都不生效。从那以后我养成了一个习惯:每次修改告警规则,都会在Prometheus的Status -> Rules确认规则数量正确,并且随便查找一个规则名,确保它出现在列表里。
7.4 TSDB磁盘占用过高
Prometheus默认保留15天数据,但我看到过不少案例,默认配置下跑了一段时间,磁盘被TSDB数据撑爆。内存单位与磁盘估算能力是监控运维的基本功。这里给出一个粗略的估算经验:一个采集1000个时间序列的实例,每天大约占用1到2GB磁盘空间。如果目标较多、采集频率高,数据量会成倍增长。
建议在启动参数中显式设置保留时间:
--storage.tsdb.retention.time=15d --storage.tsdb.retention.size=50GBretention.size是按存储容量限制数据保留,当数据目录超过指定大小,Prometheus会优先删除最老的数据块。两个参数配合使用,既保证时间跨度,又限制磁盘占用。设置完之后,用du -sh /data/prometheus定期观察数据目录大小,建立容量监控,这个问题基本就能避免。
7.5 告警重复轰炸或长时间收不到
告警重复轰炸绝大多数是Alertmanager的repeat_interval设置太短。我之前一度设为30分钟,有一次网络设备故障导致连续告警,手机半小时震一次,整晚没法睡。之后统一调整为4小时,并且按告警严重级别分成了warning和critical两档,critical的重复间隔可以更短,warning则保持4小时。分组策略group_by也应该合理设置,按alertname和instance组合分组,可以把同一台机器的多个相关告警合并成一条,而不是一条一条地刷屏。
收不到告警则要检查Alertmanager界面http://<IP>:9093的Status页,确认告警是否到了Alertmanager且是否成功发送。如果状态显示为Notified,说明通知已经发出,问题大概率出在webhook服务的转发逻辑上;如果还在Active状态,说明正在等待接收者处理,需要检查路由配置和接收人配置是否匹配。
8. 写在最后的实际体会
这套Prometheus监控平台从搭建到现在,支撑了我这边几十台服务器、上百个业务接口的日常监控。回看整个过程,最有价值的不只是它帮我抓出了多少次磁盘将满、多少次服务异常,而是它把“运维靠感觉”变成了“运维靠数据”。以前别人问我某台机器最近负载怎么样,我只能说“还行吧”;现在我可以直接把最近一周的CPU、内存、磁盘趋势图拉出来,一目了然。
最后再分享一个小技巧:我建议每个新手在布置完监控平台后,都主动做一次“拔网线测试”——手动停掉一个Node Exporter容器,观察Prometheus的Targets页面能否及时显示DOWN,告警能否在预期时间内推送到手机上。把这条链路完整走通一遍,你对整个监控体系的信任度会完全不同。等你摸熟了这套链路,再往Kubernetes监控、业务指标埋点、长期存储与多集群统一监控等方向扩展,就会顺理成章得多。