☰
平台雷达PLFM_RADAR:自己动手搭建平台运行状态监控系统
2026/11/2 10:35:43 网站建设 项目流程

从最早注意到“PLFM_RADAR”这个代号开始,身边就有朋友问我:“这名字到底是什么意思?”其实拆开看很简单——PLFM取的是Platform的前四个字母,RADAR就是雷达。中文直译过来就是“平台雷达”。我做这个项目的初衷,是想给自己负责的几个线上平台装一双“眼睛”,让它们运行得怎么样、哪里有隐患、什么时候需要干预,都能在第一时间被我看见。熟悉监控系统的人应该都知道,很多团队不是没有监控,而是监控太多太散,告警一堆却没人看,真出事儿的时候反而被淹没在噪音里。PLFM_RADAR要解决的,正是这个问题。

这套系统不是一个商业产品,也不是什么高大上的科研项目,而是一套可以自己动手搭建的平台运行状态监控与洞察方案。它做的事情可以概括成三件:采集平台的关键运行指标,基于指标计算健康度与趋势,在异常发生时通过分级告警通知到对应的人。适合谁看?如果你正在维护一个Web服务、小程序后端、物联网设备接入平台,或者任何你觉得“心里没底”的系统,这篇文章里的思路和代码骨架,应该都能直接用上。

1. 为什么叫“平台雷达”:从监控痛点说起

1.1 监控系统的三个认知误区

先说个扎心的现象。很多团队一提到监控,第一反应是“我们有”,然后打开Grafana给你看十几个面板,CPU、内存、磁盘、网络流量,花花绿绿一片。但你要是追问一句:“这些指标异常了然后呢?”答案往往就变成了“哎,主要是人肉盯”。这就是典型的“有监控,但没有在线监控”。

我做PLFM_RADAR之前,自己踩过不少坑,总结下来有三个常见的认知误区:

  • 误区一:指标多等于监控完善。采集了一百个指标,但没有一个能准确反映“用户现在能不能正常下单”,那这一百个指标的价值就很有限。监控的终极目标是回答业务问题,而不是展示技术参数。
  • 误区二:告警不分级,全部一个通道。磁盘快满了也钉一下,服务挂了也钉一下,发版后QPS小幅波动也钉一下。久而久之,接收人看到告警的第一反应是“又来了”,甚至直接把机器人屏蔽。这会直接废掉整个告警体系。
  • 误区三:重展示、轻处理。把精力花在把仪表盘做得好看上,却没有定义异常发生后的响应流程。真正出事的时候,大家不知道该谁上、该先看哪儿。

PLFM_RADAR在设计的时候,刻意避开了这三个误区。它的核心不是“多采集”,而是“聚焦关键信号、算出健康度、自动分级触达”。

1.2 雷达隐喻:PLFM_RADAR的核心设计哲学

雷达这个东西很有意思。它在工作时不断向外发射电磁波,接收回波,然后从杂波中识别出真正的目标,持续跟踪并判断威胁等级。民用机场的雷达甚至还要区分出“这是一架客机”还是“一只鸟”。

PLFM_RADAR的灵感正是来自这个机制。如果把平台运行过程中产生的大量数据比作“回波”,那么真正值得关注的“目标”就是那些会影响用户体验和系统稳定性的异常信号。系统要做的,不是把所有回波都显示在屏幕上,而是从数据中识别异常、判断威胁等级,然后按优先级呈现给值班人员。

这个设计哲学落实到具体功能上,可以归纳成下面这张对应关系:

雷达机制PLFM_RADAR的实现
发射信号定时采集指标(采集器、Exporter、探针)
接收回波写入时序数据库,做聚合与存储
识别目标PromQL规则计算健康度,识别异常模式
威胁分级按规则匹配告警级别(P0/P1/P2)
跟踪预警告警路由、抑制、静默与自动恢复通知

这套机制的好处是:团队成员不需要记住几十个指标的含义,只需要理解“健康度”这一个数——就像雷达屏幕前的人只需要看那一个亮点。

2. 整体架构与方案选型

2.1 分层架构设计

