Memecahkan masalah akun layanan di GKE


Halaman ini menunjukkan cara memecahkan masalah akun layanan Google Kubernetes Engine (GKE).

Memberikan peran yang diperlukan untuk GKE ke akun layanan node

Akun layanan IAM yang digunakan node GKE Anda harus memiliki semua izin yang disertakan dalam peran IAM Kubernetes Engine Default Node Service Account (roles/container.defaultNodeServiceAccount). Jika akun layanan node GKE tidak memiliki satu atau beberapa izin ini, GKE tidak dapat melakukan tugas sistem seperti berikut:

Akun layanan node mungkin tidak memiliki izin tertentu yang diperlukan karena alasan seperti berikut:

Jika akun layanan node Anda tidak memiliki izin yang diperlukan GKE, Anda mungkin melihat error dan pemberitahuan seperti berikut:

  • Di Google Cloud konsol, di halaman Kubernetes clusters, pesan error Grant critical permissions akan muncul di kolom Notifications untuk cluster tertentu.
  • Di konsol Google Cloud , pada halaman detail cluster untuk cluster tertentu, pesan error berikut akan muncul:

    Grant roles/container.defaultNodeServiceAccount role to Node service account to allow for non-degraded operations.
    
  • Di Cloud Audit Logs, log Aktivitas Admin untuk Google Cloud API seperti monitoring.googleapis.com memiliki nilai berikut jika izin yang sesuai untuk mengakses API tersebut tidak ada di akun layanan node:

    • Tingkat keseriusan: ERROR
    • Pesan: Permission denied (or the resource may not exist)
  • Log untuk node tertentu tidak ada di Cloud Logging dan log Pod untuk agen logging di node tersebut menampilkan error 401. Untuk mendapatkan log Pod ini, jalankan perintah berikut:

    [[ $(kubectl logs -l k8s-app=fluentbit-gke -n kube-system -c fluentbit-gke | grep -cw "Received 401") -gt 0 ]] && echo "true" || echo "false"
    

    Jika outputnya adalah true, berarti beban kerja sistem mengalami error 401, yang menunjukkan kurangnya izin.

Untuk mengatasi masalah ini, berikan peran Akun Layanan Node Default Kubernetes Engine (roles/container.defaultNodeServiceAccount) di project ke akun layanan yang menyebabkan error. Pilih salah satu opsi berikut:

console

Untuk menemukan nama akun layanan yang digunakan node Anda, lakukan hal berikut:

  1. Buka halaman Cluster Kubernetes:

    Buka cluster Kubernetes

  2. Dalam daftar cluster, klik nama cluster yang ingin diperiksa.

  3. Temukan nama akun layanan node. Anda akan memerlukan nama ini nanti.

    • Untuk cluster mode Autopilot, di bagian Security, temukan kolom Service account.
    • Untuk cluster mode Standard, lakukan hal berikut:
    1. Klik tab Nodes.
    2. Di tabel Node pool, klik nama node pool. Halaman Detail node pool akan terbuka.
    3. Di bagian Keamanan, temukan kolom Service account.

    Jika nilai di kolom Service account adalah default, node Anda akan menggunakan akun layanan default Compute Engine. Jika nilai di kolom ini bukan default, node Anda menggunakan akun layanan kustom.

Untuk memberikan peran Kubernetes Engine Default Node Service Account ke akun layanan, lakukan hal berikut:

  1. Buka halaman Sambutan:

    Buka Selamat Datang

  2. Di kolom Project number, klik Copy to clipboard.

  3. Buka halaman IAM:

    Buka IAM

  4. Klik Berikan akses.

  5. Di kolom New principals, tentukan nama akun layanan node Anda. Jika node Anda menggunakan akun layanan Compute Engine default, tentukan nilai berikut:

    PROJECT_NUMBER-compute@developer.gserviceaccount.com
    

    Ganti PROJECT_NUMBER dengan nomor project yang Anda salin.

  6. Di menu Select a role, pilih peran Kubernetes Engine Default Node Service Account.

  7. Klik Simpan.

