排查已部署工作负载的问题


本页面介绍了如何解决在 Google Kubernetes Engine (GKE) 中部署的工作负载的错误。

如需了解有关排查应用问题的更多一般性建议,请参阅 Kubernetes 文档中的排查应用问题

所有错误:检查 Pod 状态

如果工作负载的 Pod 存在问题,Kubernetes 会更新 Pod 状态并显示错误消息。使用 Google Cloud 控制台或 kubectl 命令行工具检查 Pod 的状态,以查看这些错误。

控制台

执行以下步骤:

  1. 在 Google Cloud 控制台中,转到工作负载页面。

    转到“工作负载”

  2. 选择要调查的工作负载。概览标签页显示工作负载的状态。

  3. 托管式 Pod 部分中,点击任何错误状态消息。

kubectl

如需查看集群中正在运行的所有 Pod,请运行以下命令:

kubectl get pods

输出类似于以下内容:

NAME       READY  STATUS             RESTARTS  AGE
POD_NAME   0/1    CrashLoopBackOff   23        8d

潜在错误会列在 Status 列中。

如需获取有关特定 Pod 的更多详细信息,请运行以下命令:

kubectl describe pod POD_NAME

POD_NAME 替换为您要调查的 Pod 的名称。

在输出中,Events 字段会显示有关错误的详细信息。

如需详细信息,请查看容器日志:

kubectl logs POD_NAME

这些日志可帮助您确定是容器中的命令还是代码导致了 Pod 崩溃。

确定错误后,请使用以下部分,尝试解决问题。

错误:CrashLoopBackOff

状态为 CrashLoopBackOff 并不表示存在特定错误,而是表示容器在重启后反复崩溃。当容器在启动 (CrashLoop) 后不久崩溃或退出时,Kubernetes 会尝试重启该容器。每次重启失败后,下次尝试之前的延迟时间 (BackOff) 会以指数方式增加(10 秒、20 秒、40 秒等),最长为 5 分钟。

以下部分可帮助您确定容器可能崩溃的原因。

使用崩溃循环的 Pod 交互式 playbook

使用 Google Cloud 控制台中的交互式 playbook 开始排查导致 CrashLoopBackOff 状态的问题:

  1. 前往崩溃循环的 Pod 交互式 playbook:

    前往 Playbook

  2. 集群下拉列表中,选择您要排查问题的集群。如果您找不到集群,请在 过滤条件字段中输入集群的名称。

  3. 命名空间下拉列表中,选择您要排查问题的命名空间。如果您找不到命名空间,请在 过滤条件字段中输入命名空间。

  4. 请逐一查看以下各部分,以帮助您确定原因:

    1. 识别应用错误
    2. 调查内存不足问题
    3. 调查节点中断
    4. 调查活跃性探测故障问题
    5. 关联更改事件
  5. 可选:如需接收有关未来 CrashLoopBackOff 错误的通知,请在未来应对措施提示部分中选择创建提醒

检查日志

容器可能由于多种原因而崩溃,检查 Pod 的日志可帮助您排查根本原因。

您可以使用 Google Cloud 控制台或 kubectl 命令行工具检查日志。

控制台

执行以下步骤:

  1. 进入 Google Cloud 控制台中的工作负载页面。

    转到“工作负载”

  2. 选择要调查的工作负载。概览标签页显示工作负载的状态。

  3. 托管 Pod 部分中,点击有问题的 Pod。

  4. 在 Pod 的菜单中,点击日志标签页。

kubectl

  1. 查看集群中正在运行的所有 Pod:

    kubectl get pods
    
  2. 在上述命令的输出中,查找 Status 列中存在 CrashLoopBackOff 错误的 Pod。

  3. 获取 Pod 的日志:

    kubectl logs POD_NAME
    

    POD_NAME 替换为有问题的 Pod 的名称。

    您还可以传入 -p 标志,以获取 Pod 容器的上一个实例的日志(如果存在)。

检查崩溃容器的退出代码

如需更好地了解容器崩溃的原因,请查找退出代码:

  1. 描述 Pod:

    kubectl describe pod POD_NAME
    

    POD_NAME 替换为有问题的 Pod 的名称。

  2. 查看 containers: CONTAINER_NAME: last state: exit code 字段中的值:

    • 如果退出代码为 1,表示容器因为应用崩溃而崩溃。
    • 如果退出代码为 0,请检查您的应用运行了多长时间。当应用的主进程退出时,容器将退出。如果您的应用很快完成执行,容器可能会继续重启。如果您遇到此错误,一种解决方法是将 restartPolicy 字段设置为 OnFailure。进行此更改后,只有在退出代码不为 0 时,应用才会重启。

连接到正在运行的容器

