排查 kubectl 命令行工具问题

使用 Google Kubernetes Engine (GKE) 时,kubectl 命令行工具出现问题可能会导致您无法部署应用或管理集群资源。这些问题通常分为两类:身份验证失败(集群无法识别您的身份)和连接失败(您的工具无法访问集群的控制平面)。

您可借助此页面诊断和解决这些问题。查找相关步骤,以排查各种身份验证问题并调试 kubectl 工具与集群控制平面之间的连接问题。了解如何检查是否已安装并配置必要的插件,以及查看 SSH 和 Konnectivity 等服务的网络政策和防火墙注意事项。

对于使用 kubectl 命令管理 GKE 上的应用或集群资源的任何人来说,此信息都非常重要。对于依赖 kubectl 命令完成核心日常任务的应用开发者以及平台管理员和运维人员而言,这一点尤为重要。如需详细了解我们在 Google Cloud内容中提及的常见角色和示例任务,请参阅常见的 GKE 用户角色和任务

如需了解相关信息,请参阅以下资源:

身份验证和授权错误

如果您在使用 kubectl 命令行工具命令时遇到与身份验证和授权相关的错误,请参阅以下部分以获取建议。

错误:401(未授权)

连接到 GKE 集群时,您可能会收到 HTTP 状态代码为 401 (Unauthorized) 的身份验证和授权错误。如果您尝试通过本地环境在 GKE 集群中运行 kubectl 命令,可能会遇到此问题。如需了解详情,请参阅问题:身份验证和授权错误

错误:身份验证范围不足

运行 gcloud container clusters get-credentials 时,您可能会收到以下错误:

ERROR: (gcloud.container.clusters.get-credentials) ResponseError: code=403, message=Request had insufficient authentication scopes.

出现此错误的原因是您正在尝试从没有 cloud-platform 范围的 Compute Engine 虚拟机访问 GKE API。

如需解决此错误,请授予缺少的 cloud-platform 范围。如需了解如何在 Compute Engine 虚拟机实例上更改范围,请参阅 Compute Engine 文档中的为实例创建和启用服务账号

错误:找不到可执行的 gke-gcloud-auth-plugin

在尝试运行 kubectl 命令或与 GKE 交互的自定义客户端时,可能会出现类似以下内容的错误消息:

Unable to connect to the server: getting credentials: exec: executable gke-gcloud-auth-plugin not found

It looks like you are trying to use a client-go credential plugin that is not installed.

To learn more about this feature, consult the documentation available at:
      https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins

Visit cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl#install_plugin to install gke-gcloud-auth-plugin.
Unable to connect to the server: getting credentials: exec: fork/exec /usr/lib/google-cloud-sdk/bin/gke-gcloud-auth-plugin: no such file or directory

如需解决此问题,请按照安装所需的插件中的说明安装 gke-gcloud-auth-plugin

错误:找不到身份验证提供方

如果 kubectl 或自定义 Kubernetes 客户端使用 Kubernetes client-go 1.26 版或更高版本构建,则会发生以下错误:

no Auth Provider found for name "gcp"

如需解决此问题,请完成以下步骤:

  1. 按照安装所需的插件中的说明安装 gke-gcloud-auth-plugin

  2. 更新到最新版 gcloud CLI:

    gcloud components update
    
  3. 更新 kubeconfig 文件。

    gcloud container clusters get-credentials CLUSTER_NAME \
        --location=CONTROL_PLANE_LOCATION
    

    替换以下内容:

    • CLUSTER_NAME:您的集群的名称。
    • CONTROL_PLANE_LOCATION:集群控制平面的 Compute Engine 位置。为区域级集群提供区域,或为可用区级集群提供可用区。

错误:GCP 身份验证插件已弃用,请改用 gcloud

安装 gke-gcloud-auth-plugin 并对 GKE 集群运行 kubectl 命令后,您可能会看到以下警告消息:

WARNING: the gcp auth plugin is deprecated in v1.22+, unavailable in v1.25+; use gcloud instead.

如果您的客户端版本低于 1.26,则会显示此消息。

