GKE の認証に関する問題のトラブルシューティング


このページでは、Google Kubernetes Engine(GKE)Autopilot クラスタと Standard クラスタのセキュリティ構成に関連する問題を解決する方法について説明します。

さらにサポートが必要な場合は、Cloud カスタマーケアにお問い合わせください。

RBAC と IAM

認証された IAM アカウントがクラスタ内アクションを実行できない

クラスタでアクションを実行しようとしたときに、GKE がアクションを認可する RBAC ポリシーを見つけられないと、次の問題が発生します。GKE は、同じ権限を付与する IAM 許可ポリシーを見つけようとします。失敗した場合、次のようなエラー メッセージが表示されます。

Error from server (Forbidden): roles.rbac.authorization.k8s.io is forbidden:
User "example-account@example-project.iam.gserviceaccount.com" 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 Namespace 内の roles オブジェクトに対する list 権限を持つロールを付与します。手順については、ロールベース アクセス制御を使用してクラスタ内のアクションを認可するをご覧ください。

GKE 用 Workload Identity 連携

Pod が Google Cloud で認証されない

アプリケーションが Google Cloud で認証されない場合は、次の設定が正しく構成されていることを確認してください。

  1. GKE クラスタを含むプロジェクトで IAM Service Account Credentials API が有効になっていることを確認します。

    IAM Credentials API を有効にする

  2. Workload Identity プールが設定されていることを確認して、クラスタで GKE 用 Workload Identity 連携が有効になっていることを確認します。

    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 クラスタの Namespace に置き換えます。
    • 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. クラスタ ネットワーク ポリシーを使用している場合は、バージョン 1.21.0-gke.1000 より前の GKE を実行しているクラスタのポート 988 で、127.0.0.1/32 への下り(外向き)が許可されていることを確認します。また、GKE バージョン 1.21.0-gke.1000 以降を実行しているクラスタのポート 988 で、169.254.169.252/32 への下り(外向き)が許可されていることを確認します。GKE Dataplane V2 を実行しているクラスタでは、ポート 80169.254.169.254/32 への下り(外向き)を許可する必要があります。

    kubectl describe networkpolicy NETWORK_POLICY_NAME
    

    NETWORK_POLICY_NAME は、GKE ネットワーク ポリシーの名前に置き換えます。

IAM サービス アカウントのアクセスが拒否される

IAM ロール バインディングを追加した直後に、GKE 用 Workload Identity 連携を使用して Pod がリソースにアクセスできない場合があります。アクセス障害は、IAM 許可ポリシー、ロール バインディング、Kubernetes Pod などのリソースが一緒に作成されるデプロイ パイプラインや宣言型の Google Cloud 構成で発生する可能性が高くなります。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 のアクセス権の変更の伝播が原因で発生することがあります。つまり、ロールの付与などのアクセス権の変更がシステム全体に伝播されるまでに時間がかかるということです。ロールの付与の場合、通常は反映に 2 分ほどかかりますが、7 分以上かかることもあります。詳細については、アクセス権の変更の伝播をご覧ください。

このエラーを解決するには、Pod が作成された後に Google Cloud リソースにアクセスする前に遅延を追加することを検討してください。

DNS 解決に関する問題

一部の Google Cloud クライアント ライブラリは、DNS 名 metadata.google.internal を解決することで GKE と Compute Engine のメタデータ サーバーに接続するように構成されています。これらのライブラリの場合、Google Cloud サービスに対するワークロードの認証には、正常なクラスタ内の DNS 解決が重要な依存関係となります。

この問題を検出する方法は、デプロイしたアプリケーションの詳細(ロギング構成など)によって異なります。次のようなエラー メッセージがないか確認します。

  • GOOGLE_APPLICATION_CREDENTIALS の構成を求めるエラー メッセージ、または
  • Google Cloud サービスへのリクエストが、認証情報がリクエストになかったために拒否されたことを示すエラー メッセージ。

metadata.google.internal の DNS 解決で問題が発生した場合は、環境変数 GCE_METADATA_HOST169.254.169.254 に設定することで、一部の Google Cloud クライアント ライブラリで DNS 解決をスキップするように指示できます。

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 ライブラリ:

  • Python
  • Java
  • Node.js
  • Golang(ただし、Golang クライアント ライブラリではすでに DNS 名ではなく IP で接続されています)。

Pod の起動時にタイムアウト エラーが発生する

GKE メタデータ サーバーが新しい Pod でリクエストの受信を開始できるようになるまでに数秒かかります。Pod が有効になってから最初の数秒間に GKE 用 Workload Identity 連携を使用して認証を試みると、タイムアウトが短いアプリケーションと Google Cloud クライアント ライブラリで認証に失敗する可能性があります。

