The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

當 Kubernetes 或 MicroK8s 遇到 Docker Hub 下載限制時的解決之道

我這幾個星期在一間企業進行為期五天的 Kubernetes 教育訓練,我們的 Lab 環境都是用輕量級的 MicroK8s 為主,但是課程一開始就不太順利,因為上課人數有 20 位左右,他們都使用公司的網路,大家都共用一個 IP 連外,以致於碰上討人厭的 Rate limit on Docker Hub 問題,所以有些人無法成功架設起叢集,原因就是 image 下載不到造成的。這篇文章我就來說明在 MicroK8s 如何正確的解決 Docker Hub 無法下載 image 的問題。

問題檢測

當問題發生時,你用 kubectl get po 應該會看到 ErrImagePullImagePullBackOff 字樣,這代表 Image 在拉取的時候發生異常。

此時你可以用 kubectl describe po/$PODNAME 查看 Events 事件資料,藉此判斷問題發生的原因,錯誤訊息如下:

Warning Failed 19m (x2 over 19m) kubelet Failed to pull image "docker.io/calico/cni:v3.19.1": rpc error: code = Unknown desc = failed to pull and unpack image "docker.io/calico/cni:v3.19.1": failed to copy: httpReadSeeker: failed open: unexpected status code https://registry-1.docker.io/v2/calico/cni/manifests/sha256:f301171be0add870152483fcce71b28cafb8e910f61ff003032e9b1053b062c4: 429 Too Many Requests - Server message: toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit

這個 kubectl describe 是我們在 Kubernetes 做 Troubleshooing 的時候一定會用的命令!

解決方案

其實解決方案有非常多種,你可以從 MicroK8s 官網的 DockerHub download rate limits 文件看到各種解決方案。

  1. 直接設定登入 DockerHub 通過 pull image 的驗證
  2. 使用替代的 Registry 來源,透過編輯現有 Deployment 的內容來修正問題
  3. 使用自行架設的 Private image registry 並用代理伺服器的方式快取 DockerHub 的 image 內容

本篇文章我主要分享登入 DockerHub 的兩種標準作法。

解決方案一:調整 containerd 的 Docker Hub Registry 設定

由於 MicroK8s 主要採用 containerd 做為容器引擎 (Container Runtime),所以最一勞永逸的方式,就是直接調整「每個節點」的 containerd 設定,讓任何要從 Docker Hub 下載的 image 都可以輸入 Docker Hub 的認證資訊(帳號密碼),以解除 Docker Hub 的 Rate limit 限制。

以下是設定步驟:

  1. 先取得 Docker Hub 的帳號與密碼

    設定時建議不要直接使用你在 Docker Hub 的密碼,而是到 Docker Hub 的 Security 設定頁面,建立一個 Access Token 來當作密碼,建立時可以在 Access permissions 選擇 Read-only 即可,確保你的帳戶安全!

    Docker Hub > Settings > Security

  2. 編輯叢集節點中的 /var/snap/microk8s/current/args/containerd-template.toml 檔案

    multipass exec 'microk8s-vm' -- vi /var/snap/microk8s/current/args/containerd-template.toml
    

    剛進入 vim 編輯器的時候,由於 Vim 預設 theme 的設定會讓你看不太清楚部分文字,建議可以輸入以下命令讓文字可以顯示的更清晰:

    :set background=dark
    

    接著在該檔案的最下方,新增以下內容: (排版沒有很重要)

    [plugins."io.containerd.grpc.v1.cri".registry.configs."registry-1.docker.io".auth]
      username = "DOCKERHUB_Username"
      password = "DOCKERHUB_AccessToken"
    

    請務必在每個節點都做一樣的設定,但叢集在升級後是否會保留設定,我還沒測試過,我會等下次升級的時候測試看看!

    以下是另一種不用開 vim 就可以將內容加入 /var/snap/microk8s/current/args/containerd-template.toml 的技巧:

    先進入 VM 的 Shell 環境:

    multipass shell microk8s-vm
    

    再將內容一次加入:

    cat <<EOF >> /var/snap/microk8s/current/args/containerd-template.toml
      [plugins."io.containerd.grpc.v1.cri".registry.configs."registry-1.docker.io".auth]
        username = "DOCKERHUB_Username"
        password = "DOCKERHUB_AccessToken"
    EOF
    
  3. 重新啟動 MicroK8s 即可

    microk8s stop
    microk8s start
    
  4. 測試部署應用程式

    kubectl create deployment microbot --image=dontrebootme/microbot:v1
    kubectl expose deployment microbot --type=NodePort --port=80 --name=microbot-service
    kubectl describe pod
    

解決方案二:使用自訂的 Private Registry 當成 Image 來源

我們自己與我們的客戶很多都會自行架設 Private Registry 當成 Image 來源,像是 ACR (Azure Container Registry) 或 Harbor 等等。當你要部署自行製作並上傳到 Private Registry 的 Image 時,就一定需要通過身份驗證,否則你將無法下載任何 Image 回來。

以下是設定步驟:

  1. 先取得 Private Registry 的帳號與密碼

    這裡我一樣以 Docker Hub 為例,設定時建議不要直接使用你在 Docker Hub 的密碼,而是到 Docker Hub 的 Security 設定頁面,建立一個 Access Token 來當作密碼,建立時可以在 Access permissions 選擇 Read-only 即可,確保你的帳戶安全!

  2. 建立一個名為 regcredSecret 物件

    Bash

    kubectl create secret docker-registry regcred \
      --docker-server="https://index.docker.io/v1/" \
      --docker-username="<Username>" \
      --docker-password="<AccessToken>" \
      --docker-email="<Email>"
    

    PowerShell

    kubectl create secret docker-registry regcred `
      --docker-server="https://index.docker.io/v1/" `
      --docker-username="<Username>" `
      --docker-password="<AccessToken>" `
      --docker-email="<Email>"
    

    建立完成後,你可以透過以下命令看到實際的設定內容:

    kubectl get secret regcred --output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode | jq -C
    

    取得當初設定的帳號與密碼

    kubectl get secret regcred --output="jsonpath={.data.\.dockerconfigjson}" | base64 -d | jq '.auths."https://index.docker.io/v1/".username' -Cr
    kubectl get secret regcred --output="jsonpath={.data.\.dockerconfigjson}" | base64 -d | jq '.auths."https://index.docker.io/v1/".password' -Cr
    
  3. 在 Pod Template 之中的 Container Spec 指定 imagePullSecrets 欄位為 regcred 即可

    apiVersion: v1
    kind: Pod
    metadata:
      name: private-reg
    spec:
      containers:
        - name: private-reg-container
          image: <your-private-image>
      imagePullSecrets:
        - name: regcred
    

設定好之後,你的 Pod 在啟動時,就可以正確的從 Private Registry 下載 Image 了! 👍

相關連結