PLFM_RADAR的整体架构,我把它分成了四层。每一层各司其职,又通过标准接口连成一个完整的链路。

  • 采集层:负责从平台各个组件里拿到原始指标。前端用探针采集页面性能,后端用Exporter采集服务状态,数据库层采集慢查询和连接数,消息队列层采集积压量。这一层不关心数据怎么用,只负责“拿到并送出”。
  • 存储与计算层:负责承接指标写入,完成聚合计算与规则匹配。我用的是Prometheus搭配Thanos做长期存储,计算引擎直接用PromQL。规则引擎每分钟跑一轮,把“过去5分钟错误率超过5%”这种条件翻译成告警事件。
  • 触达层:负责把告警送到正确的人手里。这里做的是分级和路由:P0告警直接电话加短信,P1告警钉钉群加短信,P2告警只在钉钉群显示。接收对象、升级策略都是可配置的。
  • 可视化层:负责给值班人员和研发提供统一的“雷达屏幕”。如果一个服务有问题,打开面板在30秒内要能看出影响范围、错误分布和链路哪一段出了问题。

选这套分层的初衷,是为了让每一层都能独立演化。比如采集层今天用StatsD,明天想换OpenTelemetry,只要接口格式不变,上层完全不受影响。存储层想加节点扩容,也只改配置不动代码。

2.2 技术选型:为什么是Prometheus + Grafana + Alertmanager

关于技术选型,我做了很多对比,最后定下的组合是Prometheus + Grafana + Alertmanager,另配一个自研的告警通知网关。简单说说取舍逻辑。

Prometheus这边,我几乎没有犹豫。原因有几点:一是它的数据模型天然适合这类平台监控场景——所有指标都是带标签的时间序列,聚合查询用PromQL表达非常顺手;二是它强大的服务发现能力,配合Consul或Kubernetes,新服务上线后几乎不需要额外配置就能自动纳入采集范围;三是社区生态成熟,node_exporter、mysqld_exporter、redis_exporter这些现成组件可以直接用,不需要从零造轮子。

Grafana作为可视化层也是顺理成章的选择。它有现成的图表库、告警面板和权限管理,支持把Prometheus、Loki、Elasticsearch数据源摆在同一张大屏上。最关键的是它有一个很大的社区面板库,像JVM监控、Nginx监控这些热门面板,搜索即用,省掉很多开发时间。

Alertmanager的核心价值不在“发通知”,而在“压制噪音”。它支持分组、抑制、静默三种机制:同一个服务多个实例同时挂掉,可以合并成一条告警;根因告警触发后,衍生告警会被自动抑制;夜间发版时可以提前设置静默窗口,避免误报打扰。这些能力对于一套真正要落地运行的监控系统来说,不是锦上添花,而是刚需。

最后补一个自研的告警通知网关,用Python写了个简单的Web服务,接收Alertmanager的Webhook,按照配置好的规则调用企业微信、钉钉、短信或电话接口。为什么不自研全部?因为通知渠道五花八门,标准协议不统一,单独抽出来做一个网关,隔离耦合,后续替换任何一家短信服务商,都只改网关里一个模块就行。

3. 核心细节解析与实操要点

3.1 指标采集层:从零写一个自定义Exporter

现成的Exporter覆盖了大多数中间件,但总有一些业务指标是现成组件拿不到的。比如“当前正在处理中的订单数”或“排队等待AI识别的图片数量”。这时候就需要写自定义Exporter。我用的方案是Prometheus官方推荐的Python client库,因为它简单、可控、易维护。

一个最小的自定义Exporter长这样:

from prometheus_client import start_http_server, Gauge import time import random # 定义指标:订单处理延迟(秒) ORDER_LATENCY = Gauge( 'plfm_order_process_latency_seconds', 'Current order processing latency in seconds', ['region'] ) def update_metrics(): # 假设从业务DB或Redis里取真实数据 # 这里用随机值模拟 for region in ['cn-east', 'cn-south', 'ap-southeast']: ORDER_LATENCY.labels(region=region).set(random.uniform(0.1, 5.0)) if __name__ == '__main__': start_http_server(9101) while True: update_metrics() time.sleep(15)

几个关键点说一下。采集间隔不要设置得太短——很多新人容易把间隔设成1秒,觉得“实时性更好”,但实际上15秒对于绝大多数业务指标已经足够,而且能显著降低对DB的查询压力。如果你的平台对实时性要求真那么高,那就不应该靠轮询,而是应该上事件流。

指标命名上也有讲究。Prometheus的命名规范是“命名空间_指标名_单位”,例如plfm_order_process_latency_seconds。其中plfm是命名空间,order_process_latency是指标名,seconds表明单位是秒。遵循这个规范,后续写PromQL和看图表都会省很多事。

3.2 规则计算层:SLO烧钱指标怎么设计

指标采回来了,怎么判断有没有问题?这层是PLFM_RADAR里最需要花心思的地方。我的做法是:不直接对原始指标设阈值,而是先计算出一组“烧钱指标”——即那些一旦出问题,就会直接带来经济损失的指标。