Untuk memverifikasi bahwa peran telah diberikan, lakukan hal berikut:

  1. Di halaman IAM, klik tab Lihat menurut peran.
  2. Luaskan bagian Kubernetes Engine Default Node Service Account. Daftar akun utama yang memiliki peran ini akan ditampilkan.
  3. Temukan akun layanan node Anda dalam daftar akun utama.

gcloud

  1. Temukan nama akun layanan yang digunakan node Anda:

    • Untuk cluster mode Autopilot, jalankan perintah berikut:
    gcloud container clusters describe CLUSTER_NAME \
        --location=LOCATION \
        --flatten=autoscaling.autoprovisioningNodePoolDefaults.serviceAccount
    
    • Untuk cluster mode Standar, jalankan perintah berikut:
    gcloud container clusters describe CLUSTER_NAME \
        --location=LOCATION \
        --format="table(nodePools.name,nodePools.config.serviceAccount)"
    

    Jika outputnya adalah default, node Anda akan menggunakan akun layanan default Compute Engine. Jika outputnya bukan default, node Anda menggunakan akun layanan kustom.

  2. Temukan Google Cloud nomor project Anda:

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

    Ganti PROJECT_ID dengan project ID Anda.

    Outputnya mirip dengan hal berikut ini:

    12345678901
    
  3. Berikan peran roles/container.defaultNodeServiceAccount ke akun layanan:

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member="SERVICE_ACCOUNT_NAME" \
        --role="roles/container.defaultNodeServiceAccount"
    

    Ganti SERVICE_ACCOUNT_NAME dengan nama akun layanan yang Anda temukan di langkah sebelumnya. Jika node Anda menggunakan akun layanan default Compute Engine, tentukan nilai berikut:

    serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com
    

    Ganti PROJECT_NUMBER dengan nomor project dari langkah sebelumnya.

  4. Pastikan bahwa peran berhasil diberikan:

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

    Output-nya adalah nama akun layanan Anda.

Mengidentifikasi akun layanan node yang tidak memiliki izin yang diperlukan

Bagian berikut menjelaskan cara mengidentifikasi akun layanan node yang tidak memiliki izin yang diperlukan untuk GKE.

Mengidentifikasi cluster yang memiliki akun layanan node dengan izin yang tidak ada

Gunakan rekomendasi GKE dari subjenis rekomendasi NODE_SA_MISSING_PERMISSIONS untuk mengidentifikasi cluster Autopilot dan Standard yang memiliki akun layanan node tanpa izin. Pemberi Rekomendasi hanya mengidentifikasi cluster yang dibuat pada atau setelah 1 Januari 2024. Untuk menemukan dan memperbaiki izin yang tidak ada menggunakan Rekomendasi, lakukan hal berikut:

  1. Temukan rekomendasi aktif dalam project Anda untuk subjenis perekomendasikan NODE_SA_MISSING_PERMISSIONS:

    gcloud recommender recommendations list \
        --recommender=google.container.DiagnosisRecommender \
        --location LOCATION \
        --project PROJECT_ID \
        --format yaml \
        --filter="recommenderSubtype:NODE_SA_MISSING_PERMISSIONS"
    

    Ganti kode berikut:

    • LOCATION: lokasi untuk menemukan rekomendasi.
    • PROJECT_ID: Google Cloud Project ID Anda.

    Output-nya mirip dengan berikut ini, yang menunjukkan bahwa cluster memiliki akun layanan node dengan izin yang tidak ada:

    associatedInsights:
    # lines omitted for clarity
    recommenderSubtype: NODE_SA_MISSING_PERMISSIONS
    stateInfo:
      state: ACTIVE
    targetResources:
    - //container.googleapis.com/projects/12345678901/locations/us-central1/clusters/cluster-1
    

    Mungkin diperlukan waktu hingga 24 jam agar rekomendasi muncul. Untuk petunjuk mendetail, lihat melihat insight dan rekomendasi.

  2. Untuk setiap cluster yang ada dalam output langkah sebelumnya, temukan akun layanan node terkait dan berikan peran yang diperlukan ke akun layanan tersebut. Untuk mengetahui detailnya, lihat petunjuk di bagian Memberikan peran yang diperlukan untuk GKE ke akun layanan node.

    Setelah Anda memberikan peran yang diperlukan ke akun layanan node yang diidentifikasi, rekomendasi tersebut mungkin tetap ada hingga 24 jam, kecuali jika Anda menutupnya secara manual.

