Soluciona problemas de registro en GKE


En esta página, se te ayuda a resolver problemas con la propia canalización de registros de Google Kubernetes Engine (GKE), como que los registros no aparezcan en Cloud Logging. Para obtener más información sobre cómo usar los registros para solucionar problemas relacionados con tus cargas de trabajo y clústeres, consulta Introducción a la solución de problemas de GKE.

Faltan registros de clústeres en Cloud Logging

Verifica que el registro esté habilitado en el proyecto

  1. Enumera los servicios habilitados:

    gcloud services list --enabled --filter="NAME=logging.googleapis.com"
    

    En el siguiente resultado, se indica que el registro está habilitado para el proyecto:

    NAME                    TITLE
    logging.googleapis.com  Cloud Logging API
    

    Opcional: Revisa los registros en el visor de registros para determinar quién inhabilitó la API y cuándo la inhabilitó:

    protoPayload.methodName="google.api.serviceusage.v1.ServiceUsage.DisableService"
    protoPayload.response.services="logging.googleapis.com"
    
  2. Si el registro está inhabilitado, habilítalo:

    gcloud services enable logging.googleapis.com
    

Verifica que el registro esté habilitado en el clúster

  1. Enumera los clústeres:

    gcloud container clusters list \
        --project=PROJECT_ID \
        '--format=value(name,loggingConfig.componentConfig.enableComponents)' \
        --sort-by=name | column -t
    

    Reemplaza lo siguiente:

    • PROJECT_ID: Es el ID de tu proyecto de Trusted Cloud by S3NS .

    El resultado es similar a este:

    cluster-1              SYSTEM_COMPONENTS
    cluster-2              SYSTEM_COMPONENTS;WORKLOADS
    cluster-3
    

    Si el valor de tu clúster está vacío, el registro se inhabilita. Por ejemplo, cluster-3 en esta salida tiene el registro inhabilitado.

  2. Habilita el registro del clúster si está configurado como NONE:

    gcloud container clusters update CLUSTER_NAME  \
        --logging=SYSTEM,WORKLOAD \
        --location=COMPUTE_LOCATION
    

    Reemplaza lo siguiente:

Después de verificar que habilitaste el registro en tu proyecto y en el clúster, considera usar las investigaciones de Gemini Cloud Assist para obtener estadísticas adicionales sobre tus registros y resolver problemas. Para obtener más información sobre las diferentes formas de iniciar una investigación con el Explorador de registros, consulta Soluciona problemas con las investigaciones de Gemini Cloud Assist en la documentación de Gemini.

Verifica que los nodos de los grupos de nodos tengan permiso de acceso a Cloud Logging

Se requiere uno de los siguientes permisos para que los nodos escriban registros en Cloud Logging:

  • https://www.googleapis.com/auth/logging.write
  • https://www.googleapis.com/auth/cloud-platform
  • https://www.googleapis.com/auth/logging.admin
  1. Verifica los permisos configurados en cada grupo de nodos en el clúster:

    gcloud container node-pools list --cluster=CLUSTER_NAME \
        --format="table(name,config.oauthScopes)" \
        --location COMPUTE_LOCATION
    

    Reemplaza lo siguiente:

    Migra tus cargas de trabajo del grupo de nodos anterior al grupo de nodos recién creado y supervisa el progreso.

  2. Crea grupos de nodos nuevos con el permiso de registro correcto:

    gcloud container node-pools create NODE_POOL_NAME \
        --cluster=CLUSTER_NAME \
        --location=COMPUTE_LOCATION \
        --scopes="gke-default"
    

    Reemplaza lo siguiente:

Identifica clústeres con cuentas de servicio de nodo a las que les faltan permisos esenciales

Para identificar los clústeres a los que les faltan permisos críticos en las cuentas de servicio de los nodos, usa las recomendaciones de GKE del subtipo de recomendador NODE_SA_MISSING_PERMISSIONS:

  • Usa la Trusted Cloud consola. Ve a la página Clústeres de Kubernetes. En la columna Notificaciones de clústeres específicos, busca la recomendación Otorga permisos esenciales.
  • Usa gcloud CLI o la API de Recommender especificando el subtipo de recomendador NODE_SA_MISSING_PERMISSIONS.

    Para consultar esta recomendación, ejecuta el siguiente comando:

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

Para implementar esta recomendación, otorga el rol roles/container.defaultNodeServiceAccount a la cuenta de servicio del nodo.

Puedes ejecutar una secuencia de comandos que busque grupos de nodos en los clústeres estándar y de Autopilot de tu proyecto para detectar cualquier cuenta de servicio de nodos que no tenga los permisos requeridos para GKE. Esta secuencia de comandos usa gcloud CLI y la utilidad jq. Para ver la secuencia de comandos, expande la siguiente sección:

Cómo ver la secuencia de comandos

#!/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.s3ns-system.iam.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.s3ns-system.iam.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

Identifica las cuentas de servicio de nodo a las que les faltan permisos esenciales en un clúster

GKE usa cuentas de servicio de IAM que se adjuntan a tus nodos para ejecutar tareas del sistema, como el registro y la supervisión. Como mínimo, estas cuentas de servicio de nodo deben tener el rol de cuenta de servicio de nodo predeterminado de Kubernetes Engine (roles/container.defaultNodeServiceAccount) en tu proyecto. De forma predeterminada, GKE usa la cuenta de servicio predeterminada de Compute Engine, que se crea automáticamente en tu proyecto, como la cuenta de servicio del nodo.

