kubernetes映象拉取失敗解決方法 ErrImagePull

LoveCoder發表於2024-11-13

被這個問題卡住了最少一個下午的時間。。。。不過就當熟悉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建立成功之後,重新部署即可。

相關文章