GKE中微服务无法重启问题排查:镜像拉取失败报“failed to copy: httpReaderSeeker: failed open: could not fetch content descriptor”错误
问题分析:缺失的内容描述符是什么?
那个sha256:4e9f2cdf438714c2c4533e28c6c41a89cc6c1b46cf77e54c488db30ca4f5b6f3是Docker镜像的分层哈希值——Docker镜像是由多个只读分层叠加而成的,每个分层都有唯一的sha256标识。拉取镜像时,kubelet需要下载所有依赖的分层,一旦某个分层在GCR中找不到,就会抛出这个NotFound错误。
为什么手动拉取成功,但GKE拉取失败?
手动拉取时,你的本地Docker可能用的是个人GCP账号权限(比如已经通过gcloud auth configure-docker授权),或者本地缓存了该分层;而GKE节点拉取镜像用的是节点池的服务账号,两者权限或缓存状态不同。结合你的场景,可能的原因有以下几个:
1. GCR的生命周期管理策略删除了镜像分层
如果你的生产环境GCR配置了自动清理规则(比如删除超过N天的旧镜像/分层),可能导致这个依赖分层被误删。虽然镜像的manifest(就是日志里的us.gcr.io/scout-productive/client@sha256:383af5c5989a3e8a5608495e589c67f444d99e7b705cfff29e57f49b900cba33)还存在,但某个底层分层已经被清理了。
排查步骤:
- 登录GCP控制台,进入Container Registry,查看你的镜像仓库是否有生命周期管理规则(路径:Container Registry → 仓库 → 生命周期)。
- 用命令查看该镜像的所有分层,确认缺失的分层是否存在:
在输出的gcloud container images describe us.gcr.io/scout-productive/client@sha256:383af5c5989a3e8a5608495e589c67f444d99e7b705cfff29e57f49b900cba33manifest.layers里查找那个缺失的sha256值,如果找不到,说明分层确实被删除了。
2. GKE节点池的服务账号权限变化
GKE节点拉取GCR镜像需要节点服务账号具备roles/storage.objectViewer权限(允许读取GCS存储桶里的镜像分层)。如果最近修改了IAM权限,比如移除了这个角色,就会导致节点无法拉取分层(即使镜像存在)。
排查步骤:
- 找到你的节点池使用的服务账号:
查看gcloud container node-pools describe YOUR_NODE_POOL_NAME --cluster YOUR_CLUSTER_NAME --zone YOUR_ZONEconfig.serviceAccount字段的值。 - 检查该服务账号的IAM权限:
确认是否包含gcloud projects get-iam-policy YOUR_PROJECT_ID --filter="bindings.members:serviceAccount:SERVICE_ACCOUNT_EMAIL"roles/storage.objectViewer角色。如果没有,需要重新添加:gcloud projects add-iam-policy-binding YOUR_PROJECT_ID --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" --role="roles/storage.objectViewer"
3. 镜像推送不完整或GCR缓存异常
如果当初推送这个镜像时中途失败,可能导致manifest已经上传,但某个分层没有完全同步到GCR。手动拉取时可能从本地缓存获取,但GKE节点没有这个缓存。另外,GCR的边缘节点缓存也可能出现异常,导致节点无法获取到分层。
解决方法:
- 重新推送该镜像:用Skaffold重新构建并推送,或者手动重新打标签推送:
然后更新Deployment的镜像为这个新标签,触发Pod重新拉取。gcloud container images add-tag us.gcr.io/scout-productive/client:v0.002-72-gaa98dde us.gcr.io/scout-productive/client:v0.002-72-gaa98dde-fix
4. GKE节点本地镜像缓存损坏
节点本地的Docker缓存可能损坏,导致无法正确拉取或解析镜像分层。这种情况在节点重启后可能出现。
解决方法:
- 删除有问题的Pod所在的节点,让GKE自动创建新节点:
新节点会重新拉取镜像,避免本地缓存的问题。kubectl delete node NODE_NAME
关于Pod突然被杀死的额外排查
你提到所有Pod同时被终止,可能的触发因素:
- GKE节点池自动升级(GKE会定期更新节点的操作系统或Kubernetes版本,可能重启节点导致Pod被驱逐)。
- 集群的资源不足(比如节点磁盘满了,触发节点重启)。
- 手动操作(比如误删节点池、重启集群)。
可以在GCP控制台的云日志里搜索kubelet日志,关键词用Terminating pod,查看Pod被终止的具体原因。
内容的提问来源于stack exchange,提问作者willi

