CCE节点扩了,Pod为何仍在Pending
CCE节点扩了,Pod为何仍在Pending
场景引入
我在一个测试用的 CCE 集群里做节点池扩容演练。控制台里的节点数量已经增加,节点列表也能看到新节点,但 pending-lab 命名空间里的 Pod 一直处于 Pending。最初我怀疑节点池伸缩没生效。后来我先检查 Pod 事件、节点 Ready 状态和 cordon 标记。再核对资源 requests、节点标签和污点,才发现扩出来的节点还得满足 Pod 的调度约束,光看节点数量不够。
为了复现这个问题,我创建了一个必须匹配特定节点标签的 Deployment。只要新节点没有该标签,即使节点池继续扩容,Pod 仍然不会被调度。这样就能稳定复现"节点扩了,Pod 仍 Pending",排查时也有明确的调度证据可查,不用围着线上偶发问题猜原因。
环境与前提
- 集群使用 CCE 标准集群,节点池通过控制台扩容,
kubectl可正常访问测试命名空间。 - 测试节点池的节点规格和操作系统镜像均采用节点池实际默认配置,容器运行时也保持默认。具体版本以控制台和节点实际信息为准。
kubectl与集群实际版本配套;实验镜像使用清单中的nginx:stable-alpine。- 网络和安全组沿用集群默认节点配置,未额外开放非必要端口。实验只验证调度,不依赖外部服务。
- 实验前先确认集群中没有节点带测试标签。执行
kubectl get nodes -l pending-lab.example.com/eligible=true返回空。如果后续扩容的节点也没打这个标签,Pod 仍无法调度。
具体步骤
创建带特定节点标签要求的 Deployment
kubectl create namespace pending-lab
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: pending-demo
namespace: pending-lab
spec:
replicas: 1
selector:
matchLabels:
app: pending-demo
template:
metadata:
labels:
app: pending-demo
spec:
nodeSelector:
pending-lab.example.com/eligible: "true"
containers:
- name: web
image: nginx:stable-alpine
resources:
requests:
cpu: 100m
memory: 64Mi
EOF
kubectl get pods -n pending-lab -o wide
kubectl create namespace 创建隔离命名空间,kubectl apply -f - 从标准输入读取清单,EOF 结束输入。
清单中,apiVersion/kind 声明 Deployment,metadata.name/namespace 确定对象位置。replicas: 1 只留一个 Pod,方便观察;selector.matchLabels 与 template.metadata.labels 对齐。调度约束落在 nodeSelector 上,它要求节点带 pending-lab.example.com/eligible=true。
image 指定测试镜像,resources.requests 声明 100m CPU 和 64Mi 内存,后续核对 requests 时会用到。创建完成后,kubectl get pods -o wide 应看到 Pod 为 Pending,且没有节点名。
先读 Pod 调度证据
kubectl describe pods -n pending-lab -l app=pending-demo
kubectl get events -n pending-lab --field-selector reason=FailedScheduling --sort-by=.metadata.creationTimestamp
先别急着改伸缩策略。describe pods 可以查看 Pod 的 Conditions 和 Events;get events 只过滤 FailedScheduling,并按创建时间排序。
重点看有没有 node selector/affinity 不匹配或 untolerated taint。如果出现 Insufficient cpu/memory 或卷相关错误,也要一并检查。事件可能汇总不同节点的多种失败原因,只看第一条容易漏掉其他约束。
核实扩容结果是否已成为可调度节点
kubectl get nodes -o wide
kubectl get nodes -o custom-columns='NAME:.metadata.name,CREATED:.metadata.creationTimestamp,READY:.status.conditions[?(@.type=="Ready")].status,UNSCHEDULABLE:.spec.unschedulable'
get nodes -o wide 用来看节点基本信息。自定义列会输出节点名和创建时间,也会列出 Ready 状态及节点是否被 cordon,便于放在一起核对。
CCE 控制台节点数量增加,不等于调度器已经获得可用节点。新节点可能尚未 Ready,也可能被标记为 UNSCHEDULABLE。结合节点名和创建时间,就能定位这次扩容出来的节点。
核对资源 requests 和单节点可分配资源
kubectl get pods -n pending-lab -l app=pending-demo -o yaml
kubectl describe nodes
用 get pods -o yaml 确认 Pod 的 resources.requests,再通过 describe nodes 查看节点 Allocatable 与 Allocated resources 中已分配的 requests。
实时 CPU 利用率低,还不能据此判断 Pod 能调度。调度器按 requests 和单节点可分配资源判断;多个节点剩余的 CPU、内存也不能相加后算给一个 Pod。除此之外,Pod 数量上限和扩展资源也要检查。
并排检查节点污点、标签与 Pod 调度约束
kubectl get nodes --show-labels
kubectl get nodes -o custom-columns='NAME:.metadata.name,TAINTS:.spec.taints'
kubectl get pods -n pending-lab -l app=pending-demo -o yaml
--show-labels 查看节点标签,TAINTS 自定义列查看污点。接着对照 Pod YAML,检查 nodeSelector 和 requiredDuringSchedulingIgnoredDuringExecution 是否与新节点匹配。podAntiAffinity、topologySpreadConstraints 和 tolerations 也要核对,别漏掉这些调度约束。
这个实验应能定位到 pending-lab.example.com/eligible 标签缺失。实际排障还要确认,新节点是否继承了目标节点池预期的标签和污点。
通过单变量变更验证根因
kubectl patch deployment pending-demo -n pending-lab --type=json -p='[{"op":"remove","path":"/spec/template/spec/nodeSelector"}]'
kubectl get pods -n pending-lab -o wide
kubectl patch 只移除实验 Deployment 的 nodeSelector,节点池和扩容策略都不动。随后查看 Pod 是否变为 Running,以及是否出现节点名。
如果移除该约束后 Pod 能调度,就能把 Pending 的主因定位到调度约束上,节点数量不足的判断也就可以排除了。验证完成后,执行 kubectl delete namespace pending-lab 删除命名空间。
遇到的问题与解决
节点列表里多了节点,Pod 却仍 Pending,检查发现 kubectl get nodes 显示新节点 Ready 为 False,或 UNSCHEDULABLE 为 true。节点初始化未完成,就先等 Ready 变为 True;如果节点被维护操作 cordon,必要时对目标节点执行 kubectl uncordon。再结合创建时间,确认处理的是这次扩容出来的节点。
监控里的 CPU 利用率不高,事件却报 Insufficient cpu,这时要看 requests。kubectl describe nodes 的 Allocated resources 已接近上限,实时利用率低只能说明当前负载低,不能替代调度所用的资源判断。
单个 Pod 必须放得进某一个合格节点,多个节点的剩余资源不能合并使用。处理时核对 requests,再清理无效占用或调整节点规格。
小结
这次复现把"节点扩了但 Pod 仍 Pending"拆成了能逐项验证的检查过程。Pod 事件先指出失败原因,节点 Ready 状态和 cordon 标记用来确认节点能否参与调度。接着核对 requests,再把标签、污点与 Pod 约束放在一起检查,用单变量 patch 验证根因。
这套检查适用于调度阶段的 Pending。如果遇到镜像拉取失败、CNI 异常或 PVC 挂载失败,就要换用对应证据排查。实际收益是能定位扩容后仍无法调度的具体约束;代价也很明确,需要 kubectl 权限和逐项核对,实验结束后还得清理命名空间及临时变更。
- 点赞
- 收藏
- 关注作者
评论(0)