Appearance
K8s 镜像拉取失败 ImagePullBackOff 排查记录
现象
在集群里部署一个简单的 hello-world-flask 应用,Pod 一直起不来。看事件(kubectl describe pod 或 kubectl 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,导致:
- 请求 mirror 获取 oauth token → 403 Forbidden
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)。