タイムアウト エラーが発生した場合は、次のことを試してください。

  • ワークロードが使用する Google Cloud クライアント ライブラリを更新します。
  • 数秒待ってから再試行するようにアプリケーション コードを変更します。
  • Pod のメインコンテナを実行する前に、GKE メタデータ サーバーの準備が完了するまで待機する initContainer をデプロイします。

    たとえば、次のマニフェストは 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 用 Workload Identity 連携が失敗する

クラスタ コントロール プレーンが利用できない場合、メタデータ サーバーは GKE 用 Workload Identity 連携を返すことができません。メタデータ サーバーを呼び出すと、ステータス コード 500 が返されます。

ログ エクスプローラに、次のようなログエントリが表示されます。

dial tcp 35.232.136.58:443: connect: connection refused

この動作により、GKE 用 Workload Identity 連携が使用できなくなります。

IP のローテーション、コントロール プレーン VM のアップグレード、クラスタまたはノードプールのサイズ変更などのクラスタ メンテナンス時に、ゾーンクラスタでコントロール プレーンが使用できない場合があります。コントロール プレーンの可用性については、リージョンまたはゾーンのコントロール プレーンの選択をご覧ください。リージョン クラスタに切り替えると、この問題は解消されます。

Istio を使用するクラスタで GKE 用 Workload Identity 連携の認証が失敗する

アプリケーションの起動時にエンドポイントとの通信を試みると、次のようなエラーが表示されることがあります。

Connection refused (169.254.169.254:80)
Connection timeout

これらのエラーは、istio-proxy コンテナの準備が整う前にアプリケーションがネットワーク接続を試行した場合に発生することがあります。デフォルトでは、Istio と Cloud Service Mesh は、トラフィックをインターセプトしてリダイレクトするサービス メッシュ プロキシ ワークロードが実行されているかどうかに関係なく、ワークロードが起動するとすぐにリクエストを送信できるようにします。GKE 用 Workload Identity 連携を使用する Pod の場合、プロキシの起動前に発生するこれらの初期リクエストは、GKE メタデータ サーバーに到達しない可能性があります。その結果、Google Cloud APIs への認証が失敗します。リクエストを再試行するようにアプリケーションを構成しない場合、ワークロードが失敗する可能性があります。

この問題がエラーの原因であることを確認するには、ログを表示して、istio-proxy コンテナが正常に起動されたかどうかを確認します。

  1. Google Cloud コンソールで、[ログ エクスプローラ] ページに移動します。

    [ログ エクスプローラ] に移動

  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 timeout または connection 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
    

この問題を解決し、再発しないようにするには、次のワークロードごとの構成オプションのいずれかをお試しください。

  • プロキシ ワークロードの準備が整うまで、アプリケーションがリクエストを送信しないようにします。Pod 仕様の metadata.annotations フィールドに次のアノテーションを追加します。

    proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }'
    
  • GKE メタデータ サーバーの IP アドレスをリダイレクトから除外するように Istio または Cloud Service Mesh を構成します。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 アドレス範囲を追加します。

  • プロキシが起動するまでアプリケーションがリクエストを送信しないようにする: 値が trueglobal.proxy.holdApplicationUntilProxyStarts グローバル構成オプションを Istio 構成に追加します。

gke-metadata-server Pod がクラッシュしている

gke-metadata-server システム DaemonSet Pod により、ノードで GKE 用 Workload Identity 連携を利用できるようになります。Pod は、クラスタ内の Kubernetes サービス アカウントの数に比例してメモリリソースを使用します。

gke-metadata-server Pod のリソース使用量が上限を超えると、次の問題が発生します。kubelet がメモリ不足エラーで Pod を強制排除します。クラスタに 3,000 を超える Kubernetes サービス アカウントがある場合、この問題が発生することがあります。

問題を特定する方法は次のとおりです。

  1. kube-system Namespace でクラッシュしている 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 未満に減らします。

DeployPatch 失敗のエラー メッセージが表示され、GKE 用 Workload Identity 連携を有効にできない

GKE は、Google Cloud が管理する Kubernetes Engine サービス エージェントを使用して、クラスタ内の GKE 用 Workload Identity 連携が機能できるようにします。Google Kubernetes Engine API を有効にすると、Google Cloud がプロジェクトに対する Kubernetes Engine サービス エージェントのロール(roles/container.serviceAgent)を、このサービス エージェントに自動的に付与します。

サービス エージェントに Kubernetes Engine サービス エージェントのロールがないプロジェクトのクラスタで GKE 用 Workload Identity 連携を有効にしようとすると、次のようなエラー メッセージが表示され、オペレーションが失敗します。

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 は、実際の Google Cloud プロジェクト ID に置き換えます。

    サービス エージェントが正しく構成されている場合、出力にはサービス エージェントの完全な ID が表示されます。

    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. GKE 用 Workload Identity 連携をもう一度有効にしてみます。

次のステップ

さらにサポートが必要な場合は、Cloud カスタマーケアにお問い合わせください。