このページでは、Google Kubernetes Engine(GKE)の使用時に発生する可能性のある 400、401、403、404 エラーを解決する方法について説明します。
問題: 認証 / 認可エラー
GKE クラスタに接続すると、HTTP ステータス コード 401 (Unauthorized) の認証 / 認可エラーが発生することがあります。この問題は、ローカル環境から GKE クラスタで kubectl コマンドを実行しようとしたときに発生することがあります。
この問題の原因は、次のいずれかである可能性があります。
- gke-gcloud-auth-plugin認証プラグインが正しくインストールされていないか、構成されていません。
- クラスタ API サーバーに接続し、kubectlコマンドを実行するための権限がありません。
原因を診断するには、次のセクションの手順を完了します。
curl を使用してクラスタに接続する
認証 / 認可エラーの原因を診断するには、curl を使用してクラスタに接続します。curl を使用すると、kubectl コマンドライン ツールと gke-gcloud-auth-plugin プラグインがバイパスされます。
- 環境変数を設定します。 - APISERVER=https://$(gcloud container clusters describe CLUSTER_NAME \ --location=COMPUTE_LOCATION --format "value(endpoint)") TOKEN=$(gcloud auth print-access-token)
- アクセス トークンが有効であることを確認します。 - curl https://oauth2.googleapis.com/tokeninfo?access_token=$TOKEN- 有効なアクセス トークンがある場合、このコマンドによって Google の OAuth 2.0 サーバーにリクエストが送信され、サーバーからトークンに関する情報が返されます。 
- API サーバーのコア API エンドポイントに接続してみます。 - # Get cluster CA certificate gcloud container clusters describe CLUSTER_NAME \ --location=COMPUTE_LOCATION \ --format "value(masterAuth.clusterCaCertificate)" | \ base64 -d > /tmp/ca.crt # Make API call with authentication and CA certificate curl -s -X GET "${APISERVER}/api/v1/namespaces" \ --header "Authorization: Bearer $TOKEN" \ --cacert /tmp/ca.crt- curlコマンドが成功すると、Namespace のリストが表示されます。kubeconfig でプラグインを構成するセクションの手順に沿って、プラグインが原因かどうかを確認します。- curlコマンドが次のような出力で失敗した場合は、クラスタにアクセスするための適切な権限がありません。- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "Unauthorized", "reason": "Unauthorized", "code": 401 }- この問題を解決するには、管理者に問い合わせて、クラスタにアクセスするための適切な権限を取得してください。 
kubeconfig でプラグインの使用を構成する
クラスタへの接続時に認証 / 認可エラーが発生した場合に、curl を使用してクラスタに接続できたときは、gke-gcloud-auth-plugin プラグインを使用せずにクラスタにアクセスできるかどうかを確認します。
この問題を解決するには、クラスタに対する認証時に gke-gcloud-auth-plugin バイナリを無視するようにローカル環境を構成します。バージョン 1.25 以降を実行している Kubernetes クライアントでは、gke-gcloud-auth-plugin バイナリが必須であるため、kubectl コマンドライン ツールにはバージョン 1.24 以前を使用する必要があります。
プラグインを使用せずにクラスタにアクセスする手順は次のとおりです。
- curlを使用して、バージョン 1.24 以前の- kubectlコマンドライン ツールをインストールします。次の例では、バージョン 1.24 のツールをインストールします。- curl -LO https://dl.k8s.io/release/v1.24.0/bin/linux/amd64/kubectl
- テキスト エディタでシェル起動スクリプト ファイルを開きます。たとえば、Bash シェルの - .bashrcを開きます。- vi ~/.bashrc- macOS を使用している場合は、これらの手順で - .bashrcではなく- ~/.bash_profileを使用します。
- 起動スクリプト ファイルに次の行を追加して保存します。 - export USE_GKE_GCLOUD_AUTH_PLUGIN=False
- 起動スクリプトを実行します。 - source ~/.bashrc
- クラスタの認証情報を取得します。これにより、 - .kube/configファイルが設定されます。- gcloud container clusters get-credentials CLUSTER_NAME \ --location=COMPUTE_LOCATION- 次のように置き換えます。 - CLUSTER_NAME: クラスタの名前。
- COMPUTE_LOCATION: Compute Engine のロケーション。
 