Mengidentifikasi semua akun layanan node yang tidak memiliki izin

Anda dapat menjalankan skrip yang menelusuri node pool di cluster Standard dan Autopilot project untuk menemukan akun layanan node yang tidak memiliki izin yang diperlukan untuk GKE. Skrip ini menggunakan gcloud CLI dan utilitas jq. Untuk melihat skrip, luaskan bagian berikut:

Melihat skrip

#!/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

Skrip ini berlaku untuk semua cluster GKE dalam project Anda.

Setelah Anda mengidentifikasi nama akun layanan yang tidak memiliki izin, berikan peran yang diperlukan. Untuk mengetahui detailnya, lihat petunjuk di bagian Memberikan peran yang diperlukan untuk GKE ke akun layanan node.

Memulihkan akun layanan default ke project Google Cloud Anda

Akun layanan default GKE, container-engine-robot, dapat tidak terikat dari project secara tidak sengaja. Peran Agen Layanan Kubernetes Engine (roles/container.serviceAgent) adalah peran Identity and Access Management (IAM) yang memberikan izin kepada akun layanan untuk mengelola resource cluster. Jika Anda menghapus binding peran ini dari akun layanan, akun layanan default akan terlepas dari project, sehingga Anda tidak dapat men-deploy aplikasi dan melakukan operasi cluster lainnya.

Untuk melihat apakah akun layanan dihapus dari project Anda, Anda dapat menggunakan konsol Google Cloud atau Google Cloud CLI.

Konsol

gcloud

  • Jalankan perintah berikut:

    gcloud projects get-iam-policy PROJECT_ID
    

    Ganti PROJECT_ID dengan project ID Anda.

Jika dasbor atau perintah tidak menampilkan container-engine-robot di antara akun layanan Anda, peran tidak terikat.

Untuk memulihkan binding peran Agen Layanan Kubernetes Engine (roles/container.serviceAgent), jalankan perintah berikut:

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

Pastikan bahwa penetapan peran dipulihkan:

gcloud projects get-iam-policy $PROJECT_ID

Jika Anda melihat nama akun layanan beserta peran container.serviceAgent, artinya binding peran telah dipulihkan. Contoh:

- members:
  - serviceAccount:service-1234567890@container-engine-robot.iam.gserviceaccount.com
  role: roles/container.serviceAgent

Mengaktifkan akun layanan default Compute Engine

Akun layanan yang digunakan untuk node pool biasanya adalah akun layanan default Compute Engine. Jika akun layanan default ini dinonaktifkan, node Anda mungkin gagal didaftarkan ke cluster.

Untuk melihat apakah akun layanan dinonaktifkan di project Anda, Anda dapat menggunakanGoogle Cloud console atau gcloud CLI.

Konsol

gcloud

  • Jalankan perintah berikut:
gcloud iam service-accounts list  --filter="NAME~'compute' AND disabled=true"

Jika akun layanan dinonaktifkan, jalankan perintah berikut untuk mengaktifkan akun layanan:

gcloud iam service-accounts enable PROJECT_ID-compute@developer.gserviceaccount.com

Ganti PROJECT_ID dengan project ID Anda.

Untuk mengetahui informasi selengkapnya, lihat Memecahkan masalah pendaftaran node.

Error 400/403: Izin edit untuk akun tidak ada

Jika akun layanan dihapus, Anda mungkin melihat error izin edit yang hilang. Untuk mempelajari cara memecahkan masalah error ini, lihat Error 400/403: Izin edit untuk akun tidak ada.

Langkah berikutnya