如需解决此问题,请指示客户端改用 gke-gcloud-auth-plugin 身份验证插件:

  1. 在文本编辑器中打开 shell 登录脚本:

    Bash

    vi ~/.bashrc

    Zsh

    vi ~/.zshrc

    如果您使用的是 PowerShell,请跳过此步骤。

  2. 设置以下环境变量:

    Bash

    export USE_GKE_GCLOUD_AUTH_PLUGIN=True
    

    Zsh

    export USE_GKE_GCLOUD_AUTH_PLUGIN=True
    

    PowerShell

    [Environment]::SetEnvironmentVariable('USE_GKE_GCLOUD_AUTH_PLUGIN', True, 'Machine')
    
  3. 在您的环境中应用该变量:

    Bash

    source ~/.bashrc

    Zsh

    source ~/.zshrc
    

    PowerShell

    退出终端并打开新的终端会话。

  4. 更新 gcloud CLI:

    gcloud components update
    
  5. 向集群进行身份验证:

    gcloud container clusters get-credentials CLUSTER_NAME \
        --location=CONTROL_PLANE_LOCATION
    

    请替换以下内容:

    • CLUSTER_NAME:您的集群的名称。
    • CONTROL_PLANE_LOCATION:集群控制平面的 Compute Engine 位置。为区域级集群提供区域,或为可用区级集群提供可用区。

问题:找不到 kubectl 命令

如果您收到一条消息,提示您未找到 kubectl 命令,请重新安装 kubectl 二进制文件并设置 $PATH 环境变量:

  1. 安装 kubectl 二进制文件:

    gcloud components update kubectl
    
  2. 当安装程序提示您修改 $PATH 环境变量时,请输入 y 以继续。修改此变量后,您可以在使用 kubectl 命令时无需键入其完整路径。

    或者,将以下行添加到 shell 存储环境变量的任何位置,例如 ~/.bashrc(在 macOS 中是 ~/.bash_profile):

    export PATH=$PATH:/usr/local/share/google/google-cloud-sdk/bin/
    
  3. 运行以下命令以加载更新后的文件。以下示例使用 .bashrc

    source ~/.bashrc
    

    如果您使用的是 macOS,请使用 ~/.bash_profile 而不是 .bashrc

问题:kubectl 命令返回“连接被拒绝”错误

如果 kubectl 命令返回“连接被拒绝”错误,则需要使用以下命令设置集群上下文:

gcloud container clusters get-credentials CLUSTER_NAME \
       --location=CONTROL_PLANE_LOCATION

替换以下内容:

  • CLUSTER_NAME:您的集群的名称。
  • CONTROL_PLANE_LOCATION:集群控制平面的 Compute Engine 位置。为区域级集群提供区域,或为可用区级集群提供可用区。

如果您不确定要为集群名称输入什么内容或位置,请使用以下命令列出您的集群:

gcloud container clusters list

错误:kubectl 命令超时

如果您创建了集群并尝试针对该集群运行 kubectl 命令,但 kubectl 命令超时,您会看到类似于以下内容的错误:

  • Unable to connect to the server: dial tcp IP_ADDRESS: connect: connection timed out
  • Unable to connect to the server: dial tcp IP_ADDRESS: i/o timeout

这些错误表示 kubectl 无法与集群控制平面通信。

如需解决此问题,请验证并设置集群所在的上下文,并确保与集群的连接:

  1. 前往 $HOME/.kube/config 或运行命令 kubectl config view 以验证配置文件是否包含集群上下文和控制平面的外部 IP 地址。

  2. 设置集群凭据:

    gcloud container clusters get-credentials CLUSTER_NAME \
        --location=CONTROL_PLANE_LOCATION \
        --project=PROJECT_ID
    

    替换以下内容:

    • CLUSTER_NAME:您的集群的名称。
    • CONTROL_PLANE_LOCATION:集群控制平面的 Compute Engine 位置。为区域级集群提供区域,或为可用区级集群提供可用区。
    • PROJECT_ID:在其中创建集群的项目的 ID。
  3. 如果您已在集群中启用授权网络,请确保其现有授权网络列表包含您尝试连接的计算机的传出 IP。您可以在控制台中查找现有授权网络,也可以通过运行以下命令来查找:

    gcloud container clusters describe CLUSTER_NAME \
        --location=CONTROL_PLANE_LOCATION \
        --project=PROJECT_ID \
        --format "flattened(controlPlaneEndpointsConfig.ipEndpointsConfig.authorizedNetwork
    sConfig.cidrBlocks[])"
    

    如果上述命令输出的授权网络列表中未包含机器的传出 IP,请完成以下步骤之一:

