gke-workload-troubleshooting について
構造化された読み取り専用の調査を通じて、GKE のアプリケーションワークロード障害を診断するスキルです。クラスタ情報、Pod の状態、イベント、ログを分析し、Kubernetes マニフェストや Google Cloud の変更を提案しますが、稼働環境への変更は実行しません。
主な機能
- 01読み取り専用の GKE 診断
- 02Pod の状態とイベントの確認
- 03アプリケーションおよびクラスタログの分析
- 04マニフェストとクラウド修正の提案
- 05クラスタに接続できない場合の代替コマンド
活用シーン
- 01Pending 状態や再起動を繰り返す Pod の調査
- 02イメージ取得やボリュームマウント障害の診断
- 03OOM やアプリケーションクラッシュの症状分析
MCP 対応
MCP コマンドの実行前に Usage Attribution を読む必要があります。稼働環境への変更は実行せず、担当者が確認できる修正案を提示します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ google/ skills --skill gke-workload-troubleshootingインストール後、Claude Code で/gke-workload-troubleshootingを入力して読み込まれたか確認します。
同梱ファイルreferences/attribution.md
詳細
GKE ワークロードのトラブルシューティングスキル
このスキルを使用して、GKE クラスターにデプロイされたアプリケーションワークロードの障害を体系的に診断し、解決します。このスキルは非対話型で動作し、読み取り専用の診断境界を適用します。修正案(Kubernetes マニフェストや設定のパッチ、または Google Cloud の変更(例:gcloud IAM バインディングやノードプールの再作成))を提案するだけで、実環境への変更を自ら実行することはありません。
帰属情報
必ずお読みください:gcloud、REST API、または MCP コマンドを実行する前に、使用状況の帰属情報を必ずお読みください。ターミナルで直接実行するコマンド(生成されたスクリプト、Terraform、ユーザーコード内では決して実行しない)では、gcloud の前に
CLOUDSDK_METRICS_REQUEST_ATTRIBUTION="agent-skills/1.0 (skill:gke-workload-troubleshooting)"
を付け、User-Agent ヘッダーに agent-skills/1.0 (skill:gke-workload-troubleshooting) を追加してください。
🔍 診断ワークフロー
ステップ 0:非対話型のコンテキスト検出と時間枠の定義
-
パラメーターの抽出:ユーザーのプロンプト、アクティブな
SETTINGS.md、またはアクティブな環境のデフォルト設定から、必要なコンテキスト(project_id、cluster_name、cluster_location、workload_name、workload_namespace)を非対話型で抽出します。- 省略されている場合、
workload_namespaceはdefaultに設定します。 - 不足しているクラスターのパラメーターは、アクティブな環境(
kubectl config current-contextまたはgcloud config get-value project)から推測します。 - 自律的な実行フローを確保するため、プロンプトと環境のデフォルト設定から非対話型でコンテキストを検出することを優先します。
- 省略されている場合、
-
クラスター認証情報とフォールバックモード:
- 認証情報の取得を試みます:
gcloud container clusters get-credentials {cluster_name} --region/--zone {cluster_location} - フォールバック/ドライランモード:クラスターに到達できない、存在しない、またはライブコマンドの実行に失敗した場合(サンドボックスでの評価、ドライランモード、オフライン分析など):
- 到達不能なクラスターのシナリオでリソースの枯渇やコンテキストの過剰な消費を避けるため、再試行回数を制限します。
- 人間のオペレーターが実行するための
kubectl診断コマンドを、正確な順序ですぐに提示します。 - 報告された症状に基づいて根本原因の分析をまとめ、提案する GitOps マニフェストの修正を出力します。
- 認証情報の取得を試みます:
-
時間の処理とフォールバック:
- 問題の発生時刻({issue_time})を決定する:
- 具体的な時刻が指定されている場合:ユーザーが指定したタイムスタンプを
{issue_time}として使用します。 - 相対的な時刻が指定されている場合(例:「5 分前」):現在のシステム時刻に基づいて対応する UTC タイムスタンプを動的に計算し、
{issue_time}として使用します。 - 時刻が指定されていない場合(デフォルト):現在のシステム時刻を
{issue_time}として使用します。
- 具体的な時刻が指定されている場合:ユーザーが指定したタイムスタンプを
- 時間枠の計算:
{issue_time}を中心とする 1 時間のクエリ時間枠を設定します(start_time={issue_time} - 30m、end_time={issue_time} + 30m)。
- 問題の発生時刻({issue_time})を決定する:
ステップ 1:Pod のステータスと状態を分析する
ワークロードのアクティブな Pod の状態とコントローラーのステータスを調査します。
診断コマンド:
# 1. Inspect the deployment's actual selector labels:
kubectl get deployment {workload_name} -n {workload_namespace} -o jsonpath='{.spec.selector.matchLabels}'
# 2. Query the pods using the returned labels, for example:
kubectl get pods -l {selector_labels} -n {workload_namespace}
kubectl get deploy/{workload_name} -n {workload_namespace} -o yaml
診断の判断ツリー:
-
フェーズ:Pending:
- Pod はどのノードにもスケジュールできません。**ステップ 2(Namespace のイベントを確認)**に進んでください。
-
状態:CrashLoopBackOff / Error:
- コンテナは起動しますが、繰り返し終了します。
kubeletは最大 5 分までバックオフ時間を延ばしながら再起動します。まず、終了時の理由と終了コードを確認してください。
kubectl describe pod {pod_name} -n {workload_namespace} kubectl get pod {pod_name} -n {workload_namespace} -o jsonpath='{.status.containerStatuses[*].lastState.terminated}'- 理由:OOMKilled(終了コード 137):コンテナのメモリ上限に達しています。コンテナレベルかノードレベルかを分類するため、ステップ 3(ログを調査)→ OOM 分析に進み、その後、修正案を提示するためにステップ 5に進んでください。
- 終了コード 0(正常終了):長時間実行する Deployment/StatefulSet では想定外です。
restartPolicy: Alwaysによって終了したプロセスが再起動され、ループが発生します。一般的な原因として、command/entrypointが永続的に実行されるプロセスを起動していない、キューが空のときにワーカーが終了する、または設定がない/無効である(例:アタッチされていない、またはキーが誤っているConfigMapボリューム)ため、アプリが正常終了することが挙げられます。**ステップ 3(ログを調査)**に進んでください。 - 終了コード 128:
command/entrypointが無効です。実行ファイルのパスが間違っているか、イメージ内に存在しません。マニフェスト内のコンテナコマンドを確認してください。 - 終了コード 1 またはその他のゼロ以外の値:アプリケーションがクラッシュしています。設定エラー、環境変数や設定ファイルが存在しない/無効、依存先に到達できない、または Google Cloud 呼び出しでの認証エラー(
401/403。Pod の IAM/Workload Identity Federation を確認してください)などが原因です。直接、**ステップ 3(ログを調査)**に進んでください。 - 終了コードが正常に見えるのにコンテナが再起動を繰り返す場合は、liveness probe の失敗を疑ってください(ステップ 3 を参照)。
- コンテナは起動しますが、繰り返し終了します。
-
状態:ImagePullBackOff / ErrImagePull:
- kubelet がコンテナイメージを取得できません。
ImagePullBackOffはバックオフしながら再試行していることを意味し、ErrImagePullは一般的な回復不能の取得エラーです。関連する状態には、InvalidImageName、RegistryUnavailable、SignatureValidationFailed、ImageInspectErrorがあります。正確な取得エラーメッセージを確認するため、**ステップ 2(Namespace のイベントを確認)**に進んでください。
- kubelet がコンテナイメージを取得できません。
-
状態:ContainerCreating:
- コンテナがボリュームのマウント、ネットワークの設定、またはイメージの取得中にブロックされています。直接、**ステップ 2(Namespace のイベントを確認)**に進んでください。
ステップ 2:Namespace のイベントを確認する
GKE でインフラストラクチャ、ボリューム、イメージ、またはスケジューリングに関する警告を確認します。
診断コマンド:
kubectl get events -n {workload_namespace} --sort-by='.metadata.creationTimestamp'
# Or query Cloud Logging for historical GKE events within the time window:
gcloud logging read "resource.type=\"k8s_cluster\" AND logName=\"projects/{project_id}/logs/events\" AND jsonPayload.involvedObject.namespace=\"{workload_namespace}\"" --start-time="{start_time}" --end-time="{end_time}" --project="{project_id}"
# Or query specifically for image pull failures within the time window:
gcloud logging read 'log_id("events") AND resource.type="k8s_pod" AND resource.labels.cluster_name="{cluster_name}" AND jsonPayload.message=~"Failed to pull image"' --project="{project_id}"
注:イベント一覧を取得して、イベントのタイムスタンプ(CreationTimestamp/LastSeen)を手動で確認し、{start_time} と {end_time} の期間内に発生した障害を特定してください。
特徴的な識別子:
FailedScheduling:ノードのリソース不足です。0/3 nodes are available: 3 Insufficient memory.のようなメッセージや、ノードのアフィニティの許容条件の不足(例:Spot VM のテイント)を確認してください。FailedMount:- PersistentVolumeClaim(
PVC)がありません。 - Secret がありません(
Secret "{secret_name}" not found)。 - ConfigMap がありません(
ConfigMap "{configmap_name}" not found)。
- PersistentVolumeClaim(
Failed/BackOff(イメージの取得):まず正確なイベントメッセージ(Failed to pull image "IMAGE": ...)を確認し、実際に記載されている内容に基づいて切り分けてください。メッセージが実際に権限または認証エラーを示している場合を除き、IAM/ノードのサービスアカウントの調査にすぐ移らないでください。-
誤ったイメージ名/タグ — まずここを確認してください(
not found、manifest unknown、InvalidImageName):最もよくある原因です。タグまたはパスが間違っているか、 イメージが削除されています。最近のデプロイ変更によって発生した可能性もあります。- 失敗しているコンテナイメージ名と無効なタグを特定します。
- 最後に正常に動作したイメージタグを Git の履歴で確認します:
git log -p -S "{image_name}" -- {manifest_file_path}(またはマニフェストを含む フォルダーに対してgit logを実行します)。 - マニフェストのパッチで、イメージタグを最後に正常に動作したバージョンに戻す(または タグを修正する)ことを提案します。
-
権限/認証エラーのみ(メッセージに
403 Forbidden/denied、または401 Unauthorized/unauthorizedが含まれる場合):ノードがレジストリへの認可または認証を行えません。以下の確認は、メッセージが一致する場合にのみ実施してください。-
403 Forbidden(認可) — ノードプールのサービスアカウント (または imagePullSecret のサービスアカウント)にレジストリの読み取り権限がありません。以下のコマンドをユーザーが確認して実行できるよう提示し、権限の付与を提案してください。実行してはいけません。Artifact Registry の場合:gcloud artifacts repositories add-iam-policy-binding {repository} \ --location={repo_location} \ --member="serviceAccount:{node_service_account_email}" \ --role="roles/artifactregistry.reader"Container Registry(
gcr.io) の場合は、バックアップバケットに対してroles/storage.objectViewerを付与します(または、gcr.ioが移行済みの場合は Artifact Registry のロールを付与します)。また、VPC Service Controls の境界で Artifact Registry が許可されていることも確認します。 -
401 Unauthorized(認証) — ノードのサービスアカウントが無効になっているか、ノードに必要な OAuth スコープがありません:gcloud container clusters describe {cluster_name} --location={cluster_location} \ --format="table(nodePools.name,nodePools.config.serviceAccount)" gcloud iam service-accounts list \ --filter="email:{node_service_account_email} AND disabled:true" --project={project_id} gcloud compute instances describe {node_name} --zone={node_zone} \ --format="flattened(serviceAccounts[].scopes)"スコープには
devstorage.read_onlyまたはcloud-platform(gke-defaultによって提供)が含まれている必要があります。ノードは変更できないため、スコープが不足している場合は--scopes="gke-default"を指定してノードプールを再作成することを提案してください。ユーザーが実行するコマンド案として提示し、実行してはいけません。 -
プライベート/セルフホストのレジストリ:有効な
imagePullSecretが存在し、Deployment から参照されていることを確認します。
-
-
その他のステータス:
RegistryUnavailable/i/o timeout/DNS のserver misbehaving→ レジストリへのネットワーク経路(DNS、ファイアウォールの送信トラフィック、Google API への接続性)。exec format errorまたは非推奨の schema-1 イメージ → アーキテクチャ/スキーマの不一致。
-
ステップ 3:アプリケーションログを調査する
アプリケーションの実行時ログから例外とスタックトレースを抽出します。
診断コマンド:
# Check current active log stream (handles multi-container pods)
kubectl logs {pod_name} -n {workload_namespace} --all-containers --tail=100
# Check logs from previously terminated container instances (handles multi-container pods)
kubectl logs {pod_name} -n {workload_namespace} --all-containers -p --tail=100
シグネチャの識別:
-
メモリ不足(OOM)の分析:まず、プロセスの強制終了を確認し、分類します。
-
コンテナレベルの OOM(最も一般的):
kubectl describe podにLast State: Terminated、Reason: OOMKilled、Exit Code: 137と表示されます。 コンテナが cgroup のメモリ上限を超えています。 アプリケーションのメモリリーク/ループ(ログや起動 コマンドで無制限の増加が見られる)と、インフラストラクチャの容量の不一致 (正当な需要がresources.limits.memoryを超えている)を区別します。 -
ノードレベル(システム)の OOM:ノード全体でメモリが不足しています。退避された Pod と ノードプレッシャーによる退避を確認します。すべての Pod のメモリ使用量の合計が ノードの容量を超えています。
-
「不可視」の OOM(
cgroup v1):子プロセスが強制終了されても、 メインプロセス(PID 1)は実行を続けるため、Kubernetes はOOMKilledとして記録しません。Cloud Logging のノードログを検索します。gcloud logging read 'resource.type="k8s_node" AND resource.labels.cluster_name="{cluster_name}" AND jsonPayload.MESSAGE:("TaskOOM event" OR "ContainerDied")' --project="{project_id}"TaskOOMエントリで OOM による強制終了を確認できます。コンテナ ID をContainerDiedエントリと照合して、影響を受けた Pod を特定します。ノード上では、journalctl -kにより、コンテナレベルの強制終了(memory cgroup、memcg)と システムレベルの強制終了(Out of memory: Killed process)を区別できます。 -
サンプリングされたメモリメトリクスだけに頼らないでください。強制終了の引き金となる 急増を見逃すことがよくあります。その後、ステップ 5 に進み、修正案を提示します (上限の引き上げ、リークの修正、ノードプールの適正化)。
-
-
Liveness Probe の失敗(アプリケーションエラーがない CrashLoop):コンテナが再起動する一方で、 ログにクラッシュが記録されていない場合、
kubeletが Liveness Probe の失敗を理由に 強制終了している可能性があります(デフォルトのfailureThreshold: 3)。Cloud Logging で確認します。gcloud logging read 'resource.type="k8s_node" AND log_id("kubelet") AND jsonPayload.MESSAGE:"failed liveness probe, will be restarted" AND resource.labels.cluster_name="{cluster_name}"' --project="{project_id}"一般的な修正方法:プローブの種類/パス/ポートを修正する、起動に時間がかかる場合は
initialDelaySecondsまたはtimeoutSeconds/failureThresholdを大きくする、 あるいはプローブのタイムアウトを引き起こす CPU/ディスク I/O の競合を軽減します。プローブのコマンドは軽量に保ってください。 -
スタックトレース/未処理の例外:言語固有のスタックトレース(例:
panic:、NullPointerException、Traceback (most recent call))を探します。これはアプリケーションのバグを示しています。 -
外向きネットワークのタイムアウト:接続のタイムアウト(例:
Connection timed out、dial tcp: i/o timeout)を探します。ステップ 4(接続の確認) に進みます。 -
権限エラー(ReadOnlyRootFilesystem):書き込みエラー(例:
/tmpまたは/var/logへの書き込み時のRead-only file system、Permission denied)を探します。 マニフェストで、そのディレクトリにemptyDirボリュームマウントを追加するよう提案します。
ステップ 4:サービスの接続とネットワークポリシーを確認する
他のサービスへの接続が切断される問題をトラブルシューティングします。
診断コマンド:
# Verify target endpoint is active
kubectl get endpoints {target_service_name} -n {target_namespace}
# Query network policies inside namespace
kubectl get networkpolicies -n {workload_namespace} -o yaml
ロジックとドライラン時のフォールバック:
-
稼働中のクラスターのモード:
kubectl get endpointsが空のリストを返す場合、対象のマイクロサービス自体がスケジュールまたは起動に失敗しています(対象サービスをトラブルシューティングしてください)。- エンドポイントが存在するものの、ログにタイムアウトが表示されている場合は、
NetworkPolicyの送信ルールによるブロックを分析し、対象サービスの IP/ポートへの送信トラフィックが許可されているか確認してください。
-
サンドボックス/ドライランモード:
- 稼働中の
kubectlクエリが失敗する場合、またはクラスターに接続できない場合は、稼働中のクラスターへのアクセスを再試行したり、接続を繰り返し試みたりしないでください。 - 直ちにアプリケーションのソースコード(例:
worker.py、app.go、DB 接続文字列)または Deployment マニフェストを確認し、対象サービスのホスト名(例:account-db)と宛先ポート(例:5432)を特定してください。 - ユーザー向けに正確な
kubectl get endpointsコマンドとkubectl get networkpoliciesコマンドを提示し、対象サービスとポートへのトラフィックを許可するために必要なNetworkPolicyの送信ルールのパッチを作成してください。
- 稼働中の
ステップ 5:GitOps による修正案の提示
GitOps の境界を守り、変更を直接適用しないでください。これには、クラスターのマニフェスト/設定のパッチと、Google Cloud の変更(例:gcloud の IAM バインディングやノードプールの再作成)の両方が含まれます。すべての変更は、レビュー可能な提案として提示してください。たとえば、マニフェストのパッチ/PR、またはユーザーが実行するコマンドとして提示します。
- 人間のオペレーター向けに根本原因の分析をまとめます(例: 「payment-api は、実際の使用量が 270Mi に達した一方でメモリ制限が 256Mi に設定されているため、終了コード 137 で失敗しています」)。
- 修正済みの YAML マニフェストのパッチを生成します(例:メモリ制限の引き上げ、不足している Secret マウントの追加、Spot ノード向けの toleration の追加)。
- このワークロード/障害に関するブランチまたは Pull Request(PR)がすでに存在するか確認します。存在する場合は、既存のブランチ/PRを更新するか、ユーザーに通知し、重複を作成しないでください。存在しない場合は、ブランチを作成して変更をコミットし、GitHub で Pull Request(PR)を作成したうえで、ワークフローを終了します(担当者によるマージを待たないでください)。