什么叫烧钱指标?举个例子:用户下单接口的P95延迟。如果P95从300毫秒涨到3秒,用户等待时间变长,跳出率上升,订单转化率下降,这就是在烧钱。再比如支付回调成功率,每掉1个百分点,可能就意味着几万块的退款纠纷和客诉成本。

基于这个思路,我定义了三类核心指标规则:

第一类:可用性规则。直接反映服务是否可用,比如:

groups: - name: plfm_availability rules: - alert: OrderServiceHighErrorRate expr: | sum(rate(http_requests_total{job="order-service", code=~"5.."}[5m])) / sum(rate(http_requests_total{job="order-service"}[5m])) > 0.05 for: 5m labels: severity: P1 annotations: summary: "订单服务5分钟错误率超过5%"

这条规则的含义是:统计order-service最近5分钟内5xx错误请求的占比,如果持续5分钟都超过5%,就触发P1告警。for: 5m这个参数很关键,它能过滤掉偶发抖动,避免因为单次抖动就炸群。

第二类:容量规则。反映资源和水位是否逼近极限。比如消息队列积压量超过阈值、连接池使用率超过90%,这类指标通常用Gauge类型采集,规则写法类似:

- alert: OrderMQBacklogTooHigh expr: plfm_mq_backlog_messages{queue="order-created"} > 5000 for: 3m labels: severity: P2

第三类:饱和度规则。反映系统是否还能继续处理新请求。典型的指标是CPU使用率、GC暂停时间、线程池活跃度。这类指标不是立刻致瘫,但如果不处理,半小时后可能就会慢慢把系统拖垮。所以规则上我会把阈值设得比容量类更保守,提前预警。

规则设计的一个核心心法是:每条规则都必须回答清楚“收到这条告警后,该做什么”。如果一条告警触发后,接收人都不知道要查什么、修什么,那这条规则就是噪音,应该删掉或者重写。

3.3 可视化层的雷达屏:30秒定位问题

可视化这部分,我做了一个类似雷达大屏的总览页面,分层展示:

  • 第一层:全局健康度。用一个大数字显示当前平台的综合健康得分。这个分数由各核心服务的可用性、错误率、延迟和容量水位加权计算得出,满分100,低于80就开始变黄,低于60变红。
  • 第二层:服务拓扑。每一个依赖关系画成一条线,线的颜色代表当前链路是否健康。比如用户服务 → 订单服务 → 支付服务这条链路,如果某个环节延迟飙升,对应的线和节点会立即变红,值班人员一眼就能看到问题出在哪一跳。
  • 第三层:告警滚动列表。按时间倒序展示当前未恢复的告警,每条告警都带有业务含义的描述,例如“订单服务P95延迟突破1秒已达10分钟”“支付回调成功率跌至92%”。点击告警可以直接跳到对应的Grafana面板或者日志查询页。

实现上,Grafana的变量模板很好用。我会把job、region、service这些维度都做成下拉变量,面板中使用变量拼接PromQL,这样一张面板就能覆盖不同服务、不同区域的切换,不需要每个服务单独配一张图。

4. 实操过程:一步步把雷达跑起来

4.1 整体部署流程与目录规划

PLFM_RADAR整套系统的部署顺序,我建议按“存储与计算 → 采集 → 告警 → 展示”这个顺序来做。先有数据,再谈展示和告警,逻辑比较顺。

我自己的服务器上用的是Docker Compose来管理这套组件,目录结构如下:

/opt/plfm-radar/ ├── docker-compose.yml ├── prometheus/ │ ├── prometheus.yml │ └── rules/ │ ├── availability.yml │ └── capacity.yml ├── grafana/ │ ├── datasources.yml │ └── dashboards/ ├── alertmanager/ │ └── alertmanager.yml └── gateway/ ├── notify_gateway.py └── config.yaml

Docker Compose的好处是环境隔离好、迁移方便,而且每条组件都可以单独升级。如果团队规模小,一台4核8G的机器就能跑完整套监控,实测这套组合的常驻内存大约在3GB左右,资源占用很可控。

4.2 Prometheus采集配置关键点

prometheus.yml里最关键的是采集配置。我的简化版本大致是这个样子:

global: scrape_interval: 15s evaluation_interval: 15s rule_files: - "rules/*.yml" scrape_configs: - job_name: 'order-service' consul_sd_configs: - server: 'localhost:8500' tags: ['order-service'] relabel_configs: - source_labels: ['__meta_consul_service'] target_label: 'service'