Si tu organización aplica la restricción de la política de la organización iam.automaticIamGrantsForDefaultServiceAccounts, es posible que la cuenta de servicio predeterminada de Compute Engine en tu proyecto no obtenga automáticamente los permisos requeridos para GKE.

  • Para verificar si faltan permisos de registro, busca errores de 401 en el registro de tu clúster:

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

    Si el resultado es true, la carga de trabajo del sistema experimenta errores de 401, lo que indica una falta de permisos. Si el resultado es false, omite el resto de estos pasos y prueba con otro procedimiento de solución de problemas.Para identificar todos los permisos críticos faltantes, consulta el script.

  1. Busca el nombre de la cuenta de servicio que usan tus nodos:

    Console

    1. Ve a la página Clústeres de Kubernetes:

      Ir a clústeres de Kubernetes

    2. En la lista de clústeres, haz clic en el nombre del clúster que deseas inspeccionar.
    3. Según el modo de operación del clúster, realiza una de las siguientes acciones:
      • Para los clústeres en modo Autopilot, en la sección Seguridad, busca el campo Cuenta de servicio.
      • Para los clústeres en modo Standard, haz lo siguiente:
        1. Haz clic en la pestaña Nodos.
        2. En la tabla Grupos de nodos, haz clic en el nombre de un grupo de nodos. Se abrirá la página Detalles del grupo de nodos.
        3. En la sección Seguridad, busca el campo Cuenta de servicio.

    Si el valor en el campo Cuenta de servicio es default, tus nodos usan la cuenta de servicio predeterminada de Compute Engine. Si el valor de este campo no es default, tus nodos usan una cuenta de servicio personalizada. Para otorgar el rol requerido a una cuenta de servicio personalizada, consulta Usa cuentas de servicio de IAM privilegio mínimo mínimos.

    gcloud

    Para los clústeres en modo Autopilot, ejecuta el siguiente comando:

    gcloud container clusters describe CLUSTER_NAME \
        --location=LOCATION \
        --flatten=autoscaling.autoprovisioningNodePoolDefaults.serviceAccount

    Para los clústeres en modo estándar, ejecuta el siguiente comando:

    gcloud container clusters describe CLUSTER_NAME \
        --location=LOCATION \
        --format="table(nodePools.name,nodePools.config.serviceAccount)"

    Si el resultado es default, tus nodos usan la cuenta de servicio predeterminada de Compute Engine. Si el resultado no es default, tus nodos usan una cuenta de servicio personalizada. Para otorgar el rol requerido a una cuenta de servicio personalizada, consulta Usa cuentas de servicio de IAM privilegio mínimo mínimos.

  2. Para otorgar el rol roles/container.defaultNodeServiceAccount a la cuenta de servicio predeterminada de Compute Engine, completa los siguientes pasos:

    Console

    1. Ve a la página Bienvenido:

      Ir a Bienvenida

    2. En el campo Número del proyecto, haz clic en Copiar en el portapapeles.
    3. Ve a la página IAM:

      Ir a IAM

    4. Haz clic en Grant access.
    5. En el campo Principales nuevas, especifica el siguiente valor:
      PROJECT_NUMBER-compute@developer.s3ns-system.iam.gserviceaccount.com
      Reemplaza PROJECT_NUMBER por el número de proyecto que copiaste.
    6. En el menú Selecciona un rol, selecciona el rol de Cuenta de servicio de nodo predeterminado de Kubernetes Engine.
    7. Haz clic en Guardar.

    gcloud

    1. Busca tu Trusted Cloud número de proyecto:
      gcloud projects describe PROJECT_ID \
          --format="value(projectNumber)"

      Reemplaza PROJECT_ID con el ID del proyecto.

      El resultado es similar a este:

      12345678901
      
    2. Otorga el rol roles/container.defaultNodeServiceAccount a la cuenta de servicio predeterminada de Compute Engine:
      gcloud projects add-iam-policy-binding PROJECT_ID \
          --member="serviceAccount:PROJECT_NUMBER-compute@developer.s3ns-system.iam.gserviceaccount.com" \
          --role="roles/container.defaultNodeServiceAccount"

      Reemplaza PROJECT_NUMBER por el número de proyecto del paso anterior.

  • Verifica que las cuentas de servicio de los nodos tengan los permisos necesarios. Consulta la secuencia de comandos para verificarlo.

Es un script para identificar los permisos faltantes de la cuenta de servicio del nodo de GKE

Puedes ejecutar una secuencia de comandos que busque grupos de nodos en los clústeres estándar y de Autopilot de tu proyecto para detectar cualquier cuenta de servicio de nodos que no tenga los permisos requeridos para GKE. Esta secuencia de comandos usa gcloud CLI y la utilidad jq. Para ver la secuencia de comandos, expande la siguiente sección:

Cómo ver la secuencia de comandos

#!/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.s3ns-system.iam.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.s3ns-system.iam.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

Verifica que no se hayan alcanzado las cuotas de la API de Write de Cloud Logging

Confirma que no alcanzaste las cuotas de escritura de API para Cloud Logging.

  1. Ve a la página Cuotas en la consola de Trusted Cloud .

    Ir a Cuotas

  2. Filtra la tabla por “API de Cloud Logging”.

  3. Confirma que no alcanzaste ninguna de las cuotas.