K8s镜像拉取失败时,Pod 可能停在 `ErrImagePull`、`ImagePullBackOff` 或长时间 `Pending`。最快的排查入口不是反复删除 Pod,而是先看 Pod events:事件会告诉你是镜像不存在、认证失败、仓库超时,还是节点运行时无法拉取。
下面这组命令可以先建立现场,不要一开始就改 YAML;这些检查围绕 Pod 事件、镜像字段和私有仓库凭据展开<sup>[1]</sup><sup>[2]</sup><sup>[3]</sup>:
kubectl describe pod <pod-name> -n <namespace>
kubectl get events -n <namespace> --sort-by=.lastTimestamp
kubectl get secret -n <namespace>
kubectl get pod <pod-name> -n <namespace> -o yaml

图1:从 Pod 事件进入 K8s 镜像拉取失败排查的决策树
先用事件区分 ErrImagePull 和 ImagePullBackOff
Kubernetes 在 Pod 生命周期中会记录容器创建和镜像拉取相关事件;当镜像拉取失败时,Pod 状态和事件会显示失败原因与后续退避重试<sup>[1]</sup>。`ErrImagePull` 更像首次拉取失败信号,`ImagePullBackOff` 表示 kubelet 已进入退避重试阶段。
| 事件或状态 | 常见含义 | 优先检查 |
|---|---|---|
| `ErrImagePull` | 镜像首次拉取失败 | 镜像名、tag、仓库地址、认证 |
| `ImagePullBackOff` | 多次失败后退避重试 | 上一次失败事件、Secret、网络 |
| `manifest unknown` | tag 或 digest 不存在 | 镜像是否已推送、tag 是否写错 |
| `unauthorized` | 私有仓库认证失败 | imagePullSecrets、账号权限 |
| `i/o timeout` | 仓库网络不可达或响应慢 | 节点网络、DNS、代理、仓库状态 |
不要只看 Pod 状态
Pod 状态只告诉你当前卡住了,事件才告诉你为什么卡住。排障时应复制完整事件文本,尤其是仓库返回的状态码、错误短语和失败镜像名。很多问题并不是 Kubernetes 调度失败,而是 kubelet 在目标节点上无法完成镜像拉取。
站内“容器镜像安全怎么做”“Kubernetes安全怎么做”等内容可在发布前确认正式 URL 后作为延伸阅读;本文聚焦拉取失败排障,不展开镜像治理的完整体系。
镜像名、tag 和拉取策略是第一层
Kubernetes 镜像字段支持 registry、repository、tag 或 digest 组合;如果不写 tag,默认行为和镜像策略可能导致环境间结果不一致。Kubernetes 官方文档建议生产环境避免依赖可变 tag,并说明了 `imagePullPolicy` 的影响<sup>[2]</sup>。
先核对这些字段:
- 镜像地址是否包含正确 registry 域名和 namespace。
- tag 是否已推送,大小写是否一致。
- 是否应该使用 digest 固定不可变镜像版本。
- `imagePullPolicy` 是否与发布策略一致。
- 节点是否能解析仓库域名并访问镜像层。
containers:
- name: app
image: registry.example.com/team/app:1.2.3
imagePullPolicy: IfNotPresent
如果团队使用同一个 tag 覆盖发布,节点缓存可能导致旧镜像继续运行;如果每次都 `Always` 拉取超大镜像,又可能放大发布窗口的仓库压力。策略要和镜像版本治理一起设计。
私有仓库要检查 imagePullSecrets 和 ServiceAccount
私有镜像仓库认证通常通过 Docker registry Secret 或 ServiceAccount 绑定 `imagePullSecrets` 实现。Kubernetes 官方任务文档提供了为私有仓库配置 pull secret 的方式<sup>[3]</sup>。