如需从容器运行 bash 命令,以便您可以测试网络或检查您是否有权访问应用使用的文件或数据库,请打开 Pod 的 shell:

kubectl exec -it POD_NAME -- /bin/bash

如果 Pod 中有多个容器,请添加 -c CONTAINER_NAME

错误:ImagePullBackOff 和 ErrImagePull

状态为 ImagePullBackOffErrImagePull 表示无法从映像注册表加载容器使用的映像。

您可以使用 Google Cloud 控制台或 kubectl 命令行工具验证此问题。

控制台

执行以下步骤:

  1. 在 Google Cloud 控制台中,转到工作负载页面。

    转到“工作负载”

  2. 选择要调查的工作负载。概览标签页显示工作负载的状态。

  3. 托管 Pod 部分中,点击有问题的 Pod。

  4. 在 Pod 的菜单中,点击事件标签页。

kubectl

要获取有关 Pod 容器映像的更多信息,请运行以下命令:

kubectl describe pod POD_NAME

问题:找不到映像

如果找不到映像,请完成以下步骤:

  1. 验证映像的名称是否正确。
  2. 验证映像的标记是否正确。(尝试使用 :latest 命令或不添加标记来拉取最新映像)。
  3. 如果映像具有完整的注册表路径,请验证它是否存在于您正在使用的 Docker 注册表中。如果仅提供映像名称,请检查 Docker Hub 注册表。
  4. 在 GKE Standard 集群中,尝试手动拉取 Docker 映像:

    1. 使用 SSH 连接到节点:

      例如,如需使用 SSH 连接到虚拟机,请运行以下命令:

      gcloud compute ssh VM_NAME --zone=ZONE_NAME
      

      替换以下内容:

    2. /home/[USER]/.docker/config.json 生成配置文件:

      docker-credential-gcr configure-docker
      

      确保位于 /home/[USER]/.docker/config.json 的配置文件包含 credHelpers 字段中的映像注册表。例如,以下文件包含托管在 asia.gcr.ioeu.gcr.iogcr.iomarketplace.gcr.ious.gcr.io 中的映像的身份验证信息:

      {
      "auths": {},
      "credHelpers": {
        "asia.gcr.io": "gcr",
        "eu.gcr.io": "gcr",
        "gcr.io": "gcr",
        "marketplace.gcr.io": "gcr",
        "us.gcr.io": "gcr"
      }
      }
      
    3. 尝试拉取映像:

      docker pull IMAGE_NAME
      

    如果手动拉取映像有效,则可能需要在 Pod 上指定 ImagePullSecrets。Pod 只能在自己的命名空间中引用映像拉取 Secret,因此每个命名空间需要执行一次此过程。

错误:权限遭拒

如果您遇到“权限被拒绝”或“无拉取路径”错误,请确认您已登录并有权访问该映像。根据您托管映像的注册表,尝试以下方法之一。

Artifact Registry

如果您的映像位于 Artifact Registry 中,您的节点池的服务账号需要对包含该映像的代码库拥有读取访问权限。

artifactregistry.reader 角色授予服务账号:

gcloud artifacts repositories add-iam-policy-binding REPOSITORY_NAME \
    --location=REPOSITORY_LOCATION \
    --member=serviceAccount:SERVICE_ACCOUNT_EMAIL \
    --role="roles/artifactregistry.reader"

替换以下内容:

  • REPOSITORY_NAME:您的 Artifact Registry 代码库的名称。
  • REPOSITORY_LOCATION:您的 Artifact Registry 代码库的区域
  • SERVICE_ACCOUNT_EMAIL:与节点池关联的 IAM 服务账号的电子邮件地址。

Container Registry

如果您的映像位于 Container Registry 中,您的节点池的服务账号需要对包含该映像的 Cloud Storage 存储桶拥有读取访问权限。

roles/storage.objectViewer 角色授予服务账号,以便它可以从存储桶读取数据:

gcloud storage buckets add-iam-policy-binding gs://BUCKET_NAME \
    --member=serviceAccount:SERVICE_ACCOUNT_EMAIL \
    --role=roles/storage.objectViewer

替换以下内容:

  • SERVICE_ACCOUNT_EMAIL:与节点池关联的服务账号的电子邮件。您可以使用 gcloud iam service-accounts list 列出项目中的所有服务账号。
  • BUCKET_NAME:包含您的映像的 Cloud Storage 存储桶的名称。您可以使用 gcloud storage ls 列出项目中的所有存储桶。

如果您的注册表管理员在 Artifact Registry 中设置 gcr.io 代码库,以存储 gcr.io 网域(而非 Container Registry)的映像,您必须授予对 Artifact Registry(而不是 Container Registry)的读取权限。

私有注册表

如果您的映像位于私有注册表中,您可能需要密钥才能访问映像。如需了解详情,请参阅 Kubernetes 文档中的使用私有注册表

