关于 gke-storage-troubleshooting
面向 GKE 工作负载的持久化存储故障诊断技能,适用于集群运维人员。它按故障类型提供诊断和配置修正建议,并要求保持只读诊断,将修正作为 GitOps 补丁提交,而不直接修改线上资源。
主要功能
- 01诊断卷挂载与附加故障
- 02排查磁盘性能和节点存储压力
- 03分析存储卷扩容未生效
- 04检查集群及节点池创建错误
- 05调查 Cloud Storage FUSE 内存溢出
- 06以 GitOps 补丁提出配置修正
适用场景
- 01处理 Pod 的卷挂载失败
- 02调查节点存储压力或磁盘变慢
- 03修复容器未反映卷扩容的问题
- 04排查 Cloud Storage FUSE Pod 的 OOM
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ google/ skills --skill gke-storage-troubleshooting安装后在 Claude Code 中输入/gke-storage-troubleshooting确认已加载。
附带文件references/attribution.md
详细介绍
GKE 存储故障排查技能
使用此技能系统地诊断并解决在 GKE 上运行的工作负载的持久性存储故障,包括卷挂接/挂载错误、磁盘性能和节点存储压力、卷扩容、与存储相关的集群/节点池创建错误,以及 Cloud Storage FUSE 内存问题。此技能以非交互方式运行,并会在提出清单或配置修正建议前,严格执行只读诊断边界。
对于常规存储配置以及 StorageClass/PVC 编写,请改用
gke-storage技能。此技能专注于故障诊断。
归属信息
必须阅读:执行任何 gcloud、REST API 或 MCP 命令之前,必须先阅读使用归属信息。对于直接在终端中执行的命令(绝不在生成的脚本、Terraform 或用户代码中执行),请在 gcloud 前加上
CLOUDSDK_METRICS_REQUEST_ATTRIBUTION="agent-skills/1.0 (skill:gke-storage-troubleshooting)"
,并在 User-Agent 标头中追加 agent-skills/1.0 (skill:gke-storage-troubleshooting)。
🔍 诊断与解决工作流
步骤 0:非交互式上下文发现与试运行回退
-
提取参数:从用户提示、当前的
SETTINGS.md或环境默认值中,以非交互方式提取所需上下文(project_id、cluster_name、cluster_location、workload_name、workload_namespace、pod_name以及相关的pvc_name/pv_name/node_name):- 如果未提供
workload_namespace,则默认为default。 - 从当前环境推断缺失的集群参数(
kubectl config current-context或gcloud config get-value project)。
- 如果未提供
-
集群凭据与回退模式:
- 尝试获取凭据:
gcloud container clusters get-credentials {cluster_name} --location {cluster_location} --project {project_id}。 - 回退/试运行模式:如果集群无法访问、不存在,或实时命令执行失败(例如在沙盒评估、试运行模式或离线分析中):
- 限制重试次数,避免资源耗尽和上下文溢出。
- 立即提供供人工操作员运行的准确
kubectl/gcloud诊断命令。 - 根据报告的症状综合分析根本原因,并输出建议的 GitOps 修正方案。
- 尝试获取凭据:
步骤 1:对存储症状进行分类
收集主要信号,然后跳转到步骤 2(解决方案)中匹配的分支——通常只需执行与你的诊断匹配的一个分支,而非全部分支。
诊断命令:
kubectl describe pod {pod_name} -n {workload_namespace}
kubectl get pvc,pv -n {workload_namespace}
kubectl get events -n {workload_namespace} --sort-by='.metadata.creationTimestamp'
kubectl describe node {node_name}
- Pod 卡在
ContainerCreating,并出现挂接/挂载事件 → 卷挂接与挂载故障。 - 节点级运行缓慢、
PLEG is not healthy或出现StoragePressureDetected事件 → 磁盘性能与节点存储压力。 - 集群/节点池创建或配置错误 → 存储配置与创建故障。
- 已扩容的卷未反映在容器内 → 卷扩容未反映在容器中。
- Cloud Storage FUSE Pod/sidecar OOM → Cloud Storage FUSE 内存不足(OOM)事件。
步骤 2:解决方案
只执行与你在步骤 1 中的诊断相匹配的分支。这些分支是互斥的备选方案,并非连续步骤。
卷挂接与挂载故障
-
Error 400: Cannot attach RePD to an optimized VM:区域级持久磁盘不能用于内存优化型或计算优化型机器类型。- 如果不强制要求使用区域级 PD,请将工作负载切换到非区域级持久磁盘 StorageClass。
- 如果确实需要区域级 PD,请使用污点和容忍度,将需要区域级 PD 的 Pod 调度到不使用优化型机器类型的节点池。
-
节点池迁移到第 4 代(N4、N4A、N4D)机器系列后,如果工作负载使用 Persistent Disk StorageClass,Pod 仍处于
Pending/FailedScheduling状态:N4/N4A/N4D 机器不支持 Persistent Disk(仅支持 Hyperdisk),因此绑定到pd-*StorageClass 的 PVC 无法绑定到这些节点,也无法在其上调度。事件通常会显示FailedScheduling,并提示卷节点亲和性/拓扑冲突。- 将工作负载切换到适用于 Gen4 节点池的Hyperdisk StorageClass(例如
type: hyperdisk-balanced)。 - 对于现有 Persistent Disk 卷,请将数据迁移到 Hyperdisk 卷;原始 PD 无法挂接到 Gen4 节点。
- 如果工作负载必须继续使用 Persistent Disk,请通过节点选择将其保留在支持 PD 的机器系列(例如 N2)上。这是机器类型与磁盘类型不兼容的问题,不是容量问题,因此增加磁盘大小或配额没有帮助。
- 将工作负载切换到适用于 Gen4 节点池的Hyperdisk StorageClass(例如
-
当计算类在不同 VM 代际之间回退(例如优先使用 N4,回退到 N2),或一个 StorageClass 必须服务于混合代际时,Hyperdisk Pod 无法调度:StorageClass 中单一的静态磁盘类型与回退列表中的所有机器系列都不兼容,因此 Pod 无法在回退节点上绑定其卷。
-
使用自动磁盘类型选择:将 StorageClass 的
parameters.type设置为dynamic,并配置hyperdisk-type、pd-type和disk-type-preference,再设置use-allowed-disk-topology: "true",这样 GKE 就会为每个节点选择兼容的磁盘类型,并且只将 Pod 调度到支持该类型的节点上。这样,一个动态 StorageClass 就能覆盖多个 VM 代际(需要使用文档中注明的 GKE 版本)。动态 StorageClass 示例(GKE 1.35.3-gke.1290000+):
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: dynamic-volume provisioner: pd.csi.storage.gke.io volumeBindingMode: WaitForFirstConsumer allowVolumeExpansion: true parameters: type: dynamic pd-type: pd-balanced hyperdisk-type: hyperdisk-balanced # 对同时支持 PD 和 Hyperdisk 的节点,优先使用的存储类型; # 如果省略,则默认为 hyperdisk-type。 disk-type-preference: hyperdisk-type # 最佳实践:仅将 Pod 调度到支持该磁盘类型的节点上。 use-allowed-disk-topology: "true"
-
-
由于
fsGroup设置导致挂载无响应:如果 Pod 在包含大量文件的卷上配置了securityContext.fsGroup,kubelet 会递归更改每个文件的所有权,这可能导致挂载超时。症状如下:Unable to attach or mount volumes for pod; skipping pod ... timed out waiting for the condition检查 Pod 日志中是否有
Setting volume ownership for ... and fsGroup set条目以确认,然后采取以下一种措施:- 减少卷中的文件数量。
- 设置
securityContext.fsGroupChangePolicy: OnRootMismatch,仅在顶层权限不匹配时更改所有权。 - 如果不需要,请停止使用
fsGroup设置。
磁盘性能与节点存储压力
-
磁盘性能不佳(症状包括
task dockerd:... blocked for more than 300 seconds、PLEG is not healthy,或缓慢的fs: disk usage扫描):节点启动磁盘由操作系统、容器镜像、overlay 文件系统和基于磁盘的emptyDir卷共享,且节点上所有同类型磁盘的性能也由它们共同使用。- 标准持久磁盘小于 200 GB 的节点通常会受到影响。请增大磁盘容量或改用 SSD,尤其是在生产环境中。
- 对于工作负载频繁使用
emptyDir的节点池,请启用用于临时存储的本地 SSD。
-
磁盘操作缓慢导致 Pod 创建失败:在受影响的节点版本上(GKE 1.18–1.23,且低于已修复的补丁版本),
k8s_node container-runtime日志会显示failed to reserve container name ... is reserved for ...(containerd 问题 #4604)。- 在 PodSpec 中设置
restartPolicy: Always或OnFailure,并提高启动磁盘 IOPS(使用更大或速度更快的磁盘类型),以缓解问题。 - 永久修复方案是使用 containerd 1.6.0+;请升级到包含此版本的 GKE 版本。
- 在 PodSpec 中设置
-
StoragePressureDetected(节点存储压力过高):节点条件StoragePressureRootFileSystem变为True(例如,Disk /dev/nvme0n1 usage 89% exceeds threshold 85%),原因可能是emptyDir写入过多、拉取大型镜像或日志不断累积。- 在受影响的节点上使用
df -h查看用量(重点关注/mnt/stateful_partition和临时挂载点)。 - 可通过使用更大的启动磁盘、为临时存储添加本地 SSD、设置合适的
ephemeral-storage请求/限制,以及清理未使用的文件/镜像/日志来解决。
- 在受影响的节点上使用
存储配置与创建失败
-
The selected machine type ... has a fixed number of local SSD(s):EphemeralStorageLocalSsdConfig/LocalNvmeSsdBlockConfig中指定的本地 SSD 数量与机器类型包含的固定数量不匹配。- 指定与机器类型匹配的本地 SSD 数量。对于第三代机器系列,请省略本地 SSD 的
count标志,系统会自动配置正确的值。
- 指定与机器类型匹配的本地 SSD 数量。对于第三代机器系列,请省略本地 SSD 的
-
Hyperdisk 存储池:创建集群或节点池失败,并出现
ZONE_RESOURCE_POOL_EXHAUSTED(或类似的 Compute Engine 资源错误):目标可用区没有足够容量来满足所请求的 Hyperdisk Balanced 磁盘或机器类型。- 在同一区域内选择一个有可用容量且支持 Hyperdisk Balanced 存储池的新可用区。由于存储池是区域级资源,请在新可用区删除并重新创建存储池,然后在那里创建集群/节点池。
卷扩容未反映到容器中
卷扩容必须始终通过PersistentVolumeClaim进行。直接编辑 PersistentVolume 可能会导致容器文件系统仍使用旧容量。
-
保持修改后的 PersistentVolume 对象不变。
-
编辑 PersistentVolumeClaim,并将
spec.resources.requests.storage设置为高于当前 PersistentVolume 容量的值。 -
kubelet 随后会自动调整 PV、PVC 和容器文件系统的大小。在 Pod 内验证:
kubectl exec {pod_name} -n {workload_namespace} -- df -h
Cloud Storage FUSE 内存不足(OOM)事件
如果 Pod 出现高内存使用率,或发生与 Cloud Storage FUSE CSI 驱动程序相关的 OOM 终止:
-
在 Cloud Storage FUSE CSI 驱动程序的 sidecar 容器上配置 Cloud Profiler,以启用 CPU/内存快照。
-
在 Cloud Logging 中按 Pod 筛选,查找 OOM 事件:
jsonPayload.involvedObject.name="{pod_name}" jsonPayload.involvedObject.kind="Pod" OOMKilled如果 sidecar 挂载器或 GCSFuse 进程发生 OOM,Pod 名称就是工作负载 Pod 的名称;如果节点驱动程序发生 OOM,则名称为
gcsfusecsi-node-*。 -
提取 Pod UID(
jsonPayload.involvedObject.uid)和时间戳,然后在 Cloud Profiler 中使用 OOM 时间戳对应的{pod_name}_{pod_uid}Service Version 分析匹配的快照。
步骤 3:提出 GitOps 修正方案
遵守只读诊断边界:不要使用
kubectl edit、kubectl patch 或 kubectl apply 应用实时变更。请改为提供修正后的 StorageClass、PersistentVolumeClaim、PodSpec(securityContext、restartPolicy)或节点池配置,作为可供审核的补丁,通过用户的 GitOps 流水线(例如 Config Sync、Argo CD 或 Flux)应用。