图2:Pod 事件、imagePullSecrets、Serv
认证排查顺序:
- Secret 是否存在于 Pod 所在 namespace。
- Secret 类型是否为 `kubernetes.io/dockerconfigjson`。
- Pod 或 ServiceAccount 是否引用了正确 `imagePullSecrets`。
- Secret 中 registry 地址是否与镜像地址完全匹配。
- 仓库账号是否有目标项目、仓库和 tag 的拉取权限。
Secret namespace 经常被忽略
Secret 是 namespace 级资源。把 Secret 创建在 `default`,但 Pod 部署在业务 namespace,是常见错误。还有一种情况是 ServiceAccount 绑定了旧 Secret,新 Secret 创建成功但 Pod 并没有引用它。下面的检查应与私有仓库凭据文档一起核对<sup>[3]</sup>。
kubectl get serviceaccount <sa-name> -n <namespace> -o yaml
kubectl get secret <secret-name> -n <namespace> -o yaml
如果事件显示 `unauthorized` 或 `authentication required`,应优先检查 Secret、ServiceAccount 和仓库权限,而不是重启节点。
节点网络和运行时决定能否真正拉取
即使镜像名和 Secret 都正确,节点也可能因为 DNS、代理、防火墙、证书、镜像仓库不可用或容器运行时配置导致拉取失败。Kubelet 负责在节点上拉取镜像并启动容器,因此节点侧网络和运行时状态同样重要<sup>[2]</sup>。
节点侧常见检查方向:
- 节点是否能解析 registry 域名。
- 节点到仓库的出站网络是否允许。
- 私有仓库 TLS 证书是否被节点信任。
- containerd 或 CRI-O 是否配置了镜像加速、代理或私有仓库。
- 仓库是否限制并发、频率或 IP 白名单。
在无法 SSH 到节点的环境中,可以使用平台提供的诊断工具或临时调试 Pod 检查网络连通性;涉及生产节点命令时,应按主流程授权执行,不在草稿中假定可直接远程操作。
修复后要验证新 Pod 和旧缓存
镜像拉取失败修复后,不要只看 Secret 已创建或仓库已恢复。要重新触发 Pod 创建,并观察新 Pod 的事件、镜像 ID 和容器启动状态。

图3:从发现镜像拉取失败到修复 Secret、仓库和新 Pod
验证步骤建议:
- 记录原始失败事件和镜像地址。
- 修复镜像 tag、Secret、仓库权限或节点网络。
- 重新创建 Pod 或触发 Deployment rollout。
- 观察新 Pod events 是否还出现拉取失败。
- 确认容器进入 Running,镜像 ID 与期望版本一致。
- 检查业务探针、日志和服务访问是否正常。
旧缓存可能掩盖问题
如果节点已经缓存了某个镜像,`IfNotPresent` 可能让 Pod 看起来启动正常,但其他节点仍然拉取失败。排查私有仓库或镜像权限问题时,最好验证至少一个未缓存该镜像的节点,或使用明确的新 tag / digest。
小结
K8s镜像拉取失败要从事件开始,而不是从猜测开始。先区分 `ErrImagePull` 和 `ImagePullBackOff`,再按镜像名、tag、拉取策略、imagePullSecrets、ServiceAccount、仓库权限、节点网络和运行时逐层排查。修复后一定要通过新 Pod 事件和镜像 ID 验证,避免节点缓存掩盖问题。
参考资料
- [1] Kubernetes Pod Lifecycle – 官方文档
- [2] Kubernetes Images – 官方文档
- [3] Kubernetes Pull an Image from a Private Registry – 官方文档
常见问题
ImagePullBackOff 和 ErrImagePull 有什么区别?
`ErrImagePull` 通常表示镜像拉取动作失败,`ImagePullBackOff` 表示 kubelet 多次失败后进入退避重试。排查时应看事件里的上一条具体错误,而不是只看最终状态名。
K8s镜像拉取失败一定是 Secret 错了吗?
不一定。Secret 错误很常见,但镜像 tag 不存在、仓库地址错误、节点网络不可达、证书不受信任、仓库限流或节点运行时配置异常,也会导致拉取失败。事件文本通常能帮助缩小范围。
修改 imagePullSecrets 后为什么 Pod 还是失败?
可能是 Secret 不在同一 namespace,Pod 使用的 ServiceAccount 没有引用新 Secret,或者旧 Pod 没有重新创建。建议检查 ServiceAccount、重新触发 Pod 创建,并观察新 Pod events 是否使用了正确凭据。