observability-k8s-investigation について
observability-k8s-investigation は、テレメトリを使って Kubernetes の問題を調査するスキルです。OTel のメトリクス、イベント、ログ、APM を関連付け、ワークロード、ノード、コントロールプレーンにまたがる原因を分析します。
主な機能
- 01複数レイヤーの Kubernetes テレメトリを関連付け
- 02コンテナのリソース指標と再起動を分析
- 03Kubernetes イベントとオブジェクト状態を確認
- 04アプリケーションログと APM データを組み合わせ
- 05証拠に基づいて原因と不確実性を評価
活用シーン
- 01Pod の再起動や OOMKilled の反復を調査
- 02CPU スロットリングによるプローブタイムアウトを診断
- 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 の問題を診断します。クラスターの状態、ポッドのランタイムメトリクス、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 コマンドが記載されています。HTTP API を直接呼び出さず、必ず CLI を使用してください。
クラスターにアクセスせずに分析する
上記の 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 と報告してください。
同時に起きる症状は、原因とは限りません。 2つのサービスが同時に劣化した場合、通常は因果関係ではなく、共通の上流が原因です。(a)一方のサービスの劣化が明らかにもう一方より先に発生し、かつ(b)差が大きい(エラー率が5倍超、レイテンシが3倍超)場合にのみ、因果関係を特定してください。
OOMKilled が発生しても、デフォルトでメモリリークとは限りません。 ワークロードのワーキングセットに対して、単に上限が小さすぎる可能性があります。メモリ曲線の形状で見分けてください。上限まで単調に増加し、再起動のたびにリセットされ、負荷が前週と比べて横ばいで、最近のデプロイもない場合は、リークの兆候です。確信度を高くして断定してください。形状が曖昧な場合(スパイク状、日周期、負荷との相関がある場合)に、同じ時刻の7日間ベースラインを参照してください。OOMKilled の調査すべてにおける前提条件ではありません。
エラー終了が発生しても、デフォルトでアプリケーションのバグとは限りません。 まず k8s.container.cpu_limit_utilization を確認してください。CFS スロットリングによってライブネスプローブがタイムアウトするケースが、この領域で最もよくある誤診です。
CPU 使用率の平均値では、スロットリングが隠れます。 平均 cpu_limit_utilization が40~60%で健全に見える Pod でも、p99 では深刻なスロットリングを受けていることがあります。Linux は100ms単位で CPU 制限を適用するため、バースト性のあるワークロードは期間の途中でクォータに達し、停止します。平均値だけでなく、最大値と p95 を確認してください。
再起動回数はカウンターではなくブール値として扱います。 k8s.container.restarts は K8s API から直接取得され、kubelet によっていつでも削除される可能性があるため、絶対値は信頼できません。== 0(最近の再起動なし)と > 0(最近再起動あり)として扱ってください。これを基に、バックオフのタイミングや「線形か指数関数的か」といったパターンを導き出してはいけません。代わりに、K8s の Killing/BackOff イベントで再起動のパターンを確認してください。
確信を作り上げるより、不確実性を報告してください。 証拠が曖昧な場合は、その旨を統合結果に記載してください。複数の仮説を並べることも、有効な出力です。
同様に、不確実性を作り上げないでください。 上記のルールは、証拠が曖昧な場合についてのものであり、表現の調子についてではありません。決定的なシグナルがあり、それを裏付ける証拠もそろっている場合は、確信度を高くして断定してください。明確な所見を「中程度」に弱めるのは、過剰な断定と同じく欠陥です。
統合結果を提示したら、そこで終えてください。 確信度を示すのは HYPOTHESIS 行の1回だけにし、箇条書きごとに繰り返さないでください。クエリの実行内容は、結果によって結論が変わった場合を除き、説明しないでください。また、アラートをそのまま読み返さないでください。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 | すべての k8s | Pod 名 |
k8s.namespace.name | メトリクスのみ | Namespace。マッピングされていますが、k8seventsreceiver では null です |
attributes.k8s.namespace.name | k8seventsreceiver | イベントの Namespace — ここではこの形式でフィルターします |
k8s.container.name | すべての k8s | Pod 内のコンテナー |
k8s.deployment.name | k8sclusterreceiver + その他 | 親デプロイ |
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 の前に一時的に超えることがあります) |
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 の名前(ログ属性。フラット形式を使用) |
コンテナレベルとポッドレベルの制限値使用率
制限値使用率はコンテナレベルで読み取ってください。k8s.container.cpu_limit_utilization と
k8s.container.memory_limit_utilization がデフォルトです。ポッドレベルのペアは別の測定値であり、同義ではありません。
レシーバーは同じデータストリーム内の別々のドキュメントに2つの系列を出力します。ポッドレベルのフィールドは
k8s.container.name を含まないドキュメントに現れ、コンテナレベルのフィールドはそれを含むドキュメントにのみ現れます。そのため、
STATS ... BY k8s.container.name ではすべてのポッドレベルフィールドが null になり、逆の場合も同様です。
稼働中の9.6.0クラスタで1時間にわたって測定したところ、コンテナ名のない42,240件のドキュメントのうち、360件に
k8s.pod.cpu_limit_utilization が含まれ、コンテナフィールドを含むものはありませんでした。コンテナ名のある18,240件のドキュメントのうち、
900件にコンテナフィールドが含まれ、ポッドフィールドを含むものはありませんでした。
利用可能性にも違いがあります。コンテナレベルの使用率は、制限値を宣言した各コンテナについて出力されますが、
ポッドレベルの集計値には、ポッド内のすべてのコンテナが制限値を宣言している必要があります。稼働中の2つのクラスタで3時間にわたって確認したところ、
ポッドレベルフィールドがあり、コンテナレベルフィールドがないポッドはありませんでした。一方、コンテナレベルフィールドがあり、ポッドレベルフィールドがないポッドは27件ありました。
そのすべてが、制限値を宣言したコンテナが一部に限られる複数コンテナのポッドでした。サイドカーが注入されたポッドでポッドレベルのスロットリングを確認すると、暗黙のうちに null が返されます。
| クラスタ | コンテナレベルのみ | 両レベル | どちらもなし | ポッドレベルのみ |
|---|---|---|---|---|
| forge-factory | 18 | 7 | 44 | 0 |
| k8s-demo | 9 | 6 | 40 | 0 |
ポッドレベルのフィールドを使うのは、ポッド全体としての実際の問い、つまりコンテナの制限値の合計に対する総消費量について調べる場合に限り、 値が入力されていることを確認した後にしてください。observability-sre-triage も同じルールを適用するため、同じポッドに対して2つのスキルは同じ回答を返します。
フィールドの利用可能性
上記のフィールドのうちいくつかは、標準の 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 をベースラインと比較して推移を確認します。どちらの代替手段もポッドレベルのドキュメントにあるため、k8s.container.name ではなく k8s.pod.name でグループ化します。 |
k8s.pod.cpu_limit_utilization / memory_limit_utilization | ポッド内のすべてのコンテナで制限の宣言が必要なため、複数のコンテナを含むポッドではほとんどの場合に欠落します。 | コンテナレベルのフィールドを使用します。こちらのほうが利用できるケースが確実に多くなります。 |
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() は、異なる値が1つの場合はスカラー、複数の場合は配列を返します。 配列を前提とするテンプレート(例:| first)では、値がスカラーの場合、文字列の先頭文字が抽出されます。MV_FIRST(VALUES(...)) を使うか、両方の形式に対応してください。
VALUES() は、このスキルの最低対応バージョンより新しい機能です。 Serverless では GA ですが、Stack では 8.14.0 からプレビューで、GA になるのは 9.4.0 です。また、8.14 未満では利用できません。使用前に GET / を確認してください。build_flavor: "serverless" であれば利用できます。それ以外の場合は version.number を確認してください。利用できない環境では、フィールドを集約する代わりに BY 句に移動してください。異なる値ごとに1行が返されるため、同じ情報を得られます。
| 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) は sum を value_count で割ります)。真のパーセンタイルが必要な場合は、Kibana Query DSL にフォールバックしてください。
COUNT(agg_metric_double) はドキュメント数ではなく value_count(イベント数)を返します。 SUM(field) は sum 成分を、AVG(field) は sum/value_count を返します。イベント数の代用として SUM(transaction.duration.summary) を使わないでください。合計継続時間が返されます。
K8s メトリクスでは、ES|QL のフィールドパスはフラットな OTel 形式です。 resource.attributes.k8s.pod.name ではなく、k8s.pod.name を使用してください。ネスト形式は生のログドキュメント用です。
障害モードの分類
各モードの主要シグナルと、ワークロード、ノード、コントロールプレーン、オートスケーリング、ネットワーキングの各レイヤーで確認する補助的なチェック項目、および2つのモードが当てはまる場合の対処方法をまとめた分類基準は、references/failure-modes.md にあります。分類する前に読んでください。
シグナルの解釈
メモリ
- 30〜60分にわたる単調な増加 → メモリリークです。言語ごとの 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 はクォータによって制限されています。- スロットリングの指標そのものではなく、その症状:liveness probe のタイムアウト、p99 レイテンシが p50 の4〜16倍、上流でのキューのバックプレッシャー、Reason が Error のコンテナ終了。
- 平均値が正常に見えても、p95 ではスロットリングが発生していることがあります。平均値だけを信頼しないでください。
再起動パターン
- 最近
restarts > 0→ ワークロードが再起動しています。回数の大きさから状態を判断しないでください(_再起動回数はブール値_を参照)。logs-k8seventsreceiver.otel-*にある K8s のKilling/BackOffイベントのタイムスタンプからパターンを確認してください。 - 再起動がメモリ圧迫(
memory_limit_utilization → 1.0)と相関 → OOMKilled の経路です。 - メモリ/CPU の圧迫がないのに再起動 → probe の設定ミス、アプリのバグ、または起動時の依存関係の障害です。
UnhealthyとKillingのイベントを取得してください。
終了理由
OOMKilled→ メモリに関する問題です。Error→ 0以外の終了コードです。アプリログを確認してください。ログが空、またはごくわずかであれば、アプリのロジックが原因だと判断する前に CPU スロットリングを確認してください。Completed→ 正常に完了しています。Jobs/CronJobs/init コンテナでは正常ですが、それ以外では異常です。ContainerCannotRun→ ランタイム/イメージ/実行に関する問題です。イメージのプルイベントを確認してください。
調査の流れ
調査はチェックリストではありません。以下のセクションでは、典型的な 一連の流れを説明しています。調査で判明した内容に応じて、圧縮したり、省略したり、再確認したりしてください。 既知の確度で結論をまとめるのに十分な証拠が得られたら、ただちに終了してください。効果が薄れた後もシグナルを追い続けるのは、徹底的な調査ではなく、失敗パターンです。
状況を把握する
対象を特定します。k8s.pod.name、k8s.namespace.name、必要に応じて k8s.deployment.name と service.name を解決します。時間範囲が指定されていない場合、Pod レベルの調査では過去 1 時間、イベントの相関では過去 2 時間、継続中または未解決のインシデントでは過去 6 時間を既定値とします。
アラートのペイロードから障害モードがすでに分かる場合(たとえば、OOMKilled でのみ発火する場合)は、それを記録して分類を省略し、確認とベースラインとの比較に進みます。
特徴を把握する
ワークロードの最近の挙動の概要を確認します。再起動回数、終了理由、フェーズ、使用率を調べます。通常は 1、2 回のクエリで十分です。
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
コンテナーごとにグループ化します。サイドカーが挿入された Pod には複数のコンテナーがあり、使用率を報告するのは上限が宣言されているコンテナーのみです。すべての列が null の場合、上限は宣言されていません。null をアイドル状態と解釈せず、フィールドの利用可否に記載の方法に切り替えてください。
分類する
references/failure-modes.md の分類体系を使用します。決め手となるシグナルが一致している必要があります。「調査」列には、裏付けとして何を確認すべきかが示されています。
複数のモードが当てはまる場合は、両方を記録し、決め手となるシグナルがより強い方から調査を進めてください。裏付けを確認する過程で、分類を見直しても構いません。
裏付けを確認する
分類から予測される証拠を収集します。一般的な情報源は次のとおりです。
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
名前空間はこのレシーバーではログ属性であり、リソース属性ではありません。 フラットな k8s.namespace.name ではなく、attributes.k8s.namespace.name でフィルタリングしてください。このデータストリームではフラット形式がマッピングされているため、フラット形式を使ったクエリは解析・実行され、エラーなく 0 行を返します。Stack 9.4.4 クラスターでは、832 件のイベントのうちフラットフィールドが値を持っていたのは 0 件でしたが、attributes. 形式では全件に名前空間が含まれていました。そのため、フラット形式のフィルターは BackOff、Killing、Unhealthy に一致する 300 件のイベントを除外しました。名前空間がリソース属性である metrics-kubeletstatsreceiver.otel-* と metrics-k8sclusterreceiver.otel-* では、フラットな k8s.* パスが機能します。イベントレシーバーは例外です。イベントの結果が空の場合は、信用する前に 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 は 2~3 文にします。EVIDENCE は 3~5 個の 1 行の箇条書きとし、再説明ではなく具体的な値をそれぞれ示します。RECOMMENDED NEXT STEPS は 1 行の項目を 2~3 個にします。考えられるすべてではなく、実際に最初に行う項目を挙げてください。DOWNSTREAM IMPACT は 1~2 文にします。十分な証拠のあるアラートなら、この分量に無理なく収まります。長さは徹底性を意味しません。インシデント対応中に画面をざっと確認する担当者は、最初の画面より先まで読まないでしょう。
2 つの仮説が有力な場合: HYPOTHESIS を COMPETING HYPOTHESES に置き換えます。両方を挙げ、どちらが有力だと考えるか、その理由と、判別に役立つ証拠を示してください。
インシデントが見つからない場合(症状が解消している、またはアラートが誤検知と思われる場合):そのことを明確に述べてください。
ALERT FIRED BUT SYSTEM APPEARS HEALTHY は有効な出力です。確認したことと、見つからなかったことを列挙してください。
確信度の調整
まず高から始め、不足している情報に応じて下げてください。
- 次の場合は中に下げます:主要なシグナルは明確だが、裏付けがない(ログがない、APM がない、ベースラインと比較できない)。または、2 つのモードが当てはまり、判別できない。
- 次の場合は低に下げます:仮説を裏付けるシグナルが 1 つだけ、シグナルが矛盾している、またはモードの判定に必要な証拠を取得できなかった。
アプリケーションのログデータがなく、仮説がアプリケーションの動作に依存する場合、高と判定してはいけません。証拠がないことは、仮説の裏付けにはなりません。
クエリのレシピ
再起動回数の多い Pod、CPU スロットリング、ノードのメモリー圧迫、アドミッション拒否、発火中のアラートに関する既成のクエリは、references/query-recipes.md にあります。
例
「Pod が CrashLoopBackOff-ing するのはなぜですか?」
まず状況を把握します。再起動回数、終了理由、メモリー使用率、CPU 使用率を取得してください。
last_terminated_reason == "OOMKilled"で、メモリー使用率が 1.0 に達した場合 → メモリーパス。7 日間のベースラインで裏付けます。数日間にわたる単調な増加はメモリーリーク、急激な変動は負荷が原因の可能性があります。言語がわかっている場合は、GC メトリクスを確認してください。last_terminated_reason == "Error"で、cpu_limit_utilization > 1.0の場合 → CPU スロットリングパス。liveness probe の設定(initialDelaySeconds、timeoutSeconds)と、Unhealthyに関する K8s イベントで裏付けます。last_terminated_reason == "Error"で、CPU に問題がない場合 → アプリケーションロジックパス。終了前の最近のログを取得してください。last_terminated_reason == "ContainerCannotRun"の場合 → イメージ/exec パス。Failedのプルイベントがないか、K8s イベントを確認してください。
適切な確信度でまとめてください。Error パスでログを利用できなかった場合は、確信度を中に下げ、その旨を記載してください。
「ロールアウトが止まっていますか?」
信頼できるシグナル:k8s.deployment.available < k8s.deployment.desired が10分を超えて継続しています。
制約要因を診断します。
- 新しい ReplicaSet の K8s イベント:
FailedCreate→ アドミッションによる拒否(クォータ、Webhook、PSP)です。FailedScheduling→ 適合するノードがありません。 - 新しい Pod の使用率:すべてメモリ使用率が0% → 起動していません(イメージのプルに失敗)。メモリ使用率が低く CPU 使用率が高い → 起動が遅く、readiness プローブに引っかかっています。
- HPA の状態:負荷がかかっている状況で
current_replicas < desired_replicasが安定している → 未準備の Pod による抑制です。
「アラートが発火しましたが、すべて正常に見えます」
これは起こり得ることであり、明示的に名前を付けておく価値があります。次の点を確認してください。
- 症状は解消しましたか?現在の使用率/再起動率をアラートの発火条件と比較してください。
- すでに収束した一時的なスパイクによるアラートでしたか?
- アラートは適切に調整されていますか(たとえば、評価ウィンドウが短すぎませんか)?
出力:確認した内容とともに ALERT FIRED BUT SYSTEM APPEARS HEALTHY とします。このパターンが繰り返し発生する場合は、アラートの調整を推奨してください。
関連項目
- ワークフロー:
K8s CrashLoopBackOff Investigation— 上記の Pod レベルの調査手順を、アラートをトリガーとして自動化したものです。 決定論的な ESQL を実行して分岐します。このスキルは、ワークフローに欠けている解釈レイヤーを提供します。 - Forge genome library: このスキルの網羅性を検証する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>' |