1. 问题背景与典型场景
Kubernetes集群中Pod调度失败(FailedScheduling)是运维人员日常遇到的高频故障之一。当kube-scheduler无法为Pod找到合适的节点时,会在事件日志中留下FailedScheduling记录。这种情况通常发生在以下典型场景:
- 集群资源不足(CPU/Memory/GPU等硬件资源耗尽)
- 节点选择器(nodeSelector)或节点亲和性(nodeAffinity)规则过于严格
- 污点(Taint)与容忍度(Toleration)配置不匹配
- 持久卷(PV)绑定失败导致存储需求无法满足
- 自定义调度器与默认调度器规则冲突
去年我们生产环境曾遇到一个典型案例:某微服务Pod连续触发FailedScheduling,检查发现是开发人员在Deployment中误加了nodeSelector: disktype=ssd,而集群中实际只有HDD节点。这种问题看似简单,但在复杂业务场景下往往需要系统化的排查方法。
2. 故障诊断三板斧
2.1 查看事件日志
第一时间的诊断命令是检查Pod事件:
kubectl describe pod <pod-name> | grep -A 10 Events典型输出示例:
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 3m default-scheduler 0/10 nodes are available: 3 node(s) didn't match Pod's node affinity, 7 node(s) didn't have free ports for the requested pod ports.关键信息提取要点:
- 不可用节点数量(0/10表示所有节点均不符合条件)
- 具体失败原因(affinity/ports/resources等)
- 事件时间线(判断是偶发还是持续性问题)
2.2 检查调度器决策详情
对于复杂调度策略,需要查看调度器详细决策过程:
kubectl get events --field-selector involvedObject.kind=Pod,reason=FailedScheduling -o yaml输出中的message字段会包含类似这样的详细信息:
Predicate Failed: NoVolumeZoneConflict (1 node(s)); NodeAffinity (3 node(s)); PodToleratesNodeTaints (2 node(s))2.3 资源使用率分析
通过Metrics Server或Prometheus检查节点资源:
kubectl top nodes输出示例:
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% node-001 3820m 95% 12Gi 85% node-002 2100m 52% 8Gi 62%经验提示:当节点CPU利用率超过90%或内存超过80%时,即使绝对资源量足够,调度器也可能出于稳定性考虑拒绝调度。
3. 常见故障模式与解决方案
3.1 资源不足型故障
特征:
- 事件信息包含"insufficient cpu/memory"
kubectl describe node显示Allocatable资源接近上限
解决方案:
- 纵向扩容:
kubectl scale deployment <deploy-name> --replicas=0 # 先停止服务 kubectl edit deployment <deploy-name> # 降低requests/limits- 横向扩容(以Cluster Autoscaler为例):
apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: name: php-apache spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: php-apache minReplicas: 1 maxReplicas: 10 targetCPUUtilizationPercentage: 503.2 亲和性配置错误
典型错误配置:
affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/arch operator: In values: ["arm64"] # 集群实际是amd64架构调试技巧:
- 检查节点标签:
kubectl get nodes --show-labels- 使用临时Pod测试选择器:
kubectl run test-$RANDOM --image=busybox --overrides='{"spec": {"nodeSelector": {"disktype":"ssd"}}}' -- sleep 36003.3 污点与容忍度问题
生产环境常见场景:专用GPU节点需要特殊容忍
# 节点设置 kubectl taint nodes gpu-node-1 gpu=true:NoSchedule # 正确Pod配置 tolerations: - key: "gpu" operator: "Equal" value: "true" effect: "NoSchedule"避坑指南:Effect字段有NoSchedule/PreferNoSchedule/NoExecute三种,混淆会导致意外调度行为。
4. 高级故障注入测试
4.1 使用KubeMonkey进行混沌测试
部署示例:
helm install kubemonkey stable/kubemonkey \ --set clusterName=production \ --set dryRun=false \ --set whitelistedNamespaces="default"配置策略(annotations示例):
annotations: kubemonkey/enabled: "true" kubemonkey/termination-mode: "ScheduledDelete" kubemonkey/mean-time-between-failures: "2h"4.2 自定义调度器压力测试
通过修改调度器配置模拟高负载:
// 示例:在scheduler配置中注入随机延迟 func (s *Scheduler) scheduleOne(ctx context.Context) { time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond) // 注入延迟 // 原有调度逻辑... }监控指标重点关注:
- scheduler_pending_pods
- scheduler_scheduling_attempts
- scheduler_binding_duration_seconds
5. 诊断工具链推荐
| 工具名称 | 适用场景 | 安装方式 |
|---|---|---|
| kube-scheduler | 内置调度器日志分析 | kubectl logs |
| Stern | 多Pod日志聚合 | brew install stern |
| K9s | 可视化资源监控 | brew install derailed/k9s/k9s |
| Popeye | 集群健康扫描 | curl -sL popeye.sh |
关键排查路径:
- 通过kubectl describe确认失败原因
- 使用kubectl get events --sort-by时间排序
- 必要时启用调度器详细日志(--v=4)
- 对节点执行kubectl describe node检查条件与污点
6. 生产环境最佳实践
6.1 资源请求规范
推荐配置示例:
resources: requests: cpu: "500m" # 必须设置 memory: "1Gi" limits: cpu: "2" # 不超过节点总核数的70% memory: "4Gi" # 不超过节点内存的80%6.2 调度策略优化
多可用区部署示例:
affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: ["nginx"] topologyKey: "topology.kubernetes.io/zone"6.3 关键监控指标
Prometheus应监控的核心指标:
- scheduler_pending_pods
- scheduler_e2e_scheduling_duration_seconds
- node_memory_MemAvailable_bytes
- container_cpu_usage_seconds_total
Grafana看板建议包含:
- 调度队列深度变化曲线
- 各节点资源利用率热力图
- Pod调度延迟百分位图
7. 疑难案例复盘
某电商大促期间遇到的真实案例:
- 现象:订单服务Pod大量Pending,事件显示"0/100 nodes available"
- 初步排查:节点资源充足,无污点限制
- 深入分析:
- 发现Pod配置了podAntiAffinity requiredDuringScheduling
- 同时设置了requests.cpu=2,但集群节点均为4核
- 导致每个节点最多运行1个Pod,无法满足突发流量
- 解决方案:
- 修改为preferredDuringScheduling
- 优化requests值为1.5
- 紧急扩容一批8核节点
这个案例教会我们:即使资源绝对量充足,调度策略配置不当仍会导致容量危机。现在我们在设计文档中强制要求进行调度模拟测试:
kubectl create --dry-run=server -f deployment.yaml # 预检调度可行性