错误:kubectl 命令返回“无法协商 API 版本”

如果 kubectl 命令返回 failed to negotiate an API version 错误,则需要确保 kubectl 具有身份验证凭据:

gcloud auth application-default login

问题:kubectl logsattachexecport-forward 命令停止响应

如果 kubectl logsattachexecport-forward 命令停止响应,通常表示 API 服务器无法与节点通信。

首先,检查您的集群是否有任何节点。如果您已将集群中的节点数减少到零,则命令将无法运行。如需解决此问题,请调整集群大小以至少拥有一个节点。

如果您的集群至少有一个节点,那么请检查您是使用 SSH 还是 Konnectivity 代理隧道来实现安全通信。以下各部分介绍了针对每项服务的特定问题排查步骤:

排查 SSH 问题

如果您使用的是 SSH,GKE 会在您的 Compute Engine 项目元数据中保存一个 SSH 公钥文件。使用 Google 提供的映像的所有 Compute Engine 虚拟机会定期检查其项目的公共元数据及其实例的元数据,以使 SSH 公钥添加到虚拟机的授权用户列表中。GKE 还为您的 Compute Engine 网络添加了防火墙规则,以允许通过 SSH 实现从控制平面的 IP 地址到集群中每个节点的访问。

以下设置可能会导致 SSH 通信出现问题:

  • 您的网络的防火墙规则不允许通过 SSH 从控制平面访问。

    所有 Compute Engine 网络都使用名为 default-allow-ssh 的防火墙规则创建,该规则允许从所有 IP 地址进行 SSH 访问(需要使用有效的私钥)。GKE 还为每个公共集群插入一项 SSH 规则(格式为 gke-CLUSTER_NAME-RANDOM_CHARACTERS-ssh),该规则允许通过 SSH 实现专门从集群的控制平面到集群的节点的访问。

    若没有这些规则,则控制平面无法打开 SSH 隧道。

    如需验证是否是此原因导致的问题,请检查您的配置是否包含这些规则。

    如需解决此问题,请确定所有集群节点上的标记,然后重新添加防火墙规则,以允许从控制平面的 IP 地址访问具有该标记的虚拟机。

  • 您的项目的 ssh-keys 的公共元数据条目已满。

    如果名为 ssh-keys 的项目元数据条目接近其大小上限,则 GKE 将无法添加自己的 SSH 密钥来打开 SSH 隧道。

    如需验证是否存在此问题,请检查 ssh-keys 列表的长度。您可以通过运行以下命令并酌情添加 --project 标志来查看项目的元数据:

    gcloud compute project-info describe [--project=PROJECT_ID]
    

    如需解决此问题,对不再需要的密钥,请删除一些 SSH 密钥

  • 您已在集群中的虚拟机上使用密钥 ssh-keys 设置元数据字段。

    相比项目范围的 SSH 密钥,虚拟机上的节点代理会优先使用每个实例的 SSH 密钥,因此如果您在集群的节点上专门设置了任何 SSH 密钥,则节点将不会遵循项目元数据中控制平面的 SSH 密钥。

    如需验证是否存在此问题,请运行 gcloud compute instances describe VM_NAME 并在元数据中查找 ssh-keys 字段。

    如需解决此问题,请从实例元数据中删除每个实例的 SSH 密钥

排查 Konnectivity 代理问题

您可以通过检查以下系统 Deployment 来确定集群是否使用 Konnectivity 代理:

kubectl get deployments konnectivity-agent --namespace kube-system

如果您的集群使用 Konnectivity 代理,则输出类似于以下内容:

NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
konnectivity-agent   3/3     3            3           18d

