一张有用的看板,应该帮助读者决定接下来检查什么。它把大量观测归纳成少数几个问题:用户操作是否成功,影响发生在哪里,资源是否正在逼近限制。但归纳不能抹掉重要区别。全局成功率仍然很好看时,一个区域可能已经严重失败;面板空白也可能是遥测缺失,而不是服务处于安静且健康的状态。看板的价值在于减少重新推理的负担,同时保留做判断必需的上下文。

给页面安排清楚的阅读顺序

先展示服务结果和观测覆盖,再给出流量、延迟、错误以及有关资源的饱和信息。如果区域、操作类型或依赖的差异会改变下一步行动,就提供对应下钻入口。变更标记和诊断资料链接也应放在相关视图附近。曲线同时变化可以帮助选择调查方向,但仅凭发生时间接近,还不能证明某次部署或某个依赖导致了事故,仍需进一步验证真实链路。

每个面板应该回答明确的问题,但不要求只放一个指标。队列长度与配置上限使用相同单位,并列比较就很有意义;把不同单位放进复杂双轴图,则容易制造没有根据的视觉关系。应直接说明单位、聚合窗口、统计总体以及阈值含义,让读者不必凭记忆猜测坐标表示毫秒、秒、零到一比例还是百分数。含义清楚通常比增加面板数量更重要。

保留请求权重和观测覆盖

计算服务错误比例时,应使用相同窗口中的全部不良事件数除以全部符合条件的事件数。直接平均区域百分比,会让很安静的区域与繁忙区域获得相同权重,这已经换成另一种统计量,不是无关紧要的显示方式。即使全局值按请求正确加权,当小范围用户仍属于服务承诺的一部分时,也需要展示能定位影响的区域或操作细分。

新鲜度同样决定统计范围。应展示最近一次成功观测的时间,或者明确的覆盖信号,并约定何时把陈旧数据视为未知。业务看板选择的新鲜度阈值,与存储系统处理时序失效的机制是不同概念。把缺口一律补零,可能让已经不可用的采集器表现成完美错误率或空队列。缺失状态应被保留下来,并提供下一步排查采集链路或服务本身的方向。

坏掉的样子

自动缩放的纵轴可能把微小变化放大成重大事故,过宽的固定范围又可能掩盖真正越过阈值的变化。应根据运维问题选择刻度,让需要比较的面板保持一致。平均延迟还会藏起慢请求群体,需要合适的分布与观测数量作为补充;不能直接平均不同实例已经计算出的高分位数,再把它当成整个服务的尾部延迟。

外观漂亮的页面也可能只是一些互不相关的快照。时区、过滤条件、刷新频率和聚合窗口不同,会妨碍连贯比较。共享变量应有明确默认值和可见范围,尤其要让读者知道当前是否查看生产环境以及哪些区域。应拿区域故障、低流量和观测缺失来检查看板,如果三种场景都表现为绿色,就说明页面在压缩信息时丢掉了做决策所需的关键事实。

快速验证

将代码保存为 dashboard.py 并用 Python 3 执行。它基于一个完整六十秒窗口中的模拟计数,计算可以用于面板展示的值。两个区域有新鲜观测,另一个区域既缺少数值,最近观测时间也较旧。九十秒阈值是示例看板的业务规则,并不是 Prometheus 默认行为;验证这部分聚合逻辑不需要安装 Grafana,也不会操作任何现有仪表盘。

import json
from fractions import Fraction

# Counts cover the same completed 60-second interval.
rows = [
    {"region": "west", "requests": 9800, "errors": 0, "age_seconds": 10},
    {"region": "east", "requests": 200, "errors": 100, "age_seconds": 10},
    {"region": "south", "requests": None, "errors": None, "age_seconds": 240},
]
fresh = [r for r in rows if r["age_seconds"] <= 90
         and r["requests"] is not None and r["errors"] is not None]
missing = [r["region"] for r in rows if r not in fresh]
requests = sum(r["requests"] for r in fresh)
errors = sum(r["errors"] for r in fresh)
regional = {r["region"]: Fraction(r["errors"], r["requests"]) for r in fresh}
known_ratio = Fraction(errors, requests)
unweighted = sum(regional.values()) / len(regional)

assert known_ratio == Fraction(1, 100)
assert regional["east"] == Fraction(1, 2) and unweighted == Fraction(1, 4)
assert missing == ["south"]
print(json.dumps({
    "coverage": "incomplete",
    "fresh_requests_per_second": round(requests / 60, 3),
    "fresh_error_percent": float(known_ratio * 100),
    "regional_error_percent": {k: float(v * 100) for k, v in regional.items()},
    "wrong_unweighted_percent": float(unweighted * 100),
    "unknown_regions": missing,
}, indent=2))

新鲜观测合计一万次请求、一百次错误,对应每秒约一百六十六点六六七次请求,以及按请求加权后的百分之一错误率。然而流量较小的东部区域有一半请求失败。若直接平均两个区域的百分比,会错误地把合并请求错误比例写成百分之二十五。正确方式是让百分之一的总体值与区域细分共同出现,而不是挑选某一个数字来遮住另一个事实。

输出同时明确标记覆盖不完整,并把南部区域列为未知,没有凭空把它的请求数和错误数填写成零。这个实验核对的是面板背后的算术和缺失数据含义,不能替代对真实查询与页面的验证。把相同定义带入实际看板后,还应使用已知事故和遥测中断案例复查,确认读者看到的统计范围、状态与下一步行动仍然一致。

延伸阅读

来源