这段配置里比较值得讲的是relabel_configs。Consul服务发现到的每个实例,默认只有__meta_consul_service这类标签,我把它重写成service=order-service。这样后续在PromQL里统一用{service="order-service"}筛选,语义清晰。如果不用Consul,也可以用static_configs直接写IP列表,或者用file_sd_configs从JSON文件里读取节点列表,适合非容器环境。

evaluation_interval要特别留意。rules里的告警规则每隔这个时间会重新计算一次。我习惯和采集间隔保持一致,都是15秒,这样计算用的数据和采集的数据是同步的,不容易出现时间差。

4.3 Alertmanager路由规则配置

Alertmanager的配置是我在整个项目里花时间打磨最多的部分。它的路由树就像电话总机一样,决定了每通电话该转到哪个分机。

route: group_by: ['service', 'alertname'] group_wait: 30s group_interval: 5m repeat_interval: 4h receiver: 'default' routes: - matchers: - severity =~ "P0|P1" receiver: 'phone-sms' - matchers: - severity = "P2" receiver: 'im-group'

解释几个关键参数。group_by指定了哪些告警会被归并成一组,同一个服务的多条告警会合并;group_wait是同一组里第一条告警后等待多久再发送,这个缓冲期会把短时间内同时触发的告警合并成一条,减少轰炸;repeat_interval是同一告警重复通知的间隔,4小时的意思是一条告警没恢复,每4小时才提醒一次,而不是每分钟刷屏。

告警接收人配置在所谓的receiver里,这里只列了两个,实际上生产环境还可能增加“值班电话组”“研发负责人”等:

receivers: - name: 'phone-sms' webhook_configs: - url: 'http://gateway:8080/notify' send_resolved: true - name: 'im-group' webhook_configs: - url: 'http://gateway:8080/notify?channel=im' send_resolved: true

send_resolved: true这个参数容易被忽略,但非常重要。它表示告警恢复时也要发送一条“已恢复”通知,让值班人员明确知道事情已经结束了,否则很多人会一直带着焦虑查看告警群。

4.4 告警通知网关实现

Alertmanager本身不直接发短信和电话,它通过Webhook把告警事件抛给接收端。我的通知网关就是一个Python Flask服务,负责把Alertmanager发来的JSON转换成不同渠道的消息。

核心代码大致如下:

from flask import Flask, request, json import requests app = Flask(__name__) def send_dingtalk(title, content): # 调用钉钉机器人接口 webhook_url = "https://oapi.dingtalk.com/robot/send?access_token=xxx" payload = {"msgtype": "text", "text": {"content": f"[{title}] {content}"}} requests.post(webhook_url, json=payload) @app.route('/notify', methods=['POST']) def handle_alert(): data = request.get_json() for alert in data.get('alerts', []): severity = alert['labels'].get('severity', 'P2') title = alert['annotations'].get('summary', 'No Summary') if severity in ('P0', 'P1'): send_dingtalk(f"P{severity}告警", title) # 这里再调短信接口 else: send_dingtalk("一般告警", title) return "OK", 200 if __name__ == '__main__': app.run(host='0.0.0.0', port=8080)

生产环境里,我还会在这个网关里加一个“告警指纹”的概念——根据服务名+告警名+短描述生成一个hash,存进Redis里设置过期时间,避免同一事件被重复传入时产生重复通知。这个设计帮我挡掉了不少告警重复轰炸的投诉。

5. 常见问题与排查技巧实录

5.1 告警风暴的抑制与静默

监控上线后的第一次高峰,一定不是业务问题,而是告警风暴。我记得有一次因为Redis抖动,几十条不同服务的告警在五分钟内接连涌入,群里瞬间刷了几百条消息。当时如果不是提前做了分组和抑制,值班人员可能直接崩溃。

Alertmanager里的抑制规则,简单说就是“当某条告警存在时,暂时不发送另一条告警”。一个很实用的场景:如果数据库实例挂了,那么依赖这个库的所有服务都会报错。此时应该只发送“数据库实例挂了”这条跟因告警,而把其他几十条“连接失败”的衍生告警全部抑制住。

配置示例:

inhibit_rules: - source_matchers: - severity = "P0" - alertname = "DatabaseInstanceDown" target_matchers: - severity = "P0" equal: ['service']

翻译成大白话就是:如果存在DatabaseInstanceDown这条P0告警,则同一服务下所有其他P0告警都先不发送,等根因恢复后再一并考虑。这套机制上线后,我们的告警数量直接下降了七成。

5.2 时间序列基数爆炸