验证您使用的是 Konnectivity 代理后,请确保 Konnectivity 代理具有所需的防火墙访问权限,并且您的网络政策设置正确无误。

允许所需的防火墙访问权限

检查您网络的防火墙规则是否允许访问以下端口:

  • 控制平面端口:在创建集群时,Konnectivity 代理会在端口 8132 上建立与控制平面的连接。运行 kubectl 命令时,API 服务器会使用此连接与集群进行通信。请确保允许出站流量通过端口 8132 进入集群控制平面(相比之下,API 服务器使用 443)。如果您有拒绝出站流量访问的规则,可能需要修改这些规则或创建例外情况。
  • kubelet 端口:由于 Konnectivity 代理是部署在集群节点上的系统 Pod,因此请确保防火墙规则允许以下类型的流量:

    • 从 Pod 范围到工作负载端口 10250 的入站流量。
    • 从 Pod 范围传出的流量。

    如果您的防火墙规则不允许此类流量,请修改规则

调整网络政策

如果集群的网络政策执行以下任一操作,Konnectivity 代理可能会出现问题:

  • 阻止从 kube-system 命名空间到 workload 命名空间的入站流量
  • 阻止通过端口 8132 出站到集群控制平面的流量

当工作负载 Pod 的网络政策阻止入站流量时,konnectivity-agent 日志会包含类似于以下内容的错误消息:

"error dialing backend" error="dial tcp POD_IP_ADDRESS:PORT: i/o timeout"

在错误消息中,POD_IP_ADDRESS 是工作负载 Pod 的 IP 地址。

当出站流量被网络政策阻止时,konnectivity-agent 日志会包含类似于以下内容的错误消息:

"cannot connect once" err="rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp CP_IP_ADDRESS:8132: i/o timeout

在错误消息中,CP_IP_ADDRESS 是集群控制平面的 IP 地址。

集群的正确运行并不需要这些功能。如果您希望集群的网络拒绝所有外部访问,请注意这些功能将停止工作。

如需验证问题是否由网络政策入站或出站规则引起,请通过运行以下命令查找受影响命名空间中的网络政策:

kubectl get networkpolicy --namespace AFFECTED_NAMESPACE

如需解决入站政策方面的问题,请将以下内容添加到网络政策的 spec.ingress 字段中:

ingress:
- from:
  - namespaceSelector:
      matchLabels:
        kubernetes.io/metadata.name: kube-system
    podSelector:
      matchLabels:
        k8s-app: konnectivity-agent

如需解决出站政策问题,请将以下内容添加到网络政策的 spec.egress 字段中:

egress:
- to:
  - ipBlock:
      cidr: CP_IP_ADDRESS/32
  ports:
  - protocol: TCP
    port: 8132

如果您的网络政策同时使用入站规则和出站规则,请考虑同时调整这两者。

调整 IP 伪装代理

如果源 IP 地址位于 Pod IP 地址范围内,集群控制平面会接受来自 Konnectivity 代理的流量。如果您修改 ip-masq-agent 的配置,以伪装流向集群控制平面的流量的源 IP 地址,Konnectivity 代理可能会遇到连接错误。

为解决此问题并帮助确保从 Konnectivity 代理到集群控制平面的流量不会伪装到节点 IP 地址,请将控制平面 IP 地址添加到 ip-masq-agent ConfigMap 中的 nonMasqueradeCIDRs 列表:

nonMasqueradeCIDRs:
- CONTROL_PLANE_IP_ADDRESS/32

如需详细了解此配置,请参阅 IP 伪装代理

错误:kubectl 命令失败,并显示“没有可用的代理”错误

当您运行需要从 GKE 控制平面连接到 Pod 的 kubectl 命令(例如 kubectl execkubectl logskubectl port-forward)时,该命令可能会失败,并显示类似于以下内容的错误消息:

Error from server: error dialing backend: No agent available
failed to call webhook: Post "https://WEBHOOK_SERVICE.WEBHOOK_NAMESPACE.svc:PORT/PATH?timeout=10s": No agent available
v1beta1.metrics.k8s.io failed with: failing or missing response from https://NODE_IP:10250/apis/metrics.k8s.io/v1beta1: Get "https://NODE_IP:10250/apis/metrics.k8s.io/v1beta1": No agent available