- kubectlコマンドを実行します。例:- kubectl cluster-info- これらのコマンドを実行した後に 401 エラーまたは同様の認可エラーが発生した場合は、適切な権限があることを確認し、エラーを返した手順を再実行します。 
エラー 400: ノードプールの再作成が必要
コントロール プレーンとノードを再作成するアクションを実行しようとすると、次のエラーが発生することがあります。
ERROR: (gcloud.container.clusters.update) ResponseError: code=400, message=Node pool "test-pool-1" requires recreation.
たとえば、進行中の認証情報のローテーションを完了したときに、このエラーが発生することがあります。
バックエンドでは、ノードプールが再作成対象としてマークされていますが、実際の再作成オペレーションの開始には時間がかかることがあります。これが原因で、GKE がクラスタ内の 1 つ以上のノードプールをまだ再作成していない場合、オペレーションは失敗します。
この問題を解決するには、次のいずれかの解決策を選択します。
- 再作成が完了するまで待ちます。既存のメンテナンスの時間枠や除外などの要因に応じて、完了までに数時間、数日、あるいは数週間かかることがあります。
- コントロール プレーンと同じバージョンへのアップグレードを開始して、影響を受けるノードプールの再作成を手動で開始します。 - 再作成を開始するには、次のコマンドを実行します。 - gcloud container clusters upgrade CLUSTER_NAME \ --node-pool=POOL_NAME- アップグレードが完了したら、操作をやり直してください。 
エラー 401: Unauthorized
重要な権限のノード サービス アカウントが存在するクラスタを特定する
ノード サービス アカウントに重要な権限がないクラスタを特定するには、NODE_SA_MISSING_PERMISSIONS recommender サブタイプの GKE 推奨事項を使用します。
- Google Cloud コンソールを使用します。[Kubernetes クラスタ] ページに移動し、特定のクラスタの [通知] 列で「重要な権限を付与する」の推奨事項を確認します。
- gcloud CLI または Recommender API を使用して、 - NODE_SA_MISSING_PERMISSIONSRecommender サブタイプを指定します。- 推奨事項をクエリするには、次のコマンドを実行します。 - gcloud recommender recommendations list \ --recommender=google.container.DiagnosisRecommender \ --location LOCATION \ --project PROJECT_ID \ --format yaml \ --filter="recommenderSubtype:NODE_SA_MISSING_PERMISSIONS"
推奨事項が表示されるまでに、最長で 24 時間ほどかかることがあります。手順については、分析情報と推奨事項を表示する方法をご覧ください。
この推奨事項を実装するには、ノード サービス アカウントに roles/container.defaultNodeServiceAccount ロールを付与します。
プロジェクトの Standard クラスタと Autopilot クラスタのノードプールで、GKE に必要な権限のないノード サービス アカウントを検索するスクリプトを実行できます。このスクリプトでは、gcloud CLI と jq ユーティリティを使用します。スクリプトを表示するには、次のセクションを開きます。
スクリプトを表示する
#!/bin/bash
# Set your project ID
project_id=PROJECT_ID
project_number=$(gcloud projects describe "$project_id" --format="value(projectNumber)")
declare -a all_service_accounts
declare -a sa_missing_permissions
# Function to check if a service account has a specific permission
# $1: project_id
# $2: service_account
# $3: permission
service_account_has_permission() {
  local project_id="$1"
  local service_account="$2"
  local permission="$3"
  local roles=$(gcloud projects get-iam-policy "$project_id" \
          --flatten="bindings[].members" \
          --format="table[no-heading](bindings.role)" \
          --filter="bindings.members:\"$service_account\"")
  for role in $roles; do
    if role_has_permission "$role" "$permission"; then
      echo "Yes" # Has permission
      return
    fi
  done
  echo "No" # Does not have permission
}
# Function to check if a role has the specific permission
# $1: role
# $2: permission
role_has_permission() {
  local role="$1"
  local permission="$2"
  gcloud iam roles describe "$role" --format="json" | \
  jq -r ".includedPermissions" | \
  grep -q "$permission"
}
# Function to add $1 into the service account array all_service_accounts
# $1: service account
add_service_account() {
  local service_account="$1"
  all_service_accounts+=( ${service_account} )
}
# Function to add service accounts into the global array all_service_accounts for a Standard GKE cluster
# $1: project_id
# $2: location
# $3: cluster_name
add_service_accounts_for_standard() {
  local project_id="$1"
  local cluster_location="$2"
  local cluster_name="$3"
  while read nodepool; do
    nodepool_name=$(echo "$nodepool" | awk '{print $1}')
    if [[ "$nodepool_name" == "" ]]; then
      # skip the empty line which is from running `gcloud container node-pools list` in GCP console
      continue
    fi
    while read nodepool_details; do
      service_account=$(echo "$nodepool_details" | awk '{print $1}')
      if [[ "$service_account" == "default" ]]; then
        service_account="${project_number}-compute@developer.gserviceaccount.com"
      fi
      if [[ -n "$service_account" ]]; then
        printf "%-60s| %-40s| %-40s| %-10s| %-20s\n" $service_account $project_id  $cluster_name $cluster_location $nodepool_name
        add_service_account "${service_account}"
      else
        echo "cannot find service account for node pool $project_id\t$cluster_name\t$cluster_location\t$nodepool_details"
      fi
    done <<< "$(gcloud container node-pools describe "$nodepool_name" --cluster "$cluster_name" --zone "$cluster_location" --project "$project_id" --format="table[no-heading](config.serviceAccount)")"
  done <<< "$(gcloud container node-pools list --cluster "$cluster_name" --zone "$cluster_location" --project "$project_id" --format="table[no-heading](name)")"
}
# Function to add service accounts into the global array all_service_accounts for an Autopilot GKE cluster
# Autopilot cluster only has one node service account.
# $1: project_id
# $2: location
# $3: cluster_name
add_service_account_for_autopilot(){
  local project_id="$1"
  local cluster_location="$2"
  local cluster_name="$3"
  while read service_account; do
      if [[ "$service_account" == "default" ]]; then
        service_account="${project_number}-compute@developer.gserviceaccount.com"
      fi
      if [[ -n "$service_account" ]]; then
        printf "%-60s| %-40s| %-40s| %-10s| %-20s\n" $service_account $project_id  $cluster_name $cluster_location $nodepool_name
        add_service_account "${service_account}"
      else
        echo "cannot find service account" for cluster  "$project_id\t$cluster_name\t$cluster_location\t"
      fi
  done <<< "$(gcloud container clusters describe "$cluster_name" --location "$cluster_location" --project "$project_id" --format="table[no-heading](autoscaling.autoprovisioningNodePoolDefaults.serviceAccount)")"
}
# Function to check whether the cluster is an Autopilot cluster or not
# $1: project_id
# $2: location
# $3: cluster_name
is_autopilot_cluster() {
  local project_id="$1"
  local cluster_location="$2"
  local cluster_name="$3"
  autopilot=$(gcloud container clusters describe "$cluster_name" --location "$cluster_location" --format="table[no-heading](autopilot.enabled)")
  echo "$autopilot"
}
echo "--- 1. List all service accounts in all GKE node pools"
printf "%-60s| %-40s| %-40s| %-10s| %-20s\n" "service_account" "project_id" "cluster_name" "cluster_location" "nodepool_name"
while read cluster; do
  cluster_name=$(echo "$cluster" | awk '{print $1}')
  cluster_location=$(echo "$cluster" | awk '{print $2}')
  # how to find a cluster is a Standard cluster or an Autopilot cluster
  autopilot=$(is_autopilot_cluster "$project_id" "$cluster_location" "$cluster_name")
  if [[ "$autopilot" == "True" ]]; then
    add_service_account_for_autopilot "$project_id" "$cluster_location"  "$cluster_name"
  else
    add_service_accounts_for_standard "$project_id" "$cluster_location"  "$cluster_name"
  fi
