mcpField
G
gke-workload-troubleshooting

gke-workload-troubleshooting

github.com/google云与 DevOps21.6k4.8k 次安装今天更新
代码仓库

关于 gke-workload-troubleshooting

通过结构化的只读排查诊断 GKE 中的应用工作负载故障。它分析集群上下文、Pod 状态、事件和日志,并提出 Kubernetes 清单或 Google Cloud 更改建议,但不会执行线上变更。

主要功能

  1. 01只读 GKE 诊断
  2. 02检查 Pod 状态和事件
  3. 03分析应用及集群日志
  4. 04提出清单和云端修复建议
  5. 05集群不可达时提供备用命令

适用场景

  1. 01排查 Pod 处于 Pending 或反复重启
  2. 02诊断镜像拉取和卷挂载故障
  3. 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:非交互式上下文发现与时间窗口定义
  1. 提取参数:以非交互方式从用户提示、当前 SETTINGS.md 或当前环境默认值中提取所需上下文(project_id、cluster_name、cluster_location、workload_name、workload_namespace):

    • 如果未提供 workload_namespace,则默认为 default。
    • 从当前环境推断缺失的集群参数(kubectl config current-context 或 gcloud config get-value project)。
    • 优先从提示和环境默认值中以非交互方式发现上下文,以确保自主执行流程。
  2. 集群凭据与回退模式:

    • 尝试获取凭据:gcloud container clusters get-credentials {cluster_name} --region/--zone {cluster_location}
    • 回退/试运行模式:如果集群无法访问、不存在,或实时命令执行失败(例如在沙盒评估、试运行模式或离线分析中):
      • 限制重试次数,避免在集群不可访问时耗尽资源或使上下文溢出。
      • 立即提供一组准确的 kubectl 诊断命令,供人工操作员运行。
      • 根据报告的症状综合分析根本原因,并输出建议的 GitOps 清单修复方案。
  3. 时间处理与回退方案:

    • 确定问题时间戳({issue_time}):
      • 提供了具体时间:如果用户提供了具体时间戳,则将其用作 {issue_time}。
      • 提供了相对时间(例如“5 分钟前”):根据当前系统时间动态计算对应的 UTC 时间戳,并将其用作 {issue_time}。
      • 未提供时间(默认):将当前系统时间用作 {issue_time}。
    • 计算时间窗口:以 {issue_time} 为中心设置 1 小时的查询窗口(start_time = {issue_time} - 30m,end_time = {issue_time} + 30m)。

步骤 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(查询命名空间事件),查看确切的拉取错误消息。
  • 状态: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)。
  • 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,提出修复方案 (提高限制、修复泄漏,或调整节点池规格)。

  • 存活探针失败(无应用错误的 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
逻辑与试运行回退:
  1. 实时集群模式:

    • 如果 kubectl get endpoints 返回空列表,则目标微服务本身无法调度或启动(排查目标服务)。
    • 如果端点存在,但日志显示超时,请分析 NetworkPolicy 的出站流量限制,确认是否允许出站流量访问目标服务的 IP/端口。
  2. 沙箱/试运行模式:

    • 如果实时 kubectl 查询失败或无法连接集群,不要重试访问实时集群,也不要反复尝试连接。
    • 立即检查应用源代码(例如 worker.py、app.go、数据库连接字符串)或 Deployment 清单,找出目标服务主机名(例如 account-db)和目标端口(例如 5432)。
    • 向用户提供准确的 kubectl get endpoints 和 kubectl get networkpolicies 命令,并生成所需的 NetworkPolicy 出站流量补丁,以允许流量访问目标服务和端口。

步骤 5:提出 GitOps 修正方案

遵循 GitOps 边界,不要直接应用更改——包括集群清单/配置补丁以及任何 Google Cloud 变更(例如 gcloud IAM 绑定或重新创建节点池)。将每项更改作为可审查的建议呈现:清单补丁/PR,或供用户运行的命令。

  1. 为人工操作员综合根因分析(例如 “payment-api 因内存限制设为 256Mi,而实际使用量飙升至 270Mi,导致退出代码为 137”)。
  2. 生成修正后的 YAML 清单补丁(例如增加内存限制、添加缺失的 Secret 挂载,或为 Spot 节点添加容忍配置)。
  3. 检查是否已存在针对该工作负载/故障的分支或 Pull Request(PR)。如果存在,则更新现有分支/PR,或通知用户,而不是创建重复内容。否则,创建分支、提交更改、在 GitHub 上打开 Pull Request(PR),然后结束工作流(无需等待人工合并)。

参考资料

评论

支持 Markdown