做监控的同学大概都遇到过这样的困惑:Prometheus面板上一片绿色,CPU、内存、磁盘全部正常,但业务方却投诉下单接口超时。问题的根源在于,集群指标反映的是资源视角的健康状态,而用户感知到的是业务视角的服务质量。要把这两者打通,就需要建立一套业务级别SLI到集群指标的映射方法。本文围绕这个话题,展开讲讲具体的思路和落地做法。

一、先厘清业务SLI与集群指标的本质差异
SLI(Service Level Indicator,服务级别指标)是SRE体系中对服务质量的量化定义。Google SRE手册中对SLI有一个非常精炼的表述:SLI是两个数字的比值,即好事件数除以总事件数。比如一个下单接口,过去一小时总共收到10000次请求,其中9950次在300毫秒内成功返回,那么SLI就是99.5%。这个定义天然站在用户视角,衡量的是服务质量而不是资源消耗。
集群指标则完全是另一个维度。它描述的是某个节点、某个Pod、某个进程的资源状态,比如CPU使用率、内存工作集、磁盘IO等待、网络丢包率等。这些指标的特点是采集简单、语义明确、与具体业务无关,Kubernetes的kubelet和cAdvisor默认就会暴露一大堆。正因为它与业务无关,所以它无法直接回答业务是否正常这个问题。资源利用率低不代表服务健康,可能是流量本来就少;资源打满也不一定意味着故障,可能是正常的批处理高峰。
理解了这个差异,映射的本质就清楚了:我们要做的,是从用户可感知的服务质量出发,定义业务级别的SLI,再通过合理的采集和聚合手段,把它落地为一组可以在现有监控体系中查询、告警、可视化的时序指标。映射不是简单的指标改名,而是一次语义上的翻译。
二、业务SLI的定义与采集方式选择
做映射的第一步是把业务SLI定义清楚。实践中最常用的三类SLI是:可用性(请求成功率的比值)、延迟(快于某个阈值的请求占比)、正确性(业务语义上正确的结果占比,比如支付扣款金额与订单金额一致)。定义时要遵循两个原则:一是SLI必须可观测,也就是每个事件都能被判定为好或坏;二是阈值要贴合用户预期,而不是拍脑袋定一个数字。
采集方式上,一般有三种路线。第一种是代码埋点,在业务代码里通过Micrometer、Prometheus client库直接打点,输出类似order_request_total{code="success"}的计数器。这种方式粒度最细,语义最准,但侵入性强,需要业务团队配合改造。第二种是网关层采集,在Nginx、Kong、APISIX这类入口网关上记录每个请求的状态码和耗时,聚合后输出指标。这种方式对业务零侵入,覆盖面广,适合作为统一的SLI采集层。第三种是日志转指标,通过Promtail加Loki或者mtail,从访问日志中提取延迟分布和错误计数。这种方式适合改造不动又没有网关的存量服务,缺点是依赖日志格式稳定,链路较长容易丢数据。
三种方式各有取舍,实际项目中经常组合使用:网关层负责全局可用性SLI,代码埋点负责核心业务流程的正确性SLI,日志转指标作为兜底补充。选定采集方式后,业务SLI本身已经是指标了,接下来才是本文的核心,如何与集群指标体系对齐和联动。
三、映射的具体方法与PromQL实现
映射的核心是把业务SLI的计算逻辑翻译成PromQL,并让它与集群指标共用同一套标签维度(namespace、pod、service等),这样在查询和告警时才能自由关联。以最典型的可用性SLI为例,假设网关暴露了requests_total计数器,带有status标签区分成功失败,那么30分钟窗口的错误率SLI可以这样写:
# 错误率 = 1 - 成功请求 / 总请求
1 - (
sum(rate(requests_total{job="gateway", status=~"2..|3.."}[30m]))
/
sum(rate(requests_total{job="gateway"}[30m]))
)
延迟SLI的映射稍微复杂一些。Prometheus的histogram类型指标(如request_duration_seconds_bucket)可以用来计算快于阈值的请求占比。假如业务约定500毫秒内返回算好事件,对应的桶是le="0.5",那么延迟SLI可以写成:
# 延迟达标率 = 500ms内完成的请求 / 总请求
sum(rate(requests_total[30m]))
/
sum(rate(request_duration_seconds_bucket{le="0.5"}[30m]))
有了这两个SLI,还可以进一步组合出错误预算的消耗速率。假设SLO目标可用性99.9%,即错误预算允许0.1%,那么快速烧毁率告警可以这样配置,当消耗速率达到预算的14.4倍(即一天烧完一个月的预算)时触发:
# 快速烧毁告警:14.4倍消耗速率,持续1小时
- alert: ErrorBudgetFastBurn
expr: |
(
1 - (
sum(rate(requests_total{status=~"2.."}[1h]))
/
sum(rate(requests_total[1h]))
)
) > 14.4 * 0.001
for: 1h
labels:
severity: critical
这套写法的妙处在于,业务SLI和集群指标在同一个Prometheus里,用同样的标签体系。当错误预算告警触发时,可以立刻在同一个面板上叠加CPU饱和度、Pod重启次数、节点内存压力等集群指标做归因分析。比如发现错误率飙升的Pod恰好同时内存接近limit并触发OOMKilled,那么从业务现象到基础设施根因的链路就打通了。
四、典型场景的映射方案与常见坑
以电商下单场景为例,完整的映射链路是:用户下单这个业务动作,定义为下单接口可用性SLI加支付回调正确性SLI;可用性SLI由网关的requests_total聚合而来,正确性SLI由订单服务的代码埋点输出;两者都带上service="order-api"标签,与Kubernetes的Service维度对齐。告警规则基于错误预算配置,面板上同时展示SLI趋势线和该服务背后Deployment的副本数、CPU、内存指标。这样运营侧看到的是业务健康度,运维侧看到的是资源健康度,两边说的是同一件事。
落地过程中有几个常见的坑值得提醒。第一是低流量服务的SLI抖动问题,流量很小的服务用rate函数计算比率时噪声极大,一次偶发错误就把错误率拉高几十个百分点。解决办法是改用绝对事件数做判断条件,或者拉长窗口,比如要求窗口内总请求超过100次才计算比率。第二是histogram桶边界设置问题,如果业务阈值是400毫秒而桶只有250和500,计算出的SLI精度会很差,一定要根据实际的延迟分布来设计桶边界。第三是标签基数问题,给指标打上user_id之类的标签会瞬间爆炸,SLI指标的标签必须收敛在service、method、region这类低基维度上。
总的来说,业务SLI到集群指标的映射,关键不在工具而在方法:先站在用户视角定义清楚什么是好事件,再选择合适的采集层把事件变成指标,最后用统一的标签体系把业务指标和集群指标纳入同一套查询语言。做到这三点,监控体系才能真正回答那个最重要的问题:用户现在到底用得好不好。