Skip to content

K8s 镜像拉取失败 ImagePullBackOff 排查记录

现象

在集群里部署一个简单的 hello-world-flask 应用,Pod 一直起不来。看事件(kubectl describe podkubectl get events):

Events:
  Type     Reason     Age    From                 Message
  Normal   Scheduled  2m2s   default-scheduler    Successfully assigned default/hello-world-flask to robot-tm1701
  Warning  Failed     60s    kubelet              spec.containers{flask}: Failed to pull image "robotzsj/hello-world-flask:latest": failed to pull and unpack image "docker.io/robotzsj/hello-world-flask:latest": failed to resolve reference "docker.io/robotzsj/hello-world-flask:latest": failed to authorize: failed to fetch oauth token: unexpected status from GET request to https://m.daocloud.io/auth/token?scope=repository%3Arobotzsj%2Fhello-world-flask%3Apull&service=docker.m.daocloud.io: 403 Forbidden
  Warning  Failed     60s    kubelet              Error: ErrImagePull
  Normal   BackOff    59s    kubelet              Back-off pulling image "robotzsj/hello-world-flask:latest"
  Warning  Failed     59s    kubelet              Error: ImagePullBackOff

核心报错一行看懂:

failed to fetch oauth token: ... https://m.daocloud.io/auth/token?service=docker.m.daocloud.io: 403 Forbidden

原因分析

节点上配置了 containerd 镜像加速(registry mirror),指向 DaoCloud 的 m.daocloud.io。Pod 调度到节点后,kubelet → containerd 拉镜像时走这个 mirror,但 DaoCloud 镜像源对 docker.io/robotzsj/hello-world-flask匿名拉取返回 403,导致:

  1. 请求 mirror 获取 oauth token → 403 Forbidden
  2. ErrImagePull → kubelet 重试 → ImagePullBackOff(指数退避,反复失败)

几个细节:

  • 报错里 auth 端点是 m.daocloud.io,而 service 是 docker.m.daocloud.io,两者不一致,说明 mirror 配置或镜像源本身有问题(DaoCloud 的加速服务对部分仓库需要认证或已失效)。
  • 镜像本身在 Docker Hub 上是公开的(robotzsj/hello-world-flask:latest),问题出在加速源而不是镜像或 Dockerfile。

排查步骤

bash
# 1. 看事件(最直接)
kubectl describe pod hello-world-flask

# 2. 确认节点上的 containerd 配置(registry mirror 在这里)
cat /etc/containerd/config.toml | grep -A 10 "mirrors"

# 3. 手动测试 mirror 是否可用
curl -I https://m.daocloud.io/v2/

修复

方案 A:移除/更换不可用的 mirror

toml
# /etc/containerd/config.toml — 删掉或注释掉坏的 mirror 条目
[plugins."io.containerd.grpc.v1.cri".registry]
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
    # 删掉指向 m.daocloud.io 的配置,或换一个可用的加速源

改完重启 containerd:

bash
systemctl restart containerd

方案 B:绕过 mirror 直连 Docker Hub(如果网络允许)

toml
[plugins."io.containerd.grpc.v1.cri".registry]
  configs."docker.io".auth = {}   # 保持默认直连

方案 C:换一个可靠的加速源(DaoCloud 之外的公开镜像加速,注意选有稳定服务的)。

改完后重新触发拉取(删除 Pod 让其重建,或 kubectl rollout restart deployment/xxx)。

复盘

  • 镜像加速是双刃剑:配置了加速源,拉取路径就多了一个失败点;加速源失效/限流时,报错信息指向 mirror 而不是 Docker Hub,容易误导排查方向。
  • 先分清"镜像问题"还是"拉取链路问题":报错里有 m.daocloud.io 等 mirror 域名,基本可以断定是加速源的问题;如果报 manifest unknown / not found,才是镜像本身不存在。
  • 多节点集群里,改 containerd 配置要所有节点都改(或走配置管理,比如 k3s 的 /etc/rancher/k3s/registries.yaml)。

记录于 2026-08,集群:robot-tm1701(k8s,containerd runtime)。

Meoo By Meoo 秒悟