这些错误表明 Konnectivity(GKE 控制平面与集群节点之间的安全通信隧道)存在问题。具体而言,这意味着控制平面上的 konnectivity-server 无法连接到 kube-system 命名空间中的任何运行正常的 konnectivity-agent Pod。

如需解决此问题,请尝试以下解决方案:

  1. 验证 konnectivity-agent Pod 的健康状况:

    1. 检查 konnectivity-agent Pod 是否正在运行:

      kubectl get pods -n kube-system -l k8s-app=konnectivity-agent
      

      输出类似于以下内容:

      NAME                                   READY   STATUS    RESTARTS  AGE
      konnectivity-agent-abc123def4-xsy1a    2/2     Running   0         31d
      konnectivity-agent-abc123def4-yza2b    2/2     Running   0         31d
      konnectivity-agent-abc123def4-zxb3c    2/2     Running   0         31d
      

      查看 Status 列中的值。如果 Pod 的状态为 Running,请查看日志以了解连接问题。否则,请调查 Pod 未运行的原因。

    2. 查看日志以了解连接问题。如果 Pod 的状态为 Running,请检查日志中是否存在连接问题。由于 kubectl logs 命令依赖于 Konnectivity,因此请在Google Cloud 控制台中使用 Logs Explorer:

      1. 在 Google Cloud 控制台中,前往 Logs Explorer

        转到日志浏览器

      2. 在查询窗格中,输入以下查询。

        resource.type="k8s_container"
        resource.labels.cluster_name="CLUSTER_NAME"
        resource.labels.namespace_name="kube-system"
        labels."k8s-pod/k8s-app"="konnectivity-agent"
        resource.labels.container_name="konnectivity-agent"
        

        CLUSTER_NAME 替换为您的集群名称。

      3. 点击运行查询

      4. 查看输出。查看 konnectivity-agent 日志时,请查找指示代理无法连接的原因的错误。身份验证或权限错误通常表示配置错误的网络钩子阻止了令牌审核。“连接被拒绝”或“超时”错误通常表示防火墙规则或网络政策正在阻止 TCP 端口 8132 上流向控制平面的流量,或者正在阻止 Konnectivity 代理与其他节点之间的流量。证书错误表明防火墙或代理正在检查并干扰加密的 TLS 流量。

    3. 调查 Pod 未运行的原因。如果 Pod 的状态为 Pending 或其他非运行状态,请调查原因。konnectivity-agent 作为 Deployment 运行,而不是作为 DaemonSet 运行。由于它作为 Deployment 运行,因此代理 Pod 只需要在一部分节点上运行。不过,如果该特定节点子集不可用,整个服务可能会失败。

      Pod 未运行的常见原因包括:

      • 可防止 Pod 被调度的自定义节点污点。
      • 节点资源(CPU 或内存)不足。
      • 会阻止 GKE 系统映像的限制性 Binary Authorization 政策。

      如需详细了解特定 Pod 未运行的原因,请使用 kubectl describe 命令:

      kubectl describe pod POD_NAME -n kube-system
      

      POD_NAME 替换为未运行的 Pod 的名称。

  2. 调查您的准入网络钩子,确保没有一个会阻止 TokenReview API 请求。konnectivity-agent 依赖于服务账号令牌,因此干扰令牌审核可能会阻止代理连接。如果问题是由网络钩子引起的,则在移除或修复有问题的网络钩子之前,Konnectivity 无法恢复。

  3. 确保防火墙规则允许从 GKE 节点到控制平面 IP 地址(端口 8132)的 TCP 出站流量。konnectivity-agent 需要通过此连接来访问 Konnectivity 服务。如需了解详情,请参阅允许所需的防火墙访问权限

  4. 确保没有限制必要 Konnectivity 流量的网络政策规则。网络政策规则应允许 kube-system 命名空间内的集群内流量 (Pod 到 Pod) 和从 konnectivity-agent Pod 到 GKE 控制平面的出站流量。

后续步骤