
前面详细介绍了各层涉及的指标主机层 50 多个 Collector、应用层十来个指标族、接入层 8 个原生指标、依赖层 5 类 Prober。指标齐了又产生了新的问题面板上几百条曲线告警规则配了四十条真出事的那一分钟值班人员还是不知道该先看哪一条。这不是采集能力的问题是缺一把挑指标的尺子。前人早就把尺子做出来了而且是三把。REDRate / Errors / Duration由 Tom Wilkie 提出把每个服务压缩成三个数USEUtilization / Saturation / Errors来自 Brendan Gregg专门用来描述资源四个黄金信号Latency / Traffic / Errors / Saturation出自 Google SRE是站在用户视角的四问。三把尺子说的不是同一件事。对比维度REDUSE黄金信号SRE提出者Tom WilkieBrendan GreggGoogle SRE观察对象请求型服务API、Web资源型组件CPU、内存、磁盘、网络面向用户的系统整体回答的问题服务好不好用资源够不够用用户感觉到了什么三个/四个量速率、错误、耗时利用率、饱和度、错误延迟、流量、错误、饱和度短板看不到资源瓶颈看不到业务影响无请求概念偏宏观不直接对应单个组件本系列对应第4篇、第5篇第3篇第5篇、第6篇三套方法全部落在前几篇已经采集到的指标上给的是可直接复制的 PromQL、可执行的阈值以及每条方法各自的适用边界不指望把三百条曲线都配上告警而是把该看的收敛到十条以内。注意RED、USE、黄金信号均为通行方法论本文的阈值与指标名沿用前几篇已给出的实测值文中 PSIPressure Stall Information相关指标需额外开启--collector.pressure本系列验证环境未启用已单独标注「未验证」。一、先把方法选对再谈配多少告警1、指标过剩的真实代价先先一个真实现象主机层把 CPU、内存、磁盘、网络全配了告警一共二十多条应用层又配了十几条。结果一次真实故障是上游调用超时应用http_server_requests_seconds的耗时曲线抬升Tomcat 线程池打满紧接着主机 CPU 反而掉下来了因为线程都堵在等下游真正告警响的是「主机 CPU 使用率 10%」这类反向规则没配一条都没响。问题不在于指标不够而在于没有区分「因果」和「现象」。CPU 使用率低是现象线程池饱和才是因果链上的那一环。缺一把尺子就只能凭经验排查。2、三把尺子各自的边界在哪三套方法不是替换关系而是分工关系。RED 看请求——只要对象在「收请求、返响应」就问三件事来量多少、错了多少、慢了多久。应用、Nginx、网关、gRPC 服务全都适用。USE 看资源——只要对象是一份「有限资源」就问三件事用了多少、排队多深、出错几次。CPU、内存、磁盘、网卡、连接池全都适用。黄金信号看用户——换到用户视角问四个问题卡不卡、量多大、失败没、快满了没。它最大的价值是逼你把延迟拆开看而不是给新指标。⚠️ 重点RED 描述的是服务USE 描述的是资源黄金信号描述的是体验。服务慢了不一定是服务的问题可能是它下面那块盘在排队此时 RED 只能告诉你「慢了」USE 才能告诉你「慢在哪」。3、选型规则三个问题定方法按顺序问以下三个问题定方法这个对象会不会「收到请求」会 → 先上 RED再补 USE 看支撑它的资源。这个对象是不是一份被共享的有限资源是 → 用 USE别用 RED磁盘没有 QPS 的概念。这次排查是从用户投诉开始的吗是 → 从黄金信号切入先定位是延迟、错误还是容量再往 RED / USE 下钻。一套监控里入口用黄金信号、服务用 RED、资源用 USE三条线各管一段谁也别越界。越界的结果就是告警互相打脸。二、RED面向请求型服务1、三个指标从哪来RED 看着简单落地时最容易出的错是指标选错。三个量在本系列环境里各自对应谁先对齐RED 维度含义应用层第4篇接入层第5篇数据来源Rate请求速率http_server_requests_seconds_countnginx_http_requests_totalMicrometer / stub_statusErrors错误计数http_server_requests_seconds_count{status~5..}OSS 版无状态码指标Micrometer / —Duration请求耗时http_server_requests_seconds_sum/_countOSS 版无耗时指标Micrometer / —⚠️ 重点OSS 版 Nginx 给不出 Errors 和 Duration。前面文章已经介绍stub_status 只有 7 个数字状态码、upstream 耗时一个都没有。所以 Nginx 这一层只能做半个 RED真正完整的 RED 要落到网关或应用侧。想在 Nginx 层拿到状态码分布只有两条路换商业版 Nginx Plus或改用 OpenResty 自采。2、常用 PromQL# ---------- RateQPS ---------- # 总 QPS按 job svc 聚合 sum(rate(http_server_requests_seconds_count[5m])) by (job, svc) # TOP 10 接口 QPS定位热点 topk(10, sum(rate(http_server_requests_seconds_count[5m])) by (job, svc, uri)) # 接入层 QPS sum(rate(nginx_http_requests_total[5m])) by (job, instance) # ---------- Errors错误率% ---------- # 5xx 错误率 sum(rate(http_server_requests_seconds_count{status~5..}[5m])) by (job, svc) / sum(rate(http_server_requests_seconds_count[5m])) by (job, svc) * 100 # 4xx 错误率客户端错误多与上游变更相关 sum(rate(http_server_requests_seconds_count{status~4..}[5m])) by (job, svc) / sum(rate(http_server_requests_seconds_count[5m])) by (job, svc) * 100 # ---------- Duration平均响应时间ms ---------- sum(rate(http_server_requests_seconds_sum[5m])) by (job, svc) / sum(rate(http_server_requests_seconds_count[5m])) by (job, svc) * 1000 # 成功请求的平均响应时间过滤掉异常请求ms sum(rate(http_server_requests_seconds_sum{status~2..}[5m])) by (job, svc) / sum(rate(http_server_requests_seconds_count{status~2..}[5m])) by (job, svc) * 1000 # 最大响应时间长尾不需要直方图 http_server_requests_seconds_max3、验证三张图一个 RowRED 的落地验收标准很具体——QPS、错误率、平均响应时间三张图放在 Dashboard 的同一行时间轴对齐。对齐之后的好处三者谁先动一目了然。响应时间先抬、QPS 不变 → 后端慢QPS 先抬、响应时间跟着抬 → 容量不够错误率先抬、QPS 下降 → 上游已经在熔断。这个先后顺序比任何单张图的信息量都大。4、分析要点与阈值5xx 错误率 1% 判定critical服务端错误直接影响可用性4xx 错误率 5% 判定warning多为上游或接口变更平均响应时间 500ms 判定warning 1s 判定critical需按应用特性调整阈值QPS 环比波动 50% 判定warning流量异动暴涨/暴跌都要看5xx 告警应配为critical且for: 5m避免单次抖动轰炸5、常见踩坑注意rate()窗口不能随手改。“第5篇 Nginx 监控流量、连接与性能指标”给过一组平衡值——抓取间隔 15s、rate窗口 5m。窗口用 1m 曲线会抖用 15m 会把 5 分钟内的流量尖峰整套抹平。RED 的三个量最好用同一个窗口否则错误率会算出 0~无穷的假象。http_server_requests_seconds默认是 summary不是 histogram。Spring Boot 3 / Micrometer 默认只发布_count和_sumhistogram_quantile()直接报空。想要 P95/P99 必须显式配置management: metrics: distribution: percentiles-histogram: http.server.requests: true⚠️ 但是开直方图会炸指标基数。每多一个uri×method×status×le的组合序列数就是乘出来的。中小规模应用先只用平均值 _max确认需要分位数再开。我在验证环境里就是先开了直方图Targets 里的scrape_samples_scraped能从两位数涨到四位数。注意Counter 会归零。nginx_http_requests_total在 Nginx 重启后归零rate()能自动处理重置但 Grafana 里若直接画原始值会出现一个向下的尖峰别当成异常。RED 的落地成本极低只要服务在用 Micrometer三个量都是白送的。任何新上线的服务RED 三张图应该和健康检查一起配好不需要等出故障再补。三、USE面向资源型组件1、三个维度怎么理解USE 的三个词直译很模糊落到具体指标上才清楚USE 维度一句话定义怎么判断超标Utilization利用率资源被占用的时间比例持续高位 资源不够Saturation饱和度资源排队/积压的程度出现排队 已经是瓶颈利用率还没满也得看Errors错误资源本身报错只要非零就要看不做阈值判断关键区别利用率高不等于有问题饱和度才是真信号。一块盘 io_time 100%如果队列深度是 1说明它忙但没堵io_time 30% 但队列一直在涨说明上游在疯狂并发提交问题在别处。2、CPU# UCPU 使用率(%) 100 - (avg by(instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100) # U按核心看定位单核打满 100 - (avg by(instance, cpu) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100) # U虚拟化视角被宿主机抢走的时间steal 长期 0 说明邻居在抢资源 avg by(instance) (rate(node_cpu_seconds_total{modesteal}[5m])) * 100 # S负载水位load1 / 逻辑核数 1 表示过载 # 两种写法 sum by(job, svc, instance) (node_load1) / count by(job, svc, instance) (node_cpu_seconds_total{modeidle}) node_load1 / on(instance) group_left() count by(instance)(node_cpu_seconds_total{modeidle}) # S阻塞进程数非零说明有进程卡在 I/O 上 node_procs_blocked注意CPU 没有 Errors 维度。USE 是一个通用模型不是所有资源都恰好凑齐三个量CPU 就是典型的「只有 U 和 S」。别为了凑数硬找个指标填进去那只会制造噪声。3、内存# U内存使用率(%)用 MemAvailable 而不是 MemFree (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 # SSwap 使用率(%)SwapTotal 0 的机器会被 and 过滤避免除零得到 NaN ((node_memory_SwapTotal_bytes - node_memory_SwapFree_bytes) / node_memory_SwapTotal_bytes * 100) and (node_memory_SwapTotal_bytes 0) # S内存压力PSI rate(node_pressure_memory_stalled_seconds_total[5m]) # EOOM Kill 次数内核 4.13vmstat collector 默认开启 increase(node_vmstat_oom_kill[1h]) 0⚠️ 注意内存的 U 一定要用MemAvailable不能用MemFree。“第3篇 主机层监控实战node_exporter 核心指标与告警”讲过原因Linux 会把空闲内存拿去当页缓存MemFree常年很低是正常现象。用MemFree算使用率会在每台健康机器上都收到 90% 的告警。increase(node_vmstat_oom_kill[1h]) 0该规则需注意OOM 是「有没有」而不是「有多少」的问题一次 OOM 就意味着一个进程被内核杀过。它不需要for不需要阈值只要不为零就该看。4、磁盘与文件系统# U磁盘 I/O 利用率(% 时间在做 I/O) rate(node_disk_io_time_seconds_total[5m]) * 100 # S平均读延迟秒reads 为 0 时为 NaN用 0 过滤 rate(node_disk_read_time_seconds_total[5m]) / rate(node_disk_reads_completed_total[5m]) 0 # S当前在途 I/O 数瞬时队列深度 node_disk_io_now # S加权 I/O 时间近似平均队列长度 rate(node_disk_io_time_weighted_seconds_total[5m]) # U空间使用率(%) (1 - node_filesystem_avail_bytes{fstype!~tmpfs|overlay|squashfs} / node_filesystem_size_bytes{fstype!~tmpfs|overlay|squashfs}) * 100 # Uinode 使用率(%) (1 - node_filesystem_files_free{fstype!~tmpfs|overlay|squashfs} / node_filesystem_files{fstype!~tmpfs|overlay|squashfs}) * 100 # E文件系统被内核降级为只读1 只读 node_filesystem_readonly{fstype!~tmpfs|overlay|squashfs} 1注意磁盘的 E 维度node_exporter 给不出来。node_exporter 注册的指标只有这些node_disk_reads_completed_total node_disk_reads_merged_total node_disk_read_bytes_total node_disk_read_time_seconds_total node_disk_writes_completed_total node_disk_writes_merged_total node_disk_written_bytes_total node_disk_write_time_seconds_total node_disk_io_now node_disk_io_time_seconds_total node_disk_io_time_weighted_seconds_total node_disk_discards_completed_total node_disk_discards_merged_total node_disk_discarded_sectors_total node_disk_discard_time_seconds_total node_disk_flush_requests_total node_disk_flush_requests_time_seconds_total没有node_disk_read_errors_total也没有node_disk_write_errors_total。网上不少指标清单会列出这两个名字直接抄进告警规则会得到一条永远不触发的空规则因为底层/proc/diskstats本身就不含错误计数。要拿到设备层的 I/O 错误现实的取舍是三条方案做法优点缺点文件系统只读检测node_filesystem_readonly 1零成本直接反映 ext4/XFS 遇 I/O 错误后的降级事后信号只能说明已经出事了textfile collector定时smartctl -a解析后写入.prom文件能拿到 SMART 属性、重分配扇区数需自建脚本与定时任务维护成本在脚本上内核日志dmesg/journalctl -k匹配I/O error信息最全含设备与扇区不在 Prometheus 体系内无法参与告警聚合日常监控用方案一兜底重要数据库所在的物理机再补方案二。不能为了补齐 USE 的第三个字母去造一个假指标。5、网络# U出口带宽利用率(%)1Gbps 网卡按 125 MB/s 折算 rate(node_network_transmit_bytes_total{device!~lo|veth.*|docker.*}[5m]) / 125000000 * 100 # E接收丢包率(%) rate(node_network_receive_drop_total{device!~lo|veth.*|docker.*}[5m]) / rate(node_network_receive_packets_total{device!~lo|veth.*|docker.*}[5m]) * 100 # ETCP 重传率(%) rate(node_netstat_Tcp_RetransSegs[5m]) / rate(node_netstat_Tcp_OutSegs[5m]) * 100 # STIME_WAIT 堆积短连接服务未开 tcp_tw_reuse 时高发 node_sockstat_TCP_tw注意device!~lo|veth.*|docker.*这个过滤不能省。若不过滤容器环境下几十个 veth 网卡会把曲线糊成一片而且它们的流量是重复计算的——同一份流量在 veth 和物理网卡上各算一次。6、分析要点与阈值资源维度指标阈值CPUU使用率 70% warning 85% criticalCPUSload1 / 核数 1.5 判定过载CPUSnode_procs_blocked非零即 I/O 瓶颈信号内存UAvailable 占比 10% 判定 critical内存SSwap 使用率 80% 且 SwapTotal 0 判定 warning内存EOOM Kill非零即告警无阈值磁盘Uio_time 20% warning 40% critical磁盘S读写延迟 10ms 需关注磁盘健康度文件系统U空间使用率 80% warning 90% critical网络U带宽利用率 70% warning 85% critical1Gbps网络E丢包率 0.1% 需排查网络ETCP 重传率 2% 表明网络质量差USE 的价值不在指标数量而在「利用率 饱和度」这一对组合。单看利用率会误判——一块 90% 忙但零排队的盘和一块 40% 忙但队列持续在涨的盘后者才是真出问题的那块。7、U和S的区别UUtilization利用率和 SSaturation饱和度看着像近义词其实回答的是两个不同问题U 回答「忙不忙」S 回答「堵不堵」关键区别在于U 衡量占用S 衡量等待。结合具体指标再进一步整理U和S的区别资源U占用时间比例S排队/积压程度CPU使用率node_cpu_seconds_total{modeidle}load1/核数、node_procs_blocked内存MemAvailable占比Swap 使用率、PSI 内存停顿磁盘node_disk_io_time_seconds_total读写延迟、node_disk_io_now、io_time_weighted网络带宽利用率TIME_WAIT 堆积node_sockstat_TCP_tw四、四个黄金信号从 SRE 到本系列的裁剪1、四个信号与两套方法的关系黄金信号不是第四套新体系它是前两套的「用户视角并集」黄金信号等价于本系列观测点Latency 延迟RED 的 Duration接口耗时、拨测耗时Traffic 流量RED 的 RateQPS、请求量Errors 错误RED 的 Errors USE 的 Errors5xx 错误率、拨测失败、OOMSaturation 饱和USE 的 Saturation线程池、连接池、连接水位一句话说清三者关系黄金信号是「对外可感知的四个面」RED 是它的服务版缩写USE 是它的资源版对偶。2、常用 PromQL# ---------- Latency 延迟 ---------- # 平均响应时间ms未开直方图时的通用解 sum(rate(http_server_requests_seconds_sum[5m])) by (job) / sum(rate(http_server_requests_seconds_count[5m])) by (job) * 1000 # P95 成功请求耗时需开启 percentiles-histogram histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{status~2..}[5m])) by (le, job)) # 拨测侧延迟第6篇P95 比平均值更能反映体验 quantile_over_time(0.95, probe_duration_seconds[10m]) # ---------- Traffic 流量 ---------- sum(rate(http_server_requests_seconds_count[5m])) by (job, svc) sum(rate(nginx_http_requests_total[5m])) by (job, instance) # ---------- Errors 错误 ---------- sum(rate(http_server_requests_seconds_count{status~5..}[5m])) by (job, svc) / sum(rate(http_server_requests_seconds_count[5m])) by (job, svc) * 100 # ---------- Saturation 饱和 ---------- # Tomcat 线程池使用率 tomcat_threads_busy_threads / tomcat_threads_config_max_threads # 数据库连接池使用率DruidHikariCP 换成 hikaricp_connections_* 同理 druid_connections_active / druid_connections_max # Nginx 连接水位512 为 worker_connections 默认值按实际改 nginx_connections_active / (count(count by (instance) (nginx_connections_active)) * 512)3、延迟一定要拆开看黄金信号里最容易被忽略的一条Google SRE 原始定义中延迟必须区分「成功请求的延迟」和「失败请求的延迟」。原因很实在。假设一个接口正常请求 20ms一旦出错在 5ms 内返回 500。当故障开始大面积发生时大量错误请求以 5ms 快速返回平均响应时间反而下降了。你盯着平均值看会以为服务变快了。所以延迟图要按状态码拆# 成功请求延迟 sum(rate(http_server_requests_seconds_sum{status~2..}[5m])) by (job) / sum(rate(http_server_requests_seconds_count{status~2..}[5m])) by (job) * 1000 # 成功请求的 max长尾才是体验杀手 http_server_requests_seconds_max{status~2..}4、饱和信号的两个实战要点Tomcat 的 tomcat_threads_busy_threads 高多半是 I/O 阻塞不是 CPU 不够。“第4篇 Spring Boot 应用监控Micrometer 与 JVM 指标”的告警表里给了这条注释。线程都停在等下游或等数据库CPU 反而是空闲的——这时候加 CPU 没用要去看下游的 RED。Druid 连接池的active持续贴近max说明「连接没被释放」不是「连接不够」。优先查慢查询其次才考虑扩容。第4篇给过阈值 80% warning 90% critical。四个信号里Latency 和 Saturation 最容易被低估。Traffic 和 Errors 是事实Latency 和 Saturation 是预兆——告警的价值在预兆这一侧。五、三套方法怎么组合落地1、分层指标清单把前六篇的采集结果按方法归一次类就是一份可以直接照着配的清单层对象主方法核心指标前缀采集组件指标出处接入层Nginx黄金信号T/E/Snginx_http_requests_total、nginx_connections_*、nginx_upnginx-prometheus-exporter第5篇接入层Spring Boot Gateway黄金信号T/E/S REDhttp_server_requests_*Micrometer Actuator第4篇应用层Spring BootRED Shttp_server_requests_*、tomcat_threads_*、druid_*Micrometer Actuator第4篇运行时JVMUSE内存/GC/线程jvm_memory_*、jvm_gc_*、jvm_threads_*Micrometer第4篇主机层NodeUSEnode_cpu_*、node_memory_*、node_disk_*、node_filesystem_*、node_network_*node_exporter第3篇依赖层外部 URL / 证书 / 域名可用性probe_success、probe_duration_seconds、probe_ssl_earliest_cert_expiryBlackbox Exporter第6篇本表可以直接当作 Dashboard 的骨架每一行一个 Row每一列一种方法。行与行之间的下钻路径也是固定的依赖层失败 → 看应用层 RED应用层延迟抬升 → 看运行时与主机层 USE。2、告警收敛三层 抑制指标清单解决「看什么」告警收敛解决「响几条」。原则是同一根因只响一条。主机层CPU / 内存 / 磁盘 / 网络共 16 条第3篇表级别以warning为主critical只留给 CPU 85%、内存 90%、磁盘 90% 这几条。应用层5xx 错误率、平均响应时间、QPS 异动、依赖组件健康共 6 条第4篇表5xx 是唯一一条必须critical的业务类告警。依赖层up、probe_success、成功率、证书到期共若干条第6篇表按通用层与 HTTP 层分层。第6篇已经提过拨测规则之间存在重叠要么取舍要么靠 Alertmanager 抑制。 跨层同样如此主机整体过载时不仅主机层规则会响应用层大概率也会跟着响。第1篇给的inhibit_rules是现成的解法当时只写了「Critical 抑制同 jobinstance 的 Warning」这一条。跨层抑制更实用# 统一保留 svc 标签第2篇约定即可用 svc 做主键做跨层抑制 inhibit_rules: # 主机层 critical 触发时抑制同一 svc 下的应用层 warning # 主机都趴了应用层的 warning 是必然结果不需要重复通知 - source_match: severity: critical type: node target_match: severity: warning type: jvm equal: [svc]⚠️ 注意若file_sd 的 labels 里少写一个svc抑制规则就永远不匹配而规则不匹配是静默失败不会报错只会让你在故障时被几十条重复告警淹没。验证抑制规则是否生效用官方工具$ docker exec -it alertmanager amtool check-config /etc/alertmanager/alertmanager.yml Checking /etc/alertmanager/alertmanager.yml SUCCESS Found: - global config - route - 1 inhibit rules - 2 receivers - 1 templates SUCCESS3、常见踩坑for不能省。所有 RED / USE 规则都要带for主机层 5m、应用层 5m、依赖层 1~2m第6篇给的值。不带for的规则会把每一次瞬时抖动都变成告警。阈值不要静态钉死。本文给的所有数字都是起点不是终点QPS 相关的规则尤其如此QPS 本身没有绝对阈值它取决于容量规划。不要为了凑齐框架去造指标。CPU 没有 Errors、OSS 版 Nginx 没有 Duration、磁盘没有原生错误计数这些缺口是真实的如实标注比硬凑一个假规则有用得多。监控体系的完备性不取决于采集了多少指标而取决于「采集—展示—告警」是否闭环。本文这套方法本质上是给这个闭环做减法。六、小结指标虽多但不必面面俱到。三套方法的适用边界很清晰入口看黄金信号服务看 RED资源看 USE。落到具体配置上一台机器真正需要长期挂着的告警不超过二十条应用层不超过六条其余指标按需在专项面板里查而不是全部转成告警。真正需要看住的主要是以下几个5xx 错误率平均响应时间线程池与连接池饱和度CPU 与内存使用率以及一次都不该出现的 OOM。落地建议上先做减法再做加法把现有告警规则按 RED / USE / 黄金信号重新归一次类删掉重复的、补上缺的 E 和 S 维度再统一svc与type标签让跨层抑制能真正生效。标签规范这一步看着琐碎但它决定了后面所有聚合、抑制、下钻能不能用。监控不是一次性的配置工作而是一项需要持续打磨的基础设施。