排解 GKE 驗證問題


本頁說明如何解決 Google Kubernetes Engine (GKE) Autopilot 和 Standard 叢集中的安全性設定相關問題。

RBAC 和 IAM

通過驗證的 IAM 帳戶無法執行叢集內動作

當您嘗試在叢集中執行動作,但 GKE 找不到授權該動作的 RBAC 政策時,就會發生下列問題。GKE 會嘗試尋找授予相同權限的 IAM 允許政策。如果失敗,您會看到類似以下的錯誤訊息:

Error from server (Forbidden): roles.rbac.authorization.k8s.io is forbidden:
User "[email protected]" cannot list resource "roles" in
API group "rbac.authorization.k8s.io" in the namespace "kube-system": requires
one of ["container.roles.list"] permission(s).

如要解決這個問題,請使用 RBAC 政策授予嘗試執行的動作權限。舉例來說,如要解決先前範例中的問題,請在 kube-system 命名空間中,將具有 list 權限的角色授予 roles 物件。如需操作說明,請參閱使用角色型存取權控管授權叢集中的動作

Workload Identity Federation for GKE

Pod 無法向 Google Cloud驗證

如果應用程式無法向 Google Cloud進行驗證,請確認下列設定是否正確:

  1. 確認您已在包含 GKE 叢集的專案中啟用 IAM 服務帳戶憑證 API。

    啟用 IAM 憑證 API

  2. 確認叢集已啟用 GKE 適用的工作負載身分聯盟,方法是驗證叢集是否已設定工作負載身分集區:

    gcloud container clusters describe CLUSTER_NAME \
        --format="value(workloadIdentityConfig.workloadPool)"
    

    CLUSTER_NAME 替換為 GKE 叢集的名稱。

    如果您尚未指定 gcloud 的預設區域或地區,執行這項指令時,可能也需要指定 --region--zone 旗標。

  3. 請確認應用程式執行的節點集區已設定 GKE 中繼資料伺服器:

    gcloud container node-pools describe NODEPOOL_NAME \
        --cluster=CLUSTER_NAME \
        --format="value(config.workloadMetadataConfig.mode)"
    

    更改下列內容:

    • NODEPOOL_NAME 替換為節點集區的名稱。
    • CLUSTER_NAME 改成 GKE 叢集名稱。
  4. 確認 Kubernetes 服務帳戶是否已正確註解:

    kubectl describe serviceaccount \
        --namespace NAMESPACE KSA_NAME
    

    更改下列內容:

    • NAMESPACE,並換成 GKE 叢集的命名空間。
    • KSA 改為 Kubernetes 服務帳戶的名稱。

    預期輸出內容應包含類似下列的註解:

    iam.gke.io/gcp-service-account: GSA_NAME@PROJECT_ID.iam.gserviceaccount.com
    
  5. 確認 IAM 服務帳戶設定正確無誤:

    gcloud iam service-accounts get-iam-policy \
        GSA_NAME@GSA_PROJECT.iam.gserviceaccount.com
    

    預期輸出內容應包含類似下列內容的繫結:

    - members:
      - serviceAccount:PROJECT_ID.svc.id.goog[NAMESPACE/KSA_NAME]
      role: roles/iam.workloadIdentityUser
    
  6. 如果您有叢集網路政策,則必須允許叢集在 988 連接埠上輸出至 127.0.0.1/32 (適用於執行 GKE 1.21.0-gke.1000 之前的版本),或在 988 連接埠上輸出至 169.254.169.252/32 (適用於執行 GKE 1.21.0-gke.1000 以上版本的叢集)。對於執行 GKE Dataplane V2 的叢集,您必須允許通訊埠 80 上的 169.254.169.254/32 輸出流量。

    kubectl describe networkpolicy NETWORK_POLICY_NAME
    

    NETWORK_POLICY_NAME 替換為 GKE 網路政策的名稱。

存取 IAM 服務帳戶遭拒

