别担心,这不是什么世界末日。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,看是否设置了nodeSelector或nodeAffinity。同时,检查节点标签是否匹配。
# 查看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=ssd 和 gpu=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问题。简单总结一下排查思路:
- 看事件 (
kubectl describe pod):获取第一手线索。 - 查资源 (
kubectl describe node,kubectl top node):确认是否资源不足。 - 对标签:检查Pod的节点选择器/亲和性与节点标签是否匹配。
- 验通行:检查节点污点与Pod容忍度是否对应。
- 保存储:确认Pod使用的PVC是否已成功绑定(Bound)。
应用场景:这套排查方法适用于任何基于Kubernetes的部署环境,无论是开发测试集群,还是生产环境的复杂部署。它是K8s运维和开发人员的必备技能。
技术优缺点:
- 优点:方法通用、逻辑清晰、层层递进,能覆盖绝大多数场景。依赖的工具(kubectl)是标准配置。
- 缺点:对于调度器自身bug等极端深层次问题,需要更深入的集群管理和日志分析能力。
注意事项:
- 保持冷静:不要盲目删除Pod或重启节点,先排查。
- 理解概念:真正理解
requests/limits、nodeSelector/Affinity、Taint/Toleration、PVC/PV这些核心概念,排查才能得心应手。 - 善用
-o yaml和-o json:kubectl get pod -o yaml可以让你看到Pod的完整配置,对于检查配置错误非常有用。
希望这篇博客能帮你下次遇到“Pending Pod”时,从容不迫,快速定位问题根源。记住,排查过程本身就是对Kubernetes调度原理的一次深刻理解。Happy troubleshooting!
评论