关于 gke-workload-troubleshooting
通过结构化的只读排查诊断 GKE 中的应用工作负载故障。它分析集群上下文、Pod 状态、事件和日志,并提出 Kubernetes 清单或 Google Cloud 更改建议,但不会执行线上变更。
主要功能
- 01只读 GKE 诊断
- 02检查 Pod 状态和事件
- 03分析应用及集群日志
- 04提出清单和云端修复建议
- 05集群不可达时提供备用命令
适用场景
- 01排查 Pod 处于 Pending 或反复重启
- 02诊断镜像拉取和卷挂载故障
- 03分析 OOM 或应用崩溃症状
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(查询命名空间事件)。
-
状态:CrashLoopBackOff / Error:
- 容器启动后反复退出;
kubelet会以逐渐延长的退避间隔重启容器,最长可达五分钟。首先查看已终止容器的 原因 和 退出代码:
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(检查日志)。 - 如果退出代码看起来正常,但容器仍不断重启,请怀疑是 存活探针失败(见步骤 3)。
- 容器启动后反复退出;
-
状态:ImagePullBackOff / ErrImagePull:
- kubelet 无法拉取容器镜像。
ImagePullBackOff表示 kubelet 会不断以退避间隔重试;ErrImagePull是一般性的、不可恢复的拉取错误。相关状态包括:InvalidImageName、RegistryUnavailable、SignatureValidationFailed、ImageInspectError。进入 步骤 2(查询命名空间事件),查看确切的拉取错误消息。
- kubelet 无法拉取容器镜像。
-
状态:ContainerCreating:
- 容器在卷挂载、网络设置或镜像拉取期间受阻。直接进入 步骤 2(查询命名空间事件)。
步骤 2:查询命名空间事件
在 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/ DNSserver 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,提出修复方案 (提高限制、修复泄漏,或调整节点池规格)。
-
-
存活探针失败(无应用错误的 CrashLoop):如果容器不断重启,但日志中没有崩溃信息,
kubelet可能因存活探针检查失败而将其终止(默认值为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、数据库连接字符串)或 Deployment 清单,找出目标服务主机名(例如account-db)和目标端口(例如5432)。 - 向用户提供准确的
kubectl get endpoints和kubectl get networkpolicies命令,并生成所需的NetworkPolicy出站流量补丁,以允许流量访问目标服务和端口。
- 如果实时
步骤 5:提出 GitOps 修正方案
遵循 GitOps 边界,不要直接应用更改——包括集群清单/配置补丁以及任何 Google Cloud 变更(例如 gcloud IAM 绑定或重新创建节点池)。将每项更改作为可审查的建议呈现:清单补丁/PR,或供用户运行的命令。
- 为人工操作员综合根因分析(例如 “payment-api 因内存限制设为 256Mi,而实际使用量飙升至 270Mi,导致退出代码为 137”)。
- 生成修正后的 YAML 清单补丁(例如增加内存限制、添加缺失的 Secret 挂载,或为 Spot 节点添加容忍配置)。
- 检查是否已存在针对该工作负载/故障的分支或 Pull Request(PR)。如果存在,则更新现有分支/PR,或通知用户,而不是创建重复内容。否则,创建分支、提交更改、在 GitHub 上打开 Pull Request(PR),然后结束工作流(无需等待人工合并)。