关于 kubesense-slo
kubesense-slo 是用于创建、预览和诊断 KubeSense 服务级目标的工作流技能。它通过 REST API 管理 SLO,并强调检查实际评估数据,而非只依赖预览结果。
主要功能
- 01发现指标、字段和告警渠道
- 02构建并预览 SLO 配置
- 03创建、读取、更新和删除 SLO
- 04检查评估桶与预算消耗
- 05配置并验证预算燃尽告警
适用场景
- 01为遥测指标创建服务级目标
- 02创建前估算合规状态
- 03排查 SLO 无数据或预算燃尽
- 04迁移或维护现有 SLO
MCP 相关
该技能说明 SLO 没有 MCP 工具,管理操作需通过 `/api/slo` REST API 和 HTTP 调用完成。MCP 查询工具可用于发现指标、字段及相关信息。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ kubesense-ai/ kubesense-mcp-skills --skill kubesense-slo安装后在 Claude Code 中输入/kubesense-slo确认已加载。
附带文件references/api-payloads.mdreferences/datadog-migration.md
详细介绍
KubeSense SLO
此处的 SLO 是一份已存储的定义,以及一个后台评估器。每隔
evaluation_interval_seconds,评估器就会对一个时间桶进行评分,将结果写入
ClickHouse,并生成 Prometheus 时间序列(kubesense_slo_*)。合规性则是基于
evaluation_window_days 计算的滚动汇总。
重要
没有 SLO MCP 工具。 日志、链路追踪、指标和告警都有对应工具;SLO 没有。此处所有操作都通过
/api/slo下的 REST API 完成,认证方式与 MCP 服务完全相同(x-api-key,或Authorization: Bearer <token>)。使用 MCP 查询工具来发现指标和字段名称,然后构建 SLO 请求体。因此,这项技能需要 MCP 连接本身无法提供的两样东西:发起 HTTP 调用的方式(带有
curl的 shell,或 HTTP 工具),以及可放入请求头的 API 凭据值——无法在工具调用中读取 MCP 服务自身的凭据。只有 MCP 的 Agent 仍然可以构建并解释请求载荷,并应明确告知用户需要自行发送。
生命周期
请按顺序执行。第 5 步和第 6 步最容易被跳过,而它们正是发现 SLO 虽然成功存储、却没有评估任何内容的关键步骤。
- 发现。 获取真实指标名称(
get-available-metrics、get-metric-labels)和真实字段名称(get-trace-or-log-fields)。获取消耗率告警的通道 ID(list-notification-channels)。如果是告警 SLO,还要获取告警规则 UUID。 - 构建请求体——包括类型、两个查询、时间窗口和
alert_types。 - 使用
POST /api/slo/preview预览,并了解预览会检查和不会检查的内容。数值明显错误时,通常是筛选条件有问题。no_data并不能证明筛选有误——真正安静的服务也会产生空时间窗口;修改任何内容前,请先区分这两种情况(见第 6 步)。对于告警 SLO,请明确传入from_time/to_time:默认时间窗口会回溯到规则创建之前,此时没有触发记录会被视为正常运行时间为 100%。 - 使用
POST /api/slo创建,并保存返回的 ID。没有 ID,就没有 SLO。 - 使用
GET /api/slo/{id}?current_time=…读取并核对,确认存储的配置与发送的内容一致——尤其是slo_type、operation和两个groupBy列表。 - 在一到两个评估间隔后,验证实际时间桶:
GET /api/slo/{id}/metrics?from_time=…&to_time=…,并再次读取 SLO。- 两次读取之间的
last_evaluated持续更新,才能证明评估器已处理该 SLO。值为 Null 或停滞不变,说明评估器从未处理它——请检查status、评估间隔和分组上限。 - 非零的
total_events(或total切片数/秒数)还能证明查询可以解析并且匹配到了数据。 - 零不一定是问题。 安静的服务会正常产生空时间桶——
by_count的结果是no_data,time_slice的结果是正常运行时间。修改 SLO 前,使用analyze-traces/analyze-logs/analyze-metrics直接查询相同范围和时间窗口:如果那里也没有记录,说明缺少遥测数据,这是需要在其他地方解决的不同问题。 - 绝不要扩大用户的筛选范围来让数值出现。 这会改变测量对象,变成没人要求测量的内容。请报告空结果以及你检查过的内容。
- 两次读取之间的
- 如果设置了
alert_types,请验证生成的告警——它们应作为规则存在,名称为"<SLO name> Fast Burn Rate"等,并使用你提供的通道 ID。 - 使用
PUT(完整请求体,ID 放在请求体内)更新,或使用DELETE /api/slo/{id}删除,然后重复第 5–7 步。更新操作会重新生成对应的规则。
三种 SLI 类型
slo_type | 分子 / 分母 | 配置项 | 计数单位 |
|---|---|---|---|
by_count | 良好事件数 / 总事件数 | 两个查询:good_events_filter 和 total_events_filter | events |
time_slice | 良好时间片数 / 总时间片数 | 一个查询(total_events_filter)+ operation + threshold_value | slices |
alert | 未触发秒数 / 总秒数 | 在 total_events_filter.alert_ids 中填写告警规则 ID | seconds |
省略 slo_type 时,默认值为 by_count。UI 中的名称分别是“按计数”“按时间片”和“按正常运行时间”。
路由
| 用户询问…… | 执行此操作 |
|---|---|
| “为 X 创建一个 SLO” | 构造 POST /api/slo 请求体 — references/api-payloads.md |
| “这个 SLO 能达标吗?”/“合规情况会如何?” | 创建前先调用 POST /api/slo/preview — 无需付费,也不会存储任何内容;但这只是估算,并非重放(限制) |
| “我们有哪些 SLO / 哪些已违约?” | 调用 POST /api/slo/list?status=active¤t_time=…,或 POST /api/slo/stats |
| “这个 SLO 为什么在消耗预算?” | 调用 GET /api/slo/{id}/burn-rate/timeseries,然后使用相同的筛选条件查询 kubesense-traces / kubesense-logs |
| “预算消耗时提醒我” | 在 SLO 上设置 alert_types — 不要手动编写燃烧率告警规则 |
| “迁移我们的 Datadog SLO” | references/datadog-migration.md |
| “在仪表板上显示 SLO” | kubesense-dashboards — panelType: "slo" |
端点
所有端点均位于 /api/slo 下,RBAC 模块为 slo。创建、更新和删除操作还需要该模块的写入权限。
| 方法 | 路径 | 用途 |
|---|---|---|
POST | /api/slo | 创建 — 在 data 中返回新 SLO 的 ID |
PUT | /api/slo | 更新 — ID 应作为请求体中的 id 传入,而非放在路径中 |
DELETE | /api/slo/{id} | 软删除(设置 removed),并移除其燃烧率告警规则 |
GET | /api/slo/{id}?current_time=<RFC3339> | 获取单个 SLO 及其汇总数据 |
POST | /api/slo/list?status=active¤t_time=<RFC3339> | 列表;筛选条件放在 JSON 请求体中 |
GET | /api/slo/search?search=<substr>&limit=50 | 仅返回 {id, name} — 用于选择器 |
POST | /api/slo/stats | 按状态统计数量:normal / warning / breached / no_data |
POST | /api/slo/preview | 对某个时间窗口执行合规性试运行,不存储任何内容(状态为 normal/breached/no_data — 永远不会是 warning) |
GET | /api/slo/{id}/metrics?from_time=&to_time= | 获取某个时间窗口的良好 / 不良 / 总量及预算 |
GET | /api/slo/{id}/groups?windows=last_24_hour,last_7_days | 获取各分组的数据,一次扫描多个时间窗口 |
GET | /api/slo/{id}/timeseries | 随时间变化的合规情况 |
GET | /api/slo/{id}/burn-rate/timeseries | 随时间变化的燃烧率 |
POST | /api/slo/filters, /api/slo/filter/search | 获取列表页的筛选值 |
警告
/list和GET /{id}必须提供current_time,且/list必须提供status。两者都是查询参数,而非请求体字段。省略current_time会导致调用返回 500 错误(“currentTime must not be zero”);省略status时,WHERE status = ?将无法匹配任何内容,结果会是空列表,看起来就像“没有 SLO”。请传入status=active。
创建负载
基于 traces 的最小可行 by_count SLO:
{
"name": "Checkout availability",
"description": "Successful checkout spans / all checkout spans",
"slo_type": "by_count",
"metric": "requests",
"slo_target_percentage": 99.9,
"evaluation_window_days": 30,
"evaluation_time_interval_seconds": 60,
"evaluate_from": "2026-09-01T00:00:00Z",
"evaluation_start_time": "2026-09-01T00:00:00Z",
"status": "active",
"cluster": "", "namespace": "", "workload": "", "customer_identifier": "",
"operation": "", "threshold_value": 0, "value": 0,
"good_events_filter": { "selectedMode": "traces", "value_operation": "row_count", "groupBy": [], "unified_filter": { "type": "common", "common_filter": [ {"field": "app_service", "operation": "IN", "values": ["checkout"]}, {"field": "status", "operation": "IN", "values": ["ok"]} ], "adv_filters": {} } },
"total_events_filter": { "selectedMode": "traces", "value_operation": "row_count", "groupBy": [], "unified_filter": { "type": "common", "common_filter": [ {"field": "app_service", "operation": "IN", "values": ["checkout"]} ], "adv_filters": {} } },
"alert_types": ["burn_rate_fast", "error_budget_low"],
"notification_channels": [1]
}
每种类型的结构、metrics 和 logs 变体,以及响应正文,都在 references/api-payloads.md 中。
正文规则:
evaluation_time_interval_seconds必须 > 0 — 服务端会直接拒绝 0,评估器也会跳过间隔为 0 的 SLO,避免无限循环。status: "active",否则评估器不会选取它。slo_target_percentage是百分比(99.9),不是小数。- 设置
warning_target_percentage时,应当高于目标值 — 这样才能在违约前发出警告。UI 会强制要求>= target + 0.1;API 完全不会验证此值,低于目标值的值会被存储,但永远不会达到,因为会先检查是否已违约。 metric是一个标签,不是查询。traces SLI 使用"requests",metrics SLI 使用 PromQL 指标名称,alert SLI 使用"alert_uptime"。系统不会根据它执行任何计算 — 它只会显示在列表页和筛选条件中。operation/threshold_value仅适用于time_slice条件。by_count会忽略它们;应将延迟阈值作为时长筛选条件并入 good 查询。- 更新是完整替换:
PUT /api/slo会根据正文写入每一列,因此请发送完整对象(先用GET /api/slo/{id}读取),并在正文中设置id。
Good 和 Total 是 Alert 查询配置
good_events_filter 和 total_events_filter 是 alert 规则使用的同一种 query_config 对象 — JSON 键、执行器和字段允许列表都相同。这是最有用的一点:任何关于构建 alert 规则查询的知识都适用于此处。
| 键 | 含义 |
|---|---|
selectedMode | traces | logs | metrics。为空时默认为 traces。 |
unified_filter | traces/logs 筛选树:{type, common_filter[], adv_filters{}} |
promql | 仅用于 metrics — 完整的 metrics 查询。(alert 规则的 queryMode 在此处没有对应字段,因此会被丢弃。) |
value_operation | row_count(通常选择此项)、unique_count、avg、sum、min、max、百分位数 |
fields | 任何非计数型 value_operation 都必需 |
groupBy | [{field, type, is_attribute}] — 参见分组。 |
alert_ids | 仅用于 alert 类型的 SLO |
重要
字段名称遵循 ALERT 引擎的词汇,而不是查询工具的词汇。 Logs 和 traces SLI 查询会通过 alert 规则使用的同一套规则引擎执行器运行,因此应使用
app_service(而不是service)、level(而不是type)、pod_name/pod、node_name/node、return_code/status_code。完整的允许列表见 kubesense-alerts — 这些字段同样适用于 SLO。还有一点差异需要注意:
POST /api/slo/preview会通过 API 自身的 explore 引擎运行查询,而评估器会通过规则引擎运行查询。 两者的字段映射有重叠,但并不完全一致,因此某个名称可能在预览中正常,但每次评估时仍然报错。预览还会对 traces 和 logs 强制使用row_count(忽略value_operation和fields,因此延迟 SLI 预览的实际上是请求数),并且对 metrics 只采样约 300 个数据点,而不是评估每个 bucket。预览可以捕获筛选条件为空或错误的情况,但不会验证数值。创建后,请在第一个真实 bucket 上确认。
公式和比率
由多个查询和类似 (A/B)*100 的表达式构建的 Datadog 监控器或 SLO,无法作为一个整体迁移。迁移目标决定了它的形式:
| 要迁移的内容 | 迁移到哪里 | 方式 |
|---|---|---|
| SLI 为分子 / 分母的 SLO | by_count SLO | 两个查询,不使用公式。 A → good_events_filter,B → total_events_filter。评估器会自行计算两者的比值。 |
| 对比率设置阈值的监控器 | 告警规则 | 告警支持真正的公式模式——将 A、B 和 expression 保留为独立查询,并将 metric_query_label 指向公式的标签。参见 kubesense-alerts。 |
| 每个时间间隔都将比率与阈值比较的 SLI | time_slice SLO | 一个内联的 PromQL,搭配 operation + threshold_value。 |
| 临时分析,不涉及 SLO 或规则 | analyze-telemetry (MCP) | 一组带标签的查询,加上 {"selectedMode": "formula", "expression": "(A/B)*100"} — 可组合日志、追踪和指标。 |
警告
selectedMode: "formula"在 SLO 中不起作用。 评估器通过一个执行器工厂解析查询;该工厂支持logs、metrics和traces,其他类型都会返回unsupported query type: formula——每次评估都会如此,因此 SLO 会永远停留在no_data。告警支持公式;SLO 不支持。
改为将其内联为一个 PromQL 字符串。 产品本身就是这样处理的:SLO 编辑器的公式构建器会在提交前,将每个标签替换为其括号括起的查询,并存储为一个扁平的 promql。因此,(A/B)*100 会存储为
(sum(increase(http_requests_total{status=~"5.."}[5m])) / sum(increase(http_requests_total[5m]))) * 100
重要
不要将比率内联到
by_countSLO 中。 在那里,比率 就是 SLO——评估器会计算 good/total,并据此构建错误预算。如果将百分比作为total_events_filter输入,它会把百分比计为事件,后续所有数值都将毫无意义。比率应放在time_slice(每个切片一个值,与阈值比较)或告警规则中。
对内联比率进行分组时,除法的两侧都要保留 by (…),并且查询中的 groupBy 不能留空——仅使用 by (…) 会让 SLO 仍走单序列路径(参见“分组”)。
分组
在 total_events_filter 上设置 groupBy(并在 good_events_filter 上设置匹配项),SLO 就会按组拆分为各自的合规性数值,并按组标识进行合并。
- 每个
kubesense_slo_*序列都会以 Prometheus 标签的形式输出各组,此外还会输出一个标记为slo_group="*"的汇总整体序列。 slo_id、slo_group、window、target_percentage、error_budget和__name__是保留字段——如果某个groupBy字段最终生成了这些名称之一,就会被丢弃。按workload、namespace或cluster分组没有问题,这正是将它们加入序列的方式。- 字段名称会转换为有效的 Prometheus 标签名称:
[a-zA-Z0-9_]以外的字符都会变成_,因此service.name→service_name。 - 最多只能有 100 个组。 超过此数量时,评估器会跳过整个存储桶并记录日志——SLO 会悄无声息地停止生成数据。切勿按
pod、instance、trace_id或任何基数不受限的字段分组。 - 良好侧中没有匹配总量组的组会被截断,并发出警告。两个
groupBy列表必须完全一致。 - 指标 SLI 也需要非空的
groupBy。 PromQL 中的by (…)本身不会启用分组:评估器读取的是total_events_filter.groupBy;如果列表为空,多序列结果会被缩减为第一个序列,其余序列会被静默丢弃。(指标上的time_slice是例外——它始终以多序列方式运行。)参见 references/api-payloads.md。
燃烧率告警由 alert_types 生成
设置 alert_types 后,API 会在与 SLO 相同的事务中为你创建告警规则:
alert_types 值 | 规则 | 严重级别 |
|---|---|---|
burn_rate_fast | kubesense_slo_burn_rate{window="1h"} 超过 (days × 24 × 0.02) / 1 | 严重 |
burn_rate_slow | kubesense_slo_burn_rate{window="6h"} 超过 (days × 24 × 0.05) / 6 | 警告 |
error_budget_critical | kubesense_slo_error_budget_balance < 5 | 严重 |
error_budget_low | kubesense_slo_error_budget_balance < 10 | 警告 |
阈值根据 evaluation_window_days 推导,因此 30 天的窗口会得到熟悉的 14.4× / 6× 多窗口燃烧率。每次更新时都会重新计算。
警告
切勿手动编写这些规则。
PUT /api/slo会删除附加到该 SLO 的所有告警规则,并根据alert_types重建它们;DELETE会将它们删除。 对生成的规则进行的编辑会在下一次 SLO 更新时被静默还原。“附加”指的是规则自身的
labels.slo_id,而不是其 PromQL。删除操作使用WHERE JSON_EXTRACT(labels, '$.slo_id') = <id>,不会检查规则是谁创建的。因此,查询中仅选择kubesense_slo_burn_rate{slo_id="…"}的手写规则会保留;但如果规则自身的标签中带有slo_id——复制生成的规则时显而易见的做法——就会和它们一起被删除。若要保留规则,请勿在其标签中添加slo_id。SLO 上的
notification_channels决定这些生成的规则会向哪些渠道发送告警。请先调用list-notification-channels(MCP)或GET /api/alerts/notification-channels——如果 ID 不存在,规则创建就会失败,并导致整个创建事务回滚。
窗口、频率与回填
evaluation_window_days是滚动合规窗口(7、30、90 等)。evaluation_time_interval_seconds是时间桶大小;对于time_slice,也是切片大小。UI 为切片提供 60 和 300 这两个选项,与 Datadog 提供的两个值相同。evaluate_from是评估的起始位置,评估器会从这里开始,以每轮 20 个时间桶的批次回填至当前时间。若将其设为几个月前且间隔为 60s,在追上实时数据前就意味着需要处理数万个时间桶——请将其设为你实际关心的窗口起点。- 如果
evaluation_start_time早于evaluate_from,就会被向后调整为evaluate_from。 - UI 和
/groups展示的汇总数据:last_2_hour、last_24_hour、last_7_days、last_28_days。
陷阱
| 陷阱 | 后果 |
|---|---|
evaluation_time_interval_seconds: 0 | 创建时返回 500;现有值为 0 时,评估器会跳过该 SLO |
status 不是 "active" | 已存储、会出现在列表中,但永远不会被评估 |
/list 未指定 status=active | 列表为空,看起来像是“没有 SLO” |
/list 或 GET /{id} 未指定 current_time | 返回 500 |
未指定 selectedMode | 会被静默视为 traces——仅设置了 promql 的指标 SLI 会查询 traces,结果一无所获 |
在 unified_filter 中使用查询工具的字段名(service、type) | 每次评估都会出错;SLO 会停留在 no_data |
groupBy 解析出的分组数 > 100 | 所有时间桶都会被跳过——没有数据,也不会在 UI 中显示错误 |
好事件和总事件的 groupBy 列表不同 | 多出的好事件分组会被截断;这些分组的合规率会显示为 100% |
未设置 alert_ids 的告警 SLO | 评估器会跳过它(否则它会永远报告完美的 100%) |
warning_target_percentage 低于目标值 | 永远不会发出警告——警告阈值必须高于目标值 |
time_slice 的 operation 使用长格式(less_than)或小写(lt) | 通过 API 验证,但评估器无法识别——每个时间片都会被判定为停机,永远是 0%。仅支持大写的 GT/GTE/LT/LTE/EQ |
time_slice 的 operation: "NEQ" | 创建时会被拒绝,尽管 UI 下拉菜单提供了此选项 |
Metrics by_count 使用 by (…),但 groupBy 为空 | 只会统计第一个序列;其余序列会被静默忽略 |
手写规则自身的标签中包含 slo_id | 下次 PUT /api/slo 时,该规则会和生成的规则一起被删除 |
evaluate_from 早于规则创建时间的告警 SLO | 会回填虚构的完美可用时间——不会记录为无数据 |
| 编辑生成的消耗率规则 | 下次 PUT /api/slo 时会被还原 |
使用不完整的请求体执行 PUT | 完整替换——未发送的字段会被写入其零值 |
evaluation_window_days 不是 7 的倍数且大于 31(例如 90) | 通过 API 可以正常使用,但 UI 的周期输入框(1–31 × 天/周)无法表示该值,因此之后在 UI 中编辑会验证失败 |
不同类型的缺失数据语义不同——这是有意设计的
by_count:分母为空时为no_data。不会计算合规率,也不会对该时间桶评分。time_slice:缺失数据计为正常运行时间,与 Datadog 一致。计数查询返回 0 时会被视为“无数据”,因此零流量分钟属于正常时间片。这意味着遥测中断也会显示为健康的 SLO。alert:停机时间是指定规则触发区间的并集;如果规则在窗口边界仍处于触发状态,则会计为持续触发,直至该边界。完全没有触发记录的时间桶会被评为完全健康,而不是无数据——因此,早于规则创建时间的evaluate_from会向预算中回填虚构的完美可用时间。请将evaluate_from设为规则开始存在的时间。
交接 SLO 时,请说明适用哪一种。这是“这个数值看起来不对”的最常见原因。
错误预算和消耗率
error_budget_percentage=100 - slo_target_percentage。error_budget_balance=(1 - error% / budget%) × 100,不设上下限——预算超支时会变成负数(实际值为 99.52%、目标值为 99.9% 时为 −380%),与 Datadog 一致。不要“修正”负数;它反映的正是预算消耗情况。- 消耗率经过归一化:无论窗口长度如何,1.0 都表示恰好消耗整个窗口的预算。列表页指示器使用 2h 窗口,超过 6 为严重,超过 1 为升高——与 Datadog 显示的阈值相同。
- 输出的序列:
kubesense_slo_evaluated_events_total/_good、kubesense_slo_evaluated_events_bad_percent、kubesense_slo_error_budget_balance、kubesense_slo_burn_rate(窗口为1h、2h、6h)、kubesense_slo_breached、kubesense_slo_status、kubesense_slo_burn_rate_status。使用 kubesense-metrics 查询这些序列。
规则
- 首先发现真实的指标和字段名称(
get-available-metrics、get-trace-or-log-fields)。绝不要臆造名称。 - 在
unified_filter中使用告警引擎的字段词汇——用app_service, 而不是service;用level,而不是type。 - 创建前务必先执行
POST /api/slo/preview,并向用户展示返回的合规性和预算数值。预览免费,且不会存储任何内容。 - 在两个筛选器上都显式设置
selectedMode。默认值是traces。 - 始终确保
evaluation_time_interval_seconds > 0且status: "active"。 good和total的groupBy必须相同,并将分组数限制在远低于 100 组;即使是指标 SLI 也要设置它,因为仅使用by (…)会让你得到一个序列。- 绝不要手写燃烧率告警规则——使用
alert_types,并先确认通道 ID 存在。 PUT会发送整个对象;编辑前先读取 SLO。time_slice运算符必须为大写的GT、GTE、LT、LTE、EQ。其他写法都无法端到端生效。- 说明你所构建类型的缺失数据语义——对于告警 SLO,还要将
evaluate_from设为其规则开始存在的时间。 - 除非
POST /api/slo返回了 ID,否则绝不要声称 SLO 已创建;在真实数据桶返回事件(步骤 6)之前,也绝不要称其正常工作。