修复资源开通API中具有误导性的指标
目标
声明 SLI 的分母,修复会遗漏失败请求的指标代码,并对照真实 HTTP 响应与指标。区分重复采集和重试后,依据三个独立观测窗口的错误预算判断是否发布普通功能。
为什么重要
只统计成功路径的指标,可能在失败越多时反而显示越高的成功率。即使 PromQL 正确,也无法修复错误采集的原始数据。本实验会在编写告警规则之前,检查什么算作一次请求,以及是否存在缺失证据。评分不只检查函数名称,还会验证多种输入、真实 HTTP 执行与自行设计的反例。
实验会真实运行 Pod 内的 Python 服务器,不会把 Kubernetes API 或 KWOK 的 Running 状态当作执行证据。实验不涉及外部系统、真实 GPU、生产 Prometheus 采集或真实部署。延迟指服务器处理时间,与客户端网络往返时间不同。30 天窗口的数据是教学用虚构数据。
工作路径是 /root/cnpe-sli。Python 仅使用标准库。会话结束时文件与诊断记录都会消失,请另行保存必要产物。辅助工具为 /opt/fixtures/cnpe_sli_workbench.py;每次评分都会读取文件并终止临时进程。
步骤
- 在 scope.json 中声明单位、分母与目标。
- 在 classifier.py 的 classify 中按状态码和路径分类。
- 在同一函数中完成对慢速成功响应的分类。
- 重放真实 HTTP 并保存 http-receipt.json。
- 使用 summarize.py 去除采集重复,但单独统计真实重试。
- 使用 budget.py 处理观测空白、分数预算与耗尽边界。
- 使用 report.py 与 report.json 合并三个窗口的可用性和延迟判断。
- 使用 counterexamples.json 区分五种错误实现。
参考
- 步骤卡中给出了准确函数签名与 JSON 字段契约。文件示例只展示格式,并非答案。
- 每一步都可通过
python3 /opt/fixtures/cnpe_sli_workbench.py grade /root/cnpe-sli 단계번호自行检查。 exercise /root/cnpe-sli会使用临时端口,真实产生正常、1.1 秒延迟、503、504、400、404。X-Lab-Scenario 是教学用故障选择头,不是生产功能。- 仅在本任务中把 2xx 与 5xx 定义为有效尝试。生产环境若无条件排除 429 或 4xx,可能隐藏应由服务负责的失败。必须共同约定真实有效请求的契约。
- 请同时阅读SLI设计、指标采集原则和错误预算策略示例。
先把分母写入契约
创建 /root/cnpe-sli/scope.json。route 为 /provision,unit 为 attempt,eligible_classes 为 [2,5],latency_limit_ms 为 1000,availability_target 为 0.999,latency_target 为 0.99,no_data 为 investigate。这两个目标是本实验的虚构策略。
一次用户操作与一次 HTTP 尝试并不相同。本任务使用尝试作为单位。无法观测的窗口不能用成功率 1 填充。
消除只统计成功路径的缺陷
在 classifier.py 中实现 classify(path, status, latency_ms)。返回 eligible、available、fast 三个 bool 字段。只有移除查询字符串后的路径为 /provision,且状态码为 2xx 或 5xx 时,eligible=True;其中 2xx 才是 available。本步骤中 fast 可以保持 False。排除 /healthz、其他路径与 4xx。
503 与 504 虽然不成功,但必须进入分母。不要把 URL 查询中的用户值用于路径分类或指标标签。
不要把慢速 201 算作良好响应
完成 classifier.py 中的 fast。只有 available=True 且 latency_ms <= 1000 时为 True。包含 1000,排除 1000.1。输入延迟是有限且非负的毫秒数。保持 eligible 与 available 契约,并返回真实 bool,而不是字符串或 0、1。
快速 503 也不是良好延迟响应。该 SLI 衡量所有有效尝试中快速且成功的响应比例,不能只把成功请求作为分母。
对照七次响应与四类指标
把辅助工具的 exercise /root/cnpe-sli 输出保存到 http-receipt.json。真实状态码依次为 200、201、201、503、504、400、404,慢速 201 的服务器处理时间至少为 1000ms。结果必须为 observed=7、eligible=4、available=2、fast=1。使用当前 classifier 重跑也应得到相同结果。
即使 classify 单元测试正确,HTTP 路径仍可能遗漏。不能只看保存的数字,还要使用当前代码重放,确认失败响应进入分母。
区分重复采集与重试
在 summarize.py 中实现 summarize(events)。每个事件含 attempt_id、path、status、latency_ms 字段。相同 attempt_id 且内容一致的重复事件只分类一次,并增加 duplicates;同一 ID 内容不同则抛出 ValueError;不同 ID 的重试分别统计。返回 eligible、available、fast、excluded、duplicates 五个整数。复用 classifier.classify。
若把失败尝试 a 与成功重试 b 覆盖为一次最终成功,就会改变基于尝试的 SLI。反之,采集器重复发送同一事件时,不能把它两次计入分母。
处理 0.1 次预算与观测空白
在 budget.py 中实现 decide(eligible, good, target, covered)。只接受满足 0 <= good <= eligible 的 int、满足 0 < target < 1 的数值和 bool covered,否则抛出 ValueError。covered=False 或 eligible=0 时,sli、allowed_bad、remaining_bad、consumed 返回 null,decision 返回 investigate。其他情况:sli=good/eligible,allowed_bad=eligible*(1-target),remaining_bad=allowed_bad-(eligible-good),consumed=(eligible-good)/allowed_bad。sli 与 consumed 四舍五入到 6 位小数。预算不能取整。剩余预算 <= 0 时返回 freeze,否则返回 ship。
100 次请求、目标 99.9% 时,允许失败量为 0.1,不能取整成 0。Decimal(str(target)) 是避免把 0.999 转换成二进制浮点误差、再比较预算边界的一种方法。还要注意 bool 是 Python 中 int 的子类型。
避免良好可用性掩盖糟糕延迟
把辅助工具 windows 输出保存到 windows.json,并在 report.py 中实现 build(windows)。输入是包含 name、eligible、available、fast、covered 的独立窗口列表。返回以窗口名称为键的对象,每项包含 availability=decide(eligible,available,0.999,covered)、latency=decide(eligible,fast,0.99,covered) 与 decision。合成判断优先 investigate,其次 freeze,最后 ship。窗口名称重复时抛出 ValueError。把结果保存到 report.json。
steady 应为 ship;slow 即使可用性良好,也应因延迟而 freeze;gap 即使数字看似完美也应 investigate。除了输出文件,函数也必须能针对新窗口输入正确计算。
确认测试能够发现错误实现
在 counterexamples.json 中编写 5~24 个 {op,args,expected} 用例。op 为 classify、summarize 或 decide,args 是相应函数的位置参数列表。expected 是精确返回对象;期望 ValueError 时为 {"error":"ValueError"}。必须包含三个函数的用例,并能发现五种错误:遗漏 5xx、把慢速成功算作 fast、重复双重计数、把预算取整、批准观测空白。
只检查正常情况的测试,也可能让错误实现通过。请为每个函数选择能让原始实现与错误实现产生不同输出的输入。写错期望值来制造红灯也不是验证。