新增 IAM 角色繫結後,Pod 可能無法立即透過 Workload Identity Federation for GKE 存取資源。在部署管道或宣告式 Google Cloud 設定中,如果同時建立 IAM 允許政策、角色繫結和 Kubernetes Pod 等資源,就更有可能發生存取失敗的情況。Pod 記錄檔中會顯示下列錯誤訊息:

HTTP/403: generic::permission_denied: loading: GenerateAccessToken("SERVICE_ACCOUNT_NAME@PROJECT_ID.iam.gserviceaccount.com", ""): googleapi: Error 403: Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist).

這個錯誤可能是因為 IAM 存取權變更傳播所致,也就是說,角色授予等存取權變更需要一段時間才能傳播至整個系統。角色授予作業通常約需兩分鐘才能完成,但有時可能需要七分鐘以上。詳情請參閱「存取權變更傳播」。

如要解決這個錯誤,請考慮在 Pod 嘗試存取 Google Cloud 資源之前新增延遲時間。

DNS 解析問題

本節說明如何找出並解決 Pod 至 Google Cloud API 的連線錯誤,這類錯誤是由 DNS 解析問題所導致。如果本節的步驟無法解決連線錯誤,請參閱「Pod 啟動時發生逾時錯誤」一節。

部分 Google Cloud 用戶端程式庫會解析 DNS 名稱 metadata.google.internal,藉此連線至 GKE 和 Compute Engine 中繼資料伺服器。對於這些程式庫,叢集內 DNS 解析的健康狀態是工作負載向Google Cloud 服務驗證身分的重要依附元件。

偵測這個問題的方式取決於已部署應用程式的詳細資料,包括記錄設定。尋找錯誤訊息,確認是否需要設定 GOOGLE_APPLICATION_CREDENTIALS、要求遭拒 (因為要求沒有憑證),或是找不到中繼資料伺服器。Google Cloud

舉例來說,下列錯誤訊息可能表示 DNS 解析發生問題:

ComputeEngineCredentials cannot find the metadata server. This is likely because code is not running on Google Compute Engine

如果 metadata.google.internal 的 DNS 解析發生問題,可以指示部分 Google Cloud 用戶端程式庫略過 DNS 解析,方法是將環境變數 GCE_METADATA_HOST 設為 169.254.169.254

apiVersion: v1
kind: Pod
metadata:
  name: example-pod
  namespace: default
spec:
  containers:
  - image: debian
    name: main
    command: ["sleep", "infinity"]
    env:
    - name: GCE_METADATA_HOST
      value: "169.254.169.254"

這是中繼資料服務在 Google Cloud 運算平台一律可用的硬式編碼 IP 位址。

系統支援下列 Google Cloud 程式庫:

Pod 啟動時發生逾時錯誤

GKE 中繼資料伺服器需要幾秒鐘,才能開始接受新 Pod 的要求。如果應用程式和 Google Cloud 用戶端程式庫設定的逾時時間較短,在 Pod 生命週期的前幾秒內,嘗試使用 GKE 的 Workload Identity Federation 進行驗證可能會失敗。

如果發生逾時錯誤,請嘗試下列做法:

  • 更新工作負載使用的 Google Cloud 用戶端程式庫。
  • 變更應用程式程式碼,等待幾秒後再重試。
  • 部署 initContainer,等待 GKE 中繼資料伺服器準備就緒,再執行 Pod 的主要容器。

    舉例來說,以下資訊清單適用於含有 initContainer 的 Pod:

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-with-initcontainer
    spec:
      serviceAccountName: KSA_NAME
      initContainers:
      - image:  gcr.io/google.com/cloudsdktool/cloud-sdk:alpine
        name: workload-identity-initcontainer
        command:
        - '/bin/bash'
        - '-c'
        - |
          curl -sS -H 'Metadata-Flavor: Google' 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token' --retry 30 --retry-connrefused --retry-max-time 60 --connect-timeout 3 --fail --retry-all-errors > /dev/null && exit 0 || echo 'Retry limit exceeded. Failed to wait for metadata server to be available. Check if the gke-metadata-server Pod in the kube-system namespace is healthy.' >&2; exit 1
      containers:
      - image: gcr.io/your-project/your-image
        name: your-main-application-container
    

