1. 从“monitor”这个词说起它到底在解决什么问题第一次看到“monitor”这个标题很多人脑子里蹦出来的可能是显示器或者某个监控大屏。但在我带过的几个项目里monitor 这个词出现的场景往往更朴素——它就是一个“盯着系统看”的东西。你写了一个服务跑起来了然后呢它有没有在跑跑得稳不稳有没有偷偷报错内存是不是在涨这些问题光靠console.log是回答不了的。monitor 就是用来回答这些问题的。我自己的理解是monitor 本质上是一套可观测性基础设施的最小实现。它不一定非得是 Prometheus 加 Grafana 那种重型组合也可以是一个几十行的脚本定时去 ping 一下你的服务把结果写进一个文件或者发个通知。关键在于它让你从“盲跑”变成“有数”。这个学习笔记的价值就在这儿它记录的是一个人从零开始搭一套监控体系时脑子里那些弯弯绕绕——为什么选这个方案、为什么这个参数要这么设、踩了哪些坑。适合谁看如果你刚接手一个线上服务或者自己写了个小工具想让它稳定运行又或者你正在准备面试被问到“你怎么保证服务可用性”这篇笔记里的思路和实操细节都能直接拿去用。我不打算讲太玄乎的理论就从最土的办法开始一步步把 monitor 这件事说透。2. 监控体系的核心设计思路拆解2.1 先想清楚你要监控的到底是什么很多人一上来就装 Prometheus结果发现指标一大堆真出问题了还是不知道看哪个。我的经验是先别急着选工具拿张纸把你要监控的对象列出来。通常分四层基础设施层CPU、内存、磁盘、网络。这层最通用任何机器都需要。服务进程层进程还在不在、端口通不通、响应时间多少。业务逻辑层订单量、登录失败率、队列积压数。这层跟具体业务强相关。用户体验层页面加载时间、API 成功率、错误码分布。这四层的优先级是递减的。刚开始做 monitor先把前两层搞定就能覆盖 80% 的故障场景。我见过太多人一上来就搞业务大盘结果机器磁盘满了都不知道服务直接挂死。提示如果你只有一台机器、一个服务别碰分布式监控那一套。一个 shell 脚本加 cron 就能解决 90% 的问题。工具越简单出故障的概率越低。2.2 推还是拉两种数据采集模式的取舍监控数据的采集方式就两种推push和拉pull。这个选择会直接影响你后面的架构。推模式是服务主动把指标发给监控端。好处是监控端不用知道有多少个被监控对象防火墙也好配。坏处是如果服务挂了它没法告诉你“我挂了”——因为它已经没能力推了。所以推模式通常需要配合一个“心跳”机制比如每 30 秒没收到数据就告警。拉模式是监控端定时去问服务要指标。好处是监控端掌握主动权服务挂了一眼就能看出来。坏处是监控端得维护一个目标列表而且如果目标太多采集压力会集中。我自己的选择是基础设施层用拉业务事件用推。机器指标用拉模式因为机器一般不会频繁变动业务事件比如“用户支付成功”这种用推模式更自然发生一次推一次。2.3 告警阈值怎么定别拍脑袋看历史数据定阈值是最容易拍脑袋的地方。我见过有人把 CPU 告警线设成 80%结果业务高峰期天天告警最后大家都麻木了。正确的做法是先跑一周把数据存下来看 P95 和 P99 是多少再往上加 20% 作为告警线。举个例子你的服务响应时间 P99 是 200ms那告警线可以设在 300ms。如果连续 3 个采集周期都超过 300ms才触发告警。这样既能抓住真问题又不会被毛刺干扰。注意告警一定要有“静默期”和“恢复通知”。不然半夜被叫醒处理完了还得再被叫醒一次告诉你恢复了体验极差。3. 核心细节解析与实操要点3.1 指标类型Counter、Gauge、Histogram 到底怎么选不管你用什么监控系统指标类型基本就这三种。选错了类型后面查询和告警都会很别扭。Counter计数器是只增不减的比如总请求数、总错误数。它的价值在于算“速率”。你直接看总数没意义但rate(request_total[5m])就能看出每秒请求量。Counter 重置比如服务重启是正常现象监控系统一般会自动处理。Gauge仪表盘是瞬时值可增可减比如当前内存使用量、当前在线人数。Gauge 直接看值就行但要注意采样频率太低了会漏掉尖峰。Histogram直方图用来统计分布比如响应时间。它把数据分到不同的桶里最后可以算出 P50、P90、P99。Histogram 的桶边界设置很关键设得太粗看不出细节设得太细又浪费存储。我一般这样设响应时间的桶[10ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, 2.5s, 5s, 10s]。这个范围覆盖了绝大多数 Web 服务的场景。如果你的服务特别快可以把前面的桶再细分。3.2 采集频率15 秒是个甜蜜点采集频率决定了监控的灵敏度和成本。太高了存储和计算压力大太低了故障发现不及时。业界比较通用的做法是15 秒一次。这个频率下一个指标一天产生 5760 个数据点。如果保留 15 天就是 86400 个点。对于几百个指标来说这个量级用一台普通机器就能扛住。但有些场景需要更高频率比如金融交易系统可能需要 1 秒级。这时候就要考虑用更高效的存储或者只对关键指标做高频采集。实操心得刚开始做 monitor别追求秒级。15 秒足够发现绝大多数问题。等你真的需要秒级了说明业务规模已经上来了那时候再升级架构也不迟。3.3 标签设计别把用户 ID 当标签标签Label是监控系统里最强大的功能也是最容易用错的地方。标签的基数Cardinality决定了存储成本。如果你把用户 ID、请求 ID 这种高基数的东西当标签存储会爆炸。正确的标签设计原则是标签值应该是有限的、可枚举的。比如methodGET、status200、regionus-east。这些值的数量是可控的。如果你确实需要追踪单个请求应该用日志Logging而不是指标Metrics。指标适合看趋势日志适合查细节。两者配合使用才是完整的可观测性。3.4 数据保留策略热数据、温数据、冷数据监控数据不是存得越久越好。我一般分三层热数据最近 7 天保留原始精度随时可查。温数据7 天到 30 天降采样到 1 分钟精度用于看趋势。冷数据30 天以上降采样到 1 小时精度或者直接归档到对象存储。这样既能满足日常排查需求又能控制成本。很多监控系统都支持这种分层保留策略配置一下就行。4. 从零搭建一个最小可用监控系统4.1 环境准备与工具选型假设你有一台 Linux 机器上面跑着一个 Web 服务。我们要搭一套最小监控能看 CPU、内存、磁盘、服务存活、响应时间。工具选型上我推荐Prometheus Node Exporter Blackbox Exporter这个组合。原因很简单生态成熟、文档多、社区活跃。Prometheus 负责存储和查询Node Exporter 采集机器指标Blackbox Exporter 探测服务可用性。如果你不想装这么多东西也可以用Netdata一个软件全搞定界面还好看。但 Netdata 的查询语言不如 PromQL 灵活后面做自定义告警会受限。我自己的选择是 Prometheus 那一套因为学会了之后换到任何云厂商的监控服务都能快速上手。4.2 安装与配置 Prometheus先下载 Prometheus 的二进制包解压后得到一个目录。核心配置文件是prometheus.yml长这样global: scrape_interval: 15s evaluation_interval: 15s scrape_configs: - job_name: node static_configs: - targets: [localhost:9100] - job_name: blackbox metrics_path: /probe params: module: [http_2xx] static_configs: - targets: - http://localhost:8080/health relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: localhost:9115这个配置的意思是每 15 秒去localhost:9100拉一次机器指标去localhost:9115让 Blackbox Exporter 帮忙探测http://localhost:8080/health是否返回 2xx。启动命令很简单./prometheus --config.fileprometheus.yml --storage.tsdb.retention.time15d--storage.tsdb.retention.time15d表示数据保留 15 天。这个参数根据你的磁盘大小调整一般 15 到 30 天比较合适。4.3 配置 Node Exporter 采集机器指标Node Exporter 也是下载解压直接跑./node_exporter默认监听9100端口。跑起来之后你可以用 curl 看看有没有数据curl http://localhost:9100/metrics | head -20你会看到一堆以node_开头的指标。常用的几个指标名含义用途node_cpu_seconds_totalCPU 时间累计算 CPU 使用率node_memory_MemAvailable_bytes可用内存算内存使用率node_filesystem_avail_bytes文件系统可用空间磁盘告警node_network_receive_bytes_total网络接收字节数算网络流量CPU 使用率的 PromQL 查询是100 - (avg by (instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100)这个查询的意思是先算出 5 分钟内的空闲 CPU 速率然后 100 减去它就是使用率。4.4 配置 Blackbox Exporter 探测服务存活Blackbox Exporter 的配置在blackbox.yml里modules: http_2xx: prober: http timeout: 5s http: valid_http_versions: [HTTP/1.1, HTTP/2.0] valid_status_codes: [200, 201, 202, 204] method: GET preferred_ip_protocol: ip4这个模块的意思是发一个 GET 请求5 秒超时如果返回 200 到 204 之间的状态码就算成功。启动./blackbox_exporter --config.fileblackbox.yml默认监听9115端口。Prometheus 会通过这个端口来触发探测。4.5 配置告警规则告警规则写在单独的文件里比如alerts.ymlgroups: - name: basic_alerts rules: - alert: InstanceDown expr: up 0 for: 1m labels: severity: critical annotations: summary: 实例 {{ $labels.instance }} 已下线 description: {{ $labels.instance }} 已经 1 分钟无法访问。 - alert: HighCpuUsage expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100) 85 for: 5m labels: severity: warning annotations: summary: 实例 {{ $labels.instance }} CPU 使用率过高 description: CPU 使用率已持续 5 分钟超过 85%。 - alert: DiskSpaceLow expr: (node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100 15 for: 10m labels: severity: warning annotations: summary: 实例 {{ $labels.instance }} 磁盘空间不足 description: 磁盘可用空间已低于 15%。然后在prometheus.yml里加上rule_files: - alerts.yml重启 Prometheus 后告警规则就生效了。但这时候告警只是存在于 Prometheus 内部还需要一个组件来发送通知比如 Alertmanager。4.6 用 Alertmanager 发送告警通知Alertmanager 负责接收 Prometheus 发来的告警然后做去重、分组、静默最后发给接收方。配置文件alertmanager.ymlroute: group_by: [alertname] group_wait: 10s group_interval: 5m repeat_interval: 1h receiver: webhook receivers: - name: webhook webhook_configs: - url: http://localhost:5000/alert send_resolved: true这个配置的意思是告警按名称分组等 10 秒看看有没有同组告警5 分钟发一次同组新告警1 小时重复一次未恢复的告警。接收方是一个 webhook你可以自己写个小服务来接收然后转发到邮件、即时通讯工具等。send_resolved: true表示恢复时也发通知这个很重要不然你不知道问题什么时候好的。5. 常见问题与排查技巧实录5.1 告警风暴一次故障收到几百条通知这是新手最容易遇到的问题。一台机器挂了上面跑着 10 个服务每个服务又有 5 个指标瞬间就是 50 条告警。再加上依赖关系可能几百条。解决办法有三个分组Alertmanager 的group_by把同类告警合并成一条。抑制如果机器级别的告警已经触发就抑制上面的服务告警。配置inhibit_rules实现。依赖关系在告警规则里加unless条件比如服务告警只在机器正常时才触发。我一般会配一条抑制规则如果InstanceDown触发了就抑制这台机器上所有其他告警。因为机器都挂了上面的服务肯定有问题没必要重复通知。5.2 指标缺失为什么图表是断的图表断线通常有三个原因采集目标挂了Prometheus 拉不到数据自然没有点。查up指标就能确认。查询范围太大PromQL 查询的时间范围超过了数据保留期前面的数据被删了。标签不匹配查询条件写错了比如jobnode写成了jobnodes。排查顺序是先看up再看时间范围最后检查标签。我遇到过最坑的一次是时区问题服务器是 UTC我按本地时间查结果查了个空。5.3 内存持续增长是泄漏还是正常缓存内存增长分两种一种是真泄漏一种是缓存占用。Linux 的MemAvailable指标会告诉你真相。如果MemAvailable在减少那才是真问题。对于 Java 服务还要看堆内存和堆外内存。有时候堆内存很稳但堆外内存在涨比如 Netty 的直接内存。这时候需要单独监控jvm_buffer_memory_used_bytes这类指标。实操心得别看到内存涨就重启服务。先观察 24 小时如果涨到一定程度就稳住了那是缓存正常。如果一直涨到 OOM那才是泄漏需要 dump 内存分析。5.4 时间不同步监控数据对不上监控系统对时间非常敏感。如果被监控机器和监控服务器时间差了几分钟你会看到很奇怪的现象数据点忽前忽后告警延迟触发。解决办法就是配 NTP 同步。所有机器都指向同一个时间源误差控制在毫秒级。这个在容器环境里尤其重要因为容器默认使用宿主机时间如果宿主机没同步容器也跟着偏。5.5 常见问题速查表现象可能原因排查方法解决方式图表断线采集目标挂了查up指标恢复目标或调整采集配置告警不触发阈值太高或for太长手动执行 PromQL 看值调整阈值或缩短for告警重复没有分组或静默查 Alertmanager 日志配置group_by和repeat_interval数据量太大标签基数过高查/api/v1/status/tsdb移除高基数标签查询很慢时间范围太大或正则太复杂用explain分析缩小范围或优化查询6. 进阶让监控真正产生价值6.1 从监控到可观测性日志和链路追踪指标只能告诉你“出问题了”但没法告诉你“为什么出问题”。这时候就需要日志和链路追踪。日志用 Loki 或者 Elasticsearch 收集按服务、级别、时间索引。链路追踪用 Jaeger 或者 Tempo记录一个请求经过的所有服务。三者结合才能快速定位问题。我一般的排查流程是先看指标大盘发现异常服务然后查该服务的日志找到错误信息最后看链路追踪确认是哪个下游依赖导致的。6.2 SLO 和错误预算让告警更有意义SLOService Level Objective是给服务定的可用性目标比如“99.9% 的请求在 200ms 内返回”。错误预算就是允许的失败次数。有了 SLO告警就不再是“CPU 超过 80%”这种技术指标而是“错误预算消耗过快”这种业务指标。这样开发团队更容易理解也更有动力去修复。计算错误预算的 PromQL 大概是sum(rate(http_requests_total{status~5..}[1h])) / sum(rate(http_requests_total[1h]))如果这个比例超过 0.1%说明错误预算在快速消耗需要关注。6.3 监控系统的自我监控监控系统本身也会挂。如果 Prometheus 挂了你什么告警都收不到。所以监控系统需要自我监控。最简单的办法是用另一套独立的监控系统来监控 Prometheus。或者用外部服务比如每 5 分钟检查一次 Prometheus 的/health接口不通就发通知。另外Prometheus 的prometheus_tsdb_head_series指标可以告诉你当前有多少时间序列。如果这个数突然暴涨说明有高基数标签混进来了需要赶紧处理。7. 我个人在实际操作中的几点体会搭监控这件事我最大的体会是别追求一步到位。我见过太多团队花几个月搭了一套“完美”的监控体系结果上线后发现没人看告警也没人处理。监控的价值不在于技术多先进而在于能不能真正帮人发现问题。我的建议是从最简单的开始先监控服务存活和机器资源跑上一周看看告警准不准。如果误报太多就调阈值如果漏报就加指标。慢慢迭代让监控体系跟着业务一起成长。还有一个坑是别把监控数据当日志用。指标适合看趋势和聚合不适合查单个请求。如果你发现自己在用 PromQL 查“某个用户的具体请求”那说明你需要的是日志系统不是监控系统。最后分享一个小技巧给每个告警加一个runbook_url注解指向一个文档说明这个告警触发时该怎么处理。这样即使是新人值班也能快速上手。文档不用写得多漂亮几行字说明“先看什么、再做什么、找谁”就够了。这个习惯坚持下来团队的故障响应速度会明显提升。
阅读完成 · 觉得有帮助?