Kaniko ECR connection Issue & debug 心得

記錄讓 GitLab runner 上的 Kaniko 以 IRSA role 推送 image 到 ECR 失敗的除錯過程,最後發現是 IRSA trusted policy 設定問題。

發佈 ~2 分鐘 #container#ECR
  • 目的:讓 internal-internal 身上具備 IRSA role 的 gitlab runner pod,可以透過 role 的權限推送 image 至 ECR(instead of IAM user access key)

  • 官方教學 GitHub - GoogleContainerTools/kaniko: Build Container Images In Kubernetes

    • 給予需要推送 image 權限的 kaniko pod 所在的 instance 相關權限,可以使用 AWS managed policy: EC2InstanceProfileForImageBuilderECRContainerBuilds
    • 需設定以下環境變數:
      • AWS_SDK_LOAD_CONFIG=true:繼承 runner manager pod 相關的 AWS 環境變數
      • AWS_EC2_METADATA_DISABLED=true:當 kaniko 所在的 EC2 具有 instance profile 時需設定,讓 kaniko 可以去吃到正確的 credentials
    • 如果不使用 instance role 的話,可以將 EKS secret mount 到 kaniko pod 上
  • Kaniko 向 ECR 推 image 的工具:Amazone-ecr-credential-helper

    因為在 debug 的過程中,還是沒有權限可以推送 image 到 ECR,因此稍微去了解了一下 kaniko 是透過什麼機制來對 ECR 連線

    • 在 Configuration section 有提到應該要給予 /root/.docker/config.json 以下內容,:

      {
      	"credHelpers": {
      		"public.ecr.aws": "ecr-login",
      		"<aws_account_id>.dkr.ecr.<region>.amazonaws.com": "ecr-login"
      	}
      }

      其他 issue 討論會需要加這段設定也是基於這個 credential helper repo 來的:

    • 在 AWS credentials section 也有提及 AWS_SDK_LOAD_CONFIG 開啟後有以下三個效果

      • 透過環境變數 (role_arn and source_profile) 進行 assume role
      • 可使用 credential_process 所拿到的權限
      • 可以拿到 IRSA 的權限

不過在 debug 過程中,不管設定檔怎麼加都沒辦法讓 kaniko 正確推送 image,因此推測是更基本的權限問題沒有設定好。且 gitlab runner manager pod 和 job pod 裡面所包含的環境變數一致,因此下一步決定在 runner manager pod 測試 aws sts get-caller-identity 。但 runner manager pod 並沒有 awscli,安裝起來也很麻煩,因此決定仿照 runner pod 所使用的 service account,開一個 awscli pod 來做測試

  • Runner pod 環境變數有以下的內容,符合 amazon-ecr-credential-helper 裡面所提到需要的內容,也確實有拿到正確的 IRSA role

    AWS_DEFAULT_REGION=ap-northeast-1
    AWS_REGION=ap-northeast-1
    AWS_ROLE_ARN=arn:aws:iam::123456789012:role/gitlab-runner20250424045204511300000028
    AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token
    AWS_STS_REGIONAL_ENDPOINTS=regional
  • AWS_WEB_IDENTITY_TOKEN_FILE 這個路徑存的內容像是 Assume role 之後會有的 token,因此我把 token 內容拿到 local 存成 AWS_STS_TOKEN 測試 aws sts get-caller-identity 的結果。但因為 role 本身只允許 AssumeRoleWithWebIdentity,且我的 local 端沒辦法執行這件事(只能做一般的 assume role),因此這條路是失敗的

  • 接下來我執行一個 kaniko push image job,並讓他 sleep 3600 好讓我進去 debug,並且下了以下指令,檢查 kaniko 是否有已經可以連線的 ECR repo:

    /kaniko/docker-credential-ecr-login list # 正常應該返回物件,但我收到空值
  • 最後因為 job pod 也沒有 awscli 可以使用,因此開了一個 pod 仿照 gitlab-runner service account 的設定:

    apiVersion: v1
    kind: Pod
    metadata:
      name: awscli-test
      namespace: gitlab-runner
    spec:
      serviceAccountName: gitlab-runner-x86
      containers:
      - name: awscli
        image: amazon/aws-cli
        command: ["/bin/bash"]
        args: ["-c", "sleep 3600"]
        env:
        - name: AWS_REGION
          value: ap-east-1
      restartPolicy: Never
    kubectl apply -f awscli-test.yaml
    kubectl exec -it awscli-test -n gitlab-runner -- bash
    # in pod
    aws sts get-caller-identity
  • 結果發現是 IRSA trusted policy 沒有改允許 SA 的 namespace 和 SA 的名字 😇

心得:在使用廣泛流通的工具時,通常 debug 到後來如果開始鬼打牆,就要有警覺「既然這東西廣泛流通,且專案上並沒有什麼特別的 limitation」時,就不太可能需要追到太深的地方去 debug。更常出現的問題會是最基本的權限設定問題,或是一些基本的限制沒寫清楚(舉例:使用 vxlan 時忘記讓 security group allow UDP,因為忘記 UDP 和 TCP 是分開的兩個協定;或者像這次一樣 trusted policy 沒寫好)