错误 401 未经授权:无法从私有 Container Registry 仓库中拉取映像

从私有 Container Registry 代码库中拉取映像时,可能会发生类似于以下内容的错误:

gcr.io/PROJECT_ID/IMAGE:TAG: rpc error: code = Unknown desc = failed to pull and
unpack image gcr.io/PROJECT_ID/IMAGE:TAG: failed to resolve reference
gcr.io/PROJECT_ID/IMAGE]:TAG: unexpected status code [manifests 1.0]: 401 Unauthorized

Warning  Failed     3m39s (x4 over 5m12s)  kubelet            Error: ErrImagePull
Warning  Failed     3m9s (x6 over 5m12s)   kubelet            Error: ImagePullBackOff
Normal   BackOff    2s (x18 over 5m12s)    kubelet            Back-off pulling image

如需解决此错误,请完成以下步骤:

  1. 确定运行 Pod 的节点:

    kubectl describe pod POD_NAME | grep "Node:"
    
  2. 验证您在上一步中确定的节点是否具有存储范围:

    gcloud compute instances describe NODE_NAME \
        --zone=COMPUTE_ZONE --format="flattened(serviceAccounts[].scopes)"
    

    节点的访问权限范围应至少包含以下一个范围:

    serviceAccounts[0].scopes[0]: https://www.googleapis.com/auth/devstorage.read_only
    serviceAccounts[0].scopes[0]: https://www.googleapis.com/auth/cloud-platform
    

    如果节点不包含以上范围之一,请重新创建节点池。

  3. 使用足够的范围重新创建节点所属的节点池。您无法修改现有节点,必须使用正确的范围重新创建节点。

    • 推荐:使用 gke-default 范围创建新的节点池:

      gcloud container node-pools create NODE_POOL_NAME \
          --cluster=CLUSTER_NAME \
          --zone=COMPUTE_ZONE \
          --scopes="gke-default"
      
    • 仅使用存储范围创建新的节点池:

      gcloud container node-pools create NODE_POOL_NAME \
          --cluster=CLUSTER_NAME \
          --zone=COMPUTE_ZONE \
          --scopes="https://www.googleapis.com/auth/devstorage.read_only"
      

错误:Pod 无法调度

状态为 PodUnschedulable 表示由于资源不足或某些配置错误而无法调度 Pod。

如果您已配置控制平面指标,则您可以在调度器指标API 服务器指标中找到有关这些错误的详细信息。

使用无法调度的 Pod 交互式 playbook

您可以使用 Google Cloud 控制台中的交互式 playbook 排查 PodUnschedulable 错误:

  1. 前往无法调度的 Pod 交互式 playbook:

    前往 Playbook

  2. 集群下拉列表中,选择您要排查问题的集群。如果您找不到集群,请在 过滤条件字段中输入集群的名称。

  3. 命名空间下拉列表中,选择您要排查问题的命名空间。如果您找不到命名空间,请在 过滤条件字段中输入命名空间。

  4. 为帮助您找出原因,请逐一查看该 playbook 中的各个部分:

    1. 调查 CPU 和内存
    2. 调查每个节点的 Pod 数量上限
    3. 调查自动扩缩器行为
    4. 调查其他故障模式
    5. 关联更改事件
  5. 可选:如需接收有关未来 PodUnschedulable 错误的通知,请在未来应对措施提示部分中选择创建提醒

错误:资源不足

您可能会遇到指示缺少 CPU、内存或其他资源的错误。例如:No nodes are available that match all of the predicates: Insufficient cpu (2),表示在两个节点上没有足够的 CPU 可用于满足 Pod 的请求。

如果您的 Pod 资源请求量超过任何符合条件的节点池中单个节点所能提供的资源,则 GKE 不会安排 Pod,也不会触发扩容以添加新节点。要让 GKE 对 Pod 进行安排,您必须为 Pod 请求少量的资源,或者创建具有足够资源的新节点池。

此外,您还可以启用节点自动预配功能,以便 GKE 能够自动创建节点池,这类节点池具有可运行未安排的 Pod 的节点。

默认 CPU 请求是 100M 或 CPU(或一个核心)的 10%。如果您想请求更多或更少的资源,请在 spec: containers: resources: requests 下的 Pod 规范中指定该值

错误:MatchNodeSelector

MatchNodeSelector 指示没有与 Pod 的标签选择器匹配的节点。

如需验证这一点,请在 spec: nodeSelector 下,检查 Pod 规范中的 nodeSelector 字段指定的标签。

如需查看集群中节点的标签,请运行以下命令:

kubectl get nodes --show-labels

如需将标签附加到节点,请运行以下命令:

kubectl label nodes NODE_NAME LABEL_KEY=LABEL_VALUE

