别担心,这不是什么世界末日。Pending状态其实就是Kubernetes在告诉你:“我知道你要运行这个Pod了,但我还在为它安排一个合适的‘座位’(也就是节点),或者正在准备它需要的‘行李’(比如镜像、存储卷等)。” 今天,我就带大家像侦探一样,一步步排查这个问题,让我们的Pod顺利跑起来。

一、第一眼:获取基本信息,看清现场

当发现Pod卡在Pending状态时,我们的第一反应不应该是重启或者删除重来。先冷静下来,收集信息。就像医生看病要先问诊一样。

首先,我们可以用kubectl describe命令来查看这个Pod的详细事件。这些事件就像是Kubernetes系统留下的“日志”,会告诉我们到底卡在了哪一步。

技术栈: Kubernetes (kubectl)

# 假设我们的Pod名字叫 my-stuck-pod,位于 default 命名空间
kubectl describe pod my-stuck-pod -n default

这个命令会输出一大堆信息。我们需要重点关注 Events: 部分。这里通常会直接告诉我们原因。常见的提示有:

  • Insufficient cpu/memory: 节点CPU或内存不足。
  • 0/3 nodes are available: 1 Insufficient cpu, 2 node(s) didn't match Pod's node affinity/selector: 节点资源不足或调度规则不匹配。
  • pod has unbound immediate PersistentVolumeClaims: 存储卷声明没有绑定成功。
  • failed to pull image "some-image:tag": 镜像拉取失败。

如果事件信息不够明确,我们就需要进入更深层次的排查。

二、深入排查:四大常见“罪魁祸首”

通常,Pod卡在Pending状态离不开下面四大原因。我们按照从常见到特殊的顺序来排查。

1. 资源不足:节点“心有余而力不足”

这是最常见的原因。你的Pod申请了2核CPU和4G内存,但集群里所有节点都挤不出这么多资源了。

排查方法: 查看集群节点的资源分配情况。

# 查看所有节点的资源请求和限制情况,非常直观
kubectl describe nodes

在输出中,找到 Allocatable(可分配总量)和 Allocated resources(已分配资源)部分进行对比。或者使用更简洁的命令:

# 这个命令能更清晰地展示每个节点的资源使用情况
kubectl top nodes

示例场景与解决: 假设我们有一个申请资源较多的Pod配置文件。

技术栈: Kubernetes (YAML)

apiVersion: v1
kind: Pod
metadata:
  name: resource-hungry-app
spec:
  containers:
  - name: app
    image: nginx:latest
    resources:
      requests:  # 容器启动至少需要的资源
        memory: "8Gi"  # 申请了8G内存
        cpu: "2"       # 申请了2核CPU
      limits:    # 容器运行最多能使用的资源
        memory: "10Gi"
        cpu: "3"

如果集群中没有节点能满足至少8G内存和2核CPU的空闲资源,这个Pod就会一直Pending。

解决办法:

  • 扩容节点:给集群增加新的工作节点。
  • 调整资源请求:优化你的应用,或者重新评估requests值是否设置过高。requests是调度依据,limits是运行限制,两者概念不同。
  • 清理资源:删除一些不再需要的Pod,释放资源。

2. 节点选择器与亲和性:Pod的“择偶标准”太苛刻

有时候,我们给Pod设置了“择偶标准”(节点选择器nodeSelector或亲和性affinity),要求它必须运行在带有特定标签的节点上。如果找不到符合条件的节点,Pod就只能等待。

排查方法: 检查Pod的配置YAML,看是否设置了nodeSelectornodeAffinity。同时,检查节点标签是否匹配。

# 查看Pod的配置定义,寻找nodeSelector等字段
kubectl get pod my-pod -o yaml | grep -A5 -B5 "nodeSelector\|affinity"

# 查看集群所有节点的标签
kubectl get nodes --show-labels

示例场景与解决: 技术栈: Kubernetes (YAML)

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  containers:
  - name: cuda-app
    image: nvidia/cuda:11.0-base
  nodeSelector:  # 节点选择器:要求节点必须有 disktype=ssd 和 gpu=true 这两个标签
    disktype: ssd
    gpu: "true"

如果集群中没有任何一个节点同时拥有 disktype=ssdgpu=true 这两个标签,这个Pod就无法被调度。

解决办法:

  • 给节点打标签:找到一个合适的节点,为其打上缺失的标签。
    kubectl label nodes <node-name> disktype=ssd gpu=true
    
  • 修改Pod配置:放宽或移除过于苛刻的节点选择条件。

3. 污点与容忍度:节点“谢绝入住”,Pod没有“通行证”

与亲和性相反,污点(Taint) 是打在节点上的,用于“排斥”某些Pod。而Pod需要通过容忍度(Toleration) 来声明自己能忍受哪些污点,才能调度到该节点。Master节点默认就有污点,防止普通Pod调度上去。

排查方法: 检查节点上的污点,以及Pod是否设置了对应的容忍度。