Prometheus新手很容易踩的一个大坑就是标签基数。我在PLFM_RADAR早期也踩过:当时为了追踪用户维度,我在指标上加了user_id标签,结果每个用户一条时间序列,几千个用户一跑,Prometheus的磁盘占用立刻飙升,查询速度急剧下降。

Prometheus官方建议是单指标时间序列数控制在10万以下。如果确实需要按用户维度分析,正确做法是把用户分桶,比如按城市、按会员等级、按设备类型,而不是精确到每个人。我还是建议在采集端直接做聚合,而不是在存储端扛。一旦进入高基数,再好的存储也扛不住。后来我把所有包含高基数标签的指标全部砍掉重设计,确认每个指标的标签组合上限不超过1000,问题才彻底解决。

5.3 告警延迟:为什么有时过了十几分钟才收到

告警延迟这个问题,排查了很长时间。现象是DB确实出问题了,Prometheus规则也在跑,但Alertmanager过了十来分钟才把消息发出来。查了一圈才发现,问题出在group_wait和repeat_interval这两个参数的叠加效应上。

如果一条告警首次触发后一直处于“未恢复”状态,并且repeated到一定程度,后续的重复通知间隔会拉得很长。而此时如果新告警被归并到了同一个group里,它就必须等待这个组的repeat_interval周期结束才能再次发送——这就会造成延迟。

解决办法是对告警分组做更细的拆分。不同业务的告警不要共享同一组,或者把repeat_interval调短一些,比如从4小时改成1小时。另一个经验是:P0级告警应该单独使用一个group,并且不进任何抑制规则的“等待”折叠,这样一个电话告警就能做到秒级触达,立刻唤醒值班的人。

5.4 告警恢复通知为什么没收到

很多人配置好了告警发送,却一直没收到“恢复”通知。排查的逻辑很简单:先确认Alertmanager收到的Webhook事件里有没有status=resolved,如果没有,那就是Prometheus规则那边没有正确触发恢复——通常是for时长太短或者表达式在恢复时返回空值。

比较常见的坑是:比如错误率从5%降到2%,告警确实应该恢复,但如果PromQL表达式算出来是NaN(比如当分母的请求数临时为0),Prometheus会认为表达式无数据,此时告警既不触发也不恢复。解决方法是给PromQL加上or on() vector(0)之类的兜底,确保表达式的值永远有定义。

6. 实操心得与扩展想法

6.1 先有有效告警,再完善图表

这是我的一个重要体会。很多团队做监控都喜欢从仪表盘开始,把图表弄得特别全面,但忽略了告警的有效性。PLFM_RADAR开发过程中,我是反过来的,先盯住最核心的两三条告警规则,跑通整个告警链路,确认“一条真实告警能被正确的人及时收到”,然后才回头补图表和面板。

这样做的原因是:告警链路涉及Prometheus、Alertmanager、网关、渠道四个环节,其中任何一个地方配置不当,整条链路都是断的。先把告警跑通,相当于先把雷达的主雷达开机了,后面加多少辅助显示都是锦上添花。反过来如果先做图表,后面才发现告警链路不通,前面做的那些展示面板其实都帮不上危急时刻。

6.2 告警文案:写清“是什么、影响谁、怎么办”

告警文案这件事,往往是最被低估的细节。我见过太多告警通知,标题只写“CPU高于90%”,正文空空如也,收到的人点开也不知道该干嘛。

我的写法是固定一个模板:发生了什么 + 影响什么 + 优先查看哪块 + 检查谁负责。比如:

订单服务错误率5分钟达到6.8%,影响到C端用户下单、下单接口成功率低于99%。请优先检查order-service的日志和最近5分钟的错误堆栈。服务负责人:刘工。

这样一段文案,值班的人哪怕是新人,看到后也知道下一步该干什么。把这些文案作为告警规则的annotations字段去维护,成本极低,但价值极高。

6.3 扩展方向:从监控到智能预警

PLFM_RADAR目前的定位还是“监控告警系统”,但我已经在计划做下一阶段的扩展,方向有两个。一个是基于历史数据的趋势预测,比如通过过去两周的流量曲线预测未来一小时的负载,提前扩容而不是等CPU报警后再处理。另一个是异常检测的自动化,用一些简单的统计方法(比如3σ法则、同比环比)替代固定阈值,减少人工调整阈值的维护成本。

不过话说回来,这套从0到1的搭建过程,最受益的不是最终跑起来的系统本身,而是整个过程中对平台运行状态的重新梳理。你会被迫把每个服务的依赖关系画清楚,把每个核心指标的业务含义想明白,把每条告警的价值判断一遍,这件事本身,就是给平台做了一次深度的健康体检。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询