控制層無法使用,導致 GKE 的工作負載身分聯盟失敗

叢集控制層無法使用時,中繼資料伺服器無法傳回 GKE 適用的 Workload Identity Federation。對中繼資料伺服器的呼叫會傳回狀態碼 500。

記錄檔探索工具中可能會顯示類似下列內容的記錄項目:

dial tcp 35.232.136.58:443: connect: connection refused

這項行為會導致 GKE 無法使用工作負載身分聯盟。

在區域叢集中,控制層在叢集維護期間可能會無法使用,這些維護工作包括輪替 IP、升級控制層 VM 或調整叢集或節點集區大小等情況。如要瞭解控制層的可用性,請參閱「選擇地區或區域控制層」。切換至區域叢集即可解決這個問題。

在叢集中使用 Istio 時,GKE 的 Workload Identity Federation 驗證失敗

應用程式啟動並嘗試與端點通訊時,您可能會看到類似下列的錯誤:

Connection refused (169.254.169.254:80)
Connection timeout

如果應用程式在 istio-proxy 容器準備就緒前嘗試建立網路連線,就可能發生這些錯誤。根據預設,Istio 和 Cloud Service Mesh 允許工作負載在啟動後立即傳送要求,無論攔截及重新導向流量的服務網格 Proxy 工作負載是否正在執行,對於使用 GKE 適用的工作負載身分聯盟的 Pod,這些在 Proxy 啟動前發生的初始要求可能無法送達 GKE 中繼資料伺服器。因此,API 驗證會失敗。 Google Cloud 如果未將應用程式設為重試要求,工作負載可能會失敗。

如要確認這個問題是否為錯誤原因,請查看記錄並檢查 istio-proxy 容器是否已順利啟動:

  1. 前往 Google Cloud 控制台的「Logs Explorer」頁面。

    前往記錄檔探索工具

  2. 在查詢窗格中,輸入下列查詢:

    (resource.type="k8s_container"
    resource.labels.pod_name="POD_NAME"
    textPayload:"Envoy proxy is ready" OR textPayload:"ERROR_MESSAGE")
    OR
    (resource.type="k8s_pod"
    logName:"events"
    jsonPayload.involvedObject.name="POD_NAME")
    

    更改下列內容:

    • POD_NAME:受影響工作負載的 Pod 名稱。
    • ERROR_MESSAGE:應用程式收到的錯誤 (connection timeoutconnection refused)。
  3. 點選「執行查詢」

  4. 查看輸出內容,並檢查 istio-proxy 容器何時準備就緒。

    在以下範例中,應用程式嘗試發出 gRPC 呼叫。不過,由於 istio-proxy 容器仍在初始化,應用程式收到 Connection refused 錯誤。Envoy proxy is ready 訊息旁邊的時間戳記表示 istio-proxy 容器何時準備好處理連線要求:

    2024-11-11T18:37:03Z started container istio-init
    2024-11-11T18:37:12Z started container gcs-fetch
    2024-11-11T18:37:42Z Initializing environment
    2024-11-11T18:37:55Z Started container istio-proxy
    2024-11-11T18:38:06Z StatusCode="Unavailable", Detail="Error starting gRPC call. HttpRequestException: Connection refused (169.254.169.254:80)
    2024-11-11T18:38:13Z Envoy proxy is ready
    

如要解決這個問題並避免再次發生,請嘗試下列任一工作負載設定選項:

  • 在 Proxy 工作負載準備就緒前,請勿讓應用程式傳送要求。在 Pod 規格的 metadata.annotations 欄位中新增下列註解:

    proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }'
    
  • 設定 Istio 或 Cloud Service Mesh,將 GKE 中繼資料伺服器的 IP 位址排除在重新導向範圍之外。在 Pod 規格的 metadata.annotations 欄位中新增下列註解:

    traffic.sidecar.istio.io/excludeOutboundIPRanges: 169.254.169.254/32
    

