describe 和节点侧命令定位失败发生在哪一层。隔离边界:使用独立的 kind 集群和一个临时 registry:2 容器,端口只映射到本机回环地址;不要在企业 registry 上推测试镜像,也不要修改节点 Docker/containerd 全局配置。确保 kind、kubectl、docker 可用,预留约 1 GB 内存和 2 GB 磁盘。
docker info --format '{{.ServerVersion}}' && kind version
mkdir -p /tmp/lab-pull/auth
printf 'labuser:%s\n' "$(openssl passwd -apr1 labpass)" > /tmp/lab-pull/auth/htpasswd
启动需要认证的 registry,并接入 kind 网络:
docker run -d --name lab-registry -p 127.0.0.1:5001:5000 -v /tmp/lab-pull/auth:/auth \
-e REGISTRY_AUTH=htpasswd -e REGISTRY_AUTH_HTPASSWD_REALM=lab \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd registry:2
kind create cluster --name lab-pull && docker network connect kind lab-registry
docker login 127.0.0.1:5001 -u labuser -p labpass
接入 kind 网络后,节点可以用容器名 lab-registry 解析到 registry。
先推送一个测试镜像,然后创建两个 Pod:一个使用不存在的 tag,一个指向私有仓库但没有 imagePullSecrets。
docker pull nginx:1.27-alpine
docker tag nginx:1.27-alpine 127.0.0.1:5001/private/app:1.0 && docker push 127.0.0.1:5001/private/app:1.0
kubectl create namespace lab-pull && kubectl -n lab-pull run bad-tag --image=nginx:1.27-no-such-tag
kubectl -n lab-pull run private-no-secret --image=lab-registry:5000/private/app:1.0
等待约 30 秒,让 kubelet 进入退避:
sleep 30
kubectl -n lab-pull get pods
kubectl -n lab-pull describe pod bad-tag | sed -n '/Events:/,$p'
kubectl -n lab-pull describe pod private-no-secret | sed -n '/Events:/,$p'
预期 bad-tag 的 Events 先出现 ErrImagePull,失败原因是 manifest unknown 或 not found,随后变成 ImagePullBackOff;private-no-secret 出现 401 Unauthorized,同样从 ErrImagePull 进入 ImagePullBackOff。ImagePullBackOff 是 kubelet 在多次失败后按指数退避重试的状态,不代表错误类型变了。
在 kind 节点上验证 DNS 和端口可达性:
docker exec lab-pull-control-plane sh -c \
'getent hosts lab-registry && timeout 5 bash -c "echo > /dev/tcp/lab-registry/5000" && echo tcp-ok'
输出 registry IP 和 tcp-ok,说明节点到 registry 的 DNS 和端口是通的,失败点在认证而不是网络。若解析失败或连接超时,问题在节点网络,不在镜像 tag。
manifest unknown 指向 tag/仓库路径;401 Unauthorized 指向 secret;no such host、i/o timeout 指向 DNS 或网络。imagePullSecrets 是否真的存在且 namespace 一致。Secret 必须和 Pod 同 namespace,名字写错时事件会显示 FailedToRetrieveImagePullSecret。节点侧可用 crictl images 和 crictl pull 复现拉取错误。ImagePullBackOff 就以为 tag 写错。它只是“拉取失败并退避”,认证失败、网络超时、仓库限流都会进入同一状态。另一个误判是把 ErrImagePull 当成另一种故障;它通常只是首次失败,退避后就显示为 ImagePullBackOff。只有部分节点失败时,优先比较节点网络和镜像缓存。为私有仓库创建 docker-registry 类型的 Secret,并在 Pod 中引用:
kubectl -n lab-pull create secret docker-registry regcred \
--docker-server=lab-registry:5000 --docker-username=labuser --docker-password=labpass
kubectl -n lab-pull delete pod private-no-secret
kubectl -n lab-pull run private-with-secret \
--image=lab-registry:5000/private/app:1.0 \
--overrides='{"spec":{"imagePullSecrets":[{"name":"regcred"}]}}'
把 bad-tag 改成真实存在的 tag:
kubectl -n lab-pull set image pod/bad-tag bad-tag=nginx:1.27-alpine
kubectl -n lab-pull wait --for=condition=Ready pod/bad-tag pod/private-with-secret --timeout=120s
两个 Pod 都变为 Running 且 READY 1/1 即修复。生产中还应把 imagePullSecrets 挂到 ServiceAccount,避免每个 Pod 手写。
kubectl -n lab-pull delete pod bad-tag private-no-secret private-with-secret --ignore-not-found
kubectl delete namespace lab-pull --ignore-not-found
kind delete cluster --name lab-pull
docker rm -f lab-registry && docker logout 127.0.0.1:5001
rm -rf /tmp/lab-pull
只删除本实验创建的 kind 集群和 registry 容器;不要执行 docker system prune。
ErrImagePull 和 ImagePullBackOff 是同一种错误吗?