kubernetes镜像拉取失败解决方法 ErrImagePull

被这个问题卡住了最少一个下午的时间。。。。不过就当熟悉k8s的命令了吧。。。只能这么安慰自己了

最近在捣鼓k8s部署.net core的后端(我是在windows上部署docker desktop来做测试的),在拉取我阿里云镜像仓库的私有镜像,搞来搞去搞了好久都没用

dock pull registry.cn-hangzhou.aliyuncs.com/xxxx/servicea_repo 这个私有地址能拉取成功(意味着我的账号密码没输错)

一搜一堆的资料不说了,要注意  imagePullSecrets 这个值,要指定一个secret,要先用命令创建一个secret的资源,如下:

kubectl create secret docker-registry mysecretaliyun --docker-server=registry.cn-hangzhou.aliyuncs.com --docker-username=hi500000000@aliyun.com --docker-password=abdecc!@#!>!~ 

坑点:我的密码包含了">"这个字符串,用命令创建secret的时候,">"这个字符串以及它后面的字符串会被忽略,也就是说我在命令行输入的密码是 abdecc!@#!>!~  但是被保存到他们的配置文件的值却是 abdecc!@#!>!~  ,所以会死活都没用,因为密码根本没对

验证保存在secret里面的密码和你实际是否有出入的方法:

你设定好secret后,使用命令:

kubectl get secret mysecretaliyun -o yaml

户出现一个json,把  .dockerconfigjson: 后面的base64的值复制下来,找一个base64解码工具解码一下这个值,就可以看到实际被保存的密码,我当初要是知道这么核对一下,我就不会卡住半天了,base64一解码就发现我的密码变为abdecc!@#! 而不是我原来输入的  abdecc!@#!>!~  

这个坑点在于要么不允许输入,允许输入就全盘接受,结果允许我输入直接截断我的输入,真的是神奇了,我估计是因为我是windows 部署的原因?没去linux系统上论证,反正windows上 > 符号以及它后面的字符串都被截断没设置进去了。

以下是找问题的时候,发现一篇文章介绍,就是跟着这篇文章去做的,一开始用chatgpt,chatgpt没有教我这么去验证(估计它也不知道密码会被截断),有一说一,chatgpt查资料是又快又准,除非遇到这种实在没办法的问题,才会用搜索引擎了,整体学k8s和docker,已经能打包自己镜像,上传镜像,k8s部署,Ingress访问等等,这些除了这个问题chatgpt没搞定,其他全部是chatgpt教的,真的没想过有一天会有这么一个好用的工具出现在这个世界上,让获取知识变得简单了很多很多(最起码写代码学代码又快又准了)

 

Docker Hub以及利用开源harbor项目搭建的镜像仓库服务,对于Docker Client发起的docker login、docker push、docker pull等命令都会做基本的用户认证, 最简单常用的认证方式就是Basic Auth,即在发起的http请求头中添加一个Authorization,其值为base64(username:password),当前Docker Client都是这么处理。

在Kubernetes中,Secret资源对象用来存储和管理一些敏感信息,比如密码、Auth Token以及SSH keys,把这些敏感信息放入Secret对象中,相对来说更安全更灵活。 Kubernetes可以通过环境变量、文件挂载等方式将Secret信息推到每一个Pod中,通过文件挂载形式还能使Secret在pod中实时更新,Kubernetes统一管理。

Kubernetes中调度Pod成功之后,则开始拉取指定镜像启动容器,在Deployment对象中有几个与镜像拉取相关的重要配置参数。

  • spec.template.spec.containers[n].image,容器启动时的镜像
  • spec.template.spec.imagePullSecrets,Secret中定义了镜像所在仓库的用户名密码
  • spec.template.spec.containers[n].imagePullPolicy,定义了镜像拉取策略

imagePullPolicy决定了是否发起镜像下拉请求,它的值范围Always、Never、IfNotPresent,默认为IfNotPresent,但标签为:latest的镜像默认为Always。

  • Always,不管宿主机上镜像是否存在,都会发起一次下拉镜像请求
  • Never,不管宿主机上镜像是否存在,都不会发起下拉镜像请求
  • IfNotPresent,如果宿主机上镜像不存在,则向仓库发起下拉镜像请求

在Kubernetes中执行应用部署命令之后,通过命令kubectl get pods查看pod状态时,经常会遇见Pod的状态是ErrImagePull或者ImagePullBackOff,出现这种情况就一步一步分析。

kube-system       monitor-6c7fdcd477-jvqjc                 0/1       ImagePullBackOff   0          1h
  1. 执行命令

    kubectl describe pod monitor-6c7fdcd477-jvqjc -n kube-system
    

    或者kubectl get events,查看是否能发现具体的错误信息,由于kubernetes中错误信息不是很明显,通常只会展示error。

  2. 在宿主机上执行docker login 检查用户名密码是否正确,接着执行docker pull imageId,操作均成功,表示镜像仓库服务正常。

  3. 由于kubernetes中的pod网络可能与宿主机网络不一致,进入某个kubernetes的pod中,可以通过部署curl的pod用于网络测试,检查镜像仓库地址是否预期一致,不一致请把镜像仓库正确的域名或者host配置在集群dns中。

  4. 查看应用部署中的deployment对应yaml中的imagePullPolicy,如果机器上无镜像,同时imagePullPolicy为Never,则镜像无法拉取。

  5. 查看deployment对应yaml中的imagePullSecrets,其中的name就是secret的名字,如果拉取的是私有镜像,imagePullSecrets是必须的,没有secret,拉取镜像时请求 仓库的http请求头Authorization则为空,仓库授权校验肯定不通过直接返回401错误,而kubernetes则可能直接显示error。 注意secret是区分namespace的,容器启动时都是使用当前容器所在pod的namespace中的secret,执行命令检查secret是否存在。

    kubectl get secret xxx -n kube-system -o yaml
    
  6. 当前namespace下对应的secret也存在,那就继续检查secret中的信息,取出上一步执行结果中显示的dockercfg字段对应的value值,应该是一长串base64编码的字符串, 类似eyJodWIua2NlLmtzeXVeikkskseYELSH8sse,解码看一下具体的信息。

    echo 'eyJodWIua2NlLmtzeXVeikkskseYELSH8sse' | base64 --decode
    

    解码之后数据(mock数据,并非真实数据)

    {"hub.test.company.com":{"username":"98766743","password":"somebaby","email":"localhost","auth":"xxeieESrweSXs="}}
    

    检查用户名、密码是否正确,用户名密码正确,还要查看域名是否与image包含的域名一致,如果deployment对应的yaml中的image为hub.test2.company.com/nginx/nginx:1.12.1, 如果这样,即使用户名、密码正确,kubernetes也不会将Authorization放在请求仓库的http header中,也会导致镜像下载失败。 7. 发现secret中信息不对,则可以将该secret删除,然后重新创建,假设deployment中对应的imagePullSecrets中的name为hub.test.company.com.key。 删除secret

    kubectl delete secret hub.test.company.com.key -n kube-system
    

    新建secret

    kubectl create secret docker-registry hub.test.company.com.key -n kube-system --docker-server=hub.test.company.com  
    --docker-username=2380997 --docker-password=RABC123456 --docker-email=test@company.com
    

    注意创建时没指定namespace,那么默认为default。secret创建成功之后,重新部署即可。

posted @ 2024-11-13 15:30  LoveCoder  阅读(63)  评论(0编辑  收藏  举报