在開放原始碼 Istio 中,您可以選擇設定下列任一全域設定選項,為所有 Pod 減輕這個問題:

  • 從重新導向中排除 GKE 中繼資料伺服器 IP 位址: 更新global.proxy.excludeIPRanges全域設定選項, 新增 169.254.169.254/32 IP 位址範圍。

  • 防止應用程式傳送要求,直到 Proxy 啟動為止: 在 Istio 設定中加入 global.proxy.holdApplicationUntilProxyStarts 全域設定選項,值為 true

gke-metadata-server Pod 當機

gke-metadata-server 系統 DaemonSet Pod 可在節點上啟用 GKE 適用的工作負載身分聯盟。Pod 使用的記憶體資源與叢集中的 Kubernetes 服務帳戶數量成正比。

gke-metadata-server Pod 的資源用量超出限制時,就會發生下列問題。Kubelet 會撤銷發生記憶體不足錯誤的 Pod。 如果叢集有超過 3,000 個 Kubernetes 服務帳戶,就可能發生這個問題。

如要找出問題,請按照下列步驟操作:

  1. kube-system 命名空間中找出當機的 gke-metadata-server Pod:

    kubectl get pods -n=kube-system | grep CrashLoopBackOff
    

    輸出結果會與下列內容相似:

    NAMESPACE     NAME                        READY     STATUS             RESTARTS   AGE
    kube-system   gke-metadata-server-8sm2l   0/1       CrashLoopBackOff   194        16h
    kube-system   gke-metadata-server-hfs6l   0/1       CrashLoopBackOff   1369       111d
    kube-system   gke-metadata-server-hvtzn   0/1       CrashLoopBackOff   669        111d
    kube-system   gke-metadata-server-swhbb   0/1       CrashLoopBackOff   30         136m
    kube-system   gke-metadata-server-x4bl4   0/1       CrashLoopBackOff   7          15m
    
  2. 說明當機的 Pod,確認原因是記憶體不足而遭驅逐:

    kubectl describe pod POD_NAME --namespace=kube-system | grep OOMKilled
    

    POD_NAME 替換為要檢查的 Pod 名稱。

如要還原 GKE 中繼資料伺服器的功能,請將叢集中的服務帳戶數量減少至 3,000 個以下。

啟用 GKE 適用的工作負載身分聯盟時,系統顯示「DeployPatch failed」錯誤訊息

GKE 會使用 Google Cloud代管的 Kubernetes Engine 服務代理程式 ,在叢集中啟用 Workload Identity Federation for GKE。啟用 Google Kubernetes Engine API 時,系統會自動將 Kubernetes Engine 服務代理程式角色 (roles/container.serviceAgent) 授予這個服務代理程式。 Google Cloud

如果您嘗試在專案的叢集上啟用 GKE 的工作負載身分聯合,但服務代理程式沒有 Kubernetes Engine 服務代理程式角色,作業就會失敗,並顯示類似以下的錯誤訊息:

Error waiting for updating GKE cluster workload identity config: DeployPatch failed

如要解決這個問題,請嘗試下列步驟:

  1. 檢查專案中是否有服務代理程式,以及設定是否正確:

    gcloud projects get-iam-policy PROJECT_ID \
        --flatten=bindings \
        --filter=bindings.role=roles/container.serviceAgent \
        --format="value[delimiter='\\n'](bindings.members)"
    

    PROJECT_ID 替換為專案 ID。 Google Cloud

    如果服務代理程式設定正確,輸出內容會顯示服務代理程式的完整身分:

    serviceAccount:service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com
    

    如果輸出內容未顯示服務代理程式,您必須授予 Kubernetes Engine 服務代理程式角色。如要授予這個角色,請完成下列步驟。

  2. 取得 Google Cloud 專案編號:

    gcloud projects describe PROJECT_ID \
        --format="value(projectNumber)"
    

    輸出結果會與下列內容相似:

    123456789012
    
  3. 將角色授予服務代理:

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member=serviceAccount:service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com \
        --role=roles/container.serviceAgent \
        --condition=None
    

    PROJECT_NUMBER 替換為專案編號。 Google Cloud

  4. 再次嘗試啟用 Workload Identity Federation for GKE。

後續步驟