done <<< "$(gcloud container clusters list --project "$project_id" --format="value(name,location)")"
echo "--- 2. Check if service accounts have permissions"
unique_service_accounts=($(echo "${all_service_accounts[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
echo "Service accounts: ${unique_service_accounts[@]}"
printf "%-60s| %-40s| %-40s| %-20s\n" "service_account" "has_logging_permission" "has_monitoring_permission" "has_performance_hpa_metric_write_permission"
for sa in "${unique_service_accounts[@]}"; do
  logging_permission=$(service_account_has_permission "$project_id" "$sa" "logging.logEntries.create")
  time_series_create_permission=$(service_account_has_permission "$project_id" "$sa" "monitoring.timeSeries.create")
  metric_descriptors_create_permission=$(service_account_has_permission "$project_id" "$sa" "monitoring.metricDescriptors.create")
  if [[ "$time_series_create_permission" == "No" || "$metric_descriptors_create_permission" == "No" ]]; then
    monitoring_permission="No"
  else
    monitoring_permission="Yes"
  fi
  performance_hpa_metric_write_permission=$(service_account_has_permission "$project_id" "$sa" "autoscaling.sites.writeMetrics")
  printf "%-60s| %-40s| %-40s| %-20s\n" $sa $logging_permission $monitoring_permission $performance_hpa_metric_write_permission
  if [[ "$logging_permission" == "No" || "$monitoring_permission" == "No" || "$performance_hpa_metric_write_permission" == "No" ]]; then
    sa_missing_permissions+=( ${sa} )
  fi
done
echo "--- 3. List all service accounts that don't have the above permissions"
if [[ "${#sa_missing_permissions[@]}" -gt 0 ]]; then
  printf "Grant roles/container.defaultNodeServiceAccount to the following service accounts: %s\n" "${sa_missing_permissions[@]}"
else
  echo "All service accounts have the above permissions"
fiクラスタで重要な権限のないノード サービス アカウントを特定する
  GKE は、ノードに接続されている IAM サービス アカウントを使用して、ロギングやモニタリングなどのシステムタスクを実行します。これらのノードサービス アカウントには、プロジェクトに対する Kubernetes Engine デフォルト ノードサービス アカウント(roles/container.defaultNodeServiceAccount)ロールが最低限必要です。デフォルトでは、GKE はプロジェクトに自動的に作成される Compute Engine のデフォルトのサービス アカウントをノードサービス アカウントとして使用します。
組織で iam.automaticIamGrantsForDefaultServiceAccounts 組織のポリシー制約が適用されている場合、GKE に必要な権限がプロジェクトのデフォルトの Compute Engine サービス アカウントに自動的に付与されないことがあります。
- 
ノードが使用するサービス アカウントの名前を確認します。 コンソール- [Kubernetes クラスタ] ページに移動します。
- クラスタのリストで、調査するクラスタの名前をクリックします。
- クラスタの運用モードに応じて、次のいずれかを行います。
- Autopilot モードのクラスタの場合は、[セキュリティ] セクションで [サービス アカウント] フィールドを見つけます。
- Standard モードのクラスタの場合は、次の操作を行います。
- [ノード] タブをクリックします。
- [ノードプール] テーブルで、ノードプールの名前をクリックします。[ノードプールの詳細] ページが開きます。
- [セキュリティ] セクションで、[サービス アカウント] フィールドを見つけます。
 
 
 [サービス アカウント] フィールドの値が defaultの場合、ノードは Compute Engine のデフォルトのサービス アカウントを使用します。このフィールドの値がdefault以外の場合、ノードはカスタム サービス アカウントを使用します。必要なロールをカスタム サービス アカウントに付与するには、最小権限の IAM サービス アカウントを使用するをご覧ください。gcloudAutopilot モードのクラスタの場合は、次のコマンドを実行します。 gcloud container clusters describe CLUSTER_NAME\ --location=LOCATION\ --flatten=autoscaling.autoprovisioningNodePoolDefaults.serviceAccountStandard モードのクラスタの場合は、次のコマンドを実行します。 gcloud container clusters describe CLUSTER_NAME\ --location=LOCATION\ --format="table(nodePools.name,nodePools.config.serviceAccount)"出力が defaultの場合、ノードは Compute Engine のデフォルトのサービス アカウントを使用します。出力がdefaultでない場合、ノードはカスタム サービス アカウントを使用しています。必要なロールをカスタム サービス アカウントに付与するには、最小権限の IAM サービス アカウントを使用するをご覧ください。
- 
Compute Engine のデフォルト サービス アカウントに roles/container.defaultNodeServiceAccountロールを付与する手順は次のとおりです。コンソール- [ようこそ] ページに移動します。
- [プロジェクト番号] フィールドで、 [クリップボードにコピー] をクリックします。
- [IAM] ページに移動します。
- [ アクセスを許可] をクリックします。
- [新しいプリンシパル] フィールドに次の値を指定します。PROJECT_NUMBER-compute@developer.gserviceaccount.comPROJECT_NUMBERは、コピーしたプロジェクト番号に置き換えます。
- [ロールを選択] メニューで、[Kubernetes Engine デフォルト ノードサービス アカウント] ロールを選択します。
- [保存] をクリックします。
 gcloud-  Google Cloud プロジェクト番号を確認します。gcloud projects describe PROJECT_ID \ --format="value(projectNumber)" PROJECT_IDは、実際のプロジェクト ID に置き換えます。出力は次のようになります。 12345678901 
- Compute Engine のデフォルト サービス アカウントに roles/container.defaultNodeServiceAccountロールを付与します。gcloud projects add-iam-policy-binding PROJECT_ID \ --member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \ --role="roles/container.defaultNodeServiceAccount" PROJECT_NUMBERは、前の手順のプロジェクト番号に置き換えます。
 
エラー 403: 十分な権限がない
gcloud container clusters get-credentials を使用して GKE クラスタに接続しようとしたときに、アカウントに Kubernetes API サーバーにアクセスする権限がないと、次のエラーが発生します。
ERROR: (gcloud.container.clusters.get-credentials) ResponseError: code=403, message=Required "container.clusters.get" permission(s) for "projects/<your-project>/locations/<region>/clusters/<your-cluster>".
この問題を解決するには、次の操作を行います。
- アクセスの問題が発生しているアカウントを特定します。 - gcloud auth list
- Kubernetes API サーバーの認証の手順に沿って、アカウントに必要なアクセス権を付与します。 
エラー 403: リトライ バジェットが残っていない
GKE クラスタを作成しようとすると、次のエラーが発生することがあります。
Error: googleapi: Error 403: Retry budget exhausted: Google Compute Engine:
Required permission 'PERMISSION_NAME' for 'RESOURCE_NAME'.
このエラー メッセージでは、次の変数が適用されます。
- PERMISSION_NAME: 権限の名前(- compute.regions.getなど)。
- RESOURCE_NAME: アクセスしようとした Google Cloudリソースのパス(Compute Engine リージョンなど)。
このエラーは、クラスタに関連付けられている IAM サービス アカウントに、クラスタの作成に最低限必要な権限がない場合に発生します。
この問題を解決するには、次の操作を行います。
- GKE クラスタを実行するために必要なすべての権限を持つ IAM サービス アカウントを作成または変更します。手順については、最小権限の IAM サービス アカウントを使用するをご覧ください。
- クラスタ作成コマンドで --service-accountフラグを使用して、更新した IAM サービス アカウントを指定します。手順については、Autopilot クラスタを作成するをご覧ください。
または、--service-account フラグを省略して、プロジェクトの Compute Engine のデフォルトのサービス アカウントを GKE で使用するようにします。このアカウントには、必要な権限がデフォルトで付与されています。
エラー 404: リソースが見つからない
gcloud container コマンドの呼び出し時にエラー 404(リソースが見つからない)が発生した場合は、Google Cloud CLI に対して再認証することで問題を解決します。
gcloud auth login
エラー 400 / 403: アカウントに編集権限がありません
アカウントに対する編集権限がないというエラー(エラー 400 または 403)は、次のいずれかが手動で削除または編集されたことを示します。
Compute Engine または GKE API を有効にすると、 Google Cloudは次のサービス アカウントとエージェントを作成します。
- プロジェクトの Compute Engine のデフォルトのサービス アカウント。GKE は、ロギングやモニタリングなどのシステムタスク用に、このサービス アカウントをデフォルトでノードに接続します。
- Google 管理のプロジェクト内の Google API サービス エージェント。プロジェクトに対する編集権限が付与されています。
- Google 管理のプロジェクト内の Google Kubernetes Engine サービス エージェント。プロジェクトに対する Kubernetes Engine サービス エージェントと Kubernetes Engine デフォルト ノードサービス エージェントが付与されています。
いずれかの時点で、これらの権限の編集、プロジェクトのロール バインディングの削除、サービス アカウントの完全削除、API の無効化が行われると、クラスタの作成とすべての管理機能が失敗します。
GKE サービス エージェントの権限を確認する
Google Kubernetes Engine には、次のサービス エージェントがあります。
- Kubernetes Engine サービス エージェント。インスタンス、ディスク、ロードバランサなどのコンピューティング リソースの管理に使用されます。 
- Kubernetes Engine のデフォルトのノード サービス エージェント。Google Kubernetes Engine ノード システム ワークロードで使用され、ロギングやモニタリングなどの標準機能をサポートします。 
プロジェクトに対する Kubernetes Engine サービス エージェント ロールが Google Kubernetes Engine サービス エージェントに割り当てられているかどうかを確認するには、次の手順を完了します。
- Google Kubernetes Engine サービス エージェントの名前を確認します。サービス エージェントの形式は次のとおりです。 - Kubernetes Engine サービス エージェント:
 - service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com- Kubernetes Engine デフォルト ノード サービス エージェント:
 - service-PROJECT_NUMBER@gcp-sa-gkenode.iam.gserviceaccount.com- PROJECT_NUMBERは、使用するプロジェクト番号に置き換えます。
- プロジェクトに対する Kubernetes Engine サービス エージェント ロールと Kubernetes Engine デフォルト ノード サービス エージェント ロールが Google Kubernetes Engine サービス エージェントに割り当てられていることを確認します。 - gcloud projects get-iam-policy PROJECT_ID- PROJECT_IDは、実際のプロジェクト ID に置き換えます。
この問題を解決するには、Google Kubernetes Engine サービス エージェントから Kubernetes Engine サービス エージェントまたは Kubernetes Engine デフォルト ノードサービス エージェントが削除されている場合は、次の手順に沿って、API Kubernetes Engine API が有効になっていることを確認し、必要なロールをサービス エージェントに付与します。
コンソール
- Google Cloud コンソールの [API とサービス] ページに移動します。 
- プロジェクトを選択します。 
- [API とサービスを有効化] をクリックします。 
- Kubernetes を検索して、検索結果から API を選択します。 
- [有効にする] をクリックします。API をすでに有効にしている場合は、まずそれを無効にしてから再度有効にする必要があります。API と関連サービスが有効になるには、数分かかることがあります。 
gcloud
gcloud CLI で次のコマンドを実行します。
PROJECT_NUMBER=$(gcloud projects describe "PROJECT_ID"
    --format 'get(projectNumber)')
gcloud projects add-iam-policy-binding PROJECT_ID \
    --member "serviceAccount:service-${PROJECT_NUMBER?}@container-engine-robot.iam.gserviceaccount.com" \
    --role roles/container.serviceAgent
gcloud projects add-iam-policy-binding PROJECT_ID \
    --member "serviceAccount:service-${PROJECT_NUMBER?}@gcp-sa-gkenode.iam.gserviceaccount.com" \
    --role roles/container.defaultNodeServiceAgent
次のステップ
- このドキュメントで問題を解決できない場合は、サポートを受けるで、次のトピックに関するアドバイスなど、詳細なヘルプをご覧ください。 - Cloud カスタマーケアに問い合わせて、サポートケースを登録する。
- StackOverflow で質問する、google-kubernetes-engineタグを使用して類似の問題を検索するなどして、コミュニティからサポートを受ける。#kubernetes-engineSlack チャネルに参加して、コミュニティ サポートを利用することもできます。
- 公開バグトラッカーを使用して、バグの報告や機能リクエストの登録を行う。