指标、日志、追踪回答的是不同的问题
一句话总结
指标回答何时、多少,日志回答为什么,追踪回答在哪里。三者并非彼此的高级替代品,而是能够回答的问题类型不同。一旦给指标加上请求 ID,它就已经不再是指标,而是压缩率很差的事件存储。
为什么需要它
故障响应时打开仪表板,里面有 40 个面板:CPU、内存、线程数、GC 次数、连接池大小、堆使用量、每秒请求数。每一项都有图表。但现在真正想知道的只有一个问题:“用户是否正在遭遇失败?如果是,比例是多少?”40 个面板里没有一个能回答。
指标设计的问题不在于数据不足,而在于已收集数据能够回答的问题,与真正需要回答的问题彼此错位。因此,起点不应是指标清单,而应是问题清单。先写下值班人员凌晨 3 点会提出的五个问题,再只创建能够回答这五个问题的指标。
工作原理
| 问题 | 指标 | 日志 | 追踪 |
|---|---|---|---|
| 从什么时候开始恶化 | 能回答 | 很困难 | 无法回答 |
| 多少比例的请求受到影响 | 能回答 | 成本高 | 无法回答 |
| 这个请求为什么失败 | 无法回答 | 能回答 | 无法回答 |
| 慢请求在哪个服务中耗时 | 无法回答 | 无法回答 | 能回答 |
指标无法回答的格子很重要。一旦试图增加标签来填补这些格子,成本就会爆炸。指标的极限由基数决定。
考试最常出现的概念则是百分位数。假设 10,000 个请求中,9,900 个耗时 50ms,100 个耗时 3,000ms,平均值是 79.5ms。实际上没有任何请求在 79.5ms 得到响应。更糟的是它的敏感度:即使 100 个慢请求从 3,000ms 恶化一倍到 6,000ms,平均值也只从 79.5 移到 109.5;上升 30ms 不会越过任何告警阈值。
由此得到两点。
第一,p99 并不代表“1% 的用户”。 它意味着每 100 个请求中有 1 个。如果一个页面调用 20 个 API,该页面遇到 p99 的概率是 18.2%;每天发起 200 次请求的用户,有 86.6% 的概率每天至少一次遇到最差区间。
第二,百分位数不能合并。 假设服务器 A 的 9,900 个请求全部为 50ms,服务器 B 的 100 个请求全部为 3,000ms,那么 p99 的平均值为 1,525ms,按请求数加权的平均值为 79.5ms,而把两台服务器的请求排成一列后得到的真实 p99 是 3,000ms。三个数字完全不同,前两个毫无意义。计数器可以相加,百分位数不能相加——这正是直方图存在的原因。
SLI 是测量指标,SLO 是其目标值,SLA 是法律合同。若 30 天目标为 99.9%,错误预算就是 0.1%,即 43,200 分钟的 0.1%——43.2 分钟。燃烧率是消耗预算速度的倍数;如果一直以 14.4 倍燃烧,只需 50 小时就会耗尽 30 天的预算。
现场会遇到的情况
作者的 7 节点家庭实验室(3 个控制平面 + 4 个 GPU 工作节点)部署了 kube-prometheus-stack,Grafana 使用 MetalLB 地址池 10.0.0.200–215 中的 10.0.0.203。计算一下这里一张直方图的成本,就能形成直观认识。
http_request_duration_seconds_bucket
route 120 × method 5 × le 11 × pod 40 = 264,000 시계열
여기에 _sum 과 _count: 120 × 5 × 40 × 2 = 48,000
------------------------------------------------
합계 약 312,000 시계열 — 메트릭 하나에서
仅仅一个指标就占用 30 多万条时间序列。按每条时间序列约 8KB 计算,1,000 万条序列就是 80GB。为什么“先全部加入,以后再减少”很危险,用这一个乘法就足以说明。
再补充一条从同一集群学到的经验:安装 KubeVirt 时,组件状态全都显示 AllComponentsReady,虚拟机却无法启动。“状态为 Ready”和“实际可用”是两个不同命题。 可观测性应以用户遭遇的症状为材料,而不是 Ready 标签。
Prometheus 看待世界的方式
使用 Prometheus 时,有些行为会让人疑惑“为什么会这样”,但大多源自拉取(pull)模型和时间序列存储结构。理解这两点,其余现象就都能解释。
目标消失,指标也随之消失。 Pod 死亡后,该时间序列不再进入系统。因此,up == 0 无法捕获已经消失的目标,因为 up 本身也不存在。要捕获消失事件,需要使用 absent(),或与服务发现中预期的目标数量比较。
数值是抓取时刻的快照。 如果每 15 秒抓取一次,中间的瞬时激增就看不到。计数器会累积,因此不会遗漏;而仪表只保留抓取瞬间的值。如果必须知道瞬时最大值,应用应自行将最大值记录为计数器或直方图。
rate() 只用于计数器。 用在仪表上没有意义;反过来,直接绘制计数器只会看到不断上升的曲线。计数器因重启归零,rate 会自动校正。
范围向量窗口至少应为抓取间隔的 4 倍。 如果使用 rate(x[1m]),而抓取间隔为 30 秒,窗口里只有两个点,结果很不稳定;漏掉其中任何一个,结果就会为空。[5m] 左右是安全的默认值。
只要有一个标签不同,就是另一条时间序列。 因此,改变标签的部署会让图表出现断点。旧序列在该时刻结束,新序列从此开始。在仪表板中查看总量时,用 sum by () 聚合,可以减弱这种断点的可见性。
存储位于本地,长期保存需要其他设施。 Prometheus 本身并不以长期留存和高可用为目标,这些职责由远程写入和独立的长期存储承担。
下一项检查要看什么
本模块是概念模块,没有实验。完成测验、确认三种信号的边界和百分位数的性质后,下一模块会让你亲自编写 prometheus.yml,并亲手决定“哪些目标保留下来、哪些指标被丢弃”。