本页面可帮助您解决使用 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
命令成功,您将看到命名空间列表。请继续使用在 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
二进制文件是必需的,因此您需要使用 1.24 或更低版本的 kubectl
命令行工具。
请按照以下步骤操作,以便在不需要插件的情况下访问集群:
使用
curl
安装 1.24 版或更低版本的kubectl
命令行工具。以下示例安装的是 1.24 版的工具:curl -LO https://dl.k8s.io/release/v1.24.0/bin/linux/amd64/kubectl
在文本编辑器中打开 shell 启动脚本文件。例如,针对 Bash shell,打开
.bashrc
:vi ~/.bashrc
如果您使用的是 macOS,请在这些说明中使用
~/.bash_profile
而非.bashrc
。将以下代码行添加到启动脚本文件中,然后保存:
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 尚未重新创建集群中的一个或多个节点池。
如需解决此问题,请选择以下解决方案之一:
- 等待重新创建进行。这可能需要几个小时、几天或几周的时间,具体取决于现有维护窗口和排除项等因素。
通过启动与控制平面相同的版本升级,手动开始创建受影响的节点池。
如需开始重新创建,请运行以下命令:
gcloud container clusters upgrade CLUSTER_NAME \ --node-pool=POOL_NAME
升级完成后,请重试该操作。
错误 401:未授权
识别节点服务账号缺少关键权限的集群
如需识别节点服务账号缺少关键权限的集群,请使用 NODE_SA_MISSING_PERMISSIONS
Recommender 子类型的 GKE 建议:
- 使用 Google Cloud 控制台。 前往 Kubernetes 集群页面,然后在特定集群的通知列中查看授予关键权限建议。
通过指定
NODE_SA_MISSING_PERMISSIONS
Recommender 子类型来使用 gcloud CLI 或 Recommender API。如需查询建议,请运行以下命令:
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 Default Node Service Account (roles/container.defaultNodeServiceAccount
) 角色。默认情况下,GKE 会将 Compute Engine 默认服务账号(在您的项目中自动创建)用作节点服务账号。
如果您的组织强制执行 iam.automaticIamGrantsForDefaultServiceAccounts
组织政策限制,则项目中的默认 Compute Engine 服务账号可能无法自动获得 GKE 所需的权限。
-
找到节点使用的服务账号的名称:
控制台
- 前往 Kubernetes 集群页面:
- 在集群列表中,点击您要检查的集群的名称。
- 根据操作的集群模式,执行以下操作之一:
- 对于 Autopilot 模式集群,在安全部分中,找到服务账号字段。
- 对于 Standard 模式集群,请执行以下操作:
- 点击节点标签页。
- 在节点池表格中,点击节点池名称。此时会打开节点池详情页面。
- 在安全部分中,找到服务账号字段。
如果服务账号字段中的值为
default
,则表示节点使用 Compute Engine 默认服务账号。如果此字段中的值不是default
,则表示节点使用自定义服务账号。如需向自定义服务账号授予所需角色,请参阅使用最小权限 IAM 服务账号。gcloud
对于 Autopilot 模式集群,请运行以下命令:
gcloud container clusters describe
CLUSTER_NAME
\ --location=LOCATION
\ --flatten=autoscaling.autoprovisioningNodePoolDefaults.serviceAccount对于 Standard 模式集群,请运行以下命令:
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
角色,请完成以下步骤:控制台
gcloud
- 找到您的 Google Cloud 项目编号:
gcloud projects describe PROJECT_ID \ --format="value(projectNumber)"
将
PROJECT_ID
替换为您的项目 ID。输出内容类似如下:
12345678901
- 将
roles/container.defaultNodeServiceAccount
角色授予 Compute Engine 默认服务账号:gcloud projects add-iam-policy-binding PROJECT_ID \ --member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \ --role="roles/container.defaultNodeServiceAccount"
将
PROJECT_NUMBER
替换为上一步中的项目编号。
- 找到您的 Google Cloud 项目编号:
错误 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 服务账号没有创建集群所需的最低权限,则会发生此错误。
如需解决此问题,请执行以下操作:
- 创建或修改 IAM 服务账号,以拥有运行 GKE 集群所需的所有权限。如需查看相关说明,请参阅使用最小权限 IAM 服务账号。
- 使用
--service-account
标志在集群创建命令中指定更新后的 IAM 服务账号。如需查看相关说明,请参阅创建 Autopilot 集群。
或者,省略 --service-account
标志,让 GKE 使用项目中的 Compute Engine 默认服务账号,该账号默认具有所需权限。
错误 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 管理的项目中具有项目的 Kubernetes Engine Service Agent 和 Kubernetes Engine Default Node Service Agent 角色的 Google Kubernetes Engine 服务代理。
如果有人在任何时候修改了这些权限、移除了项目上的角色绑定、完全移除了服务账号或停用了 API,则集群创建和所有管理功能都将失败。
验证 GKE 服务代理的权限
Google Kubernetes Engine 具有以下服务代理:
Kubernetes Engine Service Agent,用于管理计算资源,例如实例、磁盘和负载均衡器。
Kubernetes Engine 默认节点服务代理,由 Google Kubernetes Engine 节点系统工作负载用于支持日志记录和监控等标准功能。
如需验证 Google Kubernetes Engine 服务代理是否已在项目中分配了 Kubernetes Engine Service Agent 角色,请完成以下步骤:
确定您的 Google Kubernetes Engine 服务代理的名称。服务代理的格式如下:
- Kubernetes Engine Service Agent:
service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com
- Kubernetes Engine Default Node Service Agent:
service-PROJECT_NUMBER@gcp-sa-gkenode.iam.gserviceaccount.com
将
PROJECT_NUMBER
替换为您的项目编号。验证您的 Google Kubernetes Engine 服务代理是否已在项目中分配了 Kubernetes Engine Service Agent 和 Kubernetes Engine Default Node Service Agent 角色:
gcloud projects get-iam-policy PROJECT_ID
将
PROJECT_ID
替换为您的项目 ID。
如需解决此问题,如果有人从您的 Google Kubernetes Engine 服务代理中移除了 Kubernetes Engine Service Agent 或 Kubernetes Engine Default Node Service Agent,请按照以下说明操作,确保 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 Customer Care 联系,以提交支持请求。
- 通过在 StackOverflow 上提问并使用
google-kubernetes-engine
标记搜索类似问题,从社区获得支持。您还可以加入#kubernetes-engine
Slack 频道,以获得更多社区支持。 - 使用公开问题跟踪器提交 bug 或功能请求。