# 查看节点的污点信息
kubectl describe node <node-name> | grep Taint

# 查看Pod的容忍度配置
kubectl get pod my-pod -o yaml | grep -A10 "tolerations"

示例场景与解决: 技术栈: Kubernetes (YAML)

# 节点被打上了一个污点
# 执行命令给节点打污点:kubectl taint nodes node1 special=true:NoSchedule

apiVersion: v1
kind: Pod
metadata:
  name: normal-pod
spec:
  containers:
  - name: app
    image: nginx
  # 这个Pod没有设置任何容忍度,因此无法被调度到带有污点的node1节点上

上面的normal-pod因为没有容忍 special=true:NoSchedule 这个污点,所以无法调度到node1

解决办法:

  • 为Pod添加容忍度
    apiVersion: v1
    kind: Pod
    metadata:
      name: tolerant-pod
    spec:
      containers:
      - name: app
        image: nginx
      tolerations:  # 添加容忍度
      - key: "special"
        operator: "Equal"
        value: "true"
        effect: "NoSchedule"
    
  • 移除节点污点(如果不必要):
    kubectl taint nodes node1 special:NoSchedule-
    

4. 存储卷问题:承诺的“移动硬盘”没到位

如果Pod声明要使用持久化存储(PersistentVolumeClaim, PVC),但对应的PVC没有成功绑定到实际的存储卷(PersistentVolume, PV),Pod也会卡在Pending。

排查方法: 检查Pod关联的PVC状态。

# 查看PVC状态,关注STATUS字段是否为Bound
kubectl get pvc

# 如果PVC是Pending,describe它查看原因
kubectl describe pvc <pvc-name>

常见原因:没有可用的PV满足PVC的请求(如存储大小、访问模式、存储类)。

示例场景与解决: 技术栈: Kubernetes (YAML)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-data-pvc
spec:
  storageClassName: fast-ssd  # 指定需要 fast-ssd 这个存储类提供的卷
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Gi  # 申请100G空间
---
apiVersion: v1
kind: Pod
metadata:
  name: app-with-storage
spec:
  containers:
  - name: app
    image: nginx
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: my-data-pvc  # 使用上面定义的PVC

如果集群中没有fast-ssd存储类,或者该存储类无法提供100G的卷,那么my-data-pvc就会一直处于Pending,进而导致Pod也Pending

解决办法:

  • 检查StorageClass:确保指定的storageClassName存在且可用 (kubectl get storageclass)。
  • 管理员提供PV:如果是静态配置,需要管理员创建符合要求的PV。
  • 调整PVC需求:减小请求的存储大小,或使用默认的StorageClass。

三、进阶与边缘案例

排查完以上四点,99%的问题都能解决。但如果还不行,可以考虑这些边缘情况:

  • 镜像拉取失败:虽然最终状态可能是ErrImagePull,但拉取超时或认证失败初期也可能表现为Pending。检查镜像地址是否正确,拉取镜像的密钥(Secret)是否已配置。
  • 调度器本身问题:极少数情况下,Kubernetes调度器(kube-scheduler)组件可能有问题。可以检查调度器Pod的日志。
    kubectl logs -n kube-system <kube-scheduler-pod-name>
    
  • 资源配额限制:命名空间(Namespace)级别设置了资源配额(ResourceQuota),导致整个命名空间的资源用量达到上限,新Pod无法被分配资源。检查命名空间的配额:kubectl describe quota -n <namespace-name>

四、总结与最佳实践

通过上面的“侦探流程”,我们可以系统性地解决Pod Pending问题。简单总结一下排查思路:

  1. 看事件 (kubectl describe pod):获取第一手线索。
  2. 查资源 (kubectl describe node, kubectl top node):确认是否资源不足。
  3. 对标签:检查Pod的节点选择器/亲和性与节点标签是否匹配。
  4. 验通行:检查节点污点与Pod容忍度是否对应。
  5. 保存储:确认Pod使用的PVC是否已成功绑定(Bound)。

应用场景:这套排查方法适用于任何基于Kubernetes的部署环境,无论是开发测试集群,还是生产环境的复杂部署。它是K8s运维和开发人员的必备技能。

技术优缺点

  • 优点:方法通用、逻辑清晰、层层递进,能覆盖绝大多数场景。依赖的工具(kubectl)是标准配置。
  • 缺点:对于调度器自身bug等极端深层次问题,需要更深入的集群管理和日志分析能力。

注意事项

  • 保持冷静:不要盲目删除Pod或重启节点,先排查。
  • 理解概念:真正理解requests/limitsnodeSelector/AffinityTaint/TolerationPVC/PV这些核心概念,排查才能得心应手。
  • 善用-o yaml-o jsonkubectl get pod -o yaml 可以让你看到Pod的完整配置,对于检查配置错误非常有用。

希望这篇博客能帮你下次遇到“Pending Pod”时,从容不迫,快速定位问题根源。记住,排查过程本身就是对Kubernetes调度原理的一次深刻理解。Happy troubleshooting!