kind 集群拉取镜像失败时的 4 种解决方案

张开发
2026/4/9 1:25:04 15 分钟阅读

分享文章

kind 集群拉取镜像失败时的 4 种解决方案
文章目录问题现象一、示例文件和目录结构二、典型现象是什么三、为什么宿主机 Docker 配了镜像源kind 还是拉不下来四、解决方案如何选择五、方案 1给 kind 集群配置镜像源1. 新建 kind 配置文件2. 删除旧集群3. 用配置文件重建集群4. 确认集群正常5. 重新部署业务 YAML六、方案 2宿主机先拉取镜像再导入 kind1. 宿主机先拉镜像2. 导入到 kind 集群3. 重新触发 Pod 创建七、方案 3给 kind 节点配置 HTTP/HTTPS 代理1. 先找宿主机在节点容器看来可访问的地址2. 给 containerd 配代理3. 给 kubelet 配代理4. 重载并重启服务5. 验证代理是否生效6. 再次触发镜像拉取八、方案 4本地 registry 代理缓存1. 启动本地 registry 代理缓存2. 在 kind 配置文件中加入本地 registry3. 重建集群4. 把本地 registry 接到 kind 网络5. 可选登记本地 registry 信息6. 再次部署应用九、如何确认故障原因1. 先看 Pod 状态2. 再看 Pod 详情3. 再看最近事件问题现象本地kind集群已经创建成功但业务 Pod 一直卡在ErrImagePullImagePullBackOff这类问题在下面几种场景中非常常见宿主机不能稳定访问外网Docker Hub 访问慢集群节点和宿主机网络环境不一致宿主机 Docker 已经配置了镜像源但kind节点仍然无法正常拉取镜像本文按推荐顺序整理 4 种常见解决方案并给出完整操作步骤。一、示例文件和目录结构为了便于直接复现先统一本文后面使用的文件名和目录结构。可以先建立一个练习目录例如mkdir-pk8s-democdk8s-demo本文默认使用下面两个文件k8s-demo/ kind-config.yaml nginx-demo.yaml其中kind-config.yaml给kind创建集群时使用的配置文件nginx-demo.yaml给 Kubernetes 集群部署Deployment Service的业务资源文件二、典型现象是什么比如已经写好了一个最小Deployment ServiceapiVersion:apps/v1kind:Deploymentmetadata:name:nginx-deploymentspec:replicas:2selector:matchLabels:app:nginxtemplate:metadata:labels:app:nginxspec:containers:-name:nginximage:nginx:latestports:-containerPort:80---apiVersion:v1kind:Servicemetadata:name:nginx-servicespec:selector:app:nginxports:-port:80targetPort:80把上面的内容保存为k8s-demo/nginx-demo.yaml执行cdk8s-demo kubectl apply-fnginx-demo.yaml kubectl get pods可能看到NAME READY STATUS RESTARTS AGE nginx-deployment-xxxxxx-aaaaa 0/1 ImagePullBackOff 0 77s nginx-deployment-xxxxxx-bbbbb 0/1 ImagePullBackOff 0 77s再执行kubectl describe podpod-name常见报错类似Failed to pull image nginx:latest: failed to resolve reference docker.io/library/nginx:latest: Head https://registry-1.docker.io/v2/library/nginx/manifests/latest: dial tcp ...:443: i/o timeout这说明Deployment本身创建成功了ReplicaSet也创建成功了Pod也已经被调度到了节点上真正失败的是kind节点里的容器运行时去拉镜像时超时了三、为什么宿主机 Docker 配了镜像源kind 还是拉不下来这是最容易误解的地方。比如宿主机已经配置了{registry-mirrors:[https://docker.m.daocloud.io,https://docker.1ms.run]}很多人会以为宿主机 Docker 能走镜像源kind 集群自然也会走镜像源。其实不是。因为宿主机上的docker有自己的 daemon 配置kind集群的节点本质上是 Docker 容器节点里真正拉镜像的是containerd也就是说镜像拉取这条链实际上是Pod - kind 节点里的 containerd - 镜像仓库而不是Pod - 宿主机 docker daemon所以宿主机 Docker 能拉镜像不等于 kind 节点里的 containerd 也已经有同样配置。四、解决方案如何选择这里给出 4 种方案对于国内个人更推荐第一和第二种方案结合使用方案适合场景优点局限给 kind 配镜像源公共镜像为主长期本地学习一次配置后很多公共镜像都能受益不用每次手工导入需要重建 kind 集群镜像源覆盖不全时仍可能失败宿主机先拉镜像再导入 kind临时学习、镜像数量少、不想重建集群最直接、最稳不需要改 kind 配置每个新镜像都要手工导入不适合长期环境给 kind 节点配置代理网络环境必须走代理镜像源无法覆盖需求能处理镜像源没有覆盖到的镜像更接近企业内网场景配置更复杂代理地址和NO_PROXY容易出错重建集群后常要重配本地 registry 代理缓存长期拉很多镜像希望本地缓存已拉取镜像可缓存对长期本地测试环境友好比镜像源多一层本地维护比手工导入更复杂五、方案 1给 kind 集群配置镜像源国内个人更推荐这种方案能满足绝大部分需求。1. 新建 kind 配置文件在练习目录下创建cdk8s-demotouchkind-config.yaml内容如下kind:ClusterapiVersion:kind.x-k8s.io/v1alpha4containerdConfigPatches:-|-[plugins.io.containerd.grpc.v1.cri.registry.mirrors.docker.io]endpoint [https://docker.m.daocloud.io,https://docker.1ms.run]把上面的内容保存为k8s-demo/kind-config.yaml这表示kind节点里的containerd在拉取docker.io镜像时优先走这两个镜像源2. 删除旧集群kind delete cluster--namedemo3. 用配置文件重建集群kind create cluster--namedemo--configkind-config.yaml4. 确认集群正常kubectl config current-context kubectl cluster-info kubectl get nodes5. 重新部署业务 YAMLcdk8s-demo kubectl apply-fnginx-demo.yaml kubectl get pods如果镜像源配置生效Pod通常就不会再卡在ErrImagePullImagePullBackOff六、方案 2宿主机先拉取镜像再导入 kind这是最稳、最适合临时救场的方案。1. 宿主机先拉镜像dockerpull nginx:latest2. 导入到 kind 集群kind load docker-image nginx:latest--namedemo3. 重新触发 Pod 创建kubectl delete pod-lappnginx kubectl get pods因为删的是 Pod不是 Deployment所以 Deployment 会自动补出新的 Pod。七、方案 3给 kind 节点配置 HTTP/HTTPS 代理如果当前环境必须通过代理出网那么可以直接给 kind 节点里的containerd和kubelet配代理。1. 先找宿主机在节点容器看来可访问的地址先执行dockerexecdemo-control-planesh-lcip route | awk /default/ {print \$3}常见会得到类似172.18.0.1这通常是 kind 节点容器访问宿主机时使用的网关地址。注意如果代理只监听127.0.0.1:7890kind 节点容器通常不能直接访问它所以代理最好监听到宿主机可被容器访问的地址。2. 给 containerd 配代理把下面命令里的代理地址替换成实际可用地址比如http://172.18.0.1:7890dockerexecdemo-control-planebash-lcmkdir -p /etc/systemd/system/containerd.service.d cat /etc/systemd/system/containerd.service.d/http-proxy.conf EOF [Service] EnvironmentHTTP_PROXYhttp://172.18.0.1:7890 EnvironmentHTTPS_PROXYhttp://172.18.0.1:7890 EnvironmentNO_PROXY127.0.0.1,localhost,10.96.0.0/12,10.244.0.0/16,.svc,.cluster.local EOF3. 给 kubelet 配代理dockerexecdemo-control-planebash-lcmkdir -p /etc/systemd/system/kubelet.service.d cat /etc/systemd/system/kubelet.service.d/http-proxy.conf EOF [Service] EnvironmentHTTP_PROXYhttp://172.18.0.1:7890 EnvironmentHTTPS_PROXYhttp://172.18.0.1:7890 EnvironmentNO_PROXY127.0.0.1,localhost,10.96.0.0/12,10.244.0.0/16,.svc,.cluster.local EOF4. 重载并重启服务dockerexecdemo-control-planebash-lcsystemctl daemon-reload systemctl restart containerd systemctl restart kubelet5. 验证代理是否生效dockerexecdemo-control-planebash-lcsystemctl show --propertyEnvironment containerddockerexecdemo-control-planebash-lcsystemctl show --propertyEnvironment kubelet如果输出中能看到HTTP_PROXY...HTTPS_PROXY...说明代理变量已经写进 systemd 服务环境。6. 再次触发镜像拉取kubectl delete pod-lappnginx kubectl get pods-w八、方案 4本地 registry 代理缓存这是一种比较折中的长期方案。思路是宿主机先启动一个本地registry:2让它作为 Docker Hub 的代理缓存kind 节点拉镜像时优先走这个本地 registry1. 启动本地 registry 代理缓存dockerrm-fkind-registry2/dev/null||truedockerrun-d--restartalways--namekind-registry-p5001:5000\-eREGISTRY_PROXY_REMOTEURLhttps://registry-1.docker.io\registry:2如果宿主机本身也必须通过代理出网可以给这个容器补HTTP_PROXYHTTPS_PROXYNO_PROXY例如dockerrm-fkind-registry2/dev/null||truedockerrun-d--restartalways--namekind-registry-p5001:5000\-eREGISTRY_PROXY_REMOTEURLhttps://registry-1.docker.io\-eHTTP_PROXYhttp://宿主机可访问的代理地址:端口\-eHTTPS_PROXYhttp://宿主机可访问的代理地址:端口\-eNO_PROXYlocalhost,127.0.0.1,kind-registry,.svc,.cluster.local\registry:2这里的代理地址不要直接写宿主机的127.0.0.1而要写容器能够访问到的宿主机地址或可达代理地址。2. 在 kind 配置文件中加入本地 registrykind:ClusterapiVersion:kind.x-k8s.io/v1alpha4containerdConfigPatches:-|-[plugins.io.containerd.grpc.v1.cri.registry.mirrors.docker.io]endpoint [http://kind-registry:5000,https://docker.m.daocloud.io,https://docker.1ms.run]3. 重建集群kind delete cluster--namedemo kind create cluster--namedemo--configkind-config.yaml4. 把本地 registry 接到 kind 网络dockernetwork connect kind kind-registry2/dev/null||true5. 可选登记本地 registry 信息catEOF|kubectl apply-f-apiVersion: v1 kind: ConfigMap metadata: name: local-registry-hosting namespace: kube-public data: localRegistryHosting.v1: | host: localhost:5001 help: Local registry proxy cache for kind EOF6. 再次部署应用kubectl delete-fnginx-demo.yaml --ignore-not-found kubectl apply-fnginx-demo.yaml kubectl get pods-w九、如何确认故障原因无论用哪种方案真正排障时的顺序都建议固定为1. 先看 Pod 状态kubectl get pods2. 再看 Pod 详情kubectl describe podpod-name重点看EventsReasonFailed to pull image3. 再看最近事件kubectl get events --sort-by.lastTimestamp这样可以快速判断是网络问题是镜像名问题是权限问题还是代理或镜像源配置问题

更多文章