关于 observability-k8s-investigation
observability-k8s-investigation 是用于排查 Kubernetes 问题的技能,面向需要分析集群遥测数据的运维人员。它关联 OTel 指标、事件、日志和 APM 信号,协助定位工作负载、节点及控制平面层面的原因。
主要功能
- 01关联 Kubernetes 多层遥测信号
- 02分析容器资源指标与重启
- 03检查 Kubernetes 事件和对象状态
- 04结合应用日志及 APM 数据
- 05基于证据评估根因与不确定性
适用场景
- 01调查 Pod 频繁重启或 OOMKilled
- 02排查 CPU 限制导致的探针超时
- 03结合事件与日志分析调度或驱逐问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ elastic/ agent-skills --skill observability-k8s-investigation安装后在 Claude Code 中输入/observability-k8s-investigation确认已加载。
附带文件references/failure-modes.mdreferences/query-recipes.md
详细介绍
Kubernetes 问题调查
使用通过 EDOT(Elastic Distribution of OpenTelemetry)和 kube-stack 收集器采集的 OTel 遥测数据诊断 Kubernetes 问题。关联集群状态、Pod 运行时指标、K8s 事件、应用日志和 APM,从工作负载、节点和控制平面各层识别根因。
环境配置
此技能通过 elastic CLI 执行 Elasticsearch 操作。如果未安装
elastic CLI,请告知用户需要它的原因。不要猜测凭据、直接调用 HTTP API 或尝试其他变通方法。
此技能以 HTTP 简写形式引用操作(例如,GET /、GET /_cat/indices、GET /{index}/_mapping、
GET /{index}/_settings/index.mode、POST /_query)。本文档末尾的 操作 表将每种简写映射到对应的 elastic CLI 命令——始终使用 CLI,不要直接调用 HTTP API。
无法访问集群时的分析
上述 CLI 检查只限制_查询集群_,不限制分析。如果用户已在问题中提供证据(指标值、计数、状态原因、日志行、告警负载、配置),请基于这些证据进行推理并给出结论。
如果确实需要用户未提供的数据,仍需说明要检查什么以及如何检查——指出能够明确问题的具体查询、索引和字段——然后再请求配置 CLI。指出要检查什么,即使无法访问集群也有帮助;只请求配置则没有帮助。
此技能及 references/query-recipes.md 中的所有 ES|QL 查询都通过 POST /_query 执行。告警状态通过 GET kbn:/api/alerting/rules/_find 读取。字段存在性检查使用 GET /<index>/_mapping 或 GET /_field_caps。操作 表将这些操作映射到对应的 elastic CLI 命令。
范围
范围内: 使用 OTel 接收器命名空间的索引(metrics-kubeletstatsreceiver.otel-*、
metrics-k8sclusterreceiver.otel-*、logs-k8seventsreceiver.otel-*、logs-k8sobjectsreceiver.otel-*)和 OTel
语义约定(k8s.pod.name、k8s.namespace.name、k8s.container.restarts)。
范围外:
- 旧版 Elastic Agent Kubernetes 集成(
metrics-kubernetes.*、logs-kubernetes.*、kubernetes.*字段)。 已弃用——不要针对这些路径编写查询。 - APM 层分析(服务 SLO 违约、事务错误率、上游依赖运行状况)。这是不同领域——确认或排除 K8s 根因后,应转交给 observability-sre-triage 技能;该技能负责 SLO 状态和消耗速率、活动告警规则、吞吐量、延迟、错误率、依赖运行状况和日志归集。如果工作负载最终并非托管在 Kubernetes 上,也应使用该技能。
- 集群配置、容量规划、成本优化。这是不同领域。
指南
这些指南适用于每次调查。如有疑问,撰写综合结论前请重新阅读。
没有证据不等于证据不存在。切勿根据空结果臆造结论。 如果日志查询返回 0 行,可能是没有收集日志,或 Pod 最近没有日志行——这 并不 意味着“依赖项不可用”或任何其他特定故障模式。请报告 no_logs_available,并据此调整其余信号的权重。
依赖项数据为空 ≠ 上游健康。 未进行 APM 插桩的服务(负载生成器、工作器)不会发出目标指标。请报告 insufficient_dependency_data,而不是“上游正常”。
共现症状不是原因。 两个服务同时降级通常是因为它们共享同一个上游,而非彼此存在因果关系。只有在 (a) 一个服务的降级明确早于另一个服务,且 (b) 差异较大(错误率 >5×,延迟 >3×)时,才归因于因果关系。
OOMKilled 默认不等于内存泄漏。 限制可能只是低于工作负载工作集所需的大小。通过内存曲线的形态区分:内存单调增长直至达到限制,每次重启后重置;负载与上一周持平,且近期没有部署——这是内存泄漏的特征,应以高置信度得出结论。仅在曲线形态不明确(有尖峰、呈昼夜规律或与负载相关)时,才参考 7 天内同一时段的基线;这不是每次发现 OOMKilled 的先决条件。
因错误终止 ≠ 默认是应用程序缺陷。 请先检查 k8s.container.cpu_limit_utilization。在此类问题中,最常见的误诊是 CFS 节流导致存活探针超时。
CPU 平均值会掩盖节流。 Pod 的平均 cpu_limit_utilization 即使看起来健康(40–60%),在 p99 时也可能遭遇严重节流。Linux 以 100ms 为周期执行 CPU 限制;突发型工作负载会在周期中途达到配额并停滞。请查看最大值和 p95,不要只看平均值。
重启次数是布尔值,不是计数器。 k8s.container.restarts 直接从 K8s API 获取,kubelet 可能随时清除该值,因此绝对值不可靠。请将其视为 == 0(近期没有重启)与 > 0(近期正在重启);不要据此推断退避时序或“线性还是指数”模式。请改为通过 K8s 的 Killing / BackOff 事件确认重启模式。
与其凭空营造信心,不如报告不确定性。 如果证据含糊不清,综合结论应明确说明。提出相互竞争的假设也是有效结论。
同样,不要凭空营造不确定性。 上述规则针对的是证据含糊,而非语气。关键指标已出现且有佐证时,应以高置信度明确下结论。对明确的发现含糊其辞、将置信度降至“中等”,与过度声称同样是缺陷。
给出综合结论后即停止。 置信度只需在 HYPOTHESIS 行中说明一次,不要在每条要点中重复。除非查询结果改变了结论,否则不要叙述运行过哪些查询,也不要向读者复述告警。请以 RECOMMENDED NEXT STEPS 或 DOWNSTREAM IMPACT 结尾;切勿以“需要我继续查看吗?”之类的提议收尾。后续工作应放在建议列表中,并以建议的形式表述。
索引和字段
查看位置
| 信号 | 索引模式 | 用途 |
|---|---|---|
| Pod/容器运行时 | metrics-kubeletstatsreceiver.otel-* | CPU、内存、网络、文件系统。利用率比率。 |
| 集群状态 | metrics-k8sclusterreceiver.otel-* | 重启、阶段、上次终止原因、HPA、配额、节点状态 |
| K8s 事件 | logs-k8seventsreceiver.otel-* | Killing、BackOff、FailedScheduling、Evicted、镜像拉取事件 |
| K8s 对象快照 | logs-k8sobjectsreceiver.otel-* | 随时间变化的 Deployment/service/configmap 状态 |
| 应用日志 | logs-*.otel-* | body.text、severity_text,按 k8s.pod.name 筛选 |
| APM | traces-*.otel-*、metrics-service_*.otel-default | 通过 service.name + K8s 资源属性进行关联 |
| ML 异常 | .ml-anomalies-* | 内存增长、重启率、限流任务(如果已配置) |
关键字段
扁平 OTel 路径可用于 ES|QL。为提高可读性,优先使用扁平形式;嵌套的 resource.attributes.* 形式仅适用于原始日志文档。
| 字段 | 索引 | 含义 |
|---|---|---|
k8s.pod.name | all k8s | Pod 名称 |
k8s.namespace.name | 仅限 metrics | Namespace。已映射,但在 k8seventsreceiver 上为 null |
attributes.k8s.namespace.name | k8seventsreceiver | 事件中的 Namespace — 在此处按此形式筛选 |
k8s.container.name | all k8s | Pod 中的容器 |
k8s.deployment.name | k8sclusterreceiver + others | 上级 Deployment |
k8s.pod.phase | k8sclusterreceiver | Pending=1/Running=2/Succeeded=3/Failed=4/Unknown=5 |
k8s.container.restarts | k8sclusterreceiver | 容器重启总次数 |
k8s.container.status.last_terminated_reason | k8sclusterreceiver | OOMKilled、Error、Completed、ContainerCannotRun |
k8s.pod.status_reason | k8sclusterreceiver | Pod 级别原因(Evicted、NodeLost) |
k8s.container.memory_limit_utilization | kubeletstatsreceiver | 0.0–1.0+(发生 OOM 前可能短暂超过 1) |
k8s.container.cpu_limit_utilization | kubeletstatsreceiver | 0.0–N(CFS 限流期间经常 >1) |
k8s.pod.memory_limit_utilization | kubeletstatsreceiver | 整个 Pod 的聚合值;使用前请参阅下方说明 |
k8s.pod.cpu_limit_utilization | kubeletstatsreceiver | 整个 Pod 的聚合值;使用前请参阅下方说明 |
k8s.pod.memory.usage / .working_set | kubeletstatsreceiver | 字节 |
k8s.node.condition_memory_pressure | k8sclusterreceiver | 1 = 有压力,0 = 正常 |
k8s.node.condition_ready | k8sclusterreceiver | 0 = NotReady |
k8s.hpa.current_replicas / .desired_replicas | k8sclusterreceiver | HPA 状态 |
attributes.k8s.event.reason | k8seventsreceiver | 事件原因(按此字段筛选) |
body.text | k8seventsreceiver / logs | 事件消息 / 日志消息 |
k8s.object.name | k8seventsreceiver | involvedObject 名称(日志属性,使用扁平形式) |
容器级与 Pod 级限额利用率
请在容器级别读取限额利用率。k8s.container.cpu_limit_utilization 和
k8s.container.memory_limit_utilization 是默认字段;Pod 级字段是另一种测量值,并非同义字段。
接收器会在同一数据流的不同文档上输出这两组字段:Pod 级字段出现在不包含 k8s.container.name 的文档上,容器级字段则只出现在包含该字段的文档上。因此,STATS ... BY k8s.container.name 对所有 Pod 级字段都会返回 null,反之亦然。在一个运行中的 9.6.0 集群上测量一小时:42,240 个不包含容器名称的文档中,有 360 个包含 k8s.pod.cpu_limit_utilization,没有文档包含容器级字段;18,240 个包含容器名称的文档中,有 900 个包含容器级字段,没有文档包含 Pod 级字段。
字段可用性也有所不同。每个声明了限额的容器都会输出容器级利用率,而 Pod 级聚合要求 Pod 中每个容器都声明限额。在两个运行中的集群上测量三小时,没有 Pod 在缺少容器级字段的情况下包含 Pod 级字段;有 27 个 Pod 包含容器级字段,但缺少 Pod 级字段——这些 Pod 全是多容器 Pod,且只有部分容器声明了限额。针对注入了 sidecar 的 Pod 进行 Pod 级限流检查时,会静默返回 null。
| 集群 | 仅容器级 | 两级都有 | 都没有 | 仅 Pod 级 |
|---|---|---|---|---|
| forge-factory | 18 | 7 | 44 | 0 |
| k8s-demo | 9 | 6 | 40 | 0 |
仅当问题确实涉及整个 Pod——即相对于其所有容器限额总和的总消耗——并且确认字段已填充后,才使用 Pod 级字段。observability-sre-triage 也遵循同一规则,因此两个技能针对同一 Pod 会返回相同答案。
字段可用性
上述几个字段在标准 kube-stack 收集器中默认关闭,需要显式配置。使用 GET /<index>/_mapping 或 GET /_field_caps 确认字段是否存在;如果不存在,请按说明使用备用方案,并在综合说明中注明替代情况。
| 字段 | 可能缺失的原因 | 替代方案 |
|---|---|---|
k8s.container.status.last_terminated_reason | k8sclusterreceiver 中的可选指标;受 metrics_collected.metadata 配置控制。 | 根据 logs-k8seventsreceiver.otel-* 中的 K8s Killing / OOMKilling 事件以及应用日志中的退出代码推断。 |
k8s.pod.status_reason | 同上——k8sclusterreceiver 中的可选指标。 | 根据事件推断:Evicted、NodeLost、Preempted。 |
k8s.container.cpu_limit_utilization / memory_limit_utilization | 仅当容器声明了相应限制,且启用了 kubeletstatsreceiver 指标时才会生成。 | k8s.pod.cpu.node.utilization / k8s.pod.memory.node.utilization 表示消耗占节点容量的比例,无论是否声明限制都会生成;或者将绝对值 container.cpu.usage / container.memory.usage 与基线进行趋势比较。这两种替代方案都位于 Pod 级文档中,因此请按 k8s.pod.name 分组,而非按 k8s.container.name 分组。 |
k8s.pod.cpu_limit_utilization / memory_limit_utilization | 要求 Pod 中的每个容器都声明限制,因此在大多数多容器 Pod 中都不存在。 | 使用容器级字段,它们的可用范围严格来说更广。 |
k8s.node.condition_memory_pressure | 受 k8sclusterreceiver 的 node_conditions_to_report 控制(默认不包含此项)。 | 将 k8s.node.memory.usage 与 k8s.node.allocatable_memory 进行比较,或查找节点上的 Evicted 事件。 |
如果使用了回退方案,请在综合分析中注明(例如,(via memory.usage; limit_utilization not collected)),以便读者知道该信号是间接的。 |
ES|QL 注意事项
编写查询前,请了解以下内容。这些情况都会悄无声息地产生错误答案,而不是明确报错。
VALUES() 在只有一个不同值时返回标量,有多个不同值时返回数组。 假设结果始终是数组的模板(例如,| first),遇到标量时会提取字符串的第一个字符。请使用 MV_FIRST(VALUES(...)),或同时处理这两种情况。
VALUES() 的版本新于此技能的最低基准版本。 在 Serverless 上它已正式发布;但在 Stack 上,它从 8.14.0 起处于预览阶段,到 9.4.0 才正式发布,且在 8.14 之前完全不可用。使用前请检查 GET /:build_flavor: "serverless" 表示该功能可用;否则请查看 version.number。如果该功能不可用,请将字段移入 BY 子句,而不是对其进行聚合——每个不同值对应一行,携带的信息相同:
| STATS restarts = MAX(k8s.container.restarts), phase = MAX(k8s.pod.phase)
BY term_reason = k8s.container.status.last_terminated_reason
| SORT restarts DESC
| LIMIT 10
PERCENTILE 不适用于 OTel histogram 类型(截至 8.15)。对于 APM 时长百分位数,请对 aggregate_metric_double 汇总字段使用 AVG(AVG(transaction.duration.summary) 会用总和除以 value_count)。如需真实百分位数,请回退到 Kibana Query DSL。
COUNT(agg_metric_double) 返回的是 value_count(事件数),而非文档数。 SUM(field) 返回总和分量;AVG(field) 返回总和除以 value_count。不要将 SUM(transaction.duration.summary) 用作事件数的替代指标——它返回的是总时长。
K8s 指标在 ES|QL 中使用扁平的 OTel 字段路径。 使用 k8s.pod.name,而不是 resource.attributes.k8s.pod.name。嵌套形式用于原始日志文档。
故障模式分类
各模式的分类术语——包括跨工作负载、节点、控制平面、自动扩缩和网络层的关键指标与佐证检查,以及两种模式都符合时的处理方法——见references/failure-modes.md。分类前请先阅读。
信号解读
内存
- 在 30–60 min 内持续上升 → 内存泄漏。检查对应语言的 GC 指标:JVM 使用
jvm.gc.duration,Go 使用process.runtime.go.gc.pause_ns,Node 使用v8js_gc_duration。在存活集稳定的情况下,GC 频率或暂停时间上升是典型的内存泄漏特征。 - 随昼夜变化或负载相关的峰值 → 由负载驱动,并非内存泄漏。考虑调整 HPA 或提高限制。
- 达到 1.0 后重启 → 已确认 OOMKilled。应用日志中的退出代码 137(SIGKILL)与此相符。
CPU
- 持续出现
cpu_limit_utilization > 1.0→ CFS 限流。节点有空闲 CPU;Pod 受到配额限制。 - 限流的症状(并非限流指标本身):存活探针超时、p99 延迟达到 p50 的 4–16×、上游队列出现背压,以及原因字段为 Error 的容器终止。
- 平均值看起来可能正常,但 p95 可能受到限流。不要只相信平均值。
重启模式
- 最近
restarts > 0→ 工作负载近期发生过重启。不要根据计数值判断程度(请参见 重启计数是布尔值);请通过logs-k8seventsreceiver.otel-*中 K8sKilling/BackOff事件的时间戳确认重启模式。 - 重启与内存压力(
memory_limit_utilization → 1.0)相关 → OOMKilled 路径。 - 没有内存或 CPU 压力却发生重启 → 探针配置错误、应用缺陷或启动依赖失败。请提取
Unhealthy和Killing事件。
终止原因
OOMKilled→ 内存问题。Error→ 非零退出。检查应用日志;如果日志为空或内容很少,请先检查 CPU 限流,再归因于应用逻辑。Completed→ 已运行完成。对于 Job/CronJob/初始化容器而言属正常情况;其他情况下则异常。ContainerCannotRun→ 运行时、镜像或执行问题。检查镜像拉取事件。
调查流程
调查不是一份检查清单。以下各节描述的是一条_典型_路径——根据发现的情况进行精简、跳过或重新审视。 一旦有足够证据达到已知置信度,就立即结束。收益递减后仍继续追查信号,这是一种失败,而不是严谨。
确定方向
确定目标:k8s.pod.name、k8s.namespace.name,以及可选的 k8s.deployment.name 和 service.name。如果未指定时间范围,Pod 级调查默认查看最近 1 小时,事件关联默认查看最近 2 小时,持续中或尚未解决的事故默认查看最近 6 小时。
如果告警负载已经说明故障模式(例如,告警专门针对 OOMKilled 触发),请记录这一点并跳过分类;转而进行确认和基线比较。
描述特征
了解工作负载近期行为的概况:重启次数、终止原因、阶段、利用率。通常一两条查询就足够。
FROM metrics-k8sclusterreceiver.otel-*
| WHERE k8s.pod.name == "<pod>" AND k8s.namespace.name == "<ns>"
AND @timestamp > NOW() - 1 hour
| STATS restarts = MAX(k8s.container.restarts),
term_reasons = VALUES(k8s.container.status.last_terminated_reason),
phase = MAX(k8s.pod.phase)
VALUES() 需要 Serverless 或 Stack 8.14+(GA 9.4)。在较旧的 Stack 集群上,请使用
ES|QL 注意事项中的 BY 子句改写,而不是从查询中删去终止原因。
FROM metrics-kubeletstatsreceiver.otel-*
| WHERE k8s.pod.name == "<pod>" AND @timestamp > NOW() - 15 minutes
| STATS mem_pct = ROUND(MAX(k8s.container.memory_limit_utilization) * 100, 1),
cpu_pct = ROUND(MAX(k8s.container.cpu_limit_utilization) * 100, 1)
BY k8s.container.name
按容器分组:注入了 sidecar 的 Pod 包含多个容器,只有声明了限制的容器才会报告利用率。如果每一列都返回
null,则表示没有声明限制——请按字段可用性中的说明进行后续处理,不要将
null 解读为空闲。
分类
使用references/failure-modes.md中的分类法。关键判定信号应当匹配;“Investigate”列会告诉你需要寻找哪些佐证。
如果两种模式都符合,请同时记录,并优先处理关键判定信号更强的一种。你可以在佐证过程中修正判断。
佐证
提取分类结果预期能找到的证据。常见来源:
该命名空间和时间范围内的 K8s 事件:
FROM logs-k8seventsreceiver.otel-*
| WHERE attributes.k8s.namespace.name == "<ns>"
AND @timestamp > NOW() - 2 hours
AND attributes.k8s.event.reason IN (
"BackOff", "Killing", "Unhealthy", "Failed",
"FailedScheduling", "Evicted", "SuccessfulRescale",
"Pulling", "Pulled", "Started", "Created"
)
| SORT @timestamp DESC
| KEEP @timestamp, attributes.k8s.event.reason, body.text, k8s.object.name
| LIMIT 30
在此接收器中,命名空间是日志属性,而不是资源属性。 请筛选 attributes.k8s.namespace.name,而不是
扁平字段 k8s.namespace.name。此数据流中映射了扁平形式,因此使用它的查询可以解析并执行,但会在无报错的情况下返回零行——在一个 Stack 9.4.4 集群上,扁平字段在 832 条事件中有 0 条填充值,而 attributes. 形式在所有事件中都携带命名空间,因此扁平筛选器丢弃了 300 条匹配的 BackOff、Killing
和 Unhealthy 事件。扁平的 k8s.* 路径在 metrics-kubeletstatsreceiver.otel-* 和
metrics-k8sclusterreceiver.otel-* 上确实有效,因为命名空间是资源属性;事件接收器是例外。在相信事件查询结果为空之前,请对比 COUNT(attributes.k8s.namespace.name) 和 COUNT(*)。
应用日志(如果可用)——查看终止时间戳之前最近的 200 行。如果没有日志,请标记
no_logs_available;不要臆造日志模式。
APM(如果 Pod 运行的是经过插桩的服务)——从 Pod 资源属性中解析 service.name,用于后续关联。SLO、延迟和错误率分析本身属于 APM 层工作,不在本技能范围内。
基线比较——对于基于利用率的发现,将当前值与 7 天前同一时段的值进行比较。只有相对于该工作负载的正常水平而言,“内存高”才有意义。
检查上游原因(有条件)
仅在症状模式表明有必要时才追查。阈值:上游错误率超过基线 5 倍,或延迟超过基线 3 倍,并且目标服务的症状出现之前就已开始退化。共同出现的症状不足以证明因果关系。
如果 metrics-service_destination.1m.otel-default 中没有该服务的数据行,请报告
insufficient_dependency_data,而不是“上游运行正常”。
检查近期变更(条件性)
过去 2 小时内的 SuccessfulCreate / Pulled 事件通常与部署相关。logs-k8sobjectsreceiver.otel-*
会显示 configmap/secret/deployment 规范的变更。症状出现前 15 分钟内发生的变更具有很强的相关性,
但仍然只是相关性——请确认它确实可以合理解释你已判定的模式。
综合并停止
一旦证据足以支撑一个具有已知置信度的假设,就立即进行综合。无需完成所有前置章节——出现以下任一情况时,调查即告终止:
- 你得出了有佐证的高置信度假设;或
- 你得出了低/中置信度假设,且进一步查询不太可能改变判断(例如,日志不可用、未检测 APM,或未发现近期变更)。
综合
默认结构:
HYPOTHESIS (confidence: high | medium | low)
<One paragraph: service, symptom, most likely cause. Name the failure mode from the taxonomy.>
EVIDENCE
- <Finding from characterization, with the concrete metric or value.>
- <Finding from events / logs / APM.>
- <Finding from baseline comparison, dependency check, or change correlation if pursued.>
CONFIDENCE NOTE
<Only if not 'high'. What specific evidence is missing or ambiguous.>
RECOMMENDED NEXT STEPS
1. <Most actionable — typically a config check or metric to observe.>
2. <Secondary.>
DOWNSTREAM IMPACT
<Services depending on this workload, or 'No downstream dependencies identified.'>
篇幅。 整个综合部分为 250–400 个词。HYPOTHESIS 用两到三句话。EVIDENCE 包含三到五条单行项目,每条引用一个具体值,而不是重新解释该值。RECOMMENDED NEXT STEPS 包含两到三条单行项目——只列出你实际会优先采取的措施,而不是所有可能的措施。DOWNSTREAM IMPACT 用一到两句话说明。证据充分的告警可以轻松控制在这个篇幅内;篇幅长不代表全面,而且值班人员在事件处理中快速浏览时不会看到首屏以外的内容。
当有两个假设尚未排除时: 将 HYPOTHESIS 替换为 COMPETING HYPOTHESES;列出两种假设,说明你更倾向哪一种及其原因,并列出可用于区分它们的证据。
未发现事故时(症状已解决,或告警似乎是误报):请直接说明。
ALERT FIRED BUT SYSTEM APPEARS HEALTHY 是有效的输出。列出你检查过的内容,以及未发现的情况。
置信度校准
从 高 开始,根据缺失信息降低置信度:
- 降至 中:主要信号明确,但缺少佐证(没有日志、没有 APM、无法与基线比较)。或者:两种模式都符合,但你无法区分。
- 降至 低:只有单一信号支持该假设、信号相互矛盾,或该模式需要你无法获取的证据。
如果没有应用程序日志数据,而假设又依赖于应用程序行为,绝不要给出 高 置信度。缺少证据不能佐证假设。
查询配方
针对重启次数最多的 Pod、CPU 限流、节点内存压力、准入拒绝和告警触发路径的现成查询,位于 references/query-recipes.md。
示例
“为什么我的 Pod 一直处于 CrashLoopBackOff 状态?”
先进行特征分析:获取重启次数、终止原因、内存和 CPU 利用率。
- 如果
last_terminated_reason == "OOMKilled"且内存利用率达到 1.0 → 内存路径。通过 7 天基线进行佐证:数日内持续上升 = 泄漏;波动剧烈 = 负载驱动。如果已知语言,检查 GC 指标。 - 如果
last_terminated_reason == "Error"且cpu_limit_utilization > 1.0→ CPU 限流路径。通过存活探针配置(initialDelaySeconds、timeoutSeconds)和 K8sUnhealthy事件进行佐证。 - 如果
last_terminated_reason == "Error"且 CPU 正常 → 应用逻辑路径。提取终止前的近期日志。 - 如果
last_terminated_reason == "ContainerCannotRun"→ 镜像/执行路径。检查 K8s 事件中是否有Failed拉取事件。
以适当的置信度进行综合。如果在 Error 路径中无法获取日志,请将置信度降至中,并说明原因。
“我的发布卡住了吗?”
权威信号:k8s.deployment.available < k8s.deployment.desired 持续超过 10 分钟。
诊断约束因素:
- 检查新 ReplicaSet 上的 K8s 事件:
FailedCreate→ 准入拒绝(配额、Webhook、PSP)。FailedScheduling→ 没有 合适的节点。 - 检查新 Pod 的利用率:内存利用率全部为 0% → 从未启动(镜像拉取失败);CPU 利用率高而内存利用率低 → 启动缓慢, 触发就绪探针。
- 检查 HPA 状态:负载下
current_replicas < desired_replicas且数值稳定 → 未就绪 Pod 抑制扩容。
“告警已触发,但一切看起来都正常”
这种情况可能发生,值得明确说明。请检查:
- 症状是否已消失?将当前利用率/重启率与告警触发点进行比较。
- 告警是否由瞬时峰值触发,且峰值已回落?
- 告警配置是否合适(例如,评估窗口过短)?
输出:ALERT FIRED BUT SYSTEM APPEARS HEALTHY,并说明已检查的内容。如果这种情况反复出现,建议调整告警配置。
相关内容
- 工作流:
K8s CrashLoopBackOff Investigation——由告警触发的自动化版本,执行上述 Pod 级排查流程。 运行确定性 ESQL 并进行分支处理;此技能提供工作流所缺少的解读层。 - **Forge 基因库:**16 种 K8s 故障场景(OOMKill 级联、CPU 限流、探针配置错误、节点 NotReady、 准入 Webhook 阻止等),用于验证此技能的覆盖范围。
运维
| HTTP API(简写) | elastic CLI 命令 |
|---|---|
GET / | elastic es info |
POST /_query | elastic es esql query --format tsv --query '<esql>' |
GET /<index>/_mapping | elastic es indices get-mapping --index '<index>' |
GET /_field_caps | elastic es field-caps --index '<index>' --fields '<fields>' |
GET kbn:/api/alerting/rules/_find | elastic kb alerting get-alerting-rules-find --filter '<filter>' |