替换以下内容:

  • NODE_NAME:要为其添加标签的节点。
  • LABEL_KEY:标签的键。
  • LABEL_VALUE:标签的值。

如需了解详情,请参阅 Kubernetes 文档中的为节点分配 Pod

错误:PodToleratesNodeTaints

PodToleratesNodeTaints 指示 Pod 无法调度到任何节点上,因为 Pod 没有与现有节点污点相对应的容忍设置。

如需验证属于这种情况,请运行以下命令:

kubectl describe nodes NODE_NAME

在输出中,选中 Taints 字段,该字段列出了键值对和调度效果。

如果列出的效果是 NoSchedule,则在该节点上没有 Pod 可以计划,除非它有一个匹配的负荷

解决此问题的一种方法是移除污点。例如,如需移除 NoSchedule 污点,请运行以下命令:

kubectl taint nodes NODE_NAME key:NoSchedule-

错误:PodFitsHostPorts

PodFitsHostPorts 指示节点正在尝试使用的端口已在使用中。

如需解决此问题,请在 spec: containers: ports: hostPort 下查看 Pod 规范的 hostPort 值。您可能需要将此值更改为另一个端口。

错误:不具备最低限度的可用性

如果节点有足够的资源但仍然显示 Does not have minimum availability 消息,请查看 Pod 的状态。如果状态为 SchedulingDisabledCordoned,则节点无法调度新 Pod。您可以使用 Google Cloud 控制台或 kubectl 命令行工具检查节点的状态。

控制台

执行以下步骤:

  1. 转到 Google Cloud 控制台中的 Google Kubernetes Engine 页面。

    转到 Google Kubernetes Engine

  2. 选择您要调查的集群。节点标签页显示节点及其状态。

如需在节点上启用调度,请执行以下步骤:

  1. 在列表中,点击您要调查的节点。

  2. 节点详情部分中,点击取消封锁

kubectl

如需获取节点的状态,请运行以下命令:

kubectl get nodes

如需在节点上启用调度,请运行:

kubectl uncordon NODE_NAME

错误:已达到每个节点的 Pod 数上限

如果集群中的所有节点都达到每个节点的 Pod 数上限,Pod 会卡在“无法调度”状态。在 Pod 事件标签页下,您会看到包含 Too many pods 字词的消息。

要解决此错误,请完成以下步骤:

  1. 在 Google Cloud 控制台中,通过 GKE 集群详情中的“节点”标签页查看 Maximum pods per node 配置。

  2. 获取节点列表:

    kubectl get nodes
    
  3. 对于每个节点,请验证在节点上运行的 Pod 数量:

    kubectl get pods -o wide | grep NODE_NAME | wc -l
    
  4. 如果达到了上限,请添加新的节点池,或向现有节点池添加更多节点。

问题:在启用集群自动扩缩器的情况下,已达到节点池大小上限

如果节点池已达到大小上限,根据集群自动扩缩器配置,GKE 不会触发使用此节点池安排的 Pod 纵向扩容。如果您希望使用此节点池调度 Pod,请更改集群自动扩缩器配置

问题:在停用集群自动扩缩器的情况下,已达到节点池大小上限

如果节点池已达到节点数量上限,并且集群自动扩缩器已停用,则 GKE 无法使用节点池调度 Pod。增加节点池的大小启用集群自动扩缩器,以便 GKE 自动调整集群的大小。

错误:PersistentVolumeClaims 未绑定

Unbound PersistentVolumeClaims 指示 Pod 引用了未绑定的 PersistentVolumeClaim。如果您的 PersistentVolume 预配失败,可能会发生此错误。您可以通过获取 PersistentVolumeClaim 事件并检查其是否失败来验证预配是否失败。

如需获取事件,请运行以下命令:

kubectl describe pvc STATEFULSET_NAME-PVC_NAME-0

请替换以下内容:

  • STATEFULSET_NAME:StatefulSet 对象的名称。
  • PVC_NAME:PersistentVolumeClaim 对象的名称。

如果您在手动预配 PersistentVolume 并将其绑定到 PersistentVolumeClaim 期间遇到配置错误,也可能会出现此问题。

如需解决此错误,请尝试再次预配卷。

错误:配额不足

验证您的项目是否有足够的 Compute Engine 配额供 GKE 纵向扩容集群。如果 GKE 尝试向集群添加节点以安排 Pod,并且扩容将超出项目的可用配额,则您会收到 scale.up.error.quota.exceeded 错误消息。

如需了解详情,请参阅扩容错误

问题:API 已弃用

确保您没有使用已弃用的 API,这些 API 已与集群的次要版本一起移除。如需了解详情,请参阅 GKE 弃用

后续步骤

如果您需要其他帮助,请与 Cloud Customer Care 联系。